From af1264a170b3d70ab9cf57e2d028e867b4c8ed3b Mon Sep 17 00:00:00 2001 From: nflnkr <105123049+nflnkr@users.noreply.github.com> Date: Tue, 30 Jan 2024 19:49:33 +0300 Subject: [PATCH] fix some types remove some ts-ignore's refactor stores --- src/api/quizRelase.ts | 6 +- src/model/api/getQuizData.ts | 55 ++ src/model/settingsData.ts | 91 +- src/pages/ViewPublicationPage/ContactForm.tsx | 644 +++++++------ src/pages/ViewPublicationPage/Footer.tsx | 581 ++++++------ src/pages/ViewPublicationPage/Question.tsx | 237 +++-- src/pages/ViewPublicationPage/ResultForm.tsx | 412 ++++----- .../StartPageViewPublication.tsx | 872 +++++++++--------- src/pages/ViewPublicationPage/index.tsx | 171 ++-- .../ViewPublicationPage/questions/Date.tsx | 10 +- .../ViewPublicationPage/questions/Emoji.tsx | 33 +- .../ViewPublicationPage/questions/File.tsx | 5 +- .../ViewPublicationPage/questions/Images.tsx | 5 +- .../ViewPublicationPage/questions/Number.tsx | 355 +++---- .../ViewPublicationPage/questions/Rating.tsx | 3 +- .../ViewPublicationPage/questions/Select.tsx | 7 +- .../ViewPublicationPage/questions/Text.tsx | 3 +- .../ViewPublicationPage/questions/Variant.tsx | 410 ++++---- .../ViewPublicationPage/questions/Varimg.tsx | 20 +- src/stores/quizData/actions.ts | 9 +- src/stores/quizData/store.ts | 32 +- src/stores/quizView/store.ts | 155 ++-- src/ui_kit/CustomCheckbox.tsx | 5 +- src/ui_kit/LoadingSkeleton.tsx | 17 + .../themes/Publication/themePublication.ts | 45 +- 25 files changed, 2061 insertions(+), 2122 deletions(-) create mode 100644 src/model/api/getQuizData.ts create mode 100644 src/ui_kit/LoadingSkeleton.tsx diff --git a/src/api/quizRelase.ts b/src/api/quizRelase.ts index 237e604..6b4448c 100644 --- a/src/api/quizRelase.ts +++ b/src/api/quizRelase.ts @@ -1,7 +1,7 @@ +import { GetQuizDataResponse } from "@model/api/getQuizData"; import axios from "axios"; import type { AxiosError } from "axios"; -import type { GetDataResponse } from "../model/settingsData"; let SESSIONS = ""; @@ -19,7 +19,7 @@ export const publicationMakeRequest = ({ url, body }: any) => { }; export async function getData(quizId: string): Promise<{ - data: GetDataResponse | null; + data: GetQuizDataResponse | null; isRecentlyCompleted: boolean; error?: string; }> { @@ -29,7 +29,7 @@ export async function getData(quizId: string): Promise<{ : "ef836ff8-35b1-4031-9acf-af5766bac2b2"; try { - const { data, headers } = await axios( + const { data, headers } = await axios( `https://s.hbpn.link/answer/settings`, { method: "POST", diff --git a/src/model/api/getQuizData.ts b/src/model/api/getQuizData.ts new file mode 100644 index 0000000..9fab68d --- /dev/null +++ b/src/model/api/getQuizData.ts @@ -0,0 +1,55 @@ +import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; +import { QuizSettings } from "@model/settingsData"; + +export interface GetQuizDataResponse { + cnt: number; + settings: { + fp: boolean; + rep: boolean; + name: string; + cfg: string; + lim: number; + due: number; + delay: number; + pausable: boolean; + }; + items: { + id: number; + title: string; + desc: string; + typ: string; + req: boolean; + p: number; + c: string; + }[]; +} + +export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: string): QuizSettings { + const items: QuizSettings["items"] = quizDataResponse.items.map((item) => { + const content = JSON.parse(item.c); + + return { + description: item.desc, + id: item.id, + page: item.p, + required: item.req, + title: item.title, + type: item.typ, + content + } as unknown as AnyTypedQuizQuestion; + }); + + const settings: QuizSettings["settings"] = { + qid: quizId, + fp: quizDataResponse.settings.fp, + rep: quizDataResponse.settings.rep, + name: quizDataResponse.settings.name, + cfg: JSON.parse(quizDataResponse?.settings.cfg), + lim: quizDataResponse.settings.lim, + due: quizDataResponse.settings.due, + delay: quizDataResponse.settings.delay, + pausable: quizDataResponse.settings.pausable + }; + + return { cnt: quizDataResponse.cnt, settings, items }; +} diff --git a/src/model/settingsData.ts b/src/model/settingsData.ts index 7568888..c7009b2 100644 --- a/src/model/settingsData.ts +++ b/src/model/settingsData.ts @@ -1,5 +1,4 @@ -import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; -// import { QuizConfig } from "@model/quizSettings"; +import { AnyTypedQuizQuestion } from "./questionTypes/shared"; export type QuizStartpageType = "standard" | "expanded" | "centered" | null; @@ -9,55 +8,50 @@ export type QuizType = "quiz" | "form"; export type QuizResultsType = true | null; +export type QuizTheme = + | "StandardTheme" + | "StandardDarkTheme" + | "PinkTheme" + | "PinkDarkTheme" + | "BlackWhiteTheme" + | "OliveTheme" + | "YellowTheme" + | "GoldDarkTheme" + | "PurpleTheme" + | "BlueTheme" + | "BlueDarkTheme"; + export type FCField = { - text: string - innerText: string - key: string - required: boolean - used: boolean -} -export interface GetDataResponse { - cnt: number; + text: string; + innerText: string; + key: string; + required: boolean; + used: boolean; +}; + +export type QuizSettings = { + items: AnyTypedQuizQuestion[]; settings: { + qid: string; fp: boolean; rep: boolean; name: string; - cfg: string; lim: number; due: number; delay: number; pausable: boolean; + cfg: QuizConfig; }; - items: GetItems[]; -} - - - -export type QuestionsStore = { - items: (AnyTypedQuizQuestion)[]; - settings: Settings; cnt: number; }; -export interface Settings { - - fp: boolean; - rep: boolean; - name: string; - lim: number; - due: number; - delay: number; - pausable: boolean; - cfg: QuizConfig -} - export interface QuizConfig { type: QuizType; noStartPage: boolean; startpageType: QuizStartpageType; results: QuizResultsType; haveRoot: string; - theme: "StandardTheme" | "StandardDarkTheme" | "PinkTheme" | "PinkDarkTheme" | "BlackWhiteTheme" | "OliveTheme" | "YellowTheme" | "GoldDarkTheme" | "PurpleTheme" | "BlueTheme" | "BlueDarkTheme"; + theme: QuizTheme; resultInfo: { when: "email" | ""; share: boolean; @@ -84,7 +78,6 @@ export interface QuizConfig { cycle: boolean; }; }; - formContact: { title: string; desc: string; @@ -93,7 +86,7 @@ export interface QuizConfig { phone: FCField; text: FCField; address: FCField; - button: string + button: string; }; info: { phonenumber: string; @@ -105,26 +98,12 @@ export interface QuizConfig { meta: string; } -export interface GetItems { - id: number; - title: string; - desc: string; - typ: string; - req: boolean; - p: number; - c: string; -} - export interface QuizItems { - - description: string; - id: number; - page: number; - required: boolean; - title: string; - type: string; - content: QuizItemsContent -} -export interface QuizItemsContent { - -} \ No newline at end of file + description: string; + id: number; + page: number; + required: boolean; + title: string; + type: string; + content: unknown; +} diff --git a/src/pages/ViewPublicationPage/ContactForm.tsx b/src/pages/ViewPublicationPage/ContactForm.tsx index 2db30c1..ec3e9da 100644 --- a/src/pages/ViewPublicationPage/ContactForm.tsx +++ b/src/pages/ViewPublicationPage/ContactForm.tsx @@ -1,379 +1,363 @@ -import { Box, Typography, Button, useMediaQuery, TextField, Link, InputAdornment, useTheme } from "@mui/material"; -import NameIcon from "@icons/ContactFormIcon/NameIcon"; +import AddressIcon from "@icons/ContactFormIcon/AddressIcon"; import EmailIcon from "@icons/ContactFormIcon/EmailIcon"; +import NameIcon from "@icons/ContactFormIcon/NameIcon"; import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon"; import TextIcon from "@icons/ContactFormIcon/TextIcon"; -import AddressIcon from "@icons/ContactFormIcon/AddressIcon"; -import { useDebouncedCallback } from "use-debounce"; +import { Box, Button, InputAdornment, Link, TextField as MuiTextField, TextFieldProps, Typography, useMediaQuery, useTheme } from "@mui/material"; + import CustomCheckbox from "@ui_kit/CustomCheckbox"; -import { useEffect, useRef, useState } from "react"; -import { useQuestionsStore } from "@stores/quizData/store"; +import { FC, useRef, useState } from "react"; -import { checkEmptyData } from "./tools/checkEmptyData"; -import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; -import { enqueueSnackbar } from "notistack"; import { sendFC } from "@api/quizRelase"; import { NameplateLogo } from "@icons/NameplateLogo"; -import { modes } from "../../utils/themes/Publication/themePublication"; import { QuizQuestionResult } from "@model/questionTypes/result"; +import { quizThemes } from "@utils/themes/Publication/themePublication"; +import { enqueueSnackbar } from "notistack"; import { ApologyPage } from "./ApologyPage"; +import { checkEmptyData } from "./tools/checkEmptyData"; +import { useQuestionsStore } from "@stores/quizData/store"; +const TextField = MuiTextField as unknown as FC; // temporary fix ts(2590) -const EMAIL_REGEXP = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu; +const EMAIL_REGEXP = /^(([^<>()[\].,:\s@"]+(\.[^<>()[\].,:\s@"]+)*)|(".+"))@(([^<>()[\].,:\s@"]+\.)+[^<>()[\].,:\s@"]{2,})$/iu; + +type ContactType = + | "name" + | "email" + | "phone" + | "text" + | "adress"; type ContactFormProps = { - currentQuestion: any; - showResultForm: boolean; - setShowContactForm: (show: boolean) => void; - setShowResultForm: (show: boolean) => void; + currentQuestion: any; + showResultForm: boolean; + setShowContactForm: (show: boolean) => void; + setShowResultForm: (show: boolean) => void; }; -const icons = [ - { type: "name", icon: NameIcon, defaultText: "Введите имя", defaultTitle: "имя", backendName: "name" }, - { type: "email", icon: EmailIcon, defaultText: "Введите Email", defaultTitle: "Email", backendName: "email" }, - { type: "phone", icon: PhoneIcon, defaultText: "Введите номер телефона", defaultTitle: "номер телефона", backendName: "phone" }, - { type: "text", icon: TextIcon, defaultText: "Введите фамилию", defaultTitle: "фамилию", backendName: "adress" }, - { type: "address", icon: AddressIcon, defaultText: "Введите адрес", defaultTitle: "адрес", backendName: "adress" }, -] - export const ContactForm = ({ - currentQuestion, - showResultForm, - setShowContactForm, - setShowResultForm, + currentQuestion, + showResultForm, + setShowContactForm, + setShowResultForm, }: ContactFormProps) => { - const theme = useTheme(); - const { settings, items } = useQuestionsStore() + const theme = useTheme(); + const { settings, items } = useQuestionsStore() - const [ready, setReady] = useState(false) - const [name, setName] = useState("") - const [email, setEmail] = useState("") - const [phone, setPhone] = useState("") - const [text, setText] = useState("") - const [adress, setAdress] = useState("") + const [ready, setReady] = useState(false) + const [name, setName] = useState("") + const [email, setEmail] = useState("") + const [phone, setPhone] = useState("") + const [text, setText] = useState("") + const [adress, setAdress] = useState("") - const fireOnce = useRef(true) - const [fire, setFire] = useState(false) - const isMobile = useMediaQuery(theme.breakpoints.down(850)); + const fireOnce = useRef(true) + const [fire, setFire] = useState(false) + const isMobile = useMediaQuery(theme.breakpoints.down(850)); - const followNextForm = () => { - setShowContactForm(false); - setShowResultForm(true); - }; - const mode = modes; - //@ts-ignore - const resultQuestion: QuizQuestionResult = items.find((question) => { - if (settings?.cfg.haveRoot) { //ветвимся - return ( - question.type === "result" && - //@ts-ignore - question.content.rule.parentId === currentQuestion.content.id - ) - } else {// не ветвимся - return ( - question.type === "result" && - question.content.rule.parentId === "line" - ) + const resultQuestion: QuizQuestionResult = items.find((question): question is QuizQuestionResult => { + if (settings?.cfg.haveRoot) { //ветвимся + return ( + question.type === "result" && + question.content.rule.parentId === currentQuestion.content.id + ); + } else {// не ветвимся + return ( + question.type === "result" && + question.content.rule.parentId === "line" + ); + } + })!; + + const inputHC = async () => { + if (!settings) return; + + const body: Partial> = {}; + + if (name.length > 0) body.name = name; + if (email.length > 0) body.email = email; + if (phone.length > 0) body.phone = phone; + if (text.length > 0) body.text = text; + if (adress.length > 0) body.adress = adress; + + if (Object.keys(body).length > 0) { + try { + await sendFC({ + questionId: resultQuestion?.id, + body: body, + qid: settings.qid + }) + + } catch (e) { + enqueueSnackbar("ответ не был засчитан") + } + + } } - - } - ); - - const inputHC = async () => { - - const body = {} //@ts-ignore - if (name.length > 0) body.name = name - //@ts-ignore - if (email.length > 0) body.email = email - //@ts-ignore - if (phone.length > 0) body.phone = phone - //@ts-ignore - if (text.length > 0) body.text = text - //@ts-ignore - if (adress.length > 0) body.adress = adress - - if (Object.keys(body).length > 0) { - try { - await sendFC({ - questionId: resultQuestion?.id, - body: body, - //@ts-ignore - qid: settings.qid - }) - - } catch (e) { - enqueueSnackbar("ответ не был засчитан") - } + const FCcopy: any = settings?.cfg.formContact.fields || settings?.cfg.formContact; + const filteredFC: any = {}; + for (const i in FCcopy) { + const field = FCcopy[i]; + console.log(filteredFC); + if (field.used) { + filteredFC[i] = field; + } } - } - //@ts-ignore - let FCcopy: any = settings?.cfg.formContact.fields || settings?.cfg.formContact; + const isWide = Object.keys(filteredFC).length > 2; - let filteredFC: any = {} - for (let i in FCcopy) { - let field = FCcopy[i] - console.log(filteredFC) - if (field.used) { - filteredFC[i] = field - } - } - let isWide = Object.keys(filteredFC).length > 2 - console.log(isWide) + if (!settings) throw new Error("settings is null"); + if (!resultQuestion) return - if (!resultQuestion) return - - return ( - - + return ( - - {settings?.cfg.formContact.title || "Заполните форму, чтобы получить результаты теста"} - - - { - settings?.cfg.formContact.desc && - - {settings?.cfg.formContact.desc} - - } - - - - - - - - - - - - { - // resultQuestion && - // settings?.cfg.resultInfo.when === "after" && - ( - - )} + > + + + {settings?.cfg.formContact.title || "Заполните форму, чтобы получить результаты теста"} - - { setReady(target.checked) }} checked={ready} colorIcon={theme.palette.primary.main} /> - - С  - - Положением об обработке персональных данных -  и  - Политикой конфиденциальности -  ознакомлен - + + { + settings?.cfg.formContact.desc && + + {settings?.cfg.formContact.desc} + + } + - - - - - Сделано на PenaQuiz - - - - - - ); + + + + + + + + + + { + // resultQuestion && + // settings?.cfg.resultInfo.when === "after" && + ( + + )} + + + { setReady(target.checked) }} checked={ready} colorIcon={theme.palette.primary.main} /> + + С  + + Положением об обработке персональных данных +  и  + Политикой конфиденциальности +  ознакомлен + + + + + + + Сделано на PenaQuiz + + + + + + ); }; const Inputs = ({ - name, setName, - email, setEmail, - phone, setPhone, - text, setText, - adress, setAdress + name, setName, + email, setEmail, + phone, setPhone, + text, setText, + adress, setAdress }: any) => { - const { settings, items } = useQuestionsStore() + const { settings } = useQuestionsStore() - console.log("______________________EMAIL_REGEXP.test(email)") - console.log(EMAIL_REGEXP.test(email)) - //@ts-ignore - const FC: any = settings?.cfg.formContact.fields || settings?.cfg.formContact + //@ts-ignore + const FC: any = settings?.cfg.formContact.fields || settings?.cfg.formContact - //@ts-ignore - const Name = setName(target.value)} id={name} title={FC["name"].innerText || "Введите имя"} desc={FC["name"].text || "имя"} Icon={NameIcon} /> - //@ts-ignore - const Email = setName(target.value)} id={name} title={FC["name"].innerText || "Введите имя"} desc={FC["name"].text || "имя"} Icon={NameIcon} /> + //@ts-ignore + const Email = setEmail(target.value)} id={email} title={FC["email"].innerText || "Введите Email"} desc={FC["email"].text || "Email"} Icon={EmailIcon} /> - //@ts-ignore - const Phone = setPhone(target.value)} id={phone} title={FC["phone"].innerText || "Введите номер телефона"} desc={FC["phone"].text || "номер телефона"} Icon={PhoneIcon} /> - //@ts-ignore - const Text = setText(target.value)} id={text} title={FC["text"].innerText || "Введите фамилию"} desc={FC["text"].text || "фамилию"} Icon={TextIcon} /> - //@ts-ignore - const Adress = setAdress(target.value)} id={adress} title={FC["address"].innerText || "Введите адрес"} desc={FC["address"].text || "адрес"} Icon={AddressIcon} /> + error = {!EMAIL_REGEXP.test(email)} + label={!EMAIL_REGEXP.test(email) ? "" : "Некорректная почта"} + //@ts-ignore + onChange={({ target }) => setEmail(target.value)} id={email} title={FC["email"].innerText || "Введите Email"} desc={FC["email"].text || "Email"} Icon={EmailIcon} /> + //@ts-ignore + const Phone = setPhone(target.value)} id={phone} title={FC["phone"].innerText || "Введите номер телефона"} desc={FC["phone"].text || "номер телефона"} Icon={PhoneIcon} /> + //@ts-ignore + const Text = setText(target.value)} id={text} title={FC["text"].innerText || "Введите фамилию"} desc={FC["text"].text || "фамилию"} Icon={TextIcon} /> + //@ts-ignore + const Adress = setAdress(target.value)} id={adress} title={FC["address"].innerText || "Введите адрес"} desc={FC["address"].text || "адрес"} Icon={AddressIcon} /> - //@ts-ignore - if (Object.values(FC).some((data) => data.used)) { - return <> - {FC["name"].used ? Name : <>} - {FC["email"].used ? Email : <>} - {FC["phone"].used ? Phone : <>} - {FC["text"].used ? Text : <>} - {FC["address"].used ? Adress : <>} - - } else { - return <> - {Name} - {Email} - {Phone} - - } + //@ts-ignore + if (Object.values(FC).some((data) => data.used)) { + return <> + {FC["name"].used ? Name : <>} + {FC["email"].used ? Email : <>} + {FC["phone"].used ? Phone : <>} + {FC["text"].used ? Text : <>} + {FC["address"].used ? Adress : <>} + + } else { + return <> + {Name} + {Email} + {Phone} + + } } const CustomInput = ({ title, desc, Icon, onChange }: any) => { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); - //@ts-ignore - return - {title} + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); - , - }} - /> - + return ( + + {title} + + , + }} + /> + + ) } diff --git a/src/pages/ViewPublicationPage/Footer.tsx b/src/pages/ViewPublicationPage/Footer.tsx index be92c3d..9dd9f3c 100644 --- a/src/pages/ViewPublicationPage/Footer.tsx +++ b/src/pages/ViewPublicationPage/Footer.tsx @@ -1,322 +1,298 @@ -import { useState, useEffect } from "react"; import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { useCallback, useMemo, useState } from "react"; -import type { AnyTypedQuizQuestion, QuizQuestionBase } from "../../model/questionTypes/shared"; -import { useQuestionsStore } from "@stores/quizData/store"; import { getQuestionById } from "@stores/quizData/actions"; -import { useQuizViewStore } from "@stores/quizView/store"; -import { enqueueSnackbar } from "notistack"; -import { NameplateLogoFQ } from "@icons/NameplateLogoFQ"; -import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark"; -import { modes } from "../../utils/themes/Publication/themePublication"; +import { enqueueSnackbar } from "notistack"; +import type { AnyTypedQuizQuestion, QuizQuestionBase } from "../../model/questionTypes/shared"; + import { checkEmptyData } from "./tools/checkEmptyData"; import type { QuizQuestionResult } from "@model/questionTypes/result"; +import { useQuestionsStore } from "@stores/quizData/store"; +import { useQuizViewStore } from "@stores/quizView/store"; type FooterProps = { - setCurrentQuestion: (step: AnyTypedQuizQuestion) => void; - question: AnyTypedQuizQuestion; - setShowContactForm: (show: boolean) => void; - setShowResultForm: (show: boolean) => void; + setCurrentQuestion: (step: AnyTypedQuizQuestion) => void; + question: AnyTypedQuizQuestion; + setShowContactForm: (show: boolean) => void; + setShowResultForm: (show: boolean) => void; }; export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setShowResultForm }: FooterProps) => { - const theme = useTheme(); + const theme = useTheme(); - const { settings, items } = useQuestionsStore(); - const { answers } = useQuizViewStore(); + const { settings, items } = useQuestionsStore(); + const answers = useQuizViewStore(state => state.answers); - const mode = modes; + const [stepNumber, setStepNumber] = useState(1); - const [stepNumber, setStepNumber] = useState(1); - const [disablePreviousButton, setDisablePreviousButton] = useState(false); - const [disableNextButton, setDisableNextButton] = useState(false); + const isMobileMini = useMediaQuery(theme.breakpoints.down(382)); + const isLinear = !items.some(({ content }) => content.rule.parentId === "root"); - const isMobileMini = useMediaQuery(theme.breakpoints.down(382)); - const linear = !items.find(({ content }) => content.rule.parentId === "root"); + const getNextQuestionId = useCallback(() => { + console.log("Смотрим какой вопрос будет дальше. Что у нас сегодня вкусненького? Щя покажу от какого вопроса мы ищем следующий шаг"); + console.log(question); + console.log("От вот этого /|"); + let readyBeNextQuestion = ""; - useEffect(() => { - // Логика для аргумента disabled у кнопки "Назад" - if (linear) { - const questionIndex = items.findIndex(({ id }) => id === question.id); + //вопрос обязателен, анализируем ответ и условия ветвления + if (answers.length) { + const answer = answers.find(({ questionId }) => questionId === question.id); - const previousQuestion = items[questionIndex - 1]; - if (previousQuestion) { - setDisablePreviousButton(false); - } else { - setDisablePreviousButton(true); - } - } else { - if (question?.content.rule.parentId === "root") { - setDisablePreviousButton(true); - } else { - setDisablePreviousButton(false); - } - } + (question as QuizQuestionBase).content.rule.main.forEach(({ next, rules }) => { + const longerArray = Math.max( + rules[0].answers.length, + answer?.answer && Array.isArray(answer?.answer) ? answer?.answer.length : [answer?.answer].length + ); - // Логика для аргумента disabled у кнопки "Далее" - const answer = answers.find(({ questionId }) => questionId === question.id); + for ( + let i = 0; + i < longerArray; + i++ // Цикл по всем эле­мен­там бОльшего массива + ) { + if (Array.isArray(answer?.answer)) { + if (answer?.answer.find((item) => String(item === rules[0].answers[i]))) { + readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны + } - if ("required" in question.content && question.content.required && answer) { - setDisableNextButton(false); + return; + } - return; - } + if (String(rules[0].answers[i]) === answer?.answer) { + readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны + } + } + }); - if ("required" in question.content && question.content.required && !answer) { - setDisableNextButton(true); + if (readyBeNextQuestion) return readyBeNextQuestion; + } - return; - } + if (!question.required) {//вопрос не обязателен и не нашли совпадений между ответами и условиями ветвления + console.log("вопрос не обязателен ищем дальше"); + const defaultQ = question.content.rule.default; + if (defaultQ.length > 1 && defaultQ !== " ") return defaultQ; + //Вопросы типа страница, ползунок, своё поле для ввода и дата не могут иметь больше 1 ребёнка. Пользователь не может настроить там дефолт + //Кинуть на ребёнка надо даже если там нет дефолта + if ( + (question?.type === "date" || + question?.type === "text" || + question?.type === "number" || + question?.type === "page") && question.content.rule.children.length === 1 + ) return question.content.rule.children[0]; - if (linear) { - return; - } - - const nextQuestionId = getNextQuestionId(); - if (nextQuestionId) { - setDisableNextButton(false); - } else { - const nextQuestion = getQuestionById(question.content.rule.default); - - if (nextQuestion?.type) { - setDisableNextButton(false); - } - } - }, [question, answers]); - - const showResult = (nextQuestion: QuizQuestionResult) => { - const isEmpty = checkEmptyData({ resultData: nextQuestion }) - console.log("isEmpty", isEmpty) - - if (nextQuestion) { - if (nextQuestion && settings?.cfg.resultInfo.showResultForm === "before") { - if (isEmpty) { - setShowContactForm(true); //до+пустая = кидать на ФК - - } else { - setShowResultForm(true); //до+заполнена = показать } - } - if (nextQuestion && settings?.cfg.resultInfo.showResultForm === "after") { - if (isEmpty) { - setShowContactForm(true); //после+пустая + //ничё не нашли, ищем резулт + console.log("ничё не нашли, ищем резулт "); + return items.find(q => { + console.log('q.type === "result"', q.type === "result"); + console.log('q.content.rule.parentId', q.content.rule.parentId); + console.log('question.content.id', question.content.id); + return q.type === "result" && q.content.rule.parentId === question.content.id; + })?.id; + }, [answers, items, question]); + + const isPreviousButtonDisabled = useMemo(() => { + // Логика для аргумента disabled у кнопки "Назад" + if (isLinear) { + const questionIndex = items.findIndex(({ id }) => id === question.id); + + const previousQuestion = items[questionIndex - 1]; + + return previousQuestion ? false : true; } else { - setShowContactForm(true); //после+заполнена = показать ФК - + return question?.content.rule.parentId === "root" ? true : false; } - } - } + }, [items, isLinear, question?.content.rule.parentId, question.id]); - }; + const isNextButtonDisabled = useMemo(() => { + // Логика для аргумента disabled у кнопки "Далее" + const answer = answers.find(({ questionId }) => questionId === question.id); - const getNextQuestionId = () => { - console.log("Смотрим какой вопрос будет дальше. Что у нас сегодня вкусненького? Щя покажу от какого вопроса мы ищем следующий шаг") - console.log(question) - console.log("От вот этого /|") - let readyBeNextQuestion = ""; + if ("required" in question.content && question.content.required && answer) { + return false; + } - //вопрос обязателен, анализируем ответ и условия ветвления - if (answers.length) { - const answer = answers.find(({ questionId }) => questionId === question.id); + if ("required" in question.content && question.content.required && !answer) { + return true; + } + if (isLinear) { + return false; + } - (question as QuizQuestionBase).content.rule.main.forEach(({ next, rules }) => { - let longerArray = Math.max( - rules[0].answers.length, - answer?.answer && Array.isArray(answer?.answer) ? answer?.answer.length : [answer?.answer].length - ); + const nextQuestionId = getNextQuestionId(); + if (nextQuestionId) { + return false; + } else { + const nextQuestion = getQuestionById(question.content.rule.default); - for ( - var i = 0; - i < longerArray; - i++ // Цикл по всем эле­мен­там бОльшего массива - ) { - if (Array.isArray(answer?.answer)) { - if (answer?.answer.find((item) => String(item === rules[0].answers[i]))) { - readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны + if (nextQuestion?.type) { + return false; + } + } + }, [answers, getNextQuestionId, isLinear, question.content, question.id]); + + const showResult = (nextQuestion: QuizQuestionResult) => { + if (!settings) return; + if (!nextQuestion) return; + + const isEmpty = checkEmptyData({ resultData: nextQuestion }); + + if (nextQuestion) { + if (nextQuestion && settings?.cfg.resultInfo.showResultForm === "before") { + if (isEmpty) { + setShowContactForm(true); //до+пустая = кидать на ФК + + } else { + setShowResultForm(true); //до+заполнена = показать + + } + } + if (nextQuestion && settings?.cfg.resultInfo.showResultForm === "after") { + if (isEmpty) { + setShowContactForm(true); //после+пустая + + } else { + setShowContactForm(true); //после+заполнена = показать ФК + + } + } + } + }; + + const followPreviousStep = () => { + if (isLinear) { + setStepNumber(q => q - 1); + + const questionIndex = items.findIndex(({ id }) => id === question.id); + + const previousQuestion = items[questionIndex - 1]; + + if (previousQuestion) { + setCurrentQuestion(previousQuestion); } return; - } - - if (String(rules[0].answers[i]) === answer?.answer) { - readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны - } - } - }); - - if (readyBeNextQuestion) return readyBeNextQuestion; - } - - if (!question.required) {//вопрос не обязателен и не нашли совпадений между ответами и условиями ветвления - console.log("вопрос не обязателен ищем дальше") - const defaultQ = question.content.rule.default - if (defaultQ.length > 1 && defaultQ !== " ") return defaultQ - //Вопросы типа страница, ползунок, своё поле для ввода и дата не могут иметь больше 1 ребёнка. Пользователь не может настроить там дефолт - //Кинуть на ребёнка надо даже если там нет дефолта - if ( - (question?.type === "date" || - question?.type === "text" || - question?.type === "number" || - question?.type === "page") && question.content.rule.children.length === 1 - ) return question.content.rule.children[0] - - - } - //ничё не нашли, ищем резулт - console.log("ничё не нашли, ищем резулт ") - return items.find(q => { - console.log('q.type === "result"', q.type === "result") - console.log('q.content.rule.parentId', q.content.rule.parentId) - //@ts-ignore - console.log('question.content.id', question.content.id) - //@ts-ignore - return q.type === "result" && q.content.rule.parentId === question.content.id - })?.id - - }; - - - const followPreviousStep = () => { - if (linear) { - setStepNumber(q => q - 1) - - const questionIndex = items.findIndex(({ id }) => id === question.id); - - const previousQuestion = items[questionIndex - 1]; - - if (previousQuestion) { - setCurrentQuestion(previousQuestion); - } - - return; - } - - if (question?.content.rule.parentId !== "root") { - const parent = getQuestionById(question?.content.rule.parentId); - if (parent?.type) { - setCurrentQuestion(parent); - } else { - enqueueSnackbar("не могу получить предыдущий вопрос"); - } - } else { - enqueueSnackbar("вы находитесь на первом вопросе"); - } - }; - - const followNextStep = () => { - if (linear) { - setStepNumber(q => q + 1) - - const questionIndex = items.findIndex(({ id }) => id === question.id); - const nextQuestion = items[questionIndex + 1]; - - if (nextQuestion && nextQuestion.type !== "result") { - setCurrentQuestion(nextQuestion); - } else { - //@ts-ignore - showResult(items.find(q => q.content.rule.parentId === "line")); - } - - return; - } - - const nextQuestionId = getNextQuestionId(); - console.log(nextQuestionId) - - if (nextQuestionId) { - const nextQuestion = getQuestionById(nextQuestionId); - console.log(nextQuestion) - - if (nextQuestion?.type && nextQuestion.type === "result") { - showResult(nextQuestion); - } else { - //@ts-ignore - setCurrentQuestion(nextQuestion); - } - } else { - enqueueSnackbar("не могу получить последующий вопрос"); - } - - }; - - return ( - - - {/*{mode[settings.cfg.theme] ? (*/} - {/* */} - {/*):(*/} - {/* */} - {/*)}*/} - {linear && - <> - - Шаг - - {stepNumber} - - Из - - {items.filter(q => q.type !== "result").length} - - - } + if (question?.content.rule.parentId !== "root") { + const parent = getQuestionById(question?.content.rule.parentId); + if (parent?.type) { + setCurrentQuestion(parent); + } else { + enqueueSnackbar("не могу получить предыдущий вопрос"); + } + } else { + enqueueSnackbar("вы находитесь на первом вопросе"); + } + }; + + const followNextStep = () => { + if (isLinear) { + setStepNumber(q => q + 1); + + const questionIndex = items.findIndex(({ id }) => id === question.id); + const nextQuestion = items[questionIndex + 1]; + + if (nextQuestion && nextQuestion.type !== "result") { + setCurrentQuestion(nextQuestion); + } else { + //@ts-ignore + showResult(items.find(q => q.content.rule.parentId === "line")); + } + + return; + } + + const nextQuestionId = getNextQuestionId(); + + if (nextQuestionId) { + const nextQuestion = getQuestionById(nextQuestionId); + + if (nextQuestion?.type && nextQuestion.type === "result") { + showResult(nextQuestion); + } else { + //@ts-ignore + setCurrentQuestion(nextQuestion); + } + } else { + enqueueSnackbar("не могу получить последующий вопрос"); + } + }; + + return ( - {/* Шаг + + {/*{mode[settings.cfg.theme] ? (*/} + {/* */} + {/*):(*/} + {/* */} + {/*)}*/} + {isLinear && + + Шаг + + {stepNumber} + + Из + + {items.filter(q => q.type !== "result").length} + + + } + + + {/* Шаг {stepNumber} */} - {/* */} - {/* Из + {/* */} + {/* Из {questions.length} */} + + + + - - - - - ); - + ); }; diff --git a/src/pages/ViewPublicationPage/Question.tsx b/src/pages/ViewPublicationPage/Question.tsx index 39480c9..03f43ac 100644 --- a/src/pages/ViewPublicationPage/Question.tsx +++ b/src/pages/ViewPublicationPage/Question.tsx @@ -1,141 +1,140 @@ -import { useState, useEffect } from "react"; -import {Box, useMediaQuery, useTheme} from "@mui/material"; +import { Box, useMediaQuery, useTheme } from "@mui/material"; +import { useEffect, useState } from "react"; -import { useQuestionsStore } from "@stores/quizData/store" import { getQuestionById } from "@stores/quizData/actions"; -import { Variant } from "./questions/Variant"; -import { Images } from "./questions/Images"; -import { Varimg } from "./questions/Varimg"; -import { Emoji } from "./questions/Emoji"; -import { Text } from "./questions/Text"; -import { Select } from "./questions/Select"; +import { ContactForm } from "./ContactForm"; +import { Footer } from "./Footer"; +import { ResultForm } from "./ResultForm"; import { Date } from "./questions/Date"; -import { Number } from "./questions/Number"; +import { Emoji } from "./questions/Emoji"; import { File } from "./questions/File"; +import { Images } from "./questions/Images"; +import { Number } from "./questions/Number"; import { Page } from "./questions/Page"; import { Rating } from "./questions/Rating"; -import { Footer } from "./Footer"; -import { ContactForm } from "./ContactForm"; -import { ResultForm } from "./ResultForm"; +import { Select } from "./questions/Select"; +import { Text } from "./questions/Text"; +import { Variant } from "./questions/Variant"; +import { Varimg } from "./questions/Varimg"; -import type { QuestionType } from "@model/questionTypes/shared"; import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; -import {NameplateLogoFQ} from "@icons/NameplateLogoFQ"; -import {NameplateLogoFQDark} from "@icons/NameplateLogoFQDark"; -import {modes} from "../../utils/themes/Publication/themePublication"; +import { NameplateLogoFQ } from "@icons/NameplateLogoFQ"; +import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark"; +import { QuizQuestionResult } from "@model/questionTypes/result"; +import { useQuestionsStore } from "@stores/quizData/store"; +import { notReachable } from "@utils/notReachable"; +import { quizThemes } from "@utils/themes/Publication/themePublication"; -type QuestionProps = { - questions: AnyTypedQuizQuestion[]; -}; +export const Question = () => { + const theme = useTheme(); + const settings = useQuestionsStore(state => state.settings); + const questions = useQuestionsStore(state => state.items); + const [currentQuestion, setCurrentQuestion] = useState(); + const [showContactForm, setShowContactForm] = useState(false); + const [showResultForm, setShowResultForm] = useState(false); + const isMobile = useMediaQuery(theme.breakpoints.down(650)); -const QUESTIONS_MAP: any = { - variant: Variant, - images: Images, - varimg: Varimg, - emoji: Emoji, - text: Text, - select: Select, - date: Date, - number: Number, - file: File, - page: Page, - rating: Rating, -}; + useEffect(() => { -export const Question = ({ questions }: QuestionProps) => { - const { settings } = useQuestionsStore() - const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(650)); - const [currentQuestion, setCurrentQuestion] = useState(); - const [showContactForm, setShowContactForm] = useState(false); - const [showResultForm, setShowResultForm] = useState(false); - const mode = modes; - console.log("currentQuestion ", currentQuestion) - useEffect(() => { + if (settings?.cfg.haveRoot) {//ветвимся + const nextQuestion = getQuestionById(settings?.cfg.haveRoot || ""); - if (settings?.cfg.haveRoot) {//ветвимся - const nextQuestion = getQuestionById(settings?.cfg.haveRoot || ""); + if (nextQuestion?.type) { + setCurrentQuestion(nextQuestion); + return; + } - if (nextQuestion?.type) { - setCurrentQuestion(nextQuestion); - return; - } + } else {//идём прямо + setCurrentQuestion(questions[0]); + } - } else {//идём прямо - setCurrentQuestion(questions[0]); - } + }, []); - }, []); + if (!settings) throw new Error("settings is null"); + if (!currentQuestion || currentQuestion.type === "result") return "не смог отобразить вопрос"; - if (!currentQuestion) return <>не смог отобразить вопрос; - - const QuestionComponent = - QUESTIONS_MAP[currentQuestion.type as Exclude]; - - return ( - - {!showContactForm && !showResultForm && ( + return ( - - {mode[settings?.cfg.theme] ? ( - - ) : ( - - )} + {!showContactForm && !showResultForm && ( + + + {quizThemes[settings.cfg.theme].isLight ? ( + + ) : ( + + )} + + )} + {showResultForm && settings?.cfg.resultInfo.showResultForm === "before" && ( + + )} + {showContactForm && ( + + )} + {showResultForm && settings?.cfg.resultInfo.showResultForm === "after" && ( + + )} + {!showContactForm && !showResultForm && ( +