Merge branch 'view-fixes' into backend-integration
This commit is contained in:
commit
6482598841
@ -18,6 +18,7 @@ export const QUIZ_QUESTION_BASE: Omit<QuizQuestionBase, "id" | "backendId"> = {
|
|||||||
video: "",
|
video: "",
|
||||||
},
|
},
|
||||||
rule: {
|
rule: {
|
||||||
|
children: [],
|
||||||
main: [] as QuestionBranchingRuleMain[],
|
main: [] as QuestionBranchingRuleMain[],
|
||||||
parentId: "",
|
parentId: "",
|
||||||
default: ""
|
default: ""
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export interface QuestionBranchingRuleMain {
|
|||||||
}
|
}
|
||||||
export interface QuestionBranchingRule {
|
export interface QuestionBranchingRule {
|
||||||
|
|
||||||
|
children: string[],
|
||||||
//список условий
|
//список условий
|
||||||
main: QuestionBranchingRuleMain[];
|
main: QuestionBranchingRuleMain[];
|
||||||
parentId: string | null | "root";
|
parentId: string | null | "root";
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||||
import Cytoscape from "cytoscape";
|
import Cytoscape from "cytoscape";
|
||||||
|
import { Button } from "@mui/material";
|
||||||
import CytoscapeComponent from "react-cytoscapejs";
|
import CytoscapeComponent from "react-cytoscapejs";
|
||||||
import popper from "cytoscape-popper";
|
import popper from "cytoscape-popper";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { updateRootContentId } from "@root/quizes/actions"
|
import { updateRootContentId } from "@root/quizes/actions"
|
||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { cleardragQuestionContentId, updateQuestion, updateOpenedModalSettingsId, getQuestionByContentId, clearRuleForAll } from "@root/questions/actions";
|
import { deleteQuestion, cleardragQuestionContentId, updateQuestion, updateOpenedModalSettingsId, getQuestionByContentId, clearRuleForAll } from "@root/questions/actions";
|
||||||
import { withErrorBoundary } from "react-error-boundary";
|
import { withErrorBoundary } from "react-error-boundary";
|
||||||
|
|
||||||
import { storeToNodes } from "./helper";
|
import { storeToNodes } from "./helper";
|
||||||
@ -111,13 +112,13 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function CsComponent ({
|
function CsComponent({
|
||||||
modalQuestionParentContentId,
|
modalQuestionParentContentId,
|
||||||
modalQuestionTargetContentId,
|
modalQuestionTargetContentId,
|
||||||
setOpenedModalQuestions,
|
setOpenedModalQuestions,
|
||||||
setModalQuestionParentContentId,
|
setModalQuestionParentContentId,
|
||||||
setModalQuestionTargetContentId
|
setModalQuestionTargetContentId
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
const { dragQuestionContentId, desireToOpenABranchingModal } = useQuestionsStore()
|
const { dragQuestionContentId, desireToOpenABranchingModal } = useQuestionsStore()
|
||||||
@ -186,18 +187,32 @@ function CsComponent ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const clearDataAfterAddNode = ({ parentNodeContentId, targetQuestion, parentNodeChildren }: { parentNodeContentId: string, targetQuestion: AnyTypedQuizQuestion, parentNodeChildren: number }) => {
|
const clearDataAfterAddNode = ({ parentNodeContentId, targetQuestion, parentNodeChildren }: { parentNodeContentId: string, targetQuestion: AnyTypedQuizQuestion, parentNodeChildren: number }) => {
|
||||||
|
|
||||||
|
const parentQuestion = { ...getQuestionByContentId(parentNodeContentId) } as AnyTypedQuizQuestion
|
||||||
|
|
||||||
|
|
||||||
|
//смотрим не добавлен ли родителю result. Если да - убираем его. Веточкам result не нужен
|
||||||
|
trashQuestions.forEach((targetQuestion) => {
|
||||||
|
if (targetQuestion.type === "result" && targetQuestion.content.rule.parentId === parentQuestion.content.id) {
|
||||||
|
deleteQuestion(targetQuestion.id);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
//предупреждаем добавленный вопрос о том, кто его родитель
|
//предупреждаем добавленный вопрос о том, кто его родитель
|
||||||
updateQuestion(targetQuestion.content.id, question => {
|
updateQuestion(targetQuestion.content.id, question => {
|
||||||
question.content.rule.parentId = parentNodeContentId
|
question.content.rule.parentId = parentNodeContentId
|
||||||
question.content.rule.main = []
|
question.content.rule.main = []
|
||||||
})
|
})
|
||||||
|
//предупреждаем родителя о новом потомке (если он ещё не знает о нём)
|
||||||
|
if (!parentQuestion.content.rule.children.includes(targetQuestion.content.id)) updateQuestion(parentNodeContentId, question => {
|
||||||
|
question.content.rule.children = [...question.content.rule.children, targetQuestion.content.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
//Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления
|
//Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления
|
||||||
if (parentNodeChildren >= 1) {
|
if (parentQuestion.content.rule.children >= 1) {
|
||||||
updateOpenedModalSettingsId(targetQuestion.content.id)
|
updateOpenedModalSettingsId(targetQuestion.content.id)
|
||||||
} else {
|
|
||||||
//Если ребёнок первый - добавляем его родителю как дефолтный
|
|
||||||
updateQuestion(parentNodeContentId, question => question.content.rule.default = targetQuestion.content.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,10 +246,18 @@ function CsComponent ({
|
|||||||
updateQuestion(targetNodeContentId, question => {
|
updateQuestion(targetNodeContentId, question => {
|
||||||
question.content.rule.parentId = ""
|
question.content.rule.parentId = ""
|
||||||
question.content.rule.main = []
|
question.content.rule.main = []
|
||||||
|
question.content.rule.children = []
|
||||||
question.content.rule.default = ""
|
question.content.rule.default = ""
|
||||||
})
|
})
|
||||||
|
trashQuestions.forEach(q => {
|
||||||
|
if (q.type === "result") {
|
||||||
|
deleteQuestion(q.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
clearRuleForAll()
|
clearRuleForAll()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
const parentQuestionContentId = cy?.$('edge[target = "' + targetNodeContentId + '"]')?.toArray()?.[0]?.data()?.source
|
const parentQuestionContentId = cy?.$('edge[target = "' + targetNodeContentId + '"]')?.toArray()?.[0]?.data()?.source
|
||||||
if (targetNodeContentId && parentQuestionContentId) {
|
if (targetNodeContentId && parentQuestionContentId) {
|
||||||
|
|
||||||
@ -244,7 +267,7 @@ function CsComponent ({
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//После всех манипуляций удаляем грани из CS и ноды из бекенда
|
//После всех манипуляций удаляем грани и ноды из CS Чистим rule потомков на беке
|
||||||
|
|
||||||
deleteNodes.forEach((nodeId) => {//Ноды
|
deleteNodes.forEach((nodeId) => {//Ноды
|
||||||
cy?.remove(cy?.$("#" + nodeId))
|
cy?.remove(cy?.$("#" + nodeId))
|
||||||
@ -253,7 +276,10 @@ function CsComponent ({
|
|||||||
question.content.rule.parentId = ""
|
question.content.rule.parentId = ""
|
||||||
question.content.rule.main = []
|
question.content.rule.main = []
|
||||||
question.content.rule.default = ""
|
question.content.rule.default = ""
|
||||||
|
question.content.rule.children = []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
deleteEdges.forEach((edge: any) => {//Грани
|
deleteEdges.forEach((edge: any) => {//Грани
|
||||||
@ -263,28 +289,41 @@ function CsComponent ({
|
|||||||
removeButtons(targetNodeContentId)
|
removeButtons(targetNodeContentId)
|
||||||
cy?.data('changed', true)
|
cy?.data('changed', true)
|
||||||
cy?.layout(lyopts).run()
|
cy?.layout(lyopts).run()
|
||||||
|
|
||||||
|
//удаляем result всех потомков
|
||||||
|
trashQuestions.forEach((qr) => {
|
||||||
|
if (qr.type === "result") {
|
||||||
|
if (deleteNodes.includes(qr.content.rule.parentId) || qr.content.rule.parentId === targetQuestion.content.id) {
|
||||||
|
deleteQuestion(qr.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const clearDataAfterRemoveNode = ({ targetQuestionContentId, parentQuestionContentId }: { targetQuestionContentId: string, parentQuestionContentId: string }) => {
|
const clearDataAfterRemoveNode = ({ targetQuestionContentId, parentQuestionContentId }: { targetQuestionContentId: string, parentQuestionContentId: string }) => {
|
||||||
|
|
||||||
console.log("target ",targetQuestionContentId, "parent ", parentQuestionContentId)
|
console.log("target ", targetQuestionContentId, "parent ", parentQuestionContentId)
|
||||||
|
|
||||||
|
|
||||||
updateQuestion(targetQuestionContentId, question => {
|
updateQuestion(targetQuestionContentId, question => {
|
||||||
question.content.rule.parentId = ""
|
question.content.rule.parentId = ""
|
||||||
|
question.content.rule.children = []
|
||||||
question.content.rule.main = []
|
question.content.rule.main = []
|
||||||
question.content.rule.default = ""
|
question.content.rule.default = ""
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
//чистим rule родителя
|
//чистим rule родителя
|
||||||
const parentQuestion = getQuestionByContentId(parentQuestionContentId)
|
const parentQuestion = getQuestionByContentId(parentQuestionContentId)
|
||||||
|
console.log(parentQuestion.content.rule.parentId)
|
||||||
const newRule = {}
|
const newRule = {}
|
||||||
|
const newChildren = [...parentQuestion.content.rule.children]
|
||||||
|
newChildren.splice(parentQuestion.content.rule.children.indexOf(targetQuestionContentId), 1);
|
||||||
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== targetQuestionContentId) //удаляем условия перехода от родителя к этому вопросу
|
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== targetQuestionContentId) //удаляем условия перехода от родителя к этому вопросу
|
||||||
newRule.parentId = parentQuestion.content.rule.parentId
|
newRule.parentId = parentQuestion.content.rule.parentId
|
||||||
newRule.default = questions.filter((q) => {
|
newRule.default = parentQuestion.content.rule.default === targetQuestionContentId ? "" : parentQuestion.content.rule.default
|
||||||
return q.content.rule.parentId === parentQuestionContentId && q.content.id !== targetQuestionContentId
|
newRule.children = newChildren
|
||||||
})[0]?.content.id || ""
|
|
||||||
//Если этот вопрос был дефолтным у родителя - чистим дефолт
|
|
||||||
//Смотрим можем ли мы заменить id на один из main
|
|
||||||
|
|
||||||
updateQuestion(parentQuestionContentId, (PQ) => {
|
updateQuestion(parentQuestionContentId, (PQ) => {
|
||||||
PQ.content.rule = newRule
|
PQ.content.rule = newRule
|
||||||
@ -335,7 +374,7 @@ function CsComponent ({
|
|||||||
positions: (e) => {
|
positions: (e) => {
|
||||||
if (!e.cy().data('changed')) {
|
if (!e.cy().data('changed')) {
|
||||||
return e.data('oldPos')
|
return e.data('oldPos')
|
||||||
}
|
}
|
||||||
const id = e.id()
|
const id = e.id()
|
||||||
const incomming = e.cy().edges(`[target="${id}"]`)
|
const incomming = e.cy().edges(`[target="${id}"]`)
|
||||||
const layer = 0
|
const layer = 0
|
||||||
@ -366,7 +405,7 @@ function CsComponent ({
|
|||||||
while (queue.length) {
|
while (queue.length) {
|
||||||
const task = queue.pop()
|
const task = queue.pop()
|
||||||
if (task.children.length === 0) {
|
if (task.children.length === 0) {
|
||||||
task.parent.data('subtreeWidth', task.parent.height()+50)
|
task.parent.data('subtreeWidth', task.parent.height() + 50)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const unprocessed = task?.children.filter(e => {
|
const unprocessed = task?.children.filter(e => {
|
||||||
@ -385,19 +424,19 @@ function CsComponent ({
|
|||||||
|
|
||||||
const pos = { x: 0, y: 0 }
|
const pos = { x: 0, y: 0 }
|
||||||
e.data('oldPos', pos)
|
e.data('oldPos', pos)
|
||||||
|
|
||||||
queue.push({task: children, parent: e})
|
queue.push({ task: children, parent: e })
|
||||||
while (queue.length) {
|
while (queue.length) {
|
||||||
const task = queue.pop()
|
const task = queue.pop()
|
||||||
const oldPos = task.parent.data('oldPos')
|
const oldPos = task.parent.data('oldPos')
|
||||||
let yoffset = oldPos.y - task.parent.data('subtreeWidth') / 2
|
let yoffset = oldPos.y - task.parent.data('subtreeWidth') / 2
|
||||||
task.task.forEach(n => {
|
task.task.forEach(n => {
|
||||||
const width = n.data('subtreeWidth')
|
const width = n.data('subtreeWidth')
|
||||||
|
|
||||||
console.log('ORORORORO',n.data(), yoffset, width, oldPos, task.parent.data('subtreeWidth'))
|
console.log('ORORORORO', n.data(), yoffset, width, oldPos, task.parent.data('subtreeWidth'))
|
||||||
n.data('oldPos',{x: 250 * n.data('layer'),y: yoffset + width/2})
|
n.data('oldPos', { x: 250 * n.data('layer'), y: yoffset + width / 2 })
|
||||||
yoffset+=width
|
yoffset += width
|
||||||
queue.push({task: n.cy().edges(`[source="${n.id()}"]`).targets(), parent: n})
|
queue.push({ task: n.cy().edges(`[source="${n.id()}"]`).targets(), parent: n })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
e.cy().data('changed', false)
|
e.cy().data('changed', false)
|
||||||
@ -407,7 +446,7 @@ function CsComponent ({
|
|||||||
const opos = e.data('oldPos')
|
const opos = e.data('oldPos')
|
||||||
if (opos) {
|
if (opos) {
|
||||||
return opos
|
return opos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, // map of (node id) => (position obj); or function(node){ return somPos; }
|
}, // map of (node id) => (position obj); or function(node){ return somPos; }
|
||||||
zoom: undefined, // the zoom level to set (prob want fit = false if set)
|
zoom: undefined, // the zoom level to set (prob want fit = false if set)
|
||||||
@ -426,7 +465,7 @@ function CsComponent ({
|
|||||||
console.log('KEKEKE')
|
console.log('KEKEKE')
|
||||||
document.querySelector("#root")?.addEventListener("mouseup", cleardragQuestionContentId);
|
document.querySelector("#root")?.addEventListener("mouseup", cleardragQuestionContentId);
|
||||||
const cy = cyRef.current;
|
const cy = cyRef.current;
|
||||||
const eles = cy?.add(storeToNodes(questions.filter((question:AnyTypedQuizQuestion) => (question.type !== "result" && question.type !== null))))
|
const eles = cy?.add(storeToNodes(questions.filter((question: AnyTypedQuizQuestion) => (question.type !== "result" && question.type !== null))))
|
||||||
cy.data('changed', true)
|
cy.data('changed', true)
|
||||||
// cy.data('changed', true)
|
// cy.data('changed', true)
|
||||||
const elecs = eles.layout(lyopts).run()
|
const elecs = eles.layout(lyopts).run()
|
||||||
@ -735,6 +774,23 @@ let pressed = false
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Button
|
||||||
|
sx={{
|
||||||
|
mb: "20px",
|
||||||
|
height: "27px",
|
||||||
|
color: "#7E2AEA",
|
||||||
|
textDecoration: "underline",
|
||||||
|
fontSize: "16px",
|
||||||
|
}}
|
||||||
|
variant="text"
|
||||||
|
onClick={() => {
|
||||||
|
|
||||||
|
//код сюда
|
||||||
|
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Выровнять
|
||||||
|
</Button>
|
||||||
<CytoscapeComponent
|
<CytoscapeComponent
|
||||||
wheelSensitivity={0.1}
|
wheelSensitivity={0.1}
|
||||||
elements={[]}
|
elements={[]}
|
||||||
@ -745,7 +801,7 @@ let pressed = false
|
|||||||
cy={(cy) => {
|
cy={(cy) => {
|
||||||
cyRef.current = cy;
|
cyRef.current = cy;
|
||||||
}}
|
}}
|
||||||
autoungrabify={true}
|
autoungrabify={true}
|
||||||
/>
|
/>
|
||||||
<button onClick={() => {
|
<button onClick={() => {
|
||||||
console.log("NODES____________________________")
|
console.log("NODES____________________________")
|
||||||
@ -761,15 +817,15 @@ let pressed = false
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function Clear () {
|
function Clear() {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
updateRootContentId(quiz.id, "")
|
updateRootContentId(quiz.id, "")
|
||||||
clearRuleForAll()
|
clearRuleForAll()
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withErrorBoundary(CsComponent, {
|
export default withErrorBoundary(CsComponent, {
|
||||||
fallback: <Clear/>,
|
fallback: <Clear />,
|
||||||
onError: (error, info) => {
|
onError: (error, info) => {
|
||||||
enqueueSnackbar("Дерево порвалось")
|
enqueueSnackbar("Дерево порвалось")
|
||||||
console.log(info)
|
console.log(info)
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const BranchingMap = () => {
|
|||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||||
marginBottom: "40px",
|
marginBottom: "40px",
|
||||||
height: "521px",
|
height: "568px",
|
||||||
border: dragQuestionContentId === null ? "none" : "#7e2aea 2px dashed"
|
border: dragQuestionContentId === null ? "none" : "#7e2aea 2px dashed"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -46,20 +46,6 @@ export default function ButtonsOptions({
|
|||||||
updateOpenedModalSettingsId(question.id)
|
updateOpenedModalSettingsId(question.id)
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickBranching = (_, value) => {
|
|
||||||
const parentId = question.content.rule.parentId
|
|
||||||
if (parentId.length === 0 ){
|
|
||||||
return enqueueSnackbar("Вопрос не учавствует в ветвлении")
|
|
||||||
}
|
|
||||||
if (parentId === "root") {
|
|
||||||
return enqueueSnackbar("У корня нет условий ветвления")
|
|
||||||
}
|
|
||||||
if (parentId.length !== 0) {
|
|
||||||
// updateOpenBranchingPanel(value)
|
|
||||||
openedModal()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttonSetting: {
|
const buttonSetting: {
|
||||||
icon: JSX.Element;
|
icon: JSX.Element;
|
||||||
@ -300,7 +286,59 @@ export default function ButtonsOptions({
|
|||||||
// deleteTimeoutId: newTimeoutId,
|
// deleteTimeoutId: newTimeoutId,
|
||||||
// });
|
// });
|
||||||
|
|
||||||
deleteQuestion(question.id, quiz.id);
|
if (question.type !== null) {
|
||||||
|
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
|
||||||
|
updateRootContentId(quiz.id, "")
|
||||||
|
clearRuleForAll()
|
||||||
|
questions.forEach(q => {
|
||||||
|
if (q.type === "result") {
|
||||||
|
deleteQuestion(q.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deleteQuestion(question.id);
|
||||||
|
} 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 (targetQuestion.type === "result") {
|
||||||
|
deleteQuestion(targetQuestion.id);
|
||||||
|
} else {
|
||||||
|
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 = ""
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
//чистим rule родителя
|
||||||
|
const parentQuestion = getQuestionByContentId(question.content.rule.parentId)
|
||||||
|
const newRule = {}
|
||||||
|
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== question.content.id) //удаляем условия перехода от родителя к этому вопросу
|
||||||
|
newRule.parentId = parentQuestion.content.rule.parentId
|
||||||
|
newRule.default = parentQuestion.content.rule.parentId === question.content.id ? "" : parentQuestion.content.rule.parentId
|
||||||
|
newRule.children = [...parentQuestion.content.rule.children].splice(parentQuestion.content.rule.children.indexOf(question.content.id), 1);
|
||||||
|
|
||||||
|
updateQuestion(question.content.rule.parentId, (PQ) => {
|
||||||
|
PQ.content.rule = newRule
|
||||||
|
})
|
||||||
|
deleteQuestion(question.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteQuestion(question.id)
|
||||||
|
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
data-cy="delete-question"
|
data-cy="delete-question"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { copyQuestion, deleteQuestion, updateQuestion } from "@root/questions/actions";
|
import { copyQuestion, deleteQuestion, updateQuestion, clearRuleForAll, getQuestionByContentId } from "@root/questions/actions";
|
||||||
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
|
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
|
||||||
import { ReallyChangingModal } from "@ui_kit/Modal/ReallyChangingModal/ReallyChangingModal";
|
import { ReallyChangingModal } from "@ui_kit/Modal/ReallyChangingModal/ReallyChangingModal";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@ -27,6 +27,7 @@ import { updateOpenBranchingPanel, updateDesireToOpenABranchingModal } from "@ro
|
|||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { updateRootContentId } from "@root/quizes/actions";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -46,7 +47,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
const isIconMobile = useMediaQuery(theme.breakpoints.down(1050));
|
const isIconMobile = useMediaQuery(theme.breakpoints.down(1050));
|
||||||
const { openBranchingPanel } = useQuestionsStore.getState()
|
const { questions } = useQuestionsStore.getState()
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -323,7 +324,59 @@ export default function ButtonsOptionsAndPict({
|
|||||||
// deleteTimeoutId: newTimeoutId,
|
// deleteTimeoutId: newTimeoutId,
|
||||||
// });
|
// });
|
||||||
|
|
||||||
deleteQuestion(question.id, quiz?.id);
|
if (question.type !== null) {
|
||||||
|
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
|
||||||
|
updateRootContentId(quiz.id, "")
|
||||||
|
clearRuleForAll()
|
||||||
|
questions.forEach(q => {
|
||||||
|
if (q.type === "result") {
|
||||||
|
deleteQuestion(q.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deleteQuestion(question.id);
|
||||||
|
} 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 (targetQuestion.type === "result") {
|
||||||
|
deleteQuestion(targetQuestion.id);
|
||||||
|
} else {
|
||||||
|
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 = ""
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
//чистим rule родителя
|
||||||
|
const parentQuestion = getQuestionByContentId(question.content.rule.parentId)
|
||||||
|
const newRule = {}
|
||||||
|
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== question.content.id) //удаляем условия перехода от родителя к этому вопросу
|
||||||
|
newRule.parentId = parentQuestion.content.rule.parentId
|
||||||
|
newRule.default = parentQuestion.content.rule.parentId === question.content.id ? "" : parentQuestion.content.rule.parentId
|
||||||
|
newRule.children = [...parentQuestion.content.rule.children].splice(parentQuestion.content.rule.children.indexOf(question.content.id), 1);
|
||||||
|
|
||||||
|
updateQuestion(question.content.rule.parentId, (PQ) => {
|
||||||
|
PQ.content.rule = newRule
|
||||||
|
})
|
||||||
|
deleteQuestion(question.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteQuestion(question.id)
|
||||||
|
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
data-cy="delete-question"
|
data-cy="delete-question"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -29,7 +29,8 @@ import {
|
|||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { copyQuestion, createUntypedQuestion, deleteQuestion, toggleExpandQuestion, updateQuestion, updateUntypedQuestion } from "@root/questions/actions";
|
import { copyQuestion, createUntypedQuestion, deleteQuestion, clearRuleForAll, toggleExpandQuestion, updateQuestion, updateUntypedQuestion, getQuestionByContentId } from "@root/questions/actions";
|
||||||
|
import { updateRootContentId } from "@root/quizes/actions";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
@ -40,6 +41,7 @@ import { ChooseAnswerModal } from "./ChooseAnswerModal";
|
|||||||
import TypeQuestions from "../TypeQuestions";
|
import TypeQuestions from "../TypeQuestions";
|
||||||
import { QuestionType } from "@model/question/question";
|
import { QuestionType } from "@model/question/question";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
||||||
@ -49,6 +51,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function QuestionsPageCard({ question, draggableProps, isDragging, index }: Props) {
|
export default function QuestionsPageCard({ question, draggableProps, isDragging, index }: Props) {
|
||||||
|
const { questions } = useQuestionsStore()
|
||||||
const [plusVisible, setPlusVisible] = useState<boolean>(false);
|
const [plusVisible, setPlusVisible] = useState<boolean>(false);
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -254,8 +257,63 @@ export default function QuestionsPageCard({ question, draggableProps, isDragging
|
|||||||
// ...question,
|
// ...question,
|
||||||
// deleteTimeoutId: newTimeoutId,
|
// deleteTimeoutId: newTimeoutId,
|
||||||
// });
|
// });
|
||||||
|
console.log(question.type)
|
||||||
|
if (question.type !== null) {
|
||||||
|
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
|
||||||
|
updateRootContentId(quiz.id, "")
|
||||||
|
clearRuleForAll()
|
||||||
|
deleteQuestion(question.id);
|
||||||
|
questions.forEach(q => {
|
||||||
|
if (q.type === "result") {
|
||||||
|
deleteQuestion(q.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (question.content.rule.parentId.length > 0) { //удалить из стора вопрос из дерева и очистить его потомков
|
||||||
|
const clearQuestions = [] as string[]
|
||||||
|
|
||||||
deleteQuestion(question.id, quiz.id);
|
//записываем потомков , а их результаты удаляем
|
||||||
|
const getChildren = (parentQuestion: AnyTypedQuizQuestion) => {
|
||||||
|
questions.forEach((targetQuestion) => {
|
||||||
|
if (targetQuestion.content.rule.parentId === parentQuestion.content.id) {//если у вопроса совпал родитель с родителем => он потомок, в кучу его
|
||||||
|
if (targetQuestion.type === "result") {
|
||||||
|
deleteQuestion(targetQuestion.id);
|
||||||
|
} else {
|
||||||
|
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 = ""
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
//чистим rule родителя
|
||||||
|
const parentQuestion = getQuestionByContentId(question.content.rule.parentId)
|
||||||
|
const newRule = {}
|
||||||
|
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== question.content.id) //удаляем условия перехода от родителя к этому вопросу
|
||||||
|
newRule.parentId = parentQuestion.content.rule.parentId
|
||||||
|
newRule.default = parentQuestion.content.rule.parentId === question.content.id ? "" : parentQuestion.content.rule.parentId
|
||||||
|
newRule.children = [...parentQuestion.content.rule.children].splice(parentQuestion.content.rule.children.indexOf(question.content.id), 1);
|
||||||
|
|
||||||
|
updateQuestion(question.content.rule.parentId, (PQ) => {
|
||||||
|
PQ.content.rule = newRule
|
||||||
|
})
|
||||||
|
deleteQuestion(question.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deleteQuestion(question.id)
|
||||||
|
} else {
|
||||||
|
console.log("удаляю безтипогово")
|
||||||
|
deleteQuestion(question.id)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
data-cy="delete-question"
|
data-cy="delete-question"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -49,7 +49,7 @@ export default function SettingSlider({ question }: SettingSliderProps) {
|
|||||||
height: isMobile ? "100%" : "auto",
|
height: isMobile ? "100%" : "auto",
|
||||||
alignItems: isMobile ? "flex-start" : "center",
|
alignItems: isMobile ? "flex-start" : "center",
|
||||||
}}
|
}}
|
||||||
label={"Выбор диапозона (два ползунка)"}
|
label={"Выбор диапазона (два ползунка)"}
|
||||||
checked={question.content.chooseRange}
|
checked={question.content.chooseRange}
|
||||||
handleChange={({ target }) => {
|
handleChange={({ target }) => {
|
||||||
updateQuestion(question.id, question => {
|
updateQuestion(question.id, question => {
|
||||||
|
|||||||
@ -16,17 +16,18 @@ export const FirstEntry = () => {
|
|||||||
|
|
||||||
const create = () => {
|
const create = () => {
|
||||||
if (quiz?.config.haveRoot) {
|
if (quiz?.config.haveRoot) {
|
||||||
if (questions.length === 0) {
|
console.log("createFrontResult")
|
||||||
enqueueSnackbar("У вас не добавлено ни одного вопроса")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
questions
|
questions
|
||||||
.filter((question:AnyTypedQuizQuestion) => question.type !== null && question.content.rule.parentId.length !== 0 && question.content.rule.default.length === 0)
|
.filter((question:AnyTypedQuizQuestion) => {
|
||||||
|
console.log(question)
|
||||||
|
return question.type !== null && question.content.rule.parentId.length !== 0 && question.content.rule.children.length === 0
|
||||||
|
})
|
||||||
.forEach(question => {
|
.forEach(question => {
|
||||||
createFrontResult(quiz.id, question.content.id)
|
createFrontResult(quiz.id, question.content.id)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
createFrontResult(quiz.id)
|
console.log("createFrontResult")
|
||||||
|
createFrontResult(quiz.id, "line")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,31 +2,46 @@ import IconPlus from "@icons/IconPlus";
|
|||||||
import Info from "@icons/Info";
|
import Info from "@icons/Info";
|
||||||
import Plus from "@icons/Plus";
|
import Plus from "@icons/Plus";
|
||||||
import ArrowLeft from "@icons/questionsPage/arrowLeft";
|
import ArrowLeft from "@icons/questionsPage/arrowLeft";
|
||||||
import { Box, Button, Typography, Paper, FormControl, TextField } from "@mui/material";
|
import { Box, Button, Typography, Paper, Modal, TextField } from "@mui/material";
|
||||||
import { incrementCurrentStep } from "@root/quizes/actions";
|
import { incrementCurrentStep } from "@root/quizes/actions";
|
||||||
import CustomWrapper from "@ui_kit/CustomWrapper";
|
import CustomWrapper from "@ui_kit/CustomWrapper";
|
||||||
import { DescriptionForm } from "./DescriptionForm/DescriptionForm";
|
import { DescriptionForm } from "./DescriptionForm/DescriptionForm";
|
||||||
import { ResultListForm } from "./ResultListForm";
|
import { ResultListForm } from "./ResultListForm";
|
||||||
import { SettingForm } from "./SettingForm";
|
import { SettingForm } from "./SettingForm";
|
||||||
import { useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { WhenCard } from "./cards/WhenCard";
|
import { WhenCard } from "./cards/WhenCard";
|
||||||
import { ResultCard } from "./cards/ResultCard";
|
import { ResultCard, checkEmptyData } from "./cards/ResultCard";
|
||||||
import { EmailSettingsCard } from "./cards/EmailSettingsCard";
|
import { EmailSettingsCard } from "./cards/EmailSettingsCard";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
import { useCurrentQuiz } from "@root/quizes/hooks"
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
|
import { createFrontResult, deleteQuestion } from "@root/questions/actions";
|
||||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
|
|
||||||
export const ResultSettings = () => {
|
export const ResultSettings = () => {
|
||||||
|
const { questions } = useQuestionsStore()
|
||||||
const quiz = useCurrentQuiz()
|
const quiz = useCurrentQuiz()
|
||||||
const results = useQuestionsStore().questions.filter(q => q.type === "result")
|
const results = useQuestionsStore().questions.filter((q): q is QuizQuestionResult => q.type === "result")
|
||||||
console.log("опросник ", quiz)
|
console.log("опросник ", quiz)
|
||||||
const [quizExpand, setQuizExpand] = useState(true)
|
const [quizExpand, setQuizExpand] = useState(true)
|
||||||
const [resultContract, setResultContract] = useState(true)
|
const [resultContract, setResultContract] = useState(true)
|
||||||
const [readyLeave, setReadyLeave] = useState(true)
|
const isReadyToLeaveRef = useRef(true);
|
||||||
|
|
||||||
const setAlertLeave = () => {
|
useEffect(function calcIsReadyToLeave(){
|
||||||
setReadyLeave(false)
|
let isReadyToLeave = true;
|
||||||
}
|
results.forEach((result) => {
|
||||||
|
if (checkEmptyData({ resultData: result })) {
|
||||||
|
isReadyToLeave = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(`setting isReadyToLeaveRef to ${isReadyToLeave}`);
|
||||||
|
isReadyToLeaveRef.current = isReadyToLeave;
|
||||||
|
}, [results])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (isReadyToLeaveRef.current === false) alert("Пожалуйста, проверьте, что вы заполнили все результаты");
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ maxWidth: "796px" }}>
|
<Box sx={{ maxWidth: "796px" }}>
|
||||||
@ -92,10 +107,17 @@ export const ResultSettings = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{
|
{
|
||||||
results.map((resultQuestion) => <ResultCard resultContract={resultContract} resultData={resultQuestion} key={resultQuestion.id} setAlertLeave={setAlertLeave}/>)
|
results.map((resultQuestion) => <ResultCard resultContract={resultContract} resultData={resultQuestion} key={resultQuestion.id} />)
|
||||||
}
|
}
|
||||||
|
<Modal
|
||||||
|
open={false}
|
||||||
|
// onClose={handleClose}
|
||||||
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description"
|
||||||
|
>
|
||||||
|
<></>
|
||||||
|
</Modal>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,18 +1,12 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { updateQuiz } from "@root/quizes/actions"
|
|
||||||
import { getQuestionByContentId, updateQuestion, uploadQuestionImage } from "@root/questions/actions"
|
import { getQuestionByContentId, updateQuestion, uploadQuestionImage } from "@root/questions/actions"
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
import { useCurrentQuiz } from "@root/quizes/hooks"
|
||||||
|
|
||||||
import { SwitchSetting } from "../SwichResult";
|
|
||||||
|
|
||||||
|
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
|
||||||
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
|
|
||||||
import { UploadImageModal } from "../../Questions/UploadImage/UploadImageModal";
|
import { UploadImageModal } from "../../Questions/UploadImage/UploadImageModal";
|
||||||
import { UploadVideoModal } from "../../Questions/UploadVideoModal";
|
|
||||||
import { useDisclosure } from "../../../utils/useDisclosure";
|
import { useDisclosure } from "../../../utils/useDisclosure";
|
||||||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
||||||
|
|
||||||
@ -32,21 +26,18 @@ import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
|
|||||||
|
|
||||||
import ExpandLessIconBG from "@icons/ExpandLessIconBG";
|
import ExpandLessIconBG from "@icons/ExpandLessIconBG";
|
||||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||||
import { OneIcon } from "@icons/questionsPage/OneIcon";
|
|
||||||
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
|
|
||||||
import Trash from "@icons/trash";
|
import Trash from "@icons/trash";
|
||||||
import Info from "@icons/Info";
|
import Info from "@icons/Info";
|
||||||
import ImageAndVideoButtons from "../DescriptionForm/ImageAndVideoButtons";
|
|
||||||
import SettingIcon from "@icons/questionsPage/settingIcon";
|
import SettingIcon from "@icons/questionsPage/settingIcon";
|
||||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
|
import { MutableRefObject } from "react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
resultContract: boolean;
|
resultContract: boolean;
|
||||||
resultData: QuizQuestionResult;
|
resultData: QuizQuestionResult;
|
||||||
setAlertLeave: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkEmptyData = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
export const checkEmptyData = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
||||||
let check = true
|
let check = true
|
||||||
if (
|
if (
|
||||||
resultData.title.length > 0 ||
|
resultData.title.length > 0 ||
|
||||||
@ -77,11 +68,6 @@ const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
|||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const id = open ? 'simple-popover' : undefined;
|
const id = open ? 'simple-popover' : undefined;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Info
|
<Info
|
||||||
@ -115,7 +101,11 @@ const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography>
|
||||||
Заголовок вопроса, после которого появится результат: "{question?.title || "нет заголовка"}"
|
{resultData?.content.rule.parentId === "line" ? "Единый результат в конце прохождения опросника без ветвления"
|
||||||
|
:
|
||||||
|
`Заголовок вопроса, после которого появится результат: "${question?.title || "нет заголовка"}"`
|
||||||
|
}
|
||||||
|
|
||||||
</Typography>
|
</Typography>
|
||||||
{checkEmpty &&
|
{checkEmpty &&
|
||||||
<Typography color="red">
|
<Typography color="red">
|
||||||
@ -129,13 +119,9 @@ const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ResultCard = ({ resultContract, resultData, setAlertLeave }: Props) => {
|
export const ResultCard = ({ resultContract, resultData }: Props) => {
|
||||||
console.log("resultData", resultData)
|
console.log("resultData", resultData)
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (checkEmptyData({resultData})) setAlertLeave()
|
|
||||||
}, [resultData])
|
|
||||||
|
|
||||||
const quizQid = useCurrentQuiz()?.qid;
|
const quizQid = useCurrentQuiz()?.qid;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@ -144,7 +130,7 @@ export const ResultCard = ({ resultContract, resultData, setAlertLeave }: Props)
|
|||||||
|
|
||||||
const [expand, setExpand] = React.useState(true)
|
const [expand, setExpand] = React.useState(true)
|
||||||
const [resultCardSettings, setResultCardSettings] = React.useState(false)
|
const [resultCardSettings, setResultCardSettings] = React.useState(false)
|
||||||
const [buttonPlus, setButtonPlus] = React.useState(false)
|
const [buttonPlus, setButtonPlus] = React.useState(true)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setExpand(true)
|
setExpand(true)
|
||||||
@ -621,4 +607,4 @@ export const ResultCard = ({ resultContract, resultData, setAlertLeave }: Props)
|
|||||||
}
|
}
|
||||||
</Paper >
|
</Paper >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,6 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
|||||||
import { getQuestionByContentId } from "@root/questions/actions";
|
import { getQuestionByContentId } from "@root/questions/actions";
|
||||||
|
|
||||||
type QuestionProps = {
|
type QuestionProps = {
|
||||||
stepNumber: number;
|
|
||||||
setStepNumber: (step: number) => void;
|
|
||||||
questions: AnyTypedQuizQuestion[];
|
questions: AnyTypedQuizQuestion[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,9 @@ import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
|||||||
export const ViewPage = () => {
|
export const ViewPage = () => {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const { questions } = useQuestions();
|
const { questions } = useQuestions();
|
||||||
const [visualStartPage, setVisualStartPage] = useState<boolean>(!quiz?.config.noStartPage);
|
const [visualStartPage, setVisualStartPage] = useState<boolean>(
|
||||||
|
!quiz?.config.noStartPage
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const link = document.querySelector('link[rel="icon"]');
|
const link = document.querySelector('link[rel="icon"]');
|
||||||
@ -21,9 +23,9 @@ export const ViewPage = () => {
|
|||||||
}
|
}
|
||||||
}, [quiz?.config.startpage.favIcon]);
|
}, [quiz?.config.startpage.favIcon]);
|
||||||
|
|
||||||
const filteredQuestions = questions.filter(
|
const filteredQuestions = (
|
||||||
({ type }) => type
|
questions.filter(({ type }) => type) as AnyTypedQuizQuestion[]
|
||||||
) as AnyTypedQuizQuestion[];
|
).sort((previousItem, item) => previousItem.page - item.page);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@ -33,9 +35,7 @@ export const ViewPage = () => {
|
|||||||
showNextButton={!!filteredQuestions.length}
|
showNextButton={!!filteredQuestions.length}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Question
|
<Question questions={filteredQuestions} />
|
||||||
questions={filteredQuestions}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import DatePicker from "react-datepicker";
|
import { DatePicker } from "@mui/x-date-pickers";
|
||||||
import { Box, Typography } from "@mui/material";
|
import { Box, Typography } from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
||||||
|
|
||||||
import "react-datepicker/dist/react-datepicker.css";
|
// import "react-datepicker/dist/react-datepicker.css";
|
||||||
|
|
||||||
import type { QuizQuestionDate } from "../../../model/questionTypes/date";
|
import type { QuizQuestionDate } from "../../../model/questionTypes/date";
|
||||||
|
import CalendarIcon from "@icons/CalendarIcon";
|
||||||
|
|
||||||
type DateProps = {
|
type DateProps = {
|
||||||
currentQuestion: QuizQuestionDate;
|
currentQuestion: QuizQuestionDate;
|
||||||
@ -31,6 +32,9 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
|
slots={{
|
||||||
|
openPickerIcon: () => <CalendarIcon />,
|
||||||
|
}}
|
||||||
selected={
|
selected={
|
||||||
answer
|
answer
|
||||||
? new window.Date(`${month}.${day}.${year}`)
|
? new window.Date(`${month}.${day}.${year}`)
|
||||||
@ -48,6 +52,30 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
slotProps={{
|
||||||
|
openPickerButton: {
|
||||||
|
sx: {
|
||||||
|
p: 0,
|
||||||
|
},
|
||||||
|
"data-cy": "open-datepicker",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
"& .MuiInputBase-root": {
|
||||||
|
backgroundColor: "#F2F3F7",
|
||||||
|
borderRadius: "10px",
|
||||||
|
maxWidth: "250px",
|
||||||
|
pr: "22px",
|
||||||
|
"& input": {
|
||||||
|
py: "11px",
|
||||||
|
pl: "20px",
|
||||||
|
lineHeight: "19px",
|
||||||
|
},
|
||||||
|
"& fieldset": {
|
||||||
|
borderColor: "#9A9AAF",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Radio,
|
Radio,
|
||||||
useTheme, FormControl,
|
useTheme,
|
||||||
|
FormControl,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@root/quizView";
|
||||||
|
|
||||||
import RadioCheck from "@ui_kit/RadioCheck";
|
import RadioCheck from "@ui_kit/RadioCheck";
|
||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
@ -22,20 +22,19 @@ type EmojiProps = {
|
|||||||
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
const { answer } =
|
||||||
|
answers.find(
|
||||||
useEffect(() => {
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
if (!answer) {
|
) ?? {};
|
||||||
updateAnswer(currentQuestion.content.id, currentQuestion.content.variants[0].id);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5">{currentQuestion.title}</Typography>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
value={currentQuestion.content.variants.findIndex(({ id }) => answer === id)}
|
value={currentQuestion.content.variants.findIndex(
|
||||||
|
({ id }) => answer === id
|
||||||
|
)}
|
||||||
onChange={({ target }) =>
|
onChange={({ target }) =>
|
||||||
updateAnswer(
|
updateAnswer(
|
||||||
currentQuestion.content.id,
|
currentQuestion.content.id,
|
||||||
@ -51,50 +50,73 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", width: "100%", gap: "42px" }}>
|
<Box sx={{ display: "flex", width: "100%", gap: "42px" }}>
|
||||||
{currentQuestion.content.variants.map(
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
({ id, answer, extendedText }, index) => (
|
<FormControl
|
||||||
<FormControl
|
key={variant.id}
|
||||||
key={id}
|
sx={{
|
||||||
sx={{
|
borderRadius: "12px",
|
||||||
borderRadius: "12px",
|
border: `1px solid ${theme.palette.grey2.main}`,
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
overflow: "hidden",
|
||||||
overflow: "hidden",
|
maxWidth: "317px",
|
||||||
maxWidth: "317px",
|
width: "100%",
|
||||||
width: "100%",
|
height: "255px",
|
||||||
height: "255px"
|
}}
|
||||||
}}
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "193px",
|
||||||
|
background: "#ffffff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
{variant.extendedText && (
|
||||||
sx={{ display: "flex", alignItems: "center", height: "193px", background: "#ffffff" }}
|
<Typography fontSize={"100px"}>
|
||||||
>
|
{variant.extendedText}
|
||||||
<Box sx={{ width: "100%", display: "flex", justifyContent: "center" }}>
|
</Typography>
|
||||||
{extendedText && (
|
)}
|
||||||
<Typography fontSize={"100px"}>{extendedText}</Typography>
|
</Box>
|
||||||
)}
|
</Box>
|
||||||
</Box>
|
<FormControlLabel
|
||||||
|
key={variant.id}
|
||||||
|
sx={{
|
||||||
|
margin: 0,
|
||||||
|
padding: "15px",
|
||||||
|
color: "#4D4D4D",
|
||||||
|
display: "flex",
|
||||||
|
gap: "10px",
|
||||||
|
}}
|
||||||
|
value={index}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
updateAnswer(
|
||||||
|
currentQuestion.content.id,
|
||||||
|
currentQuestion.content.variants[index].id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (answer === currentQuestion.content.variants[index].id) {
|
||||||
|
deleteAnswer(currentQuestion.content.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
control={
|
||||||
|
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Box sx={{ display: "flex", gap: "10px" }}>
|
||||||
|
<Typography>{variant.answer}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<FormControlLabel
|
}
|
||||||
key={id}
|
/>
|
||||||
sx={{
|
</FormControl>
|
||||||
margin: 0,
|
))}
|
||||||
padding: "15px",
|
|
||||||
color: "#4D4D4D",
|
|
||||||
display: "flex",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
value={index}
|
|
||||||
control={
|
|
||||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Box sx={{ display: "flex", gap: "10px" }}>
|
|
||||||
<Typography>{answer}</Typography>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Radio,
|
Radio,
|
||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery, FormControl,
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@root/quizView";
|
||||||
import RadioCheck from "@ui_kit/RadioCheck";
|
import RadioCheck from "@ui_kit/RadioCheck";
|
||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
|
|
||||||
@ -22,28 +21,21 @@ type ImagesProps = {
|
|||||||
export const Images = ({ currentQuestion }: ImagesProps) => {
|
export const Images = ({ currentQuestion }: ImagesProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
const { answer } =
|
||||||
|
answers.find(
|
||||||
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
|
) ?? {};
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
const isMobile = useMediaQuery(theme.breakpoints.down(500));
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!answer) {
|
|
||||||
updateAnswer(currentQuestion.content.id, currentQuestion.content.variants[0].id);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5">{currentQuestion.title}</Typography>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
value={currentQuestion.content.variants.findIndex(({ id }) => answer === id)}
|
value={currentQuestion.content.variants.findIndex(
|
||||||
onChange={({ target }) =>
|
({ id }) => answer === id
|
||||||
updateAnswer(
|
)}
|
||||||
currentQuestion.content.id,
|
|
||||||
currentQuestion.content.variants[Number(target.value)].id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
@ -64,50 +56,58 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{currentQuestion.content.variants.map(
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
({ id, answer, extendedText }, index) => (
|
<Box
|
||||||
<Box
|
key={index}
|
||||||
key={index}
|
sx={{
|
||||||
sx={{
|
borderRadius: "5px",
|
||||||
borderRadius: "5px",
|
border: `1px solid ${theme.palette.grey2.main}`,
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||||
<Box
|
<Box sx={{ width: "100%", height: "300px" }}>
|
||||||
sx={{ display: "flex", alignItems: "center", gap: "10px" }}
|
{variant.extendedText && (
|
||||||
>
|
<img
|
||||||
<Box sx={{ width: "100%", height: "300px" }}>
|
src={variant.extendedText}
|
||||||
{extendedText && (
|
alt=""
|
||||||
<img
|
style={{
|
||||||
src={extendedText}
|
display: "block",
|
||||||
alt=""
|
width: "100%",
|
||||||
style={{
|
height: "100%",
|
||||||
display: "block",
|
objectFit: "cover",
|
||||||
width: "100%",
|
}}
|
||||||
height: "100%",
|
/>
|
||||||
objectFit: "cover",
|
)}
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
<FormControlLabel
|
|
||||||
key={id}
|
|
||||||
sx={{
|
|
||||||
display: "block",
|
|
||||||
textAlign: "center",
|
|
||||||
color: theme.palette.grey2.main,
|
|
||||||
marginTop: "10px",
|
|
||||||
}}
|
|
||||||
value={index}
|
|
||||||
control={
|
|
||||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
|
||||||
}
|
|
||||||
label={answer}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
<FormControlLabel
|
||||||
)}
|
key={variant.id}
|
||||||
|
sx={{
|
||||||
|
display: "block",
|
||||||
|
textAlign: "center",
|
||||||
|
color: theme.palette.grey2.main,
|
||||||
|
marginTop: "10px",
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
updateAnswer(
|
||||||
|
currentQuestion.content.id,
|
||||||
|
currentQuestion.content.variants[index].id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (answer === currentQuestion.content.variants[index].id) {
|
||||||
|
deleteAnswer(currentQuestion.content.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={index}
|
||||||
|
control={
|
||||||
|
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
||||||
|
}
|
||||||
|
label={variant.answer}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Box, Typography, Slider, useTheme } from "@mui/material";
|
import { Box, Typography, Slider, useTheme } from "@mui/material";
|
||||||
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
|
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
|
|
||||||
@ -12,8 +13,30 @@ type NumberProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Number = ({ currentQuestion }: NumberProps) => {
|
export const Number = ({ currentQuestion }: NumberProps) => {
|
||||||
|
const [minRange, setMinRange] = useState<string>("0");
|
||||||
|
const [maxRange, setMaxRange] = useState<string>("100");
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
|
const updateMinRangeDebounced = useDebouncedCallback(
|
||||||
|
(value, crowded = false) => {
|
||||||
|
if (crowded) {
|
||||||
|
setMinRange(maxRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAnswer(currentQuestion.content.id, value);
|
||||||
|
},
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
const updateMaxRangeDebounced = useDebouncedCallback(
|
||||||
|
(value, crowded = false) => {
|
||||||
|
if (crowded) {
|
||||||
|
setMaxRange(minRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAnswer(currentQuestion.content.id, value);
|
||||||
|
},
|
||||||
|
1000
|
||||||
|
);
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.content.id
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
@ -23,6 +46,11 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
const max = window.Number(currentQuestion.content.range.split("—")[1]);
|
const max = window.Number(currentQuestion.content.range.split("—")[1]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log("ans", currentQuestion.content.start);
|
||||||
|
if (answer) {
|
||||||
|
setMinRange(answer.split("—")[0]);
|
||||||
|
setMaxRange(answer.split("—")[1]);
|
||||||
|
}
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
updateAnswer(
|
updateAnswer(
|
||||||
currentQuestion.content.id,
|
currentQuestion.content.id,
|
||||||
@ -31,8 +59,11 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
: String(currentQuestion.content.start),
|
: String(currentQuestion.content.start),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setMinRange(String(currentQuestion.content.start));
|
||||||
|
setMaxRange(String(max));
|
||||||
}
|
}
|
||||||
}, [answer]);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@ -45,14 +76,66 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<Slider
|
||||||
|
value={
|
||||||
|
currentQuestion.content.chooseRange
|
||||||
|
? answer?.split("—").length || 0 > 1
|
||||||
|
? answer?.split("—").map((item) => window.Number(item))
|
||||||
|
: [min, min + 1]
|
||||||
|
: window.Number(answer || 1)
|
||||||
|
}
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
step={currentQuestion.content.step || 1}
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.brightPurple.main,
|
||||||
|
padding: "0",
|
||||||
|
marginTop: "75px",
|
||||||
|
"& .MuiSlider-valueLabel":{
|
||||||
|
background: theme.palette.brightPurple.main,
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "60px",
|
||||||
|
height: "36px"
|
||||||
|
},
|
||||||
|
"& .MuiSlider-valueLabel::before": {
|
||||||
|
width: "6px",
|
||||||
|
height: "2px",
|
||||||
|
transform: "translate(-50%, 50%) rotate(90deg)",
|
||||||
|
bottom: "-5px"
|
||||||
|
},
|
||||||
|
"& .MuiSlider-rail": {
|
||||||
|
backgroundColor: "#F2F3F7",
|
||||||
|
border: `1px solid #9A9AAF`,
|
||||||
|
height: "12px"
|
||||||
|
},
|
||||||
|
"& .MuiSlider-thumb": {
|
||||||
|
border: "3px #f2f3f7 solid",
|
||||||
|
height: "23px",
|
||||||
|
width: "23px"
|
||||||
|
},
|
||||||
|
"& .MuiSlider-track": {
|
||||||
|
height: "12px"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onChange={(_, value) => {
|
||||||
|
const range = String(value).replace(",", "—");
|
||||||
|
updateAnswer(currentQuestion.content.id, range);
|
||||||
|
}}
|
||||||
|
onChangeCommitted={(_, value) => {
|
||||||
|
if (currentQuestion.content.chooseRange) {
|
||||||
|
const range = value as number[];
|
||||||
|
|
||||||
|
setMinRange(String(range[0]));
|
||||||
|
setMaxRange(String(range[1]));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{!currentQuestion.content.chooseRange && (
|
{!currentQuestion.content.chooseRange && (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
value={
|
value={answer}
|
||||||
currentQuestion.content.chooseRange
|
|
||||||
? answer?.split("—")[0]
|
|
||||||
: answer
|
|
||||||
}
|
|
||||||
onChange={({ target }) => {
|
onChange={({ target }) => {
|
||||||
updateAnswer(
|
updateAnswer(
|
||||||
currentQuestion.content.id,
|
currentQuestion.content.id,
|
||||||
@ -69,6 +152,7 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{currentQuestion.content.chooseRange && (
|
{currentQuestion.content.chooseRange && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -79,40 +163,39 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
>
|
>
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
value={
|
value={minRange}
|
||||||
currentQuestion.content.chooseRange
|
|
||||||
? answer?.split("—")[0]
|
|
||||||
: answer
|
|
||||||
}
|
|
||||||
onChange={({ target }) => {
|
onChange={({ target }) => {
|
||||||
updateAnswer(
|
setMinRange(target.value);
|
||||||
currentQuestion.content.id,
|
|
||||||
window.Number(target.value) >
|
if (window.Number(target.value) >= window.Number(maxRange)) {
|
||||||
window.Number(answer?.split("—")[1])
|
updateMinRangeDebounced(`${maxRange}—${maxRange}`, true);
|
||||||
? `${answer?.split("—")[1]}—${answer?.split("—")[1]}`
|
|
||||||
: window.Number(target.value) < min
|
return;
|
||||||
? `${min}—${answer?.split("—")[1]}`
|
}
|
||||||
: `${target.value}—${answer?.split("—")[1]}`
|
|
||||||
);
|
updateMinRangeDebounced(`${target.value}—${maxRange}`);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "80px",
|
maxWidth: "80px",
|
||||||
"& .MuiInputBase-input": { textAlign: "center" },
|
"& .MuiInputBase-input": { textAlign: "center" },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
до
|
||||||
|
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
value={answer?.split("—")[1]}
|
value={maxRange}
|
||||||
onChange={({ target }) => {
|
onChange={({ target }) => {
|
||||||
updateAnswer(
|
setMaxRange(target.value);
|
||||||
currentQuestion.content.id,
|
|
||||||
window.Number(target.value) > max
|
if (window.Number(target.value) <= window.Number(minRange)) {
|
||||||
? `${answer?.split("—")[0]}—${max}`
|
updateMaxRangeDebounced(`${minRange}—${minRange}`, true);
|
||||||
: window.Number(target.value) <
|
|
||||||
window.Number(answer?.split("—")[0])
|
return;
|
||||||
? `${answer?.split("—")[0]}—${answer?.split("—")[0]}`
|
}
|
||||||
: `${answer?.split("—")[0]}—${target.value}`
|
|
||||||
);
|
updateMaxRangeDebounced(`${minRange}—${target.value}`);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "80px",
|
maxWidth: "80px",
|
||||||
@ -121,29 +204,7 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Slider
|
|
||||||
value={
|
|
||||||
currentQuestion.content.chooseRange
|
|
||||||
? answer?.split("—").length || 0 > 1
|
|
||||||
? answer?.split("—").map((item) => window.Number(item))
|
|
||||||
: [min, min + 1]
|
|
||||||
: window.Number(answer || 1)
|
|
||||||
}
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
step={currentQuestion.content.step || 1}
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.brightPurple.main,
|
|
||||||
padding: "0",
|
|
||||||
marginTop: "25px",
|
|
||||||
}}
|
|
||||||
onChange={(_, value) => {
|
|
||||||
updateAnswer(
|
|
||||||
currentQuestion.content.id,
|
|
||||||
String(value).replace(",", "—")
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -18,36 +18,38 @@ type RatingProps = {
|
|||||||
export const Rating = ({ currentQuestion }: RatingProps) => {
|
export const Rating = ({ currentQuestion }: RatingProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
const { answer } =
|
||||||
|
answers.find(
|
||||||
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
|
) ?? {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5">{currentQuestion.title}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "inline-block",
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
width: "100%",
|
||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RatingComponent
|
<RatingComponent
|
||||||
value={Number(answer || 0)}
|
value={Number(answer || 0)}
|
||||||
onChange={(_, value) => updateAnswer(currentQuestion.content.id, String(value))}
|
onChange={(_, value) =>
|
||||||
|
updateAnswer(currentQuestion.content.id, String(value))
|
||||||
|
}
|
||||||
sx={{ height: "50px" }}
|
sx={{ height: "50px" }}
|
||||||
max={currentQuestion.content.steps}
|
max={currentQuestion.content.steps}
|
||||||
icon={
|
icon={
|
||||||
<StarIconMini
|
<StarIconMini
|
||||||
color={theme.palette.brightPurple.main}
|
color={theme.palette.brightPurple.main}
|
||||||
width={50}
|
width={50}
|
||||||
sx={{ transform: "scale(0.8)" }}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
emptyIcon={
|
emptyIcon={
|
||||||
<StarIconMini
|
<StarIconMini
|
||||||
color={theme.palette.grey2.main}
|
color={theme.palette.grey2.main}
|
||||||
width={50}
|
width={50}
|
||||||
sx={{ transform: "scale(0.8)" }}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -59,8 +61,12 @@ export const Rating = ({ currentQuestion }: RatingProps) => {
|
|||||||
color: theme.palette.grey2.main,
|
color: theme.palette.grey2.main,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{currentQuestion.content.ratingNegativeDescription}</Typography>
|
<Typography>
|
||||||
<Typography>{currentQuestion.content.ratingPositiveDescription}</Typography>
|
{currentQuestion.content.ratingNegativeDescription}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
{currentQuestion.content.ratingPositiveDescription}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -12,7 +12,10 @@ type SelectProps = {
|
|||||||
|
|
||||||
export const Select = ({ currentQuestion }: SelectProps) => {
|
export const Select = ({ currentQuestion }: SelectProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
const { answer } =
|
||||||
|
answers.find(
|
||||||
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
|
) ?? {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@ -26,7 +29,7 @@ export const Select = ({ currentQuestion }: SelectProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
activeItemIndex={Number(answer) || 0}
|
activeItemIndex={answer ? Number(answer) : -1}
|
||||||
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
||||||
onChange={(_, value) => {
|
onChange={(_, value) => {
|
||||||
updateAnswer(currentQuestion.content.id, String(value));
|
updateAnswer(currentQuestion.content.id, String(value));
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
@ -8,7 +7,7 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@root/quizView";
|
||||||
|
|
||||||
import RadioCheck from "@ui_kit/RadioCheck";
|
import RadioCheck from "@ui_kit/RadioCheck";
|
||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
@ -23,13 +22,10 @@ type VariantProps = {
|
|||||||
export const Variant = ({ currentQuestion }: VariantProps) => {
|
export const Variant = ({ currentQuestion }: VariantProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
const { answer } =
|
||||||
|
answers.find(
|
||||||
useEffect(() => {
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
if (!answer) {
|
) ?? {};
|
||||||
updateAnswer(currentQuestion.content.id, currentQuestion.content.variants[0].id);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@ -37,13 +33,9 @@ export const Variant = ({ currentQuestion }: VariantProps) => {
|
|||||||
<Box sx={{ display: "flex" }}>
|
<Box sx={{ display: "flex" }}>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
value={currentQuestion.content.variants.findIndex(({ id }) => answer === id)}
|
value={currentQuestion.content.variants.findIndex(
|
||||||
onChange={({ target }) =>
|
({ id }) => answer === id
|
||||||
updateAnswer(
|
)}
|
||||||
currentQuestion.content.id,
|
|
||||||
currentQuestion.content.variants[Number(target.value)].id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
@ -53,10 +45,18 @@ export const Variant = ({ currentQuestion }: VariantProps) => {
|
|||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", flexDirection: "row", flexWrap: "wrap", width: "100%", gap: "20px", }}>
|
<Box
|
||||||
{currentQuestion.content.variants.map(({ id, answer }, index) => (
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
width: "100%",
|
||||||
|
gap: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
key={id}
|
key={variant.id}
|
||||||
sx={{
|
sx={{
|
||||||
margin: "0",
|
margin: "0",
|
||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
@ -65,14 +65,26 @@ export const Variant = ({ currentQuestion }: VariantProps) => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
maxWidth: "685px",
|
maxWidth: "685px",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
width: "100%"
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
value={index}
|
value={index}
|
||||||
labelPlacement="start"
|
labelPlacement="start"
|
||||||
control={
|
control={
|
||||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
||||||
}
|
}
|
||||||
label={answer}
|
label={variant.answer}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
updateAnswer(
|
||||||
|
currentQuestion.content.id,
|
||||||
|
currentQuestion.content.variants[index].id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (answer === currentQuestion.content.variants[index].id) {
|
||||||
|
deleteAnswer(currentQuestion.content.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
@ -8,7 +7,7 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@root/quizView";
|
||||||
|
|
||||||
import RadioCheck from "@ui_kit/RadioCheck";
|
import RadioCheck from "@ui_kit/RadioCheck";
|
||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
@ -22,28 +21,23 @@ type VarimgProps = {
|
|||||||
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
const { answer } =
|
||||||
const variant = currentQuestion.content.variants.find(({ id }) => answer === id);
|
answers.find(
|
||||||
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
useEffect(() => {
|
) ?? {};
|
||||||
if (!answer) {
|
const variant = currentQuestion.content.variants.find(
|
||||||
updateAnswer(currentQuestion.content.id, currentQuestion.content.variants[0].id);
|
({ id }) => answer === id
|
||||||
}
|
);
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5">{currentQuestion.title}</Typography>
|
||||||
<Box sx={{ display: "flex", marginTop: "20px", }}>
|
<Box sx={{ display: "flex", marginTop: "20px" }}>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
value={currentQuestion.content.variants.findIndex(({ id }) => answer === id)}
|
value={currentQuestion.content.variants.findIndex(
|
||||||
onChange={({ target }) =>
|
({ id }) => answer === id
|
||||||
updateAnswer(
|
)}
|
||||||
currentQuestion.content.id,
|
|
||||||
currentQuestion.content.variants[Number(target.value)].id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
@ -53,9 +47,9 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
{currentQuestion.content.variants.map(({ id, answer }, index) => (
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
key={id}
|
key={variant.id}
|
||||||
sx={{
|
sx={{
|
||||||
marginBottom: "15px",
|
marginBottom: "15px",
|
||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
@ -65,18 +59,41 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
}}
|
}}
|
||||||
value={index}
|
value={index}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
updateAnswer(
|
||||||
|
currentQuestion.content.id,
|
||||||
|
currentQuestion.content.variants[index].id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (answer === currentQuestion.content.variants[index].id) {
|
||||||
|
deleteAnswer(currentQuestion.content.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
control={
|
control={
|
||||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
||||||
}
|
}
|
||||||
label={answer}
|
label={variant.answer}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
{(variant?.extendedText || currentQuestion.content.back) && (
|
{(variant?.extendedText || currentQuestion.content.back) && (
|
||||||
<Box sx={{ maxWidth: "450px", width: "100%", height: "450px", border: "1px solid #E3E3E3", borderRadius: "12px", overflow: "hidden", }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
maxWidth: "450px",
|
||||||
|
width: "100%",
|
||||||
|
height: "450px",
|
||||||
|
border: "1px solid #E3E3E3",
|
||||||
|
borderRadius: "12px",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src={answer ? variant?.extendedText : currentQuestion.content.back}
|
src={
|
||||||
|
answer ? variant?.extendedText : currentQuestion.content.back
|
||||||
|
}
|
||||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import { nanoid } from "nanoid";
|
|||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
||||||
import { RequestQueue } from "../../utils/requestQueue";
|
import { RequestQueue } from "../../utils/requestQueue";
|
||||||
import { updateRootContentId } from "@root/quizes/actions"
|
import { updateRootContentId } from "@root/quizes/actions";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { QuestionsStore, useQuestionsStore } from "./store";
|
import { QuestionsStore, useQuestionsStore } from "./store";
|
||||||
import { withErrorBoundary } from "react-error-boundary";
|
import { withErrorBoundary } from "react-error-boundary";
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ const updateQuestionOrders = () => {
|
|||||||
const questions = useQuestionsStore.getState().questions.filter(
|
const questions = useQuestionsStore.getState().questions.filter(
|
||||||
(question): question is AnyTypedQuizQuestion => question.type !== null && question.type !== "result"
|
(question): question is AnyTypedQuizQuestion => question.type !== null && question.type !== "result"
|
||||||
);
|
);
|
||||||
console.log(questions)
|
console.log(questions);
|
||||||
|
|
||||||
questions.forEach((question, index) => {
|
questions.forEach((question, index) => {
|
||||||
updateQuestion(question.id, question => {
|
updateQuestion(question.id, question => {
|
||||||
@ -163,6 +163,10 @@ export const updateQuestion = (
|
|||||||
try {
|
try {
|
||||||
const response = await questionApi.edit(questionToEditQuestionRequest(q));
|
const response = await questionApi.edit(questionToEditQuestionRequest(q));
|
||||||
|
|
||||||
|
//Если мы делаем листочек веточкой - удаляем созданный к нему результ
|
||||||
|
const questionResult = useQuestionsStore.getState().questions.find(questionResult => questionResult.type === "result" && questionResult.content.rule.parentId === q.content.id);
|
||||||
|
if (questionResult && q.content.rule.default.length !== 0) deleteQuestion(questionResult.quizId);
|
||||||
|
deleteQuestion;
|
||||||
setQuestionBackendId(questionId, response.updated);
|
setQuestionBackendId(questionId, response.updated);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isAxiosCanceledError(error)) return;
|
if (isAxiosCanceledError(error)) return;
|
||||||
@ -307,13 +311,15 @@ export const createTypedQuestion = async (
|
|||||||
if (!question) return;
|
if (!question) return;
|
||||||
if (question.type !== null) throw new Error("Cannot upgrade already typed question");
|
if (question.type !== null) throw new Error("Cannot upgrade already typed question");
|
||||||
|
|
||||||
|
const untypedOrResultQuestionsLength = questions.filter(q => q.type === "result" || q.type === null).length;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const createdQuestion = await questionApi.create({
|
const createdQuestion = await questionApi.create({
|
||||||
quiz_id: question.quizId,
|
quiz_id: question.quizId,
|
||||||
type,
|
type,
|
||||||
title: question.title,
|
title: question.title,
|
||||||
description: question.description,
|
description: question.description,
|
||||||
page: questions.length,
|
page: questions.length - untypedOrResultQuestionsLength,
|
||||||
required: false,
|
required: false,
|
||||||
content: JSON.stringify(defaultQuestionByType[type].content),
|
content: JSON.stringify(defaultQuestionByType[type].content),
|
||||||
});
|
});
|
||||||
@ -335,11 +341,13 @@ export const createTypedQuestion = async (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const deleteQuestion = async (questionId: string, quizId: string) => requestQueue.enqueue(async () => {
|
export const deleteQuestion = async (questionId: string) => requestQueue.enqueue(async () => {
|
||||||
|
|
||||||
const question = useQuestionsStore.getState().questions.find(q => q.id === questionId);
|
const question = useQuestionsStore.getState().questions.find(q => q.id === questionId);
|
||||||
if (!question) return;
|
if (!question) return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (question.type === null) {
|
if (question.type === null) {
|
||||||
removeQuestion(questionId);
|
removeQuestion(questionId);
|
||||||
return;
|
return;
|
||||||
@ -347,46 +355,10 @@ export const deleteQuestion = async (questionId: string, quizId: string) => requ
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await questionApi.delete(question.backendId);
|
await questionApi.delete(question.backendId);
|
||||||
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
|
|
||||||
updateRootContentId(quizId, "")
|
|
||||||
clearRuleForAll()
|
|
||||||
} 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 = ""
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
//чистим rule родителя
|
|
||||||
const parentQuestion = getQuestionByContentId(question.content.rule.parentId)
|
|
||||||
const newRule = {}
|
|
||||||
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== question.content.id) //удаляем условия перехода от родителя к этому вопросу
|
|
||||||
newRule.parentId = parentQuestion.content.rule.parentId
|
|
||||||
newRule.default = questions.filter((q) => {
|
|
||||||
return q.content.rule.parentId === question.content.rule.parentId && q.content.id !== question.content.id
|
|
||||||
})[0]?.content.id || ""
|
|
||||||
//Если этот вопрос был дефолтным у родителя - чистим дефолт
|
|
||||||
//Смотрим можем ли мы заменить id на один из main
|
|
||||||
|
|
||||||
updateQuestion(question.content.rule.parentId, (PQ) => {
|
|
||||||
PQ.content.rule = newRule
|
|
||||||
})
|
|
||||||
}
|
|
||||||
removeQuestion(questionId);
|
removeQuestion(questionId);
|
||||||
|
|
||||||
|
updateQuestionOrders();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
devlog("Error deleting question", error);
|
devlog("Error deleting question", error);
|
||||||
enqueueSnackbar("Не удалось удалить вопрос");
|
enqueueSnackbar("Не удалось удалить вопрос");
|
||||||
@ -400,8 +372,8 @@ export const copyQuestion = async (questionId: string, quizId: number) => reques
|
|||||||
const frontId = nanoid();
|
const frontId = nanoid();
|
||||||
if (question.type === null) {
|
if (question.type === null) {
|
||||||
const copiedQuestion = structuredClone(question);
|
const copiedQuestion = structuredClone(question);
|
||||||
copiedQuestion.id = frontId
|
copiedQuestion.id = frontId;
|
||||||
copiedQuestion.content.id = frontId
|
copiedQuestion.content.id = frontId;
|
||||||
|
|
||||||
setProducedState(state => {
|
setProducedState(state => {
|
||||||
state.questions.push(copiedQuestion);
|
state.questions.push(copiedQuestion);
|
||||||
@ -421,7 +393,7 @@ export const copyQuestion = async (questionId: string, quizId: number) => reques
|
|||||||
copiedQuestion.backendId = newQuestionId;
|
copiedQuestion.backendId = newQuestionId;
|
||||||
copiedQuestion.id = frontId;
|
copiedQuestion.id = frontId;
|
||||||
copiedQuestion.content.id = frontId;
|
copiedQuestion.content.id = frontId;
|
||||||
copiedQuestion.content.rule = { main: [], parentId: "", default: "" };
|
copiedQuestion.content.rule = { main: [], parentId: "", default: "", children: [] };
|
||||||
|
|
||||||
setProducedState(state => {
|
setProducedState(state => {
|
||||||
state.questions.push(copiedQuestion);
|
state.questions.push(copiedQuestion);
|
||||||
@ -469,41 +441,41 @@ export const updateDragQuestionContentId = (contentId?: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const clearRuleForAll = () => {
|
export const clearRuleForAll = () => {
|
||||||
const { questions } = useQuestionsStore.getState()
|
const { questions } = useQuestionsStore.getState();
|
||||||
questions.forEach(question => {
|
questions.forEach(question => {
|
||||||
if (question.type !== null && (question.content.rule.main.length > 0 || question.content.rule.default.length > 0 || question.content.rule.parentId.length > 0)) {
|
if (question.type !== null && (question.content.rule.main.length > 0 || question.content.rule.default.length > 0 || question.content.rule.parentId.length > 0)) {
|
||||||
updateQuestion(question.content.id, question => {
|
updateQuestion(question.content.id, question => {
|
||||||
question.content.rule.parentId = ""
|
question.content.rule.parentId = "";
|
||||||
question.content.rule.main = []
|
question.content.rule.main = [];
|
||||||
question.content.rule.default = ""
|
question.content.rule.default = "";
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const updateOpenBranchingPanel = (value: boolean) => useQuestionsStore.setState({ openBranchingPanel: value });
|
export const updateOpenBranchingPanel = (value: boolean) => useQuestionsStore.setState({ openBranchingPanel: value });
|
||||||
|
|
||||||
|
|
||||||
let UDTOABM: ReturnType<typeof setTimeout>;
|
let UDTOABM: ReturnType<typeof setTimeout>;
|
||||||
export const updateDesireToOpenABranchingModal = (contentId: string) => {
|
export const updateDesireToOpenABranchingModal = (contentId: string) => {
|
||||||
useQuestionsStore.setState({ desireToOpenABranchingModal: contentId })
|
useQuestionsStore.setState({ desireToOpenABranchingModal: contentId });
|
||||||
clearTimeout(UDTOABM)
|
clearTimeout(UDTOABM);
|
||||||
UDTOABM = setTimeout(() => {
|
UDTOABM = setTimeout(() => {
|
||||||
useQuestionsStore.setState({ desireToOpenABranchingModal: null })
|
useQuestionsStore.setState({ desireToOpenABranchingModal: null });
|
||||||
}, 7000)
|
}, 7000);
|
||||||
}
|
};
|
||||||
export const clearDesireToOpenABranchingModal = () => {
|
export const clearDesireToOpenABranchingModal = () => {
|
||||||
useQuestionsStore.setState({ desireToOpenABranchingModal: null })
|
useQuestionsStore.setState({ desireToOpenABranchingModal: null });
|
||||||
}
|
};
|
||||||
export const updateEditSomeQuestion = (contentId?: string) => {
|
export const updateEditSomeQuestion = (contentId?: string) => {
|
||||||
useQuestionsStore.setState({ editSomeQuestion: contentId === undefined ? null : contentId })
|
useQuestionsStore.setState({ editSomeQuestion: contentId === undefined ? null : contentId });
|
||||||
}
|
};
|
||||||
|
|
||||||
export const createFrontResult = (quizId: number, parentContentId?: string) => setProducedState(state => {
|
export const createFrontResult = (quizId: number, parentContentId?: string) => setProducedState(state => {
|
||||||
const frontId = nanoid()
|
const frontId = nanoid();
|
||||||
const content = JSON.parse(JSON.stringify(defaultQuestionByType["result"].content))
|
const content = JSON.parse(JSON.stringify(defaultQuestionByType["result"].content));
|
||||||
content.id = frontId
|
content.id = frontId;
|
||||||
if (parentContentId) content.rule.parentId = parentContentId
|
if (parentContentId) content.rule.parentId = parentContentId;
|
||||||
state.questions.push({
|
state.questions.push({
|
||||||
id: frontId,
|
id: frontId,
|
||||||
quizId,
|
quizId,
|
||||||
|
|||||||
@ -41,3 +41,12 @@ export const updateAnswer = (
|
|||||||
|
|
||||||
useQuizViewStore.setState({ answers });
|
useQuizViewStore.setState({ answers });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const deleteAnswer = (questionId: string) => {
|
||||||
|
const answers = [...useQuizViewStore.getState().answers];
|
||||||
|
const filteredItems = answers.filter(
|
||||||
|
(answer) => questionId !== answer.questionId
|
||||||
|
);
|
||||||
|
|
||||||
|
useQuizViewStore.setState({ answers: filteredItems });
|
||||||
|
};
|
||||||
|
|||||||
@ -82,17 +82,15 @@ export default function QuizPreviewLayout() {
|
|||||||
{quiz.config.startpage.description}
|
{quiz.config.startpage.description}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box>
|
<Box>
|
||||||
{quiz.config.startpage.button && (
|
<Button
|
||||||
<Button
|
variant="contained"
|
||||||
variant="contained"
|
sx={{
|
||||||
sx={{
|
fontSize: "16px",
|
||||||
fontSize: "16px",
|
padding: "10px 15px",
|
||||||
padding: "10px 15px",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{quiz.config.startpage.button ? quiz.config.startpage.button : "Пройти тест"}
|
||||||
{quiz.config.startpage.button}
|
</Button>
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user