@@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file @@ -0,0 +1,5 @@ + + + \ No newline at end of file @@ -0,0 +1,11 @@ +import { API_URL } from '#/shared/lib/constants' +import axios from 'axios' +import { IModel } from '../types' + +export async function getBotParams(slug: string, token?: string) { + return await axios.get(API_URL + `/ml_models/${slug}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) +} @@ -0,0 +1,11 @@ +import { API_URL } from '#/shared/lib/constants' +import axios from 'axios' +import { IShortModel } from '../types' + +export async function getModelsImages(token?: string) { + return await axios.get(API_URL + '/ml_models/?category=images', { + headers: { + Authorization: `Bearer ${token}`, + }, + }) +} @@ -0,0 +1,2 @@ +export * from './bot.route' +export * from './images-bots.route' @@ -0,0 +1 @@ +export * from './use-images-bots' \ No newline at end of file @@ -0,0 +1,118 @@ +import { useState } from 'react' +import { IModel } from '../types' +import { useSession } from 'next-auth/react' +import { getBotParams } from '../api' +import { useAppDispatch } from '#/app/store/store' +import { setParams } from '#/app/store/model-parametres-store' + +export function useImageBot(slug: string) { + const [botParams, setBotParams] = useState(null) + const [version, setVersion] = useState('') + const [modelType, setModelType] = useState('') + + const dispatch = useAppDispatch() + + const { data } = useSession() + + function setDefault(bot: IModel) { + setVersion('') + + dispatch(setParams(bot.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {}))) + } + + // это пиз**ц + // нужен рефакторинг (я то в этом не разбираюсь) + // а стажеры и подавно)))) + function setStoreParams(bot: IModel) { + dispatch( + setParams( + bot.parameters.reduce( + (a, v) => + v.versions.includes(bot.versions[0].slug) + ? { ...a, [v.key]: v.values.default } + : { ...a }, + {} + ) + ) + ) + } + + function setAllParams(bot: IModel) { + setBotParams(bot) + setModelType(bot.slug) + + // store + if (bot.versions.length === 0) return setDefault(bot) + + setVersion(bot.versions[0].slug) + setStoreParams(bot) + } + + // это пиз**ц + const resetParams = () => { + if (botParams) { + dispatch(setParams({})) + if (botParams.versions.length !== 0) { + setVersion(botParams.versions[0].slug) + dispatch( + setParams( + botParams.parameters.reduce( + (a, v) => + v.versions.includes(botParams.versions[0].slug) + ? { ...a, [v.key]: v.values.default } + : { ...a }, + {} + ) + ) + ) + } else { + setVersion(botParams.slug) + dispatch( + setParams(botParams.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {})) + ) + } + } + } + + // это пиз**ц + const setDefaultParams = () => { + if (!botParams) return + + dispatch(setParams({})) + + if (version === '') { + return dispatch( + setParams(botParams.parameters.reduce((a, v) => ({ ...a, [v.key]: v.values.default }), {})) + ) + } + + dispatch( + setParams( + botParams.parameters.reduce( + (a, v) => (v.versions.includes(version) ? { ...a, [v.key]: v.values.default } : { ...a }), + {} + ) + ) + ) + } + + // api functions + + async function fetchBotParams() { + if (!data) return + + const { data: bot, ...response } = await getBotParams(slug, data.access) + + if (response.status < 400) setAllParams(bot) + } + + return { + botParams, + version, + modelType, + fetchBotParams, + setVersion, + resetParams, + setDefaultParams, + } +} @@ -0,0 +1,24 @@ +import { useState } from 'react' +import { getModelsImages } from '../api' +import { useSession } from 'next-auth/react' +import { IShortModel } from '../types' + +export function useImagesBots() { + const [bots, setBots] = useState([]) + + const { data } = useSession() + + async function fetchBots() { + if (!data) return + + const response = await getModelsImages(data.access) + + if (response.status < 400) setBots(response.data) + } + + return { + bots, + fetchBots, + setBots + } +} @@ -0,0 +1 @@ +export * from './model.types' \ No newline at end of file @@ -0,0 +1,55 @@ +type ModelForChats = 'chatgpt' | 'llama2' | 'vicuna' | 'deepl' | 'mistral' + +export interface IShortModel { + uid: string + title: string + description: string + slug: string + image: string + blocked: boolean + actual_stat: { + generation_time: string + tokens_cost: string + } +} + +export interface IModel { + uid: string + title: string + description: string + slug: string + image: string + settings: { is_active: boolean } + parameters: IModelParams[] + versions: IModelVersions[] + inputs: IModelInputs[] +} +export interface IModelParams { + name: string + description: string + key: string + type: string + required: boolean + values: { + availables: string[] + default: any + end: number + start: number + step: number + } + versions: string[] +} +export interface IModelVersions { + name: string + description: string + default: boolean + slug: string +} +export interface IModelInputs { + // type: 'image' | 'zip' | 'text' | 'audio' | 'pdf' | 'txt' + type: string + required: boolean + versions: string[] +} + +export default ModelForChats @@ -0,0 +1,3 @@ +export * from './api' +export * from './model' +export * from './types' \ No newline at end of file @@ -1,31 +1,72 @@ .card { - background-color: var(--new-ui-main-color); - width: 381px; - height: 227px; - border-radius: 15px; - margin-right: 20px; - margin-top: 20px; - - cursor: pointer; - padding: 30px; - box-sizing: border-box; - - @media (max-width:420px) { - width: 92vw; - } - - .description { - margin-top: 20px; - - .title { - font-weight: 600; - } - - .text { - color: var(--new-ui-gray-color); - font-weight: 400; - margin-top: 6px; - font-size: 15px; - } - } -} \ No newline at end of file + 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; + } + } +} + +.blocked { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + top: 20px; + right: 20px; + background-color: white; + border-radius: 100px; + padding-right: 20px; + padding: 8px 12px; + + span { + color: #ff2372; + font-weight: 500; + padding-left: 10px; + font-size: 14px; + } +} + +.locked { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.7); + border-radius: 15px; + padding-right: 20px; + + span { + color: white; + padding-top: 10px; + font-weight: 500; + } +} @@ -2,29 +2,37 @@ import React, { useEffect, useMemo } from 'react' import { Avatar, Box, Button, Typography } from '@mui/material' import Image from 'next/image' import Link from 'next/link' +import LockSvg from '@/src/assets/svg/lock.svg?react' +import BlockedSvg from '@/src/assets/svg/blocked.svg?react' import styles from './card.module.scss' +import { useThemeAndDevice } from '@/src/shared/lib/hooks' export type CardProps = { title: string icon: string text: string uid: string + blocked?: boolean slug: string accessed_models?: string[] | null } -const ChatCard = ({ text, icon, title, uid, slug, accessed_models }: CardProps) => { - +const ChatCard = ({ text, icon, title, uid, slug, accessed_models, blocked }: CardProps) => { const link = useMemo(() => { return accessed_models && !accessed_models.includes(slug) ? '/account?scope=subscribe' : `chat-bot/${slug}` }, [accessed_models]) + const { theme } = useThemeAndDevice() + return ( - + @@ -33,46 +41,28 @@ const ChatCard = ({ text, icon, title, uid, slug, accessed_models }: CardProps) {text} - {accessed_models && !accessed_models.includes(slug) && ( - - - - - + + + Модель недоступна + + + ) : ( + accessed_models && + !accessed_models.includes(slug) && ( +
+ + Недоступно в текущем тарифе +
+ ) )}
@@ -9,8 +9,8 @@ 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' +import { IShortModel } from '@/src/entities/model-entity' export async function getServerSideProps(context: any): Promise<{ props: any }> { const deviceType = getTypeDevice(context) @@ -42,7 +42,9 @@ const Page: React.FC = ({ deviceType }) => { return ( - Чат-боты + + Чат-боты + {bots ? ( bots.map((item, idx) => ( @@ -50,6 +52,7 @@ const Page: React.FC = ({ deviceType }) => { accessed_models={user.payment_plan.plan.accessed_models} uid={item.uid} key={idx} + blocked={item.blocked} title={item.title} icon={item.image} text={item.description} @@ -8,9 +8,9 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations' 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' +import { IShortModel } from '@/src/entities/model-entity' export async function getServerSideProps(context: any): Promise<{ props: any }> { const deviceType = getTypeDevice(context) @@ -38,7 +38,9 @@ export const Images: React.FC = ({ deviceType }) => { return ( - Изображения + + Изображения + {bots ? ( bots.map((item, idx) => { @@ -46,8 +48,9 @@ export const Images: React.FC = ({ deviceType }) => { { @@ -1,32 +1,70 @@ .card { - width: 381px; - overflow: hidden; - position: relative; - height: 290px; - border-radius: 15px; - background-color: var(--new-ui-main-color); - margin-top: 15px; - margin-right: 20px; + width: 381px; + overflow: hidden; + position: relative; + height: 290px; + border-radius: 15px; + background-color: var(--new-ui-main-color); + margin-top: 15px; + margin-right: 20px; - @media (max-width:420px) { - width: 92vw; - height: 300px; - } - - .description { - padding: 15px 20px; + @media (max-width: 420px) { + width: 92vw; + height: 300px; + } - .title { - color: var(--new-ui-text-color); - font-weight: 600; - font-size: 21px; - } - .text { - margin-top: 15px; - font-size: 15px; - color: var(--new-ui-gray-color); - } - } + .description { + padding: 15px 20px; + .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 +.blocked { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + top: 20px; + right: 20px; + background-color: white; + border-radius: 100px; + padding-right: 20px; + padding: 8px 12px; + + span { + color: #ff2372; + font-weight: 500; + padding-left: 10px; + font-size: 14px; + } +} +.locked { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.7); + border-radius: 15px; + padding-right: 20px; + + span { + color: white; + padding-top: 10px; + font-weight: 500; + } +} @@ -2,8 +2,11 @@ import React, { useMemo } from 'react' import { Box, Button, Typography } from '@mui/material' import Image from 'next/image' import Link from 'next/link' +import LockSvg from '@/src/assets/svg/lock.svg?react' +import BlockedSvg from '@/src/assets/svg/blocked.svg?react' import styles from './card.module.scss' +import { useThemeAndDevice } from '../lib/hooks' interface IProps { text: string @@ -11,19 +14,22 @@ interface IProps { title: string slug: string uid: string + blocked: boolean accessed_models?: string[] | null } -export const Card = ({ text, icon, title, uid, slug, accessed_models }: IProps) => { +export const Card = ({ text, icon, title, blocked, slug, accessed_models }: IProps) => { const link = useMemo(() => { return accessed_models && !accessed_models.includes(slug) ? '/account?scope=subscribe' : `/images/${slug}` }, [accessed_models]) + const { theme } = useThemeAndDevice() + return ( - + {text} - {accessed_models && !accessed_models.includes(slug) && ( - - - - - + + + Модель недоступна + + + ) : ( + accessed_models && + !accessed_models.includes(slug) && ( +
+ + Недоступно в текущем тарифе +
+ ) )}
@@ -254,7 +254,9 @@ export const SideMenu = ({ device, sidemenuDefaultOpen }: { device: Device; side
)} - {openErrorModal && } + {openErrorModal && ( + + )} {open && ( @@ -282,7 +284,10 @@ type MenuItemProps = { } export function MenuItem(props: MenuItemProps) { - const isActive = useMemo(() => props.link === props.pathname || props.activeList?.some((el) => props.pathname.includes(el)), [props.pathname]) + const isActive = useMemo( + () => props.link === props.pathname || props.activeList?.some((el) => props.pathname.includes(el)), + [props.pathname] + ) const theme = useAppSelector((state) => state.theme.theme) return ( @@ -307,7 +312,7 @@ export function MenuItem(props: MenuItemProps) { }, }} > - + !props.link && e.preventDefault()} href={props.link || ''}> - {''} + {typeof props.icon === 'string' ? ( + {''} + ) : ( +
+ {props.icon} +
+ )}