diff --git a/src/App.tsx b/src/App.tsx index c186a89..1247bea 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,20 +1,22 @@ import { Box } from "@mui/material"; -import { useEffect, useState } from "react"; +import { startTransition, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import QuizAnswerer from "./QuizAnswerer"; import { QuizIdContext } from "./contexts/QuizIdContext"; import { RootContainerWidthContext } from "./contexts/RootContainerWidthContext"; -const defaultQuizId = "ef836ff8-35b1-4031-9acf-af5766bac2b2"; +const defaultQuizId = "45ef7f9c-784d-4e58-badb-f6b337f08ba0"; export default function App() { const quizId = useParams().quizId ?? defaultQuizId; - const [rootContainerSize, setRootContainerSize] = useState(Infinity); + const [rootContainerSize, setRootContainerSize] = useState(() => window.innerWidth); useEffect(() => { const handleWindowResize = () => { - setRootContainerSize(window.innerWidth); + startTransition(() => { + setRootContainerSize(window.innerWidth); + }); }; window.addEventListener("resize", handleWindowResize); diff --git a/src/QuizAnswerer.tsx b/src/QuizAnswerer.tsx index 3d3e935..d39e206 100644 --- a/src/QuizAnswerer.tsx +++ b/src/QuizAnswerer.tsx @@ -2,6 +2,7 @@ import { CssBaseline, ThemeProvider } from "@mui/material"; import { LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment"; import { ruRU } from '@mui/x-date-pickers/locales'; +import ErrorBoundaryFallback from "@ui_kit/ErrorBoundaryFallback"; import LoadingSkeleton from "@ui_kit/LoadingSkeleton"; import { handleComponentError } from "@utils/handleComponentError"; import moment from "moment"; @@ -9,8 +10,7 @@ import { SnackbarProvider } from 'notistack'; import { Suspense } from "react"; import { ErrorBoundary } from "react-error-boundary"; import { SWRConfig } from "swr"; -import { ApologyPage } from "./pages/ViewPublicationPage/ApologyPage"; -import { ViewPage } from "./pages/ViewPublicationPage/ViewPublicationPage"; +import ViewPublicationPage from "./pages/ViewPublicationPage/ViewPublicationPage"; import lightTheme from "./utils/themes/light"; @@ -32,11 +32,11 @@ export default function QuizAnswerer() { > } + FallbackComponent={ErrorBoundaryFallback} onError={handleComponentError} > }> - + diff --git a/src/WidgetApp.tsx b/src/WidgetApp.tsx index 142b00b..f04d727 100644 --- a/src/WidgetApp.tsx +++ b/src/WidgetApp.tsx @@ -1,5 +1,5 @@ import { Box } from "@mui/material"; -import { useEffect, useRef, useState } from "react"; +import { startTransition, useEffect, useRef, useState } from "react"; import QuizAnswerer from "./QuizAnswerer"; import { QuizIdContext } from "./contexts/QuizIdContext"; import { RootContainerWidthContext } from "./contexts/RootContainerWidthContext"; @@ -10,14 +10,14 @@ interface Props { } export default function WidgetApp({ quizId }: Props) { - const [rootContainerSize, setRootContainerSize] = useState(Infinity); + const [rootContainerSize, setRootContainerSize] = useState(() => window.innerWidth); const rootContainerRef = useRef(null); useEffect(() => { const handleWindowResize = () => { - if (!rootContainerRef.current) return; - - setRootContainerSize(rootContainerRef.current.clientWidth); + startTransition(() => { + if (rootContainerRef.current) setRootContainerSize(rootContainerRef.current.clientWidth); + }); }; window.addEventListener("resize", handleWindowResize); diff --git a/src/api/quizRelase.ts b/src/api/quizRelase.ts index 6b4448c..6e63e2d 100644 --- a/src/api/quizRelase.ts +++ b/src/api/quizRelase.ts @@ -6,143 +6,124 @@ import type { AxiosError } from "axios"; let SESSIONS = ""; export const publicationMakeRequest = ({ url, body }: any) => { - console.log(body); - return axios(url, { - //@ts-ignore - data: body, - headers: { - "X-Sessionkey": SESSIONS, - "Content-Type": "multipart/form-data", - }, - method: "POST", - }); + console.log(body); + return axios(url, { + data: body, + headers: { + "X-Sessionkey": SESSIONS, + "Content-Type": "multipart/form-data", + }, + method: "POST", + }); }; export async function getData(quizId: string): Promise<{ - data: GetQuizDataResponse | null; - isRecentlyCompleted: boolean; - error?: string; + data: GetQuizDataResponse | null; + isRecentlyCompleted: boolean; + error?: string; }> { - const QID = - process.env.NODE_ENV === "production" - ? window.location.pathname.replace(/\//g, "") - : "ef836ff8-35b1-4031-9acf-af5766bac2b2"; + try { + const { data, headers } = await axios( + `https://s.hbpn.link/answer/settings`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + data: { + quiz_id: quizId, + limit: 100, + page: 0, + need_config: true, + }, + } + ); + const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); - try { - const { data, headers } = await axios( - `https://s.hbpn.link/answer/settings`, - { - method: "POST", - // headers, - data: JSON.stringify({ - quiz_id: quizId, - limit: 100, - page: 0, - need_config: true, - }), - } - ); - const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); + if (typeof sessions[quizId] === "number") { + // unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше + if (Date.now() - sessions[quizId] < 86400000) { + return { data, isRecentlyCompleted: true }; + } + } - if (typeof sessions[QID] === "number") { - // unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше - if (Date.now() - sessions[QID] < 86400000) { - return { data, isRecentlyCompleted: true }; - } + SESSIONS = headers["x-sessionkey"]; + + return { data, isRecentlyCompleted: false }; + } catch (nativeError) { + const error = nativeError as AxiosError; + + return { data: null, isRecentlyCompleted: false, error: error.message }; } - - SESSIONS = headers["x-sessionkey"]; - - return { data, isRecentlyCompleted: false }; - } catch (nativeError) { - const error = nativeError as AxiosError; - - return { data: null, isRecentlyCompleted: false, error: error.message }; - } } export function sendAnswer({ questionId, body, qid }: any) { - const formData = new FormData(); - console.log(qid); - const answers = [ - { - question_id: questionId, - content: body, //тут массив с ответом - }, - ]; - formData.append("answers", JSON.stringify(answers)); - formData.append("qid", qid); + const formData = new FormData(); + console.log(qid); + const answers = [ + { + question_id: questionId, + content: body, //тут массив с ответом + }, + ]; + formData.append("answers", JSON.stringify(answers)); + formData.append("qid", qid); - return publicationMakeRequest({ - url: `https://s.hbpn.link/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: `https://s.hbpn.link/answer/answer`, + body: formData, + method: "POST", + }); } //body ={file, filename} export function sendFile({ questionId, body, qid }: any) { - console.log(body); - const formData = new FormData(); + console.log(body); + const formData = new FormData(); - const answers: any = [ - { - question_id: questionId, - content: "file:" + body.name, - }, - ]; + const answers: any = [ + { + question_id: questionId, + content: "file:" + body.name, + }, + ]; - formData.append("answers", JSON.stringify(answers)); - formData.append(body.name, body.file); - formData.append("qid", qid); + formData.append("answers", JSON.stringify(answers)); + formData.append(body.name, body.file); + formData.append("qid", qid); - return publicationMakeRequest({ - url: `https://s.hbpn.link/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: `https://s.hbpn.link/answer/answer`, + body: formData, + method: "POST", + }); } -const fields = [ - "name", - "email", - "phone", - "adress", - "telegram", - "wechat", - "viber", - "vk", - "skype", - "whatsup", - "messenger", - "text", -]; - //форма контактов export function sendFC({ questionId, body, qid }: any) { - const formData = new FormData(); + const formData = new FormData(); - // const keysBody = Object.keys(body) - // const content:any = {} - // fields.forEach((key) => { - // if (keysBody.includes(key)) content[key] = body.key - // }) + // const keysBody = Object.keys(body) + // const content:any = {} + // fields.forEach((key) => { + // if (keysBody.includes(key)) content[key] = body.key + // }) - const answers = [ - { - question_id: questionId, - content: JSON.stringify(body), - result: true, - qid, - }, - ]; + const answers = [ + { + question_id: questionId, + content: JSON.stringify(body), + result: true, + qid, + }, + ]; - formData.append("answers", JSON.stringify(answers)); - formData.append("qid", qid); + formData.append("answers", JSON.stringify(answers)); + formData.append("qid", qid); - return publicationMakeRequest({ - url: `https://s.hbpn.link/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: `https://s.hbpn.link/answer/answer`, + body: formData, + method: "POST", + }); } diff --git a/src/model/api/getQuizData.ts b/src/model/api/getQuizData.ts index 96c44fd..b7385eb 100644 --- a/src/model/api/getQuizData.ts +++ b/src/model/api/getQuizData.ts @@ -24,7 +24,7 @@ export interface GetQuizDataResponse { }[]; } -export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: string): Omit { +export function parseQuizData(quizDataResponse: GetQuizDataResponse): Omit { const items: QuizSettings["questions"] = quizDataResponse.items.map((item) => { const content = JSON.parse(item.c); @@ -40,7 +40,6 @@ export function parseQuizData(quizDataResponse: GetQuizDataResponse, quizId: str }); const settings: QuizSettings["settings"] = { - qid: quizId, fp: quizDataResponse.settings.fp, rep: quizDataResponse.settings.rep, name: quizDataResponse.settings.name, diff --git a/src/model/questionTypes/shared.ts b/src/model/questionTypes/shared.ts index 9545bf1..a8fa5b2 100644 --- a/src/model/questionTypes/shared.ts +++ b/src/model/questionTypes/shared.ts @@ -1,3 +1,4 @@ +import { nanoid } from "nanoid"; import type { QuizQuestionDate } from "./date"; import type { QuizQuestionEmoji } from "./emoji"; import type { QuizQuestionFile } from "./file"; @@ -5,24 +6,23 @@ import type { QuizQuestionImages } from "./images"; import type { QuizQuestionNumber } from "./number"; import type { QuizQuestionPage } from "./page"; import type { QuizQuestionRating } from "./rating"; +import type { QuizQuestionResult } from "./result"; import type { QuizQuestionSelect } from "./select"; import type { QuizQuestionText } from "./text"; import type { QuizQuestionVariant } from "./variant"; import type { QuizQuestionVarImg } from "./varimg"; -import type { QuizQuestionResult } from "./result"; -import { nanoid } from "nanoid"; export interface QuestionBranchingRuleMain { - next: string; - or: boolean; - rules: { - question: string; //id родителя (пока что) - answers: string[] - }[] + next: string; + or: boolean; + rules: { + question: string; //id родителя (пока что) + answers: string[]; + }[]; } -export interface QuestionBranchingRule { - children: string[], +export interface QuestionBranchingRule { + children: string[]; //список условий main: QuestionBranchingRuleMain[]; parentId: string | null | "root"; @@ -85,7 +85,6 @@ export interface QuizQuestionBase { }; } - export type AnyTypedQuizQuestion = | QuizQuestionVariant | QuizQuestionImages @@ -100,7 +99,7 @@ export type AnyTypedQuizQuestion = | QuizQuestionRating | QuizQuestionResult; - +export type RealTypedQuizQuestion = Exclude; type FilterQuestionsWithVariants = T extends { content: { variants: QuestionVariant[]; }; @@ -109,18 +108,19 @@ type FilterQuestionsWithVariants = T extends { export type QuizQuestionsWithVariants = FilterQuestionsWithVariants; -export const createBranchingRuleMain: (targetId:string, parentId:string) => QuestionBranchingRuleMain = (targetId, parentId) => ({ +export const createBranchingRuleMain: (targetId: string, parentId: string) => QuestionBranchingRuleMain = (targetId, parentId) => ({ next: targetId, or: false, rules: [{ question: parentId, answers: [] as string[], }] -}) +}); + export const createQuestionVariant: () => QuestionVariant = () => ({ id: nanoid(), answer: "", extendedText: "", hints: "", originalImageUrl: "", -}); +}); diff --git a/src/model/settingsData.ts b/src/model/settingsData.ts index bfbbd1d..06f1e18 100644 --- a/src/model/settingsData.ts +++ b/src/model/settingsData.ts @@ -8,6 +8,8 @@ export type QuizType = "quiz" | "form"; export type QuizResultsType = true | null; +export type QuizStep = "startpage" | "question" | "contactform"; + export type QuizTheme = | "StandardTheme" | "StandardDarkTheme" @@ -32,7 +34,6 @@ export type FCField = { export type QuizSettings = { questions: AnyTypedQuizQuestion[]; settings: { - qid: string; fp: boolean; rep: boolean; name: string; diff --git a/src/pages/ViewPublicationPage/ApologyPage.tsx b/src/pages/ViewPublicationPage/ApologyPage.tsx index ac27537..38b442a 100644 --- a/src/pages/ViewPublicationPage/ApologyPage.tsx +++ b/src/pages/ViewPublicationPage/ApologyPage.tsx @@ -1,22 +1,25 @@ import { Box, Typography } from "@mui/material"; -export const ApologyPage = ({message}:{message: string}) => { +type Props = { + message: string; +}; + +export const ApologyPage = ({ message }: Props) => { + return ( {message || "что-то пошло не так"} + >{message || "Что-то пошло не так"} - - - ) -} \ No newline at end of file + ); +}; diff --git a/src/pages/ViewPublicationPage/ContactForm.tsx b/src/pages/ViewPublicationPage/ContactForm.tsx index 600d248..9b2e05f 100644 --- a/src/pages/ViewPublicationPage/ContactForm.tsx +++ b/src/pages/ViewPublicationPage/ContactForm.tsx @@ -11,69 +11,25 @@ import { FC, useRef, useState } from "react"; import { sendFC } from "@api/quizRelase"; import { NameplateLogo } from "@icons/NameplateLogo"; import { QuizQuestionResult } from "@model/questionTypes/result"; +import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; import { useQuizData } from "@utils/hooks/useQuizData"; import { quizThemes } from "@utils/themes/Publication/themePublication"; import { enqueueSnackbar } from "notistack"; +import { useQuizId } from "../../contexts/QuizIdContext"; import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; -import { ApologyPage } from "./ApologyPage"; -import { checkEmptyData } from "./tools/checkEmptyData"; const TextField = MuiTextField as unknown as FC; // temporary fix ts(2590) const EMAIL_REGEXP = /^(([^<>()[\].,:\s@"]+(\.[^<>()[\].,:\s@"]+)*)|(".+"))@(([^<>()[\].,:\s@"]+\.)+[^<>()[\].,:\s@"]{2,})$/iu; -type ContactFormProps = { - currentQuestion: any; - showResultForm: boolean; - setShowContactForm: (show: boolean) => void; - setShowResultForm: (show: boolean) => void; +type Props = { + currentQuestion: AnyTypedQuizQuestion; + onShowResult: () => 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, -}: ContactFormProps) => { +export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { const theme = useTheme(); + const qid = useQuizId(); const { settings, questions } = useQuizData(); const [ready, setReady] = useState(false); @@ -87,78 +43,103 @@ export const ContactForm = ({ const [fire, setFire] = useState(false); const isMobile = useRootContainerSize() < 850; - const followNextForm = () => { - setShowContactForm(false); - setShowResultForm(true); - }; + const resultQuestion = currentQuestion.type === "result" + ? currentQuestion + : questions.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" + ); + } + }); - //@ts-ignore - const resultQuestion: QuizQuestionResult = questions.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" - ); - } - }); + if (!resultQuestion) throw new Error("Result question not found"); const inputHC = async () => { - //@ts-ignore - const FC = settings?.cfg.formContact.fields || settings?.cfg.formContact; - const body = {}; - //@ts-ignore + const FC = settings.cfg.formContact.fields || settings.cfg.formContact; + const body = {} as any; 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 (adress.length > 0) body.address = adress; - //@ts-ignore if (text.length > 0) body.customs = { [FC.text.text || "Фамилия"]: text }; if (Object.keys(body).length > 0) { try { await sendFC({ - questionId: resultQuestion?.id, + questionId: currentQuestion.id, body: body, - qid: settings.qid, + qid, }); const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); localStorage.setItem( "sessions", - JSON.stringify({ ...sessions, [settings.qid]: new Date().getTime() }) + JSON.stringify({ ...sessions, [qid]: new Date().getTime() }) ); } catch (e) { enqueueSnackbar("ответ не был засчитан"); } } }; - //@ts-ignore - let FCcopy: any = settings?.cfg.formContact.fields || settings?.cfg.formContact; - let filteredFC: any = {}; - for (let i in FCcopy) { - let field = FCcopy[i]; + 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; } } - let isWide = Object.keys(filteredFC).length > 2; + const isWide = Object.keys(filteredFC).length > 2; - if (!resultQuestion) - return ( - - ); + async function handleShowResultsClick() { + //@ts-ignore + const FC: any = settings.cfg.formContact.fields || settings.cfg.formContact; + if (FC["email"].used !== EMAIL_REGEXP.test(email)) { + return enqueueSnackbar("введена некорректная почта"); + } + + //почта валидна + setFire(true); + + if (fireOnce.current) { + if ( + name.length === 0 + && email.length === 0 + && phone.length === 0 + && text.length === 0 + && adress.length === 0 + ) return enqueueSnackbar("Пожалуйста, заполните поля"); + + try { + await inputHC(); + fireOnce.current = false; + const sessions: any = JSON.parse( + localStorage.getItem("sessions") || "{}" + ); + sessions[qid] = Date.now(); + localStorage.setItem( + "sessions", + JSON.stringify(sessions) + ); + enqueueSnackbar("Данные успешно отправлены"); + } catch (e) { + enqueueSnackbar("повторите попытку позже"); + } + + onShowResult(); + } + + setFire(false); + } return ( - {settings?.cfg.formContact.title || + {settings.cfg.formContact.title || "Заполните форму, чтобы получить результаты теста"} - {settings?.cfg.formContact.desc && ( + {settings.cfg.formContact.desc && ( - {settings?.cfg.formContact.desc} + {settings.cfg.formContact.desc} )} @@ -254,64 +235,13 @@ export const ContactForm = ({ { // resultQuestion && - // settings?.cfg.resultInfo.when === "after" && + // settings.cfg.resultInfo.when === "after" && } @@ -391,7 +321,7 @@ const Inputs = ({ const { settings } = useQuizData(); // @ts-ignore - const FC = settings?.cfg.formContact.fields || settings?.cfg.formContact; + const FC = settings.cfg.formContact.fields || settings.cfg.formContact; if (!FC) return null; diff --git a/src/pages/ViewPublicationPage/Footer.tsx b/src/pages/ViewPublicationPage/Footer.tsx index 5b89510..6706e08 100644 --- a/src/pages/ViewPublicationPage/Footer.tsx +++ b/src/pages/ViewPublicationPage/Footer.tsx @@ -1,228 +1,17 @@ -import { Box, Button, Typography, useTheme } from "@mui/material"; -import { useCallback, useMemo, useState } from "react"; +import { Box, Typography, useTheme } from "@mui/material"; +import { ReactNode } from "react"; -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 { useQuizViewStore } from "@stores/quizView/store"; import { useQuizData } from "@utils/hooks/useQuizData"; -import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; type FooterProps = { - setCurrentQuestion: (step: AnyTypedQuizQuestion) => void; - question: AnyTypedQuizQuestion; - setShowContactForm: (show: boolean) => void; - setShowResultForm: (show: boolean) => void; + stepNumber: number | null; + nextButton: ReactNode; + prevButton: ReactNode; }; -export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setShowResultForm }: FooterProps) => { +export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => { const theme = useTheme(); - - const { settings, questions } = useQuizData(); - const answers = useQuizViewStore(state => state.answers); - - const [stepNumber, setStepNumber] = useState(1); - - const isMobileMini = useRootContainerSize() < 382; - const isLinear = !questions.some(({ content }) => content.rule.parentId === "root"); - - const getNextQuestionId = useCallback(() => { - console.log("Смотрим какой вопрос будет дальше. Что у нас сегодня вкусненького? Щя покажу от какого вопроса мы ищем следующий шаг"); - console.log(question); - console.log("От вот этого /|"); - let readyBeNextQuestion = ""; - - //вопрос обязателен, анализируем ответ и условия ветвления - if (answers.length) { - const answer = answers.find(({ questionId }) => questionId === question.id); - - - (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 - ); - - 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; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны - } - - 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 questions.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, questions, question]); - - const isPreviousButtonDisabled = useMemo(() => { - // Логика для аргумента disabled у кнопки "Назад" - if (isLinear) { - const questionIndex = questions.findIndex(({ id }) => id === question.id); - - const previousQuestion = questions[questionIndex - 1]; - - return previousQuestion ? false : true; - } else { - return question?.content.rule.parentId === "root" ? true : false; - } - }, [questions, isLinear, question?.content.rule.parentId, question.id]); - - const isNextButtonDisabled = useMemo(() => { - // Логика для аргумента disabled у кнопки "Далее" - const answer = answers.find(({ questionId }) => questionId === question.id); - - if ("required" in question.content && question.content.required && answer) { - return false; - } - - if ("required" in question.content && question.content.required && !answer) { - return true; - } - - if (isLinear) { - return false; - } - - const nextQuestionId = getNextQuestionId(); - if (nextQuestionId) { - return false; - } else { - const questionId = question.content.rule.default; - const nextQuestion = questions.find(q => q.id === questionId || q.content.id === questionId) || null; - - if (nextQuestion?.type) { - return false; - } - } - }, [answers, getNextQuestionId, isLinear, question.content, question.id]); - - const showResult = (nextQuestion: QuizQuestionResult) => { - 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 = questions.findIndex(({ id }) => id === question.id); - - const previousQuestion = questions[questionIndex - 1]; - - if (previousQuestion) { - setCurrentQuestion(previousQuestion); - } - - return; - } - - if (question?.content.rule.parentId !== "root") { - const questionId = question?.content.rule.parentId; - const parent = questions.find(q => q.id === questionId || q.content.id === questionId) || null; - if (parent?.type) { - setCurrentQuestion(parent); - } else { - enqueueSnackbar("не могу получить предыдущий вопрос"); - } - } else { - enqueueSnackbar("вы находитесь на первом вопросе"); - } - }; - - const followNextStep = () => { - if (isLinear) { - setStepNumber(q => q + 1); - - const questionIndex = questions.findIndex(({ id }) => id === question.id); - const nextQuestion = questions[questionIndex + 1]; - - if (nextQuestion && nextQuestion.type !== "result") { - setCurrentQuestion(nextQuestion); - } else { - //@ts-ignore - showResult(questions.find(q => q.content.rule.parentId === "line")); - } - - return; - } - - const nextQuestionId = getNextQuestionId(); - - if (nextQuestionId) { - const nextQuestion = questions.find(q => q.id === nextQuestionId || q.content.id === nextQuestionId) || null; - - if (nextQuestion?.type && nextQuestion.type === "result") { - showResult(nextQuestion); - } else { - //@ts-ignore - setCurrentQuestion(nextQuestion); - } - } else { - enqueueSnackbar("не могу получить последующий вопрос"); - } - }; + const { questions } = useQuizData(); return ( */} {/*)}*/} - {isLinear && + {stepNumber !== null && */} - - + {prevButton} + {nextButton} ); diff --git a/src/pages/ViewPublicationPage/Question.tsx b/src/pages/ViewPublicationPage/Question.tsx index 2d42501..9d73360 100644 --- a/src/pages/ViewPublicationPage/Question.tsx +++ b/src/pages/ViewPublicationPage/Question.tsx @@ -1,9 +1,6 @@ import { Box, useTheme } from "@mui/material"; -import { useEffect, useState } from "react"; -import { ContactForm } from "./ContactForm"; import { Footer } from "./Footer"; -import { ResultForm } from "./ResultForm"; import { Date } from "./questions/Date"; import { Emoji } from "./questions/Emoji"; import { File } from "./questions/File"; @@ -16,108 +13,67 @@ import { Text } from "./questions/Text"; import { Variant } from "./questions/Variant"; import { Varimg } from "./questions/Varimg"; -import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; +import type { RealTypedQuizQuestion } from "../../model/questionTypes/shared"; import { NameplateLogoFQ } from "@icons/NameplateLogoFQ"; import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark"; -import { QuizQuestionResult } from "@model/questionTypes/result"; import { useQuizData } from "@utils/hooks/useQuizData"; import { notReachable } from "@utils/notReachable"; import { quizThemes } from "@utils/themes/Publication/themePublication"; +import { ReactNode } from "react"; import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; -export const Question = () => { +type Props = { + currentQuestion: RealTypedQuizQuestion; + currentQuestionStepNumber: number | null; + nextButton: ReactNode; + prevButton: ReactNode; +}; + +export const Question = ({ + currentQuestion, + currentQuestionStepNumber, + nextButton, + prevButton, +}: Props) => { const theme = useTheme(); - const { settings, questions } = useQuizData(); - const [currentQuestion, setCurrentQuestion] = useState(); - const [showContactForm, setShowContactForm] = useState(false); - const [showResultForm, setShowResultForm] = useState(false); + const { settings } = useQuizData(); const isMobile = useRootContainerSize() < 650; - useEffect(() => { - if (settings?.cfg.haveRoot) {//ветвимся - const questionId = settings?.cfg.haveRoot || ""; - const nextQuestion = questions.find(q => q.id === questionId || q.content.id === questionId) || null; - - if (nextQuestion?.type) { - setCurrentQuestion(nextQuestion); - return; - } - - } else {//идём прямо - setCurrentQuestion(questions[0]); - } - }, []); - - if (!currentQuestion || currentQuestion.type === "result") return "не смог отобразить вопрос"; - return ( - - {!showContactForm && !showResultForm && ( - - - {quizThemes[settings.cfg.theme].isLight ? ( - - ) : ( - - )} - - )} - {showResultForm && settings?.cfg.resultInfo.showResultForm === "before" && ( - - )} - {showContactForm && ( - - )} - {showResultForm && settings?.cfg.resultInfo.showResultForm === "after" && ( - - )} - {!showContactForm && !showResultForm && ( -