diff --git a/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx b/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx index c10dba0..dfd7f6d 100644 --- a/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx @@ -26,6 +26,7 @@ import type { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; import { isProduction } from "@/utils/defineDomain"; import { useQuizStore } from "@/stores/useQuizStore"; import { useTranslation } from "react-i18next"; +import { isNeftyanka } from "@/ui_kit/neftyankacrutch"; type Props = { currentQuestion: AnyTypedQuizQuestion; @@ -318,7 +319,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { }, }} > - {settings.cfg.formContact?.button || t("Get results")} + {isNeftyanka ? t("neftyanka button") : settings.cfg.formContact?.button || t("Get results")} {show_badge && ( diff --git a/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx b/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx index 1240c71..62b441a 100644 --- a/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx @@ -3,6 +3,7 @@ import { useRootContainerSize } from "@contexts/RootContainerWidthContext.ts"; import { QuizSettingsConfig } from "@model/settingsData.ts"; import { FC } from "react"; import { useTranslation } from "react-i18next"; +import { isNeftyanka } from "@/ui_kit/neftyankacrutch"; type ContactTextBlockProps = { settings: QuizSettingsConfig; @@ -47,7 +48,9 @@ export const ContactTextBlock: FC = ({ settings }) => { wordBreak: "break-word", }} > - {settings.cfg.formContact.title || t("Fill out the form to receive your test results")} + {isNeftyanka + ? t("neftyanka FK") + : settings.cfg.formContact.title || t("Fill out the form to receive your test results")} {settings.cfg.formContact.desc && ( state.answers); const isMobile = useRootContainerSize() < 450; const isTablet = useRootContainerSize() < 850; + const [isSending, setIsSending] = useState(false); + const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState(false); + const { quizId, preview } = useQuizStore(); + const [imageUrl, setImageUrl] = useState(null); const canvasRef = useRef(null); const containerCanvasRef = useRef(null); @@ -127,6 +146,38 @@ export const ImageVariant = ({ } }; + const handleFileUpload = async (file: File | undefined) => { + if (isSending || !file) return; + + const result = await uploadFile({ + file, + questionId, + quizId, + fileType: "picture", + preview, + onSuccess: (fileUrl) => { + setImageUrl(URL.createObjectURL(file)); + }, + onError: (error) => { + console.error(error); + enqueueSnackbar(t(error.message)); + }, + onProgress: () => { + setIsSending(true); + }, + }); + + setIsSending(false); + }; + + const onDrop = (event: React.DragEvent) => { + event.preventDefault(); + setIsDropzoneHighlighted(false); + + const file = event.dataTransfer.files[0]; + handleFileUpload(file); + }; + const choiceImgUrl = useMemo(() => { if (variant.editedUrlImagesList !== undefined && variant.editedUrlImagesList !== null) { return variant.editedUrlImagesList[isMobile ? "mobile" : isTablet ? "tablet" : "desktop"]; @@ -171,30 +222,48 @@ export const ImageVariant = ({ onClick={onVariantClick} > - + {variant.extendedText && ( - - - // + <> + + {own && ( + + handleFileUpload(target.files?.[0])} + hidden + accept={ACCEPT_SEND_FILE_TYPES_MAP.picture.join(",")} + type="file" + /> + + + )} + )} diff --git a/lib/components/ViewPublicationPage/questions/Text/TextNeftyanka.tsx b/lib/components/ViewPublicationPage/questions/Text/TextNeftyanka.tsx new file mode 100644 index 0000000..e2324ef --- /dev/null +++ b/lib/components/ViewPublicationPage/questions/Text/TextNeftyanka.tsx @@ -0,0 +1,120 @@ +import { Box, TextField as MuiTextField, TextFieldProps, Typography, useTheme } from "@mui/material"; + +import { Answer, useQuizViewStore } from "@stores/quizView"; +import { useRootContainerSize } from "@contexts/RootContainerWidthContext"; + +import { quizThemes } from "@utils/themes/Publication/themePublication"; + +import type { ChangeEvent, FC } from "react"; +import type { QuizQuestionText } from "@model/questionTypes/text"; +import { useQuizStore } from "@/stores/useQuizStore"; + +const TextField = MuiTextField as unknown as FC; // temporary fix ts(2590) + +interface TextSpecialProps { + currentQuestion: QuizQuestionText; + answer?: Answer; + stepNumber?: number | null; +} + +function highlightQuestions(text: string) { + // Регулярка с учётом возможной точки в конце + const regex = /(вопрос\s\d+[a-zA-Zа-яА-Я]\.?)/g; + + // Замена на с жирным текстом + return text.replace(regex, '$1'); +} + +export const TextNeftyanka = ({ currentQuestion, answer, stepNumber }: TextSpecialProps) => { + const { settings } = useQuizStore(); + const { updateAnswer } = useQuizViewStore((state) => state); + const isHorizontal = true; + const theme = useTheme(); + const isMobile = useRootContainerSize() < 650; + + const onInputChange = async ({ target }: ChangeEvent) => { + updateAnswer(currentQuestion.id, target.value, 0); + }; + + return ( + + + {isHorizontal && currentQuestion.content.back && currentQuestion.content.back !== " " && ( + event.preventDefault()} + > + + + )} + + {highlightQuestions(currentQuestion.title)} + + { + + } + + {!isHorizontal && currentQuestion.content.back && currentQuestion.content.back !== " " && ( + event.preventDefault()} + > + + + )} + + ); +}; diff --git a/lib/components/ViewPublicationPage/questions/Text/index.tsx b/lib/components/ViewPublicationPage/questions/Text/index.tsx index 081da54..6ecf3f2 100644 --- a/lib/components/ViewPublicationPage/questions/Text/index.tsx +++ b/lib/components/ViewPublicationPage/questions/Text/index.tsx @@ -5,6 +5,8 @@ import { TextSpecialHorisontal } from "./TextSpecialHorisontal"; import type { QuizQuestionText } from "@model/questionTypes/text"; import { useQuizStore } from "@/stores/useQuizStore"; +import { isNeftyanka } from "@/ui_kit/neftyankacrutch"; +import { TextNeftyanka } from "./TextNeftyanka"; type TextProps = { currentQuestion: QuizQuestionText; @@ -18,7 +20,16 @@ export const Text = ({ currentQuestion, stepNumber }: TextProps) => { const answers = useQuizViewStore((state) => state.answers); const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {}; - if (pathOnly === "/92ed5e3e-8e6a-491e-87d0-d3197682d0e3" || pathOnly === "/cc006b40-ccbd-4600-a1d3-f902f85aa0a0") + if (isNeftyanka) + return ( + + ); + + if (pathOnly === "/92ed5e3e-8e6a-491e-87d0-d3197682d0e3") return ( void; + onError?: (error: Error) => void; + onProgress?: (progress: number) => void; +} + +export interface UploadFileResult { + success: boolean; + fileUrl?: string; + error?: Error; +} + +export async function uploadFile({ + file, + questionId, + quizId, + fileType, + preview, + onSuccess, + onError, + onProgress, +}: UploadFileOptions): Promise { + try { + // Проверка размера файла + if (file.size > MAX_FILE_SIZE) { + const error = new Error("File is too big. Maximum size is 50 MB"); + onError?.(error); + return { success: false, error }; + } + + // Проверка типа файла + const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP[fileType].some((fileType) => + file.name.toLowerCase().endsWith(fileType) + ); + + if (!isFileTypeAccepted) { + const error = new Error("Incorrect file type selected"); + onError?.(error); + return { success: false, error }; + } + + // Загрузка файла + const data = await sendFile({ + questionId, + body: { + file, + name: file.name, + preview, + }, + qid: quizId, + }); + + // Отправка ответа + await sendAnswer({ + questionId, + body: `${data!.data.fileIDMap[questionId]}`, + qid: quizId, + preview, + }); + + const fileUrl = `${file.name}|${URL.createObjectURL(file)}`; + onSuccess?.(fileUrl); + onProgress?.(100); + + return { success: true, fileUrl }; + } catch (error) { + const err = error instanceof Error ? error : new Error("Unknown error occurred"); + onError?.(err); + return { success: false, error: err }; + } +} diff --git a/public/locales/ru.json b/public/locales/ru.json index 81a1bae..25ee072 100644 --- a/public/locales/ru.json +++ b/public/locales/ru.json @@ -53,5 +53,7 @@ "and": "и", "Get results": "Получить результаты", "Data sent successfully": "Данные успешно отправлены", - "Step": "Шаг" + "Step": "Шаг", + "neftyanka FK": "Заполните форму, чтобы отправить ваши ответы на викторину", + "neftyanka button": "Отправить" }