From 8e6852f38c1a944340ed94ac1ecf5dbc011cb839 Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 5 Dec 2023 10:34:41 +0300 Subject: [PATCH 1/7] feat: question analysis in view --- src/pages/ViewPublicationPage/Footer.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/ViewPublicationPage/Footer.tsx b/src/pages/ViewPublicationPage/Footer.tsx index 2e11952d..5b46c9c1 100644 --- a/src/pages/ViewPublicationPage/Footer.tsx +++ b/src/pages/ViewPublicationPage/Footer.tsx @@ -37,13 +37,16 @@ export const Footer = ({ setCurrentQuestion, question }: FooterProps) => { const followNextStep = () => { if (answers.length) { + const answer = answers.find( + ({ questionId }) => questionId === question.content.id + ); + let readyBeNextQuestion = ""; question.content.rule.main.forEach(({ next, rules }) => { - let longerArray = Math.max( rules[0].answers.length, - [answers.at(-1)?.answer].length + [answer?.answer].length ); for ( @@ -51,13 +54,12 @@ export const Footer = ({ setCurrentQuestion, question }: FooterProps) => { i < longerArray; i++ // Цикл по всем эле­мен­там бОльшего массива ) { - if (rules[0].answers[i] === answers.at(-1)?.answer) { + if (rules[0].answers[i] === answer?.answer) { readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны } } }); if (readyBeNextQuestion) { - const nextQuestion = getQuestionByContentId(readyBeNextQuestion); if (nextQuestion) { From ac31202af6f5b7caafa376c228a1da43648bfb3e Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Tue, 5 Dec 2023 13:24:31 +0300 Subject: [PATCH 2/7] feat: View footer buttons disabled logic --- src/pages/ViewPublicationPage/Footer.tsx | 86 ++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/src/pages/ViewPublicationPage/Footer.tsx b/src/pages/ViewPublicationPage/Footer.tsx index 5b46c9c1..4a7c1e70 100644 --- a/src/pages/ViewPublicationPage/Footer.tsx +++ b/src/pages/ViewPublicationPage/Footer.tsx @@ -19,19 +19,95 @@ export const Footer = ({ setCurrentQuestion, question }: FooterProps) => { const [disabledQuestionsId, setDisabledQuestionsId] = useState>( new Set() ); + const [disablePreviousButton, setDisablePreviousButton] = + useState(false); + const [disableNextButton, setDisableNextButton] = useState(false); const { answers } = useQuizViewStore(); const theme = useTheme(); + useEffect(() => { + // Логика для аргумента disabled у кнопки "Назад" + if (question?.content.rule.parentId === "root") { + setDisablePreviousButton(true); + } else { + setDisablePreviousButton(false); + } + + // Логика для аргумента disabled у кнопки "Далее" + const nextQuestionId = getNextQuestionId(); + if (nextQuestionId) { + setDisableNextButton(false); + } else { + const nextQuestion = getQuestionByContentId( + question.content.rule.default + ); + if (nextQuestion?.type) { + setDisableNextButton(false); + } else { + setDisableNextButton(true); + } + } + }, [question]); + + const getNextQuestionId = () => { + if (answers.length) { + const answer = answers.find( + ({ questionId }) => questionId === question.content.id + ); + + let readyBeNextQuestion = ""; + + question.content.rule.main.forEach(({ next, rules }) => { + let longerArray = Math.max( + rules[0].answers.length, + [answer?.answer].length + ); + + for ( + var i = 0; + i < longerArray; + i++ // Цикл по всем эле­мен­там бОльшего массива + ) { + if (rules[0].answers[i] === answer?.answer) { + readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны + } + } + }); + + return readyBeNextQuestion; + } + }; + const followPreviousStep = () => { if (question?.content.rule.parentId !== "root") { const parent = getQuestionByContentId(question?.content.rule.parentId); - if (parent) { + if (parent?.type) { setCurrentQuestion(parent); } else { enqueueSnackbar("не могу получить предыдущий вопрос"); } + } + + const nextQuestionId = getNextQuestionId(); + + if (nextQuestionId) { + const nextQuestion = getQuestionByContentId(nextQuestionId); + + if (nextQuestion?.type) { + setCurrentQuestion(nextQuestion); + return; + } else { + enqueueSnackbar("не могу получить последующий вопрос"); + } } else { - enqueueSnackbar("вы находитесь на первом вопросе"); + const nextQuestion = getQuestionByContentId( + question.content.rule.default + ); + if (nextQuestion?.type) { + setCurrentQuestion(nextQuestion); + } else { + enqueueSnackbar("не могу получить последующий вопрос (дефолтный)"); + } } }; @@ -62,7 +138,7 @@ export const Footer = ({ setCurrentQuestion, question }: FooterProps) => { if (readyBeNextQuestion) { const nextQuestion = getQuestionByContentId(readyBeNextQuestion); - if (nextQuestion) { + if (nextQuestion?.type) { setCurrentQuestion(nextQuestion); return; } else { @@ -72,7 +148,7 @@ export const Footer = ({ setCurrentQuestion, question }: FooterProps) => { const nextQuestion = getQuestionByContentId( question.content.rule.default ); - if (nextQuestion) { + if (nextQuestion?.type) { setCurrentQuestion(nextQuestion); } else { enqueueSnackbar("не могу получить последующий вопрос (дефолтный)"); @@ -130,6 +206,7 @@ export const Footer = ({ setCurrentQuestion, question }: FooterProps) => { */} { openBranchingPanel === true && } diff --git a/src/pages/Questions/ButtonsOptions.tsx b/src/pages/Questions/ButtonsOptions.tsx index 3a0fa529..929372d2 100644 --- a/src/pages/Questions/ButtonsOptions.tsx +++ b/src/pages/Questions/ButtonsOptions.tsx @@ -56,8 +56,7 @@ const {openBranchingPanel} = useQuestionsStore.getState() return enqueueSnackbar("У корня нет условий ветвления") } if (parentId.length !== 0) { - updateOpenBranchingPanel(!value) - openedModal() + updateOpenBranchingPanel(value) } } diff --git a/src/pages/Questions/ButtonsOptionsAndPict.tsx b/src/pages/Questions/ButtonsOptionsAndPict.tsx index 5ade8e55..6e7ec85a 100644 --- a/src/pages/Questions/ButtonsOptionsAndPict.tsx +++ b/src/pages/Questions/ButtonsOptionsAndPict.tsx @@ -65,8 +65,7 @@ export default function ButtonsOptionsAndPict({ return enqueueSnackbar("У корня нет условий ветвления") } if (parentId.length !== 0) { - updateOpenBranchingPanel(!value) - updateOpenedModalSettingsId(question.id) + updateOpenBranchingPanel(value) } } diff --git a/src/pages/Questions/QuestionsPage.tsx b/src/pages/Questions/QuestionsPage.tsx index e08e222c..cd9913d4 100755 --- a/src/pages/Questions/QuestionsPage.tsx +++ b/src/pages/Questions/QuestionsPage.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useLayoutEffect } from "react" +import { useState, useEffect, useLayoutEffect, useRef } from "react" import { Box, Button, @@ -30,12 +30,14 @@ export default function QuestionsPage() { updateOpenBranchingPanel(true) },[]) + const ref = useRef() if (!quiz) return null; return ( <> + + { openBranchingPanel === true && } diff --git a/src/pages/startPage/StartPage.tsx b/src/pages/startPage/StartPage.tsx index 75031c12..253ddd46 100755 --- a/src/pages/startPage/StartPage.tsx +++ b/src/pages/startPage/StartPage.tsx @@ -9,8 +9,8 @@ import { Button, Container, FormControl, - IconButton, - TextField, + IconButton, Switch, + TextField, Typography, useMediaQuery, useTheme, } from "@mui/material"; @@ -29,7 +29,9 @@ import { useEffect, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import useSWR from "swr"; import { SidebarMobile } from "./Sidebar/SidebarMobile"; -import { cleanQuestions } from "@root/questions/actions"; +import {cleanQuestions, updateOpenBranchingPanel} from "@root/questions/actions"; +import {BranchingPanel} from "../Questions/BranchingPanel"; +import {useQuestionsStore} from "@root/questions/store"; export default function StartPage() { @@ -50,7 +52,7 @@ export default function StartPage() { const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isMobile = useMediaQuery(theme.breakpoints.down(660)); const [mobileSidebar, setMobileSidebar] = useState(false); - + const {openBranchingPanel} = useQuestionsStore.getState() const quizConfig = quiz?.config; useEffect(() => { @@ -228,9 +230,9 @@ export default function StartPage() { + + { + updateOpenBranchingPanel(value) + }} + sx={{ + width: 50, + height: 30, + padding: 0, + "& .MuiSwitch-switchBase": { + padding: 0, + margin: "2px", + transitionDuration: "300ms", + "&.Mui-checked": { + transform: "translateX(20px)", + color: theme.palette.brightPurple.main, + "& + .MuiSwitch-track": { + backgroundColor: "#E8DCF9", + opacity: 1, + border: 0, + }, + "&.Mui-disabled + .MuiSwitch-track": { opacity: 0.5 }, + }, + "&.Mui-disabled .MuiSwitch-thumb": { + color: + theme.palette.mode === "light" + ? theme.palette.grey[100] + : theme.palette.grey[600], + }, + "&.Mui-disabled + .MuiSwitch-track": { + opacity: theme.palette.mode === "light" ? 0.7 : 0.3, + }, + }, + "& .MuiSwitch-thumb": { + boxSizing: "border-box", + width: 25, + height: 25, + }, + "& .MuiSwitch-track": { + borderRadius: 13, + backgroundColor: + theme.palette.mode === "light" ? "#E9E9EA" : "#39393D", + opacity: 1, + transition: theme.transitions.create(["background-color"], { + duration: 500, + }), + }, + }} + /> + + + Логика ветвления + + + Настройте связи между вопросами + + + + - - + + ))} diff --git a/src/pages/Questions/SwitchBranchingPanel/index.tsx b/src/pages/Questions/SwitchBranchingPanel/index.tsx new file mode 100644 index 00000000..3191dad0 --- /dev/null +++ b/src/pages/Questions/SwitchBranchingPanel/index.tsx @@ -0,0 +1,91 @@ +import {Box, Typography, Switch, useTheme, Button, useMediaQuery} from "@mui/material"; + +import { QuestionsList } from "./QuestionsList"; +import { updateOpenBranchingPanel } from "@root/questions/actions"; +import {useQuestionsStore} from "@root/questions/store"; +import {useRef} from "react"; + + +export const SwitchBranchingPanel = () => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down(660)); + const {openBranchingPanel} = useQuestionsStore.getState() + console.log(openBranchingPanel) + const ref = useRef() + return ( + + + { + console.log("меняю на " + value) + updateOpenBranchingPanel(value) + }} + sx={{ + width: 50, + height: 30, + padding: 0, + "& .MuiSwitch-switchBase": { + padding: 0, + margin: "2px", + transitionDuration: "300ms", + "&.Mui-checked": { + transform: "translateX(20px)", + color: theme.palette.brightPurple.main, + "& + .MuiSwitch-track": { + backgroundColor: "#E8DCF9", + opacity: 1, + border: 0, + }, + "&.Mui-disabled + .MuiSwitch-track": { opacity: 0.5 }, + }, + "&.Mui-disabled .MuiSwitch-thumb": { + color: + theme.palette.mode === "light" + ? theme.palette.grey[100] + : theme.palette.grey[600], + }, + "&.Mui-disabled + .MuiSwitch-track": { + opacity: theme.palette.mode === "light" ? 0.7 : 0.3, + }, + }, + "& .MuiSwitch-thumb": { + boxSizing: "border-box", + width: 25, + height: 25, + }, + "& .MuiSwitch-track": { + borderRadius: 13, + backgroundColor: + theme.palette.mode === "light" ? "#E9E9EA" : "#39393D", + opacity: 1, + transition: theme.transitions.create(["background-color"], { + duration: 500, + }), + }, + }} + /> + + + Логика ветвления + + + Настройте связи между вопросами + + + + + { openBranchingPanel && } + + ); +}; diff --git a/src/stores/questions/actions.ts b/src/stores/questions/actions.ts index c552ac83..6d8c0b13 100644 --- a/src/stores/questions/actions.ts +++ b/src/stores/questions/actions.ts @@ -308,8 +308,8 @@ export const createTypedQuestion = async ( }); export const deleteQuestion = async (questionId: string, quizId: string) => requestQueue.enqueue(async () => { - - const question = useQuestionsStore.getState().questions.find(q => q.id === questionId); + const { questions } = useQuestionsStore.getState() + const question = questions.find(q => q.id === questionId); if (!question) return; if (question.type === null) { @@ -322,6 +322,27 @@ export const deleteQuestion = async (questionId: string, quizId: string) => requ if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам updateRootContentId(quizId, "") clearRoleForAll() + } else if (question.content.rule.parentId.length > 0) { //удалить из стора вопрос из дерева и очищаем его потомков + const clearQuestions = [] as string[] + + const getChildren = (parentQuestion: AnyTypedQuizQuestion) => { + questions.forEach((targetQuestion) => { + if (targetQuestion.content.rule.parentId === parentQuestion.content.id) {//если у вопроса совпал родитель с родителем => он потомок, в кучу его + if (!clearQuestions.includes(targetQuestion.content.id)) clearQuestions.push(targetQuestion.content.id) + getChildren(targetQuestion) //и ищем его потомков + } + }) + } + getChildren(question) + //чистим потомков от инфы ветвления + clearQuestions.forEach((id) => { + updateQuestion(id, question => { + question.content.rule.parentId = "" + question.content.rule.main = [] + question.content.rule.default = "" + }) + }) + } removeQuestion(questionId); @@ -394,7 +415,7 @@ export const getQuestionByContentId = (questionContentId: string | null) => { if (questionContentId === null) return null; return useQuestionsStore.getState().questions.find(q => { if (q.type === null) return false; - + return (q.content.id === questionContentId); }) || null; }; @@ -417,4 +438,20 @@ export const clearRoleForAll = () => { }); } -export const updateOpenBranchingPanel = (value: boolean) => useQuestionsStore.setState({openBranchingPanel: !value}); +export const updateOpenBranchingPanel = (value: boolean) => useQuestionsStore.setState({ openBranchingPanel: value }); + + +let UDTOABM: ReturnType; +export const updateDesireToOpenABranchingModal = (contentId: string) => { + useQuestionsStore.setState({ desireToOpenABranchingModal: contentId }) + clearTimeout(UDTOABM) + UDTOABM = setTimeout(() => { + useQuestionsStore.setState({ desireToOpenABranchingModal: null }) + }, 7000) +} +export const clearDesireToOpenABranchingModal = () => { + useQuestionsStore.setState({ desireToOpenABranchingModal: null }) +} +export const updateEditSomeQuestion = (contentId?: string) => { + useQuestionsStore.setState({ editSomeQuestion: contentId === undefined ? null : contentId }) +} \ No newline at end of file diff --git a/src/stores/questions/store.ts b/src/stores/questions/store.ts index c669606d..7ea73ee8 100644 --- a/src/stores/questions/store.ts +++ b/src/stores/questions/store.ts @@ -8,6 +8,8 @@ export type QuestionsStore = { openedModalSettingsId: string | null; dragQuestionContentId: string | null; openBranchingPanel: boolean; + desireToOpenABranchingModal: string | null; + editSomeQuestion: string | null; }; const initialState: QuestionsStore = { @@ -15,6 +17,9 @@ const initialState: QuestionsStore = { openedModalSettingsId: null as null, dragQuestionContentId: null, openBranchingPanel: false, + desireToOpenABranchingModal: null as null, + editSomeQuestion: null as null, + }; export const useQuestionsStore = create()(