Merge branch 'dev' into backend-integration

This commit is contained in:
Nastya 2023-12-04 23:41:44 +03:00
commit f07d7ab3e2
27 changed files with 20249 additions and 842 deletions

19001
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

@ -30,7 +30,6 @@ async function getQuestionList(body?: Partial<GetQuestionListRequest>) {
} }
function editQuestion(body: EditQuestionRequest, signal?: AbortSignal) { function editQuestion(body: EditQuestionRequest, signal?: AbortSignal) {
console.log("`${baseUrl}/question/edit` start")
return makeRequest<EditQuestionRequest, EditQuestionResponse>({ return makeRequest<EditQuestionRequest, EditQuestionResponse>({
url: `${baseUrl}/question/edit`, url: `${baseUrl}/question/edit`,
body, body,

@ -1,7 +1,7 @@
import { MessageIcon } from "@icons/messagIcon"; import { MessageIcon } from "@icons/messagIcon";
import { PointsIcon } from "@icons/questionsPage/PointsIcon"; import { PointsIcon } from "@icons/questionsPage/PointsIcon";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon"; import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import TextareaAutosize from "@mui/base/TextareaAutosize"; import {TextareaAutosize} from "@mui/base/TextareaAutosize";
import { import {
Box, Box,
FormControl, FormControl,

@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useLayoutEffect, useRef, useState } from "react";
import Cytoscape from "cytoscape"; import Cytoscape from "cytoscape";
import CytoscapeComponent from "react-cytoscapejs"; import CytoscapeComponent from "react-cytoscapejs";
import popper from "cytoscape-popper"; import popper from "cytoscape-popper";
@ -122,16 +122,19 @@ export const CsComponent = ({
const crossesContainer = useRef<HTMLDivElement | null>(null); const crossesContainer = useRef<HTMLDivElement | null>(null);
const gearsContainer = useRef<HTMLDivElement | null>(null); const gearsContainer = useRef<HTMLDivElement | null>(null);
useLayoutEffect(() => {
updateOpenedModalSettingsId()
}, [])
useEffect(() => { useEffect(() => {
if (modalQuestionTargetContentId.length !== 0 && modalQuestionParentContentId.length !== 0) { if (modalQuestionTargetContentId.length !== 0 && modalQuestionParentContentId.length !== 0) {
console.log("был выбран вопрос " + modalQuestionTargetContentId) addNode({ parentNodeContentId: modalQuestionParentContentId, targetNodeContentId: modalQuestionTargetContentId })
addNode({ parentNodeContentId:modalQuestionParentContentId, targetNodeContentId:modalQuestionTargetContentId })
} }
setModalQuestionParentContentId("")
setModalQuestionTargetContentId("")
}, [modalQuestionTargetContentId]) }, [modalQuestionTargetContentId])
const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => { const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => {
console.log("dragQuestionContentId " + dragQuestionContentId)
const cy = cyRef?.current const cy = cyRef?.current
const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length
//если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа //если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа
@ -139,7 +142,7 @@ export const CsComponent = ({
if (Object.keys(targetQuestion).length !== 0 && Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) { if (Object.keys(targetQuestion).length !== 0 && Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) {
clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren }) clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
cy?.data('changed',true) cy?.data('changed', true)
cy?.add([ cy?.add([
{ {
data: { data: {
@ -161,8 +164,6 @@ export const CsComponent = ({
} }
const clearDataAfterAddNode = ({ parentNodeContentId, targetQuestion, parentNodeChildren }: { parentNodeContentId: string, targetQuestion: AnyTypedQuizQuestion, parentNodeChildren: number }) => { const clearDataAfterAddNode = ({ parentNodeContentId, targetQuestion, parentNodeChildren }: { parentNodeContentId: string, targetQuestion: AnyTypedQuizQuestion, parentNodeChildren: number }) => {
console.log("записываю на бек ид родителя")
console.log({ parentNodeContentId, targetQuestion, parentNodeChildren })
//предупреждаем добавленный вопрос о том, кто его родитель //предупреждаем добавленный вопрос о том, кто его родитель
updateQuestion(targetQuestion.content.id, question => { updateQuestion(targetQuestion.content.id, question => {
question.content.rule.parentId = parentNodeContentId question.content.rule.parentId = parentNodeContentId
@ -170,8 +171,8 @@ export const CsComponent = ({
}) })
//Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления //Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления
if (parentNodeChildren > 1) { if (parentNodeChildren >= 1) {
updateOpenedModalSettingsId(parentNodeContentId) updateOpenedModalSettingsId(targetQuestion.content.id)
} else { } else {
//Если ребёнок первый - добавляем его родителю как дефолтный //Если ребёнок первый - добавляем его родителю как дефолтный
updateQuestion(parentNodeContentId, question => question.content.rule.default = targetQuestion.content.id) updateQuestion(parentNodeContentId, question => question.content.rule.default = targetQuestion.content.id)
@ -179,9 +180,8 @@ export const CsComponent = ({
} }
const removeNode = ({ targetNodeContentId }: { targetNodeContentId: string }) => { const removeNode = ({ targetNodeContentId }: { targetNodeContentId: string }) => {
const deleteNodes = [] as string[] const deleteNodes = [] as string[]
const deleteEdges:any = [] const deleteEdges: any = []
console.log("remove")
const cy = cyRef?.current const cy = cyRef?.current
const findChildrenToDelete = (node) => { const findChildrenToDelete = (node) => {
@ -205,60 +205,51 @@ export const CsComponent = ({
const targetQuestion = getQuestionByContentId(targetNodeContentId) const targetQuestion = getQuestionByContentId(targetNodeContentId)
if (targetQuestion.content.rule.parentId === "root" && quiz) { if (targetQuestion.content.rule.parentId === "root" && quiz) {
console.log("click ROOT") updateRootContentId(quiz?.id, "")
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.default = "" question.content.rule.default = ""
}) })
updateRootContentId(quiz?.id, false)
} else { } else {
console.log("click not ROOT")
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) {
clearDataAfterRemoveNode({ targetQuestionContentId: targetNodeContentId, parentQuestionContentId }) clearDataAfterRemoveNode({ targetQuestionContentId: targetNodeContentId, parentQuestionContentId })
cy?.remove(cy?.$('#' + targetNodeContentId)).layout(lyopts).run() cy?.remove(cy?.$('#' + targetNodeContentId)).layout(lyopts).run()
} }
console.log(deleteNodes, deleteEdges)
//После всех манипуляций удаляем грани из CS и ноды из бекенда
deleteNodes.forEach((nodeId) => {//Ноды
cy?.remove(cy?.$("#" + nodeId))
removeButtons(nodeId)
updateQuestion(nodeId, question => {
question.content.rule.parentId = ""
question.content.rule.main = []
question.content.rule.default = ""
})
})
deleteEdges.forEach((edge:any) => {//Грани
cy?.remove(edge)
})
removeButtons(targetNodeContentId)
cy?.data('changed',true)
cy?.layout(lyopts).run()
} }
//После всех манипуляций удаляем грани из CS и ноды из бекенда
deleteNodes.forEach((nodeId) => {//Ноды
cy?.remove(cy?.$("#" + nodeId))
removeButtons(nodeId)
updateQuestion(nodeId, question => {
question.content.rule.parentId = ""
question.content.rule.main = []
question.content.rule.default = ""
})
})
deleteEdges.forEach((edge: any) => {//Грани
cy?.remove(edge)
})
removeButtons(targetNodeContentId)
cy?.data('changed', true)
cy?.layout(lyopts).run()
} }
const clearDataAfterRemoveNode = ({ targetQuestionContentId, parentQuestionContentId }: { targetQuestionContentId: string, parentQuestionContentId: string }) => { const clearDataAfterRemoveNode = ({ targetQuestionContentId, parentQuestionContentId }: { targetQuestionContentId: string, parentQuestionContentId: string }) => {
console.log({ targetQuestionContentId, parentQuestionContentId })
updateQuestion(targetQuestionContentId, question => { updateQuestion(targetQuestionContentId, 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 = ""
}) })
updateQuestion(parentQuestionContentId, question => { updateQuestion(parentQuestionContentId, question => {
console.log("parent default " + question.content.rule.default) //Заменяем id удаляемого вопроса либо на id одного из оставшихся, либо на пустую строку
console.log("target ID " + targetQuestionContentId)
console.log(question.content.rule.default === targetQuestionContentId)
if (question.content.rule.default === targetQuestionContentId) question.content.rule.default = "" if (question.content.rule.default === targetQuestionContentId) question.content.rule.default = ""
}) })
@ -282,22 +273,20 @@ export const CsComponent = ({
const readyLO = (e) => { const readyLO = (e) => {
console.log(e.cy.data('firstNode'),"SKEEER",e.cy.data('changed'))
if (e.cy.data('firstNode') === 'nonroot') { if (e.cy.data('firstNode') === 'nonroot') {
e.cy.data('firstNode','root') e.cy.data('firstNode', 'root')
e.cy.nodes().sort((a,b) => (a.data('root')?1:-1)).layout(lyopts).run() e.cy.nodes().sort((a, b) => (a.data('root') ? 1 : -1)).layout(lyopts).run()
} else {
} else {
e.cy.data('changed', false)
e.cy.removeData('firstNode')
} e.cy.data('changed', false)
e.cy.removeData('firstNode')
}
//удаляем иконки //удаляем иконки
e.cy.nodes().forEach((ele: any) => { e.cy.nodes().forEach((ele: any) => {
const data = ele.data() const data = ele.data()
console.log(data)
data.id && removeButtons(data.id); data.id && removeButtons(data.id);
}) })
initialPopperIcons(e) initialPopperIcons(e)
@ -307,11 +296,9 @@ console.log(e.cy.data('firstNode'),"SKEEER",e.cy.data('changed'))
name: 'preset', name: 'preset',
positions: (e) => { positions: (e) => {
console.log('BBBBBBBBBBBBBBB', e.cy().data('changed'))
if (!e.cy().data('changed')) { if (!e.cy().data('changed')) {
return e.data('oldPos') return e.data('oldPos')
} else {e.removeData('oldPos')} } else { e.removeData('oldPos') }
console.log('POSITIIIIIIIONS')
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
@ -319,7 +306,7 @@ console.log(e.cy.data('firstNode'),"SKEEER",e.cy.data('changed'))
if (incomming.length === 0) { if (incomming.length === 0) {
if (e.cy().data('firstNode') === undefined) if (e.cy().data('firstNode') === undefined)
e.cy().data('firstNode','root') e.cy().data('firstNode', 'root')
e.data('root', true) e.data('root', true)
const children = e.cy().edges(`[source="${id}"]`).targets() const children = e.cy().edges(`[source="${id}"]`).targets()
e.data('layer', layer) e.data('layer', layer)
@ -331,14 +318,14 @@ console.log(e.cy.data('firstNode'),"SKEEER",e.cy.data('changed'))
while (queue.length) { while (queue.length) {
const task = queue.pop() const task = queue.pop()
task.task.data('layer', task.layer) task.task.data('layer', task.layer)
console.log('SAMSHIIIIT',task.layer,task.task.data().layer) task.task.removeData('subtreeWidth')
const children = e.cy().edges(`[source="${task.task.id()}"]`).targets() const children = e.cy().edges(`[source="${task.task.id()}"]`).targets()
task.task.data('children', children.length) task.task.data('children', children.length)
if (children.length !== 0) { if (children.length !== 0) {
children.forEach(n => queue.push({ task: n, layer: task.layer + 1 })) children.forEach(n => queue.push({ task: n, layer: task.layer + 1 }))
} }
} }
queue.push({ parent: e, children: children.targets() }) queue.push({ parent: e, children: children })
while (queue.length) { while (queue.length) {
const task = queue.pop() const task = queue.pop()
if (task.children.length === 0) { if (task.children.length === 0) {
@ -359,23 +346,20 @@ console.log(e.cy.data('firstNode'),"SKEEER",e.cy.data('changed'))
task?.parent.data('subtreeWidth', task.children.reduce((p, n) => p + n.data('subtreeWidth'), 0)) task?.parent.data('subtreeWidth', task.children.reduce((p, n) => p + n.data('subtreeWidth'), 0))
} }
const pos = { x: 0, y: 0 } const pos = { x: 0, y: 0 }
console.log(e.data())
e.data('oldPos', pos) e.data('oldPos', pos)
return pos return pos
} else { } else {
console.log(e.cy().data('firstNode')) if (e.cy().data('firstNode') !== 'root') {
if (e.cy().data('firstNode') !== 'root') { e.cy().data('firstNode', 'nonroot')
e.cy().data('firstNode','nonroot') return { x: 0, y: 0 }
return {x:0,y:0}
} }
if (e.cy().data('firstNode') === undefined) if (e.cy().data('firstNode') === undefined)
e.cy().data('firstNode','nonroot') e.cy().data('firstNode', 'nonroot')
const parent = e.cy().edges(`[target="${e.id()}"]`)[0].source() const parent = e.cy().edges(`[target="${e.id()}"]`)[0].source()
const wing = (parent.data('children') === 1) ? 0 : parent.data('subtreeWidth') / 2 + 50 const wing = (parent.data('children') === 1) ? 0 : parent.data('subtreeWidth') / 2 + 50
const lastOffset = parent.data('lastChild') const lastOffset = parent.data('lastChild')
const step = wing * 2 / (parent.data('children') - 1) const step = wing * 2 / (parent.data('children') - 1)
//e.removeData('subtreeWidth') //e.removeData('subtreeWidth')
console.log('AAAAAAAAAAa', e.data(), lastOffset, step, parent.position().y, wing)
if (lastOffset !== undefined) { if (lastOffset !== undefined) {
parent.data('lastChild', lastOffset + step) parent.data('lastChild', lastOffset + step)
const pos = { x: 250 * e.data('layer'), y: (lastOffset + step) } const pos = { x: 250 * e.data('layer'), y: (lastOffset + step) }
@ -386,7 +370,7 @@ if (e.cy().data('firstNode') !== 'root') {
const pos = { x: 250 * e.data('layer'), y: (parent.position().y - wing) } const pos = { x: 250 * e.data('layer'), y: (parent.position().y - wing) }
e.data('oldPos', pos) e.data('oldPos', pos)
return pos return pos
} }
} }
}, // 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)
@ -502,7 +486,7 @@ if (e.cy().data('firstNode') !== 'root') {
layoutElement.classList.add("popper-layout"); layoutElement.classList.add("popper-layout");
layoutElement.setAttribute("data-id", item.id()); layoutElement.setAttribute("data-id", item.id());
layoutElement.addEventListener("mouseup", () => { layoutElement.addEventListener("mouseup", () => {
//Узнаём грани, идущие от этой ноды //Узнаём грани, идущие от этой ноды
setModalQuestionParentContentId(item.id()) setModalQuestionParentContentId(item.id())
setOpenedModalQuestions(true) setOpenedModalQuestions(true)
}); });
@ -533,7 +517,7 @@ if (e.cy().data('firstNode') !== 'root') {
plusElement.addEventListener("mouseup", () => { plusElement.addEventListener("mouseup", () => {
setStartCreate(node.id()); setStartCreate(node.id());
}); });
plusesContainer.current?.appendChild(plusElement); plusesContainer.current?.appendChild(plusElement);
return plusElement; return plusElement;
@ -594,7 +578,6 @@ if (e.cy().data('firstNode') !== 'root') {
gearElement.style.zIndex = "1" gearElement.style.zIndex = "1"
gearsContainer.current?.appendChild(gearElement); gearsContainer.current?.appendChild(gearElement);
gearElement.addEventListener("mouseup", (e) => { gearElement.addEventListener("mouseup", (e) => {
console.log(item.id())
updateOpenedModalSettingsId(item.id()) updateOpenedModalSettingsId(item.id())
}); });
@ -672,23 +655,27 @@ if (e.cy().data('firstNode') !== 'root') {
return ( return (
<> <>
<CytoscapeComponent <CytoscapeComponent
wheelSensitivity={0.1} wheelSensitivity={0.1}
elements={[]} elements={[]}
// elements={createGraphElements(tree, quiz)} // elements={createGraphElements(tree, quiz)}
style={{ height: "480px", background: "#F2F3F7" }} style={{ height: "480px", background: "#F2F3F7" }}
stylesheet={stylesheet} stylesheet={stylesheet}
layout={(lyopts)} layout={(lyopts)}
cy={(cy) => { cy={(cy) => {
cyRef.current = cy; cyRef.current = cy;
}} }}
/> />
<button onClick={() => { {/* <button onClick={() => {
console.log("ELEMENTS____________________________") console.log("NODES____________________________")
cyRef.current?.elements().forEach((ele:any) => { cyRef.current?.elements().forEach((ele: any) => {
console.log(ele.data()) console.log(ele.data())
}) })
}}>elements</button> }}>nodes</button>
<button onClick={() => {
console.log("ELEMENTS____________________________")
console.log(questions)
}}>elements</button> */}
</> </>
); );
}; };

@ -2,8 +2,8 @@ import { Box } from "@mui/material"
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { updateDragQuestionContentId, updateQuestion } from "@root/questions/actions" import { updateDragQuestionContentId, updateQuestion } from "@root/questions/actions"
import { updateRootContentId } from "@root/quizes/actions" import { updateRootContentId } from "@root/quizes/actions"
import { useCurrentQuiz } from "@root/quizes/hooks"
import { useQuestionsStore } from "@root/questions/store" import { useQuestionsStore } from "@root/questions/store"
import { useCurrentQuiz } from "@root/quizes/hooks";
import { enqueueSnackbar } from "notistack"; import { enqueueSnackbar } from "notistack";
interface Props { interface Props {
@ -11,17 +11,14 @@ interface Props {
modalQuestionTargetContentId: string; modalQuestionTargetContentId: string;
} }
export const FirstNodeField = ({ setOpenedModalQuestions, modalQuestionTargetContentId }: Props) => { export const FirstNodeField = ({ setOpenedModalQuestions, modalQuestionTargetContentId }: Props) => {
const { dragQuestionContentId } = useQuestionsStore()
const Container = useRef<HTMLDivElement | null>(null);
const quiz = useCurrentQuiz(); const quiz = useCurrentQuiz();
console.log(dragQuestionContentId) const { dragQuestionContentId, questions } = useQuestionsStore()
const Container = useRef<HTMLDivElement | null>(null);
const modalOpen = () => setOpenedModalQuestions(true) const modalOpen = () => setOpenedModalQuestions(true)
const newRootNode = () => { const newRootNode = () => {
if (quiz) { if (quiz) {
console.log(dragQuestionContentId)
if (dragQuestionContentId) { if (dragQuestionContentId) {
updateRootContentId(quiz?.id, dragQuestionContentId) updateRootContentId(quiz?.id, dragQuestionContentId)
updateQuestion(dragQuestionContentId, (question) => question.content.rule.parentId = "root") updateQuestion(dragQuestionContentId, (question) => question.content.rule.parentId = "root")
@ -29,6 +26,7 @@ export const FirstNodeField = ({ setOpenedModalQuestions, modalQuestionTargetCon
} else { } else {
enqueueSnackbar("Нет информации о взятом опроснике") enqueueSnackbar("Нет информации о взятом опроснике")
} }
} }
useEffect(() => { useEffect(() => {
@ -53,6 +51,7 @@ export const FirstNodeField = ({ setOpenedModalQuestions, modalQuestionTargetCon
}, [modalQuestionTargetContentId]) }, [modalQuestionTargetContentId])
return ( return (
<Box <Box
ref={Container} ref={Container}

@ -15,11 +15,9 @@ interface Edges {
} }
export const storeToNodes = (questions: AnyTypedQuizQuestion[]) => { export const storeToNodes = (questions: AnyTypedQuizQuestion[]) => {
console.log(questions)
const nodes: Nodes[] = [] const nodes: Nodes[] = []
const edges: Edges[] = [] const edges: Edges[] = []
questions.forEach((question) => { questions.forEach((question) => {
console.log(question)
if (question.content.rule.parentId) { if (question.content.rule.parentId) {
nodes.push({data: { nodes.push({data: {
id: question.content.id, id: question.content.id,
@ -38,6 +36,5 @@ export const storeToNodes = (questions: AnyTypedQuizQuestion[]) => {
}}) }})
} }
}) })
console.log([...nodes, ...edges])
return [...nodes, ...edges]; return [...nodes, ...edges];
} }

@ -14,6 +14,8 @@ export const BranchingMap = () => {
const [modalQuestionTargetContentId, setModalQuestionTargetContentId] = useState<string>("") const [modalQuestionTargetContentId, setModalQuestionTargetContentId] = useState<string>("")
const [openedModalQuestions, setOpenedModalQuestions] = useState<boolean>(false) const [openedModalQuestions, setOpenedModalQuestions] = useState<boolean>(false)
return ( return (
<Box <Box
id="cytoscape-container" id="cytoscape-container"

@ -32,34 +32,32 @@ export default function BranchingQuestions() {
const { openedModalSettingsId } = useQuestionsStore(); const { openedModalSettingsId } = useQuestionsStore();
const [targetQuestion, setTargetQuestion] = useState<AnyTypedQuizQuestion | null>(getQuestionById(openedModalSettingsId) || getQuestionByContentId(openedModalSettingsId)) const [targetQuestion, setTargetQuestion] = useState<AnyTypedQuizQuestion | null>(getQuestionById(openedModalSettingsId) || getQuestionByContentId(openedModalSettingsId))
console.log(targetQuestion)
const [parentQuestion, setParentQuestion] = useState<AnyTypedQuizQuestion | null>(getQuestionByContentId(targetQuestion?.content.rule.parentId)) const [parentQuestion, setParentQuestion] = useState<AnyTypedQuizQuestion | null>(getQuestionByContentId(targetQuestion?.content.rule.parentId))
console.log(parentQuestion)
useLayoutEffect(() => { useLayoutEffect(() => {
if (parentQuestion.content.rule.main.length === 0) updateQuestion(parentQuestion.id, question => question.content.rule.main.push({ if (parentQuestion === null) return
next: targetQuestion.content.id, if (parentQuestion.content.rule.main.length === 0) {
or: true, let mutate = JSON.parse(JSON.stringify(parentQuestion))
rules: [{ mutate.content.rule.main = [{
question: parentQuestion.content.id, next: targetQuestion.content.id,
answers: [] or: true,
rules: [{
question: parentQuestion.content.id,
answers: []
}]
}] }]
})) setParentQuestion(mutate)
}
}) })
if (targetQuestion === null || parentQuestion === null) { if (targetQuestion === null || parentQuestion === null) {
console.log(openedModalSettingsId)
enqueueSnackbar("Невозможно найти данные ветвления для этого вопроса") enqueueSnackbar("Невозможно найти данные ветвления для этого вопроса")
return <></> return <></>
} }
const saveData = () => { const saveData = () => {
console.log(parentQuestion)
if (parentQuestion !== null) { if (parentQuestion !== null) {
updateQuestion(parentQuestion.content.id, question => question.content = parentQuestion.content) updateQuestion(parentQuestion.content.id, question => question.content = parentQuestion.content)
} }
handleClose() handleClose()
@ -148,11 +146,11 @@ export default function BranchingQuestions() {
marginBottom: "10px", marginBottom: "10px",
cursor: "pointer" cursor: "pointer"
}} }}
onClick={() => { onClick={() => {
const mutate = JSON.parse(JSON.stringify(parentQuestion)) const mutate = JSON.parse(JSON.stringify(parentQuestion))
mutate.content.rule.main.push(createBranchingRuleMain(targetQuestion.content.id, parentQuestion.content.id)) mutate.content.rule.main.push(createBranchingRuleMain(targetQuestion.content.id, parentQuestion.content.id))
setParentQuestion(mutate) setParentQuestion(mutate)
}} }}
> >
Добавить условие Добавить условие
</Link> </Link>
@ -163,7 +161,7 @@ export default function BranchingQuestions() {
sx={{ sx={{
margin: 0 margin: 0
}} }}
checked={parentQuestion.content.rule.default === targetQuestion.id} checked={parentQuestion.content.rule.default === targetQuestion.content.id}
onClick={() => { onClick={() => {
let mutate = JSON.parse(JSON.stringify(parentQuestion)) let mutate = JSON.parse(JSON.stringify(parentQuestion))

@ -1,4 +1,4 @@
import { Box, MenuItem, FormControl, Checkbox, FormControlLabel, Radio, RadioGroup, Typography, useTheme, Select, Chip, IconButton, TextField } from "@mui/material" import { Box, MenuItem, FormControl, Checkbox, FormControlLabel, Radio, RadioGroup, Typography, useTheme, Select, useMediaQuery, IconButton, TextField } from "@mui/material"
import RadioCheck from "@ui_kit/RadioCheck" import RadioCheck from "@ui_kit/RadioCheck"
import RadioIcon from "@ui_kit/RadioIcon" import RadioIcon from "@ui_kit/RadioIcon"
import { QuizQuestionBase } from "model/questionTypes/shared" import { QuizQuestionBase } from "model/questionTypes/shared"
@ -8,7 +8,10 @@ import { useQuestionsStore } from "@root/questions/store";
import { updateQuestion, getQuestionById } from "@root/questions/actions"; import { updateQuestion, getQuestionById } from "@root/questions/actions";
import { AnyTypedQuizQuestion } from "../../../model/questionTypes/shared" import { AnyTypedQuizQuestion } from "../../../model/questionTypes/shared"
import { SelectChangeEvent } from '@mui/material/Select'; import { SelectChangeEvent } from '@mui/material/Select';
import CalendarIcon from "@icons/CalendarIcon";
import { DatePicker } from "@mui/x-date-pickers";
import * as dayjs from 'dayjs'
import { TimePicker } from '@mui/x-date-pickers/TimePicker';
import InfoIcon from "@icons/Info"; import InfoIcon from "@icons/Info";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon"; import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
@ -26,7 +29,6 @@ interface Props {
//Этот компонент вызывается 1 раз на каждое условие родителя для перехода к этому вопросу. Поэтому для изменения стора мы знаем индекс //Этот компонент вызывается 1 раз на каждое условие родителя для перехода к этому вопросу. Поэтому для изменения стора мы знаем индекс
export const TypeSwitch = ({ parentQuestion, targetQuestion, ruleIndex, setParentQuestion }: Props) => { export const TypeSwitch = ({ parentQuestion, targetQuestion, ruleIndex, setParentQuestion }: Props) => {
console.log("index " + ruleIndex)
switch (parentQuestion.type) { switch (parentQuestion.type) {
// case 'nonselected': // case 'nonselected':
@ -45,11 +47,10 @@ export const TypeSwitch = ({ parentQuestion, targetQuestion, ruleIndex, setParen
) )
break; break;
case "date": case "date":
// return <DateInputsType targetQuestion={targetQuestion} parentQuestion={parentQuestion} ruleIndex={ruleIndex} setParentQuestion={setParentQuestion} /> return <DateInputsType targetQuestion={targetQuestion} parentQuestion={parentQuestion} ruleIndex={ruleIndex} setParentQuestion={setParentQuestion} />
return <></>
break; break;
case "number": case "number":
return <NumberInputsType targetQuestion={targetQuestion} parentQuestion={parentQuestion} ruleIndex={ruleIndex} setParentQuestion={setParentQuestion} /> return <NumberInputsType targetQuestion={targetQuestion} parentQuestion={parentQuestion} ruleIndex={ruleIndex} setParentQuestion={setParentQuestion} />
@ -57,7 +58,6 @@ export const TypeSwitch = ({ parentQuestion, targetQuestion, ruleIndex, setParen
break; break;
case "page": case "page":
case "date":
return <BlockRule text={"У такого родителя может быть только один потомок"} /> return <BlockRule text={"У такого родителя может быть только один потомок"} />
break; break;
@ -96,7 +96,6 @@ export const BlockRule = ({ text }: { text: string }) => {
const SelectorType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQuestion }: Props) => { const SelectorType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQuestion }: Props) => {
const theme = useTheme(); const theme = useTheme();
const quizId = Number(useParams().quizId); const quizId = Number(useParams().quizId);
console.log(parentQuestion)
return ( return (
<Box <Box
@ -148,15 +147,12 @@ const SelectorType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQues
</Box> </Box>
<Select <Select
multiple multiple
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers || []} value={parentQuestion.content?.rule?.main[ruleIndex]?.rules[0]?.answers || []}
onChange={(event: SelectChangeEvent) => { onChange={(event: SelectChangeEvent) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion)) let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion))
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = (event.target as HTMLSelectElement).value newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = (event.target as HTMLSelectElement).value
setParentQuestion(newParentQuestion) setParentQuestion(newParentQuestion)
console.log(parentQuestion.content.rule.main[ruleIndex])
console.log(parentQuestion.content.rule.main[ruleIndex].rules[0])
console.log(parentQuestion.content.rule.main[ruleIndex].rules[0].answers)
}} }}
sx={{ sx={{
width: "100%", width: "100%",
@ -209,8 +205,257 @@ const SelectorType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQues
) )
} }
const DateInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQuestion }: Props) => { const DateInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQuestion }: Props) => {
const theme = useTheme();
const upLg = useMediaQuery(theme.breakpoints.up("md"));
return <></> const time = dayjs(new Date)
const [firstDate, setFirstDate] = useState(time)
const [secondDate, setSecondDate] = useState(time)
const [firstTime, setFirstTime] = useState(time)
const [secondTime, setSecondTime] = useState(time)
useEffect(() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion))
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] = time
if (newParentQuestion.content.dateRange) parentQuestion.content.rule.main[ruleIndex].rules[0].answers[1] = time
setParentQuestion(newParentQuestion)
}, [firstDate, secondDate, firstTime, secondTime])
// {/* //dateRange выбор диапазона дат */}
// {/* time выбор времени */}
return <Box
sx={{
padding: "20px",
margin: "20px",
borderRadius: "8px",
bgcolor: "#F2F3F7",
height: "280px",
overflow: "auto"
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
pb: "5px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Новое условие
</Typography>
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion))
newParentQuestion.content.rule.main.splice(ruleIndex, 1)
setParentQuestion(newParentQuestion)
}}
>
<DeleteIcon color={"#4D4D4D"} />
</IconButton>
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
pb: "10px",
}}
>
<Typography sx={{ color: "#4D4D4D", fontWeight: "500" }}>
Дан ответ
</Typography>
<Typography sx={{ color: "#7E2AEA", pl: "10px" }}>
(Укажите один или несколько вариантов)
</Typography>
</Box>
<Box
sx={{
backgroundColor: "#E8EAEE",
margin: "10px"
}}
>
{
parentQuestion.content.dateRange &&
<Typography sx={{ color: "#4D4D4D", p: "10px" }}>
(Начало периода)
</Typography>
}
<DatePicker
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]}
onChange={(e) => console.log(e)}
slots={{
openPickerIcon: () => <CalendarIcon />,
}}
slotProps={{
openPickerButton: {
sx: {
p: 0,
},
"data-cy": "open-datepicker",
},
}}
sx={{
p: "10px",
"& .MuiInputBase-root": {
minWidth: "325px",
backgroundColor: "#F2F3F7",
borderRadius: "10px",
pr: "31px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
}}
/>
{parentQuestion.content.time &&
<TimePicker
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]}
sx={{
p: "10px",
"& .MuiInputBase-root": {
minWidth: "325px",
backgroundColor: "#F2F3F7",
borderRadius: "10px",
pr: "22px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
}}
/>
}
</Box>
{parentQuestion.content.dateRange &&
<Box
sx={{
backgroundColor: "#E8EAEE",
margin: "10px"
}}
>
{
parentQuestion.content.dateRange &&
<Typography sx={{ color: "#4D4D4D", p: "10px" }}>
(Конец периода)
</Typography>
}
<DatePicker
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[1]}
onChange={() => { }}
slots={{
openPickerIcon: () => <CalendarIcon />,
}}
slotProps={{
openPickerButton: {
sx: {
p: 0,
},
"data-cy": "open-datepicker",
},
}}
sx={{
p: "10px",
"& .MuiInputBase-root": {
minWidth: "325px",
backgroundColor: "#F2F3F7",
borderRadius: "10px",
pr: "31px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
}}
/>
{parentQuestion.content.time &&
<TimePicker
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[1]}
sx={{
p: "10px",
"& .MuiInputBase-root": {
minWidth: "325px",
backgroundColor: "#F2F3F7",
borderRadius: "10px",
pr: "22px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
}}
/>
}
</Box>
}
{/* <TextField
sx={{
marginTop: "20px",
width: "100%"
}}
placeholder="от"
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]}
onChange={(event: React.FormEvent<HTMLInputElement>) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion))
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] = Number((event.target as HTMLInputElement).value.replace(/[^0-9,\s]/g, ""))
if (newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[1] === undefined) newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[1] = 0
setParentQuestion(newParentQuestion)
}}
/>
{parentQuestion.content.chooseRange &&
<TextField
placeholder="до"
sx={{
marginTop: "20px",
width: "100%"
}}
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[1]}
onChange={(event: React.FormEvent<HTMLInputElement>) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion))
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[1] = Number((event.target as HTMLInputElement).value.replace(/[^0-9,\s]/g, ""))
if (newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] === undefined) newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] = 0
setParentQuestion(newParentQuestion)
}}
/>
} */}
</Box >
} }
const NumberInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQuestion }: Props) => { const NumberInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQuestion }: Props) => {
const theme = useTheme(); const theme = useTheme();
@ -264,21 +509,43 @@ const NumberInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParent
(Укажите один или несколько вариантов) (Укажите один или несколько вариантов)
</Typography> </Typography>
</Box> </Box>
{/* <TextField <TextField
sx={{ sx={{
marginTop: "20px", marginTop: "20px",
width: "100%" width: "100%"
}} }}
placeholder="от"
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]} value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[0]}
onChange={(event: React.FormEvent<HTMLInputElement>) => { onChange={(event: React.FormEvent<HTMLInputElement>) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion)) let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion))
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = [(event.target as HTMLInputElement).value.replace(/[^0-9,\s]/g, "")] newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] = Number((event.target as HTMLInputElement).value.replace(/[^0-9,\s]/g, ""))
if (newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[1] === undefined) newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[1] = 0
setParentQuestion(newParentQuestion) setParentQuestion(newParentQuestion)
}} }}
/> */} />
{parentQuestion.content.chooseRange &&
<TextField
placeholder="до"
sx={{
marginTop: "20px",
width: "100%"
}}
value={parentQuestion.content.rule.main[ruleIndex].rules[0].answers[1]}
onChange={(event: React.FormEvent<HTMLInputElement>) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion))
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[1] = Number((event.target as HTMLInputElement).value.replace(/[^0-9,\s]/g, ""))
if (newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] === undefined) newParentQuestion.content.rule.main[ruleIndex].rules[0].answers[0] = 0
setParentQuestion(newParentQuestion)
}}
/>
}
@ -338,7 +605,7 @@ const TextInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQu
(Укажите текст, при совпадении с которым пользователь попадёт на этот вопрос) (Укажите текст, при совпадении с которым пользователь попадёт на этот вопрос)
</Typography> </Typography>
</Box> </Box>
{/* <TextField <TextField
sx={{ sx={{
marginTop: "20px", marginTop: "20px",
width: "100%" width: "100%"
@ -351,7 +618,7 @@ const TextInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParentQu
setParentQuestion(newParentQuestion) setParentQuestion(newParentQuestion)
}} }}
/> */} />
@ -475,10 +742,10 @@ const RatingInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParent
}} }}
> >
<Typography sx={{ color: "#7E2AEA", pl: "10px", fontSize: "12px" }}> <Typography sx={{ color: "#7E2AEA", pl: "10px", fontSize: "12px" }}>
(Ожидаемое количество ячеек) Ожидаемое количество ячеек(не более доступного количества)
</Typography> </Typography>
</Box> </Box>
{/* <TextField <TextField
sx={{ sx={{
marginTop: "20px", marginTop: "20px",
width: "100%" width: "100%"
@ -487,11 +754,13 @@ const RatingInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParent
onChange={(event: React.FormEvent<HTMLInputElement>) => { onChange={(event: React.FormEvent<HTMLInputElement>) => {
let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion)) let newParentQuestion = JSON.parse(JSON.stringify(parentQuestion))
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = [(event.target as HTMLInputElement).value.replace(/[^0-9,\s]/g, "")] let valueNumber = Number((event.target as HTMLInputElement).value.replace(/[^0-9,\s]/g, ""))
valueNumber = valueNumber > parentQuestion.content.steps ? parentQuestion.content.steps : valueNumber
newParentQuestion.content.rule.main[ruleIndex].rules[0].answers = [valueNumber]
setParentQuestion(newParentQuestion) setParentQuestion(newParentQuestion)
}} }}
/> */} />
@ -499,23 +768,3 @@ const RatingInputsType = ({ parentQuestion, targetQuestion, ruleIndex, setParent
</Box > </Box >
) )
} }
// {
// content: {
// rule: {
// main: [
// {
// next: "id of next question", // 2 string
// or: true // 3 boolean
// rules: [
// {
// question: "question id", string
// answers: [] // ответы на вопросы. для вариантов выбора - конкретные айдишники, для полей ввода текста - текст по полному совпадению, для ввода файла ии ещё какой подобной дичи - просто факт того что файл ввели, т.е. boolean
// }
// ]
// }
// ],
// default: ID string
// }
// }
// }

@ -23,7 +23,6 @@ type AnyQuestion = UntypedQuizQuestion | AnyTypedQuizQuestion
export const QuestionsList = () => { export const QuestionsList = () => {
const { questions } = useQuestionsStore() const { questions } = useQuestionsStore()
console.log(questions)
return ( return (
<Box sx={{ padding: "15px" }}> <Box sx={{ padding: "15px" }}>

@ -1,15 +1,13 @@
import { Box, Typography, Switch, useTheme } from "@mui/material"; import { Box, Typography, Switch, useTheme } from "@mui/material";
import { QuestionsList } from "./QuestionsList"; import { QuestionsList } from "./QuestionsList";
import { updateOpenBranchingPanel } from "@root/questions/actions";
import {useQuestionsStore} from "@root/questions/store";
type BranchingPanelProps = {
active: boolean;
setActive: (active: boolean) => void;
};
export const BranchingPanel = ({ active, setActive }: BranchingPanelProps) => { export const BranchingPanel = () => {
const theme = useTheme(); const theme = useTheme();
const {openBranchingPanel} = useQuestionsStore.getState()
return ( return (
<Box sx={{ userSelect: "none", maxWidth: "350px", width: "100%" }}> <Box sx={{ userSelect: "none", maxWidth: "350px", width: "100%" }}>
<Box <Box
@ -24,8 +22,10 @@ export const BranchingPanel = ({ active, setActive }: BranchingPanelProps) => {
}} }}
> >
<Switch <Switch
value={active} value={openBranchingPanel}
onChange={(_, value) => setTimeout(() => setActive(value), 10)} onChange={(_, value) => {
updateOpenBranchingPanel(!value)
}}
sx={{ sx={{
width: 50, width: 50,
height: 30, height: 30,
@ -79,7 +79,7 @@ export const BranchingPanel = ({ active, setActive }: BranchingPanelProps) => {
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
{ active && <QuestionsList /> } { openBranchingPanel === true && <QuestionsList /> }
</Box> </Box>
); );
}; };

@ -1,24 +1,30 @@
import { useParams } from "react-router-dom";
import { Box, Modal, Button, Typography } from "@mui/material"; import { Box, Modal, Button, Typography } from "@mui/material";
import { useQuestionsStore } from "@root/questions/store"; import { useQuestionsStore } from "@root/questions/store";
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "@model/questionTypes/shared"; import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
type AnyQuestion = UntypedQuizQuestion | AnyTypedQuizQuestion
interface Props { interface Props {
openedModalQuestions: boolean; openedModalQuestions: boolean;
setModalQuestionTargetContentId: (contentId:string) => void; setModalQuestionTargetContentId: (contentId: string) => void;
setOpenedModalQuestions: (open:boolean) => void; setOpenedModalQuestions: (open: boolean) => void;
setModalQuestionParentContentId: (open: string) => void;
} }
export const BranchingQuestionsModal = ({ openedModalQuestions, setOpenedModalQuestions, setModalQuestionTargetContentId}:Props) => { export const BranchingQuestionsModal = ({
const quizId = Number(useParams().quizId); openedModalQuestions,
setOpenedModalQuestions,
setModalQuestionTargetContentId,
setModalQuestionParentContentId
}: Props) => {
const { questions } = useQuestionsStore(); const { questions } = useQuestionsStore();
const handleClose = () => { const handleClose = () => {
setOpenedModalQuestions(false); setOpenedModalQuestions(false);
}; };
const typedQuestions: AnyTypedQuizQuestion[] = questions.filter(
(question) => question.type && !question.content.rule.parentId
) as AnyTypedQuizQuestion[];
return ( return (
<Modal open={openedModalQuestions} onClose={handleClose}> <Modal open={openedModalQuestions} onClose={handleClose}>
<Box <Box
@ -37,11 +43,11 @@ export const BranchingQuestionsModal = ({ openedModalQuestions, setOpenedModalQu
}} }}
> >
<Box sx={{ margin: "0 auto", maxWidth: "350px" }}> <Box sx={{ margin: "0 auto", maxWidth: "350px" }}>
{questions.filter((q:AnyQuestion) => (q.type && !q.content.rule.parentId)).map((question: AnyTypedQuizQuestion, index:number) => ( {typedQuestions.map((question) => (
<Button <Button
key={question.content.id} key={question.content.id}
onClick={() => { onClick={() => {
setModalQuestionTargetContentId(question.content.id) setModalQuestionTargetContentId(question.content.id);
handleClose(); handleClose();
}} }}
sx={{ sx={{

@ -11,7 +11,7 @@ import {
useMediaQuery, useMediaQuery,
useTheme, useTheme,
} from "@mui/material"; } from "@mui/material";
import { copyQuestion, deleteQuestion, updateQuestion } from "@root/questions/actions"; import {copyQuestion, deleteQuestion, updateOpenBranchingPanel, updateQuestion} from "@root/questions/actions";
import MiniButtonSetting from "@ui_kit/MiniButtonSetting"; import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon"; import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon";
import Branching from "../../assets/icons/questionsPage/branching"; import Branching from "../../assets/icons/questionsPage/branching";
@ -20,6 +20,9 @@ import { HideIcon } from "../../assets/icons/questionsPage/hideIcon";
import SettingIcon from "../../assets/icons/questionsPage/settingIcon"; import SettingIcon from "../../assets/icons/questionsPage/settingIcon";
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
import { updateOpenedModalSettingsId } from "@root/questions/actions"; import { updateOpenedModalSettingsId } from "@root/questions/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import {enqueueSnackbar} from "notistack";
import {useQuestionsStore} from "@root/questions/store";
interface Props { interface Props {
@ -37,11 +40,28 @@ export default function ButtonsOptions({
const theme = useTheme(); const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isMobile = useMediaQuery(theme.breakpoints.down(790));
const isWrappMiniButtonSetting = useMediaQuery(theme.breakpoints.down(920)); const isWrappMiniButtonSetting = useMediaQuery(theme.breakpoints.down(920));
const quiz = useCurrentQuiz();
const {openBranchingPanel} = useQuestionsStore.getState()
const openedModal = () => { const openedModal = () => {
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;
title: string; title: string;
@ -78,7 +98,7 @@ export default function ButtonsOptions({
), ),
title: "Ветвление", title: "Ветвление",
value: "branching", value: "branching",
myFunc: openedModal, myFunc: () => handleClickBranching(question.id, openBranchingPanel),
}, },
]; ];
@ -277,7 +297,7 @@ export default function ButtonsOptions({
// deleteTimeoutId: newTimeoutId, // deleteTimeoutId: newTimeoutId,
// }); // });
deleteQuestion(question.id); deleteQuestion(question.id, quiz.id);
}} }}
data-cy="delete-question" data-cy="delete-question"
> >

@ -23,6 +23,10 @@ import ImgIcon from "../../assets/icons/questionsPage/imgIcon";
import SettingIcon from "../../assets/icons/questionsPage/settingIcon"; import SettingIcon from "../../assets/icons/questionsPage/settingIcon";
import { QuizQuestionVariant } from "@model/questionTypes/variant"; import { QuizQuestionVariant } from "@model/questionTypes/variant";
import { updateOpenedModalSettingsId } from "@root/questions/actions"; import { updateOpenedModalSettingsId } from "@root/questions/actions";
import { updateOpenBranchingPanel } from "@root/questions/actions";
import {useQuestionsStore} from "@root/questions/store";
import { enqueueSnackbar } from "notistack";
import { useCurrentQuiz } from "@root/quizes/hooks";
interface Props { interface Props {
@ -42,6 +46,8 @@ 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 quiz = useCurrentQuiz();
useEffect(() => { useEffect(() => {
if (question.deleteTimeoutId) { if (question.deleteTimeoutId) {
@ -49,6 +55,22 @@ export default function ButtonsOptionsAndPict({
} }
}, [question]); }, [question]);
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)
updateOpenedModalSettingsId(question.id)
}
}
return ( return (
<Box <Box
sx={{ sx={{
@ -184,7 +206,7 @@ export default function ButtonsOptionsAndPict({
onMouseEnter={() => setButtonHover("branching")} onMouseEnter={() => setButtonHover("branching")}
onMouseLeave={() => setButtonHover("")} onMouseLeave={() => setButtonHover("")}
onClick={() => { onClick={() => {
updateOpenedModalSettingsId(question.id) handleClickBranching(question.id, openBranchingPanel)
}} }}
sx={{ sx={{
height: "30px", height: "30px",
@ -316,7 +338,7 @@ export default function ButtonsOptionsAndPict({
// deleteTimeoutId: newTimeoutId, // deleteTimeoutId: newTimeoutId,
// }); // });
deleteQuestion(question.id); deleteQuestion(question.id, quiz?.id);
}} }}
data-cy="delete-question" data-cy="delete-question"
> >

@ -39,7 +39,7 @@ import SwitchQuestionsPage from "../SwitchQuestionsPage";
import { ChooseAnswerModal } from "./ChooseAnswerModal"; 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"
interface Props { interface Props {
question: AnyTypedQuizQuestion | UntypedQuizQuestion; question: AnyTypedQuizQuestion | UntypedQuizQuestion;
@ -55,6 +55,7 @@ export default function QuestionsPageCard({ question, draggableProps, isDragging
const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isMobile = useMediaQuery(theme.breakpoints.down(790));
const anchorRef = useRef(null); const anchorRef = useRef(null);
const quiz = useCurrentQuiz();
const setTitle = useDebouncedCallback((title) => { const setTitle = useDebouncedCallback((title) => {
const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion; const updateQuestionFn = question.type === null ? updateUntypedQuestion : updateQuestion;
@ -237,7 +238,7 @@ export default function QuestionsPageCard({ question, draggableProps, isDragging
margin: "0 5px 0 10px", margin: "0 5px 0 10px",
}} }}
onClick={() => { // TODO onClick={() => { // TODO
// const removedId = question.id; const removedId = question.id;
// if (question.deleteTimeoutId) { // if (question.deleteTimeoutId) {
// clearTimeout(question.deleteTimeoutId); // clearTimeout(question.deleteTimeoutId);
// } // }
@ -253,7 +254,7 @@ export default function QuestionsPageCard({ question, draggableProps, isDragging
// deleteTimeoutId: newTimeoutId, // deleteTimeoutId: newTimeoutId,
// }); // });
deleteQuestion(question.id); deleteQuestion(question.id, quiz.id);
}} }}
data-cy="delete-question" data-cy="delete-question"
> >

@ -5,22 +5,19 @@ import {
import { DraggableList } from "./DraggableList"; import { DraggableList } from "./DraggableList";
import { BranchingPanel } from "./BranchingPanel"; import { BranchingPanel } from "./BranchingPanel";
import { BranchingMap } from "./BranchingMap"; import { BranchingMap } from "./BranchingMap";
import { updateOpenBranchingPanel } from "@root/questions/actions";
import {useQuestionsStore} from "@root/questions/store";
interface Props {
settingBranching: boolean;
setSettingBranching: (active: boolean) => void
}
export const QuestionSwitchWindowTool = ({settingBranching, setSettingBranching}:Props) => {
export const QuestionSwitchWindowTool = () => {
const {openBranchingPanel} = useQuestionsStore.getState()
return ( return (
<Box sx={{ display: "flex", gap: "20px", flexWrap: "wrap" }}> <Box sx={{ display: "flex", gap: "20px", flexWrap: "wrap" }}>
<Box sx={{ flexBasis: "796px" }}> <Box sx={{ flexBasis: "796px" }}>
{settingBranching ? <BranchingMap /> : <DraggableList />} {openBranchingPanel? <BranchingMap /> : <DraggableList />}
</Box> </Box>
<BranchingPanel <BranchingPanel
active={settingBranching}
setActive={setSettingBranching}
/> />
</Box> </Box>
) )

@ -1,4 +1,4 @@
import { useState, useEffect } from "react" import { useState, useEffect, useLayoutEffect } from "react"
import { import {
Box, Box,
Button, Button,
@ -17,6 +17,7 @@ import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
import BranchingQuestions from "./BranchingModal/BranchingQuestionsModal" import BranchingQuestions from "./BranchingModal/BranchingQuestionsModal"
import { QuestionSwitchWindowTool } from "./QuestionSwitchWindowTool"; import { QuestionSwitchWindowTool } from "./QuestionSwitchWindowTool";
import { useQuestionsStore } from "@root/questions/store"; import { useQuestionsStore } from "@root/questions/store";
import { updateOpenBranchingPanel } from "@root/questions/actions";
export default function QuestionsPage() { export default function QuestionsPage() {
@ -24,10 +25,14 @@ export default function QuestionsPage() {
const { openedModalSettingsId } = useQuestionsStore(); const { openedModalSettingsId } = useQuestionsStore();
const isMobile = useMediaQuery(theme.breakpoints.down(660)); const isMobile = useMediaQuery(theme.breakpoints.down(660));
const quiz = useCurrentQuiz(); const quiz = useCurrentQuiz();
const {openBranchingPanel} = useQuestionsStore.getState()
useLayoutEffect(() => {
updateOpenBranchingPanel(true)
},[])
const [settingBranching, setSettingBranching] = useState<boolean>(false);
if (!quiz) return null; if (!quiz) return null;
return ( return (
<> <>
<Box <Box
@ -42,7 +47,7 @@ export default function QuestionsPage() {
<Typography variant={"h5"}>Заголовок квиза</Typography> <Typography variant={"h5"}>Заголовок квиза</Typography>
<Button <Button
sx={{ sx={{
display: settingBranching ? "none" : "flex", display: openBranchingPanel ? "none" : "flex",
fontSize: "16px", fontSize: "16px",
lineHeight: "19px", lineHeight: "19px",
padding: 0, padding: 0,
@ -55,7 +60,7 @@ export default function QuestionsPage() {
Свернуть всё Свернуть всё
</Button> </Button>
</Box> </Box>
<QuestionSwitchWindowTool settingBranching={settingBranching} setSettingBranching={setSettingBranching} /> <QuestionSwitchWindowTool/>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",

@ -1,9 +1,12 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Box, Typography, Button, useTheme } from "@mui/material"; import { Box, Typography, Button, useTheme } from "@mui/material";
import { useQuizViewStore, getAnswersByQuestionId } from "@root/quizView"; import { useQuizViewStore } from "@root/quizView";
import type { AnyTypedQuizQuestion, QuizQuestionBase } from "../../model/questionTypes/shared"; import type {
AnyTypedQuizQuestion,
QuizQuestionBase,
} from "../../model/questionTypes/shared";
import { getQuestionByContentId } from "@root/questions/actions"; import { getQuestionByContentId } from "@root/questions/actions";
import { enqueueSnackbar } from "notistack"; import { enqueueSnackbar } from "notistack";
@ -12,10 +15,7 @@ type FooterProps = {
question: QuizQuestionBase; question: QuizQuestionBase;
}; };
export const Footer = ({ export const Footer = ({ setCurrentQuestion, question }: FooterProps) => {
setCurrentQuestion,
question,
}: FooterProps) => {
const [disabledQuestionsId, setDisabledQuestionsId] = useState<Set<string>>( const [disabledQuestionsId, setDisabledQuestionsId] = useState<Set<string>>(
new Set() new Set()
); );
@ -24,60 +24,59 @@ export const Footer = ({
const followPreviousStep = () => { const followPreviousStep = () => {
if (question?.content.rule.parentId !== "root") { if (question?.content.rule.parentId !== "root") {
const parent = getQuestionByContentId(question?.content.rule.parentId) const parent = getQuestionByContentId(question?.content.rule.parentId);
if (parent) { if (parent) {
setCurrentQuestion(parent) setCurrentQuestion(parent);
} else { } else {
enqueueSnackbar("не могу получить предыдущий вопрос") enqueueSnackbar("не могу получить предыдущий вопрос");
} }
} else { } else {
enqueueSnackbar("вы находитесь на первом вопросе") enqueueSnackbar("вы находитесь на первом вопросе");
} }
}; };
const followNextStep = () => { const followNextStep = () => {
const answers = getAnswersByQuestionId(question.content.id) || [] if (answers.length) {
console.log(answers) let readyBeNextQuestion = "";
if (answers) {
let readyBeNextQuestion = ""
question.content.rule.main.forEach(({ next, rules }) => { question.content.rule.main.forEach(({ next, rules }) => {
console.log({ next, rules })
console.log("[storeAnswers] ", rules[0].answers) let longerArray = Math.max(
console.log("[answers.answer] ", [answers.answer]) rules[0].answers.length,
[answers.at(-1)?.answer].length
);
let longerArray = Math.max(rules[0].answers.length, [answers.answer].length) for (
var i = 0;
for (var i = 0; i < longerArray; i++) // Цикл по всем эле­мен­там бОльшего массива i < longerArray;
if (rules[0].answers[i] !== [answers.answer][i]) readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны i++ // Цикл по всем эле­мен­там бОльшего массива
) {
if (rules[0].answers[i] === answers.at(-1)?.answer) {
}) readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны
if (readyBeNextQuestion) {
console.log("мы нашли совпадение в " + readyBeNextQuestion)
const nextQuestion = getQuestionByContentId(readyBeNextQuestion)
console.log("next question ", nextQuestion)
if (nextQuestion) {
console.log("я устанавливаю следующий вопрос " + question.title)
setCurrentQuestion(nextQuestion)
return
} else {
enqueueSnackbar("не могу получить последующий вопрос")
}
} else {
const nextQuestion = getQuestionByContentId(question.content.rule.default)
console.log("я устанавливаю дефолтный вопрос")
if (nextQuestion) {
setCurrentQuestion(nextQuestion)
} else {
enqueueSnackbar("не могу получить последующий вопрос (дефолтный)")
} }
} }
});
if (readyBeNextQuestion) {
const nextQuestion = getQuestionByContentId(readyBeNextQuestion);
if (nextQuestion) {
setCurrentQuestion(nextQuestion);
return;
} else {
enqueueSnackbar("не могу получить последующий вопрос");
}
} else {
const nextQuestion = getQuestionByContentId(
question.content.rule.default
);
if (nextQuestion) {
setCurrentQuestion(nextQuestion);
} else {
enqueueSnackbar("не могу получить последующий вопрос (дефолтный)");
}
}
} }
}; };
return ( return (

@ -44,7 +44,6 @@ export const Question = ({
}: QuestionProps) => { }: QuestionProps) => {
const quiz = useCurrentQuiz(); const quiz = useCurrentQuiz();
const [currentQuestion, setCurrentQuestion] = useState(getQuestionByContentId(quiz?.config.haveRoot || "")) const [currentQuestion, setCurrentQuestion] = useState(getQuestionByContentId(quiz?.config.haveRoot || ""))
console.log("root " + quiz?.config.haveRoot)
if (!currentQuestion) return <>не смог отобразить вопрос</> if (!currentQuestion) return <>не смог отобразить вопрос</>
const QuestionComponent = const QuestionComponent =

@ -11,8 +11,6 @@ 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();
console.log(questions)
const [visualStartPage, setVisualStartPage] = useState<boolean>(!quiz?.config.noStartPage); const [visualStartPage, setVisualStartPage] = useState<boolean>(!quiz?.config.noStartPage);

@ -126,7 +126,6 @@ export const updateQuestion = <T extends AnyTypedQuizQuestion>(
questionIndex: number, questionIndex: number,
recipe: (question: T) => void, recipe: (question: T) => void,
) => setProducedState(state => { ) => setProducedState(state => {
console.log("начинаю отправку fire квиза " )
const question = state.listQuestions[quizId][questionIndex] as T; const question = state.listQuestions[quizId][questionIndex] as T;
recipe(question); recipe(question);

@ -10,6 +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 { useCurrentQuiz } from "@root/quizes/hooks"
import { QuestionsStore, useQuestionsStore } from "./store"; import { QuestionsStore, useQuestionsStore } from "./store";
@ -138,10 +140,8 @@ export const updateQuestion = (
// requestTimeoutId = setTimeout(() => { // requestTimeoutId = setTimeout(() => {
requestQueue.enqueue(async () => { requestQueue.enqueue(async () => {
const q = useQuestionsStore.getState().questions.find(q => q.id === questionId) || useQuestionsStore.getState().questions.find(q => q.type !== null && q.content.id === questionId); const q = useQuestionsStore.getState().questions.find(q => q.id === questionId) || useQuestionsStore.getState().questions.find(q => q.type !== null && q.content.id === questionId);
console.log("мы пытаемся найти вопрос ");
if (!q) return; if (!q) return;
if (q.type === null) throw new Error("Cannot send update request for untyped question"); if (q.type === null) throw new Error("Cannot send update request for untyped question");
console.log(q.title);
const response = await questionApi.edit(questionToEditQuestionRequest(q)); const response = await questionApi.edit(questionToEditQuestionRequest(q));
@ -261,8 +261,13 @@ export const changeQuestionType = (
type: QuestionType, type: QuestionType,
) => { ) => {
updateQuestion(questionId, question => { updateQuestion(questionId, question => {
const oldId = question.content.id
const oldRule = question.content.rule
oldRule.main = []
question.type = type; question.type = type;
question.content = defaultQuestionByType[type].content; question.content = defaultQuestionByType[type].content;
question.content.id = oldId
question.content.rule = oldRule
}); });
}; };
@ -302,7 +307,8 @@ export const createTypedQuestion = async (
} }
}); });
export const deleteQuestion = async (questionId: string) => requestQueue.enqueue(async () => { export const deleteQuestion = async (questionId: string, quizId: 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;
@ -313,6 +319,10 @@ export const deleteQuestion = async (questionId: string) => requestQueue.enqueue
try { try {
await questionApi.delete(question.backendId); await questionApi.delete(question.backendId);
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
updateRootContentId(quizId, "")
clearRoleForAll()
}
removeQuestion(questionId); removeQuestion(questionId);
} catch (error) { } catch (error) {
@ -325,9 +335,11 @@ export const copyQuestion = async (questionId: string, quizId: number) => reques
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;
const frontId = nanoid();
if (question.type === null) { if (question.type === null) {
const copiedQuestion = structuredClone(question); const copiedQuestion = structuredClone(question);
copiedQuestion.id = nanoid(); copiedQuestion.id = frontId
copiedQuestion.content.id = frontId
setProducedState(state => { setProducedState(state => {
state.questions.push(copiedQuestion); state.questions.push(copiedQuestion);
@ -345,7 +357,9 @@ export const copyQuestion = async (questionId: string, quizId: number) => reques
const copiedQuestion = structuredClone(question); const copiedQuestion = structuredClone(question);
copiedQuestion.backendId = newQuestionId; copiedQuestion.backendId = newQuestionId;
copiedQuestion.id = nanoid(); copiedQuestion.id = frontId
copiedQuestion.content.id = frontId
copiedQuestion.content.rule = { main: [], parentId: "", default: "" }
setProducedState(state => { setProducedState(state => {
state.questions.push(copiedQuestion); state.questions.push(copiedQuestion);
@ -377,19 +391,30 @@ export const getQuestionById = (questionId: string | null) => {
return useQuestionsStore.getState().questions.find(q => q.id === questionId) || null; return useQuestionsStore.getState().questions.find(q => q.id === questionId) || null;
}; };
export const getQuestionByContentId = (questionContentId: string | null) => { export const getQuestionByContentId = (questionContentId: string | null) => {
console.log("questionContentId " + questionContentId);
if (questionContentId === null) return null; if (questionContentId === null) return null;
return useQuestionsStore.getState().questions.find(q => { return useQuestionsStore.getState().questions.find(q => {
if (q.type === null) return false; if (q.type === null) return false;
console.log(q.content.id);
console.log(q.content.id === questionContentId);
return (q.content.id === questionContentId); return (q.content.id === questionContentId);
}) || null; }) || null;
}; };
export const updateOpenedModalSettingsId = (id?: string) => useQuestionsStore.setState({ openedModalSettingsId: id ? id : null }); export const updateOpenedModalSettingsId = (id?: string) => useQuestionsStore.setState({ openedModalSettingsId: id ? id : null });
export const updateDragQuestionContentId = (contentId?: string) => { export const updateDragQuestionContentId = (contentId?: string) => {
console.log("contentId " + contentId);
useQuestionsStore.setState({ dragQuestionContentId: contentId ? contentId : null }); useQuestionsStore.setState({ dragQuestionContentId: contentId ? contentId : null });
}; }
export const clearRoleForAll = () => {
const { questions } = useQuestionsStore.getState()
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)) {
updateQuestion(question.content.id, question => {
question.content.rule.parentId = ""
question.content.rule.main = []
question.content.rule.default = ""
})
}
});
}
export const updateOpenBranchingPanel = (value: boolean) => useQuestionsStore.setState({openBranchingPanel: !value});

@ -7,12 +7,14 @@ export type QuestionsStore = {
questions: (AnyTypedQuizQuestion | UntypedQuizQuestion)[]; questions: (AnyTypedQuizQuestion | UntypedQuizQuestion)[];
openedModalSettingsId: string | null; openedModalSettingsId: string | null;
dragQuestionContentId: string | null; dragQuestionContentId: string | null;
openBranchingPanel: boolean;
}; };
const initialState: QuestionsStore = { const initialState: QuestionsStore = {
questions: [], questions: [],
openedModalSettingsId: null as null, openedModalSettingsId: null as null,
dragQuestionContentId: null, dragQuestionContentId: null,
openBranchingPanel: false,
}; };
export const useQuestionsStore = create<QuestionsStore>()( export const useQuestionsStore = create<QuestionsStore>()(

@ -33,9 +33,3 @@ export const updateAnswer = (questionId: string, answer: string) => {
useQuizViewStore.setState({ answers }); useQuizViewStore.setState({ answers });
}; };
export const getAnswersByQuestionId = (questionId: string) => {
if (questionId === null) return null;
const answers = [...useQuizViewStore.getState().answers];
return answers.find(a => a.questionId === questionId) || null;
}

@ -10,6 +10,7 @@ import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
import { RequestQueue } from "../../utils/requestQueue"; import { RequestQueue } from "../../utils/requestQueue";
import { QuizStore, useQuizStore } from "./store"; import { QuizStore, useQuizStore } from "./store";
import { createUntypedQuestion } from "@root/questions/actions"; import { createUntypedQuestion } from "@root/questions/actions";
import { useCurrentQuiz } from "./hooks"
export const setEditQuizId = (quizId: number | null) => setProducedState(state => { export const setEditQuizId = (quizId: number | null) => setProducedState(state => {
@ -178,10 +179,11 @@ export const deleteQuiz = async (quizId: string) => requestQueue.enqueue(async (
export const updateRootContentId = (quizId: string, id:string) => updateQuiz( export const updateRootContentId = (quizId: string, id:string) => updateQuiz(
quizId, quizId,
quiz => { quiz => {
quiz.config.haveRoot = id; quiz.config.haveRoot = id
}, },
); );
// TODO copy quiz // TODO copy quiz
export const uploadQuizImage = async ( export const uploadQuizImage = async (

@ -42,8 +42,6 @@ export default function Rating({ question }: Props) {
const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isMobile = useMediaQuery(theme.breakpoints.down(790));
const [selectedRating, setSelectedRating] = useState<number>(0); const [selectedRating, setSelectedRating] = useState<number>(0);
console.log(question);
const RatingIconComponent = const RatingIconComponent =
ratingIconComponentByType[question.content.form as RatingIconType]; ratingIconComponentByType[question.content.form as RatingIconType];

1249
yarn.lock

File diff suppressed because it is too large Load Diff