@@ -1,3 +1,3 @@ { - "main-page": "AIR" + "main-page": "AIR" } @@ -1,3 +1,3 @@ { - "main-page": "AIR" + "main-page": "AIR" } @@ -1,19 +1,18 @@ { - "name": "AIR", - "short_name": "AIR", - "icons": [ - { - "src": "/logo192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/logo512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "start_url": "/", - "display": "standalone" - } - \ No newline at end of file + "name": "AIR", + "short_name": "AIR", + "icons": [ + { + "src": "/logo192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/logo512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "start_url": "/", + "display": "standalone" +} @@ -2,9 +2,9 @@ import React from 'react' import { FormControlLabel, Typography } from '@mui/material' import Switch from '@mui/material/Switch' -import { setParams } from '@/src/main/store/model-parametres-store' -import { useAppDispatch } from '@/src/main/store/store' -import TooltipModelTypes from '@/src/widgets/filters-gpt/ui/tooltip-model-types' +import { setParams } from '#/app/store/model-parametres-store' +import { useAppDispatch } from '#/app/store/store' +import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' interface IProps { name: string @@ -4,11 +4,11 @@ import { TextField, Typography } from '@mui/material' import Box from '@mui/material/Box' import { TextFieldProps } from '@mui/material/TextField/TextField' -import { setParams } from '@/src/main/store/model-parametres-store' -import { useAppDispatch } from '@/src/main/store/store' -import { InputStyleDark, InputStyleLight } from '@/src/shared' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' -import TooltipModelTypes from '@/src/widgets/filters-gpt/ui/tooltip-model-types' +import { setParams } from '#/app/store/model-parametres-store' +import { useAppDispatch } from '#/app/store/store' +import { InputStyleDark, InputStyleLight } from '#/shared' +import { useThemeAndDevice } from '#/shared/lib/hooks' +import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' interface IProps { title: string @@ -2,10 +2,10 @@ import * as React from 'react' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import { MenuItem, Select, SelectChangeEvent, Stack, Typography } from '@mui/material' -import { setParams } from '@/src/main/store/model-parametres-store' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { baseColor } from '@/src/shared/lib/constants/colors' -import TooltipModelTypes from '@/src/widgets/filters-gpt/ui/tooltip-model-types' +import { setParams } from '#/app/store/model-parametres-store' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { baseColor } from '#/shared/lib/constants/colors' +import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' interface IProps { selects: { availables: string[]; default: any; end: number; start: number; step: number } @@ -1,9 +1,9 @@ import * as React from 'react' -import { setParams } from '@/src/main/store/model-parametres-store' -import { useAppDispatch } from '@/src/main/store/store' -import { Slider as Sl } from '@/src/shared' -import TooltipModelTypes from '@/src/widgets/filters-gpt/ui/tooltip-model-types' +import { setParams } from '#/app/store/model-parametres-store' +import { useAppDispatch } from '#/app/store/store' +import { Slider as Sl } from '#/shared' +import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' interface IProps { title: string @@ -2,8 +2,8 @@ import React, { useRef, useState } from 'react' import { Box, Typography } from '@mui/material' import Image from 'next/image' -import { useAppSelector } from '@/src/main/store/store' -import { TooltipCustom } from '@/src/shared' +import { useAppSelector } from '#/app/store/store' +import { TooltipCustom } from '#/shared' interface IProps { loading: boolean @@ -59,7 +59,14 @@ export const LoadImage = ({ loading, unpinImage, image, imageLoad, types }: IPro } return ( <> - + { - const link = useMemo(() => { - return accessed_models && !accessed_models.includes(slug) - ? '/account?scope=subscribe' - : `chat-bot/${slug}` + return accessed_models && !accessed_models.includes(slug) ? '/account?scope=subscribe' : `chat-bot/${slug}` }, [accessed_models]) return ( @@ -57,11 +54,7 @@ const ChatCard = ({ text, icon, title, uid, slug, accessed_models }: CardProps) textAlign: 'center', }} > - { if (Object.values(el)[0]) { @@ -26,11 +31,14 @@ async function getStats( }) try { - const { data } = await axios.get>(API_URL + '/admin/stats' + queryParams, { - headers: { - Authorization: `Bearer ${token}`, - }, - }) + const { data } = await axios.get>( + API_URL + '/admin/stats' + queryParams, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ) return data } catch (err) { @@ -1,6 +1,6 @@ import { Dayjs } from 'dayjs' -import { IProps } from '@/src/shared/lib/types/entities' +import { IProps } from '#/shared/lib/types/entities' export type IStatsProps = RequestStats & IProps @@ -3,10 +3,10 @@ import { KeyboardArrowLeft, KeyboardArrowRight } from '@mui/icons-material' import { Box, Button, MobileStepper, Table, TableBody, TableCell, TableHead, TableRow, Typography } from '@mui/material' import CircularProgress from '@mui/material/CircularProgress' -import { change } from '@/src/entities/theme' -import { TranslateFields } from '@/src/features/get-admin-stats/lib/constants' -import { useAppDispatch } from '@/src/main/store/store' -import { TypographyDark, TypographyLight } from '@/src/shared/lib/constants/styles' +import { change } from '#/entities/theme' +import { TranslateFields } from '#/features/get-admin-stats/lib/constants' +import { useAppDispatch } from '#/app/store/store' +import { TypographyDark, TypographyLight } from '#/shared/lib/constants/styles' import { IStatsProps } from '../model/types' import { useGetStats } from '../model/use-get-stats/use-get-stats' @@ -26,7 +26,15 @@ export const Stats: React.FC = ({ token, device, product, group_by, return ( - + {stats.length !== 0 && loading === false && ( = ({ token, device, product, group_by, variant='text' position='static' nextButton={ - } backButton={ - @@ -53,16 +69,28 @@ export const Stats: React.FC = ({ token, device, product, group_by, ) : ( <> {stats !== undefined && Object.keys(stats).length === 0 ? ( - Данные отсутствуют + + Данные отсутствуют + ) : ( {Object.entries(stats[activeStep]).map(([key, value]) => { return ( <> - + - {TranslateFields[key as keyof typeof TranslateFields]} + { + TranslateFields[ + key as keyof typeof TranslateFields + ] + } {value} @@ -53,7 +53,11 @@ export default function FullScreenModal({ modal, setModal, image }: IProps) { className={styles.image_style} /> ) : ( - К сожалению, изображение не загрузилось + К сожалению, изображение не загрузилось )} @@ -1,30 +1,30 @@ -.close_block{ - position: absolute; - z-index: 105; - cursor: pointer; - right: 25px; - top: 25px; +.close_block { + position: absolute; + z-index: 105; + cursor: pointer; + right: 25px; + top: 25px; } -.image_block{ - position: absolute; - padding: 0 20px; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 105; - margin: auto; - width: fit-content; - height: fit-content; +.image_block { + position: absolute; + padding: 0 20px; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 105; + margin: auto; + width: fit-content; + height: fit-content; } -.image_style{ - position: relative; - width: 100%; - height: 100%; - max-width: 960px; - max-height: 700px; - border-radius: 15px; - object-fit: contain; -} \ No newline at end of file +.image_style { + position: relative; + width: 100%; + height: 100%; + max-width: 960px; + max-height: 700px; + border-radius: 15px; + object-fit: contain; +} @@ -1,6 +1,6 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '@/src/shared/lib/constants' +import { API_URL } from '#/shared/lib/constants' import { InviteRoles, inviteRoles } from '../lib/constants' import { ResponseGetPersons } from '../model/types' @@ -1,4 +1,4 @@ -import { PersonInBusiness } from '@/src/features/invite-person-in-business' +import { PersonInBusiness } from '#/features/invite-person-in-business' type Roles = 'Владелец' | 'Сотрудник' | 'Администратор' | 'Сотрудник безопасности' export const RoleSelect: Record = { @@ -10,4 +10,8 @@ export const RoleSelect: Record = { export type InviteRoles = 'Сотрудник' | 'Администратор' | 'Сотрудник безопасности' -export const inviteRoles = { regular: 'Сотрудник', admin: 'Администратор', sec: 'Сотрудник безопасности' } +export const inviteRoles = { + regular: 'Сотрудник', + admin: 'Администратор', + sec: 'Сотрудник безопасности', +} @@ -20,7 +20,4 @@ margin-top: 15px; text-align: right; } - } - - @@ -4,9 +4,9 @@ import { SubmitErrorHandler } from 'react-hook-form/dist/types/form' import { Box, TextField, Typography } from '@mui/material' import { useSession } from 'next-auth/react' -import { invitePerson } from '@/src/features/invite-person-in-business/api/invite-person' -import { IEmailForms } from '@/src/features/register-by-email/model/types' -import { useAppSelector } from '@/src/main/store/store' +import { invitePerson } from '#/features/invite-person-in-business/api/invite-person' +import { IEmailForms } from '#/features/register-by-email/model/types' +import { useAppSelector } from '#/app/store/store' import { ButtonGray, ButtonUI, @@ -19,9 +19,9 @@ import { ModalProps, onlyNumbersOption, Select, -} from '@/src/shared' -import { useShowData } from '@/src/shared/lib/hooks' -import { getBusinessGroups } from '@/src/widgets/business-persons/api/get-businessGroups' +} from '#/shared' +import { useShowData } from '#/shared/lib/hooks' +import { getBusinessGroups } from '#/widgets/business-persons/api/get-businessGroups' import { InviteRoles, inviteRoles } from '../lib/constants' import { ResponseGetBusinessGroups, ResponseGetPersons } from '../model/types' @@ -85,7 +85,11 @@ export const InviteModal: FC = ({ open, onClose, showNewPerson {/* Поле с выбором роли сотрудника */} Выберите роль - + {/* Поле с выбором группы */} Выберите бизнес-группу @@ -1,7 +1,12 @@ import axios from 'axios' -import { FieldActivity, FieldActivitySelect, Frequency, FrequencySelect } from '@/src/features/register-business/lib/constants-step-information' -import { API_URL } from '@/src/shared/lib/constants' +import { + FieldActivity, + FieldActivitySelect, + Frequency, + FrequencySelect, +} from '#/features/register-business/lib/constants-step-information' +import { API_URL } from '#/shared/lib/constants' import { DataForCreate } from '../../model/stepper-slice' @@ -10,7 +15,9 @@ import { DataForCreateBusiness } from './types' export const createAccount = async (information: DataForCreate, token: string | null) => { const indexOfS = Object.values(FrequencySelect).indexOf(information.frequency as unknown as FrequencySelect) - const companySector = Object.entries(FieldActivitySelect).find(([key, value]) => value === information.fieldActivity) + const companySector = Object.entries(FieldActivitySelect).find( + ([key, value]) => value === information.fieldActivity + ) const data: DataForCreateBusiness = { company_name: information.companyName, @@ -12,14 +12,18 @@ export const getCompanyData = async (prompt: any) => { } try { - const { data } = await axios.post>(url, JSON.stringify({ query: prompt }), { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - Authorization: 'Token ' + token, - }, - }) + const { data } = await axios.post>( + url, + JSON.stringify({ query: prompt }), + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: 'Token ' + token, + }, + } + ) return data } catch (err) { return null @@ -1,7 +1,7 @@ import { useEffect } from 'react' import { UseFormReturn } from 'react-hook-form' -import { useAppDispatch } from '@/src/main/store/store' +import { useAppDispatch } from '#/app/store/store' import { getDataCompany } from '../stepper-slice' @@ -23,7 +23,11 @@ export const useCheckTypeCompany = (company?: string): [{ inn: RegisterOptions; } setIsIP(true) setRulesData((prev) => { - return { ...prev, inn: { ...innOptions, validate: validateInnForIP }, ogrn: { ...ogrnOptions, validate: validateOgrnForIp } } + return { + ...prev, + inn: { ...innOptions, validate: validateInnForIP }, + ogrn: { ...ogrnOptions, validate: validateOgrnForIp }, + } }) }, [company]) @@ -1,8 +1,8 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit' -import { createAccount } from '@/src/features/register-business/api/create-account/create-account' -import { CompanyData } from '@/src/features/register-business/api/get-company-date/types' -import { loadingThunk, RootState } from '@/src/main/store/store' +import { createAccount } from '#/features/register-business/api/create-account/create-account' +import { CompanyData } from '#/features/register-business/api/get-company-date/types' +import { loadingThunk, RootState } from '#/app/store/store' import { getCompanyData } from '../api/get-company-date/get-company-date' import { FieldActivity, Frequency } from '../lib/constants-step-information' @@ -90,7 +90,15 @@ export const stepperSlice = createSlice({ setProcessCreate(state, action: PayloadAction) { state.loadingCreate = action.payload }, - setContact(state, action: PayloadAction<{ name: string; email: string; phone: string; job_title: string }>) { + setContact( + state, + action: PayloadAction<{ + name: string + email: string + phone: string + job_title: string + }> + ) { const { phone, email, name, job_title } = action.payload state.dataForCreate.phone = phone state.dataForCreate.email = email @@ -5,13 +5,13 @@ import { SubmitErrorHandler } from 'react-hook-form/dist/types/form' import { Box, TextField, Typography } from '@mui/material' import { useSession } from 'next-auth/react' -import styles from '@/src/features/register-business/ui/step-information/step-information.module.scss' -import { IEmailForms } from '@/src/features/register-by-email/model/types' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { ButtonUI, Error, InputStyleDark, InputStyleLight } from '@/src/shared' -import { emailOptions } from '@/src/shared' -import { phoneOptions } from '@/src/shared/lib/constants/hook-form-options' -import { useShowData } from '@/src/shared/lib/hooks' +import styles from '#/features/register-business/ui/step-information/step-information.module.scss' +import { IEmailForms } from '#/features/register-by-email/model/types' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { ButtonUI, Error, InputStyleDark, InputStyleLight } from '#/shared' +import { emailOptions } from '#/shared' +import { phoneOptions } from '#/shared/lib/constants/hook-form-options' +import { useShowData } from '#/shared/lib/hooks' import { nameOptions } from '../../lib/constants-step-contact' import { createBusinessCompany, setContact } from '../../model/stepper-slice' @@ -4,13 +4,18 @@ import { FormProvider, useForm } from 'react-hook-form' import { SubmitErrorHandler } from 'react-hook-form/dist/types/form' import { Box, TextField, Typography } from '@mui/material' -import { FieldActivity, FieldActivitySelect, Frequency, FrequencySelect } from '@/src/features/register-business/lib/constants-step-information' -import { IEmailForms } from '@/src/features/register-by-email/model/types' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { ButtonUI, InputStyleDark, InputStyleLight } from '@/src/shared' -import { Error } from '@/src/shared' -import { useShowData } from '@/src/shared/lib/hooks' -import { SelectUI } from '@/src/shared/ui/select' +import { + FieldActivity, + FieldActivitySelect, + Frequency, + FrequencySelect, +} from '#/features/register-business/lib/constants-step-information' +import { IEmailForms } from '#/features/register-by-email/model/types' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { ButtonUI, InputStyleDark, InputStyleLight } from '#/shared' +import { Error } from '#/shared' +import { useShowData } from '#/shared/lib/hooks' +import { SelectUI } from '#/shared/ui/select' import { setFieldActivity, setFrequency, setNumberStuff, switchNextStep } from '../../model/stepper-slice' @@ -4,13 +4,13 @@ import { useForm } from 'react-hook-form' import { SubmitErrorHandler } from 'react-hook-form/dist/types/form' import { Autocomplete, Box, TextField, Typography } from '@mui/material' -import { CompanyData } from '@/src/features/register-business/api/get-company-date/types' -import { useCheckTypeCompany } from '@/src/features/register-business/model/legal-info/use-check-type-company' -import styles from '@/src/features/register-business/ui/step-information/step-information.module.scss' -import { IEmailForms } from '@/src/features/register-by-email/model/types' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { ButtonUI, Error, InputStyleDark, InputStyleLight } from '@/src/shared' -import { useShowData } from '@/src/shared/lib/hooks' +import { CompanyData } from '#/features/register-business/api/get-company-date/types' +import { useCheckTypeCompany } from '#/features/register-business/model/legal-info/use-check-type-company' +import styles from '#/features/register-business/ui/step-information/step-information.module.scss' +import { IEmailForms } from '#/features/register-by-email/model/types' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { ButtonUI, Error, InputStyleDark, InputStyleLight } from '#/shared' +import { useShowData } from '#/shared/lib/hooks' import { companyNameOptions, innOptions, ogrnOptions } from '../../lib/constatnts-step-legal-informative' import { useAutoLoadingInfo } from '../../model/legal-info/use-auto-loading-info' @@ -5,9 +5,9 @@ import StepperMui from '@mui/material/Stepper' import Image from 'next/image' import { useRouter } from 'next/router' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { Error } from '@/src/shared' -import { useShowData } from '@/src/shared/lib/hooks' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { Error } from '#/shared' +import { useShowData } from '#/shared/lib/hooks' import { steps } from '../../lib/constants' import { switchPreviousStep, switchStep } from '../../model/stepper-slice' @@ -104,7 +104,13 @@ export const Stepper = () => { ))} - {activeStep === 0 ? : activeStep === 1 ? : } + {activeStep === 0 ? ( + + ) : activeStep === 1 ? ( + + ) : ( + + )} @@ -10,16 +10,16 @@ import axios from 'axios' import Link from 'next/link' import { useRouter } from 'next/router' -import { PasswordOptions } from '@/src/features/register-by-email/lib/constants' -import { IEmailForms } from '@/src/features/register-by-email/model/types' -import { useAppSelector } from '@/src/main/store/store' -import { emailOptions } from '@/src/shared' -import { CheckBoxAgreeWithRules } from '@/src/shared' -import { Error } from '@/src/shared' -import { InputStyleDark, InputStyleLight } from '@/src/shared' -import { API_URL } from '@/src/shared/lib/constants/constants' -import { useShowData } from '@/src/shared/lib/hooks' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' +import { PasswordOptions } from '#/features/register-by-email/lib/constants' +import { IEmailForms } from '#/features/register-by-email/model/types' +import { useAppSelector } from '#/app/store/store' +import { emailOptions } from '#/shared' +import { CheckBoxAgreeWithRules } from '#/shared' +import { Error } from '#/shared' +import { InputStyleDark, InputStyleLight } from '#/shared' +import { API_URL } from '#/shared/lib/constants/constants' +import { useShowData } from '#/shared/lib/hooks' +import { useThemeAndDevice } from '#/shared/lib/hooks' interface IRegisterEmailFormProps { successLogin: () => void @@ -1,7 +1,7 @@ import axios, { AxiosResponse } from 'axios' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' -import { API_URL } from '@/src/shared/lib/constants' +import { ResponseGetPersons } from '#/features/invite-person-in-business' +import { API_URL } from '#/shared/lib/constants' export const removePerson = async (email: string, token?: string): Promise => { if (!token) { @@ -1,7 +1,7 @@ import { Dispatch, SetStateAction, useState } from 'react' import { useSession } from 'next-auth/react' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' +import { ResponseGetPersons } from '#/features/invite-person-in-business' import { removePerson } from '../api/remove' @@ -1,11 +1,17 @@ import { Box, Typography } from '@mui/material' import Image from 'next/image' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' -import { ButtonGray, ButtonUI, Modal } from '@/src/shared' +import { ResponseGetPersons } from '#/features/invite-person-in-business' +import { ButtonGray, ButtonUI, Modal } from '#/shared' import { useRemove } from '../../model/remove' -export const XMark = ({ email, setNewPersons }: { email: string; setNewPersons: (persons: ResponseGetPersons) => void }) => { +export const XMark = ({ + email, + setNewPersons, +}: { + email: string + setNewPersons: (persons: ResponseGetPersons) => void +}) => { const [open, setOpen, removeFn] = useRemove(setNewPersons, email) return ( @@ -29,7 +35,14 @@ export const XMark = ({ email, setNewPersons }: { email: string; setNewPersons: > Вы дейсвительно хотите удалить пользователя {email} ? - + { e.stopPropagation() @@ -1,7 +1,7 @@ import React from 'react' import { Box, Typography } from '@mui/material' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' +import { useThemeAndDevice } from '#/shared/lib/hooks' interface ISuggestedMessage { title: string @@ -1,9 +1,9 @@ import React from 'react' import { Box, Stack, Typography } from '@mui/material' -import { suggestedMessagesList } from '@/src/features/send-suggested-message/lib/constants' -import SuggestedMessage from '@/src/features/send-suggested-message/ui/suggested-message' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' +import { suggestedMessagesList } from '#/features/send-suggested-message/lib/constants' +import SuggestedMessage from '#/features/send-suggested-message/ui/suggested-message' +import { useThemeAndDevice } from '#/shared/lib/hooks' interface IProps { device: 'mobile' | 'desktop' @@ -1 +1 @@ -export { SuggestedMessages } from '@/src/features/send-suggested-message/ui/suggested-messages' +export { SuggestedMessages } from '#/features/send-suggested-message/ui/suggested-messages' @@ -2,7 +2,7 @@ import React from 'react' import { Box, Typography } from '@mui/material' import Link from 'next/link' -import styles from '@/src/shared/styles/chats-bot-pages.module.scss' +import styles from '#/shared/styles/chats-bot-pages.module.scss' export default function Title(props: any) { return ( @@ -1,8 +1,8 @@ import React from 'react' import { Box, Tooltip, Typography } from '@mui/material' -import { useAppSelector } from '@/src/main/store/store' -import { baseColor } from '@/src/shared/lib/constants/colors' +import { useAppSelector } from '#/app/store/store' +import { baseColor } from '#/shared/lib/constants/colors' interface ITooltipMy { begin: () => void @@ -1,8 +1,8 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' -import { CopywriteProxy } from '@/src/domains/copywrite/proxy/copywrite-proxy' -import { Template } from '@/src/domains/copywrite/proxy/types/template' -import { Message } from '@/src/shared/lib/types/model' +import { CopywriteProxy } from '#/domains/copywrite/proxy/copywrite-proxy' +import { Template } from '#/domains/copywrite/proxy/types/template' +import { Message } from '#/shared/lib/types/model' export interface Theme { templates: Template[] | null @@ -14,13 +14,19 @@ const initialState: Theme = { generation: null, } -export const loadTemplates = createAsyncThunk('copywrite/loadTemplates', async (token?: string): Promise => { - return await CopywriteProxy.getTemplates(token) -}) - -export const loadGeneration = createAsyncThunk('copywrite/loadGeneration', async (token?: string): Promise => { - return await CopywriteProxy.getGeneration(token) -}) +export const loadTemplates = createAsyncThunk( + 'copywrite/loadTemplates', + async (token?: string): Promise => { + return await CopywriteProxy.getTemplates(token) + } +) + +export const loadGeneration = createAsyncThunk( + 'copywrite/loadGeneration', + async (token?: string): Promise => { + return await CopywriteProxy.getGeneration(token) + } +) export const copySlice = createSlice({ name: 'copySlice', @@ -2,11 +2,11 @@ import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react' import { useRouter } from 'next/router' import { useSession } from 'next-auth/react' -import { emptyTemplate } from '@/src/domains/copywrite/lib/constants' -import { Template } from '@/src/domains/copywrite/proxy/types/template' -import { loadGeneration, loadTemplates } from '@/src/features/use-copy/copy-slice' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { Message } from '@/src/shared/lib/types/model' +import { emptyTemplate } from '#/domains/copywrite/lib/constants' +import { Template } from '#/domains/copywrite/proxy/types/template' +import { loadGeneration, loadTemplates } from '#/features/use-copy/copy-slice' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { Message } from '#/shared/lib/types/model' interface UseCopy { currentTemplate: Template | null @@ -3,12 +3,12 @@ import axios, { AxiosError, AxiosResponse } from 'axios' import { ContentState, EditorState } from 'draft-js' import { useSession } from 'next-auth/react' -import { Template } from '@/src/domains/copywrite/proxy/types/template' -import { loadGeneration } from '@/src/features/use-copy/copy-slice' -import { useAppDispatch } from '@/src/main/store/store' -import { useShowData } from '@/src/shared' -import { API_URL } from '@/src/shared/lib/constants' -import { Message, MessageSend } from '@/src/shared/lib/types/model' +import { Template } from '#/domains/copywrite/proxy/types/template' +import { loadGeneration } from '#/features/use-copy/copy-slice' +import { useAppDispatch } from '#/app/store/store' +import { useShowData } from '#/shared' +import { API_URL } from '#/shared/lib/constants' +import { Message, MessageSend } from '#/shared/lib/types/model' type Languages = 'ru' | 'en' | 'it' | 'fr' type LanguagesText = 'Русский' | 'Английский' | 'Итальянский' | 'Французский' @@ -22,7 +22,16 @@ export const langs: Record = { export const languages = { ...langs, Немецкий: 'de' } export const target_audiences = ['Вся', '18+', '21+', '30+', '14-20', '35-40'] -export const tovs = ['Нейтральный', 'Спокойный', 'Агрессивный', 'Серьезный', 'Провокационный', 'Остроумный', 'Наставнический', 'Дружелюбный'] +export const tovs = [ + 'Нейтральный', + 'Спокойный', + 'Агрессивный', + 'Серьезный', + 'Провокационный', + 'Остроумный', + 'Наставнический', + 'Дружелюбный', +] type Setting = Pick @@ -2,9 +2,9 @@ import { useEffect, useState } from 'react' import axios from 'axios' import { useSession } from 'next-auth/react' -import { dataLine as data } from '@/src/shared' -import { API_URL } from '@/src/shared/lib/constants' -import { Error } from '@/src/shared/lib/types/entities' +import { dataLine as data } from '#/shared' +import { API_URL } from '#/shared/lib/constants' +import { Error } from '#/shared/lib/types/entities' type ResponseStats = { source: string; amount: string }[] @@ -19,9 +19,12 @@ const statsColors = ['#8280FF', '#67D9A2', '#FF867E', '#7ED1FF', '#FFD700', '#FF const getStatistic = async (type: string, interval: string, token?: string): Promise => { try { - const { data } = await axios.get(API_URL + `/payments/expenses?source_strategy=${type}&interval_strategy=${interval}`, { - headers: { Authorization: `Bearer ${token}` }, - }) + const { data } = await axios.get( + API_URL + `/payments/expenses?source_strategy=${type}&interval_strategy=${interval}`, + { + headers: { Authorization: `Bearer ${token}` }, + } + ) return data } catch (e) { return { @@ -1,31 +0,0 @@ -.card { - background-color: var(--new-ui-main-color); - width: 381px; - height: 227px; - border-radius: 15px; - margin-right: 20px; - margin-top: 20px; - - cursor: pointer; - padding: 30px; - box-sizing: border-box; - - @media (max-width:420px) { - width: 92vw; - } - - .description { - margin-top: 20px; - - .title { - font-weight: 600; - } - - .text { - color: var(--new-ui-gray-color); - font-weight: 400; - margin-top: 6px; - font-size: 15px; - } - } -} \ No newline at end of file @@ -1 +0,0 @@ -export { Layout } from '@/src/main/layout/ui/layout' @@ -1,28 +0,0 @@ -.btn-classic { - background-color: var(--air-color); - border-radius: 13px; - text-transform: none; - color: white; - height: 43px; - font-size: 13px; - font-weight: 600; - border: none; - box-shadow: none; - &:hover { - background-color: var(--text-color-purple); - opacity: 0.9; - } - &:disabled{ - color: white; - } -} - -.btn-danger { - @extend .btn-classic; - color:#FF2372; - background-color: var(--new-ui-btn-danger-bg); - &:hover { - background-color: var(--new-ui-btn-danger-bg); - opacity: 0.9; - } -} \ No newline at end of file @@ -1,116 +0,0 @@ -.toggle_button { - padding: 0; - margin-right: 12px; - border: none; -} - -.toggle_button:hover { - background-color: transparent; -} - -.wrap_toggle_button { - border: none; - border-radius: 13px; - text-transform: none; - font-weight: 500; - font-size: 15px; - /*white-space: nowrap;*/ - color: var(--new-ui-gray-color); -} - -.wrap_toggle_button_active { - border: none; - background-color: var(--new-ui-main-color); - border-radius: 13px; - text-transform: none; - font-weight: 500; - font-size: 15px; - color: var(--text-color-main); -} - -.toggle_text_active { - font-weight: 500; - font-size: 15px; - color: var(--text-color-main); -} - -.wrap_toggle_button_active_chat { - border: none; - background-color: #f2f2ff; - border-radius: 13px; - padding: 10px 15px; - text-transform: none; -} - -.toggle_text_active_chat { - font-weight: 500; - font-size: 15px; - color: #8280ff; -} - -.chat-item { - width: 100%; - border: none; - background-color: white; - border-radius: 13px; - padding: 10px 15px; - text-transform: none; -} -.chat-item_active { - width: 100%; - border: none; - background-color: #f2f2ff; - border-radius: 13px; - padding: 10px 15px; - text-transform: none; -} - -.chat-text { - font-weight: 500; - font-size: 15px; - color: var(--new-ui-main-color); -} - -.toggle_text { - font-weight: 500; - font-size: 15px; - /*white-space: nowrap;*/ - color: var(--new-ui-gray-color); - text-transform:none; -} - -.wrap_block { - width: 100%; - border-radius: 13px; - background-color: var(--new-ui-main-color); - padding: 30px; -} - -@media (max-width: 768px) { - .main_block { - width: 100%; - display: flex; - flex-direction: column; - } - - .main_block > div { - margin: 0; - } -} - - - -.main_block { - display: flex; - align-items: flex-start; - justify-content: space-between; - margin-top: 20px; -} - -.input_title { - font-size: 15px; - margin-bottom: 5px; - margin-left: 3px; - font-weight: 400; - color: var(--new-ui-gray-color); -} @@ -2,8 +2,8 @@ import axios, { AxiosResponse } from 'axios' import { User } from 'next-auth' import { JWT } from 'next-auth/jwt' -import { getAll } from '@/src/entities/user-account/model/user-type-slice' -import { API_URL } from '@/src/shared/lib/constants' +import { getAll } from '#/entities/user-account/model/user-type-slice' +import { API_URL } from '#/shared/lib/constants' import { AuthConstants } from './constants' @@ -1,117 +1,6 @@ -import * as React from 'react' -import { useEffect, useState } from 'react' -import { Box, Typography } from '@mui/material' -import axios from 'axios' -import { useSession } from 'next-auth/react' -import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { AudioModelsPage } from '#/views/audio' +import { getDefaultLayout } from '#/widgets/layouts' -import { Layout } from '@/src/main/layout' -import { addToFavorites, deleteFromFavorites } from '@/src/shared/api/endpoints' -import { Card } from '@/src/shared/card/card' -import { API_URL } from '@/src/shared/lib/constants' -import { getTypeDevice } from '@/src/shared/lib/helpers' +AudioModelsPage.getLayout = getDefaultLayout({ titlePage: 'Аудио' }) -interface Model { - uid: string - title: string - description: string - link: string - model: string - languages: string[] - slug: string - companies: string - token_price: number - associations?: [] - model_config: { - n: number - model: string - top_p: number - temperature: number - presence_penalty: number - frequency_penalty: number - } - image_link: string - is_favourite: boolean - available_versions: null -} - -export async function getServerSideProps(context: any): Promise<{ props: any }> { - const deviceType = getTypeDevice(context) - - return { - props: { - ...(await serverSideTranslations(context.locale, ['common'])), - deviceType, - }, - } -} - -async function getImages(token?: string): Promise { - try { - const { data } = await axios.get(API_URL + '/ml_models/?category=audio', { - headers: { - Authorization: `Bearer ${token}`, - }, - }) - return data - } catch (e) { - return [] - } -} - -export const Images: React.FC = ({ deviceType }) => { - const [bots, setBots] = useState(null) - - const { data } = useSession() - - useEffect(() => { - if (!data?.access) { - return - } - - getImages(data?.access).then((res) => setBots(res)) - }, [data?.access]) - - const changeFavorite = async (uid: string) => { - let res = 400 - - if (bots?.find((el) => el.uid === uid)!.is_favourite === true) { - res = await deleteFromFavorites(uid, data?.access) - } else { - res = await addToFavorites(uid, data?.access) - } - - if (res !== 400) - setBots((prev) => - prev!.map((el) => { - if (el.uid === uid) { - el.is_favourite = !el.is_favourite - } - - return el - }) - ) - } - - return ( - - Аудио - - {bots?.map((item, idx) => { - return ( - - ) - })} - - - ) -} - -export default Images +export default AudioModelsPage @@ -1,413 +1,6 @@ -import React, { useCallback, useEffect } from 'react' -import { useDispatch } from 'react-redux' -import { Box, Collapse, Stack, Typography } from '@mui/material' -import { useRouter } from 'next/router' -import { useSession } from 'next-auth/react' -import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { ChatBotPage } from '#/views/chat-bot' +import { getDefaultLayout } from '#/widgets/layouts' -import BotParamsMap from '@/src/features/bot-params/bot-params-map' -import { useChats } from '@/src/features/chats' -import Title from '@/src/features/title/title' -import { TutorialContext } from '@/src/features/tutorial-context/tutorial-context' -import { ChatSelect } from '@/src/main/components/chat_select' -import { AllChatWindow } from '@/src/main/components/chat_window' -import { ResetFilters } from '@/src/main/components/filters/reset_filters' -import { Layout } from '@/src/main/layout' -import { setParams as setParametres } from '@/src/main/store/model-parametres-store' -import { useAppSelector } from '@/src/main/store/store' -import { DrawerCustom, Error, useModel, useShowData } from '@/src/shared' -import model_api from '@/src/shared/api/models/api' -import { IModel } from '@/src/shared/api/models/models' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { getDeviceOs } from '@/src/shared/lib/helpers/get-type-device' -import styles from '@/src/shared/styles/chats-bot-pages.module.scss' -import { Chats } from '@/src/widgets/chats' +ChatBotPage.getLayout = getDefaultLayout() -export async function getServerSideProps(context: any): Promise<{ props: any }> { - const deviceType = getTypeDevice(context) - const deviceOs = getDeviceOs(context) - - return { - props: { - ...(await serverSideTranslations(context.locale, ['common'])), - deviceType, - deviceOs, - }, - } -} - -const Page: React.FC = ({ deviceType, deviceOs }) => { - const [botParams, setBotParams] = React.useState(null) - const [modelType, setModelType] = React.useState('') - const [openFiltersMobile, setOpenFiltersMobile] = React.useState(false) - const [version, setVersion] = React.useState('') - const [step, setStep] = React.useState(0) - // const [filters, setFilters] = React.useState() - const [params, setParams] = React.useState(false) - const [file, setFile] = React.useState(null) - const { error, showError } = useShowData() - const desktop = deviceType === 'desktop' - const { data } = useSession() - const router = useRouter() - - const { - chats, - currentChat, - chatSetting, - setChatSetting, - setChat, - removeChat, - createNewChat, - handleClickChatSetting, - isTryRename, - setIsTryRename, - } = useChats(modelType) - const { messages, sendMessage, loading, getMessagesPagination, deleteMessage } = useModel( - currentChat, - showError, - modelType - ) - - const includeParams = useAppSelector((state) => state.params.params) - const dispatch = useDispatch() - - const deleteMessageMemo = useCallback(deleteMessage, [currentChat, messages]) - - React.useEffect(() => { - if (data?.access) { - model_api.getBotParams(router.asPath.split('/')[2], data.access).then((res) => { - setBotParams(res) - setModelType(res.slug) - if (res.versions.length !== 0) { - setVersion(res.versions[0].slug) - dispatch( - setParametres( - res.parameters.reduce( - (a, v) => - v.versions.includes(res.versions[0].slug) - ? { ...a, [v.key]: v.values.default } - : { ...a }, - {} - ) - ) - ) - } else { - setVersion('') - dispatch( - setParametres( - res.parameters.reduce( - (a, v) => ({ ...a, [v.key]: v.values.default }), - {} - ) - ) - ) - } - }) - } - }, [data?.access, router.query]) - - const resetParams = () => { - if (botParams) { - dispatch(setParametres({})) - if (botParams.versions?.length !== 0) { - setVersion(botParams.versions[0].slug) - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => - v.versions.includes(botParams.versions[0].slug) - ? { ...a, [v.key]: v.values.default } - : { ...a }, - {} - ) - ) - ) - } else { - setVersion(botParams.slug) - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => ({ ...a, [v.key]: v.values.default }), - {} - ) - ) - ) - } - } - } - - const setDefaultParams = () => { - if (botParams) { - dispatch(setParametres({})) - if (version !== '') { - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => - v.versions.includes(version) - ? { ...a, [v.key]: v.values.default } - : { ...a }, - {} - ) - ) - ) - } else { - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => ({ ...a, [v.key]: v.values.default }), - {} - ) - ) - ) - } - } - } - - const viewMobileSettings = () => { - setOpenFiltersMobile(true) - } - - const hideMobileSettings = () => { - setOpenFiltersMobile(false) - } - - const onSendMessage = (input: string, required: (string | null)[]) => { - if (required.includes('text') && (input === '' || input === null)) { - showError('Введите сообщение!') - return false - } - if (required.includes('image') && file === null) { - showError('Прикрепите изображение!') - return false - } - - let data = {} - if (version === '') { - data = { - ...includeParams, - } - } else { - data = { - version: version, - ...includeParams, - } - } - - sendMessage({ - content: input, - file: file, - info: { - ...data, - }, - }) - return true - } - - return ( - - - - <Box className={styles.main}> - <Box className={styles.chatWindow}> - <AllChatWindow - deviceOs={deviceOs} - input_types={botParams?.inputs} - setFile={setFile} - file={file} - getMessagesPagination={getMessagesPagination} - sendMessage={onSendMessage} - deleteMessage={deleteMessageMemo} - openMobileFilters={viewMobileSettings} - isCalculating={true} - device={deviceType} - loading={loading} - messages={messages || []} - modelType={modelType} - modelTitle={botParams?.title} - currentVersion={version} - /> - </Box> - <Box className={styles.settings}> - <Chats - desktop={desktop} - isTryRename={isTryRename} - setIsTryRename={setIsTryRename} - currentChat={currentChat} - setChat={setChat} - chats={chats} - chatSetting={chatSetting} - setChatSetting={setChatSetting} - removeChat={removeChat} - createNewChat={createNewChat} - handleClickChatSetting={handleClickChatSetting} - /> - {desktop && ( - <Stack className='pd-30 bg-color-block border-radius-main' spacing={2}> - {botParams?.versions && botParams.versions?.length !== 0 && ( - <> - <Typography - sx={{ - color: '#A4AAB5', - fontWeight: '600', - fontSize: '14px', - letterSpacing: '0.1px', - }} - > - ВЕРСИИ - </Typography> - <ChatSelect - setDefaultParams={setDefaultParams} - value={version} - list={botParams.versions} - setValue={setVersion} - /> - </> - )} - {botParams && botParams.parameters?.length > 0 && ( - <Box - display={'flex'} - alignItems={'center'} - gap={'5px'} - sx={{ cursor: 'pointer' }} - onClick={() => { - setParams(!params) - }} - > - <Typography - sx={{ - color: '#A4AAB5', - fontWeight: '600', - fontSize: '14px', - letterSpacing: '0.1px', - }} - > - ПАРАМЕТРЫ - </Typography> - <svg - width='14' - height='20' - viewBox='0 0 21 13' - fill='none' - xmlns='http://www.w3.org/2000/svg' - className={`${ - params ? 'rotate-180' : 'rotate-0' - }`} - > - <path - fillRule='evenodd' - clipRule='evenodd' - d='M0.614851 0.615358C1.00866 0.221668 1.54271 0.000505666 2.09955 0.000505642C2.6564 0.000505617 3.19044 0.221668 3.58425 0.615357L10.4996 7.53066L17.4149 0.615357C17.8109 0.232825 18.3414 0.0211567 18.892 0.0259414C19.4426 0.0307261 19.9693 0.25158 20.3587 0.640937C20.748 1.03029 20.9689 1.557 20.9737 2.10761C20.9784 2.65823 20.7668 3.18869 20.3842 3.58476L11.9843 11.9848C11.5904 12.3784 11.0564 12.5996 10.4996 12.5996C9.94271 12.5996 9.40866 12.3784 9.01485 11.9848L0.614851 3.58476C0.221162 3.19095 -4.3461e-07 2.6569 -4.5895e-07 2.10006C-4.8329e-07 1.54321 0.221162 1.00917 0.614851 0.615358Z' - fill='#7f7df3' - /> - </svg> - </Box> - )} - - {botParams && botParams.parameters?.length > 0 ? ( - <Collapse - in={params} - orientation='vertical' - collapsedSize={0} - sx={{}} - > - <BotParamsMap - currentVersion={version} - params={botParams?.parameters} - /> - <ResetFilters - desktop={desktop} - closeDrawer={hideMobileSettings} - reset={resetParams} - /> - </Collapse> - ) : ( - <Typography - sx={{ - color: '#6e6e6e', - fontSize: '15px', - fontWeight: '500', - }} - > - Параметры отсутствуют - </Typography> - )} - </Stack> - )} - {!desktop && ( - <DrawerCustom - open={openFiltersMobile} - onClose={hideMobileSettings} - > - <Stack spacing={1} padding={2.4}> - {botParams?.versions && botParams.versions?.length !== 0 && ( - <> - <Typography - sx={{ - color: '#A4AAB5', - fontWeight: '600', - fontSize: '14px', - letterSpacing: '0.1px', - margin: '20px 0px 0px !important', - }} - > - ВЕРСИИ - </Typography> - <ChatSelect - setDefaultParams={setDefaultParams} - value={version} - list={botParams.versions} - setValue={setVersion} - /> - </> - )} - {botParams && botParams.parameters?.length > 0 ? ( - <> - <Typography - sx={{ - color: '#A4AAB5', - fontWeight: '600', - fontSize: '14px', - letterSpacing: '0.1px', - margin: '20px 0px 0px !important', - }} - > - ПАРАМЕТРЫ - </Typography> - <BotParamsMap - currentVersion={version} - params={botParams?.parameters} - /> - <ResetFilters - closeDrawer={hideMobileSettings} - desktop={desktop} - reset={resetParams} - /> - </> - ) : ( - <Typography - sx={{ - color: '#6e6e6e', - fontSize: '15px', - fontWeight: '500', - }} - > - Параметры отсутствуют - </Typography> - )} - </Stack> - </DrawerCustom> - )} - </Box> - <Error error={error} open={Boolean(error)} /> - </Box> - </TutorialContext.Provider> - </Layout> - ) -} - -export default Page +export default ChatBotPage @@ -1,78 +1,6 @@ -import * as React from 'react' -import { useEffect, useState } from 'react' -import { Box, Typography } from '@mui/material' -import CircularProgress from '@mui/material/CircularProgress' -import { useSession } from 'next-auth/react' -import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { ChatBotsPage } from "#/views/chat-bot"; +import { getDefaultLayout } from "#/widgets/layouts"; -import ChatCard from '@/src/main/components/chat_bot_card' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import model_api from '@/src/shared/api/models/api' -import { IShortModel } from '@/src/shared/api/models/models' -import { getTypeDevice } from '@/src/shared/lib/helpers' +ChatBotsPage.getLayout = getDefaultLayout({ titlePage: 'Чат-боты' }); -export async function getServerSideProps(context: any): Promise<{ props: any }> { - const deviceType = getTypeDevice(context) - - return { - props: { - ...(await serverSideTranslations(context.locale, ['common'])), - deviceType, - }, - } -} - -const Page: React.FC<any> = ({ deviceType }) => { - const [bots, setBots] = useState<IShortModel[] | null>(null) - const { data } = useSession() - const user = useAppSelector((state) => state.user) - - useEffect(() => { - if (!data?.access) { - return - } - model_api - .getBots(data?.access) - .then((res) => { - setBots(res) - }) - .catch((err) => {}) - }, [data?.access]) - - return ( - <Layout titlePage={'Чат-боты'} device={deviceType}> - <Typography sx={{ fontSize: 24, fontWeight: 'bold', marginTop: '25px' }}>Чат-боты</Typography> - <Box display='flex' flexWrap='wrap' width='100%' justifyContent='start'> - {bots ? ( - bots.map((item, idx) => ( - <ChatCard - accessed_models={user.payment_plan.plan.accessed_models} - uid={item.uid} - key={idx} - title={item.title} - icon={item.image} - text={item.description} - slug={item.slug} - /> - )) - ) : ( - <CircularProgress - size={50} - thickness={3} - sx={{ - color: '#7F7DF3', - position: 'absolute', - top: '40%', - left: 0, - right: 0, - margin: '0 auto', - }} - /> - )} - </Box> - </Layout> - ) -} - -export default Page +export default ChatBotsPage \ No newline at end of file @@ -1,30 +1,30 @@ .card { - background-color: var(--new-ui-main-color); - width: 381px; - height: 227px; - border-radius: 15px; - margin-right: 20px; - margin-top: 20px; + background-color: var(--new-ui-main-color); + width: 381px; + height: 227px; + border-radius: 15px; + margin-right: 20px; + margin-top: 20px; - cursor: pointer; - padding: 30px; - box-sizing: border-box; + cursor: pointer; + padding: 30px; + box-sizing: border-box; - @media (max-width:768px) { - width: 100%; - } - .description { - margin-top: 20px; + @media (max-width: 768px) { + width: 100%; + } + .description { + margin-top: 20px; - .title { - font-weight: 600; - } + .title { + font-weight: 600; + } - .text { - color: var(--new-ui-gray-color); - font-weight: 400; - margin-top: 6px; - font-size: 15px; - } - } + .text { + color: var(--new-ui-gray-color); + font-weight: 400; + margin-top: 6px; + font-size: 15px; + } + } } @@ -7,16 +7,16 @@ import dynamic from 'next/dynamic' import Image from 'next/image' import { getSession } from 'next-auth/react' -import { toEditorState } from '@/src/domains/copywrite/lib/helper' -import { useCopy } from '@/src/features/use-copy/use-copy' -import { languages, target_audiences, tovs, useTemplate } from '@/src/features/use-copy/use-template' -import { Layout } from '@/src/main/layout' -import { Input, Loader, TooltipCustom } from '@/src/shared' -import { api } from '@/src/shared/api/endpoints' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { Message } from '@/src/shared/lib/types/model' -import { IDalleProps } from '@/src/shared/lib/types/types-dalle' -import { SelectUI } from '@/src/shared/ui/select' +import { toEditorState } from '#/domains/copywrite/lib/helper' +import { useCopy } from '#/features/use-copy/use-copy' +import { languages, target_audiences, tovs, useTemplate } from '#/features/use-copy/use-template' +import { Layout } from '#/app/layout' +import { Input, Loader, TooltipCustom } from '#/shared' +import { api } from '#/shared/api/endpoints' +import { getTypeDevice } from '#/shared/lib/helpers' +import { Message } from '#/shared/lib/types/model' +import { IDalleProps } from '#/shared/lib/types/types-dalle' +import { SelectUI } from '#/shared/ui/select' import Title from '../../features/title/title' @@ -106,7 +106,9 @@ const Create: React.FC<IDalleProps> = ({ device, token, favorites }) => { setShowGeneration(false) } - const Editor = dynamic(() => import('react-draft-wysiwyg').then((res) => res.Editor), { ssr: false }) + const Editor = dynamic(() => import('react-draft-wysiwyg').then((res) => res.Editor), { + ssr: false, + }) const EditorWrap = (): JSX.Element | null => { if (showGeneration) { @@ -127,7 +129,13 @@ const Create: React.FC<IDalleProps> = ({ device, token, favorites }) => { > <Typography className='title-struct'>{el.content.slice(0, 60)}...</Typography> <TooltipCustom title={'Скопировать ответ'}> - <Image onClick={() => copy(el.content)} src={'/svg/copy.svg'} width={22} height={22} alt={'copy'} /> + <Image + onClick={() => copy(el.content)} + src={'/svg/copy.svg'} + width={22} + height={22} + alt={'copy'} + /> </TooltipCustom> </Box> )) @@ -148,18 +156,30 @@ const Create: React.FC<IDalleProps> = ({ device, token, favorites }) => { return ( <Layout isLoader={currentTemplate === null} titlePage={'Копирайтинг'} device={device}> - <Title title={showGeneration ? 'Мои генерации' : theme ? theme : 'Пустой шаблон'} type={'Копирайтинг'} linkBack={'/copy'} /> + <Title + title={showGeneration ? 'Мои генерации' : theme ? theme : 'Пустой шаблон'} + type={'Копирайтинг'} + linkBack={'/copy'} + /> <Box display={'flex'} justifyContent='space-between' flexDirection={desktop ? 'row' : 'column-reverse'} - sx={{ marginBottom: desktop ? 0 : 3, width: desktop ? '98%' : '100%', marginTop: desktop ? 3 : 0 }} + sx={{ + marginBottom: desktop ? 0 : 3, + width: desktop ? '98%' : '100%', + marginTop: desktop ? 3 : 0, + }} > <Box className='pd-30 bg-color-block border-radius-main' width='100%' position='relative'> <Box height='95%' sx={{ overflowY: 'auto' }}> <Box width='50%' display='flex' marginBottom={2}> {!showGeneration && ( - <Button fullWidth className='btn-classic' onClick={() => setShowGeneration(true)}> + <Button + fullWidth + className='btn-classic' + onClick={() => setShowGeneration(true)} + > Мои генерации </Button> )} @@ -178,17 +198,32 @@ const Create: React.FC<IDalleProps> = ({ device, token, favorites }) => { <EditorWrap /> </Box> </Box> - <Box height='auto' className='pd-30 bg-color-block border-radius-main' width='500px' marginLeft={1.5}> + <Box + height='auto' + className='pd-30 bg-color-block border-radius-main' + width='500px' + marginLeft={1.5} + > <Typography className='font-16 color-gray'>Настройки генерации</Typography> <Stack spacing={0.5} sx={{ marginTop: 2.5 }}> <Typography className='title-main-gray'>Ваш запрос</Typography> <textarea value={content} onChange={(e) => setContent(e.target.value)} /> </Stack> <Box sx={{ marginTop: 1 }}> - <SelectUI title={'Тон'} value={tov} onChange={(e) => setTov(e.target.value)} list={tovs} /> + <SelectUI + title={'Тон'} + value={tov} + onChange={(e) => setTov(e.target.value)} + list={tovs} + /> </Box> <Box sx={{ marginTop: 1 }}> - <SelectUI title={'Язык'} value={lang} onChange={(e) => setLang(e.target.value)} list={Object.keys(languages)} /> + <SelectUI + title={'Язык'} + value={lang} + onChange={(e) => setLang(e.target.value)} + list={Object.keys(languages)} + /> </Box> <Box sx={{ marginTop: 1 }}> <SelectUI @@ -208,10 +243,18 @@ const Create: React.FC<IDalleProps> = ({ device, token, favorites }) => { </Stack> <Stack spacing={0.5} sx={{ marginTop: 1 }}> <Typography className='title-main-gray'>Ресурсы (ссылки, через запятую)</Typography> - <Input value={resource_urls} onChange={(e) => setResourceUrls(e.target.value.split(','))} /> + <Input + value={resource_urls} + onChange={(e) => setResourceUrls(e.target.value.split(','))} + /> </Stack> <Stack sx={{ marginTop: 1 }}> - <Typography onClick={clearSetting} className='text' color='#FF2372 !important' sx={{ cursor: 'pointer' }}> + <Typography + onClick={clearSetting} + className='text' + color='#FF2372 !important' + sx={{ cursor: 'pointer' }} + > Сбросить настройки </Typography> </Stack> @@ -4,12 +4,12 @@ import { Typography } from '@mui/material' import Box from '@mui/material/Box' import { getSession, useSession } from 'next-auth/react' -import { loadTemplates } from '@/src/features/use-copy/copy-slice' -import { Layout } from '@/src/main/layout' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { api } from '@/src/shared/api/endpoints' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { IDalleProps } from '@/src/shared/lib/types/types-dalle' +import { loadTemplates } from '#/features/use-copy/copy-slice' +import { Layout } from '#/app/layout' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { api } from '#/shared/api/endpoints' +import { getTypeDevice } from '#/shared/lib/helpers' +import { IDalleProps } from '#/shared/lib/types/types-dalle' import Card from './card' @@ -54,7 +54,11 @@ const CopyPage: React.FC<IDalleProps> = ({ device, token, favorites }) => { <Box display={'flex'} flexDirection={desktop ? 'row' : 'column-reverse'} - sx={{ marginBottom: desktop ? 0 : 3, width: desktop ? '98%' : '100%', marginTop: desktop ? 3 : 0 }} + sx={{ + marginBottom: desktop ? 0 : 3, + width: desktop ? '98%' : '100%', + marginTop: desktop ? 3 : 0, + }} > {templates?.map((el) => { return ( @@ -1,552 +1,6 @@ -import * as React from 'react' -import { useRef } from 'react' -import { useDispatch } from 'react-redux' -import { Collapse, Typography } from '@mui/material' -import Box from '@mui/material/Box' -import Stack from '@mui/material/Stack' -import { useRouter } from 'next/router' -import { useSession } from 'next-auth/react' -import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { ImageModelPage } from '#/views/images' +import { getDefaultLayout } from '#/widgets/layouts' -import { Setting } from '@/src/domains/images-bots/babes/types' -import BotParamsMap from '@/src/features/bot-params/bot-params-map' -import Title from '@/src/features/title/title' -import { ChatSelect } from '@/src/main/components/chat_select' -import { ResetFilters } from '@/src/main/components/filters/reset_filters' -import { UniqInput } from '@/src/main/components/uniq_input' -import { Layout } from '@/src/main/layout' -import { setParams as setParametres } from '@/src/main/store/model-parametres-store' -import { useAppSelector } from '@/src/main/store/store' -import { DrawerCustom, Error } from '@/src/shared' -import model_api from '@/src/shared/api/models/api' -import { useModelImages } from '@/src/shared/api/models/endpoints' -import { IModel } from '@/src/shared/api/models/models' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { getDeviceOs } from '@/src/shared/lib/helpers/get-type-device' -import { useShowData } from '@/src/shared/lib/hooks' -import { ArrowDownScroll } from '@/src/shared/ui/icon-components/scroll-down-arrow' -import { ImageMessagesList } from '@/src/widgets/messages/image-messages-list' +ImageModelPage.getLayout = getDefaultLayout() -export async function getServerSideProps(context: any): Promise<{ props: any }> { - const deviceType = getTypeDevice(context) - const deviceOs = getDeviceOs(context) - - return { - props: { - ...(await serverSideTranslations(context.locale, ['common'])), - deviceType, - deviceOs, - }, - } -} - -const Images: React.FC<any> = ({ deviceType, deviceOs }) => { - const [image, setImage] = React.useState<File | null>(null) - const desktop = deviceType === 'desktop' - const ios = deviceOs === 'ios' - const { error, showError } = useShowData() - const [openFiltersMobile, setOpenFiltersMobile] = React.useState<boolean>(false) - const router = useRouter() - const { data } = useSession() - const [botParams, setBotParams] = React.useState<IModel | null>(null) - const [version, setVersion] = React.useState<string>('') - const [modelType, setModelType] = React.useState<string>('') - const [params, setParams] = React.useState<boolean>(false) - const includeParams = useAppSelector((state) => state.params.params) - const dispatch = useDispatch() - - const { messages, loading, createImage, isComplete, getMessagesPagination } = useModelImages<Setting>(showError, modelType, deviceType) - - const [chatScrollHeight, setChatScrollHeight] = React.useState(0) - const [scrollBottom, setScrollBottom] = React.useState(0) - const refScrollMobile = useRef<any>() - const [isPaginating, setIsPaginating] = React.useState(false) - - React.useEffect(() => { - model_api - .getBotParams(router.asPath.split('/')[2], data?.access) - .then((res) => { - setBotParams(res) - setModelType(res.slug) - if (res.versions.length !== 0) { - setVersion(res.versions[0].slug) - dispatch( - setParametres( - res.parameters.reduce( - (a, v) => - v.versions.includes(res.versions[0].slug) - ? { ...a, [v.key]: v.values.default } - : { ...a }, - {} - ) - ) - ) - } else { - setVersion('') - dispatch( - setParametres( - res.parameters.reduce( - (a, v) => ({ ...a, [v.key]: v.values.default }), - {} - ) - ) - ) - } - }) - .catch((err) => {}) - }, [data?.access, router.query]) - - const onLoadImage = (event: React.ChangeEvent<HTMLInputElement>) => { - if (event.target.files) { - setImage(event.target.files[0]) - // showError('Файл успешно загружен, можете отправлять его!') - } - } - - const viewMobileSettings = () => { - setOpenFiltersMobile(true) - } - - const hideMobileSettings = () => { - setOpenFiltersMobile(false) - } - - const resetParams = () => { - if (botParams) { - dispatch(setParametres({})) - if (botParams.versions.length !== 0) { - setVersion(botParams.versions[0].slug) - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => - v.versions.includes(botParams.versions[0].slug) - ? { ...a, [v.key]: v.values.default } - : { ...a }, - {} - ) - ) - ) - } else { - setVersion(botParams.slug) - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => ({ ...a, [v.key]: v.values.default }), - {} - ) - ) - ) - } - } - } - - const setDefaultParams = () => { - if (botParams) { - dispatch(setParametres({})) - if (version !== '') { - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => - v.versions.includes(version) - ? { ...a, [v.key]: v.values.default } - : { ...a }, - {} - ) - ) - ) - } else { - dispatch( - setParametres( - botParams.parameters.reduce( - (a, v) => ({ ...a, [v.key]: v.values.default }), - {} - ) - ) - ) - } - } - } - - const onCreateImage = (input: string, required: (string | null)[]) => { - if (required.includes('text') && (input === '' || input === null)) { - showError('Введите сообщение!') - return false - } - if (required.includes('image') && image === null) { - showError('Прикрепите изображение!') - return false - } - if (required.includes('zip') && image === null) { - showError('Прикрепите архив!') - return false - } - let data = {} - if (version === '') { - data = { - ...includeParams, - } - } else { - data = { - version: version, - ...includeParams, - } - } - - createImage({ - content: input, - file: image, - info: { - ...data, - }, - }) - return true - } - - React.useEffect(() => { - if (isComplete) { - if (!desktop) { - const block = refScrollMobile.current - if (block) { - //@ts-ignore - block.scrollTop = block.scrollHeight - } - } else { - window.scroll(0, 0) - } - } - }, [isComplete]) - - const handleMobileScroll = () => { - setScrollBottom(refScrollMobile.current?.scrollHeight - refScrollMobile.current?.scrollTop - refScrollMobile.current?.clientHeight) - - if (refScrollMobile.current && messages?.length !== 0) { - const { scrollTop, scrollHeight, clientHeight } = refScrollMobile.current - if (scrollTop === 0) { - if (getMessagesPagination) { - setIsPaginating(true) - getMessagesPagination(deviceType) - } - } - } - } - - const handleScroll = () => { - if (window.scrollY + window.innerHeight >= document.documentElement.scrollHeight) { - setIsPaginating(true) - getMessagesPagination(deviceType) - } - } - - React.useEffect(() => { - window.addEventListener('scroll', handleScroll) - return () => { - window.removeEventListener('scroll', handleScroll) - } - }, []) - - React.useEffect(() => { - const block = deviceType === 'desktop' ? window : refScrollMobile.current - if (block) { - if (messages != undefined && !isPaginating) { - setChatScrollHeight(block.scrollHeight) - const time = setTimeout(() => { - //@ts-ignore - block.scrollTo({ - top: block.scrollHeight, - behavior: 'smooth', // добавляем плавную прокрутку - }) - }, 350) - return () => clearTimeout(time) - } else if (messages != undefined && isPaginating) { - //@ts-ignore - block.scrollTop = block.scrollHeight - chatScrollHeight - - setChatScrollHeight(block.scrollHeight) - } - } - setIsPaginating(false) - }, [messages]) - - return ( - <Layout titlePage={botParams?.title ? botParams?.title : ''} device={deviceType}> - <Title title={botParams?.title} type={'Изображения'} linkBack={'/images'} /> - <Box - display={'flex'} - justifyContent='space-between' - alignItems='start' - flexDirection={desktop ? 'row' : 'column-reverse'} - sx={{ - marginBottom: desktop ? 0 : 3, - width: desktop ? '97%' : '100%', - marginTop: desktop ? 3 : '15px', - }} - > - <Box - sx={{ - width: desktop ? '73%' : '100%', - marginLeft: 0, - display: 'flex', - flexDirection: desktop ? 'column' : 'column-reverse', - }} - > - {desktop ? ( - <> - <Stack alignItems='center' sx={{ marginBottom: '15px' }}> - {botParams && ( - <UniqInput - currentVersion={version} - styles={'images'} - input_types={botParams.inputs} - image={image} - value={prompt} - desktop={desktop} - loading={loading} - imageLoad={onLoadImage} - sendMessage={onCreateImage} - unpinImage={() => setImage(null)} - viewMobileSettings={viewMobileSettings} - /> - )} - </Stack> - <Box - className={'bg-color-block border-radius-main'} - sx={{ padding: '30px' }} - > - <ImageMessagesList - isComplete={isComplete} - device={deviceType} - images={messages} - /> - </Box> - </> - ) : ( - <Box className={'bg-color-block border-radius-main'}> - {scrollBottom > 500 && ( - <Box - sx={{ - position: 'absolute', - left: 0, - right: 0, - width: 'fit-content', - cursor: 'pointer', - margin: '0 auto', - bottom: '100px', - zIndex: 10, - }} - onClick={() => { - const block = refScrollMobile.current - - block.scrollTo({ - top: block.scrollHeight, - behavior: 'smooth', // добавляем плавную прокрутку - }) - }} - > - <ArrowDownScroll /> - </Box> - )} - <Box - ref={refScrollMobile} - sx={{ - padding: '30px', - height: ios - ? 'calc(200px + (400 - 200) * ((100vh - 400px) / (600 - 400)) - 100px)' - : 'calc(200px + (400 - 200) * ((100vh - 400px) / (600 - 400)))', - overflowY: 'scroll', - overflowX: 'hidden', - }} - className={'smallScroll'} - onScroll={handleMobileScroll} - > - <ImageMessagesList - isComplete={isComplete} - device={deviceType} - images={messages} - /> - </Box> - <Stack alignItems='center'> - {botParams && ( - <UniqInput - currentVersion={version} - styles={'images'} - input_types={botParams.inputs} - image={image} - value={prompt} - desktop={desktop} - loading={loading} - imageLoad={onLoadImage} - sendMessage={onCreateImage} - unpinImage={() => setImage(null)} - viewMobileSettings={viewMobileSettings} - /> - )} - </Stack> - </Box> - )} - </Box> - {desktop && ( - <Stack - spacing={2} - className='pd-30 bg-color-block border-radius-main' - sx={{ width: '25.5%', height: 'auto' }} - > - {botParams?.versions && botParams.versions.length !== 0 ? ( - <> - <Typography - sx={{ - color: '#A4AAB5', - fontWeight: '600', - fontSize: '14px', - letterSpacing: '0.1px', - }} - > - ВЕРСИИ - </Typography> - <ChatSelect - setDefaultParams={setDefaultParams} - value={version} - list={botParams.versions} - setValue={setVersion} - /> - </> - ) : ( - <></> - )} - {botParams && botParams.parameters?.length > 0 && ( - <Box - display={'flex'} - alignItems={'center'} - gap={'5px'} - sx={{ cursor: 'pointer' }} - onClick={() => { - setParams(!params) - }} - > - <Typography - sx={{ - color: '#A4AAB5', - fontWeight: '600', - fontSize: '14px', - letterSpacing: '0.1px', - }} - > - ПАРАМЕТРЫ - </Typography> - <svg - width='14' - height='20' - viewBox='0 0 21 13' - fill='none' - xmlns='http://www.w3.org/2000/svg' - className={`${params ? 'rotate-180' : 'rotate-0'}`} - > - <path - fillRule='evenodd' - clipRule='evenodd' - d='M0.614851 0.615358C1.00866 0.221668 1.54271 0.000505666 2.09955 0.000505642C2.6564 0.000505617 3.19044 0.221668 3.58425 0.615357L10.4996 7.53066L17.4149 0.615357C17.8109 0.232825 18.3414 0.0211567 18.892 0.0259414C19.4426 0.0307261 19.9693 0.25158 20.3587 0.640937C20.748 1.03029 20.9689 1.557 20.9737 2.10761C20.9784 2.65823 20.7668 3.18869 20.3842 3.58476L11.9843 11.9848C11.5904 12.3784 11.0564 12.5996 10.4996 12.5996C9.94271 12.5996 9.40866 12.3784 9.01485 11.9848L0.614851 3.58476C0.221162 3.19095 -4.3461e-07 2.6569 -4.5895e-07 2.10006C-4.8329e-07 1.54321 0.221162 1.00917 0.614851 0.615358Z' - fill='#7f7df3' - /> - </svg> - </Box> - )} - - {botParams && botParams.parameters?.length > 0 ? ( - <Collapse - in={params} - orientation='vertical' - collapsedSize={0} - sx={{}} - > - <BotParamsMap - currentVersion={version} - params={botParams?.parameters} - /> - <ResetFilters - desktop={desktop} - closeDrawer={hideMobileSettings} - reset={resetParams} - /> - </Collapse> - ) : ( - <Typography - sx={{ - color: '#6e6e6e', - fontSize: '15px', - fontWeight: '500', - }} - > - Параметры отсутствуют - </Typography> - )} - </Stack> - )} - <DrawerCustom open={openFiltersMobile} onClose={hideMobileSettings}> - <Stack spacing={1} padding={2.4}> - {botParams?.versions && botParams.versions.length !== 0 ? ( - <> - <Typography - sx={{ - color: '#A4AAB5', - fontWeight: '600', - fontSize: '14px', - letterSpacing: '0.1px', - margin: '20px 0px 0px !important', - }} - > - ВЕРСИИ - </Typography> - <ChatSelect - setDefaultParams={setDefaultParams} - value={version} - list={botParams.versions} - setValue={setVersion} - /> - </> - ) : ( - <></> - )} - {botParams && botParams.parameters?.length > 0 ? ( - <> - <Typography - sx={{ - color: '#A4AAB5', - fontWeight: '600', - fontSize: '14px', - letterSpacing: '0.1px', - margin: '20px 0px 0px !important', - }} - > - ПАРАМЕТРЫ - </Typography> - <BotParamsMap - currentVersion={version} - params={botParams?.parameters} - /> - <ResetFilters - closeDrawer={hideMobileSettings} - desktop={desktop} - reset={resetParams} - /> - </> - ) : ( - <Typography - sx={{ - color: '#6e6e6e', - fontSize: '15px', - fontWeight: '500', - }} - > - Параметры отсутствуют - </Typography> - )} - </Stack> - </DrawerCustom> - <Error error={error} open={Boolean(error)} /> - </Box> - </Layout> - ) -} - -export default Images +export default ImageModelPage @@ -1,76 +1,6 @@ -import * as React from 'react' -import { useEffect, useState } from 'react' -import { Box, Typography } from '@mui/material' -import CircularProgress from '@mui/material/CircularProgress' -import { useSession } from 'next-auth/react' -import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { ImageModelsPage } from "#/views/images"; +import { getDefaultLayout } from "#/widgets/layouts"; -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import model_api from '@/src/shared/api/models/api' -import { IShortModel } from '@/src/shared/api/models/models' -import { Card } from '@/src/shared/card/card' -import { getTypeDevice } from '@/src/shared/lib/helpers' +ImageModelsPage.getLayout = getDefaultLayout({ titlePage: 'Изображения' }) -export async function getServerSideProps(context: any): Promise<{ props: any }> { - const deviceType = getTypeDevice(context) - - return { - props: { - ...(await serverSideTranslations(context.locale, ['common'])), - deviceType, - }, - } -} - -export const Images: React.FC<any> = ({ deviceType }) => { - const [bots, setBots] = useState<IShortModel[] | null>(null) - const user = useAppSelector((state) => state.user) - const { data } = useSession() - - useEffect(() => { - if (!data?.access) { - return - } - - model_api.getImages(data?.access).then((res) => setBots(res)) - }, [data?.access]) - - return ( - <Layout titlePage={'Изображения'} device={deviceType}> - <Typography sx={{ fontSize: 24, fontWeight: 'bold', marginTop: '25px' }}>Изображения</Typography> - <Box display='flex' flexWrap='wrap'> - {bots ? ( - bots.map((item, idx) => { - return ( - <Card - accessed_models={user.payment_plan.plan.accessed_models} - uid={item.uid} - key={idx} - slug={item.slug} - text={item.description} - title={item.title} - icon={item.image} - /> - ) - }) - ) : ( - <CircularProgress - size={50} - thickness={3} - sx={{ - color: '#7F7DF3', - position: 'absolute', - top: '40%', - left: 0, - right: 0, - margin: '0 auto', - }} - /> - )} - </Box> - </Layout> - ) -} - -export default Images +export default ImageModelsPage \ No newline at end of file @@ -4,7 +4,7 @@ import { Box } from '@mui/material' import CircularProgress from '@mui/material/CircularProgress' import { useRouter } from 'next/router' -import { addReferral } from '@/src/entities/user-account/model/user-type-slice' +import { addReferral } from '#/entities/user-account/model/user-type-slice' export async function getServerSideProps({ params }: { params: Promise<{ email: string }> }) { const email = (await params).email @@ -3,8 +3,8 @@ import { Box, Typography } from '@mui/material' import Link from 'next/link' import { useRouter } from 'next/router' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' +import { Layout } from '#/app/layout' +import { useAppSelector } from '#/app/store/store' const Custom404 = () => { const theme = useAppSelector((state) => state.theme.theme) @@ -1,4 +1,4 @@ -import React from 'react' +import React, { ReactElement, ReactNode } from 'react' import { Provider } from 'react-redux' import { StyledEngineProvider } from '@mui/material' import axios from 'axios' @@ -8,12 +8,13 @@ import { Raleway } from 'next/font/google' import { SessionProvider } from 'next-auth/react' import { appWithTranslation } from 'next-i18next' -import { store } from '@/src/main/store/store' +import { store } from '#/app/store/store' import ErrorBoundary from './error-boundary' -import '@/src/main/styles/globals.css' -import '@/src/main/styles/styles-pages/system.scss' +import '#/app/styles/globals.css' +import '#/app/styles/styles-pages/system.scss' +import { NextPage } from 'next' axios.defaults.httpsAgent = new https.Agent({ rejectUnauthorized: false, @@ -21,7 +22,17 @@ axios.defaults.httpsAgent = new https.Agent({ const inter = Raleway({ subsets: ['latin'] }) -function App({ Component, pageProps: { session, ...pageProps } }: AppProps) { +export type NextPageWithLayout<P = {}, DP = {}, IP = P> = NextPage<P, IP> & { + getLayout?: (page: ReactElement) => ReactNode + dynamicProps?: DP +} + +export interface AppPropsWithLayout extends AppProps { + Component: NextPageWithLayout +} + +function App({ Component, pageProps: { session, ...pageProps } }: AppPropsWithLayout) { + const getLayout = Component.getLayout ?? ((page) => page) return ( <> @@ -35,7 +46,7 @@ function App({ Component, pageProps: { session, ...pageProps } }: AppProps) { <StyledEngineProvider injectFirst> <Provider store={store}> <SessionProvider session={session} refetchInterval={5 * 60}> - <Component {...pageProps} /> + {getLayout(<Component {...pageProps} />)} </SessionProvider> </Provider> </StyledEngineProvider> @@ -27,7 +27,11 @@ export default function Document() { <body> <noscript> <div> - <img src='https://mc.yandex.ru/watch/93475901' style={{ position: 'absolute', left: '-9999px' }} alt='' /> + <img + src='https://mc.yandex.ru/watch/93475901' + style={{ position: 'absolute', left: '-9999px' }} + alt='' + /> </div> </noscript> <Main /> @@ -8,22 +8,22 @@ import { useRouter } from 'next/router' import { getServerSession } from 'next-auth' import { signOut, useSession } from 'next-auth/react' -import { getUserBalance } from '@/src/entities/balance' -import { getAllInfo, unfollowEmail } from '@/src/entities/user-account/model/user-type-slice' -import { Layout } from '@/src/main/layout' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import styles2 from '@/src/main/styles/accountTabs.module.css' -import styles from '@/src/main/styles/business.module.scss' -import { ButtonUI, Input, Loader, Modal, Success, SwitchCustom, useShowData } from '@/src/shared' -import { accountApi } from '@/src/shared/api/account-endpoints' -import { API_URL } from '@/src/shared/lib/constants/constants' -import { getAccessToken, getTypeDevice } from '@/src/shared/lib/helpers' -import { IProps } from '@/src/shared/lib/types/entities' -import { ScreenForInactive } from '@/src/widgets/business' -import BusinessHost from '@/src/widgets/business-host/business-host' -import { Info } from '@/src/widgets/business-info' -import { Subscription } from '@/src/widgets/payment/model/payment' -import { Referral } from '@/src/widgets/referral' +import { getUserBalance } from '#/entities/balance' +import { getAllInfo, unfollowEmail } from '#/entities/user-account/model/user-type-slice' +import { Layout } from '#/app/layout' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import styles2 from '#/app/styles/accountTabs.module.css' +import styles from '#/app/styles/business.module.scss' +import { ButtonUI, Input, Loader, Modal, Success, SwitchCustom, useShowData } from '#/shared' +import { accountApi } from '#/shared/api/account-endpoints' +import { API_URL } from '#/shared/lib/constants/constants' +import { getAccessToken, getTypeDevice } from '#/shared/lib/helpers' +import { IProps } from '#/shared/lib/types/entities' +import { ScreenForInactive } from '#/widgets/business' +import BusinessHost from '#/widgets/business-host/business-host' +import { Info } from '#/widgets/business-info' +import { Subscription } from '#/widgets/payment/model/payment' +import { Referral } from '#/widgets/referral' import { DownloadModal } from '../features/business-security-download/ui/download-modal' @@ -201,7 +201,11 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { ) } if (type === 'business_account') { - return <Box className={styles.main}>Информация о корп.аккаунте доступна только владельцу и администраторам.</Box> + return ( + <Box className={styles.main}> + Информация о корп.аккаунте доступна только владельцу и администраторам. + </Box> + ) } if (type === 'business_host') { return <BusinessHost /> @@ -354,7 +358,11 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { disableRipple onClick={() => changeScope(el.scope)} key={el.title} - className={scope === el.scope ? styles2.wrap_toggle_button_active : styles2.wrap_toggle_button} + className={ + scope === el.scope + ? styles2.wrap_toggle_button_active + : styles2.wrap_toggle_button + } value={el.scope} label={el.title} /> @@ -367,7 +375,13 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { <Typography className='title-block'>Основные</Typography> <Box className={styles2.main_block}> <Box sx={{ marginRight: 2 }}> - <Box onClick={handleDivClick} sx={{ cursor: 'pointer', position: 'relative' }}> + <Box + onClick={handleDivClick} + sx={{ + cursor: 'pointer', + position: 'relative', + }} + > <input type='file' ref={fileInputRef} @@ -384,7 +398,12 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { }} > {!loading || !(userInfoLoaded === 'succeeded') ? ( - <Image src={'/Edit.svg'} height={30} width={30} alt={'Image change'} /> + <Image + src={'/Edit.svg'} + height={30} + width={30} + alt={'Image change'} + /> ) : ( <Loader /> )} @@ -412,7 +431,9 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { /> </Box> <Box sx={{ marginTop: '15px' }}> - <Typography className={styles2.input_title}>Фамилия</Typography> + <Typography className={styles2.input_title}> + Фамилия + </Typography> <Input inputProps={{ maxLength: 15 }} value={lastName} @@ -421,9 +442,19 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { /> </Box> <Box sx={{ marginTop: '15px' }}> - <Box display='flex' alignItems='center' justifyContent='space-between'> - <Typography className={styles2.input_title}>Email</Typography> - <Box display='flex' alignItems='center' sx={{ marginBottom: '5px' }}> + <Box + display='flex' + alignItems='center' + justifyContent='space-between' + > + <Typography className={styles2.input_title}> + Email + </Typography> + <Box + display='flex' + alignItems='center' + sx={{ marginBottom: '5px' }} + > <SwitchCustom checked={is_subscribed_to_emails} onChange={async () => { @@ -445,10 +476,16 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { <Input value={email} fullWidth /> </Box> <Box sx={{ marginTop: '15px' }}> - <Typography className={styles2.input_title}>Никнейм</Typography> + <Typography className={styles2.input_title}> + Никнейм + </Typography> <Input value={username} fullWidth /> </Box> - <ButtonUI onClick={changeUserData} sx={{ marginTop: '20px' }} text={'Сохранить'} /> + <ButtonUI + onClick={changeUserData} + sx={{ marginTop: '20px' }} + text={'Сохранить'} + /> {/*referral_code.code.trim() && ( <Box marginTop={1} display='flex'> <Typography className='text'>Ваша реферальная ссылка:</Typography> @@ -484,7 +521,9 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { <Typography className='title-block'>Изменить пароль</Typography> <Box className={styles2.main_block} flexDirection='column'> <Box sx={{ width: '100%' }}> - <Typography className={styles2.input_title}>Текущий пароль</Typography> + <Typography className={styles2.input_title}> + Текущий пароль + </Typography> <Input placeholder='Укажите текущий пароль' value={currentPassword} @@ -492,9 +531,18 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { fullWidth /> </Box> - <Box display='flex' justifyContent='space-between' sx={{ width: '100%', marginTop: '15px' }}> + <Box + display='flex' + justifyContent='space-between' + sx={{ + width: '100%', + marginTop: '15px', + }} + > <Box sx={{ width: '48%' }}> - <Typography className={styles2.input_title}>Новый пароль</Typography> + <Typography className={styles2.input_title}> + Новый пароль + </Typography> <Input placeholder='Укажите новый пароль' value={newPassword1} @@ -503,7 +551,9 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { /> </Box> <Box sx={{ width: '48%' }}> - <Typography className={styles2.input_title}>Подтвердить пароль</Typography> + <Typography className={styles2.input_title}> + Подтвердить пароль + </Typography> <Input placeholder='Укажите новый пароль ещё раз' value={newPassword2} @@ -523,7 +573,9 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { )} <Stack spacing={2} className={styles2.wrap_block} sx={{ marginBottom: 5 }}> <Typography className='title-block'>Удаление аккаунта</Typography> - <Typography className='text'>Удаление аккаунта приведет к потере всех настроек</Typography> + <Typography className='text'> + Удаление аккаунта приведет к потере всех настроек + </Typography> <ButtonUI fullWidth onClick={() => setConfirmDeleteModal(true)} @@ -540,8 +592,15 @@ const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { <Modal open={confirmDeleteModal} onClose={() => setConfirmDeleteModal(false)}> <Stack spacing={2}> <Typography className='title-struct'>Удаление аккаунта</Typography> - <Typography className='text'>Вы действительно хотите удалить ваш аккаунт?</Typography> - <Button onClick={deleteAccount} fullWidth sx={{ marginTop: 3 }} className='btn-danger'> + <Typography className='text'> + Вы действительно хотите удалить ваш аккаунт? + </Typography> + <Button + onClick={deleteAccount} + fullWidth + sx={{ marginTop: 3 }} + className='btn-danger' + > Удалить </Button> </Stack> @@ -2,11 +2,11 @@ import React from 'react' import { Stack } from '@mui/material' import { getSession } from 'next-auth/react' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import { getAccessToken, getTypeDevice } from '@/src/shared/lib/helpers' -import { IProps } from '@/src/shared/lib/types/entities' -import { StatsField } from '@/src/widgets/stats-field' +import { Layout } from '#/app/layout' +import { useAppSelector } from '#/app/store/store' +import { getAccessToken, getTypeDevice } from '#/shared/lib/helpers' +import { IProps } from '#/shared/lib/types/entities' +import { StatsField } from '#/widgets/stats-field' export async function getServerSideProps(context: any): Promise<{ props: any }> { const device = getTypeDevice(context) @@ -39,7 +39,12 @@ const Admin: React.FC<IProps> = ({ token, device }) => { return ( <Layout titlePage={'Статистика'} device={device}> - <Stack direction='column' justifyContent='flex-start' alignItems='center' sx={{ width: '80vw', margin: '40px auto' }}> + <Stack + direction='column' + justifyContent='flex-start' + alignItems='center' + sx={{ width: '80vw', margin: '40px auto' }} + > <StatsField device={device} token={token} theme={theme} /> </Stack> </Layout> @@ -7,15 +7,15 @@ import Image from 'next/image' import Link from 'next/link' import { useSession } from 'next-auth/react' -import { getAll, ResponseAllInfo } from '@/src/entities/user-account/model/user-type-slice' -import { ApiKeyModal } from '@/src/features/api-key-modal/api-key-modal' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import { TooltipCustom } from '@/src/shared' -import { accountApi } from '@/src/shared/api/account-endpoints' -import { API_URL } from '@/src/shared/lib/constants' -import styles from '@/src/shared/styles/api-keys.module.scss' -import { InputStyleSmallDark, InputStyleSmallLight } from '@/src/shared/ui/input' +import { getAll, ResponseAllInfo } from '#/entities/user-account/model/user-type-slice' +import { ApiKeyModal } from '#/features/api-key-modal/api-key-modal' +import { Layout } from '#/app/layout' +import { useAppSelector } from '#/app/store/store' +import { TooltipCustom } from '#/shared' +import { accountApi } from '#/shared/api/account-endpoints' +import { API_URL } from '#/shared/lib/constants' +import styles from '#/shared/styles/api-keys.module.scss' +import { InputStyleSmallDark, InputStyleSmallLight } from '#/shared/ui/input' const ApiKeys: React.FC = () => { const [keys, setKeys] = useState<Array<any> | null>(null) @@ -78,15 +78,27 @@ const ApiKeys: React.FC = () => { <Stack spacing={2} className={styles.wrap}> <Stack spacing={2} className='pd-30 bg-color-block border-radius-main'> <Typography className='text'> - API-ключ — это инструмент, который идентифицирует пользователя или программу, запрашивающих доступ к API платформы. - С помощью ключа можно отслеживать, кто и когда пользуется API, рассчитывать оплату. + API-ключ — это инструмент, который идентифицирует пользователя или программу, + запрашивающих доступ к API платформы. С помощью ключа можно отслеживать, кто и когда + пользуется API, рассчитывать оплату. </Typography> <Box display='flex' alignItems='center'> - <Button onClick={() => setModal(true)} disabled={disabled} className='btn-classic' sx={{ width: '130px' }}> + <Button + onClick={() => setModal(true)} + disabled={disabled} + className='btn-classic' + sx={{ width: '130px' }} + > Создать ключ </Button> <Link href='https://air-docs.readthedocs.io/' target='_blank'> - <Typography className='text' sx={{ marginLeft: '15px', color: '#8280FF !important' }}> + <Typography + className='text' + sx={{ + marginLeft: '15px', + color: '#8280FF !important', + }} + > Документация </Typography> </Link> @@ -98,7 +110,13 @@ const ApiKeys: React.FC = () => { <Typography className='title-block'>Мои ключи</Typography> <Table sx={{ minWidth: 650, overflowX: 'scroll' }} aria-label='simple table'> <TableHead> - <TableRow sx={{ '&:last-child td, &:last-child th ': { border: 0 } }}> + <TableRow + sx={{ + '&:last-child td, &:last-child th ': { + border: 0, + }, + }} + > <TableCell align='left'>Имя</TableCell> <TableCell align='left'>Ключ</TableCell> <TableCell align='left'>Лимит токенов</TableCell> @@ -125,7 +143,13 @@ const ApiKeys: React.FC = () => { </Table> </Stack> ) : ( - <Box height={140} width={'100%'} display='flex' alignItems='center' justifyContent='center'> + <Box + height={140} + width={'100%'} + display='flex' + alignItems='center' + justifyContent='center' + > <Box textAlign='center'> <Typography className='title-vspomogatel'>У вас пока нет ключей 😞 </Typography> <Typography className='title-main-gray'>Создайте первый ключ</Typography> @@ -4,10 +4,10 @@ import axios from 'axios' import { useRouter } from 'next/router' import process from 'process' -import { Layout } from '@/src/main/layout' -import { API_URL } from '@/src/shared/lib/constants' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { Device } from '@/src/shared/lib/types/entities' +import { Layout } from '#/app/layout' +import { API_URL } from '#/shared/lib/constants' +import { getTypeDevice } from '#/shared/lib/helpers' +import { Device } from '#/shared/lib/types/entities' const client_secret = process.env.NEXT_PUBLIC_DJANGO_GOOGLE_APP_CLIENT_SECRET const client_id = process.env.NEXT_PUBLIC_DJANGO_GOOGLE_APP_CLIENT_ID @@ -7,12 +7,12 @@ import Image from 'next/image' import Link from 'next/link' import { useRouter } from 'next/router' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import { getRandomImage } from '@/src/pages/login' -import { API_URL } from '@/src/shared/lib/constants/constants' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { TDeviceProp } from '@/src/shared/lib/types/entities' +import { Layout } from '#/app/layout' +import { useAppSelector } from '#/app/store/store' +import { getRandomImage } from '#/pages/login' +import { API_URL } from '#/shared/lib/constants/constants' +import { getTypeDevice } from '#/shared/lib/helpers' +import { TDeviceProp } from '#/shared/lib/types/entities' export async function getServerSideProps(context: any): Promise<{ props: TDeviceProp }> { const device = getTypeDevice(context) @@ -90,7 +90,10 @@ const ChangePassword: React.FC<TDeviceProp> = ({ device }) => { </Link> {desktop && ( <Card> - <CardMedia sx={{ height: '100vh', width: '600px' }} image={`/login_images/${refImage.current}`}> + <CardMedia + sx={{ height: '100vh', width: '600px' }} + image={`/login_images/${refImage.current}`} + > <Stack direction='column-reverse' justifyContent='flex-start' @@ -4,8 +4,8 @@ import axios from 'axios' import Link from 'next/link' import { useRouter } from 'next/router' -import { Layout } from '@/src/main/layout' -import { API_URL } from '@/src/shared/lib/constants/constants' +import { Layout } from '#/app/layout' +import { API_URL } from '#/shared/lib/constants/constants' export async function getServerSideProps(context: any): Promise<{ props: any }> { const UA = context.req.headers['user-agent'] @@ -1,88 +1,7 @@ -import * as React from 'react' -import { useMemo } from 'react' -import { useCookies } from 'react-cookie' -import { Box, Stack, Typography } from '@mui/material' -import { serialize } from 'cookie' -import { GetServerSidePropsContext } from 'next' -import { useRouter } from 'next/router' -import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { MainPage } from '#/views/index' +import { getDefaultLayout } from '#/widgets/layouts' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import styles from '@/src/main/styles/styles-pages/index-styles.module.scss' -import { ReferralBlock } from '@/src/shared' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { getCurrentTimeAndGreeting } from '@/src/shared/lib/helpers/getCurrentDateForMainScreen' -import { TelegramBlock } from '@/src/shared/ui/telegram-block' -import { DemoInfo } from '../shared/ui/demo-info' -import { SocialMedia } from '../shared/ui/social-media-block' -import { Statistics } from '../widgets/stats/stats' +MainPage.getLayout = getDefaultLayout({ titlePage: 'AIR' }) -export async function getServerSideProps(context: GetServerSidePropsContext): Promise<{ props: any }> { - const deviceType = getTypeDevice(context) - - const { res, query } = context - - const allCookies = Object.entries(query).map((key, value) => serialize(key.toString(), value.toString())) - - res.setHeader('Set-Cookie', allCookies) - res.setHeader('Set-Cookie', '111') - return { - props: { - ...(await serverSideTranslations((context as any).locale, ['common'])), - deviceType, - }, - } -} - -export const Main: React.FC<any> = ({ deviceType }) => { - const { first_name, account_type } = useAppSelector((state) => state.user) - - const [, setCookie] = useCookies() - - const { query, push } = useRouter() - - React.useEffect(() => { - Object.entries(query).forEach(([key, value]) => setCookie(key, value)) - }, []) - - const [greeting, time] = useMemo(() => getCurrentTimeAndGreeting(first_name), [first_name]) - - return ( - <Layout titlePage={'AIR'} device={deviceType}> - <Box width='100%'> - <Box className={styles.wrap}> - <Typography className='title-main-gray'>{time}</Typography> - <Typography sx={{ fontSize: deviceType === 'desktop' ? '28px' : '24px', fontWeight: 600 }}>{greeting}</Typography> - <Box className={styles.main}> - <Statistics deviceType={deviceType} /> - <Stack className={deviceType === 'desktop' ? styles.side_desk : styles.side_mobile}> - {/*<TelegramBlock />*/} - <DemoInfo /> - {account_type === 'regular' && <ReferralBlock />} - <SocialMedia /> - <Stack className='pd-30 bg-color-block border-radius-main mt-15' spacing={2}> - <Typography className='title-block'>Полезная статья</Typography> - <Box> - <Typography className='title-vspomogatel' sx={{ fontSize: '21px !important' }}> - {' '} - Что такое токен ? - </Typography> - <Typography className='text' sx={{ marginTop: '10px' }}> - Токен — это цифровая валюта для оплаты ИИ-продуктов и услуг. Юзеры используют эту валюту, чтобы - получить либо доступ к нейросетям, либо результат генерации — текст, картинку и т.д. Еще токенами - называют единицы текста, на которых обучается нейросеть. Например, в предложении «‎Я люблю шоколад» - слова «‎Я», «‎люблю» и «шоколад» могут быть токенами. - </Typography> - </Box> - </Stack> - </Stack> - </Box> - </Box> - </Box> - </Layout> - ) -} - -export default Main +export default MainPage @@ -11,16 +11,16 @@ import Router from 'next/router' import Script from 'next/script' import { signIn } from 'next-auth/react' -import { authTelegram } from '@/src/features/auth-telegram' -import { PasswordOptions } from '@/src/features/register-by-email/lib/constants' -import { IEmailForms } from '@/src/features/register-by-email/model/types' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import { emailOptions, Error } from '@/src/shared' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { useShowData } from '@/src/shared/lib/hooks' -import { TDeviceProp } from '@/src/shared/lib/types/entities' -import { InputStyleDark, InputStyleLight } from '@/src/shared/ui/input' +import { authTelegram } from '#/features/auth-telegram' +import { PasswordOptions } from '#/features/register-by-email/lib/constants' +import { IEmailForms } from '#/features/register-by-email/model/types' +import { Layout } from '#/app/layout' +import { useAppSelector } from '#/app/store/store' +import { emailOptions, Error } from '#/shared' +import { getTypeDevice } from '#/shared/lib/helpers' +import { useShowData } from '#/shared/lib/hooks' +import { TDeviceProp } from '#/shared/lib/types/entities' +import { InputStyleDark, InputStyleLight } from '#/shared/ui/input' export async function getServerSideProps(context: any): Promise<{ props: TDeviceProp }> { const device = getTypeDevice(context) @@ -116,7 +116,15 @@ const Login: React.FC<TDeviceProp> = ({ device }) => { backgroundColor: theme === 'light' ? 'white' : '#303035', }} > - {!desktop && <Image style={{ marginTop: desktop ? 0 : '80px' }} src={'/logo.svg'} alt={''} width={50} height={50} />} + {!desktop && ( + <Image + style={{ marginTop: desktop ? 0 : '80px' }} + src={'/logo.svg'} + alt={''} + width={50} + height={50} + /> + )} <Box sx={{ width: '50%', @@ -177,7 +185,13 @@ const Login: React.FC<TDeviceProp> = ({ device }) => { }, }} > - <Image src={'/svg/auth/yandex.svg'} alt={''} style={{ marginRight: 15 }} width={26} height={26} /> + <Image + src={'/svg/auth/yandex.svg'} + alt={''} + style={{ marginRight: 15 }} + width={26} + height={26} + /> Войти с Яндекс ID </Button> <Typography sx={{ fontSize: '15px', color: '#A4AAB5' }}>или email</Typography> @@ -286,10 +300,20 @@ const Login: React.FC<TDeviceProp> = ({ device }) => { justifyContent: 'center', }} > - <CircularProgress size={30} thickness={5} sx={{ color: '#7F7DF3' }} /> + <CircularProgress + size={30} + thickness={5} + sx={{ color: '#7F7DF3' }} + /> </Box> )} - <Box sx={{ width: '100%', textAlign: 'center', marginTop: '15px' }}> + <Box + sx={{ + width: '100%', + textAlign: 'center', + marginTop: '15px', + }} + > <Link href='/register' style={{ @@ -307,8 +331,22 @@ const Login: React.FC<TDeviceProp> = ({ device }) => { </form> </Box> {desktop && ( - <Box sx={{ width: '50%', height: '100vh', backgroundColor: '#8280FF', overflow: 'hidden', position: 'relative' }}> - <Image style={{ position: 'absolute', top: 35, left: 35 }} src={'/svg/logo.svg'} alt={''} width={42} height={42} /> + <Box + sx={{ + width: '50%', + height: '100vh', + backgroundColor: '#8280FF', + overflow: 'hidden', + position: 'relative', + }} + > + <Image + style={{ position: 'absolute', top: 35, left: 35 }} + src={'/svg/logo.svg'} + alt={''} + width={42} + height={42} + /> <Image src={imageLink} alt={''} @@ -1,10 +1,10 @@ import React from 'react' import { getSession } from 'next-auth/react' -import { Stepper } from '@/src/features/register-business' -import { Layout } from '@/src/main/layout' -import { getAccessToken, getTypeDevice } from '@/src/shared/lib/helpers' -import { Device } from '@/src/shared/lib/types/entities' +import { Stepper } from '#/features/register-business' +import { Layout } from '#/app/layout' +import { getAccessToken, getTypeDevice } from '#/shared/lib/helpers' +import { Device } from '#/shared/lib/types/entities' type BusinessProps = { device: Device @@ -10,11 +10,11 @@ import Image from 'next/image' import { useRouter } from 'next/router' import { signIn } from 'next-auth/react' -import { addReferral } from '@/src/entities/user-account/model/user-type-slice' -import { RegisterEmailForm } from '@/src/features/register-by-email' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import { TDeviceProp } from '@/src/shared/lib/types/entities' +import { addReferral } from '#/entities/user-account/model/user-type-slice' +import { RegisterEmailForm } from '#/features/register-by-email' +import { Layout } from '#/app/layout' +import { useAppSelector } from '#/app/store/store' +import { TDeviceProp } from '#/shared/lib/types/entities' export async function getServerSideProps(context: any): Promise<{ props: TDeviceProp }> { const UA = context.req.headers['user-agent'] @@ -67,7 +67,15 @@ const Register: React.FC<TDeviceProp> = ({ device }) => { backgroundColor: theme === 'light' ? 'white' : '#303035', }} > - {!desktop && <Image style={{ marginTop: desktop ? 0 : '20px' }} src={'/logo.svg'} alt={''} width={50} height={50} />} + {!desktop && ( + <Image + style={{ marginTop: desktop ? 0 : '20px' }} + src={'/logo.svg'} + alt={''} + width={50} + height={50} + /> + )} <Box sx={{ width: desktop ? '50%' : '100%', @@ -93,11 +101,12 @@ const Register: React.FC<TDeviceProp> = ({ device }) => { <Typography className='text'> Вы успешно зарегистрированы. <br /> - Мы отправили письмо для верификации на ваш email. В случае отсутствия, проверьте папку Спам. + Мы отправили письмо для верификации на ваш email. В случае отсутствия, + проверьте папку Спам. <br /> <br /> - Если вы регистрировались с корпоративной почты и письмо-подтверждение вам не пришло, рекомендуем - зарегистрироваться с личной почты + Если вы регистрировались с корпоративной почты и письмо-подтверждение вам + не пришло, рекомендуем зарегистрироваться с личной почты </Typography> </Box> ) : ( @@ -150,7 +159,9 @@ const Register: React.FC<TDeviceProp> = ({ device }) => { /> Войти с Яндекс ID </Button> - <Typography sx={{ fontSize: '15px', color: '#A4AAB5' }}>или email</Typography> + <Typography sx={{ fontSize: '15px', color: '#A4AAB5' }}> + или email + </Typography> </> )} <RegisterEmailForm successLogin={onSuccessRegister} /> @@ -159,8 +170,22 @@ const Register: React.FC<TDeviceProp> = ({ device }) => { </Box> </Box> {desktop && ( - <Box sx={{ width: '50%', height: '100vh', backgroundColor: '#8280FF', overflow: 'hidden', position: 'relative' }}> - <Image style={{ position: 'absolute', top: 35, left: 35 }} src={'/svg/logo.svg'} alt={''} width={42} height={42} /> + <Box + sx={{ + width: '50%', + height: '100vh', + backgroundColor: '#8280FF', + overflow: 'hidden', + position: 'relative', + }} + > + <Image + style={{ position: 'absolute', top: 35, left: 35 }} + src={'/svg/logo.svg'} + alt={''} + width={42} + height={42} + /> <Image src={imageLink} alt={''} @@ -5,9 +5,9 @@ import { Box, Button, Stack, TextField, Typography } from '@mui/material' import axios from 'axios' import { useSession } from 'next-auth/react' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import { API_URL } from '@/src/shared/lib/constants/constants' +import { Layout } from '#/app/layout' +import { useAppSelector } from '#/app/store/store' +import { API_URL } from '#/shared/lib/constants/constants' const styleSend = { position: 'absolute' as 'absolute', @@ -110,7 +110,8 @@ const ReportsMobile = () => { <Box sx={theme === 'light' ? styleSend : styleSendDark}> <Typography sx={{ color: '#22B47F', fontSize: '23px' }}>Спасибо!</Typography> <Typography sx={{ fontSize: '17px', marginTop: 2 }}> - Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при регистрации + Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при + регистрации </Typography> </Box> ) : ( @@ -169,7 +170,12 @@ const ReportsMobile = () => { height: 25, }} /> - <input onChange={handleFileChange} type='file' hidden accept='.jpg,.jpeg,.png' /> + <input + onChange={handleFileChange} + type='file' + hidden + accept='.jpg,.jpeg,.png' + /> </Button> </> ), @@ -187,7 +193,9 @@ const ReportsMobile = () => { /> {file && ( <Box sx={{ display: 'flex', alignItems: 'center' }}> - <Typography sx={{ fontSize: '15px' }}>Добавлено 1 изображение: {file.name}</Typography> + <Typography sx={{ fontSize: '15px' }}> + Добавлено 1 изображение: {file.name} + </Typography> <CloseIcon onClick={() => setFile(undefined)} sx={{ @@ -200,7 +208,13 @@ const ReportsMobile = () => { /> </Box> )} - <Stack direction={'column-reverse'} justifyContent='flex-end' alignItems='flex-end' spacing={1} sx={{ marginTop: 1.5 }}> + <Stack + direction={'column-reverse'} + justifyContent='flex-end' + alignItems='flex-end' + spacing={1} + sx={{ marginTop: 1.5 }} + > <Button fullWidth sx={{ @@ -6,13 +6,13 @@ import axios from 'axios' import Image from 'next/image' import Router from 'next/router' -import { Layout } from '@/src/main/layout' -import { useAppSelector } from '@/src/main/store/store' -import { getRandomImage } from '@/src/pages/login' -import { Error, Input } from '@/src/shared' -import { API_URL } from '@/src/shared/lib/constants/constants' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { TDeviceProp } from '@/src/shared/lib/types/entities' +import { Layout } from '#/app/layout' +import { useAppSelector } from '#/app/store/store' +import { getRandomImage } from '#/pages/login' +import { Error, Input } from '#/shared' +import { API_URL } from '#/shared/lib/constants/constants' +import { getTypeDevice } from '#/shared/lib/helpers' +import { TDeviceProp } from '#/shared/lib/types/entities' export async function getServerSideProps(context: any): Promise<{ props: TDeviceProp }> { const device = getTypeDevice(context) @@ -64,7 +64,10 @@ const Reset: React.FC<TDeviceProp> = ({ device }) => { > {desktop && ( <Card> - <CardMedia sx={{ height: '100vh', width: '600px' }} image={`/login_images/${refImage.current}`}> + <CardMedia + sx={{ height: '100vh', width: '600px' }} + image={`/login_images/${refImage.current}`} + > <Stack direction='column-reverse' justifyContent='flex-start' @@ -114,18 +117,29 @@ const Reset: React.FC<TDeviceProp> = ({ device }) => { alignItems: 'center', }} > - <Stack direction='column' justifyContent='center' alignItems='center' spacing={0} sx={{ width: '350px' }}> + <Stack + direction='column' + justifyContent='center' + alignItems='center' + spacing={0} + sx={{ width: '350px' }} + > <Typography sx={{ fontSize: 25 }}>Забыли пароль?</Typography> <Typography marginTop={1.5} sx={{ fontSize: 16 }}> - Введите адрес электронной почты, которую вы использовали для регистрации в сервисе + Введите адрес электронной почты, которую вы использовали для регистрации в + сервисе </Typography> <Box textAlign='left' width='100%' marginTop={1.5}> <Typography marginLeft={0.5} className='title-main-gray'> Email </Typography> </Box> - <Input onChange={(event) => set_Email(event.target.value)} type={'email'} fullWidth /> + <Input + onChange={(event) => set_Email(event.target.value)} + type={'email'} + fullWidth + /> <Link style={{ position: 'absolute', @@ -139,7 +153,12 @@ const Reset: React.FC<TDeviceProp> = ({ device }) => { <Image src={'/x-mark.svg'} width={18} height={18} alt={''} /> </Link> <Box marginTop={2} width='100%'> - <Button variant='contained' fullWidth className='btn-classic' onClick={sendMail}> + <Button + variant='contained' + fullWidth + className='btn-classic' + onClick={sendMail} + > Восстановить аккаунт </Button> </Box> @@ -1,10 +1,10 @@ import React from 'react' import { Box } from '@mui/material' -import { Layout } from '@/src/main/layout' -import { getTypeDevice } from '@/src/shared/lib/helpers' -import { Device } from '@/src/shared/lib/types/entities' -import { WhisperWidget } from '@/src/widgets/whisper' +import { Layout } from '#/app/layout' +import { getTypeDevice } from '#/shared/lib/helpers' +import { Device } from '#/shared/lib/types/entities' +import { WhisperWidget } from '#/widgets/whisper' export async function getServerSideProps(context: any): Promise<{ props: { device: Device } }> { const device = getTypeDevice(context) @@ -1,7 +1,7 @@ import axios from 'axios' -import { IModel, IShortModel } from '@/src/shared/api/models/models' -import { API_URL } from '@/src/shared/lib/constants' +import { IModel, IShortModel } from '#/shared/api/models/models' +import { API_URL } from '#/shared/lib/constants' const model_api = { async getBots(token?: string): Promise<IShortModel[]> { @@ -4,12 +4,12 @@ import base64 from 'base64-encode-file' import { useSession } from 'next-auth/react' import process from 'process' -import { getUserBalance } from '@/src/entities/balance' -import { useAppDispatch } from '@/src/main/store/store' -import { API_URL } from '@/src/shared/lib/constants' -import { Device } from '@/src/shared/lib/types/entities' -import { Message, MessageSend } from '@/src/shared/lib/types/model' -import { IMessageRequest } from '@/src/shared/lib/types/types-gpt' +import { getUserBalance } from '#/entities/balance' +import { useAppDispatch } from '#/app/store/store' +import { API_URL } from '#/shared/lib/constants' +import { Device } from '#/shared/lib/types/entities' +import { Message, MessageSend } from '#/shared/lib/types/model' +import { IMessageRequest } from '#/shared/lib/types/types-gpt' const formDataHelper = (file: File, dataForSend: MessageSend<any>): FormData => { const FD = new FormData() @@ -23,11 +23,14 @@ const formDataHelper = (file: File, dataForSend: MessageSend<any>): FormData => export const ModelsWithChatsEndpoints = { getData: async (chatUid: string, offset: number, token?: string) => { try { - const { data } = await axios.get<Message[]>(API_URL + `/chats/${chatUid}/messages/?limit=10&offset=${offset}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - }) + const { data } = await axios.get<Message[]>( + API_URL + `/chats/${chatUid}/messages/?limit=10&offset=${offset}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ) return data } catch (err: any) { return { @@ -41,13 +44,17 @@ export const ModelsWithChatsEndpoints = { const HeaderDataType = dataForSend instanceof FormData ? 'multipart/form-data' : 'application/json' try { - const { data } = await axios.post<IMessageRequest, AxiosResponse<Message[]>>(API_URL + `/chats/${chatUid}/messages/`, dataForSend, { - withCredentials: true, - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': HeaderDataType, - }, - }) + const { data } = await axios.post<IMessageRequest, AxiosResponse<Message[]>>( + API_URL + `/chats/${chatUid}/messages/`, + dataForSend, + { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': HeaderDataType, + }, + } + ) return data } catch (err: any) { @@ -165,7 +172,7 @@ export function useModel( setMessages((prev) => [...prev!, userMessage]) try { //@ts-ignore - const message = (result.details as AxiosError).response.data.trim() ?? 'Ошибка отправки сообщения' + const message = (result.details as AxiosError).response!.data.trim() ?? 'Ошибка отправки сообщения' showError(message) return } catch (e) { @@ -212,13 +219,17 @@ export const ModelsWithImagesEndpoints = { const HeaderDataType = dataForSend instanceof FormData ? 'multipart/form-data' : 'application/json' try { - const { data } = await axios.post<IMessageRequest, AxiosResponse<Message[]>>(API_URL + `/media/image/${type}`, dataForSend, { - withCredentials: true, - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': HeaderDataType, - }, - }) + const { data } = await axios.post<IMessageRequest, AxiosResponse<Message[]>>( + API_URL + `/media/image/${type}`, + dataForSend, + { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': HeaderDataType, + }, + } + ) return data } catch (err: any) { @@ -375,12 +386,16 @@ export const ModelsMediaApi = { }, sendData: async <T>(type: string | null, dataForSend: MessageSend<T> | FormData, token?: string) => { try { - const { data } = await axios.post<IMessageRequest, AxiosResponse<Message[]>>(API_URL + `/media/audio/${type}`, dataForSend, { - withCredentials: true, - headers: { - Authorization: `Bearer ${token}`, - }, - }) + const { data } = await axios.post<IMessageRequest, AxiosResponse<Message[]>>( + API_URL + `/media/audio/${type}`, + dataForSend, + { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + }, + } + ) return data } catch (err: any) { @@ -1,8 +1,8 @@ import axios, { AxiosResponse } from 'axios' import { User } from 'next-auth' -import { API_URL } from '@/src/shared/lib/constants' -import { IOffer } from '@/src/widgets/payment/model/payment' +import { API_URL } from '#/shared/lib/constants' +import { IOffer } from '#/widgets/payment/model/payment' interface IUserBalance { current_token_balance: number @@ -61,7 +61,12 @@ export const accountApi = { } }, - async changePassword(token: string | null, password_1: string, password_2: string, current_password: string): Promise<number> { + async changePassword( + token: string | null, + password_1: string, + password_2: string, + current_password: string + ): Promise<number> { try { const { status } = await axios.put( API_URL + '/auth/reset-pass', @@ -2,10 +2,10 @@ import axios, { AxiosResponse } from 'axios' import { Session } from 'next-auth' import * as process from 'process' -import { Chat } from '@/src/shared/api/type-model-chats' -import { Error } from '@/src/shared/lib/types/entities' -import { IImagesResponse } from '@/src/shared/lib/types/types-dalle' -import { IMessageRequest, ISendMessageResponse } from '@/src/shared/lib/types/types-gpt' +import { Chat } from '#/shared/api/type-model-chats' +import { Error } from '#/shared/lib/types/entities' +import { IImagesResponse } from '#/shared/lib/types/types-dalle' +import { IMessageRequest, ISendMessageResponse } from '#/shared/lib/types/types-gpt' import { Message } from '../lib/types/model' @@ -96,12 +96,16 @@ export const api = { async sendMessageChatGPT(token: string, message: any, uid: string): Promise<ISendMessageResponse> { try { - const { data } = await axios.post<IMessageRequest, AxiosResponse<Message[]>>(API_URL + `/chats/${uid}/messages/`, message, { - withCredentials: true, - headers: { - Authorization: `Bearer ${token}`, - }, - }) + const { data } = await axios.post<IMessageRequest, AxiosResponse<Message[]>>( + API_URL + `/chats/${uid}/messages/`, + message, + { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + }, + } + ) return { data, isError: false } } catch (err) { @@ -157,7 +161,9 @@ export const createChat = async (model: string, token?: string) => { export const getAllChats = async (model: string, token?: string): Promise<Chat[] | []> => { try { - const { data } = await axios.get(API_URL + `/chats/?model=${model}`, { headers: { Authorization: `Bearer ${token}` } }) + const { data } = await axios.get(API_URL + `/chats/?model=${model}`, { + headers: { Authorization: `Bearer ${token}` }, + }) if (data.length === 0) { const newChat = await createChat(model, token) @@ -1,32 +1,30 @@ .card { - width: 381px; - overflow: hidden; - position: relative; - height: 290px; - border-radius: 15px; - background-color: var(--new-ui-main-color); - margin-top: 15px; - margin-right: 20px; + width: 381px; + overflow: hidden; + position: relative; + height: 290px; + border-radius: 15px; + background-color: var(--new-ui-main-color); + margin-top: 15px; + margin-right: 20px; - @media (max-width:420px) { - width: 92vw; - height: 300px; - } - - .description { - padding: 15px 20px; + @media (max-width: 420px) { + width: 92vw; + height: 300px; + } - .title { - color: var(--new-ui-text-color); - font-weight: 600; - font-size: 21px; - } - .text { - margin-top: 15px; - font-size: 15px; - color: var(--new-ui-gray-color); - } - } + .description { + padding: 15px 20px; - -} \ No newline at end of file + .title { + color: var(--new-ui-text-color); + font-weight: 600; + font-size: 21px; + } + .text { + margin-top: 15px; + font-size: 15px; + color: var(--new-ui-gray-color); + } + } +} @@ -16,9 +16,7 @@ interface IProps { export const Card = ({ text, icon, title, uid, slug, accessed_models }: IProps) => { const link = useMemo(() => { - return accessed_models && !accessed_models.includes(slug) - ? '/account?scope=subscribe' - : `/images/${slug}` + return accessed_models && !accessed_models.includes(slug) ? '/account?scope=subscribe' : `/images/${slug}` }, [accessed_models]) return ( @@ -63,11 +61,7 @@ export const Card = ({ text, icon, title, uid, slug, accessed_models }: IProps) textAlign: 'center', }} > - <Button - disabled - className='btn-classic' - sx={{ width: '86%', top: '10%' }} - > + <Button disabled className='btn-classic' sx={{ width: '86%', top: '10%' }}> <Image style={{ marginRight: '7px', marginBottom: '2px' }} src={'/star-white.svg'} @@ -13,7 +13,7 @@ export enum ModelPagesList { export const API_URL = process.env.NEXT_PUBLIC_API_HOST export const surpriseMePrompts = [ - 'Vintage 90\'s anime style. stylish model posing in 7/11 convenience store., sci-fi.', + "Vintage 90's anime style. stylish model posing in 7/11 convenience store., sci-fi.", 'an exhausted pepe the frog at the frontlines in the army at night with his platoon fighting, key lighting, soft lights, foggy, by steve hanks, by lisa yuskavage, by serov valentin, by tarkovsky, 8 k render, detailed, cute cartoon style, very cute adorable face', @@ -29,7 +29,7 @@ export const surpriseMePrompts = [ '8k render of a glowing reflective iridescent male marble bust, glowing, dark hues, 8k. iridescent. very iridescent. trending on behance', - 'cute adorable baby phoenix made of crystal ball with low poly eye\'s highly detailed intricated concept art trending artstation 8k', + "cute adorable baby phoenix made of crystal ball with low poly eye's highly detailed intricated concept art trending artstation 8k", 'lighthouse,retro cover, high details, intricate details, by vincent di fate, artgerm julie bell beeple, 60s, inking, vintage 60s print, screen print', @@ -1,6 +1,7 @@ import { RegisterOptions } from 'react-hook-form/dist/types/validator' -const EMAIL_REGEXP = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu +const EMAIL_REGEXP = + /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu const PHONE_REGEXP = /^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$/ export const emailOptions: RegisterOptions = { @@ -1,4 +1,4 @@ -import { colorDark, colorLight } from '@/src/shared/lib/constants/colors' +import { colorDark, colorLight } from '#/shared/lib/constants/colors' export const TypographyLight = { fontWeight: 600, @@ -1,7 +1,10 @@ import { GetServerSidePropsContext } from 'next' -import { Device, DeviceOs } from '@/src/shared/lib/types/entities' +import { Device, DeviceOs } from '#/shared/lib/types/entities' +/** + * @deprecated + */ export const getTypeDevice = (context: GetServerSidePropsContext): Device => { const UA = context.req.headers['user-agent'] @@ -14,6 +17,22 @@ export const getTypeDevice = (context: GetServerSidePropsContext): Device => { return isMobile ? 'mobile' : 'desktop' } +export const getDeviceType = (): Device => { + const UA = navigator.userAgent + + if (!UA) { + return 'desktop' + } + + const isMobile = Boolean(UA.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i)) + + return isMobile ? 'mobile' : 'desktop' +} + + +/** + * @deprecated + */ export const getDeviceOs = (context: GetServerSidePropsContext): DeviceOs => { const UA = context.req.headers['user-agent'] @@ -25,3 +44,15 @@ export const getDeviceOs = (context: GetServerSidePropsContext): DeviceOs => { return isIos ? 'ios' : 'android' } + +export const getOs = (): DeviceOs => { + const UA = navigator.userAgent + + if (!UA) { + return 'android' + } + + const isIos = UA.includes('iPhone') || UA.includes('iPad') + + return isIos ? 'ios' : 'android' +} @@ -12,7 +12,20 @@ export function getCurrentTimeAndGreeting(userName: string) { greeting = 'Добрый вечер' } - const months = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'] + const months = [ + 'января', + 'февраля', + 'марта', + 'апреля', + 'мая', + 'июня', + 'июля', + 'августа', + 'сентября', + 'октября', + 'ноября', + 'декабря', + ] const formattedDate = `Сегодня, ${day} ${months[monthIndex]} ${year}` const greetingMessage = `${greeting}, ${userName}! 👋` @@ -1,3 +1,4 @@ export { getAccessToken } from './get-token' export { getTypeDevice } from './get-type-device' export { useConcat } from './reactive-concat' +export * from './get-type-device' @@ -7,7 +7,7 @@ export class LocalStorageHelper { this.modelTitle = key.toLowerCase() } - public readonly getSettingFromLS = (key: string, defaultValue: any, availableModels?:any[]) => { + public readonly getSettingFromLS = (key: string, defaultValue: any, availableModels?: any[]) => { const finalKey = this.modelTitle + ': ' + key if (typeof localStorage !== 'undefined') { @@ -7,7 +7,10 @@ export const useAutoLoad = (desktop: boolean): [boolean, Dispatch<SetStateAction const isMobile = desktop ? document : document.getElementById('messages-list') const scrollHandler = (e: any) => { - if (e.target.documentElement.scrollHeight - (e.target.documentElement.scrollTop + window.innerHeight) < 100) { + if ( + e.target.documentElement.scrollHeight - (e.target.documentElement.scrollTop + window.innerHeight) < + 100 + ) { setFetching(true) } } @@ -1,4 +1,4 @@ -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' interface IThemeAndDevice { theme: 'dark' | 'light' @@ -1,7 +1,7 @@ import React from 'react' -import { IModelInputs } from '@/src/shared/api/models/models' -import { Device, DeviceOs } from '@/src/shared/lib/types/entities' +import { IModelInputs } from '#/shared/api/models/models' +import { Device, DeviceOs } from '#/shared/lib/types/entities' export type MessageSend<T> = { content: string @@ -49,4 +49,6 @@ export type ChatProps<T> = { currentVersion: string } -export type FiltersChatProps<T> = Pick<ChatProps<T>, 'device' | 'setting'> & { resetSettings: () => void } +export type FiltersChatProps<T> = Pick<ChatProps<T>, 'device' | 'setting'> & { + resetSettings: () => void +} @@ -1,4 +1,4 @@ -import { IProps } from '@/src/shared/lib/types/entities' +import { IProps } from '#/shared/lib/types/entities' export interface IImagesResponse { question: string @@ -1,4 +1,4 @@ -import { IProps } from '@/src/shared/lib/types/entities' +import { IProps } from '#/shared/lib/types/entities' export interface IMessageResponse { question: string @@ -1,4 +1,4 @@ -import { IProps } from '@/src/shared/lib/types/entities' +import { IProps } from '#/shared/lib/types/entities' export interface IResponseSD { question: string @@ -1,3 +1,3 @@ -import { RootState } from '@/src/main/store/store' +import { RootState } from '#/app/store/store' export const balanceSelector = (state: RootState) => Math.floor(state.balance.balance) @@ -1,12 +1,11 @@ .wrap { - width: 85%; + width: 85%; - @media (max-width: 1200px) { - width: 95%; - } + @media (max-width: 1200px) { + width: 95%; + } - @media (max-width: 768px) { - width: 100%; - } - -} \ No newline at end of file + @media (max-width: 768px) { + width: 100%; + } +} @@ -1,44 +1,44 @@ .main { - width: 100%; - display: flex; - justify-content: flex-start; - gap: 20px; - margin-bottom: 24px; + width: 100%; + display: flex; + justify-content: flex-start; + gap: 20px; + margin-bottom: 24px; - .chatWindow { - width: 70%; - } + .chatWindow { + width: 70%; + } - .settings { - width: 25%; - } + .settings { + width: 25%; + } - @media (max-width:768px) { - width: 100%; - display: flex; - - flex-direction: column-reverse; - - .chatWindow { - width: 100%; - } + @media (max-width: 768px) { + width: 100%; + display: flex; - .settings { - width: 100%; - } - } + flex-direction: column-reverse; + + .chatWindow { + width: 100%; + } + + .settings { + width: 100%; + } + } } -.wrapModelTitle{ - margin-bottom: 20px; - margin-top: 15px; - @media (max-width:768px) { - margin: 0; - margin-top: 15px; - } - .bigTitle { - @media (max-width:768px) { - display: none; - overflow: hidden; - } - } +.wrapModelTitle { + margin-bottom: 20px; + margin-top: 15px; + @media (max-width: 768px) { + margin: 0; + margin-top: 15px; + } + .bigTitle { + @media (max-width: 768px) { + display: none; + overflow: hidden; + } + } } @@ -1,82 +1,77 @@ +.wrap { + width: 100%; + background-color: var(--new-ui-main-color); + border-radius: 15px; + padding: 30px; + height: fit-content; + //margin-bottom: 15px; + max-height: 350px; + overflow-y: auto; + @media (max-width: 768px) { + padding: 10px 20px; + } -.wrap { - width: 100%; - background-color: var(--new-ui-main-color); - border-radius: 15px; - padding: 30px; - height: fit-content; - //margin-bottom: 15px; - max-height: 350px; - overflow-y: auto; + .container { + display: block; - @media (max-width:768px) { - padding: 10px 20px; - } + @media (max-width: 768px) { + display: flex; + overflow-x: scroll; + scrollbar-width: none; + -ms-overflow-style: none; + &::-webkit-scrollbar { + width: 2px; + } + } - .container { - display: block; - - @media (max-width:768px) { - display: flex; - overflow-x: scroll; - scrollbar-width: none; - -ms-overflow-style: none; - &::-webkit-scrollbar { - width: 2px ; - } - } + .item { + padding: 0; + border: none; + width: 100%; - .item { - padding: 0; - border:none; - width: 100%; - - - &:hover { - background-color: transparent; - } - - .main { - display: flex; - width: 100%; - border:none; - justify-content: space-between; - background-color:transparent; - border-radius:13px; - padding:12px 15px; - text-transform: none; - - &_active { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - border:none; - border-radius:13px; - padding:12px 15px; - text-transform: none; - background-color:rgba(130, 128, 255, 0.15); - - @media (max-width:768px) { - margin-right: 10px; - margin-top: 10px; - width: 125px; - max-width: 130px; - overflow: hidden; - padding: 8px 12px; - justify-content: space-between; - } - } - - @media (max-width:768px) { - margin-top: 10px; - width: 100px; - padding: 0; - } - } - } - - } -} + &:hover { + background-color: transparent; + } + + .main { + display: flex; + width: 100%; + border: none; + justify-content: space-between; + background-color: transparent; + border-radius: 13px; + padding: 12px 15px; + text-transform: none; + &_active { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + border: none; + border-radius: 13px; + padding: 12px 15px; + text-transform: none; + background-color: rgba(130, 128, 255, 0.15); + + @media (max-width: 768px) { + margin-right: 10px; + margin-top: 10px; + width: 125px; + max-width: 130px; + overflow: hidden; + padding: 8px 12px; + justify-content: space-between; + } + } + + @media (max-width: 768px) { + margin-top: 10px; + width: 100px; + padding: 0; + } + } + } + } +} @@ -1,5 +1,5 @@ .button { - background-color: #8280FF; + background-color: #8280ff; border-radius: 13px; text-transform: none; color: white; @@ -7,7 +7,7 @@ import Typography from '@mui/material/Typography' import Image from 'next/image' import Link from 'next/link' -import { translateTypeModel } from '@/src/shared' +import { translateTypeModel } from '#/shared' export const Card: React.FC<any> = ({ uid, @@ -160,9 +160,19 @@ export const Card: React.FC<any> = ({ } > {is_favourite ? ( - <Image src={'/svg/sub_menu/favourite.svg'} alt={'1'} width={25} height={25} /> + <Image + src={'/svg/sub_menu/favourite.svg'} + alt={'1'} + width={25} + height={25} + /> ) : ( - <Image src={'/svg/sub_menu/favourite_off.svg'} alt={'1'} width={25} height={25} /> + <Image + src={'/svg/sub_menu/favourite_off.svg'} + alt={'1'} + width={25} + height={25} + /> )} </Box> </Box> @@ -5,8 +5,8 @@ import CardMedia from '@mui/material/CardMedia' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' -import { translateTypeModel } from '@/src/shared' -import { Device } from '@/src/shared/lib/types/entities' +import { translateTypeModel } from '#/shared' +import { Device } from '#/shared/lib/types/entities' export const UnActiveCard: React.FC<any> = ({ title, image_link, languages, description, model_config, device }) => { const desktop = device === 'desktop' @@ -3,7 +3,7 @@ import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers' import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' import dayjs, { Dayjs } from 'dayjs' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' import styles from './date.module.scss' @@ -44,7 +44,13 @@ export const DateInput = ({ width: '100%', }} label={label ? label : 'Дата'} - value={date ? dayjs(new Date().toDateString()) : prevdate ? dayjs(new Date().toDateString()).add(-1, 'year') : null} + value={ + date + ? dayjs(new Date().toDateString()) + : prevdate + ? dayjs(new Date().toDateString()).add(-1, 'year') + : null + } onChange={(date) => { if (date?.isValid()) { setDate(JSON.stringify(date).split('"')[1].split('T')[0]) @@ -1,5 +1,5 @@ .button { - background-color: #8280FF; + background-color: #8280ff; border-radius: 13px; text-transform: none; color: white; @@ -69,7 +69,12 @@ export const dataLine = { return null } - const gradient = ctx.createLinearGradient(chartArea.left, chartArea.bottom, chartArea.left, chartArea.top) + const gradient = ctx.createLinearGradient( + chartArea.left, + chartArea.bottom, + chartArea.left, + chartArea.top + ) gradient.addColorStop(0.68, 'rgba(130, 128, 255, 0.20)') gradient.addColorStop(1, 'rgba(130, 128, 255, 0.10)') @@ -5,7 +5,13 @@ export const ArrowDownScroll: React.FC<SVGProps<SVGSVGElement>> = ({ ...props }) return ( <svg width='36' height='36' viewBox='0 0 36 36' fill='none' xmlns='http://www.w3.org/2000/svg'> <rect width='36' height='36' rx='18' fill='#7F7DF3' /> - <path d='M25 16L18 23L11 16' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' /> + <path + d='M25 16L18 23L11 16' + stroke='white' + stroke-width='2' + stroke-linecap='round' + stroke-linejoin='round' + /> </svg> ) } @@ -6,9 +6,9 @@ import Stack from '@mui/material/Stack' import { TextFieldProps } from '@mui/material/TextField/TextField' import Image from 'next/image' -import { Calculation } from '@/src/features/calculation-tokens-gpt' -import { useAppSelector } from '@/src/main/store/store' -import { styleInputWithoutBorderFocus } from '@/src/shared/ui/input' +import { Calculation } from '#/features/calculation-tokens-gpt' +import { useAppSelector } from '#/app/store/store' +import { styleInputWithoutBorderFocus } from '#/shared/ui/input' interface Input { loading: boolean @@ -104,7 +104,16 @@ export const InputImagesModels: FC<Input & TextFieldProps> = ({ const src = `/svg/chatgpt/send_message${theme === 'light' ? '' : '_dark'}.svg` - return <Image onClick={requestImage} height={26} width={26} className='pointer' src={src} alt={'Отправить запрос'} /> + return ( + <Image + onClick={requestImage} + height={26} + width={26} + className='pointer' + src={src} + alt={'Отправить запрос'} + /> + ) } return ( @@ -24,5 +24,3 @@ right: 25px; } } - - @@ -3,7 +3,7 @@ import { Box } from '@mui/material' import Dialog from '@mui/material/Dialog' import Image from 'next/image' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' import styles from './modal.module.scss' export interface ModalProps { @@ -3,7 +3,7 @@ import { TextField } from '@mui/material' import { TextFieldProps } from '@mui/material/TextField/TextField' import Image from 'next/image' -import { styleInputWithoutBorderFocus } from '@/src/shared/ui/input' +import { styleInputWithoutBorderFocus } from '#/shared/ui/input' import styles from './search.module.scss' export const Search = (props: TextFieldProps) => { @@ -12,7 +12,11 @@ export const Search = (props: TextFieldProps) => { sx={styleInputWithoutBorderFocus} className={styles.search} {...props} - InputProps={{ endAdornment: <Image src={'/search.svg'} style={{ marginRight: '15px' }} width={20} height={20} alt={'1'} /> }} + InputProps={{ + endAdornment: ( + <Image src={'/search.svg'} style={{ marginRight: '15px' }} width={20} height={20} alt={'1'} /> + ), + }} /> ) } @@ -2,8 +2,8 @@ import React from 'react' import { Typography } from '@mui/material' import Box from '@mui/material/Box' -import { useAppSelector } from '@/src/main/store/store' -import { PrettoSlider, PrettoSliderDark } from '@/src/widgets/filters-gpt/ui/filters' +import { useAppSelector } from '#/app/store/store' +import { PrettoSlider, PrettoSliderDark } from '#/widgets/filters-gpt/ui/filters' export const Slider = ({ value, @@ -2,9 +2,9 @@ import React from 'react' import { styled } from '@mui/material' import Switch, { SwitchProps } from '@mui/material/Switch' -import { useAppSelector } from '@/src/main/store/store' -import { baseColor } from '@/src/shared/lib/constants/colors' -import { pingFangFont } from '@/src/shared/lib/constants/font/font' +import { useAppSelector } from '#/app/store/store' +import { baseColor } from '#/shared/lib/constants/colors' +import { pingFangFont } from '#/shared/lib/constants/font/font' const AntSwitch = styled(Switch)(({ theme }) => ({ width: 29, @@ -1,7 +1,7 @@ import React from 'react' import { Tooltip } from '@mui/material' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' import styles from './styles.module.scss' interface Props { @@ -3,7 +3,7 @@ import { Box, Divider, Link, Menu, MenuItem, Typography } from '@mui/material' import Router from 'next/router' import { signOut, useSession } from 'next-auth/react' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' interface IAccountMenu { anchorEl: HTMLElement | null @@ -111,7 +111,12 @@ export const AccountMenu: React.FC<IAccountMenu> = ({ anchorEl, changeVisible, d fontWeight: '400', }} > - <Link target='_blank' href='https://air.fail/blog' underline='none' sx={{ color: '#868686' }}> + <Link + target='_blank' + href='https://air.fail/blog' + underline='none' + sx={{ color: '#868686' }} + > Блог </Link> </MenuItem> @@ -168,7 +173,12 @@ export const AccountMenu: React.FC<IAccountMenu> = ({ anchorEl, changeVisible, d marginTop: 1.2, }} > - <Link target='_blank' href='https://air.fail/blog' underline='none' sx={{ color: '#868686' }}> + <Link + target='_blank' + href='https://air.fail/blog' + underline='none' + sx={{ color: '#868686' }} + > Блог </Link> </Typography> @@ -6,7 +6,7 @@ import FormGroup from '@mui/material/FormGroup' import Menu from '@mui/material/Menu' import MenuItem from '@mui/material/MenuItem' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' interface IDalleFilterMenu { anchorEl: null | HTMLElement @@ -18,7 +18,15 @@ interface IDalleFilterMenu { sliderChange: Function } -export const DalleFilterMenu: React.FC<IDalleFilterMenu> = ({ anchorEl, closeMenu, format, changeFormat, numberImages, sliderChange, open }) => { +export const DalleFilterMenu: React.FC<IDalleFilterMenu> = ({ + anchorEl, + closeMenu, + format, + changeFormat, + numberImages, + sliderChange, + open, +}) => { const theme = useAppSelector((state) => state.theme.theme) return ( @@ -2,7 +2,7 @@ import { Button, Stack, Typography } from '@mui/material' import Image from 'next/image' import Link from 'next/link' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' export const DemoInfo = () => { const { payment_plan, status } = useAppSelector((state) => state.user) @@ -16,18 +16,27 @@ export const DemoInfo = () => { const DemoDanger = () => { return ( - <Stack className='pd-30 bg-color-block border-radius-main mt-15' sx={{ backgroundColor: '#FF6969 !important' }}> + <Stack + className='pd-30 bg-color-block border-radius-main mt-15' + sx={{ backgroundColor: '#FF6969 !important' }} + > <Image src={'/dagner-payment.svg'} width={45} height={45} alt='1' /> - <Typography sx={{ color: 'white', fontSize: 19, fontWeight: 600, marginTop: '30px' }}>Демо доступ</Typography> + <Typography sx={{ color: 'white', fontSize: 19, fontWeight: 600, marginTop: '30px' }}> + Демо доступ + </Typography> <Typography sx={{ color: 'white', fontSize: 15, fontWeight: 500, marginTop: '10px' }}> - Вам доступны 5 токенов, которые можно потратить на любые продукты в AIR. Чтобы продолжить пользоваться платформой вы можете купить - токены. + Вам доступны 5 токенов, которые можно потратить на любые продукты в AIR. Чтобы продолжить + пользоваться платформой вы можете купить токены. </Typography> <Link href='/account?scope=subscribe'> <Button fullWidth className='btn-danger' - sx={{ marginTop: '20px', color: 'white !important', backgroundColor: 'rgba(255, 255, 255, 0.20) !important' }} + sx={{ + marginTop: '20px', + color: 'white !important', + backgroundColor: 'rgba(255, 255, 255, 0.20) !important', + }} > Купить токены </Button> @@ -1,7 +1,7 @@ import React from 'react' import { Drawer } from '@mui/material' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' type DrawerProps = { open: boolean @@ -6,8 +6,8 @@ import { Box, Button, Modal, Stack, TextField, Typography } from '@mui/material' import axios from 'axios' import { useSession } from 'next-auth/react' -import { useAppSelector } from '@/src/main/store/store' -import { API_URL } from '@/src/shared/lib/constants' +import { useAppSelector } from '#/app/store/store' +import { API_URL } from '#/shared/lib/constants' const style = { position: 'absolute' as 'absolute', @@ -146,7 +146,8 @@ const ErrorModal: React.FC<IErrorModal> = ({ open, handleClose, device }) => { color: '#868686', }} > - Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при регистрации + Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при + регистрации </Typography> </Box> ) : ( @@ -202,7 +203,12 @@ const ErrorModal: React.FC<IErrorModal> = ({ open, handleClose, device }) => { height: 25, }} /> - <input onChange={handleFileChange} type='file' hidden accept='.jpg,.jpeg,.png' /> + <input + onChange={handleFileChange} + type='file' + hidden + accept='.jpg,.jpeg,.png' + /> </Button> </> ), @@ -225,7 +231,9 @@ const ErrorModal: React.FC<IErrorModal> = ({ open, handleClose, device }) => { alignItems: 'center', }} > - <Typography sx={{ fontSize: '15px' }}>Добавлено 1 изображение: {file.name}</Typography> + <Typography sx={{ fontSize: '15px' }}> + Добавлено 1 изображение: {file.name} + </Typography> <CloseIcon onClick={() => setFile(undefined)} sx={{ @@ -2,7 +2,7 @@ import React from 'react' import { Alert, Slide, Snackbar, Typography } from '@mui/material' import Image from 'next/image' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' +import { useThemeAndDevice } from '#/shared/lib/hooks' export interface IError { error: string @@ -13,7 +13,15 @@ export interface IError { const ErrorIcon = () => { const { theme } = useThemeAndDevice() - return <Image src={`/error2${theme === 'light' ? '_white' : ''}.svg`} alt={''} width={20} height={20} style={{ marginTop: 2.5 }} /> + return ( + <Image + src={`/error2${theme === 'light' ? '_white' : ''}.svg`} + alt={''} + width={20} + height={20} + style={{ marginTop: 2.5 }} + /> + ) } export const Error: React.FC<IError> = ({ error, handleClose, open }) => { @@ -21,7 +29,12 @@ export const Error: React.FC<IError> = ({ error, handleClose, open }) => { return ( <Slide direction='down' in={open} mountOnEnter unmountOnExit> - <Snackbar anchorOrigin={{ vertical: 'top', horizontal: 'right' }} open={open} autoHideDuration={6000} onClose={handleClose}> + <Snackbar + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + open={open} + autoHideDuration={6000} + onClose={handleClose} + > <Alert icon={<ErrorIcon />} severity='error' @@ -1,31 +1,24 @@ -import Stack from '@mui/material/Stack'; -import useMediaQuery from '@mui/material/useMediaQuery'; +import Stack from '@mui/material/Stack' +import useMediaQuery from '@mui/material/useMediaQuery' -import { DrawerCustom } from './drawer'; +import { DrawerCustom } from './drawer' -export function FiltersWrapper (props: {open: boolean, onClose: (value: boolean) => void, filetrs: React.ReactNode}) { +export function FiltersWrapper(props: { open: boolean; onClose: (value: boolean) => void; filetrs: React.ReactNode }) { + const isDesktop = useMediaQuery('(min-width: 1025px)') - const isDesktop = useMediaQuery('(min-width: 1025px)'); + if (isDesktop) { + return ( + <Stack spacing={1} width='100%' height='auto' className='pd-30 bg-color-block border-radius-main'> + {props.filetrs} + </Stack> + ) + } - if (isDesktop) { - return ( - <Stack - spacing={1} - width='100%' - height='auto' - className='pd-30 bg-color-block border-radius-main' - > - {props.filetrs} - </Stack> - ) - } - - return ( - <DrawerCustom open={props.open} onClose={() => props.onClose(false)}> - <Stack spacing={1} padding={2.4}> - {props.filetrs} - </Stack> - </DrawerCustom> - ) - -} \ No newline at end of file + return ( + <DrawerCustom open={props.open} onClose={() => props.onClose(false)}> + <Stack spacing={1} padding={2.4}> + {props.filetrs} + </Stack> + </DrawerCustom> + ) +} @@ -3,8 +3,8 @@ import { RegisterOptions } from 'react-hook-form/dist/types/validator' import { TextField } from '@mui/material' import { TextFieldProps } from '@mui/material/TextField/TextField' -import { baseColor } from '@/src/shared/lib/constants/colors' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' +import { baseColor } from '#/shared/lib/constants/colors' +import { useThemeAndDevice } from '#/shared/lib/hooks' export const styleInputWithoutBorderFocus = { '& .MuiOutlinedInput-root:hover': { @@ -2,7 +2,7 @@ import * as React from 'react' import Menu from '@mui/material/Menu' import Typography from '@mui/material/Typography' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' interface INotificationMenu { anchorEl: null | HTMLElement @@ -3,7 +3,7 @@ import { Card, CardMedia } from '@mui/material' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' -import { getRandomImage } from '@/src/pages/login' +import { getRandomImage } from '#/pages/login' export const RandomImage = () => { const refImage = React.useRef(getRandomImage()) @@ -4,11 +4,20 @@ import Link from 'next/link' export const ReferralBlock = () => { return ( <Box> - <Box className='pd-30 bg-color-block border-radius-main mt-15' sx={{ width: '100%', backgroundColor: '#8280FF !important' }}> - <Typography className='title-block' sx={{ fontWeight: 600, marginBottom: '20px', color: 'white !important' }} fontSize='16px'> + <Box + className='pd-30 bg-color-block border-radius-main mt-15' + sx={{ width: '100%', backgroundColor: '#8280FF !important' }} + > + <Typography + className='title-block' + sx={{ fontWeight: 600, marginBottom: '20px', color: 'white !important' }} + fontSize='16px' + > Реферальная программа </Typography> - <Typography sx={{ fontSize: '15px', marginTop: '10px', fontWeight: 500, color: '#ffffff !important' }}> + <Typography + sx={{ fontSize: '15px', marginTop: '10px', fontWeight: 500, color: '#ffffff !important' }} + > Получай токены: 20% за подписки твоих рефералов! </Typography> <Link href={'/account?scope=referral'}> @@ -24,7 +33,9 @@ export const ReferralBlock = () => { width: '100%', }} > - <Typography sx={{ marginLeft: '10px', color: '#ffffff', fontSize: 16, fontWeight: 500 }}>Пригласить друга</Typography> + <Typography sx={{ marginLeft: '10px', color: '#ffffff', fontSize: 16, fontWeight: 500 }}> + Пригласить друга + </Typography> </Box> </Link> </Box> @@ -2,8 +2,8 @@ import React from 'react' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import { MenuItem, Select, SelectChangeEvent, Stack, Typography } from '@mui/material' -import { useAppSelector } from '@/src/main/store/store' -import { baseColor } from '@/src/shared/lib/constants/colors' +import { useAppSelector } from '#/app/store/store' +import { baseColor } from '#/shared/lib/constants/colors' interface ISelect { value: any @@ -26,7 +26,9 @@ export const SocialMedia = () => { }} > <Image src={'/svg/TG.svg'} height={20} width={20} alt='Telegram' /> - <Typography sx={{ marginLeft: '10px', color: '#208DD3', fontSize: 16, fontWeight: 500 }}>Telegram</Typography> + <Typography sx={{ marginLeft: '10px', color: '#208DD3', fontSize: 16, fontWeight: 500 }}> + Telegram + </Typography> </Box> </Link> <Link href={'https://vk.com/platform_air'} target='_blank'> @@ -43,7 +45,9 @@ export const SocialMedia = () => { }} > <Image src={'/svg/VK.svg'} height={20} width={20} alt='VK' /> - <Typography sx={{ marginLeft: '10px', color: '#0A5CFF', fontSize: 16, fontWeight: 500 }}>ВКонтакте</Typography> + <Typography sx={{ marginLeft: '10px', color: '#0A5CFF', fontSize: 16, fontWeight: 500 }}> + ВКонтакте + </Typography> </Box> </Link> </Box> @@ -2,7 +2,7 @@ import React, { useMemo } from 'react' import { Alert, Slide, Snackbar, Typography } from '@mui/material' import Image from 'next/image' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' +import { useThemeAndDevice } from '#/shared/lib/hooks' interface ISuccess { message: string @@ -23,10 +23,17 @@ export const Success: React.FC<ISuccess> = ({ message, handleClose, open, isErro return ( <Slide direction='down' in={open} mountOnEnter unmountOnExit> - <Snackbar anchorOrigin={{ vertical: 'top', horizontal: 'right' }} open={open} autoHideDuration={6000} onClose={handleClose}> + <Snackbar + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + open={open} + autoHideDuration={6000} + onClose={handleClose} + > <Alert iconMapping={{ - success: <Image src={WidgetIcon} style={{ marginTop: 3 }} width={20} alt={''} height={20} />, + success: ( + <Image src={WidgetIcon} style={{ marginTop: 3 }} width={20} alt={''} height={20} /> + ), }} severity='success' sx={ @@ -5,16 +5,27 @@ import Link from 'next/link' export const TelegramBlock = () => { return ( <Box> - <Box className='pd-30 bg-color-block border-radius-main mt-15' sx={{ width: '100%', backgroundColor: '#8280FF !important' }}> + <Box + className='pd-30 bg-color-block border-radius-main mt-15' + sx={{ width: '100%', backgroundColor: '#8280FF !important' }} + > <Image src={'/settings.svg'} height={47} width={50} alt='Telegram' /> - <Typography sx={{ fontSize: '20px', marginTop: '10px', fontWeight: 500, color: 'white !important' }}> + <Typography + sx={{ fontSize: '20px', marginTop: '10px', fontWeight: 500, color: 'white !important' }} + > Бесплатные токены </Typography> - <Typography sx={{ fontSize: '15px', marginTop: '10px', fontWeight: 500, color: 'white !important' }}> + <Typography + sx={{ fontSize: '15px', marginTop: '10px', fontWeight: 500, color: 'white !important' }} + > Получите 5 токенов за подписку на наш телеграм канал! </Typography> <Link href={'https://t.me/air_platform_bot'} target='_blank'> - <Typography sx={{ marginTop: '20px', fontSize: 16, fontWeight: 500, color: 'white !important' }}>Подписаться</Typography> + <Typography + sx={{ marginTop: '20px', fontSize: 16, fontWeight: 500, color: 'white !important' }} + > + Подписаться + </Typography> </Link> </Box> </Box> @@ -27,5 +27,5 @@ export { Success } from './ui/success' export { SwitchCustom } from './ui/switch/switch' export { TooltipCustom } from './ui/tooltip/tooltip' export { TooltipFreeTokens } from './ui/tooltip-free-tokens' -export { useModel } from '@/src/shared/api/models/endpoints' -export type { ChatProps, FiltersChatProps } from '@/src/shared/lib/types/model' +export { useModel } from '#/shared/api/models/endpoints' +export type { ChatProps, FiltersChatProps } from '#/shared/lib/types/model' @@ -0,0 +1,42 @@ +import * as React from 'react' +import { useEffect, useState } from 'react' +import { Box, Typography } from '@mui/material' +import { useSession } from 'next-auth/react' +import { Card } from '#/shared/card/card' +import { AudioModel } from '#/app/types/model' +import { getAudio } from '#/entities/audio-model' +import { NextPageWithLayout } from '#/pages/_app' + +export const AudioModelsPage: NextPageWithLayout = () => { + const [bots, setBots] = useState<AudioModel[] | null>(null) + + const { data, status } = useSession() + + useEffect(() => { + if (!data) return + + getAudio(data.access).then((res) => setBots(res)) + }, [status]) + + return ( + <> + <Typography sx={{ fontSize: 24, fontWeight: 'bold', marginTop: '25px' }}>Аудио</Typography> + <Box display='flex' flexWrap='wrap'> + {bots?.map((item, idx) => { + return ( + <Card + uid={item.uid} + key={item.uid} + title={item.title} + icon={item.image_link} + text={item.description} + slug={item.slug} + /> + ) + })} + </Box> + </> + ) +} + +export default AudioModelsPage @@ -0,0 +1 @@ +export { default as AudioModelsPage } from './audio-models' @@ -0,0 +1 @@ +export * from './ui' \ No newline at end of file @@ -0,0 +1,380 @@ +import React, { useCallback, useEffect } from 'react' +import { useDispatch } from 'react-redux' +import { Box, Collapse, Stack, Typography } from '@mui/material' +import { useRouter } from 'next/router' +import { useSession } from 'next-auth/react' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' + +import BotParamsMap from '#/features/bot-params/bot-params-map' +import { useChats } from '#/features/chats' +import Title from '#/features/title/title' +import { TutorialContext } from '#/features/tutorial-context/tutorial-context' +import { ChatSelect } from '#/app/components/chat_select' +import { AllChatWindow } from '#/app/components/chat_window' +import { ResetFilters } from '#/app/components/filters/reset_filters' +import { setParams as setParametres } from '#/app/store/model-parametres-store' +import { useAppSelector } from '#/app/store/store' +import { DrawerCustom, Error, useModel, useShowData } from '#/shared' +import model_api from '#/shared/api/models/api' +import { IModel } from '#/shared/api/models/models' +import styles from '#/shared/styles/chats-bot-pages.module.scss' +import { Chats } from '#/widgets/chats' +import { getDeviceType, getOs } from '#/shared/lib/helpers' +import { NextPageWithLayout } from '#/pages/_app' +import { getDefaultLayoutDynamic } from '#/widgets/layouts' +import Head from 'next/head' + +export interface ChatBotPageProps {} + +const Page: NextPageWithLayout<ChatBotPageProps> = () => { + const [botParams, setBotParams] = React.useState<IModel | null>(null) + const [modelType, setModelType] = React.useState<string>('') + const [openFiltersMobile, setOpenFiltersMobile] = React.useState(false) + const [version, setVersion] = React.useState<string>('') + const [step, setStep] = React.useState<number>(0) + + const [params, setParams] = React.useState<boolean>(false) + const [file, setFile] = React.useState<File | null>(null) + const { error, showError } = useShowData() + + const deviceType = getDeviceType() + const deviceOs = getOs() + + const desktop = deviceType === 'desktop' + const { data } = useSession() + const router = useRouter() + + const { + chats, + currentChat, + chatSetting, + setChatSetting, + setChat, + removeChat, + createNewChat, + handleClickChatSetting, + isTryRename, + setIsTryRename, + } = useChats(modelType) + + const { messages, sendMessage, loading, getMessagesPagination, deleteMessage } = useModel( + currentChat, + showError, + modelType + ) + + const includeParams = useAppSelector((state) => state.params.params) + const dispatch = useDispatch() + + const deleteMessageMemo = useCallback(deleteMessage, [currentChat, messages]) + + React.useEffect(() => { + if (data?.access) { + model_api.getBotParams(router.asPath.split('/')[2], data.access).then((res) => { + setBotParams(res) + setModelType(res.slug) + if (res.versions.length !== 0) { + setVersion(res.versions[0].slug) + dispatch( + setParametres( + res.parameters.reduce( + (a, v) => + v.versions.includes(res.versions[0].slug) + ? { ...a, [v.key]: v.values.default } + : { ...a }, + {} + ) + ) + ) + } else { + setVersion('') + dispatch( + setParametres(res.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {})) + ) + } + }) + } + }, [data?.access, router.query]) + + const resetParams = () => { + if (botParams) { + dispatch(setParametres({})) + if (botParams.versions?.length !== 0) { + setVersion(botParams.versions[0].slug) + dispatch( + setParametres( + botParams.parameters.reduce( + (a, v) => + v.versions.includes(botParams.versions[0].slug) + ? { ...a, [v.key]: v.values.default } + : { ...a }, + {} + ) + ) + ) + } else { + setVersion(botParams.slug) + dispatch( + setParametres(botParams.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {})) + ) + } + } + } + + const setDefaultParams = () => { + if (botParams) { + dispatch(setParametres({})) + if (version !== '') { + dispatch( + setParametres( + botParams.parameters.reduce( + (a, v) => + v.versions.includes(version) ? { ...a, [v.key]: v.values.default } : { ...a }, + {} + ) + ) + ) + } else { + dispatch( + setParametres(botParams.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {})) + ) + } + } + } + + const viewMobileSettings = () => { + setOpenFiltersMobile(true) + } + + const hideMobileSettings = () => { + setOpenFiltersMobile(false) + } + + const onSendMessage = (input: string, required: (string | null)[]) => { + if (required.includes('text') && (input === '' || input === null)) { + showError('Введите сообщение!') + return false + } + if (required.includes('image') && file === null) { + showError('Прикрепите изображение!') + return false + } + + let data = {} + if (version === '') { + data = { + ...includeParams, + } + } else { + data = { + version: version, + ...includeParams, + } + } + + sendMessage<any>({ + content: input, + file: file, + info: { + ...data, + }, + }) + return true + } + + return ( + <> + <Head> + <title>{botParams ? botParams.title : 'Загрузка...'} + + + + <Box className={styles.main}> + <Box className={styles.chatWindow}> + <AllChatWindow + deviceOs={deviceOs} + input_types={botParams?.inputs} + setFile={setFile} + file={file} + getMessagesPagination={getMessagesPagination} + sendMessage={onSendMessage} + deleteMessage={deleteMessageMemo} + openMobileFilters={viewMobileSettings} + isCalculating={true} + device={deviceType} + loading={loading} + messages={messages || []} + modelType={modelType} + modelTitle={botParams?.title} + currentVersion={version} + /> + </Box> + <Box className={styles.settings}> + <Chats + desktop={desktop} + isTryRename={isTryRename} + setIsTryRename={setIsTryRename} + currentChat={currentChat} + setChat={setChat} + chats={chats} + chatSetting={chatSetting} + setChatSetting={setChatSetting} + removeChat={removeChat} + createNewChat={createNewChat} + handleClickChatSetting={handleClickChatSetting} + /> + {desktop && ( + <Stack className='pd-30 bg-color-block border-radius-main' spacing={2}> + {botParams?.versions && botParams.versions?.length !== 0 && ( + <> + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + }} + > + ВЕРСИИ + </Typography> + <ChatSelect + setDefaultParams={setDefaultParams} + value={version} + list={botParams.versions} + setValue={setVersion} + /> + </> + )} + {botParams && botParams.parameters?.length > 0 && ( + <Box + display={'flex'} + alignItems={'center'} + gap={'5px'} + sx={{ cursor: 'pointer' }} + onClick={() => { + setParams(!params) + }} + > + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + }} + > + ПАРАМЕТРЫ + </Typography> + <svg + width='14' + height='20' + viewBox='0 0 21 13' + fill='none' + xmlns='http://www.w3.org/2000/svg' + className={`${params ? 'rotate-180' : 'rotate-0'}`} + > + <path + fillRule='evenodd' + clipRule='evenodd' + d='M0.614851 0.615358C1.00866 0.221668 1.54271 0.000505666 2.09955 0.000505642C2.6564 0.000505617 3.19044 0.221668 3.58425 0.615357L10.4996 7.53066L17.4149 0.615357C17.8109 0.232825 18.3414 0.0211567 18.892 0.0259414C19.4426 0.0307261 19.9693 0.25158 20.3587 0.640937C20.748 1.03029 20.9689 1.557 20.9737 2.10761C20.9784 2.65823 20.7668 3.18869 20.3842 3.58476L11.9843 11.9848C11.5904 12.3784 11.0564 12.5996 10.4996 12.5996C9.94271 12.5996 9.40866 12.3784 9.01485 11.9848L0.614851 3.58476C0.221162 3.19095 -4.3461e-07 2.6569 -4.5895e-07 2.10006C-4.8329e-07 1.54321 0.221162 1.00917 0.614851 0.615358Z' + fill='#7f7df3' + /> + </svg> + </Box> + )} + + {botParams && botParams.parameters?.length > 0 ? ( + <Collapse in={params} orientation='vertical' collapsedSize={0} sx={{}}> + <BotParamsMap + currentVersion={version} + params={botParams?.parameters} + /> + <ResetFilters + desktop={desktop} + closeDrawer={hideMobileSettings} + reset={resetParams} + /> + </Collapse> + ) : ( + <Typography + sx={{ + color: '#6e6e6e', + fontSize: '15px', + fontWeight: '500', + }} + > + Параметры отсутствуют + </Typography> + )} + </Stack> + )} + {!desktop && ( + <DrawerCustom open={openFiltersMobile} onClose={hideMobileSettings}> + <Stack spacing={1} padding={2.4}> + {botParams?.versions && botParams.versions?.length !== 0 && ( + <> + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + margin: '20px 0px 0px !important', + }} + > + ВЕРСИИ + </Typography> + <ChatSelect + setDefaultParams={setDefaultParams} + value={version} + list={botParams.versions} + setValue={setVersion} + /> + </> + )} + {botParams && botParams.parameters?.length > 0 ? ( + <> + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + margin: '20px 0px 0px !important', + }} + > + ПАРАМЕТРЫ + </Typography> + <BotParamsMap + currentVersion={version} + params={botParams?.parameters} + /> + <ResetFilters + closeDrawer={hideMobileSettings} + desktop={desktop} + reset={resetParams} + /> + </> + ) : ( + <Typography + sx={{ + color: '#6e6e6e', + fontSize: '15px', + fontWeight: '500', + }} + > + Параметры отсутствуют + </Typography> + )} + </Stack> + </DrawerCustom> + )} + </Box> + <Error error={error} open={Boolean(error)} /> + </Box> + </TutorialContext.Provider> + </> + ) +} + +export default Page @@ -0,0 +1,65 @@ +import * as React from 'react' +import { useEffect, useState } from 'react' +import { Box, Typography } from '@mui/material' +import CircularProgress from '@mui/material/CircularProgress' +import { useSession } from 'next-auth/react' + +import ChatCard from '#/app/components/chat_bot_card' +import { useAppSelector } from '#/app/store/store' +import model_api from '#/shared/api/models/api' +import { IShortModel } from '#/shared/api/models/models' +import { NextPageWithLayout } from '#/pages/_app' + +const Page: NextPageWithLayout = () => { + const [bots, setBots] = useState<IShortModel[] | null>(null) + const { data } = useSession() + const user = useAppSelector((state) => state.user) + + useEffect(() => { + if (!data?.access) { + return + } + model_api + .getBots(data?.access) + .then((res) => { + setBots(res) + }) + .catch((err) => {}) + }, [data?.access]) + + return ( + <> + <Typography sx={{ fontSize: 24, fontWeight: 'bold', marginTop: '25px' }}>Чат-боты</Typography> + <Box display='flex' flexWrap='wrap' width='100%' justifyContent='start'> + {bots ? ( + bots.map((item, idx) => ( + <ChatCard + accessed_models={user.payment_plan.plan.accessed_models} + uid={item.uid} + key={idx} + title={item.title} + icon={item.image} + text={item.description} + slug={item.slug} + /> + )) + ) : ( + <CircularProgress + size={50} + thickness={3} + sx={{ + color: '#7F7DF3', + position: 'absolute', + top: '40%', + left: 0, + right: 0, + margin: '0 auto', + }} + /> + )} + </Box> + </> + ) +} + +export default Page @@ -0,0 +1,2 @@ +export { default as ChatBotsPage } from './chat-bots' +export { default as ChatBotPage } from './chat-bot' @@ -0,0 +1 @@ +export * from './ui' \ No newline at end of file @@ -0,0 +1,523 @@ +import * as React from 'react' +import { useRef } from 'react' +import { useDispatch } from 'react-redux' +import { Collapse, Typography } from '@mui/material' +import Box from '@mui/material/Box' +import Stack from '@mui/material/Stack' +import { useRouter } from 'next/router' +import { useSession } from 'next-auth/react' + +import { Setting } from '#/domains/images-bots/babes/types' +import BotParamsMap from '#/features/bot-params/bot-params-map' +import Title from '#/features/title/title' +import { ChatSelect } from '#/app/components/chat_select' +import { ResetFilters } from '#/app/components/filters/reset_filters' +import { UniqInput } from '#/app/components/uniq_input' +import { setParams as setParametres } from '#/app/store/model-parametres-store' +import { useAppSelector } from '#/app/store/store' +import { DrawerCustom, Error } from '#/shared' +import model_api from '#/shared/api/models/api' +import { useModelImages } from '#/shared/api/models/endpoints' +import { IModel } from '#/shared/api/models/models' +import { useShowData } from '#/shared/lib/hooks' +import { ArrowDownScroll } from '#/shared/ui/icon-components/scroll-down-arrow' +import { ImageMessagesList } from '#/widgets/messages/image-messages-list' +import { getDeviceType, getOs } from '#/shared/lib/helpers' +import Head from 'next/head' +import { NextPageWithLayout } from '#/pages/_app' + +const ImageModelPage: NextPageWithLayout = () => { + const [image, setImage] = React.useState<File | null>(null) + + const deviceType = getDeviceType() + const deviceOs = getOs() + + const desktop = deviceType === 'desktop' + const ios = deviceOs === 'ios' + + const { error, showError } = useShowData() + const [openFiltersMobile, setOpenFiltersMobile] = React.useState<boolean>(false) + const router = useRouter() + const { data } = useSession() + const [botParams, setBotParams] = React.useState<IModel | null>(null) + const [version, setVersion] = React.useState<string>('') + const [modelType, setModelType] = React.useState<string>('') + const [params, setParams] = React.useState<boolean>(false) + const includeParams = useAppSelector((state) => state.params.params) + const dispatch = useDispatch() + + const { messages, loading, createImage, isComplete, getMessagesPagination } = useModelImages<Setting>( + showError, + modelType, + deviceType + ) + + const [chatScrollHeight, setChatScrollHeight] = React.useState(0) + const [scrollBottom, setScrollBottom] = React.useState(0) + const refScrollMobile = useRef<any>() + const [isPaginating, setIsPaginating] = React.useState(false) + + React.useEffect(() => { + model_api + .getBotParams(router.asPath.split('/')[2], data?.access) + .then((res) => { + setBotParams(res) + setModelType(res.slug) + if (res.versions.length !== 0) { + setVersion(res.versions[0].slug) + dispatch( + setParametres( + res.parameters.reduce( + (a, v) => + v.versions.includes(res.versions[0].slug) + ? { ...a, [v.key]: v.values.default } + : { ...a }, + {} + ) + ) + ) + } else { + setVersion('') + dispatch( + setParametres(res.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {})) + ) + } + }) + .catch((err) => {}) + }, [data?.access, router.query]) + + const onLoadImage = (event: React.ChangeEvent<HTMLInputElement>) => { + if (event.target.files) { + setImage(event.target.files[0]) + // showError('Файл успешно загружен, можете отправлять его!') + } + } + + const viewMobileSettings = () => { + setOpenFiltersMobile(true) + } + + const hideMobileSettings = () => { + setOpenFiltersMobile(false) + } + + const resetParams = () => { + if (botParams) { + dispatch(setParametres({})) + if (botParams.versions.length !== 0) { + setVersion(botParams.versions[0].slug) + dispatch( + setParametres( + botParams.parameters.reduce( + (a, v) => + v.versions.includes(botParams.versions[0].slug) + ? { ...a, [v.key]: v.values.default } + : { ...a }, + {} + ) + ) + ) + } else { + setVersion(botParams.slug) + dispatch( + setParametres(botParams.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {})) + ) + } + } + } + + const setDefaultParams = () => { + if (botParams) { + dispatch(setParametres({})) + if (version !== '') { + dispatch( + setParametres( + botParams.parameters.reduce( + (a, v) => + v.versions.includes(version) ? { ...a, [v.key]: v.values.default } : { ...a }, + {} + ) + ) + ) + } else { + dispatch( + setParametres(botParams.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {})) + ) + } + } + } + + const onCreateImage = (input: string, required: (string | null)[]) => { + if (required.includes('text') && (input === '' || input === null)) { + showError('Введите сообщение!') + return false + } + if (required.includes('image') && image === null) { + showError('Прикрепите изображение!') + return false + } + if (required.includes('zip') && image === null) { + showError('Прикрепите архив!') + return false + } + let data = {} + if (version === '') { + data = { + ...includeParams, + } + } else { + data = { + version: version, + ...includeParams, + } + } + + createImage({ + content: input, + file: image, + info: { + ...data, + }, + }) + return true + } + + React.useEffect(() => { + if (isComplete) { + if (!desktop) { + const block = refScrollMobile.current + if (block) { + //@ts-ignore + block.scrollTop = block.scrollHeight + } + } else { + window.scroll(0, 0) + } + } + }, [isComplete]) + + const handleMobileScroll = () => { + setScrollBottom( + refScrollMobile.current?.scrollHeight - + refScrollMobile.current?.scrollTop - + refScrollMobile.current?.clientHeight + ) + + if (refScrollMobile.current && messages?.length !== 0) { + const { scrollTop, scrollHeight, clientHeight } = refScrollMobile.current + if (scrollTop === 0) { + if (getMessagesPagination) { + setIsPaginating(true) + getMessagesPagination(deviceType) + } + } + } + } + + const handleScroll = () => { + if (window.scrollY + window.innerHeight >= document.documentElement.scrollHeight) { + setIsPaginating(true) + getMessagesPagination(deviceType) + } + } + + React.useEffect(() => { + window.addEventListener('scroll', handleScroll) + return () => { + window.removeEventListener('scroll', handleScroll) + } + }, []) + + React.useEffect(() => { + const block = deviceType === 'desktop' ? window : refScrollMobile.current + if (block) { + if (messages != undefined && !isPaginating) { + setChatScrollHeight(block.scrollHeight) + const time = setTimeout(() => { + //@ts-ignore + block.scrollTo({ + top: block.scrollHeight, + behavior: 'smooth', // добавляем плавную прокрутку + }) + }, 350) + return () => clearTimeout(time) + } else if (messages != undefined && isPaginating) { + //@ts-ignore + block.scrollTop = block.scrollHeight - chatScrollHeight + + setChatScrollHeight(block.scrollHeight) + } + } + setIsPaginating(false) + }, [messages]) + + return ( + <> + <Head> + <title>{botParams ? botParams.title : 'Загрузка...'} + + + <Box + display={'flex'} + justifyContent='space-between' + alignItems='start' + flexDirection={desktop ? 'row' : 'column-reverse'} + sx={{ + marginBottom: desktop ? 0 : 3, + width: desktop ? '97%' : '100%', + marginTop: desktop ? 3 : '15px', + }} + > + <Box + sx={{ + width: desktop ? '73%' : '100%', + marginLeft: 0, + display: 'flex', + flexDirection: desktop ? 'column' : 'column-reverse', + }} + > + {desktop ? ( + <> + <Stack alignItems='center' sx={{ marginBottom: '15px' }}> + {botParams && ( + <UniqInput + currentVersion={version} + styles={'images'} + input_types={botParams.inputs} + image={image} + value={prompt} + desktop={desktop} + loading={loading} + imageLoad={onLoadImage} + sendMessage={onCreateImage} + unpinImage={() => setImage(null)} + viewMobileSettings={viewMobileSettings} + /> + )} + </Stack> + <Box className={'bg-color-block border-radius-main'} sx={{ padding: '30px' }}> + <ImageMessagesList + isComplete={isComplete} + device={deviceType} + images={messages} + /> + </Box> + </> + ) : ( + <Box className={'bg-color-block border-radius-main'}> + {scrollBottom > 500 && ( + <Box + sx={{ + position: 'absolute', + left: 0, + right: 0, + width: 'fit-content', + cursor: 'pointer', + margin: '0 auto', + bottom: '100px', + zIndex: 10, + }} + onClick={() => { + const block = refScrollMobile.current + + block.scrollTo({ + top: block.scrollHeight, + behavior: 'smooth', // добавляем плавную прокрутку + }) + }} + > + <ArrowDownScroll /> + </Box> + )} + <Box + ref={refScrollMobile} + sx={{ + padding: '30px', + height: ios + ? 'calc(200px + (400 - 200) * ((100vh - 400px) / (600 - 400)) - 100px)' + : 'calc(200px + (400 - 200) * ((100vh - 400px) / (600 - 400)))', + overflowY: 'scroll', + overflowX: 'hidden', + }} + className={'smallScroll'} + onScroll={handleMobileScroll} + > + <ImageMessagesList + isComplete={isComplete} + device={deviceType} + images={messages} + /> + </Box> + <Stack alignItems='center'> + {botParams && ( + <UniqInput + currentVersion={version} + styles={'images'} + input_types={botParams.inputs} + image={image} + value={prompt} + desktop={desktop} + loading={loading} + imageLoad={onLoadImage} + sendMessage={onCreateImage} + unpinImage={() => setImage(null)} + viewMobileSettings={viewMobileSettings} + /> + )} + </Stack> + </Box> + )} + </Box> + {desktop && ( + <Stack + spacing={2} + className='pd-30 bg-color-block border-radius-main' + sx={{ width: '25.5%', height: 'auto' }} + > + {botParams?.versions && botParams.versions.length !== 0 ? ( + <> + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + }} + > + ВЕРСИИ + </Typography> + <ChatSelect + setDefaultParams={setDefaultParams} + value={version} + list={botParams.versions} + setValue={setVersion} + /> + </> + ) : ( + <></> + )} + {botParams && botParams.parameters?.length > 0 && ( + <Box + display={'flex'} + alignItems={'center'} + gap={'5px'} + sx={{ cursor: 'pointer' }} + onClick={() => { + setParams(!params) + }} + > + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + }} + > + ПАРАМЕТРЫ + </Typography> + <svg + width='14' + height='20' + viewBox='0 0 21 13' + fill='none' + xmlns='http://www.w3.org/2000/svg' + className={`${params ? 'rotate-180' : 'rotate-0'}`} + > + <path + fillRule='evenodd' + clipRule='evenodd' + d='M0.614851 0.615358C1.00866 0.221668 1.54271 0.000505666 2.09955 0.000505642C2.6564 0.000505617 3.19044 0.221668 3.58425 0.615357L10.4996 7.53066L17.4149 0.615357C17.8109 0.232825 18.3414 0.0211567 18.892 0.0259414C19.4426 0.0307261 19.9693 0.25158 20.3587 0.640937C20.748 1.03029 20.9689 1.557 20.9737 2.10761C20.9784 2.65823 20.7668 3.18869 20.3842 3.58476L11.9843 11.9848C11.5904 12.3784 11.0564 12.5996 10.4996 12.5996C9.94271 12.5996 9.40866 12.3784 9.01485 11.9848L0.614851 3.58476C0.221162 3.19095 -4.3461e-07 2.6569 -4.5895e-07 2.10006C-4.8329e-07 1.54321 0.221162 1.00917 0.614851 0.615358Z' + fill='#7f7df3' + /> + </svg> + </Box> + )} + + {botParams && botParams.parameters?.length > 0 ? ( + <Collapse in={params} orientation='vertical' collapsedSize={0} sx={{}}> + <BotParamsMap currentVersion={version} params={botParams?.parameters} /> + <ResetFilters + desktop={desktop} + closeDrawer={hideMobileSettings} + reset={resetParams} + /> + </Collapse> + ) : ( + <Typography + sx={{ + color: '#6e6e6e', + fontSize: '15px', + fontWeight: '500', + }} + > + Параметры отсутствуют + </Typography> + )} + </Stack> + )} + <DrawerCustom open={openFiltersMobile} onClose={hideMobileSettings}> + <Stack spacing={1} padding={2.4}> + {botParams?.versions && botParams.versions.length !== 0 ? ( + <> + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + margin: '20px 0px 0px !important', + }} + > + ВЕРСИИ + </Typography> + <ChatSelect + setDefaultParams={setDefaultParams} + value={version} + list={botParams.versions} + setValue={setVersion} + /> + </> + ) : ( + <></> + )} + {botParams && botParams.parameters?.length > 0 ? ( + <> + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + margin: '20px 0px 0px !important', + }} + > + ПАРАМЕТРЫ + </Typography> + <BotParamsMap currentVersion={version} params={botParams?.parameters} /> + <ResetFilters + closeDrawer={hideMobileSettings} + desktop={desktop} + reset={resetParams} + /> + </> + ) : ( + <Typography + sx={{ + color: '#6e6e6e', + fontSize: '15px', + fontWeight: '500', + }} + > + Параметры отсутствуют + </Typography> + )} + </Stack> + </DrawerCustom> + <Error error={error} open={Boolean(error)} /> + </Box> + </> + ) +} + +export default ImageModelPage @@ -0,0 +1,61 @@ +import * as React from 'react' +import { useEffect, useState } from 'react' +import { Box, Typography } from '@mui/material' +import CircularProgress from '@mui/material/CircularProgress' +import { useSession } from 'next-auth/react' + +import { useAppSelector } from '#/app/store/store' +import model_api from '#/shared/api/models/api' +import { IShortModel } from '#/shared/api/models/models' +import { Card } from '#/shared/card/card' +import { NextPageWithLayout } from '#/pages/_app' + +export const ImageModelsPage: NextPageWithLayout = () => { + const [bots, setBots] = useState<IShortModel[] | null>(null) + const user = useAppSelector((state) => state.user) + const { data, status } = useSession() + + useEffect(() => { + if (!data) return + + model_api.getImages(data.access).then((res) => setBots(res)) + }, [status]) + + return ( + <> + <Typography sx={{ fontSize: 24, fontWeight: 'bold', marginTop: '25px' }}>Изображения</Typography> + <Box display='flex' flexWrap='wrap'> + {bots ? ( + bots.map((item, idx) => { + return ( + <Card + accessed_models={user.payment_plan.plan.accessed_models} + uid={item.uid} + key={idx} + slug={item.slug} + text={item.description} + title={item.title} + icon={item.image} + /> + ) + }) + ) : ( + <CircularProgress + size={50} + thickness={3} + sx={{ + color: '#7F7DF3', + position: 'absolute', + top: '40%', + left: 0, + right: 0, + margin: '0 auto', + }} + /> + )} + </Box> + </> + ) +} + +export default ImageModelsPage @@ -0,0 +1,2 @@ +export { default as ImageModelsPage } from './image-models' +export { default as ImageModelPage } from './image-model' @@ -0,0 +1 @@ +export * from './ui' \ No newline at end of file @@ -0,0 +1 @@ +export { default as MainPage } from './main' @@ -0,0 +1,66 @@ +import * as React from 'react' +import { useMemo } from 'react' +import { Box, Stack, Typography } from '@mui/material' + +import { useAppSelector } from '#/app/store/store' +import styles from '#/app/styles/styles-pages/index-styles.module.scss' +import { ReferralBlock } from '#/shared' +import { getCurrentTimeAndGreeting } from '#/shared/lib/helpers/getCurrentDateForMainScreen' + +import { DemoInfo } from '#//shared/ui/demo-info' +import { SocialMedia } from '#//shared/ui/social-media-block' +import { Statistics } from '#//widgets/stats/stats' +import { NextPageWithLayout } from '#//pages/_app' +import { getDeviceType } from '#//shared/lib/helpers/get-type-device' + +export interface MainPageProps {} + +const Main: NextPageWithLayout<MainPageProps> = () => { + const { first_name, account_type } = useAppSelector((state) => state.user) + + const deviceType = getDeviceType() + + const [greeting, time] = useMemo(() => getCurrentTimeAndGreeting(first_name), [first_name]) + + return ( + <Box width='100%'> + <Box className={styles.wrap}> + <Typography className='title-main-gray'>{time}</Typography> + <Typography + sx={{ + fontSize: deviceType === 'desktop' ? '28px' : '24px', + fontWeight: 600, + }} + > + {greeting} + </Typography> + <Box className={styles.main}> + <Statistics deviceType={deviceType} /> + <Stack className={deviceType === 'desktop' ? styles.side_desk : styles.side_mobile}> + <DemoInfo /> + {account_type === 'regular' && <ReferralBlock />} + <SocialMedia /> + <Stack className='pd-30 bg-color-block border-radius-main mt-15' spacing={2}> + <Typography className='title-block'>Полезная статья</Typography> + <Box> + <Typography className='title-vspomogatel' sx={{ fontSize: '21px !important' }}> + {' '} + Что такое токен ? + </Typography> + <Typography className='text' sx={{ marginTop: '10px' }}> + Токен — это цифровая валюта для оплаты ИИ-продуктов и услуг. Юзеры + используют эту валюту, чтобы получить либо доступ к нейросетям, либо + результат генерации — текст, картинку и т.д. Еще токенами называют единицы + текста, на которых обучается нейросеть. Например, в предложении «‎Я люблю + шоколад» слова «‎Я», «‎люблю» и «шоколад» могут быть токенами. + </Typography> + </Box> + </Stack> + </Stack> + </Box> + </Box> + </Box> + ) +} + +export default Main @@ -0,0 +1 @@ +export * from './ui' @@ -1,5 +1,4 @@ .screenWrap { - } .screen { @@ -3,14 +3,20 @@ import { Box, Typography } from '@mui/material' import Image from 'next/image' import Link from 'next/link' -import { ButtonUI } from '@/src/shared' +import { ButtonUI } from '#/shared' import styles from './screen-for-inactive.module.scss' export const ScreenForInactive = () => { return ( <Box className={styles.screenWrap}> <Box className={styles.screen}> - <Image className={styles.screenIcon} src={'/svg/business/profile.svg'} width={31} height={46} alt={''} /> + <Image + className={styles.screenIcon} + src={'/svg/business/profile.svg'} + width={31} + height={46} + alt={''} + /> <Typography className={styles.screenTitle}>Корпоративный аккаунт</Typography> <Typography className={styles.screenDescription}>Для бизнеса и команд</Typography> <Box className={styles.screenIcons}> @@ -5,24 +5,24 @@ import axios from 'axios' import { Dayjs } from 'dayjs' import { useSession } from 'next-auth/react' -import { getAll, ResponseAllInfo } from '@/src/entities/user-account/model/user-type-slice' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' -import { useAppSelector } from '@/src/main/store/store' -import styles from '@/src/main/styles/business.module.scss' -import { InputStyleDark, InputStyleLight } from '@/src/shared' -import { API_URL } from '@/src/shared/lib/constants' -import { DateInput } from '@/src/shared/ui/date-input/date-input' -import { Info } from '@/src/widgets/business-info' -import { getInfo, InfoBusiness } from '@/src/widgets/business-info/api/get-info' -import { ModelsList } from '@/src/widgets/business-models' -import styles2 from '@/src/widgets/business-models/ui/models-list/models-list.module.scss' -import { PersonsList } from '@/src/widgets/business-persons' -import { getPersons } from '@/src/widgets/business-persons/api/get-persons' -import { BusinessGroups } from '@/src/widgets/business-persons/ui/persons-list/business-group' -import { IpList } from '@/src/widgets/business-persons/ui/persons-list/ip-list' -import { LogList } from '@/src/widgets/business-persons/ui/persons-list/log-list' -import { SecurityList } from '@/src/widgets/business-persons/ui/persons-list/security-list' -import ArrowUpOrDown from '@/src/widgets/top-bar-model/ui/arrow-up-or-down' +import { getAll, ResponseAllInfo } from '#/entities/user-account/model/user-type-slice' +import { ResponseGetPersons } from '#/features/invite-person-in-business' +import { useAppSelector } from '#/app/store/store' +import styles from '#/app/styles/business.module.scss' +import { InputStyleDark, InputStyleLight } from '#/shared' +import { API_URL } from '#/shared/lib/constants' +import { DateInput } from '#/shared/ui/date-input/date-input' +import { Info } from '#/widgets/business-info' +import { getInfo, InfoBusiness } from '#/widgets/business-info/api/get-info' +import { ModelsList } from '#/widgets/business-models' +import styles2 from '#/widgets/business-models/ui/models-list/models-list.module.scss' +import { PersonsList } from '#/widgets/business-persons' +import { getPersons } from '#/widgets/business-persons/api/get-persons' +import { BusinessGroups } from '#/widgets/business-persons/ui/persons-list/business-group' +import { IpList } from '#/widgets/business-persons/ui/persons-list/ip-list' +import { LogList } from '#/widgets/business-persons/ui/persons-list/log-list' +import { SecurityList } from '#/widgets/business-persons/ui/persons-list/security-list' +import ArrowUpOrDown from '#/widgets/top-bar-model/ui/arrow-up-or-down' type ListType = 'personal' | 'security' @@ -63,10 +63,13 @@ export default function BusinessHost() { } const download = () => { - axios.get(API_URL + `/auth/business-host/download-expenses?type=employees&from_date=${fromDate}&to_date=${toDate}`, { - responseType: 'arraybuffer', - headers: { Authorization: `Bearer ${data?.access}` }, - }).then((res) => { + axios.get( + API_URL + `/auth/business-host/download-expenses?type=employees&from_date=${fromDate}&to_date=${toDate}`, + { + responseType: 'arraybuffer', + headers: { Authorization: `Bearer ${data?.access}` }, + } + ).then((res) => { const file = new Blob([res.data], { type: 'application/ms-excel;charset=utf-8', }) @@ -80,7 +83,11 @@ export default function BusinessHost() { } useEffect(() => { - axios.put(API_URL + '/auth/business-host', { token_cap_enabled: mailing }, { headers: { Authorization: `Bearer ${data?.access}` } }) + axios.put( + API_URL + '/auth/business-host', + { token_cap_enabled: mailing }, + { headers: { Authorization: `Bearer ${data?.access}` } } + ) }, [mailing]) return ( @@ -95,7 +102,11 @@ export default function BusinessHost() { <Box className={styles2.title2}> <Box> <Typography className={styles.name}>Затраты</Typography> - <ArrowUpOrDown directionY={showPersons} onClick={() => setShowPersons((prev) => !prev)} className={styles2.arrow} /> + <ArrowUpOrDown + directionY={showPersons} + onClick={() => setShowPersons((prev) => !prev)} + className={styles2.arrow} + /> </Box> </Box> {showPersons && ( @@ -148,7 +159,11 @@ const MailingBlock = ({ info, mailing }: { info: InfoBusiness | null | undefined } const req = async (mailingArr: string[]) => { - await axios.put(API_URL + '/auth/business-host', { token_cap_emails: mailingArr }, { headers: { Authorization: `Bearer ${data?.access}` } }) + await axios.put( + API_URL + '/auth/business-host', + { token_cap_emails: mailingArr }, + { headers: { Authorization: `Bearer ${data?.access}` } } + ) } return ( @@ -175,7 +190,11 @@ const MailingBlock = ({ info, mailing }: { info: InfoBusiness | null | undefined placeholder={'Введите почту'} onChange={(e) => setNewMail(e.target.value)} fullWidth - sx={theme === 'light' ? { ...InputStyleLight } : { ...InputStyleDark }} + sx={ + theme === 'light' + ? { ...InputStyleLight } + : { ...InputStyleDark } + } /> </Box> @@ -1,6 +1,6 @@ import axios from 'axios' -import { API_URL } from '@/src/shared/lib/constants' +import { API_URL } from '#/shared/lib/constants' export type InfoBusiness = { company_name: string @@ -4,10 +4,10 @@ import Switch from '@mui/material/Switch' import axios from 'axios' import { useSession } from 'next-auth/react' -import { ResponseAllInfo } from '@/src/entities/user-account/model/user-type-slice' -import { useAppSelector } from '@/src/main/store/store' -import { Balance } from '@/src/shared' -import { API_URL } from '@/src/shared/lib/constants' +import { ResponseAllInfo } from '#/entities/user-account/model/user-type-slice' +import { useAppSelector } from '#/app/store/store' +import { Balance } from '#/shared' +import { API_URL } from '#/shared/lib/constants' import { getInfo, InfoBusiness } from '../../api/get-info' @@ -52,7 +52,13 @@ export const Info = ({ <Box className={styles.wrap}> <Box className={styles.left}> {!security ? <Typography className={styles.title}>{data?.company_name}</Typography> : <></>} - <Typography className={styles.role} sx={{ fontSize: !security ? '17px' : '21px', fontWeight: !security ? '400' : '600' }}> + <Typography + className={styles.role} + sx={{ + fontSize: !security ? '17px' : '21px', + fontWeight: !security ? '400' : '600', + }} + > Роль:{' '} {type === 'business_host' ? 'Владелец' @@ -65,7 +71,9 @@ export const Info = ({ {type !== 'business_security' ? ( <Typography className={styles.role} sx={{ fontSize: '17px', fontWeight: '400' }}> Режим валидации по IP :{' '} - <Typography component='span' sx={{ color: '#7f7df3' }}>{data?.is_ip_whitelist_enabled ? 'включен' : 'выключен'}</Typography> + <Typography component='span' sx={{ color: '#7f7df3' }}> + {data?.is_ip_whitelist_enabled ? 'включен' : 'выключен'} + </Typography> </Typography> ) : ( <></> @@ -1,7 +1,7 @@ import axios from 'axios' -import { AllowedModels } from '@/src/features/changeAccessToModel' -import { API_URL } from '@/src/shared/lib/constants' +import { AllowedModels } from '#/features/changeAccessToModel' +import { API_URL } from '#/shared/lib/constants' export const getModels = async (token?: string): Promise<AllowedModels[] | []> => { try { const { data } = await axios.get<AllowedModels[]>(API_URL + '/auth/business-host/allowed-models', { @@ -9,9 +9,9 @@ import TableHead from '@mui/material/TableHead' import TableRow from '@mui/material/TableRow' import { useSession } from 'next-auth/react' -import { AllowedModels, SwitchAccess } from '@/src/features/changeAccessToModel' -import { Search, translateTypeModel } from '@/src/shared' -import ArrowUpOrDown from '@/src/widgets/top-bar-model/ui/arrow-up-or-down' +import { AllowedModels, SwitchAccess } from '#/features/changeAccessToModel' +import { Search, translateTypeModel } from '#/shared' +import ArrowUpOrDown from '#/widgets/top-bar-model/ui/arrow-up-or-down' import { getModels } from '../../api/get-models' @@ -35,7 +35,11 @@ export const ModelsList = () => { <Box className={styles.title}> <Typography className={styles.name}>Нейросети</Typography> <Typography className={styles.count}>{models.length}</Typography> - <ArrowUpOrDown directionY={showModels} onClick={() => setShowModels((prev) => !prev)} className={styles.arrow} /> + <ArrowUpOrDown + directionY={showModels} + onClick={() => setShowModels((prev) => !prev)} + className={styles.arrow} + /> </Box> {showModels && ( @@ -52,9 +56,18 @@ export const ModelsList = () => { <TableBody> {models.map((model) => { return ( - <TableRow key={model.title} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableRow + key={model.title} + sx={{ + '&:last-child td, &:last-child th': { + border: 0, + }, + }} + > <TableCell align='left'>{model.title}</TableCell> - <TableCell align='center'>{translateTypeModel(model.model_type)}</TableCell> + <TableCell align='center'> + {translateTypeModel(model.model_type)} + </TableCell> <TableCell className={styles.tableSwitch} align='center'> <Box> <SwitchAccess @@ -1,8 +1,8 @@ import axios from 'axios' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' -import { ResponseGetBusinessGroups, ResponseGetIpList } from '@/src/features/invite-person-in-business/model/types' -import { API_URL } from '@/src/shared/lib/constants' +import { ResponseGetPersons } from '#/features/invite-person-in-business' +import { ResponseGetBusinessGroups, ResponseGetIpList } from '#/features/invite-person-in-business/model/types' +import { API_URL } from '#/shared/lib/constants' export const getBusinessGroups = async (token?: string): Promise<ResponseGetBusinessGroups[] | null> => { try { @@ -1,8 +1,8 @@ import axios from 'axios' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' -import { ResponseGetIpList } from '@/src/features/invite-person-in-business/model/types' -import { API_URL } from '@/src/shared/lib/constants' +import { ResponseGetPersons } from '#/features/invite-person-in-business' +import { ResponseGetIpList } from '#/features/invite-person-in-business/model/types' +import { API_URL } from '#/shared/lib/constants' export const getIpList = async (token?: string): Promise<ResponseGetIpList | null> => { try { @@ -1,8 +1,8 @@ import axios from 'axios' import { Dayjs } from 'dayjs' -import { ResponseGetLogsList } from '@/src/features/invite-person-in-business/model/types' -import { API_URL } from '@/src/shared/lib/constants' +import { ResponseGetLogsList } from '#/features/invite-person-in-business/model/types' +import { API_URL } from '#/shared/lib/constants' export const getLogsList = async ( offset: number, @@ -1,7 +1,7 @@ import axios from 'axios' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' -import { API_URL } from '@/src/shared/lib/constants' +import { ResponseGetPersons } from '#/features/invite-person-in-business' +import { API_URL } from '#/shared/lib/constants' export const getPersons = async (token?: string, security?: boolean): Promise<ResponseGetPersons[] | null> => { try { @@ -1,4 +1,4 @@ -import { AcceptanceStatus } from '@/src/features/invite-person-in-business/model/types' +import { AcceptanceStatus } from '#/features/invite-person-in-business/model/types' export const translateEmailStatus = (status: AcceptanceStatus) => { switch (status) { @@ -12,22 +12,24 @@ import { filter } from 'lodash' import Image from 'next/image' import { useSession } from 'next-auth/react' -import { AddBusinessGroup } from '@/src/features/business-group' -import { ChangeBusinessGroup } from '@/src/features/business-group/ui/change-business-group' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' -import { ResponseGetBusinessGroups } from '@/src/features/invite-person-in-business/model/types' -import { XMark } from '@/src/features/remove-person' -import { Search } from '@/src/shared' -import { API_URL } from '@/src/shared/lib/constants' -import styles from '@/src/widgets/business-models/ui/models-list/models-list.module.scss' -import { getBusinessGroups } from '@/src/widgets/business-persons/api/get-businessGroups' -import ArrowUpOrDown from '@/src/widgets/top-bar-model/ui/arrow-up-or-down' +import { AddBusinessGroup } from '#/features/business-group' +import { ChangeBusinessGroup } from '#/features/business-group/ui/change-business-group' +import { ResponseGetPersons } from '#/features/invite-person-in-business' +import { ResponseGetBusinessGroups } from '#/features/invite-person-in-business/model/types' +import { XMark } from '#/features/remove-person' +import { Search } from '#/shared' +import { API_URL } from '#/shared/lib/constants' +import styles from '#/widgets/business-models/ui/models-list/models-list.module.scss' +import { getBusinessGroups } from '#/widgets/business-persons/api/get-businessGroups' +import ArrowUpOrDown from '#/widgets/top-bar-model/ui/arrow-up-or-down' import { getIpList } from '../../api/get-ipList' export const BusinessGroups = ({ company_uid }: { company_uid?: string }) => { const [businessGroups, setBusinessGroups] = useState<ResponseGetBusinessGroups[] | null>() - const [searchGroup, setSearchGroup] = useState<ResponseGetBusinessGroups[] | undefined | null | undefined>(businessGroups ? businessGroups : []) + const [searchGroup, setSearchGroup] = useState<ResponseGetBusinessGroups[] | undefined | null | undefined>( + businessGroups ? businessGroups : [] + ) const [businessAddModal, setBusinessAddModal] = useState<boolean>(false) const [showPersons, setShowPersons] = useState(true) const [search, setSearch] = useState('') @@ -96,7 +98,11 @@ export const BusinessGroups = ({ company_uid }: { company_uid?: string }) => { <Box className={styles.title2}> <Box> <Typography className={styles.name}>Бизнес-группы</Typography> - <ArrowUpOrDown directionY={showPersons} onClick={() => setShowPersons((prev) => !prev)} className={styles.arrow} /> + <ArrowUpOrDown + directionY={showPersons} + onClick={() => setShowPersons((prev) => !prev)} + className={styles.arrow} + /> </Box> <Typography onClick={() => { @@ -129,12 +135,22 @@ export const BusinessGroups = ({ company_uid }: { company_uid?: string }) => { {searchGroup && searchGroup.length !== 0 ? ( searchGroup?.map((group) => { return ( - <TableRow key={group.uid} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableRow + key={group.uid} + sx={{ + '&:last-child td, &:last-child th': { border: 0 }, + }} + > <TableCell className={styles.tableEmail} align='left'> {group.title} </TableCell> - <TableCell className={styles.tableEmail} align='center'> - {group.token_limit ? group.token_limit.split('.')[0] : 'Не указано'} + <TableCell + className={styles.tableEmail} + align='center' + > + {group.token_limit + ? group.token_limit.split('.')[0] + : 'Не указано'} </TableCell> <TableCell @@ -162,7 +178,13 @@ export const BusinessGroups = ({ company_uid }: { company_uid?: string }) => { ) }) ) : ( - <TableRow sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableRow + sx={{ + '&:last-child td, &:last-child th': { + border: 0, + }, + }} + > <TableCell className={styles.tableEmail} align='center'> Не найдено группы с таким именем </TableCell> @@ -9,11 +9,11 @@ import TableHead from '@mui/material/TableHead' import TableRow from '@mui/material/TableRow' import { useSession } from 'next-auth/react' -import { ResponseGetPersons } from '@/src/features/invite-person-in-business' -import { ResponseGetIpList } from '@/src/features/invite-person-in-business/model/types' -import { Search, Success, useShowData } from '@/src/shared' -import styles from '@/src/widgets/business-models/ui/models-list/models-list.module.scss' -import ArrowUpOrDown from '@/src/widgets/top-bar-model/ui/arrow-up-or-down' +import { ResponseGetPersons } from '#/features/invite-person-in-business' +import { ResponseGetIpList } from '#/features/invite-person-in-business/model/types' +import { Search, Success, useShowData } from '#/shared' +import styles from '#/widgets/business-models/ui/models-list/models-list.module.scss' +import ArrowUpOrDown from '#/widgets/top-bar-model/ui/arrow-up-or-down' import { getIpList } from '../../api/get-ipList' @@ -62,13 +62,22 @@ export const IpList = () => { ) : ( <></> )} - <ArrowUpOrDown directionY={showPersons} onClick={() => setShowPersons((prev) => !prev)} className={styles.arrow} /> + <ArrowUpOrDown + directionY={showPersons} + onClick={() => setShowPersons((prev) => !prev)} + className={styles.arrow} + /> </Box> </Box> {showPersons && ( <Box className={styles.listWrap}> <Box className={styles.search}> - <Search value={search} onChange={(e) => setSearch(e.target.value)} fullWidth placeholder='Введите IP-адрес' /> + <Search + value={search} + onChange={(e) => setSearch(e.target.value)} + fullWidth + placeholder='Введите IP-адрес' + /> </Box> <TableContainer className={styles.table} component={Paper}> <Table sx={{ minWidth: 650 }} aria-label='simple table'> @@ -81,7 +90,12 @@ export const IpList = () => { {ipList && searchIp && searchIp.length > 0 ? ( searchIp.map((ip) => { return ( - <TableRow key={ip} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableRow + key={ip} + sx={{ + '&:last-child td, &:last-child th': { border: 0 }, + }} + > <TableCell className={styles.tableEmail} align='left'> {ip} </TableCell> @@ -89,7 +103,13 @@ export const IpList = () => { ) }) ) : ( - <TableRow sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableRow + sx={{ + '&:last-child td, &:last-child th': { + border: 0, + }, + }} + > <TableCell className={styles.tableEmail} align='center'> IP-адреса не найдены </TableCell> @@ -10,13 +10,13 @@ import TableRow from '@mui/material/TableRow' import { Dayjs } from 'dayjs' import { useSession } from 'next-auth/react' -import { ResponseGetLogsList } from '@/src/features/invite-person-in-business/model/types' -import { useAppSelector } from '@/src/main/store/store' -import { Search } from '@/src/shared' -import { DateInput } from '@/src/shared/ui/date-input/date-input' -import styles from '@/src/widgets/business-models/ui/models-list/models-list.module.scss' -import { getLogsList } from '@/src/widgets/business-persons/api/get-logsList' -import ArrowUpOrDown from '@/src/widgets/top-bar-model/ui/arrow-up-or-down' +import { ResponseGetLogsList } from '#/features/invite-person-in-business/model/types' +import { useAppSelector } from '#/app/store/store' +import { Search } from '#/shared' +import { DateInput } from '#/shared/ui/date-input/date-input' +import styles from '#/widgets/business-models/ui/models-list/models-list.module.scss' +import { getLogsList } from '#/widgets/business-persons/api/get-logsList' +import ArrowUpOrDown from '#/widgets/top-bar-model/ui/arrow-up-or-down' export const LogList = () => { const [logs, setLogs] = useState<ResponseGetLogsList[] | null>() @@ -44,7 +44,9 @@ export const LogList = () => { if (search) { return logs?.filter( - (el) => el.user.toLowerCase().includes(search.toLowerCase()) || el.message.toLowerCase().includes(search.toLowerCase()) + (el) => + el.user.toLowerCase().includes(search.toLowerCase()) || + el.message.toLowerCase().includes(search.toLowerCase()) ) } else { return logs @@ -64,7 +66,11 @@ export const LogList = () => { <Box className={styles.title2}> <Box> <Typography className={styles.name}>Журнал</Typography> - <ArrowUpOrDown directionY={showPersons} onClick={() => setShowPersons((prev) => !prev)} className={styles.arrow} /> + <ArrowUpOrDown + directionY={showPersons} + onClick={() => setShowPersons((prev) => !prev)} + className={styles.arrow} + /> </Box> </Box> @@ -116,7 +122,12 @@ export const LogList = () => { </Box> <Box className={styles.listWrap}> <Box className={styles.search}> - <Search value={search} onChange={(e) => setSearch(e.target.value)} fullWidth placeholder='Введите пользователя' /> + <Search + value={search} + onChange={(e) => setSearch(e.target.value)} + fullWidth + placeholder='Введите пользователя' + /> </Box> <TableContainer className={styles.table} component={Paper}> <Table sx={{ minWidth: 650 }} aria-label='simple table'> @@ -131,22 +142,47 @@ export const LogList = () => { {searchLogs && searchLogs.length !== 0 ? ( searchLogs?.map((log, idx) => { return ( - <TableRow key={idx} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableRow + key={idx} + sx={{ + '&:last-child td, &:last-child th': { border: 0 }, + }} + > <TableCell className={styles.tableEmail} align='left'> {log.user} </TableCell> <TableCell align='center'> {log.action_time.split('T')[0]} |{' '} - {log.action_time.split('T')[1].split('.')[0].split(':')[0]}: - {log.action_time.split('T')[1].split('.')[0].split(':')[1]} + { + log.action_time + .split('T')[1] + .split('.')[0] + .split(':')[0] + } + : + { + log.action_time + .split('T')[1] + .split('.')[0] + .split(':')[1] + } </TableCell> <TableCell align='right'>{log.message}</TableCell> </TableRow> ) }) ) : ( - <TableRow sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> - <TableCell className={styles.tableEmail} align='center'></TableCell> + <TableRow + sx={{ + '&:last-child td, &:last-child th': { + border: 0, + }, + }} + > + <TableCell + className={styles.tableEmail} + align='center' + ></TableCell> <TableCell className={styles.tableEmail} align='center'> Логов не найдено </TableCell> @@ -9,15 +9,15 @@ import TableHead from '@mui/material/TableHead' import TableRow from '@mui/material/TableRow' import { useSession } from 'next-auth/react' -import { PersonInBusiness } from '@/src/features/business-security-download' -import { LimitModal } from '@/src/features/change-limit' -import { InviteModal, ResponseGetPersons, RoleSelect } from '@/src/features/invite-person-in-business' -import { InviteRoles } from '@/src/features/invite-person-in-business/lib/constants' -import { XMark } from '@/src/features/remove-person' -import { Search, Success, useShowData } from '@/src/shared' -import styles from '@/src/widgets/business-models/ui/models-list/models-list.module.scss' -import { getPersons } from '@/src/widgets/business-persons/api/get-persons' -import ArrowUpOrDown from '@/src/widgets/top-bar-model/ui/arrow-up-or-down' +import { PersonInBusiness } from '#/features/business-security-download' +import { LimitModal } from '#/features/change-limit' +import { InviteModal, ResponseGetPersons, RoleSelect } from '#/features/invite-person-in-business' +import { InviteRoles } from '#/features/invite-person-in-business/lib/constants' +import { XMark } from '#/features/remove-person' +import { Search, Success, useShowData } from '#/shared' +import styles from '#/widgets/business-models/ui/models-list/models-list.module.scss' +import { getPersons } from '#/widgets/business-persons/api/get-persons' +import ArrowUpOrDown from '#/widgets/top-bar-model/ui/arrow-up-or-down' import { translateEmailStatus } from '../../lib/lib' @@ -99,7 +99,11 @@ export const PersonsList = ({ <Box> <Typography className={styles.name}>Сотрудники </Typography> {persons?.length !== 0 && <Typography className={styles.count}>{persons?.length}</Typography>} - <ArrowUpOrDown directionY={showPersons} onClick={() => setShowPersons((prev) => !prev)} className={styles.arrow} /> + <ArrowUpOrDown + directionY={showPersons} + onClick={() => setShowPersons((prev) => !prev)} + className={styles.arrow} + /> </Box> <Typography onClick={() => setInviteModal(true)}>Добавить сотрудника</Typography> </Box> @@ -134,33 +138,54 @@ export const PersonsList = ({ <TableBody> {searchPersons?.map((person) => { return ( - <TableRow key={person.email} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableRow + key={person.email} + sx={{ + '&:last-child td, &:last-child th': { border: 0 }, + }} + > <TableCell className={styles.tableEmail} align='left'> {person.email} </TableCell> - <TableCell align='center'>{RoleSelect[person.account_type]}</TableCell> + <TableCell align='center'> + {RoleSelect[person.account_type]} + </TableCell> <TableCell align='center'> {translateEmailStatus(person.acceptance_status)} </TableCell> - <TableCell className={styles.tableEmail} align='center'> + <TableCell + className={styles.tableEmail} + align='center' + > {Math.floor(+person.token_limit)} </TableCell> <TableCell onClick={() => - setCurrentPerson(persons?.find((el) => el.email === person.email) || null) + setCurrentPerson( + persons?.find( + (el) => el.email === person.email + ) || null + ) } className={styles.tableLimit} align='center' > <span className={styles.change}>Изменить</span> </TableCell> - <TableCell className={styles.tableLimit} align='center'> + <TableCell + className={styles.tableLimit} + align='center' + > <XMark setNewPersons={(persons) => setPersons( (prev) => - prev?.filter((el) => el.email !== persons.email) || null + prev?.filter( + (el) => + el.email !== + persons.email + ) || null ) } email={person.email} @@ -13,15 +13,15 @@ import axios, { AxiosResponse } from 'axios' import Image from 'next/image' import { useSession } from 'next-auth/react' -import { InviteRoles } from '@/src/features/business-group/lib/constants' -import { ResponseGetBusinessGroups } from '@/src/features/business-group/model/types' -import { LimitModal } from '@/src/features/change-limit' -import { ResponseGetPersons, RoleSelect } from '@/src/features/invite-person-in-business' -import { invitePerson } from '@/src/features/invite-person-in-business/api/invite-person' -import { inviteRoles } from '@/src/features/invite-person-in-business/lib/constants' -import styles2 from '@/src/features/invite-person-in-business/ui/invite-modal.module.scss' -import { IEmailForms } from '@/src/features/register-by-email/model/types' -import { useAppSelector } from '@/src/main/store/store' +import { InviteRoles } from '#/features/business-group/lib/constants' +import { ResponseGetBusinessGroups } from '#/features/business-group/model/types' +import { LimitModal } from '#/features/change-limit' +import { ResponseGetPersons, RoleSelect } from '#/features/invite-person-in-business' +import { invitePerson } from '#/features/invite-person-in-business/api/invite-person' +import { inviteRoles } from '#/features/invite-person-in-business/lib/constants' +import styles2 from '#/features/invite-person-in-business/ui/invite-modal.module.scss' +import { IEmailForms } from '#/features/register-by-email/model/types' +import { useAppSelector } from '#/app/store/store' import { ButtonGray, ButtonUI, @@ -34,12 +34,12 @@ import { ModalProps, onlyNumbersOption, Select, -} from '@/src/shared' -import { Search, Success, useShowData } from '@/src/shared' -import { API_URL } from '@/src/shared/lib/constants' -import styles from '@/src/widgets/business-models/ui/models-list/models-list.module.scss' -import { getBusinessGroups } from '@/src/widgets/business-persons/api/get-businessGroups' -import ArrowUpOrDown from '@/src/widgets/top-bar-model/ui/arrow-up-or-down' +} from '#/shared' +import { Search, Success, useShowData } from '#/shared' +import { API_URL } from '#/shared/lib/constants' +import styles from '#/widgets/business-models/ui/models-list/models-list.module.scss' +import { getBusinessGroups } from '#/widgets/business-persons/api/get-businessGroups' +import ArrowUpOrDown from '#/widgets/top-bar-model/ui/arrow-up-or-down' import { translateEmailStatus } from '../../lib/lib' @@ -121,7 +121,11 @@ export const SecurityList = ({ <Box> <Typography className={styles.name}>Сотрудники безопасности</Typography> {persons?.length !== 0 && <Typography className={styles.count}>{persons?.length}</Typography>} - <ArrowUpOrDown directionY={showPersons} onClick={() => setShowPersons((prev) => !prev)} className={styles.arrow} /> + <ArrowUpOrDown + directionY={showPersons} + onClick={() => setShowPersons((prev) => !prev)} + className={styles.arrow} + /> </Box> <Typography onClick={() => setInviteModal(true)}>Добавить сотрудника</Typography> </Box> @@ -156,42 +160,71 @@ export const SecurityList = ({ <TableBody> {searchPersons?.map((person) => { return ( - <TableRow key={person.email} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}> + <TableRow + key={person.email} + sx={{ + '&:last-child td, &:last-child th': { border: 0 }, + }} + > <TableCell className={styles.tableEmail} align='left'> {person.email} </TableCell> - <TableCell align='center'>{RoleSelect[person.account_type]}</TableCell> + <TableCell align='center'> + {RoleSelect[person.account_type]} + </TableCell> <TableCell align='center'> {translateEmailStatus(person.acceptance_status)} </TableCell> - <TableCell className={styles.tableEmail} align='center'> + <TableCell + className={styles.tableEmail} + align='center' + > {Math.floor(+person.token_limit)} </TableCell> <TableCell onClick={() => - setCurrentPerson(persons?.find((el) => el.email === person.email) || null) + setCurrentPerson( + persons?.find( + (el) => el.email === person.email + ) || null + ) } className={styles.tableLimit} align='center' > <span className={styles.change}>Изменить</span> </TableCell> - <TableCell className={styles.tableLimit} align='center'> + <TableCell + className={styles.tableLimit} + align='center' + > <Image onClick={(e) => { axios.put< - { token_limit: string; role: string }, + { + token_limit: string + role: string + }, AxiosResponse<ResponseGetPersons> >( - API_URL + `/auth/business-host/accounts/${person.email}`, - { account_privileges: 'regular' }, - { headers: { Authorization: `Bearer ${data?.access}` } } + API_URL + + `/auth/business-host/accounts/${person.email}`, + { + account_privileges: 'regular', + }, + { + headers: { + Authorization: `Bearer ${data?.access}`, + }, + } ).then((res) => { if (securityList) setSecurityList( securityList.filter( - (el) => el.email !== res.data.email + (el) => + el.email !== + res.data.email ) ) addList(res.data, 'personal') @@ -214,7 +247,11 @@ export const SecurityList = ({ </Box> )} <Success message={message} open={Boolean(message)} isError={isError} /> - <InviteSecurityModal open={inviteModal} onClose={() => setInviteModal(false)} showNewPersons={showNewPerson} /> + <InviteSecurityModal + open={inviteModal} + onClose={() => setInviteModal(false)} + showNewPersons={showNewPerson} + /> {currentPerson && ( <LimitModal list={securityList} @@ -287,7 +324,11 @@ export const InviteSecurityModal: FC<InviteModalProps> = ({ open, onClose, showN {/* Поле с выбором роли сотрудника */} <Typography className={styles2.hint}>Выберите роль</Typography> - <Select list={Object.values(securityRole)} onChange={(e) => setRole(e.target.value as InviteRoles)} value={role}></Select> + <Select + list={Object.values(securityRole)} + onChange={(e) => setRole(e.target.value as InviteRoles)} + value={role} + ></Select> {/* Поле с выбором группы */} <Typography className={styles2.hint}>Выберите бизнес-группу</Typography> @@ -1,7 +1,7 @@ import React from 'react' import { Box, Button, Grow, Menu, Typography } from '@mui/material' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' interface IContext { context: { message: string; uid: string }[] @@ -10,7 +10,12 @@ interface IContext { device?: 'mobile' | 'desktop' } -const Context: React.FC<IContext> = ({ context, deleteContextMessage, deleteAllContextMessage, device = 'desktop' }) => { +const Context: React.FC<IContext> = ({ + context, + deleteContextMessage, + deleteAllContextMessage, + device = 'desktop', +}) => { const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null) const open = Boolean(anchorEl) const handleClick = (event: React.MouseEvent<HTMLElement>) => { @@ -101,7 +106,12 @@ const Context: React.FC<IContext> = ({ context, deleteContextMessage, deleteAllC <> {context.map((message) => { return ( - <Grow key={message.uid} in={true} style={{ transformOrigin: '0 0 0' }} {...(true ? { timeout: 800 } : {})}> + <Grow + key={message.uid} + in={true} + style={{ transformOrigin: '0 0 0' }} + {...(true ? { timeout: 800 } : {})} + > <Box onClick={() => deleteContextMessage(message.uid)} sx={{ width: 'fit-content' }}> <Typography sx={{ @@ -8,11 +8,11 @@ // import { useSession } from 'next-auth/react' // import remarkGfm from 'remark-gfm' // -// import FullScreenModal from '@/src/features/image-modal/full-screen-modal' -// import { useAppSelector } from '@/src/main/store/store' -// import { TooltipCustom } from '@/src/shared' -// import { useAutoScroll } from '@/src/shared/lib/hooks' -// import { Message } from '@/src/shared/lib/types/model' +// import FullScreenModal from '#/features/image-modal/full-screen-modal' +// import { useAppSelector } from '#/app/store/store' +// import { TooltipCustom } from '#/shared' +// import { useAutoScroll } from '#/shared/lib/hooks' +// import { Message } from '#/shared/lib/types/model' // // import '../styles.module.css' // @@ -1,8 +1,8 @@ import React from 'react' import { Box, Typography } from '@mui/material' -import { errorColor } from '@/src/shared/lib/constants/colors' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' +import { errorColor } from '#/shared/lib/constants/colors' +import { useThemeAndDevice } from '#/shared/lib/hooks' const TooltipContextText = () => { const { theme } = useThemeAndDevice() @@ -1,8 +1,8 @@ import React from 'react' import { Tooltip } from '@mui/material' -import { useThemeAndDevice } from '@/src/shared/lib/hooks' -import TooltipContextText from '@/src/widgets/chat-gpt-field/ui/tooltip-context-text' +import { useThemeAndDevice } from '#/shared/lib/hooks' +import TooltipContextText from '#/widgets/chat-gpt-field/ui/tooltip-context-text' interface ITooltipContext { children: React.ReactNode @@ -1,15 +1,25 @@ import React from 'react' -import { Box, Input, Menu, MenuItem, Popover, TextField, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material' +import { + Box, + Input, + Menu, + MenuItem, + Popover, + TextField, + ToggleButton, + ToggleButtonGroup, + Typography, +} from '@mui/material' import axios from 'axios' import Image from 'next/image' import { useSession } from 'next-auth/react' -import { Chat, ChatsReturn } from '@/src/features/chats' -import { useAppSelector } from '@/src/main/store/store' -import styles2 from '@/src/main/styles/account.module.css' -import { TooltipCustom } from '@/src/shared' -import { api } from '@/src/shared/api/endpoints' -import chatsStyles from '@/src/shared/styles/chats-menu.module.scss' +import { Chat, ChatsReturn } from '#/features/chats' +import { useAppSelector } from '#/app/store/store' +import styles2 from '#/app/styles/account.module.css' +import { TooltipCustom } from '#/shared' +import { api } from '#/shared/api/endpoints' +import chatsStyles from '#/shared/styles/chats-menu.module.scss' export const Chats = ({ chats, @@ -60,7 +70,10 @@ export const Chats = ({ const renameChat = () => { if (newTitle !== '' && newTitle !== 'Новый чат') { api.updRenameChat(localChat, newTitle, data?.access).then((res) => { - if (res.data && chats) setLocalChats(chats.filter((el) => (el.uid === res.data.uid ? (el.title = res.data.title) : el))) + if (res.data && chats) + setLocalChats( + chats.filter((el) => (el.uid === res.data.uid ? (el.title = res.data.title) : el)) + ) setNewTitle('') }) } @@ -69,7 +82,16 @@ export const Chats = ({ return ( <Box className={chatsStyles.wrap} sx={{ margin: desktop ? '0 0 15px 0' : '15px 0 0' }}> <Box display='flex' alignItems='center' justifyContent='space-between'> - <Typography sx={{ color: '#A4AAB5', fontWeight: '600', fontSize: '14px', letterSpacing: '0.1px' }}>ЧАТЫ</Typography> + <Typography + sx={{ + color: '#A4AAB5', + fontWeight: '600', + fontSize: '14px', + letterSpacing: '0.1px', + }} + > + ЧАТЫ + </Typography> <TooltipCustom title='Создать чат'> <Box onClick={createNewChat} @@ -185,9 +207,19 @@ export const Chats = ({ }} /> ) : ( - <Box className={el?.uid === currentChat ? chatsStyles.main_active : chatsStyles.main}> + <Box + className={ + el?.uid === currentChat + ? chatsStyles.main_active + : chatsStyles.main + } + > <Typography - className={el?.uid === currentChat ? styles2.toggle_text_active_chat : styles2.toggle_text} + className={ + el?.uid === currentChat + ? styles2.toggle_text_active_chat + : styles2.toggle_text + } > {el?.title} </Typography> @@ -195,7 +227,9 @@ export const Chats = ({ {el?.uid === currentChat && ( <Image onClick={handleClick} - aria-describedby={Boolean(chatSetting) ? 'simple-popover' : undefined} + aria-describedby={ + Boolean(chatSetting) ? 'simple-popover' : undefined + } src={'/svg/chat-setting.svg'} alt={'Настройка чата'} width={18} @@ -211,12 +245,14 @@ export const Chats = ({ onClick={handleClose} sx={{ '& .MuiMenu-list': { - backgroundColor: theme === 'dark' ? '#303035' : '#EFF0F2', + backgroundColor: + theme === 'dark' ? '#303035' : '#EFF0F2', color: '#8280FF', borderRadius: '15px', }, '& .MuiPopover-paper': { - backgroundColor: theme === 'dark' ? '#303035' : '#EFF0F2', + backgroundColor: + theme === 'dark' ? '#303035' : '#EFF0F2', borderRadius: '15px', }, }} @@ -14,13 +14,17 @@ const advantagesDaVinci = const warningDaVinci = 'Тратит в 10 раз больше токенов, чем модели ChatGPT и Curie.' const descriptionCurie = 'Очень мощный, при этом быстрее и дешевле, чем text-DaVinci-003.' -const advantagesCurie = 'Способен выполнять языковой перевод, классифицировать и обобщать информацию, соблюдать заданный эмоциональный тон.' +const advantagesCurie = + 'Способен выполнять языковой перевод, классифицировать и обобщать информацию, соблюдать заданный эмоциональный тон.' const descriptionBabbage = 'Способен выполнять простые задачи, очень быстрый и недорогой.' -const advantagesBabbage = 'Способен классифицировать информацию по заданным параметрам и осуществлять семантический поиск.' +const advantagesBabbage = + 'Способен классифицировать информацию по заданным параметрам и осуществлять семантический поиск.' -const descriptionAda = 'Способен выполнять простые задачи. В серии GPT-3 является самой быстрой моделью по самой низкой цене.' -const advantagesAda = 'Способен синтаксически анализировать текст, выполнять базовую классификацию, подбирать ключевые слова.' +const descriptionAda = + 'Способен выполнять простые задачи. В серии GPT-3 является самой быстрой моделью по самой низкой цене.' +const advantagesAda = + 'Способен синтаксически анализировать текст, выполнять базовую классификацию, подбирать ключевые слова.' export const ParameterTopP = 'Тор_p - альтернатива температуре. Не\n' + @@ -2,10 +2,10 @@ import React from 'react' import { Drawer, MenuItem, Slider, Typography } from '@mui/material' import Stack from '@mui/material/Stack' -import { useAppSelector } from '@/src/main/store/store' -import { Select } from '@/src/shared' -import { IFiltersChatGPT } from '@/src/widgets/filters-gpt/ui/filters' -import TooltipModelTypes from '@/src/widgets/filters-gpt/ui/tooltip-model-types' +import { useAppSelector } from '#/app/store/store' +import { Select } from '#/shared' +import { IFiltersChatGPT } from '#/widgets/filters-gpt/ui/filters' +import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' const FiltersMobile: React.FC<IFiltersChatGPT> = ({ open, @@ -5,12 +5,17 @@ import { SelectChangeEvent } from '@mui/material/Select' import Stack from '@mui/material/Stack' import { styled } from '@mui/material/styles' -import { TutorialContext } from '@/src/features/tutorial-context/tutorial-context' -import { useAppSelector } from '@/src/main/store/store' -import { Select, Slider as Sl, SwitchCustom, TooltipCustom } from '@/src/shared' -import { ITypeModels, ParameterPresencePenalty, ParameterTemperature, ParameterTopP } from '@/src/widgets/filters-gpt/lib/constants' -import FiltersMobile from '@/src/widgets/filters-gpt/ui/filters-mobile' -import TooltipModelTypes from '@/src/widgets/filters-gpt/ui/tooltip-model-types' +import { TutorialContext } from '#/features/tutorial-context/tutorial-context' +import { useAppSelector } from '#/app/store/store' +import { Select, Slider as Sl, SwitchCustom, TooltipCustom } from '#/shared' +import { + ITypeModels, + ParameterPresencePenalty, + ParameterTemperature, + ParameterTopP, +} from '#/widgets/filters-gpt/lib/constants' +import FiltersMobile from '#/widgets/filters-gpt/ui/filters-mobile' +import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' import 'intro.js/introjs.css' @@ -137,7 +142,13 @@ export const Filters: React.FC<IFiltersChatGPT> = memo( padding: '30px', height: 'fit-content', borderRadius: '15px', - backgroundColor: desktop ? (theme === 'light' ? 'white' : '#151518') : theme === 'light' ? 'white' : '#4B4B4B', + backgroundColor: desktop + ? theme === 'light' + ? 'white' + : '#151518' + : theme === 'light' + ? 'white' + : '#4B4B4B', }} > <Typography className='title-block' sx={{ marginBottom: '15px' }}> @@ -181,7 +192,12 @@ export const Filters: React.FC<IFiltersChatGPT> = memo( </Select> </Box> {isAdditionalCtx && ( - <Box width='100%' display='flex' alignItems='center' justifyContent='space-between'> + <Box + width='100%' + display='flex' + alignItems='center' + justifyContent='space-between' + > <Typography sx={{ color: '#868686', @@ -192,7 +208,10 @@ export const Filters: React.FC<IFiltersChatGPT> = memo( > Увеличенный контекст </Typography> - <SwitchCustom checked={isAdditional} onChange={() => changeAdditionalCtx!()} /> + <SwitchCustom + checked={isAdditional} + onChange={() => changeAdditionalCtx!()} + /> </Box> )} <TooltipCustom title={ParameterTemperature} placement='left'> @@ -1,7 +1,7 @@ import React from 'react' import { Box, Tooltip, Typography } from '@mui/material' -import { useAppSelector } from '@/src/main/store/store' +import { useAppSelector } from '#/app/store/store' interface ITooltip { title: string @@ -1,8 +1,15 @@ -import { IProps } from '@/src/shared/lib/types/entities' -import { Styles } from '@/src/widgets/filters-sd/ui/filters' +import { IProps } from '#/shared/lib/types/entities' +import { Styles } from '#/widgets/filters-sd/ui/filters' export type TImageFormat = '512x512' | '640x384' | '384x640' | '768x512' | '512x768' | '1024x1024' -export type TClipGuidancePresets = 'FAST_BLUE' | 'FAST_GREEN' | 'Без фильтров' | 'SIMPLE' | 'SLOW' | 'SLOWER' | 'SLOWEST' +export type TClipGuidancePresets = + | 'FAST_BLUE' + | 'FAST_GREEN' + | 'Без фильтров' + | 'SIMPLE' + | 'SLOW' + | 'SLOWER' + | 'SLOWEST' export type TSamplerTypes = | 'DDIM' | 'DDPM' @@ -2,14 +2,20 @@ import React, { useState } from 'react' import SettingsSuggestIcon from '@mui/icons-material/SettingsSuggest' import { Box, Button, Drawer, Stack, Typography } from '@mui/material' -import { useAppSelector } from '@/src/main/store/store' -import { Select, SwitchCustom } from '@/src/shared' -import { Slider } from '@/src/shared' -import { SelectUI } from '@/src/shared/ui/select' -import { PrettoSlider, PrettoSliderDark } from '@/src/widgets/filters-gpt/ui/filters' -import { GeneratingModel } from '@/src/widgets/filters-sd/lib/constants' -import { ISDFilters, TClipGuidancePresets, TGeneratingModel, TImageFormat, TSamplerTypes } from '@/src/widgets/filters-sd/lib/types' -import { Styles,styles } from '@/src/widgets/filters-sd/ui/filters' +import { useAppSelector } from '#/app/store/store' +import { Select, SwitchCustom } from '#/shared' +import { Slider } from '#/shared' +import { SelectUI } from '#/shared/ui/select' +import { PrettoSlider, PrettoSliderDark } from '#/widgets/filters-gpt/ui/filters' +import { GeneratingModel } from '#/widgets/filters-sd/lib/constants' +import { + ISDFilters, + TClipGuidancePresets, + TGeneratingModel, + TImageFormat, + TSamplerTypes, +} from '#/widgets/filters-sd/lib/types' +import { Styles, styles } from '#/widgets/filters-sd/ui/filters' interface ISDFiltersMobile extends ISDFilters { isOpenDrawer: boolean @@ -142,7 +148,9 @@ export const FiltersMobile: React.FC<ISDFiltersMobile> = ({ size={'small'} title={'clip_guidance_preset'} value={clipGuidancePreset} - onChange={(e) => changeClipGuidancePreset(e.target.value as TClipGuidancePresets)} + onChange={(e) => + changeClipGuidancePreset(e.target.value as TClipGuidancePresets) + } list={clipGuidancePresets} /> </Box> @@ -1,10 +1,16 @@ import React from 'react' import { Box, Stack, Typography } from '@mui/material' -import { Slider, SwitchCustom } from '@/src/shared' -import { SelectUI } from '@/src/shared/ui/select' -import { GeneratingModel } from '@/src/widgets/filters-sd/lib/constants' -import { ISDFilters, TClipGuidancePresets, TGeneratingModel, TImageFormat, TSamplerTypes } from '@/src/widgets/filters-sd/lib/types' +import { Slider, SwitchCustom } from '#/shared' +import { SelectUI } from '#/shared/ui/select' +import { GeneratingModel } from '#/widgets/filters-sd/lib/constants' +import { + ISDFilters, + TClipGuidancePresets, + TGeneratingModel, + TImageFormat, + TSamplerTypes, +} from '#/widgets/filters-sd/lib/types' export type Styles = | null @@ -81,7 +87,12 @@ export const FiltersSd: React.FC<ISDFilters> = ({ /> <Box sx={{ marginTop: 1 }}> - <SelectUI title={'Стиль'} value={style} onChange={(e) => changeStyle(e.target.value as Styles)} list={Object.values(styles)} /> + <SelectUI + title={'Стиль'} + value={style} + onChange={(e) => changeStyle(e.target.value as Styles)} + list={Object.values(styles)} + /> </Box> <Slider @@ -0,0 +1,12 @@ +import { ReactElement } from 'react' +import { Layout } from '../ui' +import { LayoutProps } from '../types' + +export const getDefaultLayout = function (layoutProps: Omit<LayoutProps, 'children'> = {}) { + return (page: ReactElement) => <Layout {...layoutProps}>{page}</Layout> +} + +export const getDefaultLayoutDynamic = function (getLayoutProps: () => Omit<LayoutProps, 'children'>) { + return (page: ReactElement) => <Layout {...getLayoutProps()}>{page}</Layout> +} + @@ -0,0 +1 @@ +export * from './get-layout' \ No newline at end of file @@ -0,0 +1 @@ +export * from './layout-props' \ No newline at end of file @@ -0,0 +1,9 @@ + + +export interface LayoutProps { + children: React.ReactNode + titlePage?: string + isAuthPage?: boolean + title?: string | React.ReactNode | null + isLoader?: boolean +} \ No newline at end of file @@ -0,0 +1,158 @@ +import React, { useState } from 'react' +import { Box } from '@mui/material' +import { ThemeProvider } from '@mui/material/styles' +import Head from 'next/head' +import { useRouter } from 'next/router' +import { useSession } from 'next-auth/react' + +import { getUserBalance } from '#/entities/balance' +import { useTheme } from '#/entities/theme' +import { getAllInfo } from '#/entities/user-account' +import { isSettingExist } from '#/entities/user-account/lib/helpers/is-setting-exist' +import { getUserAccountSettings, setSettings } from '#/entities/user-account/model/settings' +import InfoBar from '#/app/layout/ui/info-bar' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { Loader } from '#/shared' +import { pingFangFont } from '#/shared/lib/constants/font/font' +import { Device } from '#/shared/lib/types/entities' +import { MainMenuMobile } from '#/widgets/main-menu' +import { SideMenu } from '#/widgets/side-menu' +import { getDeviceType } from '#/shared/lib/helpers' +import { LayoutProps } from '../types' + +const freeRoutes = ['/login', '/register', '/reset', '/changePassword'] + +export const Layout: React.FC<LayoutProps> = ({ + children, + isAuthPage = false, + titlePage, + title = titlePage, + isLoader, +}) => { + const { data: sessionData } = useSession() + const appState = useAppSelector((state) => state) + const dispatch = useAppDispatch() + + const device = getDeviceType() + + const desktop = device === 'desktop' + + const [sidemenuDefaultOpen, setSidemenuDefaultOpen] = useState(true) + + useTheme() + + const { data, status } = useSession() + + const router = useRouter() + + React.useEffect(() => { + if (status === 'authenticated') { + dispatch(getUserBalance(data?.access)) + dispatch(getAllInfo(data?.access)) + } + + if (status === 'unauthenticated' && !freeRoutes.includes(router.route)) { + router.push('/login') + } + }, [status]) + + React.useEffect(() => { + if ( + (sessionData && !sessionStorage.getItem('firstRender')) || + (sessionData && !localStorage.getItem('global_settings')) + ) { + dispatch(getUserAccountSettings(sessionData.access)) + sessionStorage.setItem('firstRender', 'true') + } + }, [sessionData]) + + React.useEffect(() => { + const lsData = localStorage.getItem('global_settings') + if (lsData !== null) { + dispatch(setSettings(JSON.parse(lsData))) + } + + window.addEventListener('beforeunload', () => { + sessionStorage.removeItem('firstRender') + sessionStorage.clear() + }) + }, []) + + React.useEffect(() => { + if (appState.settings.state !== null && device) { + let setting = isSettingExist({ + settings: appState.settings.state, + targetDevice: device, + targetType: 'sidemenu', + }) + if (setting) + setting?.value?.sidemenu_state === 'opened' + ? setSidemenuDefaultOpen(true) + : setSidemenuDefaultOpen(false) + } + }, [appState.settings.state]) + + if (status === 'loading' || isLoader) { + return ( + <Box width='100vw' height='100vh' display='flex' alignItems='center' justifyContent='center'> + <Loader size={50} /> + </Box> + ) + } + + return ( + <> + <Head> + {titlePage && <title>{titlePage}} + + + + + + + + + + {!isAuthPage ? ( + desktop ? ( + + + + + + {children} + + + + ) : ( + + + {children} + + ) + ) : ( + <>{children} + )} + + + + + ) +} @@ -0,0 +1 @@ +export * from './default' @@ -0,0 +1,3 @@ +export * from './ui' +export * from './model' +export * from './types' \ No newline at end of file @@ -9,10 +9,10 @@ import Link from 'next/link' import Router, { useRouter } from 'next/router' import { signOut, useSession } from 'next-auth/react' -import { change } from '@/src/entities/theme' -import { getAll, ResponseAllInfo } from '@/src/entities/user-account/model/user-type-slice' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { AccountMenu } from '@/src/shared' +import { change } from '#/entities/theme' +import { getAll, ResponseAllInfo } from '#/entities/user-account/model/user-type-slice' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { AccountMenu } from '#/shared' import { menuListMiddle, menuListTop } from '../../side-menu/ui/side-menu' @@ -155,14 +155,31 @@ export const MainMenuMobile = memo(() => { dispatch(change(null)) }} > - + signOut()}> - + - + Реквизиты @@ -170,7 +187,11 @@ export const MainMenuMobile = memo(() => { - push('/account')} sx={{ width: 29, height: 29 }} src={String(profile_picture_link)}> + push('/account')} + sx={{ width: 29, height: 29 }} + src={String(profile_picture_link)} + > {first_name[0]} @@ -192,7 +213,10 @@ type MenuItemProps = { } export function MenuItem(props: MenuItemProps) { - const isActive = useMemo(() => props.link === props.pathname || props.activeList?.some((el) => props.pathname.includes(el)), [props.pathname]) + const isActive = useMemo( + () => props.link === props.pathname || props.activeList?.some((el) => props.pathname.includes(el)), + [props.pathname] + ) return ( @@ -8,11 +8,11 @@ import Image from 'next/image' import Router, { useRouter } from 'next/router' import { useSession } from 'next-auth/react' -import { change } from '@/src/entities/theme' -import { useAppDispatch, useAppSelector } from '@/src/main/store/store' -import { AccountMenu } from '@/src/shared' -import { NotificationMenu } from '@/src/shared' -import { TooltipFreeTokens } from '@/src/shared' +import { change } from '#/entities/theme' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { AccountMenu } from '#/shared' +import { NotificationMenu } from '#/shared' +import { TooltipFreeTokens } from '#/shared' function a11yProps(index: number) { return { @@ -79,7 +79,7 @@ export const MainMenu: React.FC = memo(() => { const selectPage = pathname === '/' ? 0 : pathname === '/service-keys' ? 1 : pathname === '/admin' ? 2 : 3 - const ErrorModalLazy = dynamic(() => import('@/src/shared/ui/error-modal'), { ssr: false }) + const ErrorModalLazy = dynamic(() => import('#/shared/ui/error-modal'), { ssr: false }) return ( @@ -111,7 +111,11 @@ export const MainMenu: React.FC = memo(() => { alt={''} height={18} width={18} - src={pathname == '/' ? '/svg/main_menu/marketplace.svg' : '/svg/main_menu/marketplace_off.svg'} + src={ + pathname == '/' + ? '/svg/main_menu/marketplace.svg' + : '/svg/main_menu/marketplace_off.svg' + } /> } iconPosition='start' @@ -134,7 +138,8 @@ export const MainMenu: React.FC = memo(() => { '& .MuiTooltip-arrow': { color: theme === 'light' ? '#E8E8FA' : '#4B4B4B', }, - boxShadow: '0px 0px 4px rgba(0, 0, 0, 0.04), 0px 4px 32px rgba(0, 0, 0, 0.16)', + boxShadow: + '0px 0px 4px rgba(0, 0, 0, 0.04), 0px 4px 32px rgba(0, 0, 0, 0.16)', }, }, }} @@ -147,7 +152,11 @@ export const MainMenu: React.FC = memo(() => { } @@ -177,7 +186,13 @@ export const MainMenu: React.FC = memo(() => { alt='Error' /> )} - {openErrorModal && } + {openErrorModal && ( + + )} { width={19} alt='Bell' /> - +