import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; import { setCurrentQuizStep, useQuizViewStore } from "@stores/quizView/store"; import { useCallback, useDebugValue, useMemo, useState } from "react"; import { isResultQuestionEmpty } from "../../pages/ViewPublicationPage/tools/checkEmptyData"; import { useQuizData } from "./useQuizData"; import moment from "moment"; export function useQuestionFlowControl() { const { settings, questions } = useQuizData(); const [currentQuestion, setCurrentQuestion] = useState(getFirstQuestion); const answers = useQuizViewStore(state => state.answers); const linearQuestionIndex = questions.every(({ content }) => content.rule.parentId !== "root") // null when branching enabled ? questions.indexOf(currentQuestion) : null; function getFirstQuestion() { if (questions.length === 0) throw new Error("No questions found"); if (settings.cfg.haveRoot) { const nextQuestion = questions.find( question => question.id === settings.cfg.haveRoot || question.content.id === settings.cfg.haveRoot ); if (!nextQuestion) throw new Error("Root question not found"); return nextQuestion; } return questions[0]; } const nextQuestionId = useMemo(() => { const questionAnswer = answers.find(({ questionId }) => questionId === currentQuestion.id); if (questionAnswer && !moment.isMoment(questionAnswer.answer)) { const userAnswers = Array.isArray(questionAnswer.answer) ? questionAnswer.answer : [questionAnswer.answer]; for (const branchingRule of currentQuestion.content.rule.main) { if (userAnswers.some(answer => branchingRule.rules[0].answers.includes(answer))) { return branchingRule.next; } } } if (!currentQuestion.required) {//вопрос не обязателен и не нашли совпадений между ответами и условиями ветвления const defaultNextQuestionId = currentQuestion.content.rule.default; if (defaultNextQuestionId.length > 1 && defaultNextQuestionId !== " ") return defaultNextQuestionId; //Вопросы типа страница, ползунок, своё поле для ввода и дата не могут иметь больше 1 ребёнка. Пользователь не может настроить там дефолт //Кинуть на ребёнка надо даже если там нет дефолта if ( ["date", "page", "text", "number"].includes(currentQuestion.type) && currentQuestion.content.rule.children.length === 1 ) return currentQuestion.content.rule.children[0]; } //ничё не нашли, ищем резулт return questions.find(q => { return q.type === "result" && q.content.rule.parentId === currentQuestion.content.id; })?.id; }, [answers, currentQuestion, questions]); const prevQuestion = linearQuestionIndex !== null ? questions[linearQuestionIndex - 1] : questions.find(q => q.id === currentQuestion.content.rule.parentId || q.content.id === currentQuestion.content.rule.parentId ); const nextQuestion = linearQuestionIndex !== null ? questions[linearQuestionIndex + 1] ?? questions.find(question => question.type === "result" && question.content.rule.parentId === "line" ) : questions.find(q => q.id === nextQuestionId || q.content.id === nextQuestionId); const showResult = useCallback(() => { if (nextQuestion?.type !== "result") throw new Error("Current question is not result"); if (isResultQuestionEmpty(nextQuestion)) { setCurrentQuizStep("contactform"); return; } setCurrentQuestion(nextQuestion); if (settings.cfg.resultInfo.showResultForm === "after") setCurrentQuizStep("contactform"); }, [nextQuestion, settings.cfg.resultInfo.showResultForm]); const showResultAfterContactForm = useCallback(() => { if (currentQuestion.type !== "result") throw new Error("Current question is not result"); if (isResultQuestionEmpty(currentQuestion)) { console.warn("Result question is empty"); return; } setCurrentQuizStep("question"); }, [currentQuestion]); const moveToPrevQuestion = useCallback(() => { if (!prevQuestion) throw new Error("Previous question not found"); setCurrentQuestion(prevQuestion); }, [prevQuestion]); const moveToNextQuestion = useCallback(() => { if (!nextQuestion) throw new Error("Next question not found"); if (nextQuestion.type === "result") return showResult(); setCurrentQuestion(nextQuestion); }, [nextQuestion, showResult]); const isPreviousButtonEnabled = Boolean(prevQuestion); const isNextButtonEnabled = useMemo(() => { const hasAnswer = answers.some(({ questionId }) => questionId === currentQuestion.id); if ("required" in currentQuestion.content && currentQuestion.content.required) { return hasAnswer; } return Boolean(nextQuestion); }, [answers, currentQuestion.content, currentQuestion.id, nextQuestion]); useDebugValue({ linearQuestionIndex, currentQuestionTitle: currentQuestion.title, prevQuestionTitle: prevQuestion?.title, nextQuestionTitle: nextQuestion?.title, }); return { currentQuestion, currentQuestionStepNumber: linearQuestionIndex === null ? null : linearQuestionIndex + 1, isNextButtonEnabled, isPreviousButtonEnabled, moveToPrevQuestion, moveToNextQuestion, showResultAfterContactForm, }; }