@@ -1,3 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib" -} Binary files a/public/images/dashboard.png and /dev/null differ @@ -1,3 +1,3 @@ { - "main-page": "AIR" + "main-page": "AIR" } @@ -1,3 +1,3 @@ { - "main-page": "AIR" + "main-page": "AIR" } @@ -1,18 +1,19 @@ { - "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" -} + "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 @@ -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; - } - } -} @@ -1 +0,0 @@ -export { Layout } from '#/app/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; - } -} @@ -1,114 +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); -} @@ -1,23 +0,0 @@ -export interface AudioModel { - 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 -} @@ -1 +0,0 @@ -export * from './audio' \ No newline at end of file @@ -1,4 +1,4 @@ -import { Template } from '#/domains/copywrite/proxy/types/template' +import { Template } from '@/src/domains/copywrite/proxy/types/template' export const emptyTemplate: Template = { id: 1000, @@ -1,8 +1,8 @@ import axios from 'axios' -import { Template } from '#/domains/copywrite/proxy/types/template' -import { API_URL } from '#/shared/lib/constants' -import { Message } from '#/shared/lib/types/model' +import { Template } from '@/src/domains/copywrite/proxy/types/template' +import { API_URL } from '@/src/shared/lib/constants' +import { Message } from '@/src/shared/lib/types/model' export class CopywriteProxy { token?: string @@ -1,7 +1,7 @@ import React from 'react' import { Typography } from '@mui/material' -import { Input, Slider } from '#/shared' +import { Input, Slider } from '@/src/shared' import { FiltersProps } from './types' @@ -20,23 +20,9 @@ export function Filters({ return ( <> - + - + Запрос для исключения из генерации @@ -1,8 +1,8 @@ import React from 'react' import { Stack, Typography } from '@mui/material' -import { Input, Slider } from '#/shared' -import { SelectUI } from '#/shared/ui/select' +import { Input, Slider } from '@/src/shared' +import { SelectUI } from '@/src/shared/ui/select' import { Filters } from './types' @@ -26,30 +26,9 @@ export function EpicPhotoFilters({ <> - - - + + + Запрос для исключения из генерации @@ -1,8 +1,8 @@ import React from 'react' import { Box, Typography } from '@mui/material' -import { Input, Slider, SwitchCustom } from '#/shared' -import { SelectUI } from '#/shared/ui/select' +import { Input, Slider, SwitchCustom } from '@/src/shared' +import { SelectUI } from '@/src/shared/ui/select' import { Filters } from './types' @@ -27,14 +27,7 @@ export function KandinskyFilters({ - + - - + + Запрос для исключения из генерации @@ -1,16 +0,0 @@ -import { AudioModel } from '#/app/types/model' -import { API_URL } from '#/shared/lib/constants' -import axios from 'axios' - -export async function getAudio(token?: string): Promise { - try { - const { data } = await axios.get(API_URL + '/ml_models/?category=audio', { - headers: { - Authorization: `Bearer ${token}`, - }, - }) - return data - } catch (e) { - return [] - } -} @@ -1 +0,0 @@ -export * from './audio-model-api' \ No newline at end of file @@ -1 +0,0 @@ -export * from './api' \ No newline at end of file @@ -1,6 +1,6 @@ import axios from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' export const getBalance = async (token: string): Promise => { try { @@ -1,7 +1,7 @@ import React from 'react' -import { change } from '#/entities/theme' -import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { change } from '@/src/entities/theme' +import { useAppDispatch, useAppSelector } from '@/src/main/store/store' export const useTheme = () => { const theme = useAppSelector((state) => state.theme.theme) @@ -1,13 +1,10 @@ import axios from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { IUserSetting } from '../model/types' -export const addUserSettings = async ( - token: string, - setting: Omit -): Promise => { +export const addUserSettings = async (token: string, setting: Omit): Promise => { try { const { data } = await axios.post(API_URL + '/api/users/settings/', setting, { headers: { @@ -1,6 +1,6 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { AccountType } from '../model/types' export const getAccountType = async (token: string | null | undefined): Promise => { @@ -9,14 +9,11 @@ export const getAccountType = async (token: string | null | undefined): Promise< } try { - const { data } = await axios.get>( - API_URL + '/auth/account-type', - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ) + const { data } = await axios.get>(API_URL + '/auth/account-type', { + headers: { + Authorization: `Bearer ${token}`, + }, + }) return data.status } catch (err) { @@ -1,6 +1,6 @@ import axios from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { IUserSetting } from '../model/types' @@ -1,16 +1,11 @@ import axios from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { getUpdatedSettingsLocal } from '../lib/helpers/update-setting-local' import { IUserSetting, SettingValueType } from '../model/types' -export const updateUserSettings = async ( - token: string, - id: string, - value: SettingValueType, - settings: IUserSetting[] -) => { +export const updateUserSettings = async (token: string, id: string, value: SettingValueType, settings: IUserSetting[]) => { try { const { data } = await axios.put( API_URL + `/api/users/settings/${id}`, @@ -1,4 +1,4 @@ -import { Device } from '#/shared/lib/types/entities' +import { Device } from '@/src/shared/lib/types/entities' import { IUserSetting, SettingType } from '../../model/types' @@ -12,25 +12,19 @@ interface IAddSettings { setting: Omit } -export const getUserAccountSettings = createAsyncThunk( - 'users/getSettings', - async (token: string | undefined | null) => { - if (!token) { - return initialState.state - } - return await getUserSettings(token) +export const getUserAccountSettings = createAsyncThunk('users/getSettings', async (token: string | undefined | null) => { + if (!token) { + return initialState.state } -) + return await getUserSettings(token) +}) -export const addUserAccountSettings = createAsyncThunk( - 'users/addSettings', - async ({ token, setting }: IAddSettings) => { - if (!token) { - return - } - return await addUserSettings(token, setting) +export const addUserAccountSettings = createAsyncThunk('users/addSettings', async ({ token, setting }: IAddSettings) => { + if (!token) { + return } -) + return await addUserSettings(token, setting) +}) export const updateUserAccountSettings = createAsyncThunk( 'users/updateSettings', @@ -1,4 +1,4 @@ -import { Device } from '#/shared/lib/types/entities' +import { Device } from '@/src/shared/lib/types/entities' export type AccountType = 'regular' | 'business_host' | 'business_account' @@ -2,8 +2,8 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' import { createAction } from '@reduxjs/toolkit/src' import axios, { AxiosResponse } from 'axios' -import { loadingThunk } from '#/app/store/store' -import { API_URL } from '#/shared/lib/constants' +import { loadingThunk } from '@/src/main/store/store' +import { API_URL } from '@/src/shared/lib/constants' type UserState = { status: loadingThunk @@ -92,11 +92,14 @@ const initialState: UserState & ResponseAllInfo = { export const getAll = async (token: string | null | undefined): Promise => { try { - const { data } = await axios.get>(API_URL + '/auth/me', { - headers: { - Authorization: `Bearer ${token}`, - }, - }) + const { data } = await axios.get>( + API_URL + '/auth/me', + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ) return data } catch (err) { @@ -104,13 +107,23 @@ export const getAll = async (token: string | null | undefined): Promise { - return await getAll(token) -}) +export const getAllInfo = createAsyncThunk( + 'user/getAllInfo', + async (token: string | null | undefined) => { + return await getAll(token) + } +) -export const unfollowEmail = createAsyncThunk('user/unfollowEmail', async (token: string | null | undefined) => { - await axios.patch(API_URL + '/auth/email-sub', {}, { headers: { Authorization: `Bearer ${token}` } }) -}) +export const unfollowEmail = createAsyncThunk( + 'user/unfollowEmail', + async (token: string | null | undefined) => { + await axios.patch( + API_URL + '/auth/email-sub', + {}, + { headers: { Authorization: `Bearer ${token}` } } + ) + } +) export const userSlice = createSlice({ name: 'userSlice', @@ -5,12 +5,12 @@ import { Dayjs } from 'dayjs' import { useRouter } from 'next/navigation' import { useSession } from 'next-auth/react' -import { useAppSelector } from '#/app/store/store' -import { ButtonGray, ButtonUI, Error, InputStyleDark, InputStyleLight, Loader, Modal } from '#/shared' -import { accountApi } from '#/shared/api/account-endpoints' -import { API_URL } from '#/shared/lib/constants' -import { useShowData } from '#/shared/lib/hooks' -import { DateInput } from '#/shared/ui/date-input/date-input' +import { useAppSelector } from '@/src/main/store/store' +import { ButtonGray, ButtonUI, Error, InputStyleDark, InputStyleLight, Loader, Modal } from '@/src/shared' +import { accountApi } from '@/src/shared/api/account-endpoints' +import { API_URL } from '@/src/shared/lib/constants' +import { useShowData } from '@/src/shared/lib/hooks' +import { DateInput } from '@/src/shared/ui/date-input/date-input' import styles from '../invite-person-in-business/ui/invite-modal.module.scss' @@ -45,10 +45,7 @@ export const ApiKeyModal = ({ setIsLoading(true) const { result } = endDate && endDate !== '' - ? await accountApi.createApiKeys( - { name: title !== '' ? title : keyName, expires_at: endDate }, - data?.access - ) + ? await accountApi.createApiKeys({ name: title !== '' ? title : keyName, expires_at: endDate }, data?.access) : await accountApi.createApiKeys({ name: title !== '' ? title : keyName }, data?.access) if (result !== null) { accountApi.getApiKeys(data?.access).then((res) => { @@ -1,13 +1,14 @@ -import { accountApi } from '#/shared/api/account-endpoints' +import { accountApi } from '@/src/shared/api/account-endpoints' export const authTelegram = async (email: any, password: any) => { const user = await accountApi.loginByEmail(email, password) if (user !== null) { - ;(window as any).Telegram.WebApp.sendData(user.token.access) + (window as any).Telegram.WebApp.sendData(user.token.access) } } + export const authTelegramYandex = async (email: any, password: any) => { const user = await accountApi.loginByEmail(email, password) @@ -1,13 +1,13 @@ import React from 'react' import { Stack } from '@mui/material' -import { CheckboxFilter } from '#/app/components/filters/checkbox_filter' -import { InputFilter } from '#/app/components/filters/input_filter' -import { SelectFilter } from '#/app/components/filters/select_filter' -import { Slide } from '#/app/components/filters/slide_filter' -import { setParams } from '#/app/store/model-parametres-store' -import { useAppDispatch, useAppSelector } from '#/app/store/store' -import { IModelParams } from '#/shared/api/models/models' +import { CheckboxFilter } from '@/src/main/components/filters/checkbox_filter' +import { InputFilter } from '@/src/main/components/filters/input_filter' +import { SelectFilter } from '@/src/main/components/filters/select_filter' +import { Slide } from '@/src/main/components/filters/slide_filter' +import { setParams } from '@/src/main/store/model-parametres-store' +import { useAppDispatch, useAppSelector } from '@/src/main/store/store' +import { IModelParams } from '@/src/shared/api/models/models' interface IProps { params: IModelParams[] @@ -23,9 +23,7 @@ export default function BotParamsMap({ params, currentVersion }: IProps) { React.useEffect(() => { if (currentVersion && params) { - const res = params.map((el) => - el.versions.length !== 0 && !el.versions.includes(currentVersion) ? null : el - ) + const res = params.map((el) => (el.versions.length !== 0 && !el.versions.includes(currentVersion) ? null : el)) if (res.every((el) => el === null)) { setNewParam({}) } @@ -1,6 +1,6 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { ResponseGetBusinessGroups, ResponseGetPersons } from '../model/types' @@ -11,10 +11,7 @@ export const addBusinessGroup = async ( token?: string ): Promise => { try { - const { data } = await axios.post< - { uid: string; title: string; token_limit: string }, - AxiosResponse - >( + const { data } = await axios.post<{ uid: string; title: string; token_limit: string }, AxiosResponse>( API_URL + '/auth/business-groups/', { title: title, @@ -1,14 +1,10 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { ResponseGetBusinessGroups, ResponseGetPersons } from '../model/types' -export const addBusinessGroupUser = async ( - group_id: string, - email: string, - token?: string -): Promise => { +export const addBusinessGroupUser = async (group_id: string, email: string, token?: string): Promise => { try { const { data } = await axios.post(API_URL + `/auth/business-groups/${group_id}/accounts/${email}/`, { headers: { @@ -1,6 +1,6 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { ResponseGetBusinessGroups } from '../model/types' @@ -1,14 +1,10 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { ResponseGetBusinessGroups, ResponseGetPersons } from '../model/types' -export const deleteBusinessGroupUser = async ( - group_id: string, - email: string, - token?: string -): Promise => { +export const deleteBusinessGroupUser = async (group_id: string, email: string, token?: string): Promise => { try { const { data } = await axios.delete(API_URL + `/auth/business-groups/${group_id}/accounts/${email}/`, { headers: { @@ -1,19 +1,16 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { ResponseGetBusinessGroups } from '../model/types' export const getBusinessGroup = async (group_id: string, token?: string): Promise => { try { - const { data } = await axios.get( - API_URL + `/auth/business-groups/${group_id}/`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ) + const { data } = await axios.get(API_URL + `/auth/business-groups/${group_id}/`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) return data } catch (err) { @@ -1,4 +1,4 @@ -import { PersonInBusiness } from '#/features/invite-person-in-business' +import { PersonInBusiness } from '@/src/features/invite-person-in-business' type Roles = 'Владелец' | 'Сотрудник' | 'Администратор' | 'Сотрудник безопасности' export const RoleSelect: Record = { @@ -11,8 +11,4 @@ export const RoleSelect: Record = { export type InviteRoles = 'Сотрудник' | 'Администратор' | 'Сотрудник безопасности' export type InviteRolesEn = 'business_host' | 'business_account' | 'business_admin' | 'business_security' -export const inviteRoles = { - regular: 'Сотрудник', - admin: 'Администратор', - sec: 'Сотрудник безопасности', -} +export const inviteRoles = { regular: 'Сотрудник', admin: 'Администратор', sec: 'Сотрудник безопасности' } @@ -20,7 +20,7 @@ margin-top: 15px; text-align: right; } - .userWrap { + .userWrap{ display: flex; align-items: flex-start; justify-content: flex-start; @@ -29,18 +29,18 @@ max-height: 100px; overflow-y: scroll; } - .accounts { + .accounts{ transition: all; transition-duration: 150ms; letter-spacing: 1px; } - .accounts:hover { + .accounts:hover{ cursor: pointer; - color: #7f7df3; + color: #7f7df3 } // настройки скрола для групп юзеров - .userWrap::-webkit-scrollbar { + .userWrap::-webkit-scrollbar{ height: 5px; width: 5px; } @@ -49,7 +49,7 @@ margin: 21px 0; } .userWrap::-webkit-scrollbar-thumb { - background-color: #d9d9d9; + background-color: #D9D9D9; border-radius: 5px; } } @@ -4,13 +4,13 @@ import { SubmitErrorHandler } from 'react-hook-form/dist/types/form' import { Box, TextField, Typography } from '@mui/material' import { useSession } from 'next-auth/react' -import { addBusinessGroup } from '#/features/business-group/api/add-business-group' -import { ResponseGetBusinessGroups } from '#/features/business-group/model/types' -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, Error, InputStyleDark, InputStyleLight, Loader, Modal, ModalProps } from '#/shared' -import { useShowData } from '#/shared/lib/hooks' +import { addBusinessGroup } from '@/src/features/business-group/api/add-business-group' +import { ResponseGetBusinessGroups } from '@/src/features/business-group/model/types' +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 { ButtonGray, ButtonUI, Error, InputStyleDark, InputStyleLight, Loader, Modal, ModalProps } from '@/src/shared' +import { useShowData } from '@/src/shared/lib/hooks' import styles from './add-business-group.module.scss' @@ -5,18 +5,18 @@ import { Box, TextField, Typography } from '@mui/material' import axios from 'axios' import { useSession } from 'next-auth/react' -import { addBusinessGroupUser } from '#/features/business-group/api/add-user' -import { changeBusinessGroup } from '#/features/business-group/api/change-group-data' -import { deleteBusinessGroupUser } from '#/features/business-group/api/delete-user' -import { getBusinessGroup } from '#/features/business-group/api/get-group-data' -import { InviteRoles, inviteRoles, InviteRolesEn, RoleSelect } from '#/features/business-group/lib/constants' -import { ResponseGetBusinessGroups } from '#/features/business-group/model/types' -import { ResponseGetPersons } from '#/features/invite-person-in-business' -import { IEmailForms } from '#/features/register-by-email/model/types' -import { useAppSelector } from '#/app/store/store' -import { ButtonGray, ButtonUI, Error, InputStyleDark, InputStyleLight, Loader, Modal, ModalProps } from '#/shared' -import { API_URL } from '#/shared/lib/constants' -import { useShowData } from '#/shared/lib/hooks' +import { addBusinessGroupUser } from '@/src/features/business-group/api/add-user' +import { changeBusinessGroup } from '@/src/features/business-group/api/change-group-data' +import { deleteBusinessGroupUser } from '@/src/features/business-group/api/delete-user' +import { getBusinessGroup } from '@/src/features/business-group/api/get-group-data' +import { InviteRoles, inviteRoles, InviteRolesEn, RoleSelect } from '@/src/features/business-group/lib/constants' +import { ResponseGetBusinessGroups } from '@/src/features/business-group/model/types' +import { ResponseGetPersons } from '@/src/features/invite-person-in-business' +import { IEmailForms } from '@/src/features/register-by-email/model/types' +import { useAppSelector } from '@/src/main/store/store' +import { ButtonGray, ButtonUI, Error, InputStyleDark, InputStyleLight, Loader, Modal, ModalProps } from '@/src/shared' +import { API_URL } from '@/src/shared/lib/constants' +import { useShowData } from '@/src/shared/lib/hooks' import styles from './add-business-group.module.scss' @@ -30,14 +30,7 @@ interface InviteModalProps extends ModalProps { current_group: ResponseGetBusinessGroups } -export const ChangeBusinessGroup: FC = ({ - open, - onClose, - company_uid, - current_group, - setChanges, - changes, -}) => { +export const ChangeBusinessGroup: FC = ({ open, onClose, company_uid, current_group, setChanges, changes }) => { const { handleSubmit, register, reset, setValue, getValues } = useForm() const [isLoading, setIsLoading] = useState(false) const [limit, setLimit] = useState('') @@ -151,14 +144,7 @@ export const ChangeBusinessGroup: FC = ({ - + В группе: @@ -175,14 +161,7 @@ export const ChangeBusinessGroup: FC = ({ - + Можно добавить: @@ -1,17 +1,14 @@ import axios from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' export const downloadFileReq = async (start_date: string, end_date: string, token?: string): Promise => { try { - const { data } = await axios.get( - API_URL + `/auth/business-security/download-report?end_date=${end_date}&start_date=${end_date}`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ) + const { data } = await axios.get(API_URL + `/auth/business-security/download-report?end_date=${end_date}&start_date=${end_date}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) return data } catch (err) { @@ -5,20 +5,14 @@ import { Dayjs } from 'dayjs' import { useRouter } from 'next/navigation' import { useSession } from 'next-auth/react' -import { ButtonGray, ButtonUI, Error, Loader, Modal } from '#/shared' -import { API_URL } from '#/shared/lib/constants' -import { useShowData } from '#/shared/lib/hooks' -import { DateInput } from '#/shared/ui/date-input/date-input' +import { ButtonGray, ButtonUI, Error, Loader, Modal } from '@/src/shared' +import { API_URL } from '@/src/shared/lib/constants' +import { useShowData } from '@/src/shared/lib/hooks' +import { DateInput } from '@/src/shared/ui/date-input/date-input' import styles from './invite-modal.module.scss' -export const DownloadModal = ({ - open, - setOpen, -}: { - open: boolean - setOpen: React.Dispatch> -}) => { +export const DownloadModal = ({ open, setOpen }: { open: boolean; setOpen: React.Dispatch> }) => { const [endDate, setEndDate] = useState() const [startDate, setStartDate] = useState() @@ -32,9 +26,7 @@ export const DownloadModal = ({ setIsLoading(true) axios.get( API_URL + - `/auth/business-security/download-report?${startDate ? `start_date=${startDate}` : ''}${ - endDate ? `&end_date=${endDate}` : '' - }`, + `/auth/business-security/download-report?${startDate ? `start_date=${startDate}` : ''}${endDate ? `&end_date=${endDate}` : ''}`, { responseType: 'arraybuffer', headers: { Authorization: `Bearer ${session?.access}` }, @@ -1 +0,0 @@ -export * from './download-modal' \ No newline at end of file @@ -1,3 +1,2 @@ export type { PersonInBusiness } from './model/types' export type { ResponseGetPersons } from './model/types' -export * from './ui' \ No newline at end of file @@ -1,10 +1,10 @@ import React from 'react' import { encoding_for_model } from '@dqbd/tiktoken' -import { costModel } from '#/features/calculation-tokens-gpt/lib/constants' -import { calculatingDalle } from '#/features/calculation-tokens-gpt/model/calculating-dalle' -import { ImessageContext } from '#/shared/lib/types/types-gpt' -import { TypeModelGPT, typeModels } from '#/widgets/filters-gpt/lib/constants' +import { costModel } from '@/src/features/calculation-tokens-gpt/lib/constants' +import { calculatingDalle } from '@/src/features/calculation-tokens-gpt/model/calculating-dalle' +import { ImessageContext } from '@/src/shared/lib/types/types-gpt' +import { TypeModelGPT, typeModels } from '@/src/widgets/filters-gpt/lib/constants' const getFullModelTypeName = (name: string): TypeModelGPT => { return typeModels.filter((el) => el.key === name)[0].value as TypeModelGPT @@ -3,12 +3,12 @@ import { Box, Tooltip } from '@mui/material' import Typography from '@mui/material/Typography' import Image from 'next/image' -import { useCalculating } from '#/features/calculation-tokens-gpt/model/use-calculating' -import TooltipCalculating from '#/features/calculation-tokens-gpt/ui/tooltip-calculating' -import { useAppSelector } from '#/app/store/store' -import { baseColor } from '#/shared/lib/constants/colors' -import { ImessageContext } from '#/shared/lib/types/types-gpt' -import { TypeModelGPT } from '#/widgets/filters-gpt/lib/constants' +import { useCalculating } from '@/src/features/calculation-tokens-gpt/model/use-calculating' +import TooltipCalculating from '@/src/features/calculation-tokens-gpt/ui/tooltip-calculating' +import { useAppSelector } from '@/src/main/store/store' +import { baseColor } from '@/src/shared/lib/constants/colors' +import { ImessageContext } from '@/src/shared/lib/types/types-gpt' +import { TypeModelGPT } from '@/src/widgets/filters-gpt/lib/constants' interface ICalculationTokenProps { model: 'gpt' | 'sd' | 'dalle' @@ -20,72 +20,70 @@ interface ICalculationTokenProps { context?: { message: string; uid: string }[] } -export const Calculation: React.FC = memo( - ({ prompt, gptType, context, isAdditional, count, quality, model }) => { - const price = useCalculating(model, count, quality, prompt, gptType, isAdditional, context) +export const Calculation: React.FC = memo(({ prompt, gptType, context, isAdditional, count, quality, model }) => { + const price = useCalculating(model, count, quality, prompt, gptType, isAdditional, context) - const theme = useAppSelector((state) => state.theme.theme) + const theme = useAppSelector((state) => state.theme.theme) - return ( - } - componentsProps={{ - tooltip: { - sx: { - '&.MuiTooltip-tooltip': { - '&.MuiTooltip-tooltipPlacementBottom': { - marginTop: '2px', - }, - '&.MuiTooltip-tooltipPlacementTop': { - marginBottom: '7px', - }, - '&.MuiTooltip-tooltipPlacementLeft': { - marginRight: '24px', - }, + return ( + } + componentsProps={{ + tooltip: { + sx: { + '&.MuiTooltip-tooltip': { + '&.MuiTooltip-tooltipPlacementBottom': { + marginTop: '2px', }, - bgcolor: theme === 'light' ? 'white' : '#4B4B4B', - borderRadius: '10px', - '& .MuiTooltip-arrow': { - color: theme === 'light' ? 'white' : '#4B4B4B', + '&.MuiTooltip-tooltipPlacementTop': { + marginBottom: '7px', }, - boxShadow: '0px 0px 4px rgba(0, 0, 0, 0.04), 0px 4px 32px rgba(0, 0, 0, 0.16)', + '&.MuiTooltip-tooltipPlacementLeft': { + marginRight: '24px', + }, + }, + bgcolor: theme === 'light' ? 'white' : '#4B4B4B', + borderRadius: '10px', + '& .MuiTooltip-arrow': { + color: theme === 'light' ? 'white' : '#4B4B4B', }, + boxShadow: '0px 0px 4px rgba(0, 0, 0, 0.04), 0px 4px 32px rgba(0, 0, 0, 0.16)', }, + }, + }} + arrow + > + - + - {''} - - {price} - - - - ) - } -) + {price} + + + + ) +}) Calculation.displayName = 'Calculation' @@ -2,8 +2,8 @@ import React from 'react' import { Box } from '@mui/material' import Typography from '@mui/material/Typography' -import { textTooltipCalculating } from '#/features/calculation-tokens-gpt/lib/constants' -import { useAppSelector } from '#/app/store/store' +import { textTooltipCalculating } from '@/src/features/calculation-tokens-gpt/lib/constants' +import { useAppSelector } from '@/src/main/store/store' const TooltipCalculating = ({ model = 'dalle' }: { model?: 'gpt' | 'dalle' | 'sd' }) => { const theme = useAppSelector((state) => state.theme.theme) @@ -29,9 +29,7 @@ const TooltipCalculating = ({ model = 'dalle' }: { model?: 'gpt' | 'dalle' | 'sd marginBottom: 0.8, }} > - - Фактическое значение может немного отличаться - + Фактическое значение может немного отличаться )} @@ -1,7 +1,7 @@ import axios, { AxiosResponse } from 'axios' -import { ResponseGetPersons } from '#/features/invite-person-in-business' -import { API_URL } from '#/shared/lib/constants' +import { ResponseGetPersons } from '@/src/features/invite-person-in-business' +import { API_URL } from '@/src/shared/lib/constants' import { InviteRoles, inviteRoles } from '../../invite-person-in-business/lib/constants' @@ -9,10 +9,7 @@ export const updateLimit = async (email?: string, limit?: string, token?: string try { const { data } = await axios.put<{ token_limit: string; role: string }, AxiosResponse>( API_URL + `/auth/business-host/accounts/${email}`, - { - token_limit: limit, - account_privileges: Object.entries(inviteRoles).find(([key, value]) => value === role)![0], - }, + { token_limit: limit, account_privileges: Object.entries(inviteRoles).find(([key, value]) => value === role)![0] }, { headers: { Authorization: `Bearer ${token}`, @@ -2,11 +2,11 @@ import React, { useEffect, useState } from 'react' import { Box, TextField, Typography } from '@mui/material' import { useSession } from 'next-auth/react' -import { ResponseGetPersons } from '#/features/invite-person-in-business' -import { useAppSelector } from '#/app/store/store' -import { ButtonUI, InputStyleDark, InputStyleLight, Modal, Select } from '#/shared' -import styles from '#/widgets/business-models/ui/models-list/models-list.module.scss' -import styles2 from '#/widgets/business-persons/ui/persons-list/persons-list.module.scss' +import { ResponseGetPersons } from '@/src/features/invite-person-in-business' +import { useAppSelector } from '@/src/main/store/store' +import { ButtonUI, InputStyleDark, InputStyleLight, Modal, Select } from '@/src/shared' +import styles from '@/src/widgets/business-models/ui/models-list/models-list.module.scss' +import styles2 from '@/src/widgets/business-persons/ui/persons-list/persons-list.module.scss' import { InviteRoles, inviteRoles } from '../../invite-person-in-business/lib/constants' import { updateLimit } from '../api/set-limit' @@ -21,15 +21,7 @@ interface LimitModalProps { addList: (newUser: ResponseGetPersons, list: 'personal' | 'security') => void } -export const LimitModal: React.FC = ({ - person, - onClose, - updateLimitProp, - list, - setList, - addList, - listType, -}) => { +export const LimitModal: React.FC = ({ person, onClose, updateLimitProp, list, setList, addList, listType }) => { const theme = useAppSelector((state) => state.theme.theme) const [limit, setLimit] = useState(person?.token_limit ? Math.floor(+person.token_limit).toString() : '0') const [role, setRole] = useState('Сотрудник') @@ -41,9 +33,7 @@ export const LimitModal: React.FC = ({ return } else if (list && res.account_type !== person?.account_type) { if ( - (res.account_type === 'business_host' || - res.account_type === 'business_account' || - res.account_type === 'business_admin') && + (res.account_type === 'business_host' || res.account_type === 'business_account' || res.account_type === 'business_admin') && listType === 'security' ) { setList(list.filter((el) => el.email !== res.email)) @@ -1,13 +1,9 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { AllowedModels } from './types' -export const changeAccess = async ( - token: string | undefined, - value: boolean, - title: string -): Promise => { +export const changeAccess = async (token: string | undefined, value: boolean, title: string): Promise => { if (!value) { try { const { data } = await axios.put<{ models: string }, AxiosResponse>( @@ -1,6 +1,6 @@ import React from 'react' -import { SwitchCustom } from '#/shared' +import { SwitchCustom } from '@/src/shared' import { changeAccess } from '../api/change-access' import { AllowedModels } from '../api/types' @@ -2,8 +2,8 @@ import React, { Dispatch, SetStateAction, useEffect, useState } from 'react' import axios from 'axios' import { useSession } from 'next-auth/react' -import { createChat, getAllChats } from '#/shared/api/endpoints' -import { API_URL } from '#/shared/lib/constants' +import { createChat, getAllChats } from '@/src/shared/api/endpoints' +import { API_URL } from '@/src/shared/lib/constants' export type Chat = { uid: string @@ -61,9 +61,7 @@ export function useChats(model: string): ChatsReturn { const removeChat = async (uid: string) => { try { setChatSetting(null) - const { status } = await axios.delete(API_URL + `/chats/${uid}/`, { - headers: { Authorization: `Bearer ${data?.access}` }, - }) + const { status } = await axios.delete(API_URL + `/chats/${uid}/`, { headers: { Authorization: `Bearer ${data?.access}` } }) if (status === 204) { let newChats = chats!.filter((el) => el.uid !== uid) @@ -1,7 +1,7 @@ import React, { ReactNode } from 'react' import { Menu, MenuItem } from '@mui/material' -import { useAppSelector } from '#/app/store/store' +import { useAppSelector } from '@/src/main/store/store' interface IProps { children: ReactNode @@ -18,11 +18,7 @@ export const ContextMenu = ({ children, pointX, pointY, clicked, handleClose }: { if (Object.values(el)[0]) { @@ -31,14 +26,11 @@ 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) { @@ -47,7 +39,7 @@ async function getStats( } export const useGetStats = ( - token: string | null | undefined, + token: string | null, start_date?: Dayjs | null, end_date?: Dayjs | null, group_by?: string, @@ -1,6 +1,6 @@ import { Dayjs } from 'dayjs' -import { IProps } from '#/shared/lib/types/entities' +import { IProps } from '@/src/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 '#/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 { 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 { IStatsProps } from '../model/types' import { useGetStats } from '../model/use-get-stats/use-get-stats' @@ -26,15 +26,7 @@ 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={ - @@ -69,28 +53,16 @@ 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,11 +53,7 @@ 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; -} +.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 @@ -1,6 +1,6 @@ import axios, { AxiosResponse } from 'axios' -import { API_URL } from '#/shared/lib/constants' +import { API_URL } from '@/src/shared/lib/constants' import { InviteRoles, inviteRoles } from '../lib/constants' import { ResponseGetPersons } from '../model/types' @@ -1,4 +1,4 @@ -import { PersonInBusiness } from '#/features/invite-person-in-business' +import { PersonInBusiness } from '@/src/features/invite-person-in-business' type Roles = 'Владелец' | 'Сотрудник' | 'Администратор' | 'Сотрудник безопасности' export const RoleSelect: Record = { @@ -10,8 +10,4 @@ export const RoleSelect: Record = { export type InviteRoles = 'Сотрудник' | 'Администратор' | 'Сотрудник безопасности' -export const inviteRoles = { - regular: 'Сотрудник', - admin: 'Администратор', - sec: 'Сотрудник безопасности', -} +export const inviteRoles = { regular: 'Сотрудник', admin: 'Администратор', sec: 'Сотрудник безопасности' } @@ -20,4 +20,7 @@ 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 '#/features/invite-person-in-business/api/invite-person' -import { IEmailForms } from '#/features/register-by-email/model/types' -import { useAppSelector } from '#/app/store/store' +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 { ButtonGray, ButtonUI, @@ -19,9 +19,9 @@ import { ModalProps, onlyNumbersOption, Select, -} from '#/shared' -import { useShowData } from '#/shared/lib/hooks' -import { getBusinessGroups } from '#/widgets/business-persons/api/get-businessGroups' +} from '@/src/shared' +import { useShowData } from '@/src/shared/lib/hooks' +import { getBusinessGroups } from '@/src/widgets/business-persons/api/get-businessGroups' import { InviteRoles, inviteRoles } from '../lib/constants' import { ResponseGetBusinessGroups, ResponseGetPersons } from '../model/types' @@ -85,11 +85,7 @@ export const InviteModal: FC = ({ open, onClose, showNewPerson {/* Поле с выбором роли сотрудника */} Выберите роль - + {/* Поле с выбором группы */} Выберите бизнес-группу @@ -1,12 +1,7 @@ import axios from 'axios' -import { - FieldActivity, - FieldActivitySelect, - Frequency, - FrequencySelect, -} from '#/features/register-business/lib/constants-step-information' -import { API_URL } from '#/shared/lib/constants' +import { FieldActivity, FieldActivitySelect, Frequency, FrequencySelect } from '@/src/features/register-business/lib/constants-step-information' +import { API_URL } from '@/src/shared/lib/constants' import { DataForCreate } from '../../model/stepper-slice' @@ -15,9 +10,7 @@ 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,18 +12,14 @@ 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 '#/app/store/store' +import { useAppDispatch } from '@/src/main/store/store' import { getDataCompany } from '../stepper-slice' @@ -23,11 +23,7 @@ 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 '#/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 { 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 { getCompanyData } from '../api/get-company-date/get-company-date' import { FieldActivity, Frequency } from '../lib/constants-step-information' @@ -90,15 +90,7 @@ 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 '#/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 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 { nameOptions } from '../../lib/constants-step-contact' import { createBusinessCompany, setContact } from '../../model/stepper-slice' @@ -4,18 +4,13 @@ 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 '#/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 { 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 { 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 '#/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 { 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 { 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 '#/app/store/store' -import { Error } from '#/shared' -import { useShowData } from '#/shared/lib/hooks' +import { useAppDispatch, useAppSelector } from '@/src/main/store/store' +import { Error } from '@/src/shared' +import { useShowData } from '@/src/shared/lib/hooks' import { steps } from '../../lib/constants' import { switchPreviousStep, switchStep } from '../../model/stepper-slice' @@ -104,13 +104,7 @@ 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 '#/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' +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' interface IRegisterEmailFormProps { successLogin: () => void @@ -1,7 +1,7 @@ import axios, { AxiosResponse } from 'axios' -import { ResponseGetPersons } from '#/features/invite-person-in-business' -import { API_URL } from '#/shared/lib/constants' +import { ResponseGetPersons } from '@/src/features/invite-person-in-business' +import { API_URL } from '@/src/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 '#/features/invite-person-in-business' +import { ResponseGetPersons } from '@/src/features/invite-person-in-business' import { removePerson } from '../api/remove' @@ -1,17 +1,11 @@ import { Box, Typography } from '@mui/material' import Image from 'next/image' -import { ResponseGetPersons } from '#/features/invite-person-in-business' -import { ButtonGray, ButtonUI, Modal } from '#/shared' +import { ResponseGetPersons } from '@/src/features/invite-person-in-business' +import { ButtonGray, ButtonUI, Modal } from '@/src/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 ( @@ -35,14 +29,7 @@ export const XMark = ({ > Вы дейсвительно хотите удалить пользователя {email} ? - + { e.stopPropagation() @@ -1,7 +1,7 @@ import React from 'react' import { Box, Typography } from '@mui/material' -import { useThemeAndDevice } from '#/shared/lib/hooks' +import { useThemeAndDevice } from '@/src/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 '#/features/send-suggested-message/lib/constants' -import SuggestedMessage from '#/features/send-suggested-message/ui/suggested-message' -import { useThemeAndDevice } from '#/shared/lib/hooks' +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' interface IProps { device: 'mobile' | 'desktop' @@ -1 +1 @@ -export { SuggestedMessages } from '#/features/send-suggested-message/ui/suggested-messages' +export { SuggestedMessages } from '@/src/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 '#/shared/styles/chats-bot-pages.module.scss' +import styles from '@/src/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 '#/app/store/store' -import { baseColor } from '#/shared/lib/constants/colors' +import { useAppSelector } from '@/src/main/store/store' +import { baseColor } from '@/src/shared/lib/constants/colors' interface ITooltipMy { begin: () => void @@ -1,8 +1,8 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' -import { CopywriteProxy } from '#/domains/copywrite/proxy/copywrite-proxy' -import { Template } from '#/domains/copywrite/proxy/types/template' -import { Message } from '#/shared/lib/types/model' +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' export interface Theme { templates: Template[] | null @@ -14,19 +14,13 @@ 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 '#/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' +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' 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 '#/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' +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' type Languages = 'ru' | 'en' | 'it' | 'fr' type LanguagesText = 'Русский' | 'Английский' | 'Итальянский' | 'Французский' @@ -22,16 +22,7 @@ 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 '#/shared' -import { API_URL } from '#/shared/lib/constants' -import { Error } from '#/shared/lib/types/entities' +import { dataLine as data } from '@/src/shared' +import { API_URL } from '@/src/shared/lib/constants' +import { Error } from '@/src/shared/lib/types/entities' type ResponseStats = { source: string; amount: string }[] @@ -19,12 +19,9 @@ 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 { @@ -2,9 +2,9 @@ import React from 'react' import { FormControlLabel, Typography } from '@mui/material' import Switch from '@mui/material/Switch' -import { setParams } from '#/app/store/model-parametres-store' -import { useAppDispatch } from '#/app/store/store' -import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' +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' 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 '#/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' +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' 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 '#/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' +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' 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 '#/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' +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' 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 '#/app/store/store' -import { TooltipCustom } from '#/shared' +import { useAppSelector } from '@/src/main/store/store' +import { TooltipCustom } from '@/src/shared' interface IProps { loading: boolean @@ -59,14 +59,7 @@ 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 ( @@ -54,7 +57,11 @@ const ChatCard = ({ text, icon, title, uid, slug, accessed_models }: CardProps) textAlign: 'center', }} > - {show_balance && ( - + {declineToken(balance.toString())} @@ -202,13 +212,14 @@ const InfoBar: React.FC = ({ device }) => { cursor: 'pointer', }} > - {email ? email[0] : 'N'} + {email[0] || 'N'} = ({ device }) => { router.push('/account?scope=setting')} + onClick={() => + router.push('/account?scope=setting') + } > = ({ device }) => { router.push('/account?scope=business')} + onClick={() => + router.push( + '/account?scope=business' + ) + } > = ({ device }) => { router.push('/account?scope=referral')} + onClick={() => + router.push( + '/account?scope=referral' + ) + } > = ({ device }) => { router.push('/account?scope=subscribe')} + onClick={() => + router.push( + '/account?scope=subscribe' + ) + } > = ({ device }) => { { - signOut() - }} + onClick={() => signOut()} sx={{ marginTop: '15px', cursor: 'pointer' }} > - - + + Выйти @@ -5,37 +5,35 @@ 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' - -const freeRoutes = ['/login', '/register', '/reset', '/change-password'] +import { getUserBalance } from '@/src/entities/balance' +import { useTheme } from '@/src/entities/theme' +import { getAllInfo } from '@/src/entities/user-account' +import { isSettingExist } from '@/src/entities/user-account/lib/helpers/is-setting-exist' +import { getUserAccountSettings, setSettings } from '@/src/entities/user-account/model/settings' +import InfoBar from '@/src/main/layout/ui/info-bar' +import { useAppDispatch, useAppSelector } from '@/src/main/store/store' +import { Loader } from '@/src/shared' +import { pingFangFont } from '@/src/shared/lib/constants/font/font' +import { Device } from '@/src/shared/lib/types/entities' +import { MainMenuMobile } from '@/src/widgets/main-menu' +import { SideMenu } from '@/src/widgets/side-menu' + +const freeRoutes = ['/login', '/register', '/reset', '/changePassword'] interface Props { children: React.ReactNode titlePage: string + device?: Device isAuthPage?: boolean title?: string | React.ReactNode | null isLoader?: boolean } -export const Layout: React.FC = ({ children, isAuthPage = false, titlePage, title = titlePage, isLoader }) => { +export const Layout: React.FC = ({ children, device, 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) @@ -47,15 +45,18 @@ export const Layout: React.FC = ({ children, isAuthPage = false, titlePag const router = useRouter() React.useEffect(() => { - dispatch(getUserBalance(data?.access)) - dispatch(getAllInfo(data?.access)) - }, [data]) + 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')) - ) { + if ((sessionData && !sessionStorage.getItem('firstRender')) || (sessionData && !localStorage.getItem('global_settings'))) { dispatch(getUserAccountSettings(sessionData.access)) sessionStorage.setItem('firstRender', 'true') } @@ -80,16 +81,19 @@ export const Layout: React.FC = ({ children, isAuthPage = false, titlePag targetDevice: device, targetType: 'sidemenu', }) - if (setting) - setting?.value?.sidemenu_state === 'opened' - ? setSidemenuDefaultOpen(true) - : setSidemenuDefaultOpen(false) + if (setting) setting?.value?.sidemenu_state === 'opened' ? setSidemenuDefaultOpen(true) : setSidemenuDefaultOpen(false) } }, [appState.settings.state]) if (status === 'loading' || isLoader) { return ( - + ) @@ -123,14 +127,8 @@ export const Layout: React.FC = ({ children, isAuthPage = false, titlePag desktop ? ( - - + + {children} @@ -0,0 +1 @@ +export { Layout } from '@/src/main/layout/ui/layout' @@ -12,10 +12,7 @@ export const paramsStore = createSlice({ name: 'paramsStore', initialState, reducers: { - setParams: ( - state, - action: { payload: { [key: string]: string | number | number[] | boolean }; type: Object } - ) => { + setParams: (state, action: { payload: { [key: string]: string | number | number[] | boolean }; type: Object }) => { if (Object.keys(action.payload).length === 0) { state.params = {} return @@ -1,12 +1,12 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { createSlice,PayloadAction } from '@reduxjs/toolkit' -const defaultDuration = 4000 +const defaultDuration = 4000; -type Notification = { - id: number - text: string - type: 'error' | 'success' - duration?: number +type Notification = { + id: number, + text: string, + type: 'error' | 'success', + duration?: number } export interface State { @@ -22,16 +22,17 @@ export const notificationSlice = createSlice({ initialState, reducers: { add: (state, action: PayloadAction) => { - const { duration, id } = action.payload - state.notifications = [...state.notifications, action.payload] - setTimeout(() => { - state.notifications = state.notifications.filter((el) => el.id !== id) - }, duration ?? defaultDuration) - }, + const { duration, id } = action.payload + + state.notifications = [...state.notifications, action.payload]; + setTimeout(() => { + state.notifications = state.notifications.filter(el => el.id !== id) + }, duration ?? defaultDuration); + } }, }) export const { add } = notificationSlice.actions -export default notificationSlice.reducer +export default notificationSlice.reducer \ No newline at end of file @@ -1,13 +1,13 @@ import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' import { configureStore } from '@reduxjs/toolkit' -import { balanceSlice } from '#/entities/balance' -import { themeSlice } from '#/entities/theme' -import { userSlice } from '#/entities/user-account' -import { settingsSlice } from '#/entities/user-account/model/settings' -import { stepperSlice } from '#/features/register-business' -import { copySlice } from '#/features/use-copy/copy-slice' -import { paramsStore } from '#/app/store/model-parametres-store' +import { balanceSlice } from '@/src/entities/balance' +import { themeSlice } from '@/src/entities/theme' +import { userSlice } from '@/src/entities/user-account' +import { settingsSlice } from '@/src/entities/user-account/model/settings' +import { stepperSlice } from '@/src/features/register-business' +import { copySlice } from '@/src/features/use-copy/copy-slice' +import { paramsStore } from '@/src/main/store/model-parametres-store' import { notificationSlice } from './notification-slice' @@ -7,14 +7,14 @@ justify-content: space-between; margin-top: 8px; - .side_desk { + .side_desk{ margin-top: 50px; - @media (max-width: 1080px) { + @media(max-width: 1080px){ margin-top: 70px; } - @media (max-width: 990px) { + @media(max-width: 990px){ margin-top: 0; } } @@ -22,7 +22,7 @@ .side_mobile { margin-top: 50px; - @media (max-width: 792px) { + @media(max-width: 792px){ margin-top: 70px; } @@ -31,6 +31,7 @@ } } + @media (max-width: 768px) { width: 100%; } @@ -74,17 +75,17 @@ height: 100%; .list { - width: 100%; + width: 100%; overflow-y: auto; - &::-webkit-scrollbar { + &::-webkit-scrollbar{ height: 5px; width: 2px; } &::-webkit-scrollbar-track { background: initial; - margin: 0; + margin: 0; } &::-webkit-scrollbar-thumb { @@ -0,0 +1,28 @@ +.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 @@ -85,12 +85,14 @@ display: flex; flex-direction: column; } - - .main_block > div { - margin: 0; - } + + .main_block > div { + margin: 0; + } } + + .main_block { display: flex; align-items: flex-start; @@ -0,0 +1,116 @@ +.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); +} @@ -20,19 +20,19 @@ } } -.buttons_block { +.buttons_block{ display: flex; justify-content: flex-start; align-items: center; gap: 20px; } -.title_text { +.title_text{ display: block; font-size: 21px; } -.chapter_block { +.chapter_block{ padding-inline: 20px; } @@ -40,7 +40,7 @@ background-color: var(--background-color-page); } -.logsList { +.logsList{ margin-top: 15px; display: flex; justify-content: flex-start; @@ -49,11 +49,11 @@ gap: 15px; } -.mails { +.mails{ font-weight: 500; color: white; } -.mailWrap { +.mailWrap{ padding: 7px 12px; border-radius: 15px; background-color: rgba(127, 125, 243, 0.68); @@ -63,6 +63,6 @@ align-items: center; gap: 8px; } -.pointer { +.pointer{ cursor: pointer; -} +} \ No newline at end of file @@ -151,8 +151,12 @@ p { /* Изменение цвета активного элемента в выпадающем списке */ .MuiAutocomplete-option.Mui-selected { - background-color: var(--new-ui-main-color); /* Замените #your-selected-color на цвет активного элемента */ - color: var(--new-ui-main-color); /* Замените #your-selected-text-color на цвет текста активного элемента */ + background-color: var( + --new-ui-main-color + ); /* Замените #your-selected-color на цвет активного элемента */ + color: var( + --new-ui-main-color + ); /* Замените #your-selected-text-color на цвет текста активного элемента */ } .introjs-tooltiptext { @@ -2,8 +2,8 @@ import axios, { AxiosResponse } from 'axios' import { User } from 'next-auth' import { JWT } from 'next-auth/jwt' -import { getAll } from '#/entities/user-account/model/user-type-slice' -import { API_URL } from '#/shared/lib/constants' +import { getAll } from '@/src/entities/user-account/model/user-type-slice' +import { API_URL } from '@/src/shared/lib/constants' import { AuthConstants } from './constants' @@ -1,6 +1,117 @@ -import { AudioModelsPage } from '#/views/audio' -import { getDefaultLayout } from '#/widgets/layouts' +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' -AudioModelsPage.getLayout = getDefaultLayout({ titlePage: 'Аудио' }) +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' -export default AudioModelsPage +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 @@ -1,6 +1,413 @@ -import { ChatBotPage } from '#/views/chat-bot' -import { getDefaultLayout } from '#/widgets/layouts' +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' -ChatBotPage.getLayout = getDefaultLayout() +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' -export default ChatBotPage +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 @@ -1,6 +1,78 @@ -import { ChatBotsPage } from "#/views/chat-bot"; -import { getDefaultLayout } from "#/widgets/layouts"; +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' -ChatBotsPage.getLayout = getDefaultLayout({ titlePage: 'Чат-боты' }); +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' -export default ChatBotsPage \ No newline at end of file +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 @@ -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 '#/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 { 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 Title from '../../features/title/title' @@ -106,9 +106,7 @@ 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) { @@ -129,13 +127,7 @@ 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> )) @@ -155,31 +147,19 @@ const Create: React.FC<IDalleProps> = ({ device, token, favorites }) => { } return ( - <Layout isLoader={currentTemplate === null} titlePage={'Копирайтинг'}> - <Title - title={showGeneration ? 'Мои генерации' : theme ? theme : 'Пустой шаблон'} - type={'Копирайтинг'} - linkBack={'/copy'} - /> + <Layout isLoader={currentTemplate === null} titlePage={'Копирайтинг'} device={device}> + <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> )} @@ -198,32 +178,17 @@ 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 @@ -243,18 +208,10 @@ 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 '#/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 { 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 Card from './card' @@ -49,16 +49,12 @@ const CopyPage: React.FC<IDalleProps> = ({ device, token, favorites }) => { }, [data?.access]) return ( - <Layout titlePage={'Копирайтинг'}> + <Layout titlePage={'Копирайтинг'} device={device}> <Typography sx={{ fontSize: 24, fontWeight: 'bold', marginTop: '25px' }}>Копирайтинг</Typography> <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,6 +1,552 @@ -import { ImageModelPage } from '#/views/images' -import { getDefaultLayout } from '#/widgets/layouts' +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' -ImageModelPage.getLayout = getDefaultLayout() +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' -export default ImageModelPage +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 @@ -1,6 +1,76 @@ -import { ImageModelsPage } from "#/views/images"; -import { getDefaultLayout } from "#/widgets/layouts"; +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' -ImageModelsPage.getLayout = getDefaultLayout({ titlePage: 'Изображения' }) +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' -export default ImageModelsPage \ No newline at end of file +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 @@ -1,7 +1,47 @@ -import { ReferralPage } from '#/views/referral' +import React, { useEffect } from 'react' +import { useDispatch } from 'react-redux' +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' export async function getServerSideProps({ params }: { params: Promise<{ email: string }> }) { - return { props: { email: (await params).email } } + const email = (await params).email + + return { + props: { + email, + }, + } +} + +function ReferralRegister({ email }: { email: string }) { + const dispatch = useDispatch() + const { push } = useRouter() + + useEffect(() => { + localStorage.setItem('referral', email) + dispatch(addReferral(email)) + push('/register') + }, []) + + return ( + <Box sx={{ width: '100vw', height: '100vh', background: '#4b4b4b', position: 'relative' }}> + <CircularProgress + size={50} + thickness={5} + sx={{ + color: '#7F7DF3', + position: 'absolute', + top: '40%', + right: 0, + left: 0, + margin: '0 auto', + }} + /> + </Box> + ) } -export default ReferralPage +export default ReferralRegister @@ -1,3 +1,56 @@ -import { NotFoundPage } from '#/views/not-found' +import React from 'react' +import { Box, Typography } from '@mui/material' +import Link from 'next/link' +import { useRouter } from 'next/router' -export default NotFoundPage +import { Layout } from '@/src/main/layout' +import { useAppSelector } from '@/src/main/store/store' + +const Custom404 = () => { + const theme = useAppSelector((state) => state.theme.theme) + + const { push } = useRouter() + + React.useEffect(() => { + setTimeout(() => push('/'), 7000) + }, []) + + return ( + <Layout titlePage={'Неизвестная страница'} isAuthPage={true}> + <Box display='flex' justifyContent='center' alignItems='center' sx={{ width: '100%', height: '90vh' }}> + <Box sx={{ width: '410px', textAlign: 'center' }}> + <Typography + sx={{ + fontSize: '23px', + color: theme === 'light' ? 'dark' : 'white', + }} + > + Ошибка 404 + </Typography> + <Typography + sx={{ + fontSize: '19px', + color: theme === 'light' ? 'dark' : 'white', + marginTop: 1.5, + }} + > + К сожалению, такая страница не найдена 😢 + </Typography> + <Link href='/'> + <Typography + sx={{ + fontSize: '18px', + marginTop: 2, + color: '#7F7DF3', + }} + > + Вернуться на главную + </Typography> + </Link> + </Box> + </Box> + </Layout> + ) +} + +export default Custom404 @@ -1,6 +1,6 @@ -import React, { ReactElement, ReactNode } from 'react' +import React from 'react' import { Provider } from 'react-redux' -import { StyledEngineProvider, ThemeProvider } from '@mui/material' +import { StyledEngineProvider } from '@mui/material' import axios from 'axios' import * as https from 'https' import type { AppProps } from 'next/app' @@ -8,14 +8,12 @@ import { Raleway } from 'next/font/google' import { SessionProvider } from 'next-auth/react' import { appWithTranslation } from 'next-i18next' -import { store } from '#/app/store/store' +import { store } from '@/src/main/store/store' import ErrorBoundary from './error-boundary' -import '#/app/styles/globals.css' -import '#/app/styles/styles-pages/system.scss' -import { NextPage } from 'next' -import { pingFangFont } from '#/shared/lib/constants/font/font' +import '@/src/main/styles/globals.css' +import '@/src/main/styles/styles-pages/system.scss' axios.defaults.httpsAgent = new https.Agent({ rejectUnauthorized: false, @@ -23,17 +21,7 @@ axios.defaults.httpsAgent = new https.Agent({ const inter = Raleway({ subsets: ['latin'] }) -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) +function App({ Component, pageProps: { session, ...pageProps } }: AppProps) { return ( <> @@ -47,9 +35,7 @@ function App({ Component, pageProps: { session, ...pageProps } }: AppPropsWithLa <StyledEngineProvider injectFirst> <Provider store={store}> <SessionProvider session={session} refetchInterval={5 * 60}> - <ThemeProvider theme={pingFangFont}> - {getLayout(<Component {...pageProps} />)} - </ThemeProvider> + <Component {...pageProps} /> </SessionProvider> </Provider> </StyledEngineProvider> @@ -27,11 +27,7 @@ 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 /> @@ -1,6 +1,581 @@ -import { AccountPage } from '#/views/account' -import { getDefaultLayout } from '#/widgets/layouts' +import * as React from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' +import { Avatar, Box, Button, Stack, Tab, Tabs, Typography } from '@mui/material' +import CircularProgress from '@mui/material/CircularProgress' +import axios from 'axios' +import Image from 'next/image' +import { useRouter } from 'next/router' +import { getServerSession } from 'next-auth' +import { signOut, useSession } from 'next-auth/react' -AccountPage.getLayout = getDefaultLayout({ titlePage: 'Аккаунт' }) +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' -export default AccountPage +import { DownloadModal } from '../features/business-security-download/ui/download-modal' + +const scopes = [ + { title: 'Настройки', scope: 'setting' }, + { title: 'Управление оплатой', scope: 'subscribe' }, + { title: 'Корпоративный аккаунт', scope: 'business' }, + { title: 'Реферальная программа', scope: 'referral' }, +] + +export interface IPaymentsPlan { + uid: string + price: string + tokens_per_plan: string +} + +interface IAccountProps extends IProps { + plans: IPaymentsPlan[] | null + redirect?: { + destination: string + permanent: boolean + } +} + +export async function getServerSideProps(context: any): Promise<{ props: IAccountProps }> { + const device = getTypeDevice(context) + + const token = (await getAccessToken(context.req)) || null + + //@ts-ignore + const session = await getServerSession(context.req, context.res) + + const plans = token ? await accountApi.getPaymentsPlans(token) : null + + //@ts-ignore + if (!session) { + return { + //@ts-ignore + redirect: { + destination: '/', + permanent: false, + }, + } + } + return { + props: { + device, + token, + plans, + }, + } +} + +const Account: React.FC<IAccountProps> = ({ device, token, plans }) => { + const { + email, + is_subscribed_to_emails, + account_type, + first_name, + last_name, + username, + profile_picture_link, + is_social, + status: userInfoLoaded, + referral_code, + } = useAppSelector((state) => state.user) + + const { status, account_type: type } = useAppSelector((state) => state.user) + + const [name, setName] = useState('') + + const [lastName, setLastName] = useState('') + + const fileInputRef = useRef(null) + + const [loading, setLoading] = useState(false) + + const { data } = useSession() + const handleDivClick = () => { + ;(fileInputRef.current! as any).click() + } + + const dispatch = useAppDispatch() + + const { error, showError, isError } = useShowData() + + const handleFileChange = async (event: any) => { + const formData = new FormData() + formData.append('new_picture', event.target.files[0]) + + try { + setLoading(true) + await axios.put(API_URL + '/auth/reset-profile-pic', formData, { + headers: { + Authorization: `Bearer ${data?.access}`, + 'Content-Type': 'multipart/form-data', + }, + }) + dispatch(getAllInfo(data?.access)) + showError('Изображение успешно загружено!') + setLoading(false) + } catch (e) { + setLoading(false) + showError('Ошибка загрузки изображения на сервере!', true) + } + } + + const [currentPassword, setCurrentPassword] = React.useState<string>('') + + const [newPassword1, setNewPassword1] = React.useState<string>('') + + const [newPassword2, setNewPassword2] = React.useState<string>('') + + const [promocode, setPromocode] = React.useState<string>('') + + const [success, setSuccess] = React.useState('') + + const [confirmDeleteModal, setConfirmDeleteModal] = useState(false) + + const { push, query } = useRouter() + + const [scope, setScope] = useState(query['scope'] || 'setting') + + const [downloadModal, setDownloadModal] = useState<boolean>(false) + + const desktop = device === 'desktop' + + const changeScope = async (scope: string) => { + setScope(scope) + await addQueryParams(scope) + } + + async function addQueryParams(scope: string) { + await push({ + pathname: '/account', + query: { scope }, + }) + } + + useEffect(() => { + if (query['scope'] !== undefined && scope !== query['scope']) { + changeScope(query['scope'] as string) + } + }, [query]) + + useEffect(() => { + if (Object.keys(query).length === 0) addQueryParams('setting') + }, []) + + const changePassword = async () => { + if (newPassword1 !== newPassword2) { + showError('Укажите одинаковые новы пароли!', true) + return + } + const status = await accountApi.changePassword(token, newPassword1, newPassword2, currentPassword) + + if (status === 200) { + showError('Пароль успешно изменён!') + setNewPassword1('') + setNewPassword2('') + setCurrentPassword('') + setTimeout(() => setSuccess(''), 6000) + return + } + + showError('К сожалению, произошла ошибка', true) + } + + function body() { + if (type === 'regular') { + return ( + <Box width='80vw' height='60vh' display='flex' alignItems='center' justifyContent='center'> + <ScreenForInactive /> + </Box> + ) + } + if (type === 'business_account') { + return <Box className={styles.main}>Информация о корп.аккаунте доступна только владельцу и администраторам.</Box> + } + if (type === 'business_host') { + return <BusinessHost /> + } + if (type === 'business_admin') { + return <BusinessHost /> + } + if (type === 'business_security') { + return ( + <> + <DownloadModal open={downloadModal} setOpen={setDownloadModal} /> + <Box className={styles.main}> + <Info security={true} /> + <Typography className={styles.tite_text} sx={{ marginTop: '20px' }}> + Раздел 1. Запросы сотрудников по моделям + </Typography> + <Box className={styles.chapter_block}> + <ButtonUI + onClick={() => { + setDownloadModal(true) + }} + sx={{ marginTop: '20px' }} + text={'Скачать'} + /> + </Box> + </Box> + </> + ) + } + } + + const isUserDataChange = useMemo(() => name !== first_name || last_name !== lastName, [name, lastName]) + + const promocodeActivate = async () => { + if (!promocode.trim()) { + showError('Введите корректный промокод', true) + return + } + let resStatus = 404 + try { + const { status } = await axios.post( + API_URL + '/payments/promocode', + { code: promocode }, + { headers: { Authorization: `Bearer ${token}` } } + ) + resStatus = status + } catch (e) {} + + if (resStatus === 200) { + showError('Промокод успешно активирован! Токены уже зачислены!') + dispatch(getUserBalance(data?.access)) + return + } + + if (resStatus === 403) { + showError('Промокод уже был активирован!', true) + return + } + + if (resStatus === 404) { + showError('Промокод не найден!', true) + return + } + } + + async function changeUserData() { + if (!isUserDataChange) { + showError('Вы не изменили данные', true) + return + } + + try { + await axios.put( + API_URL + '/auth/user-data', + { + username: username, + email: email, + first_name: name, + last_name: lastName, + }, + { headers: { Authorization: `Bearer ${token}` } } + ) + + showError('Данные успешно изменены!') + dispatch(getAllInfo(token)) + } catch (e) {} + } + + const deleteAccount = async () => { + try { + const { status } = await axios.delete(API_URL + '/auth/remove', { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + + if (status === 200) await signOut() + } catch (err) {} + } + + useEffect(() => { + setName(first_name) + setLastName(last_name) + }, [first_name, last_name]) + + return ( + <Layout titlePage={'Аккаунт'} device={device}> + <Box + sx={{ + width: '100%', + height: '100%', + marginTop: desktop ? 3 : 2, + }} + > + <Stack + direction={desktop ? 'row' : 'column'} + justifyContent='start' + alignItems={desktop ? 'flex-start' : 'center'} + spacing={2} + sx={{ overflowY: 'auto', overflowX: 'hidden' }} + > + <Stack + direction='column' + justifyContent='flex-start' + alignItems='flex-start' + spacing={2} + sx={{ + width: desktop ? '90%' : '100%', + height: '100%', + }} + > + <Typography sx={{ fontSize: 24, fontWeight: 'bold' }}>Ваш аккаунт</Typography> + <Tabs + value={scope} + variant='scrollable' + className={styles2.wrap_toggle_button} + aria-label='Platform' + scrollButtons={false} + sx={{ '& .MuiTabs-indicator': { display: 'none' } }} + > + {scopes.map((el) => { + if (!desktop && el.scope === 'business') { + return null + } + if (el.scope === 'referral' && account_type !== 'regular') { + return null + } + return ( + <Tab + disableRipple + onClick={() => changeScope(el.scope)} + key={el.title} + className={scope === el.scope ? styles2.wrap_toggle_button_active : styles2.wrap_toggle_button} + value={el.scope} + label={el.title} + /> + ) + })} + </Tabs> + {scope === 'setting' ? ( + <> + <Box className={styles2.wrap_block}> + <Typography className='title-block'>Основные</Typography> + <Box className={styles2.main_block}> + <Box sx={{ marginRight: 2 }}> + <Box onClick={handleDivClick} sx={{ cursor: 'pointer', position: 'relative' }}> + <input + type='file' + ref={fileInputRef} + style={{ display: 'none' }} + onChange={handleFileChange} + accept='image/*' + /> + <Box + sx={{ + position: 'absolute', + zIndex: 100, + top: '40px', + right: '37px', + }} + > + {!loading || !(userInfoLoaded === 'succeeded') ? ( + <Image src={'/Edit.svg'} height={30} width={30} alt={'Image change'} /> + ) : ( + <Loader /> + )} + </Box> + <Avatar + src={profile_picture_link as string} + sx={{ + width: 110, + height: 110, + backgroundColor: '#8280FF', + }} + > + {''} + </Avatar> + </Box> + </Box> + <Box width='100%' sx={{ marginLeft: '30px' }}> + <Box sx={{ marginTop: '0px' }}> + <Typography className={styles2.input_title}>Имя</Typography> + <Input + inputProps={{ maxLength: 15 }} + value={name} + onChange={(e) => setName(e.target.value)} + fullWidth + /> + </Box> + <Box sx={{ marginTop: '15px' }}> + <Typography className={styles2.input_title}>Фамилия</Typography> + <Input + inputProps={{ maxLength: 15 }} + value={lastName} + onChange={(e) => setLastName(e.target.value)} + fullWidth + /> + </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' }}> + <SwitchCustom + checked={is_subscribed_to_emails} + onChange={async () => { + await dispatch(unfollowEmail(token)) + showError('Данные изменены!') + }} + /> + <Typography + sx={{ + color: '#868686', + fontSize: '15px', + marginLeft: 1, + }} + > + Отписаться от рассылки + </Typography> + </Box> + </Box> + <Input value={email} fullWidth /> + </Box> + <Box sx={{ marginTop: '15px' }}> + <Typography className={styles2.input_title}>Никнейм</Typography> + <Input value={username} fullWidth /> + </Box> + <ButtonUI onClick={changeUserData} sx={{ marginTop: '20px' }} text={'Сохранить'} /> + {/*referral_code.code.trim() && ( + <Box marginTop={1} display='flex'> + <Typography className='text'>Ваша реферальная ссылка:</Typography> +   + <TooltipCustom title='Нажмите чтобы скопировать'> + <Typography + onClick={() => navigator.clipboard.writeText(referral_code.code.trim())} + className='text' + sx={{ color: '#8280FF !important', cursor: 'pointer' }} + > + {referral_code.code} + </Typography> + </TooltipCustom> + </Box> +) */} + </Box> + </Box> + </Box> + <Stack spacing={2} className={styles2.wrap_block}> + <Typography className='title-block'>Активация промокода</Typography> + <Input + placeholder='Введите ваш промокод' + value={promocode} + onChange={(e) => setPromocode(e.target.value)} + fullWidth + /> + <Button onClick={promocodeActivate} className='btn-classic'> + Активировать + </Button> + </Stack> + {!is_social && ( + <Box className={styles2.wrap_block}> + <Typography className='title-block'>Изменить пароль</Typography> + <Box className={styles2.main_block} flexDirection='column'> + <Box sx={{ width: '100%' }}> + <Typography className={styles2.input_title}>Текущий пароль</Typography> + <Input + placeholder='Укажите текущий пароль' + value={currentPassword} + onChange={(e) => setCurrentPassword(e.target.value)} + fullWidth + /> + </Box> + <Box display='flex' justifyContent='space-between' sx={{ width: '100%', marginTop: '15px' }}> + <Box sx={{ width: '48%' }}> + <Typography className={styles2.input_title}>Новый пароль</Typography> + <Input + placeholder='Укажите новый пароль' + value={newPassword1} + onChange={(e) => setNewPassword1(e.target.value)} + fullWidth + /> + </Box> + <Box sx={{ width: '48%' }}> + <Typography className={styles2.input_title}>Подтвердить пароль</Typography> + <Input + placeholder='Укажите новый пароль ещё раз' + value={newPassword2} + onChange={(e) => setNewPassword2(e.target.value)} + fullWidth + /> + </Box> + </Box> + <ButtonUI + onClick={changePassword} + sx={{ marginTop: '20px' }} + text={'Изменить пароль'} + style={{ width: '150px' }} + /> + </Box> + </Box> + )} + <Stack spacing={2} className={styles2.wrap_block} sx={{ marginBottom: 5 }}> + <Typography className='title-block'>Удаление аккаунта</Typography> + <Typography className='text'>Удаление аккаунта приведет к потере всех настроек</Typography> + <ButtonUI + fullWidth + onClick={() => setConfirmDeleteModal(true)} + sx={{ marginTop: '20px' }} + text={'Удалить аккаунт'} + style={{ + width: '100%', + backgroundColor: 'rgba(255, 35, 114, 0.10)', + color: '#FF2372', + }} + /> + </Stack> + + <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'> + Удалить + </Button> + </Stack> + </Modal> + </> + ) : scope === 'business' ? ( + <Box> + {status == 'pending' || type === null ? ( + <Box className={styles.loading}> + <CircularProgress + size={25} + thickness={5} + sx={{ + color: '#7F7DF3', + }} + /> + </Box> + ) : ( + <Box>{body()}</Box> + )} + </Box> + ) : scope === 'subscribe' ? ( + <Subscription device={device} /> + ) : scope === 'referral' && account_type === 'regular' ? ( + <Referral device={device} /> + ) : ( + <></> + )} + <Success message={error} open={Boolean(error)} isError={isError} /> + </Stack> + </Stack> + </Box> + </Layout> + ) +} + +export default Account @@ -1,6 +1,49 @@ -import { AdminPage } from '#/views/admin' -import { getDefaultLayout } from '#/widgets/layouts' +import React from 'react' +import { Stack } from '@mui/material' +import { getSession } from 'next-auth/react' -AdminPage.getLayout = getDefaultLayout({ titlePage: 'Админка' }) +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' -export default AdminPage +export async function getServerSideProps(context: any): Promise<{ props: any }> { + const device = getTypeDevice(context) + + const token = (await getAccessToken(context.req)) || null + + const { req } = context + const session = await getSession({ req }) + + if (true) { + return { + //@ts-ignore + redirect: { + destination: '/', + permanent: false, + }, + } + } + + return { + props: { + device, + token, + }, + } +} + +const Admin: React.FC<IProps> = ({ token, device }) => { + const theme = useAppSelector((state) => state.theme.theme) + + return ( + <Layout titlePage={'Статистика'} device={device}> + <Stack direction='column' justifyContent='flex-start' alignItems='center' sx={{ width: '80vw', margin: '40px auto' }}> + <StatsField device={device} token={token} theme={theme} /> + </Stack> + </Layout> + ) +} + +export default Admin @@ -1,6 +1,219 @@ -import { ApiKeysPage } from '#/views/api-keys' -import { getDefaultLayout } from '#/widgets/layouts' +import React, { useEffect, useState } from 'react' +import { Box, Stack, Table, TableBody, TableHead, TableRow, TextField, Typography } from '@mui/material' +import Button from '@mui/material/Button' +import TableCell from '@mui/material/TableCell' +import axios from 'axios' +import Image from 'next/image' +import Link from 'next/link' +import { useSession } from 'next-auth/react' -ApiKeysPage.getLayout = getDefaultLayout({ device: 'desktop', titlePage: 'API-ключи' }) +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' -export default ApiKeysPage +const ApiKeys: React.FC = () => { + const [keys, setKeys] = useState<Array<any> | null>(null) + const [loading, setLoading] = useState(false) + const { data, status } = useSession() + const [modal, setModal] = useState<boolean>(false) + const [userInfo, setUserInfo] = useState<ResponseAllInfo | null>() + const [disabled, setDisabled] = React.useState<boolean>(true) + + useEffect(() => { + getAll(data?.access).then((res) => setUserInfo(res)) + }, [data?.access]) + + useEffect(() => { + if (status === 'authenticated') { + setLoading(true) + accountApi.getApiKeys(data?.access).then((res) => { + setKeys(res) + setLoading(false) + }) + } + }, [status]) + + React.useEffect(() => { + if ( + userInfo?.account_type === 'business_security' || + userInfo?.account_type === 'business_account' || + status !== 'authenticated' || + loading + ) { + setDisabled(true) + } else { + setDisabled(false) + } + }, [status, userInfo, loading]) + + const deleteKey = async (name: string) => { + if (keys === null || status !== 'authenticated') { + return + } + + setLoading(true) + const result = await accountApi.deleteApiKey(name, data?.access) + if (result !== null && result === 200) { + accountApi.getApiKeys(data?.access).then((res) => { + setKeys(res) + setLoading(false) + }) + } + setLoading(false) + } + + return ( + <> + <ApiKeyModal open={modal} setOpen={setModal} keys={keys} setKeys={setKeys} /> + <Layout titlePage={'API ключи'} device={'desktop'}> + <Box style={{ marginTop: '20px', marginBottom: '20px' }}> + <Typography sx={{ fontSize: 35, fontWeight: '600' }}>API ключи</Typography> + </Box> + <Stack spacing={2} className={styles.wrap}> + <Stack spacing={2} className='pd-30 bg-color-block border-radius-main'> + <Typography className='text'> + API-ключ — это инструмент, который идентифицирует пользователя или программу, запрашивающих доступ к API платформы. + С помощью ключа можно отслеживать, кто и когда пользуется API, рассчитывать оплату. + </Typography> + <Box display='flex' alignItems='center'> + <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> + </Link> + </Box> + </Stack> + + {keys && keys.length !== 0 ? ( + <Stack spacing={1} className='pd-30 bg-color-block border-radius-main'> + <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 } }}> + <TableCell align='left'>Имя</TableCell> + <TableCell align='left'>Ключ</TableCell> + <TableCell align='left'>Лимит токенов</TableCell> + <TableCell align='left'>Создан</TableCell> + <TableCell align='left'>Действителен до</TableCell> + </TableRow> + </TableHead> + <TableBody> + {keys && + keys.map((el) => { + return ( + <KeyRow + key={el.key} + keyValue={el.key} + name={el.name} + limit={el.token_limit} + created_at={el.created_at.split('T')[0]} + expires_at={el.expires_at} + deleteKey={deleteKey} + /> + ) + })} + </TableBody> + </Table> + </Stack> + ) : ( + <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> + </Box> + </Box> + )} + </Stack> + </Layout> + </> + ) +} + +const KeyRow = (props: any) => { + const [isCopy, setIsCopy] = useState(false) + const [limit, setLimit] = useState(props.limit) + const theme = useAppSelector((state) => state.theme.theme) + const { data: session } = useSession() + + const copy = (text: string) => { + navigator.clipboard.writeText(text) + setIsCopy(true) + setTimeout(() => setIsCopy(false), 3000) + } + + useEffect(() => { + let timeout = window.setTimeout(() => { + if (limit !== props.limit) { + axios.patch( + API_URL + '/public/api-key', + { token_limit: Number(limit), name: props.name }, + { headers: { Authorization: `Bearer ${session?.access}` } } + ) + } + }, 1000) + return () => window.clearTimeout(timeout) + }, [limit]) + + return ( + <TableRow key={props.key} sx={{ '&:last-child td, &:last-child th ': { border: 0 } }}> + <TableCell component='th' align='left' scope='row'> + {props.name} + </TableCell> + <TableCell component='th' align='left' sx={{ color: '#8280FF !important' }}> + {props.keyValue} + </TableCell> + <TableCell component='th' align='center' sx={{ color: '#8280FF !important' }} width={160}> + <TextField + value={limit} + placeholder={limit === null || limit === '' ? 'Бесконечно' : ''} + type='text' + onChange={(e) => setLimit(e.target.value)} + sx={theme === 'light' ? { ...InputStyleSmallLight } : { ...InputStyleSmallDark }} + /> + </TableCell> + <TableCell component='th' align='left'> + {props.created_at.split('T')[0]} + </TableCell> + <TableCell component='th' align='left'> + {props.expires_at !== null ? props.expires_at : 'Бессрочно'} + </TableCell> + <TableCell component='th' align='left'> + <Stack direction='row' spacing={2}> + {isCopy ? ( + <Image src={'/svg/tic.svg'} width={20} height={22} alt={'copy'} /> + ) : ( + <TooltipCustom title={'Скопировать ключ'}> + <Image + onClick={() => copy(props.keyValue)} + src={'/svg/copy.svg'} + width={20} + height={20} + style={{ cursor: 'pointer' }} + alt={'copy'} + /> + </TooltipCustom> + )} + <Image + onClick={() => props.deleteKey(props.name)} + style={{ cursor: 'pointer' }} + src='/svg/main_menu/trash.svg' + width={20} + height={20} + alt='Удалить' + /> + </Stack> + </TableCell> + </TableRow> + ) +} + +export default ApiKeys @@ -1,6 +1,50 @@ -import AuthSocial from '#/views/auth-social/ui/auth-social' -import { getDefaultLayout } from '#/widgets/layouts' +import React, { useEffect } from 'react' +import { Box } from '@mui/system' +import axios from 'axios' +import { useRouter } from 'next/router' +import process from 'process' -AuthSocial.getLayout = getDefaultLayout({ titlePage: 'Редирект' }) +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' + +const client_secret = process.env.NEXT_PUBLIC_DJANGO_GOOGLE_APP_CLIENT_SECRET +const client_id = process.env.NEXT_PUBLIC_DJANGO_GOOGLE_APP_CLIENT_ID + +export async function getServerSideProps(context: any): Promise<{ props: { device: Device } }> { + const device = getTypeDevice(context) + + return { + props: { + device, + }, + } +} + +async function getTokenByAPIGoogle(token: string) { + const { data } = await axios.post(API_URL + '/auth/login-social/convert-token', { + grant_type: 'convert_token', + client_id: client_id, + client_secret: client_secret, + backend: 'google-oauth2', + token: token, + }) + + return data +} + +const AuthSocial: React.FC<{ device: Device }> = ({ device }) => { + useEffect(() => { + const token = new URLSearchParams(window.location.href.replace('#', '&')).get('access_token') || '' + getTokenByAPIGoogle(token).then((res) => res) + }, []) + + return ( + <Layout titlePage={'Подтверждение авторизации...'} device={device}> + <Box></Box> + </Layout> + ) +} export default AuthSocial @@ -1,6 +0,0 @@ -import { ChangePasswordPage } from '#/views/change-password' -import { getLayout, LayoutWithoutSideMenu } from '#/widgets/layouts' - -ChangePasswordPage.getLayout = getLayout(LayoutWithoutSideMenu, { titlePage: 'Смена пароля' }) - -export default ChangePasswordPage @@ -1,23 +1,35 @@ -import React, { useRef, useState } from 'react' +import React from 'react' import { Box, Card, CardMedia, Stack, TextField, Typography } from '@mui/material' import Button from '@mui/material/Button' import axios from 'axios' +import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' import { useRouter } from 'next/router' -import { useAppSelector } from '#/app/store/store' -import { API_URL } from '#/shared/lib/constants/constants' -import { getDeviceType, getRandomImage } from '#/shared/lib/helpers' -import { NextPageWithLayout } from '#/pages/_app' -const ChangePassword: NextPageWithLayout = () => { - const device = getDeviceType() +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' - const [password1, setPassword1] = useState<string>('') +export async function getServerSideProps(context: any): Promise<{ props: TDeviceProp }> { + const device = getTypeDevice(context) - const [password2, setPassword2] = useState<string>('') + return { + props: { + device, + }, + } +} + +const ChangePassword: React.FC<TDeviceProp> = ({ device }) => { + const [password1, setPassword1] = React.useState<string>('') - const [isChange, setIsChange] = useState(false) + const [password2, setPassword2] = React.useState<string>('') + + const [isChange, setIsChange] = React.useState(false) const { query, push } = useRouter() @@ -42,12 +54,18 @@ const ChangePassword: NextPageWithLayout = () => { const desktop = device === 'desktop' - const refImage = useRef(getRandomImage()) + const refImage = React.useRef(getRandomImage()) const theme = useAppSelector((state) => state.theme.theme) return ( - <> + <Layout titlePage={'Авторизация'} isAuthPage={true}> + <Head> + <title>Восстановление аккаунта + + + + { {desktop && ( - + { )} - + ) } @@ -1,6 +1,83 @@ -import { ConfirmPage } from '#/views/confirm' -import { getLayout, LayoutWithoutSideMenu } from '#/widgets/layouts' +import React from 'react' +import { Box, Typography } from '@mui/material' +import axios from 'axios' +import Link from 'next/link' +import { useRouter } from 'next/router' -ConfirmPage.getLayout = getLayout(LayoutWithoutSideMenu, { titlePage: 'Подтверждение' }) +import { Layout } from '@/src/main/layout' +import { API_URL } from '@/src/shared/lib/constants/constants' -export default ConfirmPage +export async function getServerSideProps(context: any): Promise<{ props: any }> { + const UA = context.req.headers['user-agent'] + const isMobile = Boolean(UA.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i)) + return { + props: { + device: isMobile ? 'mobile' : 'desktop', + }, + } +} + +const Confirm: React.FC = () => { + const { push, query } = useRouter() + const [success, setSuccess] = React.useState(false) + + React.useEffect(() => { + let formData: any = new FormData() + + formData.append('token', query.token) + + axios.post(API_URL + '/auth/confirm', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) + .then(() => { + setSuccess(true) + }) + .catch(() => { + push('/') + }) + }, []) + + React.useEffect(() => { + if (success) { + setTimeout(() => push('/login'), 10000) + } + }, [success]) + + return ( + + + + {' '} + {success ? ( + <> + + Ваша почта подтверждена, вы будете перенаправлены на страницу авторизации + + + Войти в аккаунт + + + ) : ( + 'Подтверждение...' + )} + + + + ) +} + +export default Confirm @@ -1,7 +1,88 @@ -import { MainPage } from '#/views/index' -import { getDefaultLayout } from '#/widgets/layouts' +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 { 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' -MainPage.getLayout = getDefaultLayout({ titlePage: 'AIR' }) +import { DemoInfo } from '../shared/ui/demo-info' +import { SocialMedia } from '../shared/ui/social-media-block' +import { Statistics } from '../widgets/stats/stats' -export default MainPage +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 = ({ 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 ( + + + + {time} + {greeting} + + + + {/**/} + + {account_type === 'regular' && } + + + Полезная статья + + + {' '} + Что такое токен ? + + + Токен — это цифровая валюта для оплаты ИИ-продуктов и услуг. Юзеры используют эту валюту, чтобы + получить либо доступ к нейросетям, либо результат генерации — текст, картинку и т.д. Еще токенами + называют единицы текста, на которых обучается нейросеть. Например, в предложении «‎Я люблю шоколад» + слова «‎Я», «‎люблю» и «шоколад» могут быть токенами. + + + + + + + + + ) +} + +export default Main @@ -1,6 +1,47 @@ -import { RegisterBusinessPage } from '#/views/register-business' -import { getLayout, LayoutWithoutSideMenu } from '#/widgets/layouts' +import React from 'react' +import { getSession } from 'next-auth/react' -RegisterBusinessPage.getLayout = getLayout(LayoutWithoutSideMenu, { titlePage: 'Регистрация' }) +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' -export default RegisterBusinessPage +type BusinessProps = { + device: Device + token: string | null +} +export async function getServerSideProps(context: any): Promise<{ props: BusinessProps }> { + const token = (await getAccessToken(context.req)) || null + + const device = getTypeDevice(context) + + const { req } = context + const session = await getSession({ req }) + + //@ts-ignore + if (!token) { + return { + //@ts-ignore + redirect: { + destination: '/', + permanent: false, + }, + } + } + + return { + props: { + device, + token, + }, + } +} +const Register: React.FC = ({ device }) => { + return ( + + + + ) +} + +export default Register @@ -1,6 +1,246 @@ -import { ReportsMobilePage } from '#/views/reports-mobile' -import { getDefaultLayout } from '#/widgets/layouts' +import React, { ChangeEvent } from 'react' +import AttachFileIcon from '@mui/icons-material/AttachFile' +import CloseIcon from '@mui/icons-material/Close' +import { Box, Button, Stack, TextField, Typography } from '@mui/material' +import axios from 'axios' +import { useSession } from 'next-auth/react' -ReportsMobilePage.getLayout = getDefaultLayout({ titlePage: 'Сообщить об ошибке', device: 'mobile' }) +import { Layout } from '@/src/main/layout' +import { useAppSelector } from '@/src/main/store/store' +import { API_URL } from '@/src/shared/lib/constants/constants' -export default ReportsMobilePage +const styleSend = { + position: 'absolute' as 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -65%)', + width: 300, + height: 'auto', + bgcolor: 'background.paper', + borderRadius: 5, + boxShadow: 16, + p: 4, + textAlign: 'center', +} + +const styleSendDark = { + position: 'absolute' as 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -65%)', + width: 300, + height: 'auto', + bgcolor: '#2B2828', + borderRadius: 5, + boxShadow: 16, + p: 4, + textAlign: 'center', +} + +const styleMobile = { + width: '100%', + position: 'absolute' as 'absolute', + top: '35%', + left: '50%', + transform: 'translate(-50%, -50%)', + p: 4, + zIndex: 99, +} + +const ReportsMobile = () => { + const [errorMessage, setErrorMessage] = React.useState('') + + const [error, setError] = React.useState(false) + + const [isSend, setIsSend] = React.useState(false) + + const [file, setFile] = React.useState() + + const { data: session } = useSession() + + const theme = useAppSelector((state) => state.theme.theme) + + const email = useAppSelector((state) => state.user.email) + + const sendError = async () => { + if (errorMessage.trim() === '') { + setError(true) + setTimeout(() => setError(false), 3000) + return + } + + let formData: any = new FormData() + + formData.append('report_text', `${email}: ${errorMessage}`) //append the values with key, value pair + if (file) { + formData.append('images', file) + } + + try { + const { status } = await axios.post(API_URL + '/reports/', formData, { + headers: { + 'content-type': 'multipart/form-data ', + 'content-length': file ? `${file.size}` : '', + Authorization: `Bearer ${session?.access}`, + }, + }) + + if (status === 201) { + setErrorMessage('') + setIsSend(true) + setTimeout(() => { + setIsSend(false) + }, 4000) + } + } catch (err) { + setError(true) + setTimeout(() => setError(false), 3000) + } + } + + const handleFileChange = (e: ChangeEvent) => { + if (e.target.files) { + setFile(e.target.files[0]) + } + } + + return ( + + {isSend ? ( + + Спасибо! + + Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при регистрации + + + ) : ( + + + Сообщить об ошибке + + setErrorMessage(evt.target.value)} + InputProps={{ + startAdornment: ( + <> + + + ), + }} + sx={{ + marginTop: 2.5, + marginBottom: 1.2, + fontSize: 13, + '& label': { color: 'red' }, + backgroundColor: theme === 'light' ? 'transparent' : '#3D3D3D', + input: { + color: theme === 'light' ? '#272727' : '#E1E1E1', + }, + }} + /> + {file && ( + + Добавлено 1 изображение: {file.name} + setFile(undefined)} + sx={{ + width: '18px', + height: '18px', + cursor: 'pointer', + marginLeft: '3px', + marginTop: '2px', + }} + /> + + )} + + + + + + )} + + ) +} + +export default ReportsMobile @@ -1,6 +1,173 @@ -import { ResetPage } from '#/views/reset' -import { getLayout, LayoutWithoutSideMenu } from '#/widgets/layouts' +import * as React from 'react' +import { useState } from 'react' +import { Box, Card, CardMedia, Link, Stack, TextField, Typography } from '@mui/material' +import Button from '@mui/material/Button' +import axios from 'axios' +import Image from 'next/image' +import Router from 'next/router' -ResetPage.getLayout = getLayout(LayoutWithoutSideMenu, { titlePage: 'Сброс пароля' }) +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' -export default ResetPage +export async function getServerSideProps(context: any): Promise<{ props: TDeviceProp }> { + const device = getTypeDevice(context) + + return { + props: { + device, + }, + } +} + +const Reset: React.FC = ({ device }) => { + const [input_email, set_Email] = useState('') + const [isSend, setIsSend] = useState(false) + const [isError, setIsError] = React.useState(false) + const sendMail = () => { + axios.post(API_URL + '/auth/update-pass', { + email: input_email, + }) + .then(function (response) { + if (response.status === 200) { + setIsSend(true) + setTimeout(() => Router.push('/login'), 10000) + } + }) + .catch(function (error) { + setIsError(true) + setTimeout(() => setIsError(false), 4000) + }) + } + + const desktop = device === 'desktop' + + const refImage = React.useRef(getRandomImage()) + + const theme = useAppSelector((state) => state.theme.theme) + + return ( + + + {desktop && ( + + + + + Midjourney + + + by honeynek + + + + + )} + {!isSend ? ( + + + Забыли пароль? + + + Введите адрес электронной почты, которую вы использовали для регистрации в сервисе + + + + Email + + + set_Email(event.target.value)} type={'email'} fullWidth /> + + + + + + + + + + ) : ( + + + Письмо для восстановления пароля отправлено вам на почту! + + + )} + + + ) +} + +export default Reset @@ -1,6 +1,30 @@ -import { WhisperPage } from '#/views/whisper' -import { getDefaultLayout } from '#/widgets/layouts' +import React from 'react' +import { Box } from '@mui/material' -WhisperPage.getLayout = getDefaultLayout({ titlePage: 'Whisper' }) +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' -export default WhisperPage +export async function getServerSideProps(context: any): Promise<{ props: { device: Device } }> { + const device = getTypeDevice(context) + + return { + props: { + device, + }, + } +} +const Whisper: React.FC<{ device: Device }> = ({ device }) => { + return ( + + + + + + + + ) +} + +export default Whisper @@ -1,7 +1,7 @@ import axios from 'axios' -import { IModel, IShortModel } from '#/shared/api/models/models' -import { API_URL } from '#/shared/lib/constants' +import { IModel, IShortModel } from '@/src/shared/api/models/models' +import { API_URL } from '@/src/shared/lib/constants' const model_api = { async getBots(token?: string): Promise { @@ -4,12 +4,12 @@ import base64 from 'base64-encode-file' import { useSession } from 'next-auth/react' import process from 'process' -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' +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' const formDataHelper = (file: File, dataForSend: MessageSend): FormData => { const FD = new FormData() @@ -23,14 +23,11 @@ const formDataHelper = (file: File, dataForSend: MessageSend): FormData => export const ModelsWithChatsEndpoints = { getData: async (chatUid: string, offset: number, token?: string) => { try { - const { data } = await axios.get( - API_URL + `/chats/${chatUid}/messages/?limit=10&offset=${offset}`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ) + const { data } = await axios.get(API_URL + `/chats/${chatUid}/messages/?limit=10&offset=${offset}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) return data } catch (err: any) { return { @@ -44,17 +41,13 @@ export const ModelsWithChatsEndpoints = { const HeaderDataType = dataForSend instanceof FormData ? 'multipart/form-data' : 'application/json' try { - const { data } = await axios.post>( - API_URL + `/chats/${chatUid}/messages/`, - dataForSend, - { - withCredentials: true, - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': HeaderDataType, - }, - } - ) + const { data } = await axios.post>(API_URL + `/chats/${chatUid}/messages/`, dataForSend, { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': HeaderDataType, + }, + }) return data } catch (err: any) { @@ -172,7 +165,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) { @@ -219,17 +212,13 @@ export const ModelsWithImagesEndpoints = { const HeaderDataType = dataForSend instanceof FormData ? 'multipart/form-data' : 'application/json' try { - const { data } = await axios.post>( - API_URL + `/media/image/${type}`, - dataForSend, - { - withCredentials: true, - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': HeaderDataType, - }, - } - ) + const { data } = await axios.post>(API_URL + `/media/image/${type}`, dataForSend, { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': HeaderDataType, + }, + }) return data } catch (err: any) { @@ -386,16 +375,12 @@ export const ModelsMediaApi = { }, sendData: async (type: string | null, dataForSend: MessageSend | FormData, token?: string) => { try { - const { data } = await axios.post>( - API_URL + `/media/audio/${type}`, - dataForSend, - { - withCredentials: true, - headers: { - Authorization: `Bearer ${token}`, - }, - } - ) + const { data } = await axios.post>(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 '#/shared/lib/constants' -import { IOffer } from '#/widgets/payment/model/payment' +import { API_URL } from '@/src/shared/lib/constants' +import { IOffer } from '@/src/widgets/payment/model/payment' interface IUserBalance { current_token_balance: number @@ -61,12 +61,7 @@ export const accountApi = { } }, - async changePassword( - token: string | null, - password_1: string, - password_2: string, - current_password: string - ): Promise { + async changePassword(token: string | null, password_1: string, password_2: string, current_password: string): Promise { 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 '#/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 { 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 { Message } from '../lib/types/model' @@ -96,16 +96,12 @@ export const api = { async sendMessageChatGPT(token: string, message: any, uid: string): Promise { try { - const { data } = await axios.post>( - API_URL + `/chats/${uid}/messages/`, - message, - { - withCredentials: true, - headers: { - Authorization: `Bearer ${token}`, - }, - } - ) + const { data } = await axios.post>(API_URL + `/chats/${uid}/messages/`, message, { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + }, + }) return { data, isError: false } } catch (err) { @@ -161,9 +157,7 @@ export const createChat = async (model: string, token?: string) => { export const getAllChats = async (model: string, token?: string): Promise => { 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,30 +1,32 @@ .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; - } + @media (max-width:420px) { + width: 92vw; + height: 300px; + } + + .description { + padding: 15px 20px; - .description { - padding: 15px 20px; + .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); + } + } - .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); - } - } -} + +} \ No newline at end of file @@ -16,7 +16,9 @@ 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 ( @@ -61,7 +63,11 @@ export const Card = ({ text, icon, title, uid, slug, accessed_models }: IProps) textAlign: 'center', }} > - @@ -1,7 +1,7 @@ import React from 'react' import { Drawer } from '@mui/material' -import { useAppSelector } from '#/app/store/store' +import { useAppSelector } from '@/src/main/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 '#/app/store/store' -import { API_URL } from '#/shared/lib/constants' +import { useAppSelector } from '@/src/main/store/store' +import { API_URL } from '@/src/shared/lib/constants' const style = { position: 'absolute' as 'absolute', @@ -146,8 +146,7 @@ const ErrorModal: React.FC = ({ open, handleClose, device }) => { color: '#868686', }} > - Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при - регистрации + Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при регистрации ) : ( @@ -203,12 +202,7 @@ const ErrorModal: React.FC = ({ open, handleClose, device }) => { height: 25, }} /> - + ), @@ -231,9 +225,7 @@ const ErrorModal: React.FC = ({ open, handleClose, device }) => { alignItems: 'center', }} > - - Добавлено 1 изображение: {file.name} - + Добавлено 1 изображение: {file.name} 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 '#/shared/lib/hooks' +import { useThemeAndDevice } from '@/src/shared/lib/hooks' export interface IError { error: string @@ -13,15 +13,7 @@ export interface IError { const ErrorIcon = () => { const { theme } = useThemeAndDevice() - return ( - - ) + return } export const Error: React.FC = ({ error, handleClose, open }) => { @@ -29,12 +21,7 @@ export const Error: React.FC = ({ error, handleClose, open }) => { return ( - + } severity='error' @@ -1,24 +1,31 @@ -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 }) { - const isDesktop = useMediaQuery('(min-width: 1025px)') +export function FiltersWrapper (props: {open: boolean, onClose: (value: boolean) => void, filetrs: React.ReactNode}) { - if (isDesktop) { - return ( - - {props.filetrs} - - ) - } + const isDesktop = useMediaQuery('(min-width: 1025px)'); - return ( - props.onClose(false)}> - - {props.filetrs} - - - ) -} + if (isDesktop) { + return ( + + {props.filetrs} + + ) + } + + return ( + props.onClose(false)}> + + {props.filetrs} + + + ) + +} \ No newline at end of file @@ -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 '#/shared/lib/constants/colors' -import { useThemeAndDevice } from '#/shared/lib/hooks' +import { baseColor } from '@/src/shared/lib/constants/colors' +import { useThemeAndDevice } from '@/src/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 '#/app/store/store' +import { useAppSelector } from '@/src/main/store/store' interface INotificationMenu { anchorEl: null | HTMLElement @@ -2,8 +2,8 @@ import React from 'react' import { Card, CardMedia } from '@mui/material' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' -import { getRandomImage } from '../lib/helpers' +import { getRandomImage } from '@/src/pages/login' export const RandomImage = () => { const refImage = React.useRef(getRandomImage()) @@ -4,20 +4,11 @@ import Link from 'next/link' export const ReferralBlock = () => { return ( - - + + Реферальная программа - + Получай токены: 20% за подписки твоих рефералов! @@ -33,9 +24,7 @@ export const ReferralBlock = () => { width: '100%', }} > - - Пригласить друга - + Пригласить друга @@ -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 '#/app/store/store' -import { baseColor } from '#/shared/lib/constants/colors' +import { useAppSelector } from '@/src/main/store/store' +import { baseColor } from '@/src/shared/lib/constants/colors' interface ISelect { value: any @@ -26,9 +26,7 @@ export const SocialMedia = () => { }} > - - Telegram - + Telegram @@ -45,9 +43,7 @@ export const SocialMedia = () => { }} > - - ВКонтакте - + ВКонтакте @@ -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 '#/shared/lib/hooks' +import { useThemeAndDevice } from '@/src/shared/lib/hooks' interface ISuccess { message: string @@ -23,17 +23,10 @@ export const Success: React.FC = ({ message, handleClose, open, isErro return ( - + - ), + success: , }} severity='success' sx={ @@ -5,27 +5,16 @@ import Link from 'next/link' export const TelegramBlock = () => { return ( - + - + Бесплатные токены - + Получите 5 токенов за подписку на наш телеграм канал! - - Подписаться - + Подписаться @@ -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 '#/shared/api/models/endpoints' -export type { ChatProps, FiltersChatProps } from '#/shared/lib/types/model' +export { useModel } from '@/src/shared/api/models/endpoints' +export type { ChatProps, FiltersChatProps } from '@/src/shared/lib/types/model' @@ -1 +0,0 @@ -export * from './scopes' \ No newline at end of file @@ -1,6 +0,0 @@ -export const scopes = [ - { title: 'Настройки', scope: 'setting' }, - { title: 'Управление оплатой', scope: 'subscribe' }, - { title: 'Корпоративный аккаунт', scope: 'business' }, - { title: 'Реферальная программа', scope: 'referral' }, -] \ No newline at end of file @@ -1 +0,0 @@ -export * from './payments' \ No newline at end of file @@ -1,5 +0,0 @@ -export interface IPaymentsPlan { - uid: string - price: string - tokens_per_plan: string -} @@ -1,586 +0,0 @@ -import * as React from 'react' -import { useEffect, useMemo, useRef, useState } from 'react' -import { Avatar, Box, Button, Stack, Tab, Tabs, Typography } from '@mui/material' -import CircularProgress from '@mui/material/CircularProgress' -import axios from 'axios' -import Image from 'next/image' -import { useRouter } from 'next/router' -import { signOut, useSession } from 'next-auth/react' - -import { getUserBalance } from '#/entities/balance' -import { getAllInfo, unfollowEmail } from '#/entities/user-account/model/user-type-slice' -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 { getDeviceType } from '#/shared/lib/helpers' -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 { NextPageWithLayout } from '#/pages/_app' -import { DownloadModal } from '#/features/business-security-download' -import { scopes } from '../config' - - -const Account: NextPageWithLayout = () => { - const device = getDeviceType() - - const { - email, - is_subscribed_to_emails, - account_type, - first_name, - last_name, - username, - profile_picture_link, - is_social, - status: userInfoLoaded, - } = useAppSelector((state) => state.user) - - const { status, account_type: type } = useAppSelector((state) => state.user) - - const [name, setName] = useState('') - - const [lastName, setLastName] = useState('') - - const fileInputRef = useRef(null) - - const [loading, setLoading] = useState(false) - - const { data } = useSession() - - const handleDivClick = () => { - ;(fileInputRef.current! as any).click() - } - - const dispatch = useAppDispatch() - - const { error, showError, isError } = useShowData() - - const handleFileChange = async (event: any) => { - const formData = new FormData() - formData.append('new_picture', event.target.files[0]) - - try { - setLoading(true) - await axios.put(API_URL + '/auth/reset-profile-pic', formData, { - headers: { - Authorization: `Bearer ${data?.access}`, - 'Content-Type': 'multipart/form-data', - }, - }) - dispatch(getAllInfo(data?.access)) - showError('Изображение успешно загружено!') - setLoading(false) - } catch (e) { - setLoading(false) - showError('Ошибка загрузки изображения на сервере!', true) - } - } - - const [currentPassword, setCurrentPassword] = useState('') - - const [newPassword1, setNewPassword1] = useState('') - - const [newPassword2, setNewPassword2] = useState('') - - const [promocode, setPromocode] = useState('') - - const [success, setSuccess] = useState('') - - const [confirmDeleteModal, setConfirmDeleteModal] = useState(false) - - const { push, query } = useRouter() - - const [scope, setScope] = useState(query['scope'] || 'setting') - - const [downloadModal, setDownloadModal] = useState(false) - - const desktop = device === 'desktop' - - const changeScope = async (scope: string) => { - setScope(scope) - await addQueryParams(scope) - } - - async function addQueryParams(scope: string) { - await push({ - pathname: '/account', - query: { scope }, - }) - } - - useEffect(() => { - if (query['scope'] !== undefined && scope !== query['scope']) { - changeScope(query['scope'] as string) - } - }, [query]) - - useEffect(() => { - if (Object.keys(query).length === 0) addQueryParams('setting') - }, []) - - const changePassword = async () => { - if (!data) return - if (newPassword1 !== newPassword2) { - showError('Укажите одинаковые новы пароли!', true) - return - } - const status = await accountApi.changePassword(data.access, newPassword1, newPassword2, currentPassword) - - if (status === 200) { - showError('Пароль успешно изменён!') - setNewPassword1('') - setNewPassword2('') - setCurrentPassword('') - setTimeout(() => setSuccess(''), 6000) - return - } - - showError('К сожалению, произошла ошибка', true) - } - - function body() { - if (type === 'regular') { - return ( - - - - ) - } - if (type === 'business_account') { - return ( - - Информация о корп.аккаунте доступна только владельцу и администраторам. - - ) - } - if (type === 'business_host') { - return - } - if (type === 'business_admin') { - return - } - if (type === 'business_security') { - return ( - <> - - - - - Раздел 1. Запросы сотрудников по моделям - - - { - setDownloadModal(true) - }} - sx={{ marginTop: '20px' }} - text={'Скачать'} - /> - - - - ) - } - } - - const isUserDataChange = useMemo(() => name !== first_name || last_name !== lastName, [name, lastName]) - - const promocodeActivate = async () => { - if (!promocode.trim()) { - showError('Введите корректный промокод', true) - return - } - let resStatus = 404 - try { - const { status } = await axios.post( - API_URL + '/payments/promocode', - { code: promocode }, - { headers: { Authorization: `Bearer ${data?.access}` } } - ) - resStatus = status - } catch (e) {} - - if (resStatus === 200) { - showError('Промокод успешно активирован! Токены уже зачислены!') - dispatch(getUserBalance(data?.access)) - return - } - - if (resStatus === 403) { - showError('Промокод уже был активирован!', true) - return - } - - if (resStatus === 404) { - showError('Промокод не найден!', true) - return - } - } - - async function changeUserData() { - if (!isUserDataChange) { - showError('Вы не изменили данные', true) - return - } - - try { - await axios.put( - API_URL + '/auth/user-data', - { - username: username, - email: email, - first_name: name, - last_name: lastName, - }, - { headers: { Authorization: `Bearer ${data?.access}` } } - ) - - showError('Данные успешно изменены!') - dispatch(getAllInfo(data?.access)) - } catch (e) {} - } - - const deleteAccount = async () => { - try { - const { status } = await axios.delete(API_URL + '/auth/remove', { - headers: { - Authorization: `Bearer ${data?.access}`, - }, - }) - - if (status === 200) await signOut() - } catch (err) {} - } - - useEffect(() => { - setName(first_name) - setLastName(last_name) - }, [first_name, last_name]) - - return ( - - - - Ваш аккаунт - - {scopes.map((el) => { - if (!desktop && el.scope === 'business') { - return null - } - if (el.scope === 'referral' && account_type !== 'regular') { - return null - } - return ( - changeScope(el.scope)} - key={el.title} - className={ - scope === el.scope - ? styles2.wrap_toggle_button_active - : styles2.wrap_toggle_button - } - value={el.scope} - label={el.title} - /> - ) - })} - - {scope === 'setting' ? ( - <> - - Основные - - - - - - {!loading || !(userInfoLoaded === 'succeeded') ? ( - - ) : ( - - )} - - - {''} - - - - - - Имя - setName(e.target.value)} - fullWidth - /> - - - Фамилия - setLastName(e.target.value)} - fullWidth - /> - - - - - Email - - - { - await dispatch(unfollowEmail(data?.access)) - showError('Данные изменены!') - }} - /> - - Отписаться от рассылки - - - - - - - Никнейм - - - - {/*referral_code.code.trim() && ( - - Ваша реферальная ссылка: -   - - navigator.clipboard.writeText(referral_code.code.trim())} - className='text' - sx={{ color: '#8280FF !important', cursor: 'pointer' }} - > - {referral_code.code} - - - -) */} - - - - - Активация промокода - setPromocode(e.target.value)} - fullWidth - /> - - - {!is_social && ( - - Изменить пароль - - - - Текущий пароль - - setCurrentPassword(e.target.value)} - fullWidth - /> - - - - - Новый пароль - - setNewPassword1(e.target.value)} - fullWidth - /> - - - - Подтвердить пароль - - setNewPassword2(e.target.value)} - fullWidth - /> - - - - - - )} - - Удаление аккаунта - - Удаление аккаунта приведет к потере всех настроек - - setConfirmDeleteModal(true)} - sx={{ marginTop: '20px' }} - text={'Удалить аккаунт'} - style={{ - width: '100%', - backgroundColor: 'rgba(255, 35, 114, 0.10)', - color: '#FF2372', - }} - /> - - - setConfirmDeleteModal(false)}> - - Удаление аккаунта - - Вы действительно хотите удалить ваш аккаунт? - - - - - - ) : scope === 'business' ? ( - - {status == 'pending' || type === null ? ( - - - - ) : ( - {body()} - )} - - ) : scope === 'subscribe' ? ( - - ) : scope === 'referral' && account_type === 'regular' ? ( - - ) : ( - <> - )} - - - - - ) -} - -export default Account @@ -1 +0,0 @@ -export { default as AccountPage } from './account' @@ -1 +0,0 @@ -export * from './ui' @@ -1,31 +0,0 @@ -import React from 'react' -import { Stack } from '@mui/material' - -import { useAppSelector } from '#/app/store/store' -import { StatsField } from '#/widgets/stats-field' -import { useSession } from 'next-auth/react' -import { getDeviceType } from '#/shared/lib/helpers' -import { NextPageWithLayout } from '#/pages/_app' - -/** - * @deprecated - */ -const Admin: NextPageWithLayout = () => { - const theme = useAppSelector((state) => state.theme.theme) - - const device = getDeviceType() - const { data: session } = useSession() - - return ( - - - - ) -} - -export default Admin @@ -1 +0,0 @@ -export { default as AdminPage } from './admin' @@ -1 +0,0 @@ -export * from './ui' @@ -1,236 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { Box, Stack, Table, TableBody, TableHead, TableRow, TextField, Typography } from '@mui/material' -import Button from '@mui/material/Button' -import TableCell from '@mui/material/TableCell' -import axios from 'axios' -import Image from 'next/image' -import Link from 'next/link' -import { useSession } from 'next-auth/react' - -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' -import { NextPageWithLayout } from '#/pages/_app' - -const ApiKeys: NextPageWithLayout = () => { - const [keys, setKeys] = useState | null>(null) - const [loading, setLoading] = useState(false) - const { data, status } = useSession() - const [modal, setModal] = useState(false) - const [userInfo, setUserInfo] = useState() - const [disabled, setDisabled] = React.useState(true) - - useEffect(() => { - getAll(data?.access).then((res) => setUserInfo(res)) - }, [data?.access]) - - useEffect(() => { - if (status === 'authenticated') { - setLoading(true) - accountApi.getApiKeys(data?.access).then((res) => { - setKeys(res) - setLoading(false) - }) - } - }, [status]) - - React.useEffect(() => { - if ( - userInfo?.account_type === 'business_security' || - userInfo?.account_type === 'business_account' || - status !== 'authenticated' || - loading - ) { - setDisabled(true) - } else { - setDisabled(false) - } - }, [status, userInfo, loading]) - - const deleteKey = async (name: string) => { - if (keys === null || status !== 'authenticated') { - return - } - - setLoading(true) - const result = await accountApi.deleteApiKey(name, data?.access) - if (result !== null && result === 200) { - accountApi.getApiKeys(data?.access).then((res) => { - setKeys(res) - setLoading(false) - }) - } - setLoading(false) - } - - return ( - <> - - - API ключи - - - - - API-ключ — это инструмент, который идентифицирует пользователя или программу, - запрашивающих доступ к API платформы. С помощью ключа можно отслеживать, кто и когда - пользуется API, рассчитывать оплату. - - - - - - Документация - - - - - - {keys && keys.length !== 0 ? ( - - Мои ключи -
- - - Имя - Ключ - Лимит токенов - Создан - Действителен до - - - - {keys && - keys.map((el) => { - return ( - - ) - })} - -
- - ) : ( - - - У вас пока нет ключей 😞 - Создайте первый ключ - - - )} - - - ) -} - -const KeyRow = (props: any) => { - const [isCopy, setIsCopy] = useState(false) - const [limit, setLimit] = useState(props.limit) - const theme = useAppSelector((state) => state.theme.theme) - const { data: session } = useSession() - - const copy = (text: string) => { - navigator.clipboard.writeText(text) - setIsCopy(true) - setTimeout(() => setIsCopy(false), 3000) - } - - useEffect(() => { - let timeout = window.setTimeout(() => { - if (limit !== props.limit) { - axios.patch( - API_URL + '/public/api-key', - { token_limit: Number(limit), name: props.name }, - { headers: { Authorization: `Bearer ${session?.access}` } } - ) - } - }, 1000) - return () => window.clearTimeout(timeout) - }, [limit]) - - return ( - - - {props.name} - - - {props.keyValue} - - - setLimit(e.target.value)} - sx={theme === 'light' ? { ...InputStyleSmallLight } : { ...InputStyleSmallDark }} - /> - - - {props.created_at.split('T')[0]} - - - {props.expires_at !== null ? props.expires_at : 'Бессрочно'} - - - - {isCopy ? ( - {'copy'} - ) : ( - - copy(props.keyValue)} - src={'/svg/copy.svg'} - width={20} - height={20} - style={{ cursor: 'pointer' }} - alt={'copy'} - /> - - )} - props.deleteKey(props.name)} - style={{ cursor: 'pointer' }} - src='/svg/main_menu/trash.svg' - width={20} - height={20} - alt='Удалить' - /> - - - - ) -} - -export default ApiKeys @@ -1 +0,0 @@ -export { default as ApiKeysPage } from './api-keys' @@ -1 +0,0 @@ -export * from './ui' @@ -1,42 +0,0 @@ -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(null) - - const { data, status } = useSession() - - useEffect(() => { - if (!data) return - - getAudio(data.access).then((res) => setBots(res)) - }, [status]) - - return ( - <> - Аудио - - {bots?.map((item, idx) => { - return ( - - ) - })} - - - ) -} - -export default AudioModelsPage @@ -1 +0,0 @@ -export { default as AudioModelsPage } from './audio-models' @@ -1 +0,0 @@ -export * from './ui' \ No newline at end of file @@ -1,17 +0,0 @@ -import { API_URL } from "#/shared/lib/constants" -import axios from "axios" - -const client_secret = process.env.NEXT_PUBLIC_DJANGO_GOOGLE_APP_CLIENT_SECRET -const client_id = process.env.NEXT_PUBLIC_DJANGO_GOOGLE_APP_CLIENT_ID - -export async function getTokenByAPIGoogle(token: string) { - const { data } = await axios.post(API_URL + '/auth/login-social/convert-token', { - grant_type: 'convert_token', - client_id: client_id, - client_secret: client_secret, - backend: 'google-oauth2', - token: token, - }) - - return data -} @@ -1 +0,0 @@ -export * from './google' \ No newline at end of file @@ -1,14 +0,0 @@ -import React, { useEffect } from 'react' -import { getTokenByAPIGoogle } from '../api' -import { NextPageWithLayout } from '#/pages/_app' - -const AuthSocial: NextPageWithLayout = () => { - useEffect(() => { - const token = new URLSearchParams(window.location.href.replace('#', '&')).get('access_token') || '' - getTokenByAPIGoogle(token).then((res) => res) - }, []) - - return <> -} - -export default AuthSocial @@ -1 +0,0 @@ -export { default as AuthSocialPage } from './auth-social' @@ -1,2 +0,0 @@ -export * from './ui' -export * from './api' \ No newline at end of file @@ -1 +0,0 @@ -export { default as ChangePasswordPage } from './change-password' @@ -1 +0,0 @@ -export * from './ui' @@ -1,380 +0,0 @@ -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 = () => { - 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 [params, setParams] = React.useState(false) - const [file, setFile] = React.useState(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({ - content: input, - file: file, - info: { - ...data, - }, - }) - return true - } - - return ( - <> - - {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 @@ -1,65 +0,0 @@ -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 @@ -1,2 +0,0 @@ -export { default as ChatBotsPage } from './chat-bots' -export { default as ChatBotPage } from './chat-bot' @@ -1 +0,0 @@ -export * from './ui' \ No newline at end of file @@ -1,71 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { Box, Typography } from '@mui/material' -import axios from 'axios' -import Link from 'next/link' -import { useRouter } from 'next/router' - -import { API_URL } from '#/shared/lib/constants/constants' -import { NextPageWithLayout } from '#/pages/_app' - -const Confirm: NextPageWithLayout = () => { - const { push, query } = useRouter() - const [success, setSuccess] = useState(false) - - useEffect(() => { - let formData: any = new FormData() - - formData.append('token', query.token) - - axios.post(API_URL + '/auth/confirm', formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }) - .then(() => { - setSuccess(true) - }) - .catch(() => { - push('/') - }) - }, []) - - useEffect(() => { - if (success) { - setTimeout(() => push('/login'), 10000) - } - }, [success]) - - return ( - <Box - sx={{ - width: '100%', - height: '100vh', - alignItems: 'center', - display: 'flex', - }} - > - <Typography - sx={{ - margin: '0 auto', - textAlign: 'center', - }} - > - {' '} - {success ? ( - <> - <Typography sx={{ color: '#7F7DF3', fontSize: 17 }}> - Ваша почта подтверждена, вы будете перенаправлены на страницу авторизации - </Typography> - <Link style={{ color: '#7F7DF3', fontSize: 17 }} href={'/login'}> - Войти в аккаунт - </Link> - </> - ) : ( - 'Подтверждение...' - )} - </Typography> - </Box> - ) -} - -export default Confirm @@ -1 +0,0 @@ -export { default as ConfirmPage } from './confirm' @@ -1 +0,0 @@ -export * from './ui' @@ -1,523 +0,0 @@ -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 @@ -1,61 +0,0 @@ -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 @@ -1,2 +0,0 @@ -export { default as ImageModelsPage } from './image-models' -export { default as ImageModelPage } from './image-model' @@ -1 +0,0 @@ -export * from './ui' \ No newline at end of file @@ -1 +0,0 @@ -export { default as MainPage } from './main' @@ -1,66 +0,0 @@ -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 @@ -1 +0,0 @@ -export * from './ui' @@ -1 +0,0 @@ -export { default as LoginPage } from './login' @@ -1,352 +0,0 @@ -import * as React from 'react' -import { useCookies } from 'react-cookie' -import { useForm } from 'react-hook-form' -import { SubmitErrorHandler } from 'react-hook-form/dist/types/form' -import { Box, Button, Link, Stack, TextField, Typography } from '@mui/material' -import CircularProgress from '@mui/material/CircularProgress' -import Image from 'next/image' -import { useRouter } from 'next/router' -import Router from 'next/router' -import Script from 'next/script' -import { signIn } from 'next-auth/react' -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 { useAppSelector } from '#/app/store/store' -import { emailOptions, Error } from '#/shared' -import { getDeviceType } from '#/shared/lib/helpers' -import { useShowData } from '#/shared/lib/hooks' -import { InputStyleDark, InputStyleLight } from '#/shared/ui/input' -import { NextPageWithLayout } from '#/pages/_app' - -const Login: NextPageWithLayout = () => { - const device = getDeviceType() - - const { query } = useRouter() - - const [, setCookie] = useCookies() - - React.useEffect(() => { - Object.entries(query).forEach(([key, value]) => setCookie(key, value)) - }, []) - - const { error, showError } = useShowData() - - const [loading, setLoading] = React.useState(false) - - const desktop = device === 'desktop' - - const { register, handleSubmit, getValues, reset } = useForm() - - async function onSubmit(data: any) { - setLoading(true) - - if (window.sessionStorage.__telegram__initParams !== '{}') { - await authTelegram(data.email, data.password) - setLoading(false) - reset() - return - } - - const res = await signIn('credentials', { - username: data.email, - password: data.password, - redirect: false, - callbackUrl: '/', - }) - if (res?.error) { - setLoading(false) - showError('Ошибка входа') - return - } - - await Router.push('/') - - setLoading(false) - - res?.error ? showError('Ошибка входа') : '' - } - - const checkError: SubmitErrorHandler<IEmailForms> = (data) => { - showError(Object.values(data)[0].message || 'Неверные данные') - } - - const handleKeyDown = (event: any) => { - if (event.key === 'Enter') { - handleSubmit(onSubmit)() - } - } - - const handleInputChangeTrim = (event: any) => { - event.target.value = event.target.value.trim() - } - - const theme = useAppSelector((state) => state.theme.theme) - - return ( - <> - <Script src='https://telegram.org/js/telegram-web-app.js' /> - <Stack - direction={desktop ? 'row' : 'column'} - alignItems='center' - justifyContent={desktop ? 'space-between' : 'center'} - sx={{ - overflow: 'hidden', - padding: 0, - margin: desktop ? 0 : '0px auto', - backgroundColor: theme === 'light' ? 'white' : '#303035', - }} - > - {!desktop && ( - <Image - style={{ marginTop: desktop ? 0 : '80px' }} - src={'/logo.svg'} - alt={''} - width={50} - height={50} - /> - )} - <Box - sx={{ - width: '50%', - height: '100vh', - display: 'flex', - justifyContent: 'center', - alignItems: desktop ? 'center' : 'start', - marginTop: desktop ? 0 : '10px', - }} - > - <form onKeyDown={handleKeyDown} autoComplete={'off'}> - <Box - sx={{ - width: '40vh', - height: '100%', - marginTop: desktop ? 0 : 5, - }} - onKeyDown={async (e) => { - if (e.key === 'Enter') { - await handleSubmit(onSubmit, checkError) - } - }} - > - <Stack direction='column' justifyContent='center' alignItems='center' spacing={2}> - <Typography - sx={{ - color: theme === 'light' ? '#181C32' : '#E1E1E1', - lineHeight: '36.4px', - fontSize: '26px', - fontWeight: 'bold', - marginBottom: '20px', - }} - > - Вход - </Typography> - <Button - variant='contained' - onClick={() => - signIn('yandex', { - callbackUrl: '/', - }) - } - sx={{ - backgroundColor: theme === 'dark' ? '#1C1C1E' : '#F1FAFF', - color: theme === 'dark' ? 'white' : '#2B2B42', - lineHeight: '20px', - fontSize: '16px', - fontWeight: '600px', - borderRadius: '13px', - textTransform: 'none', - width: '100%', - boxShadow: 'none', - height: 50, - '&:hover': { - backgroundColor: '#F1FAFF', - opacity: 1, - boxShadow: 'none', - }, - }} - > - <Image - src={'/svg/auth/yandex.svg'} - alt={''} - style={{ marginRight: 15 }} - width={26} - height={26} - /> - Войти с Яндекс ID - </Button> - <Typography sx={{ fontSize: '15px', color: '#A4AAB5' }}>или email</Typography> - <Typography - variant='body2' - sx={{ - color: '#A4AAB5', - lineHeight: '19.6px', - fontSize: '14px', - paddingTop: 1, - paddingRight: '90%', - }} - > - Email - </Typography> - <TextField - fullWidth - sx={theme === 'light' ? { ...InputStyleLight } : { ...InputStyleDark }} - {...register('email', emailOptions)} - onChange={handleInputChangeTrim} - /> - - <Box - sx={{ - width: '100%', - justifyContent: 'space-between', - display: 'flex', - }} - > - <Box> - <Typography - variant='body2' - display='inline' - sx={{ - color: '#A4AAB5', - lineHeight: '19.6px', - fontSize: '14px', - }} - > - Пароль - </Typography> - </Box> - <Box> - <Typography - variant='body2' - display='inline' - sx={{ - color: '#8153FB', - lineHeight: '19.6px', - fontSize: '14px', - fontWeight: '500', - }} - > - <Link - href='/reset' - style={{ - textDecoration: 'none', - color: '#7F7DF3', - }} - > - Забыли пароль? - </Link> - </Typography> - </Box> - </Box> - - <TextField - type='password' - fullWidth - sx={theme === 'light' ? { ...InputStyleLight } : { ...InputStyleDark }} - {...register('password', { - ...PasswordOptions, - })} - /> - <Box sx={{ height: '50px', width: '100%' }}> - {!loading ? ( - <Button - variant='contained' - style={{ - marginTop: '15px', - }} - sx={{ - backgroundColor: '#7F7DF3', - color: '#FFFFFF', - lineHeight: '20px', - fontSize: '16px', - fontWeight: '600px', - borderRadius: '13px', - textTransform: 'none', - width: '100%', - height: 48, - '&:hover': { - backgroundColor: '#7F7DF3', - opacity: [0.9, 0.8, 0.7], - }, - }} - onClick={handleSubmit(onSubmit, checkError)} - > - Войти - </Button> - ) : ( - <Box - style={{ - margin: '25px auto', - display: 'flex', - justifyContent: 'center', - }} - > - <CircularProgress - size={30} - thickness={5} - sx={{ color: '#7F7DF3' }} - /> - </Box> - )} - <Box - sx={{ - width: '100%', - textAlign: 'center', - marginTop: '15px', - }} - > - <Link - href='/register' - style={{ - textDecoration: 'none', - color: '#7F7DF3', - }} - > - Зарегистрироваться - </Link> - </Box> - </Box> - <Error error={error} open={Boolean(error)} /> - </Stack> - </Box> - </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} - /> - <Image - src={'/images/dashboard.png'} - alt={''} - width={900} - height={1002} - style={{ - borderRadius: '20px', - position: 'relative', - zIndex: 1000, - bottom: -260, - right: -390, - border: '10px solid rgba(255, 255, 255, 0.30)', - transform: 'rotate(-13deg)', - }} - /> - </Box> - )} - </Stack> - </> - ) -} -export default Login @@ -1 +0,0 @@ -export * from './ui' @@ -1 +0,0 @@ -export { default as NotFoundPage } from './not-found' @@ -1,57 +0,0 @@ -import React, { useEffect } from 'react' -import { Box, Typography } from '@mui/material' -import Link from 'next/link' -import { useRouter } from 'next/router' -import { Layout } from '#/app/layout' -import { useAppSelector } from '#/app/store/store' - -const NotFoundPage = () => { - const theme = useAppSelector((state) => state.theme.theme) - - const { push } = useRouter() - - useEffect(() => { - const timeout = setTimeout(() => push('/'), 7000) - - return () => clearTimeout(timeout) - }, []) - - return ( - <Layout titlePage={'Неизвестная страница'} isAuthPage={true}> - <Box display='flex' justifyContent='center' alignItems='center' sx={{ width: '100%', height: '90vh' }}> - <Box sx={{ width: '410px', textAlign: 'center' }}> - <Typography - sx={{ - fontSize: '23px', - color: theme === 'light' ? 'dark' : 'white', - }} - > - Ошибка 404 - </Typography> - <Typography - sx={{ - fontSize: '19px', - color: theme === 'light' ? 'dark' : 'white', - marginTop: 1.5, - }} - > - К сожалению, такая страница не найдена 😢 - </Typography> - <Link href='/'> - <Typography - sx={{ - fontSize: '18px', - marginTop: 2, - color: '#7F7DF3', - }} - > - Вернуться на главную - </Typography> - </Link> - </Box> - </Box> - </Layout> - ) -} - -export default NotFoundPage @@ -1 +0,0 @@ -export * from './ui' @@ -1 +0,0 @@ -export { default as ReferralPage } from './referral' @@ -1,42 +0,0 @@ -import React, { useEffect } from 'react' -import { useDispatch } from 'react-redux' -import { Box } from '@mui/material' -import CircularProgress from '@mui/material/CircularProgress' -import { useRouter } from 'next/router' - -import { addReferral } from '#/entities/user-account/model/user-type-slice' -import { NextPageWithLayout } from '#/pages/_app' - -export interface ReferralRegisterProps { - email: string -} - -const ReferralRegister: NextPageWithLayout<ReferralRegisterProps> = ({ email }) => { - const dispatch = useDispatch() - const { push } = useRouter() - - useEffect(() => { - localStorage.setItem('referral', email) - dispatch(addReferral(email)) - push('/register') - }, []) - - return ( - <Box sx={{ width: '100vw', height: '100vh', background: '#4b4b4b', position: 'relative' }}> - <CircularProgress - size={50} - thickness={5} - sx={{ - color: '#7F7DF3', - position: 'absolute', - top: '40%', - right: 0, - left: 0, - margin: '0 auto', - }} - /> - </Box> - ) -} - -export default ReferralRegister @@ -1 +0,0 @@ -export * from './ui' @@ -1 +0,0 @@ -export { default as RegisterPage } from './register' @@ -1,201 +0,0 @@ -import * as React from 'react' -import { useEffect, useState } from 'react' -import { useCookies } from 'react-cookie' -import { useDispatch } from 'react-redux' -import Box from '@mui/material/Box' -import Button from '@mui/material/Button' -import Stack from '@mui/material/Stack' -import Typography from '@mui/material/Typography' -import Image from 'next/image' -import { useRouter } from 'next/router' -import { signIn } from 'next-auth/react' - -import { addReferral } from '#/entities/user-account/model/user-type-slice' -import { RegisterEmailForm } from '#/features/register-by-email' -import { useAppSelector } from '#/app/store/store' -import { TDeviceProp } from '#/shared/lib/types/entities' -import { getDeviceType } from '#/shared/lib/helpers' -import { NextPageWithLayout } from '#/pages/_app' - -const Register: NextPageWithLayout = () => { - const device = getDeviceType() - - const desktop = device === 'desktop' - - const [success, setSuccess] = useState(false) - const [isReferral, setIsReferral] = useState<boolean>(true) - - const theme = useAppSelector((state) => state.theme.theme) - const user = useAppSelector((state) => state.user) - const dispatch = useDispatch() - - const { push } = useRouter() - - const onSuccessRegister = () => { - setSuccess(true) - setTimeout(() => push('/login'), 15000) - } - - useEffect(() => { - if (localStorage.getItem('referral')) { - setIsReferral(false) - dispatch(addReferral(localStorage.getItem('referral'))) - console.log(localStorage.getItem('referral')) - } else { - setIsReferral(true) - } - }, []) - - return ( - <Stack - direction={desktop ? 'row' : 'column'} - alignItems='center' - justifyContent={desktop ? 'space-between' : 'center'} - sx={{ - overflow: 'hidden', - padding: 0, - margin: desktop ? 0 : '0px auto', - backgroundColor: theme === 'light' ? 'white' : '#303035', - }} - > - {!desktop && ( - <Image - style={{ marginTop: desktop ? 0 : '20px' }} - src={'/logo.svg'} - alt={''} - width={50} - height={50} - /> - )} - <Box - sx={{ - width: desktop ? '50%' : '100%', - height: '100vh', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - marginTop: desktop ? 0 : '10px', - }} - > - <Box - sx={{ - width: '40vh', - height: '100%', - marginTop: desktop ? 0 : 2, - justifyContent: 'center', - alignItems: 'center', - display: 'flex', - }} - > - {success ? ( - <Box textAlign='center'> - <Typography className='text'> - Вы успешно зарегистрированы. - <br /> - Мы отправили письмо для верификации на ваш email. В случае отсутствия, проверьте - папку Спам. - <br /> - <br /> - Если вы регистрировались с корпоративной почты и письмо-подтверждение вам не - пришло, рекомендуем зарегистрироваться с личной почты - </Typography> - </Box> - ) : ( - <Stack direction='column' justifyContent='center' alignItems='center' spacing={2}> - <Typography - sx={{ - color: theme === 'light' ? '#181C32' : '#E1E1E1', - lineHeight: '36.4px', - fontSize: '26px', - fontWeight: 'bold', - marginBottom: '20px', - }} - > - Регистрация - </Typography> - - {user.referral === '' && isReferral && ( - <> - <Button - variant='contained' - onClick={() => - signIn('yandex', { - callbackUrl: '/', - }) - } - sx={{ - backgroundColor: '#F1FAFF', - color: '#7F7DF3', - lineHeight: '20px', - fontSize: '16px', - fontWeight: '600px', - borderRadius: '13px', - textTransform: 'none', - width: '100%', - boxShadow: 'none', - height: 50, - '&:hover': { - backgroundColor: '#F1FAFF', - opacity: 1, - boxShadow: 'none', - }, - }} - > - <Image - src={'/svg/auth/yandex.svg'} - alt={''} - style={{ marginRight: 15 }} - width={26} - height={26} - /> - Войти с Яндекс ID - </Button> - <Typography sx={{ fontSize: '15px', color: '#A4AAB5' }}> - или email - </Typography> - </> - )} - <RegisterEmailForm successLogin={onSuccessRegister} /> - </Stack> - )} - </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} - /> - <Image - src={'/images/dashboard.png'} - alt={''} - width={900} - height={1002} - style={{ - borderRadius: '20px', - position: 'relative', - zIndex: 1000, - bottom: -260, - right: -390, - border: '10px solid rgba(255, 255, 255, 0.30)', - transform: 'rotate(-13deg)', - }} - /> - </Box> - )} - </Stack> - ) -} - -export default Register @@ -1 +0,0 @@ -export * from './ui' @@ -1 +0,0 @@ -export { default as RegisterBusinessPage } from './register-business' @@ -1,8 +0,0 @@ -import { Stepper } from '#/features/register-business' -import { NextPageWithLayout } from '#/pages/_app' - -const Register: NextPageWithLayout = () => { - return <Stepper /> -} - -export default Register @@ -1 +0,0 @@ -export * from './ui' @@ -1 +0,0 @@ -export { default as ReportsMobilePage } from './reports-mobile' @@ -1,260 +0,0 @@ -import React, { ChangeEvent } from 'react' -import AttachFileIcon from '@mui/icons-material/AttachFile' -import CloseIcon from '@mui/icons-material/Close' -import { Box, Button, Stack, TextField, Typography } from '@mui/material' -import axios from 'axios' -import { useSession } from 'next-auth/react' - -import { useAppSelector } from '#/app/store/store' -import { API_URL } from '#/shared/lib/constants/constants' -import { NextPageWithLayout } from '#/pages/_app' - -const styleSend = { - position: 'absolute' as 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -65%)', - width: 300, - height: 'auto', - bgcolor: 'background.paper', - borderRadius: 5, - boxShadow: 16, - p: 4, - textAlign: 'center', -} - -const styleSendDark = { - position: 'absolute' as 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -65%)', - width: 300, - height: 'auto', - bgcolor: '#2B2828', - borderRadius: 5, - boxShadow: 16, - p: 4, - textAlign: 'center', -} - -const styleMobile = { - width: '100%', - position: 'absolute' as 'absolute', - top: '35%', - left: '50%', - transform: 'translate(-50%, -50%)', - p: 4, - zIndex: 99, -} - -const ReportsMobile: NextPageWithLayout = () => { - const [errorMessage, setErrorMessage] = React.useState<string>('') - - const [error, setError] = React.useState(false) - - const [isSend, setIsSend] = React.useState(false) - - const [file, setFile] = React.useState<File>() - - const { data: session } = useSession() - - const theme = useAppSelector((state) => state.theme.theme) - - const email = useAppSelector((state) => state.user.email) - - const sendError = async () => { - if (errorMessage.trim() === '') { - setError(true) - setTimeout(() => setError(false), 3000) - return - } - - let formData: any = new FormData() - - formData.append('report_text', `${email}: ${errorMessage}`) //append the values with key, value pair - if (file) { - formData.append('images', file) - } - - try { - const { status } = await axios.post(API_URL + '/reports/', formData, { - headers: { - 'content-type': 'multipart/form-data ', - 'content-length': file ? `${file.size}` : '', - Authorization: `Bearer ${session?.access}`, - }, - }) - - if (status === 201) { - setErrorMessage('') - setIsSend(true) - setTimeout(() => { - setIsSend(false) - }, 4000) - } - } catch (err) { - setError(true) - setTimeout(() => setError(false), 3000) - } - } - - const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => { - if (e.target.files) { - setFile(e.target.files[0]) - } - } - - return ( - <> - {isSend ? ( - <Box sx={theme === 'light' ? styleSend : styleSendDark}> - <Typography sx={{ color: '#22B47F', fontSize: '23px' }}>Спасибо!</Typography> - <Typography sx={{ fontSize: '17px', marginTop: 2 }}> - Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при - регистрации - </Typography> - </Box> - ) : ( - <Box sx={styleMobile}> - <Typography - variant='h6' - component='h2' - sx={{ - color: theme === 'light' ? '#5A5A5A' : '#E1E1E1', - lineHeight: '28.5px', - fontSize: '21px', - fontWeight: '600px', - }} - > - Сообщить об ошибке - </Typography> - <TextField - error={error} - id='outlined-multiline-static' - multiline - rows={4} - placeholder='Опишите детально возникшую проблему' - fullWidth - style={{ - color: theme === 'light' ? '#272727' : '#E1E1E1', - }} - onChange={(evt) => setErrorMessage(evt.target.value)} - InputProps={{ - startAdornment: ( - <> - <Button - variant='contained' - component='label' - sx={{ - position: 'absolute', - padding: 0, - bottom: 13, - left: -10, - backgroundColor: 'transparent', - border: 'none', - boxShadow: 'none', - margin: 0, - rotate: '30deg', - '&:hover': { - backgroundColor: 'transparent', - boxShadow: 'none', - }, - }} - > - <AttachFileIcon - color='info' - sx={{ - cursor: 'pointer', - color: '#868686', - width: 25, - height: 25, - }} - /> - <input - onChange={handleFileChange} - type='file' - hidden - accept='.jpg,.jpeg,.png' - /> - </Button> - </> - ), - }} - sx={{ - marginTop: 2.5, - marginBottom: 1.2, - fontSize: 13, - '& label': { color: 'red' }, - backgroundColor: theme === 'light' ? 'transparent' : '#3D3D3D', - input: { - color: theme === 'light' ? '#272727' : '#E1E1E1', - }, - }} - /> - {file && ( - <Box sx={{ display: 'flex', alignItems: 'center' }}> - <Typography sx={{ fontSize: '15px' }}> - Добавлено 1 изображение: {file.name} - </Typography> - <CloseIcon - onClick={() => setFile(undefined)} - sx={{ - width: '18px', - height: '18px', - cursor: 'pointer', - marginLeft: '3px', - marginTop: '2px', - }} - /> - </Box> - )} - <Stack - direction={'column-reverse'} - justifyContent='flex-end' - alignItems='flex-end' - spacing={1} - sx={{ marginTop: 1.5 }} - > - <Button - fullWidth - sx={{ - color: theme === 'light' ? '#5A5A5A' : '#D0D0D0', - backgroundColor: theme === 'light' ? '#E8E8E8' : '#5D5A5A', - fontWeight: 500, - fontSize: '16px', - textTransform: 'none', - padding: '8px 12px', - borderRadius: '13px', - border: 'none', - cursor: 'pointer', - marginTop: 1, - }} - > - Отмена - </Button> - <Button - fullWidth - variant='contained' - onClick={sendError} - sx={{ - color: 'white', - backgroundColor: '#7F7DF3', - fontWeight: 500, - fontSize: '16px', - textTransform: 'none', - padding: '7px 12px', - borderRadius: '13px', - border: 'none', - cursor: 'pointer', - }} - > - Отправить - </Button> - </Stack> - </Box> - )} - </> - ) -} - -export default ReportsMobile @@ -1 +0,0 @@ -export * from './ui' @@ -1 +0,0 @@ -export { default as ResetPage } from './reset' @@ -1,170 +0,0 @@ -import * as React from 'react' -import { useState } from 'react' -import { Box, Card, CardMedia, Link, Stack, Typography } from '@mui/material' -import Button from '@mui/material/Button' -import axios from 'axios' -import Image from 'next/image' -import Router from 'next/router' -import { Layout } from '#/app/layout' -import { useAppSelector } from '#/app/store/store' -import { Error, Input } from '#/shared' -import { API_URL } from '#/shared/lib/constants/constants' -import { getDeviceType, getRandomImage } from '#/shared/lib/helpers' -import { NextPageWithLayout } from '#/pages/_app' - -const Reset: NextPageWithLayout = () => { - const device = getDeviceType() - - const [input_email, set_Email] = useState<string>('') - const [isSend, setIsSend] = useState(false) - const [isError, setIsError] = React.useState(false) - const sendMail = () => { - axios.post(API_URL + '/auth/update-pass', { - email: input_email, - }) - .then(function (response) { - if (response.status === 200) { - setIsSend(true) - setTimeout(() => Router.push('/login'), 10000) - } - }) - .catch(function (error) { - setIsError(true) - setTimeout(() => setIsError(false), 4000) - }) - } - - const desktop = device === 'desktop' - - const refImage = React.useRef(getRandomImage()) - - const theme = useAppSelector((state) => state.theme.theme) - - return ( - <Stack - direction={desktop ? 'row' : 'column'} - alignItems='center' - justifyContent={desktop ? 'space-between' : 'center'} - sx={{ - padding: 0, - margin: desktop ? 0 : '60px auto', - backgroundColor: theme === 'light' ? 'white' : '#373737', - }} - > - {desktop && ( - <Card> - <CardMedia - sx={{ height: '100vh', width: '600px' }} - image={`/login_images/${refImage.current}`} - > - <Stack - direction='column-reverse' - justifyContent='flex-start' - alignItems='flex-start' - spacing={2} - sx={{ - wdith: '100%', - height: '100%', - paddingBottom: 3, - paddingLeft: 3, - }} - > - <Typography - variant='body2' - sx={{ - color: '#FFFFFF', - lineHeight: '23.8px', - fontSize: '17px', - fontWeight: '400px', - }} - > - Midjourney - </Typography> - <Typography - variant='body2' - sx={{ - color: '#FFFFFF', - lineHeight: '28px', - fontSize: '20px', - fontWeight: '600px', - }} - > - by honeynek - </Typography> - </Stack> - </CardMedia> - </Card> - )} - {!isSend ? ( - <Box - sx={{ - width: '70%', - height: '100%', - marginTop: desktop ? 0 : 25, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }} - > - <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 /> - <Link - style={{ - position: 'absolute', - top: 45, - right: '7%', - cursor: 'pointer', - fontSize: 17, - }} - href={'/'} - > - <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> - </Box> - <Error open={isError} error={'Аккаунт с такой почтой не найдено'} /> - </Stack> - </Box> - ) : ( - <Box - sx={{ - width: '40vh', - height: '100%', - marginRight: '25%', - }} - > - <Typography - sx={{ - fontSize: 21, - textAlign: 'center', - color: theme === 'light' ? '#272727' : '#E1E1E1', - }} - > - Письмо для восстановления пароля отправлено вам на почту! - </Typography> - </Box> - )} - </Stack> - ) -} - -export default Reset @@ -1 +0,0 @@ -export * from './ui' @@ -1 +0,0 @@ -export { default as WhisperPage } from './whisper' @@ -1,16 +0,0 @@ -import React from 'react' -import { Box } from '@mui/material' -import { WhisperWidget } from '#/widgets/whisper' -import { NextPageWithLayout } from '#/pages/_app' - -const Whisper: NextPageWithLayout = () => { - return ( - <Box sx={{ width: '100%' }}> - <Box sx={{ width: '50%', margin: '20px auto' }}> - <WhisperWidget /> - </Box> - </Box> - ) -} - -export default Whisper @@ -1 +0,0 @@ -export * from './ui' @@ -1,4 +1,5 @@ .screenWrap { + } .screen { @@ -3,20 +3,14 @@ import { Box, Typography } from '@mui/material' import Image from 'next/image' import Link from 'next/link' -import { ButtonUI } from '#/shared' +import { ButtonUI } from '@/src/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 '#/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' +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' type ListType = 'personal' | 'security' @@ -63,13 +63,10 @@ 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', }) @@ -83,11 +80,7 @@ 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 ( @@ -102,11 +95,7 @@ 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 && ( @@ -159,11 +148,7 @@ 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 ( @@ -190,11 +175,7 @@ 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 '#/shared/lib/constants' +import { API_URL } from '@/src/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 '#/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 { 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 { getInfo, InfoBusiness } from '../../api/get-info' @@ -52,13 +52,7 @@ 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' ? 'Владелец' @@ -71,9 +65,7 @@ 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 '#/features/changeAccessToModel' -import { API_URL } from '#/shared/lib/constants' +import { AllowedModels } from '@/src/features/changeAccessToModel' +import { API_URL } from '@/src/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 '#/features/changeAccessToModel' -import { Search, translateTypeModel } from '#/shared' -import ArrowUpOrDown from '#/widgets/top-bar-model/ui/arrow-up-or-down' +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 { getModels } from '../../api/get-models' @@ -35,11 +35,7 @@ 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 && ( @@ -56,18 +52,9 @@ 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 '#/features/invite-person-in-business' -import { ResponseGetBusinessGroups, ResponseGetIpList } from '#/features/invite-person-in-business/model/types' -import { API_URL } from '#/shared/lib/constants' +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' export const getBusinessGroups = async (token?: string): Promise<ResponseGetBusinessGroups[] | null> => { try { @@ -1,8 +1,8 @@ import axios from 'axios' -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' +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' 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 '#/features/invite-person-in-business/model/types' -import { API_URL } from '#/shared/lib/constants' +import { ResponseGetLogsList } from '@/src/features/invite-person-in-business/model/types' +import { API_URL } from '@/src/shared/lib/constants' export const getLogsList = async ( offset: number, @@ -1,7 +1,7 @@ import axios from 'axios' -import { ResponseGetPersons } from '#/features/invite-person-in-business' -import { API_URL } from '#/shared/lib/constants' +import { ResponseGetPersons } from '@/src/features/invite-person-in-business' +import { API_URL } from '@/src/shared/lib/constants' export const getPersons = async (token?: string, security?: boolean): Promise<ResponseGetPersons[] | null> => { try { @@ -1,4 +1,4 @@ -import { AcceptanceStatus } from '#/features/invite-person-in-business/model/types' +import { AcceptanceStatus } from '@/src/features/invite-person-in-business/model/types' export const translateEmailStatus = (status: AcceptanceStatus) => { switch (status) { @@ -12,24 +12,22 @@ import { filter } from 'lodash' import Image from 'next/image' import { useSession } from 'next-auth/react' -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 { 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 { 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('') @@ -98,11 +96,7 @@ 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={() => { @@ -135,22 +129,12 @@ 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 @@ -178,13 +162,7 @@ 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 '#/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 { 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 { getIpList } from '../../api/get-ipList' @@ -62,22 +62,13 @@ 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'> @@ -90,12 +81,7 @@ 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> @@ -103,13 +89,7 @@ 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 '#/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' +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' export const LogList = () => { const [logs, setLogs] = useState<ResponseGetLogsList[] | null>() @@ -44,9 +44,7 @@ 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 @@ -66,11 +64,7 @@ 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> @@ -122,12 +116,7 @@ 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'> @@ -142,47 +131,22 @@ 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 '#/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 { 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 { translateEmailStatus } from '../../lib/lib' @@ -99,11 +99,7 @@ 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> @@ -138,54 +134,33 @@ 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 '#/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 { 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 { ButtonGray, ButtonUI, @@ -34,12 +34,12 @@ import { ModalProps, onlyNumbersOption, Select, -} 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' +} 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' import { translateEmailStatus } from '../../lib/lib' @@ -121,11 +121,7 @@ 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> @@ -160,71 +156,42 @@ 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') @@ -247,11 +214,7 @@ 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} @@ -324,11 +287,7 @@ 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 '#/app/store/store' +import { useAppSelector } from '@/src/main/store/store' interface IContext { context: { message: string; uid: string }[] @@ -10,12 +10,7 @@ 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>) => { @@ -106,12 +101,7 @@ const Context: React.FC<IContext> = ({ <> {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 '#/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 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 '../styles.module.css' // @@ -1,8 +1,8 @@ import React from 'react' import { Box, Typography } from '@mui/material' -import { errorColor } from '#/shared/lib/constants/colors' -import { useThemeAndDevice } from '#/shared/lib/hooks' +import { errorColor } from '@/src/shared/lib/constants/colors' +import { useThemeAndDevice } from '@/src/shared/lib/hooks' const TooltipContextText = () => { const { theme } = useThemeAndDevice() @@ -1,8 +1,8 @@ import React from 'react' import { Tooltip } from '@mui/material' -import { useThemeAndDevice } from '#/shared/lib/hooks' -import TooltipContextText from '#/widgets/chat-gpt-field/ui/tooltip-context-text' +import { useThemeAndDevice } from '@/src/shared/lib/hooks' +import TooltipContextText from '@/src/widgets/chat-gpt-field/ui/tooltip-context-text' interface ITooltipContext { children: React.ReactNode @@ -1,25 +1,15 @@ 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 '#/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' +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' export const Chats = ({ chats, @@ -70,10 +60,7 @@ 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('') }) } @@ -82,16 +69,7 @@ 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} @@ -207,19 +185,9 @@ 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> @@ -227,9 +195,7 @@ 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} @@ -245,14 +211,12 @@ 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,17 +14,13 @@ 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 '#/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' +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' const FiltersMobile: React.FC<IFiltersChatGPT> = ({ open, @@ -5,17 +5,12 @@ import { SelectChangeEvent } from '@mui/material/Select' import Stack from '@mui/material/Stack' import { styled } from '@mui/material/styles' -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 { 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 'intro.js/introjs.css' @@ -142,13 +137,7 @@ 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' }}> @@ -192,12 +181,7 @@ 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', @@ -208,10 +192,7 @@ 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 '#/app/store/store' +import { useAppSelector } from '@/src/main/store/store' interface ITooltip { title: string @@ -1,15 +1,8 @@ -import { IProps } from '#/shared/lib/types/entities' -import { Styles } from '#/widgets/filters-sd/ui/filters' +import { IProps } from '@/src/shared/lib/types/entities' +import { Styles } from '@/src/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,20 +2,14 @@ import React, { useState } from 'react' import SettingsSuggestIcon from '@mui/icons-material/SettingsSuggest' import { Box, Button, Drawer, Stack, Typography } from '@mui/material' -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' +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' interface ISDFiltersMobile extends ISDFilters { isOpenDrawer: boolean @@ -148,9 +142,7 @@ 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,16 +1,10 @@ import React from 'react' import { Box, Stack, Typography } from '@mui/material' -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' +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' export type Styles = | null @@ -87,12 +81,7 @@ 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 @@ -1,19 +0,0 @@ -import { Attributes, FC, ReactElement } from 'react' -import { Layout } from '../ui' -import { LayoutProps } from '../types' -import React from 'react' - -export const getDefaultLayout = function (layoutProps: Omit<LayoutProps, 'children'> = {}) { - return (page: ReactElement) => <Layout {...layoutProps}>{page}</Layout> -} - -export const getLayout = function ( - Element: (...props: any) => JSX.Element, - layoutProps: Omit<LayoutProps, 'children'> = {} -) { - return (page: ReactElement) => <Element {...layoutProps}>{page}</Element> -} - -export const getDefaultLayoutDynamic = function (getLayoutProps: () => Omit<LayoutProps, 'children'>) { - return (page: ReactElement) => <Layout {...getLayoutProps()}>{page}</Layout> -} @@ -1 +0,0 @@ -export * from './get-layout' \ No newline at end of file @@ -1 +0,0 @@ -export * from './layout-props' \ No newline at end of file @@ -1,10 +0,0 @@ -import { Device } from "#/shared/lib/types/entities" - - -export interface LayoutProps { - children: React.ReactNode - titlePage?: string - title?: string | React.ReactNode | null - device?: Device - isLoader?: boolean -} \ No newline at end of file @@ -1,144 +0,0 @@ -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', '/change-password'] - -export const Layout: React.FC<LayoutProps> = ({ - children, - titlePage, - title = titlePage, - isLoader, - device = getDeviceType(), -}) => { - const { data: sessionData } = useSession() - const appState = useAppSelector((state) => state) - const dispatch = useAppDispatch() - - const desktop = device === 'desktop' - - const [sidemenuDefaultOpen, setSidemenuDefaultOpen] = useState(true) - - useTheme() - - const { data, status } = useSession() - - const router = useRouter() - - React.useEffect(() => { - dispatch(getUserBalance(data?.access)) - dispatch(getAllInfo(data?.access)) - }, [data]) - - 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> - <title>{titlePage} - - - - - - - - - {desktop ? ( - - - - - - {children} - - - - ) : ( - - - {children} - - )} - - - - ) -} @@ -1,2 +0,0 @@ -export * from './default' -export * from './layout-without-sidemenu' @@ -1,40 +0,0 @@ -import React, { Component, FunctionComponent } from 'react' -import { LayoutProps } from '../types' -import { Box } from '@mui/material' -import Head from 'next/head' -import { getDeviceType } from '#/shared/lib/helpers' -import { useTheme } from '#/entities/theme' - -export const LayoutWithoutSideMenu = ({ children, titlePage, device = getDeviceType() }: LayoutProps) => { - const desktop = device === 'desktop' - - useTheme() - - return ( - <> - - {titlePage && {titlePage}} - - - - - - - - - {children} - - - - ) -} @@ -1,3 +0,0 @@ -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 '#/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 { 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 { menuListMiddle, menuListTop } from '../../side-menu/ui/side-menu' @@ -155,31 +155,14 @@ export const MainMenuMobile = memo(() => { dispatch(change(null)) }} > - +
signOut()}> - + - + Реквизиты @@ -187,11 +170,7 @@ export const MainMenuMobile = memo(() => {
Error - 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]} @@ -213,10 +192,7 @@ 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 '#/entities/theme' -import { useAppDispatch, useAppSelector } from '#/app/store/store' -import { AccountMenu } from '#/shared' -import { NotificationMenu } from '#/shared' -import { TooltipFreeTokens } from '#/shared' +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' 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('#/shared/ui/error-modal'), { ssr: false }) + const ErrorModalLazy = dynamic(() => import('@/src/shared/ui/error-modal'), { ssr: false }) return ( @@ -111,11 +111,7 @@ 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' @@ -138,8 +134,7 @@ 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)', }, }, }} @@ -152,11 +147,7 @@ export const MainMenu: React.FC = memo(() => { {''} } @@ -186,13 +177,7 @@ export const MainMenu: React.FC = memo(() => { alt='Error' /> )} - {openErrorModal && ( - - )} + {openErrorModal && } { width={19} alt='Bell' /> - +