@@ -1,3 +1,11 @@ +## Precondition + + + +## Steps to reproduce + + + ## Expected Result @@ -10,10 +18,9 @@ Прикрепляйте видео/скриншоты тут --> +## Notes -## Steps to reproduce - - + *** @@ -0,0 +1,40 @@ +#!/usr/bin/env sh +# +# If you create/switch to a branch without a prefix (no '/'), +# automatically rename it to "feature/". +# +# Allowed prefixes (when '/' is present): +# fix/, feature/, release/, chore/ +# + +set -eu + +IS_BRANCH_CHECKOUT="${3:-0}" +if [ "$IS_BRANCH_CHECKOUT" != "1" ]; then + exit 0 +fi + +BRANCH="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)" +if [ -z "$BRANCH" ]; then + exit 0 +fi + +case "$BRANCH" in + fix/*|feature/*|release/*|chore/*) + exit 0 + ;; +esac + +# No prefix -> default to feature/ +if ! printf "%s" "$BRANCH" | grep -q '/'; then + NEW_BRANCH="feature/$BRANCH" + if git show-ref --verify --quiet "refs/heads/$NEW_BRANCH"; then + exit 0 + fi + git branch -m "$BRANCH" "$NEW_BRANCH" 2>/dev/null || true + exit 0 +fi + +# Has a prefix but it's not allowed: cannot reliably fix here (checkout already happened). +exit 0 + @@ -0,0 +1,35 @@ +#!/usr/bin/env sh +# +# Enforce allowed branch prefixes: +# fix/, feature/, release/, chore/ +# +# If branch has no prefix (no '/'), automatically rename to feature/. +# + +set -eu + +BRANCH="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)" +if [ -z "$BRANCH" ]; then + exit 0 +fi + +case "$BRANCH" in + fix/*|feature/*|release/*|chore/*) + exit 0 + ;; +esac + +# No slash => default to feature/ +if ! printf "%s" "$BRANCH" | grep -q '/'; then + NEW_BRANCH="feature/$BRANCH" + if ! git show-ref --verify --quiet "refs/heads/$NEW_BRANCH"; then + git branch -m "$BRANCH" "$NEW_BRANCH" + fi + exit 0 +fi + +echo >&2 "Ошибка: недопустимый префикс ветки '$BRANCH'." +echo >&2 "Разрешены только: fix/, feature/, release/, chore/." +echo >&2 "Либо используйте ветку без префикса — она будет автоматически переименована в feature/<имя>." +exit 1 + @@ -0,0 +1,65 @@ +#!/usr/bin/env sh +# +# Auto-prefix commit messages based on branch name: +# fix/..., feature/..., release/..., chore/... +# -> "refs #: " +# + +set -eu + +COMMIT_MSG_FILE="${1:-}" +COMMIT_SOURCE="${2:-}" + +if [ -z "$COMMIT_MSG_FILE" ] || [ ! -f "$COMMIT_MSG_FILE" ]; then + exit 0 +fi + +# Skip for merges/squashes/amends/templates (avoid fighting Git-generated messages). +case "$COMMIT_SOURCE" in + merge|squash|commit|template) + exit 0 + ;; +esac + +BRANCH="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)" +TASK_NUMBER="" +if printf "%s" "$BRANCH" | grep -Eq '^(fix|feature|release|chore)/[0-9]+'; then + TASK_NUMBER="$(printf "%s" "$BRANCH" | sed -E 's#^(fix|feature|release|chore)/([0-9]+).*#\2#')" +else + exit 0 +fi + +FIRST_LINE="$(LC_ALL=C sed -n '1p' "$COMMIT_MSG_FILE" || true)" + +# Do not touch special commit subjects. +case "$FIRST_LINE" in + "Merge "*|"fixup! "*|"squash! "*|"revert! "*) + exit 0 + ;; +esac + +PREFIX="refs #${TASK_NUMBER}: " + +# Already has the right prefix (or already references the number at start) -> no-op. +case "$FIRST_LINE" in + "$PREFIX"*|"refs #${TASK_NUMBER} "*|"refs #${TASK_NUMBER}:"*) + exit 0 + ;; +esac + +# If message already starts with some refs prefix, don't double-prefix. +case "$FIRST_LINE" in + "refs #"*|"refs: #"* ) + exit 0 + ;; +esac + +TMP_FILE="${COMMIT_MSG_FILE}.tmp.$$" +{ + printf "%s%s\n" "$PREFIX" "$FIRST_LINE" + LC_ALL=C sed -n '2,$p' "$COMMIT_MSG_FILE" +} > "$TMP_FILE" + +mv "$TMP_FILE" "$COMMIT_MSG_FILE" +exit 0 + @@ -1,14 +1,13 @@ -import { Modal, usePlatesStore } from '#/features/modals/model' +import { Modal, useModal } from '#/features/modals/model' import { useEffect } from 'react' import { jestRender } from './render' const ModalTester = ({ id, onModal }: { id: string; onModal: (modal: Modal) => void }) => { - const { getModal } = usePlatesStore() + const modal = useModal(id) useEffect(() => { - const modal = getModal(id) onModal(modal) - }, [id, onModal]) + }, [id, modal, onModal]) return null } Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-Black.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-BlackItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-Bold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-BoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-ExtraBold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-ExtraBoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-ExtraLight.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-ExtraLightItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-Italic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-Light.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-LightItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-Medium.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-MediumItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-Regular.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-SemiBold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-SemiBoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-Thin.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_18pt-ThinItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-Black.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-BlackItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-Bold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-BoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-ExtraBold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-ExtraBoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-ExtraLight.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-ExtraLightItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-Italic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-Light.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-LightItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-Medium.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-MediumItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-Regular.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-SemiBold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-SemiBoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-Thin.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_24pt-ThinItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-Black.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-BlackItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-Bold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-BoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-ExtraBold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-ExtraBoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-ExtraLight.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-ExtraLightItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-Italic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-Light.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-LightItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-Medium.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-MediumItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-Regular.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-SemiBold.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-SemiBoldItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-Thin.ttf differ Binary files /dev/null and b/public/fonts/Inter/static/Inter_28pt-ThinItalic.ttf differ Binary files /dev/null and b/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf differ Binary files /dev/null and b/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf differ Binary files a/public/fonts/Raleway/static/Raleway-Black.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-BlackItalic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-Bold.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-BoldItalic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-ExtraBold.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-ExtraBoldItalic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-ExtraLight.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-ExtraLightItalic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-Italic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-Light.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-LightItalic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-Medium.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-MediumItalic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-Regular.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-SemiBold.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-SemiBoldItalic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-Thin.ttf and /dev/null differ Binary files a/public/fonts/Raleway/static/Raleway-ThinItalic.ttf and /dev/null differ Binary files a/public/fonts/Raleway/Raleway-Italic-VariableFont_wght.ttf and /dev/null differ Binary files a/public/fonts/Raleway/Raleway-VariableFont_wght.ttf and /dev/null differ Binary files /dev/null and b/public/subscription/svg-icons/copy.png differ Binary files /dev/null and b/public/subscription/svg-icons/decor.png differ Binary files a/public/voice-clone/add-voice.png and b/public/voice-clone/add-voice.png differ Binary files a/public/voice-clone/my-generations.png and b/public/voice-clone/my-generations.png differ Binary files a/public/voice-clone/step-1.png and b/public/voice-clone/step-1.png differ @@ -2,8 +2,6 @@ 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' interface IProps { @@ -19,10 +17,8 @@ export const CheckboxFilter = ({ name, value, filters, item_key, description, se const [check, setCheck] = React.useState(value || false) React.useEffect(() => { - if (filters[item_key] !== undefined && filters[item_key] !== check) { - setCheck(filters[item_key]) - } - }, [filters]) + setCheck(filters[item_key] ?? value) + }, [filters, item_key, value]) return ( @@ -1,8 +1,9 @@ import React from 'react' import { Typography } from '@mui/material' import Box from '@mui/material/Box' -import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' + import { CommonTextArea } from '#/shared/ui/common-textarea' +import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' interface IProps { title: string @@ -0,0 +1,3 @@ +.selectTransparent { + background-color: transparent; +} @@ -2,11 +2,11 @@ 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 styles from './select_filter.module.scss' + interface IProps { selects: { availables: string[]; default: any; end: number; start: number; step: number } name: string @@ -20,10 +20,8 @@ export const SelectFilter = ({ selects, filters, item_key, name, description, se const [value, setValue] = React.useState(selects.default) React.useEffect(() => { - if (filters[item_key] !== undefined && filters[item_key] !== value) { - setValue(filters[item_key]) - } - }, [filters]) + setValue(filters[item_key] ?? selects.default) + }, [filters, item_key, selects.default]) const onChange = (e: SelectChangeEvent) => { setNewParam({ [item_key]: e.target.value }) @@ -38,19 +36,16 @@ export const SelectFilter = ({ selects, filters, item_key, name, description, se IconComponent={KeyboardArrowDownIcon} value={value} onChange={onChange} - inputProps={{ - MenuProps: { - MenuListProps: { - sx: { - color: '#A6A5A5', - backgroundColor: '#151518', - }, - }, + MenuProps={{ + transitionDuration: 0, + PaperProps: { + sx: { backgroundColor: '#151518', borderRadius: '8px', marginTop: '8px' }, + }, + MenuListProps: { + sx: { backgroundColor: '#151518', color: '#A6A5A5' }, }, }} - style={{ - backgroundColor: 'transparent', - }} + className={styles.selectTransparent} sx={{ boxShadow: 'none', borderRadius: '13px', @@ -1,7 +1,5 @@ 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' @@ -27,10 +25,8 @@ export const Slide = ({ title, values, filters, item_key, description, setNewPar } React.useEffect(() => { - if (filters[item_key] !== undefined && filters[item_key] !== range) { - setRange(filters[item_key]) - } - }, [filters]) + setRange(filters[item_key] ?? values.default) + }, [filters, item_key, values.default]) return ( <> @@ -1,8 +1,9 @@ import React, { useRef, useState } from 'react' -import Image from 'next/image' import { Box, Tooltip, Typography } from '@mui/material' +import Image from 'next/image' import { getFileTypeIcon, isImageFile } from '#/shared/lib/helpers' + import styles from './load_image.module.scss' const isVideoFile = (file: File) => { @@ -0,0 +1,3 @@ +.selectTransparent { + background-color: transparent; +} @@ -6,6 +6,8 @@ import { IModelVersions } from '#/shared/api/models/models' import { baseColor } from '#/shared/lib/constants/colors' import TooltipModelTypes from '#/widgets/filters-gpt/ui/tooltip-model-types' +import styles from './chat_select.module.scss' + interface ISelect { value: string setValue: React.Dispatch> @@ -28,19 +30,16 @@ export const ChatSelect: React.FC = ({ value, setValue, list, setDefaul IconComponent={KeyboardArrowDownIcon} value={value} onChange={onChange} - inputProps={{ - MenuProps: { - MenuListProps: { - sx: { - color: '#A6A5A5', - backgroundColor: '#151518', - }, - }, + MenuProps={{ + transitionDuration: 0, + PaperProps: { + sx: { backgroundColor: '#151518', borderRadius: '8px', marginTop: '8px' }, + }, + MenuListProps: { + sx: { backgroundColor: '#151518', color: '#A6A5A5' }, }, }} - style={{ - backgroundColor: 'transparent', - }} + className={styles.selectTransparent} sx={{ boxShadow: 'none', borderRadius: '13px', @@ -0,0 +1,3 @@ +.sectionSpacing { + margin-top: 15px; +} @@ -1,21 +1,22 @@ import React from 'react' import { Avatar, Box, Popover, Stack, Typography } from '@mui/material' import Button from '@mui/material/Button' -import axios from 'axios' import Image from 'next/image' import Link from 'next/link' import { useRouter } from 'next/router' import { signOut } from 'next-auth/react' import styles from '#/app/layout/styles/styles.module.css' + +import infoBarStyles from './info-bar.module.scss' import { useAppSelector } from '#/app/store/store' import { TooltipCustom } from '#/shared' -import { useFeatureFlag } from '#/shared/lib/hooks' -import { SvgIcon } from '#/shared/ui/svg' +import { getDaysLeft } from '#/shared/lib/helpers/date-helper' import { declineToken } from '#/shared/lib/helpers/get-token' +import { useFeatureFlag } from '#/shared/lib/hooks' import { IProps } from '#/shared/lib/types/entities' -import { getDaysLeft } from '#/shared/lib/helpers/date-helper' import { SubscriptionDaysBadge } from '#/shared/ui/subscription-days-badge/subscription-days-badge' +import { SvgIcon } from '#/shared/ui/svg' interface InfoBarProps extends IProps {} @@ -190,7 +191,7 @@ const InfoBar: React.FC = ({ device }) => { - + router.push('/account?scope=business')}> {''} @@ -203,7 +204,7 @@ const InfoBar: React.FC = ({ device }) => { {account_type === 'regular' && ( - + router.push('/account?scope=referral')}> {''} @@ -4,13 +4,12 @@ import { configureStore } from '@reduxjs/toolkit' import { paramsStore } from '#/app/store/model-parametres-store' import { balanceSlice } from '#/entities/balance' import { userSlice } from '#/entities/user-account' -import { settingsSlice } from '#/entities/user-account/model/settings' import { chatsReducer } from '#/features/chats/chats-slice' +import modalsReducer from '#/features/modals/model/modals-slice' import { pendingSlice } from '#/features/pending' import { stepperSlice } from '#/features/register-business' -import { copySlice } from '#/features/use-copy/copy-slice' - import { notificationSlice } from './notification-slice' +import toastReducer from './toast-slice' export const store = configureStore({ reducer: { @@ -18,9 +17,9 @@ export const store = configureStore({ stepper: stepperSlice.reducer, user: userSlice.reducer, notification: notificationSlice.reducer, + toast: toastReducer, + modals: modalsReducer, params: paramsStore.reducer, - settings: settingsSlice.reducer, - copy: copySlice.reducer, loading: pendingSlice.reducer, chats: chatsReducer, }, @@ -0,0 +1,43 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' + +export type ToastVariant = 'error' | 'success' + +export interface ToastState { + message: string + variant: ToastVariant + isOpened: boolean +} + +const initialState: ToastState = { + message: '', + variant: 'error', + isOpened: false, +} + +export const toastSlice = createSlice({ + name: 'toast', + initialState, + reducers: { + show: (state, action: PayloadAction<{ message: string; variant: ToastVariant }>) => { + state.message = action.payload.message + state.variant = action.payload.variant + state.isOpened = true + }, + hide: (state) => { + state.isOpened = false + }, + setOpened: (state, action: PayloadAction) => { + state.isOpened = action.payload + }, + setMessage: (state, action: PayloadAction) => { + state.message = action.payload + }, + setVariant: (state, action: PayloadAction) => { + state.variant = action.payload + }, + }, +}) + +export const { show, hide, setOpened, setMessage, setVariant } = toastSlice.actions + +export default toastSlice.reducer @@ -73,6 +73,14 @@ justify-content: flex-start; height: 100%; + .chartColumn { + display: flex; + justify-content: flex-start; + width: 53%; + height: 100%; + margin-right: 15px; + } + .list { width: 100%; overflow-y: auto; @@ -1,3 +1,9 @@ +/* Yandex Metrika noscript pixel (off-screen) */ +.metrika-pixel-hidden { + position: absolute; + left: -9999px; +} + :root { --air-color: #8280ff; --background-color-main: #303030; @@ -27,19 +33,19 @@ } @font-face { - font-family: 'Raleway'; + font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap; - src: url('/fonts/Raleway/Raleway-VariableFont_wght.ttf') format('truetype'); + src: url('/fonts/Inter/Inter-VariableFont_opsz,wght.ttf') format('truetype'); } @font-face { - font-family: 'Raleway'; + font-family: 'Inter'; font-style: italic; font-weight: 100 900; font-display: swap; - src: url('/fonts/Raleway/Raleway-Italic-VariableFont_wght.ttf') format('truetype'); + src: url('/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf') format('truetype'); } * { @@ -49,7 +55,7 @@ } html { - font-family: 'Raleway', sans-serif; + font-family: 'Inter', sans-serif; } body { @@ -449,17 +455,6 @@ button { font: inherit; color: inherit; background-color: transparent; - text-align: left; - cursor: pointer; -} - -button { - padding: 0; - border: none; - font: inherit; - color: inherit; - background-color: transparent; - text-align: left; cursor: pointer; } @@ -1,14 +0,0 @@ -import { Template } from '#/domains/copywrite/proxy/types/template' - -export const emptyTemplate: Template = { - id: 1000, - title: 'Пустой шаблон', - content: '', - keywords: [], - tov: '', - language: '', - resources_urls: [], - picture: '123', - target_audience: '', - description: '', -} @@ -1,6 +0,0 @@ -import { ContentState, EditorState } from 'draft-js' - -export const toEditorState = (text: string) => { - const newContentState = ContentState.createFromText(text) - return EditorState.createWithContent(newContentState) -} @@ -1,12 +0,0 @@ -export interface Template { - id: number - title: string - description: string - picture: string - content: string - target_audience: string - resources_urls: string[] - keywords: string[] - tov: string - language: string -} @@ -1,34 +0,0 @@ -import axios from 'axios' - -import { Template } from '#/domains/copywrite/proxy/types/template' -import { getApiUrl } from '#/shared/lib/constants' -import { Message } from '#/shared/lib/types/model' - -export class CopywriteProxy { - token?: string - - constructor(token: string | undefined) { - this.token = token - } - - static async getGeneration(token?: string): Promise { - const { data } = await axios.get(getApiUrl() + '/copywrite/', { - headers: { Authorization: `Bearer ${token}` }, - }) - return data - } - - static async getTemplates(token?: string): Promise { - const { data } = await axios.get(getApiUrl() + '/copywrite/templates/', { - headers: { Authorization: `Bearer ${token}` }, - }) - return data - } - - static async createTemplates(token?: string): Promise { - const { data } = await axios.post(getApiUrl() + '/copywrite/templates/', { - headers: { Authorization: `Bearer ${token}` }, - }) - return data - } -} @@ -1,44 +0,0 @@ -import React from 'react' -import { Typography } from '@mui/material' - -import { Input, Slider } from '#/shared' - -import { FiltersProps } from './types' - -export function Filters({ - strength, - setStrength, - upscale, - setUpscale, - negative_prompt, - num_inference_steps, - guidance_scale, - setGuidanceScale, - setNegative_prompt, - setSteps, -}: FiltersProps) { - return ( - <> - - - - - Запрос для исключения из генерации - - - ) -} @@ -1,17 +0,0 @@ -import { ChangeEvent } from 'react' - -export interface Setting { - strength: number - upscale: number - negative_prompt: string - num_inference_steps: number - guidance_scale: number -} - -export interface FiltersProps extends Setting { - setStrength: (e: Event, cur: number | number[]) => void - setUpscale: (e: Event, cur: number | number[]) => void - setGuidanceScale: (e: Event, cur: number | number[]) => void - setNegative_prompt: (e: ChangeEvent) => void - setSteps: (e: Event, cur: number | number[]) => void -} @@ -1,57 +0,0 @@ -import React from 'react' -import { Stack, Typography } from '@mui/material' - -import { Input, Slider } from '#/shared' -import { SelectUI } from '#/shared/ui/select' - -import { Filters } from './types' - -const sizes = [128, 256, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024] - -export function EpicPhotoFilters({ - guidance_scale, - height, - negative_prompt, - num_inference_steps, - num_outputs, - setGuidance_scale, - setHeight, - setNegative_prompt, - setNum_inference_steps, - setNum_outputs, - setWidth, - width, -}: Filters) { - return ( - <> - - - - - - Запрос для исключения из генерации - - - ) -} @@ -1,2 +0,0 @@ -export * from './epic-photo-filters' -export * from './types' @@ -1,20 +0,0 @@ -import { ChangeEvent, ChangeEventHandler } from 'react' -import { SelectChangeEvent } from '@mui/material' - -export interface Setting { - num_outputs: number - negative_prompt: string - width: number - height: number - num_inference_steps: number - guidance_scale: number -} - -export interface Filters extends Setting { - setWidth: (e: SelectChangeEvent) => void - setHeight: (e: SelectChangeEvent) => void - setNum_outputs: (e: Event, cur: number | number[]) => void - setNum_inference_steps: (e: Event, cur: number | number[]) => void - setGuidance_scale: (e: Event, cur: number | number[]) => void - setNegative_prompt: (e: ChangeEvent) => void -} @@ -1,2 +0,0 @@ -export * from './kandinsky-filters' -export * from './types' @@ -1,83 +0,0 @@ -import React from 'react' -import { Box, Typography } from '@mui/material' - -import { Input, Slider, SwitchCustom } from '#/shared' -import { SelectUI } from '#/shared/ui/select' - -import { Filters } from './types' - -export function KandinskyFilters({ - height, - isTranslate, - negativePrompt, - num_outputs, - setHeight, - setNegative_prompt, - setNumber, - setSteps, - setWidth, - steps, - width, - setIsTranslate, -}: Filters) { - return ( - <> - Настройки - - - - - - - - - - - - - - - - Переводить запрос - - - - - - - Запрос для исключения из генерации - - - - - ) -} @@ -1,20 +0,0 @@ -import { ChangeEvent } from 'react' -import { SelectChangeEvent } from '@mui/material' - -export interface Setting { - steps: number - num_outputs: number - width: number - height: number - isTranslate: boolean - negativePrompt: string -} - -export interface Filters extends Setting { - setWidth: (e: SelectChangeEvent) => void - setHeight: (e: SelectChangeEvent) => void - setSteps: (e: Event, cur: number | number[]) => void - setNumber: (e: Event, cur: number | number[]) => void - setNegative_prompt: (e: ChangeEvent) => void - setIsTranslate: () => void -} @@ -1,47 +0,0 @@ -import React from 'react' -import { Typography } from '@mui/material' - -import { Input, Slider } from '#/shared' -import { SelectUI } from '#/shared/ui/select' - -import { FiltersProps } from './types' - -const sizes = [384, 512, 576, 640, 704, 768] - -export function Filters({ - height, - negative_prompt, - num_inference_steps, - num_outputs, - setHeight, - setNegative_prompt, - setNumOutputs, - setSteps, - setWidth, - width, -}: FiltersProps) { - return ( - <> - - - - - Запрос для исключения из генерации - - - ) -} @@ -1,2 +0,0 @@ -export * from './filters' -export * from './types' @@ -1,18 +0,0 @@ -import { ChangeEvent } from 'react' -import { SelectChangeEvent } from '@mui/material' - -export interface Setting { - width: number - height: number - num_outputs: number - negative_prompt: string - num_inference_steps: number -} - -export interface FiltersProps extends Setting { - setWidth: (e: SelectChangeEvent) => void - setHeight: (e: SelectChangeEvent) => void - setNumOutputs: (e: Event, cur: number | number[]) => void - setSteps: (e: Event, cur: number | number[]) => void - setNegative_prompt: (e: ChangeEvent) => void -} @@ -34,7 +34,7 @@ export function AudioModelCard({ return ( -
+
{title}
@@ -181,3 +181,11 @@ letter-spacing: -1%; } } + +.card_blocked { + user-select: none; +} + +.card_selectable { + user-select: auto !important; +} @@ -179,3 +179,11 @@ letter-spacing: -1%; } } + +.card_blocked { + user-select: none; +} + +.card_selectable { + user-select: auto !important; +} @@ -25,8 +25,7 @@ export function ChatModelCard({ description, image, title, slug, accessed_models
{title} @@ -25,8 +25,7 @@ export function ImageModelCard({ description, image, title, slug, accessed_model
{title} @@ -23,7 +23,7 @@ export function VideoModelCard({ description, image, title, slug, accessed_model return ( -
+
{title}
@@ -1,17 +0,0 @@ -import { api } from '#/shared/api' - -import { IUserSetting } from '../model/types' - -export const addUserSettings = async ( - token: string, - setting: Omit -): Promise => { - try { - const { data } = await api.post('/api/users/settings/', setting, { - headers: { Authorization: `Bearer ${token}` }, - }) - return data - } catch (err) { - return null - } -} @@ -2,7 +2,7 @@ import axios, { AxiosResponse } from 'axios' import { getApiUrl } from '#/shared/lib/constants' -import { AccountType } from '../model/types' +import { AccountType } from '../model/account.types' export const getAccountType = async (token: string | null | undefined): Promise => { if (!token) { return 'regular' @@ -1,14 +0,0 @@ -import { api } from '#/shared/api' - -import { IUserSetting } from '../model/types' - -export const getUserSettings = async (token: string): Promise => { - try { - const { data } = await api.get('/api/users/settings/', { - headers: { Authorization: `Bearer ${token}` }, - }) - return data - } catch (err) { - return [] - } -} @@ -1 +0,0 @@ -export * from './settings.routes' \ No newline at end of file @@ -1,15 +0,0 @@ -import { api } from '#/shared/api' -import { Agent } from 'https' -import { IUserSetting } from '../model/types' - -export function getUserSettings() { - return api.get('/api/users/settings/') -} - -export function postUserSettings(option: Omit) { - return api.post('/api/users/settings/', option) -} - -export function updateUserSettings(id: string, value: any) { - return api.put(`/api/users/settings/${id}`, { value }) -} @@ -1,20 +0,0 @@ -import { api } from '#/shared/api' - -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[] -) => { - try { - await api.put(`/api/users/settings/${id}`, { value }, { - headers: { Authorization: `Bearer ${token}` }, - }) - return getUpdatedSettingsLocal({ settings, id, value }) - } catch (err) { - return null - } -} @@ -1,23 +0,0 @@ -import { Device } from '#/shared/lib/types/entities' - -import { IUserSetting, SettingType } from '../../model/types' - -interface IProps { - settings: Omit | null - targetType: SettingType - targetDevice: Device -} - -export function isSettingExist({ settings, targetDevice, targetType }: IProps): IUserSetting | null { - if (!settings || settings.length === 0) { - return null - } - - for (let i = 0; i < settings.length; i++) { - if (settings[i].type === targetType && settings[i].device === targetDevice) { - return settings[i] - } - } - - return null -} @@ -1,22 +0,0 @@ -import { IUserSetting, SettingValueType } from '../../model/types' - -interface IProps { - settings: IUserSetting[] - id: string - value: SettingValueType -} - -export function getUpdatedSettingsLocal({ settings, id, value }: IProps): IUserSetting[] { - if (settings.length === 0) { - return settings - } - - return settings.map((setting) => - setting.id === id - ? { - ...setting, - value, - } - : setting - ) -} @@ -0,0 +1,2 @@ +export type AccountType = 'regular' | 'business_host' | 'business_account' + @@ -1,2 +1 @@ -export * from './settings-context' -export * from './use-global-settings' \ No newline at end of file +export {} \ No newline at end of file @@ -1,9 +0,0 @@ -import { createUseContext } from '#/shared' -import { createContext } from 'react' -import { useGlobalSettings } from './use-global-settings' - -export const UserSettingsContext = createContext | null>(null) - -export const useUserSettingsContext = createUseContext(UserSettingsContext) - -export const UserSettingsContextProvider = UserSettingsContext.Provider @@ -1,93 +0,0 @@ -import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' - -import { addUserSettings } from '../api/add-user-settings' -import { getUserSettings } from '../api/get-user-settings' -import { updateUserSettings } from '../api/update-user-settings' -import { getUpdatedSettingsLocal } from '../lib/helpers/update-setting-local' - -import { IUserSetting, SettingValueType } from './types' -import { useLocalStorageSave } from '#/shared/lib/helpers/local-storage-helper' - -interface IAddSettings { - token: string | undefined | null - setting: Omit -} - -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 updateUserAccountSettings = createAsyncThunk( - 'users/updateSettings', - async ({ - token, - id, - value, - settings, - }: { - token: string | undefined | null - id: string - value: SettingValueType - settings: IUserSetting[] | null - }) => { - if (!token) { - return initialState.state - } - if (!settings) { - return initialState.state - } - return await updateUserSettings(token, id, value, settings) - } -) - -const initialState: { state: IUserSetting[] | null } = { - state: null, -} - -export const settingsSlice = createSlice({ - name: 'balance', - initialState, - reducers: { - setSettings: (state, action) => { - state.state = action.payload - }, - }, - extraReducers: (builder) => { - builder.addCase(getUserAccountSettings.fulfilled, (state, action) => { - state.state = action.payload - localStorage.setItem('global_settings', JSON.stringify(action.payload)) - }) - builder.addCase(addUserAccountSettings.fulfilled, (state, action) => { - if (state.state === null) state.state = [] - if (action.payload) { - state.state = [...state.state, action.payload] - localStorage.setItem('global_settings', JSON.stringify([...state.state, action.payload])) - } - }) - - builder.addCase(updateUserAccountSettings.fulfilled, (state, action) => { - if (action.payload) { - state.state = action.payload - localStorage.setItem('global_settings', JSON.stringify(action.payload)) - } - }) - }, -}) - -export const { setSettings } = settingsSlice.actions @@ -1,16 +0,0 @@ -import { Device } from '#/shared/lib/types/entities' - -export type AccountType = 'regular' | 'business_host' | 'business_account' - -export type SettingType = 'sidemenu' | string - -export type SettingValueType = { - sidemenu_state?: 'opened' | 'closed' -} - -export interface IUserSetting { - id: string - device: Device - type: SettingType - value: SettingValueType | any -} @@ -1,66 +0,0 @@ -import { useLocalStorage } from 'usehooks-ts' -import { IUserSetting } from './types' -import { makePrivateRequest } from '#/shared/api' -import { getUserSettings, postUserSettings, updateUserSettings } from '../api' - -import { getDeviceType } from '#/shared' -import { useCallback, useEffect, useRef, useState } from 'react' -import { useSession } from 'next-auth/react' -import { useShowDataStore } from '#/shared/lib/hooks/use-show-data' - -export function useGlobalSettings() { - const [settings, setSettings] = useLocalStorage('global_settings', []) - - const { showMessage } = useShowDataStore() - - const device = getDeviceType() - - const { data } = useSession() - - const addSettings = makePrivateRequest(async (type: string, value: any) => { - const { status, data } = await postUserSettings({ device, type, value }) - - if (status !== 200) return showMessage('Ошибка создания настроек') - - setSettings((s) => [...s, data]) - }) - - const updateSettings = useCallback( - makePrivateRequest(async (type: string, value: any) => { - const option = settings.find((x) => x.type === type) - - if (!option) return showMessage('Ошибка присвоения настроек') - - const { status } = await updateUserSettings(option.id, value) - - if (status !== 204) return showMessage('Ошибка создания настроек') - - setSettings((s) => [...s.filter((x) => x.type !== type), { ...option, value }]) - }), - [settings, data] - ) - - const fetchUserSettings = makePrivateRequest(async () => { - const { status, data } = await getUserSettings() - - const device = getDeviceType() - - if (status !== 200) return showMessage('Ошибка загрузки данных пользователя') - - setSettings(data.filter((s) => s.device === device)) - }) - - const getOptionValue = (type: string, initial: any) => { - const option = settings.find((o) => o.type === type) - return option ? option.value : initial - } - - return { - settings, - setSettings, - addSettings, - fetchUserSettings, - updateSettings, - getOptionValue, - } -} @@ -1,8 +1,6 @@ import React, { useState } from 'react' import { Box, TextField, Typography } from '@mui/material' -import axios from 'axios' -import { Dayjs } from 'dayjs' -import { useRouter } from 'next/navigation' +import dayjs, { Dayjs } from 'dayjs' import { useSession } from 'next-auth/react' import { ButtonGray, ButtonUI, Error, InputStyleDark, Loader, Modal } from '#/shared' @@ -38,6 +36,11 @@ export const ApiKeyModal = ({ const keyName = `Ключ ${Math.floor(Math.random() * 100000) + 1}` + if (endDate && dayjs(endDate).isBefore(dayjs(), 'day')) { + showMessage('Указана неверная дата') + return + } + setIsLoading(true) const response = endDate && endDate !== '' @@ -1,17 +0,0 @@ -import { accountApi } from '#/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) - } -} - -export const authTelegramYandex = async (email: any, password: any) => { - const user = await accountApi.loginByEmail(email, password) - - if (user !== null) { - ;(window as any).Telegram.WebApp.sendData(user.token.access) - } -} @@ -1 +0,0 @@ -export { authTelegram } from './model/auth' @@ -0,0 +1,55 @@ +import { IModelParams } from '#/shared/api/models/models' + +export type ModelParamValues = Record + +export function getAllowedParamKeys(params: IModelParams[], currentVersion: string): Set { + return new Set( + params + .filter((el) => { + if (el.versions.length === 0) { + return true + } + + if (!currentVersion) { + return true + } + + return el.versions.includes(currentVersion) + }) + .map((el) => el.key) + ) +} + +export function pruneModelParams( + includeParams: ModelParamValues, + params: IModelParams[], + currentVersion: string +): ModelParamValues | null { + const allowedKeys = getAllowedParamKeys(params, currentVersion) + + if (allowedKeys.size === 0) { + return Object.keys(includeParams).length !== 0 ? {} : null + } + + const filteredEntries = Object.entries(includeParams).filter(([key]) => allowedKeys.has(key)) + + if (filteredEntries.length === Object.keys(includeParams).length) { + return null + } + + return filteredEntries.length ? Object.fromEntries(filteredEntries) : {} +} + +export function buildDefaultParamsForVersion(parameters: IModelParams[], version: string): ModelParamValues { + if (!version) { + return parameters.reduce((acc, param) => ({ ...acc, [param.key]: param.values.default }), {}) + } + + return parameters.reduce((acc, param) => { + if (param.versions.length === 0 || param.versions.includes(version)) { + return { ...acc, [param.key]: param.values.default } + } + + return acc + }, {} as ModelParamValues) +} @@ -0,0 +1,27 @@ +import React from 'react' + +import { replaceParams } from '#/app/store/model-parametres-store' +import { useAppDispatch, useAppSelector } from '#/app/store/store' +import { IModelParams } from '#/shared/api/models/models' + +import { pruneModelParams } from '../lib/prune-model-params' + +export function usePruneModelParams(params: IModelParams[] | undefined, currentVersion: string) { + const includeParams = useAppSelector((state) => state.params.params) as Record< + string, + string | number | number[] | boolean + > + const dispatch = useAppDispatch() + + React.useEffect(() => { + if (!params?.length) { + return + } + + const nextPayload = pruneModelParams(includeParams, params, currentVersion) + + if (nextPayload !== null) { + dispatch(replaceParams(nextPayload)) + } + }, [currentVersion, dispatch, includeParams, params]) +} @@ -1,126 +1,94 @@ -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 { replaceParams, setParams } from '#/app/store/model-parametres-store' -import { useAppDispatch, useAppSelector } from '#/app/store/store' -import { IModelParams } from '#/shared/api/models/models' - -interface IProps { - params: IModelParams[] - currentVersion: string -} -export default function BotParamsMap({ params, currentVersion }: IProps) { - const includeParams = useAppSelector((state) => state.params.params) - const dispatch = useAppDispatch() - - const setNewParam = React.useCallback( - (payload: { [key: string]: string | number | number[] | boolean }) => { - dispatch(setParams(payload)) - }, - [dispatch] - ) - - React.useEffect(() => { - if (!params) { - return - } - - const allowedKeys = new Set( - params - .filter((el) => { - if (el.versions.length === 0) { - return true - } - - // Согласовано с chat-bot filteredParams: без выбранной версии считаем все параметры допустимыми - if (!currentVersion) { - return true - } - - return el.versions.includes(currentVersion) - }) - .map((el) => el.key) - ) - - if (allowedKeys.size === 0) { - if (Object.keys(includeParams).length !== 0) { - dispatch(replaceParams({})) - } - return - } - - const filteredEntries = Object.entries(includeParams).filter(([key]) => allowedKeys.has(key)) - - if (filteredEntries.length !== Object.keys(includeParams).length) { - const nextPayload = filteredEntries.length ? Object.fromEntries(filteredEntries) : {} - dispatch(replaceParams(nextPayload)) - } - }, [currentVersion, dispatch, includeParams, params, setNewParam]) - - return ( - - {params.map((item, idx) => { - if (item.versions.length === 0 || item.versions.includes(currentVersion)) { - if (item.type == 'floatrange' || item.type == 'intrange') { - return ( - - ) - } - if (item.type == 'bool') { - return ( - - ) - } - - if (item.type == 'list') { - return ( - - ) - } - - if (item.type == 'int' || item.type == 'str') { - return ( - - ) - } - } - })} - - ) -} +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 { usePruneModelParams } from './model/use-prune-model-params' + +interface IProps { + params: IModelParams[] + currentVersion: string +} +export default function BotParamsMap({ params, currentVersion }: IProps) { + const includeParams = useAppSelector((state) => state.params.params) + const dispatch = useAppDispatch() + + usePruneModelParams(params, currentVersion) + + const setNewParam = React.useCallback( + (payload: { [key: string]: string | number | number[] | boolean }) => { + dispatch(setParams(payload)) + }, + [dispatch] + ) + + return ( + + {params.map((item, idx) => { + if (item.versions.length === 0 || item.versions.includes(currentVersion)) { + if (item.type == 'floatrange' || item.type == 'intrange') { + return ( + + ) + } + if (item.type == 'bool') { + return ( + + ) + } + + if (item.type == 'list') { + return ( + + ) + } + + if (item.type == 'int' || item.type == 'str') { + return ( + + ) + } + } + })} + + ) +} + \ No newline at end of file @@ -1,4 +1,4 @@ -import axios, { AxiosResponse } from 'axios' +import axios from 'axios' import { getApiUrl } from '#/shared/lib/constants' @@ -1,4 +1,4 @@ -import axios, { AxiosResponse } from 'axios' +import axios from 'axios' import { getApiUrl } from '#/shared/lib/constants' @@ -1,4 +1,4 @@ -import axios, { AxiosResponse } from 'axios' +import axios from 'axios' import { getApiUrl } from '#/shared/lib/constants' @@ -1,4 +1,4 @@ -import axios, { AxiosResponse } from 'axios' +import axios from 'axios' import { getApiUrl } from '#/shared/lib/constants' @@ -1,6 +1,6 @@ import React, { FC, useState } from 'react' import { useForm } from 'react-hook-form' -import { SubmitErrorHandler } from 'react-hook-form/dist/types/form' +import { SubmitErrorHandler } from 'react-hook-form' import { Box, TextField, Typography } from '@mui/material' import { useSession } from 'next-auth/react' @@ -1,6 +1,6 @@ import React, { FC, useEffect, useState } from 'react' import { useForm } from 'react-hook-form' -import { SubmitErrorHandler } from 'react-hook-form/dist/types/form' +import { SubmitErrorHandler } from 'react-hook-form' import { Box, TextField, Typography } from '@mui/material' import axios from 'axios' import { useSession } from 'next-auth/react' @@ -2,7 +2,6 @@ import React, { useState } from 'react' import { Box, Typography } from '@mui/material' import axios from 'axios' import { Dayjs } from 'dayjs' -import { useRouter } from 'next/navigation' import { useSession } from 'next-auth/react' import { ButtonGray, ButtonUI, Error, Loader, Modal } from '#/shared' @@ -77,7 +77,7 @@ export const LimitModal: React.FC = ({ }} > - Сотрудник {person?.email} + Сотрудник {person?.email} @@ -1,4 +1,3 @@ -import { useSession } from 'next-auth/react' import { changePassword } from '../api/change-password' import { useState } from 'react' import { getModalById, PLATE_CHANGE_PASSWORD } from '#/features/modals' @@ -1,32 +0,0 @@ -import React, { ReactNode } from 'react' -import { Menu, MenuItem } from '@mui/material' - -interface IProps { - children: ReactNode - clicked: boolean - handleClose: () => void - pointY: number - pointX: number -} - -export const ContextMenu = ({ children, pointX, pointY, clicked, handleClose }: IProps) => { - return ( - - {children} - - ) -} @@ -13,7 +13,7 @@ export function useCreateMediaMessage( modelType: 'video' | 'image' | 'audio' | 'voice', device: Device, setMessages: Dispatch>, - mobileScrollContainer: RefObject + mobileScrollContainer: RefObject ) { const [isComplete, setIsComplete] = useState(false) const [createLoading, setCreateLoading] = useState(false) @@ -4,8 +4,7 @@ interface SendReport { errorMessage: string email: string file?: File - company?:string -} +} export async function sendReport({errorMessage, email, file}: SendReport) { let formData = new FormData() @@ -16,15 +15,3 @@ export async function sendReport({errorMessage, email, file}: SendReport) { } return await api.post('/reports/', formData) } - - -export async function sendBusinessReport({errorMessage, email, company, file}: SendReport) { - let formData = new FormData() - - formData.append('report_text', `${email}: ${errorMessage}`) - formData.append('company_name', `${company}`) - if (file) { - formData.append('images', file) - } - return await api.post('/reports/business-support/', formData) -} @@ -5,11 +5,11 @@ import { useAppSelector } from '#/app/store/store' import { makePrivateRequest } from '#/shared/api' import { useShowDataStore } from '#/shared/lib/hooks/use-show-data' -import { sendBusinessReport, sendReport } from '../api' +import { sendReport } from '../api' const MAX_FILE_SIZE = 4.5 * 1024 * 1024 // 4.5 MB -export const useErrorReport = (reportType:'business' | 'default' = 'default') => { +export const useErrorReport = () => { const email = useAppSelector((state) => state.user.email) const [file, setFile] = React.useState() @@ -34,7 +34,7 @@ export const useErrorReport = (reportType:'business' | 'default' = 'default') => } } - const sendError = makePrivateRequest(async (company) => { + const sendError = makePrivateRequest(async () => { if (errorMessage.trim() === '') { showMessage('Поле не должно быть пустым.') setError(true) @@ -42,10 +42,7 @@ export const useErrorReport = (reportType:'business' | 'default' = 'default') => return } - const response = await (reportType === 'business' - ? sendBusinessReport({ errorMessage, email, company, file }) - : sendReport({ errorMessage, email, file }) - ) + const response = await sendReport({ errorMessage, email, file }) if (response.status === 201) { setErrorMessage('') @@ -1,120 +0,0 @@ -import * as React from 'react' -import { useForm } from 'react-hook-form' -import { Typography } from '@mui/material' - -import { useAppSelector } from '#/app/store/store' -import AttachSvg from '#/assets/svg/attach.svg?react' -import PlusSvg from '#/assets/svg/plus.svg?react' -import { BUSINESS_ERROR_REPORT, getModalById, PlateTemplate } from '#/features/modals' -import { CompanyAutocomplete } from '#/features/register-business/api/company-autocomplete' -import { companyNameOptions } from '#/features/register-business/lib/constatnts-step-legal-informative' -import { c } from '#/shared' -import { useShowDataStore } from '#/shared/lib/hooks/use-show-data' -import { CommonButton } from '#/shared/ui/button' -import { CommonTextArea } from '#/shared/ui/common-textarea' - -import { useErrorReport } from '../model/use-error-report' - -import styles from './error-report.module.scss' - -export const BusinessErrorReportPlate = () => { - const modal = getModalById(BUSINESS_ERROR_REPORT) - const { error, isSend, setErrorMessage, handleFileChange, file, setFile, sendError } = useErrorReport('business') - const { showMessage } = useShowDataStore() - - const { companyName } = useAppSelector((state) => state.stepper.dataForCreate) - - const methods = useForm({ - defaultValues: { - companyName - }, - }) - - const handleSubmit = () => { - const formValues = methods.getValues() - const company = formValues.companyName?.trim() - - if (!company) { - showMessage('Пожалуйста, укажите наименование организации') - return - } - - sendError(company) - } - - return ( - - {isSend ? ( -
-

Спасибо!

-

Ваше сообщение направлено нашим специалистам. Ответ придет на почту, указанную при регистрации

-
- ) : ( -
e.stopPropagation()}> -

Запрос для компаний и партнеров

- - Опишите ваш сценарий — подберем решение и ответим за день. Работаем для корпораций, агентств и интеграторов - - - - - setErrorMessage(evt.target.value)} - buttomSlot={ - <> - - - } - /> - {file && ( -
-
- -
- {file.name} - {(file.size / 1024).toFixed(1)} KB -
-
- - -
- )} - -
- modal.setState(false)}> - Отмена - - - Отправить - -
-
- )} -
- ) -} \ No newline at end of file @@ -120,6 +120,14 @@ } } +.fileInputHidden { + display: none; +} + +.fileInputLabel { + cursor: pointer; +} + .textarea { &__file { background: rgba(255, 255, 255, 0.06); @@ -40,10 +40,10 @@ export const ErrorReportPlate = () => { onChange={handleFileChange} type='file' accept='.jpg,.jpeg,.png' - style={{ display: 'none' }} + className={styles.fileInputHidden} id='file-input' /> -