Merge branch 'result-page' into dev
This commit is contained in:
commit
e0cc10dcac
19013
package-lock.json
generated
Normal file
19013
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ export default function ExpandIcon({ sx }: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box sx={{ ...sx }}>
|
||||
<Box sx={{ ...sx, display: "flex", alignItems:"center", justifyContent: "center" }}>
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="30" height="30" rx="6" fill="#EEE4FC" />
|
||||
<path d="M22.5 11.25L15 18.75L7.5 11.25" stroke="#7E2AEA" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round" />
|
||||
|
||||
@ -1,13 +1,20 @@
|
||||
import { IconButton } from "@mui/material";
|
||||
import { IconButton, SxProps } from "@mui/material";
|
||||
|
||||
type InfoProps = {
|
||||
width?: number;
|
||||
height?: number;
|
||||
sx?: SxProps;
|
||||
onClick?: any;
|
||||
className?: string
|
||||
};
|
||||
|
||||
export default function Info({ width = 20, height = 20 }: InfoProps) {
|
||||
export default function Info({ width = 20, height = 20, sx, onClick, className }: InfoProps) {
|
||||
return (
|
||||
<IconButton>
|
||||
<IconButton
|
||||
sx={sx}
|
||||
className={className}
|
||||
onClick={onClick}
|
||||
>
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
interface Props {
|
||||
color: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export default function SettingIcon({ color }: Props) {
|
||||
|
||||
21
src/assets/icons/trash.tsx
Normal file
21
src/assets/icons/trash.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { useTheme, SxProps, Box } from "@mui/material";
|
||||
|
||||
interface Props {
|
||||
sx?: SxProps;
|
||||
}
|
||||
|
||||
export default function Trash({ sx }: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box sx={{ ...sx, display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.25 5.73438H3.75" stroke="#4D4D4D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M9.75 10.2344V16.2344" stroke="#4D4D4D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M14.25 10.2344V16.2344" stroke="#4D4D4D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M18.75 5.73438V19.9844C18.75 20.1833 18.671 20.3741 18.5303 20.5147C18.3897 20.6554 18.1989 20.7344 18 20.7344H6C5.80109 20.7344 5.61032 20.6554 5.46967 20.5147C5.32902 20.3741 5.25 20.1833 5.25 19.9844V5.73438" stroke="#4D4D4D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M15.75 5.73438V4.23438C15.75 3.83655 15.592 3.45502 15.3107 3.17371C15.0294 2.89241 14.6478 2.73438 14.25 2.73438H9.75C9.35218 2.73438 8.97064 2.89241 8.68934 3.17371C8.40804 3.45502 8.25 3.83655 8.25 4.23438V5.73438" stroke="#4D4D4D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@ -18,6 +18,7 @@ export const QUIZ_QUESTION_BASE: Omit<QuizQuestionBase, "id" | "backendId"> = {
|
||||
video: "",
|
||||
},
|
||||
rule: {
|
||||
children: [],
|
||||
main: [] as QuestionBranchingRuleMain[],
|
||||
parentId: "",
|
||||
default: ""
|
||||
|
||||
@ -12,6 +12,6 @@ export const QUIZ_QUESTION_RESULT: Omit<QuizQuestionResult, "id" | "backendId">
|
||||
innerName: "",
|
||||
text: "",
|
||||
price: [0],
|
||||
rangePrice: false
|
||||
useImage: true
|
||||
},
|
||||
};
|
||||
|
||||
@ -10,4 +10,20 @@ body {
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
@keyframes blinking {
|
||||
0% {
|
||||
opacity: 100;
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation: blinking 2s infinite ;
|
||||
}
|
||||
@ -14,7 +14,7 @@ export interface QuizQuestionResult extends QuizQuestionBase {
|
||||
innerName: string;
|
||||
text: string;
|
||||
price: [number] | [number, number];
|
||||
rangePrice: boolean;
|
||||
useImage: boolean;
|
||||
rule: QuestionBranchingRule,
|
||||
hint: QuestionHint;
|
||||
autofill: boolean;
|
||||
|
||||
@ -23,6 +23,7 @@ export interface QuestionBranchingRuleMain {
|
||||
}
|
||||
export interface QuestionBranchingRule {
|
||||
|
||||
children: string[],
|
||||
//список условий
|
||||
main: QuestionBranchingRuleMain[];
|
||||
parentId: string | null | "root";
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import Cytoscape from "cytoscape";
|
||||
import { Button } from "@mui/material";
|
||||
import CytoscapeComponent from "react-cytoscapejs";
|
||||
import popper from "cytoscape-popper";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { updateRootContentId } from "@root/quizes/actions"
|
||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"
|
||||
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 { storeToNodes } from "./helper";
|
||||
@ -20,9 +21,7 @@ import type {
|
||||
AbstractEventObject,
|
||||
ElementDefinition,
|
||||
} from "cytoscape";
|
||||
import { QuestionsList } from "../SwitchBranchingPanel/QuestionsList";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
type PopperItem = {
|
||||
id: () => string;
|
||||
@ -113,18 +112,18 @@ interface Props {
|
||||
}
|
||||
|
||||
|
||||
function CsComponent ({
|
||||
function CsComponent({
|
||||
modalQuestionParentContentId,
|
||||
modalQuestionTargetContentId,
|
||||
setOpenedModalQuestions,
|
||||
setModalQuestionParentContentId,
|
||||
setModalQuestionTargetContentId
|
||||
}: Props) {
|
||||
}: Props) {
|
||||
const quiz = useCurrentQuiz();
|
||||
|
||||
const { dragQuestionContentId, desireToOpenABranchingModal } = useQuestionsStore()
|
||||
const trashQuestions = useQuestionsStore().questions
|
||||
const questions = trashQuestions.filter((question) => question.type !== "result")
|
||||
const questions = trashQuestions.filter((question) => question.type !== "result" && question.type !== null)
|
||||
const [startCreate, setStartCreate] = useState("");
|
||||
const [startRemove, setStartRemove] = useState("");
|
||||
|
||||
@ -166,7 +165,7 @@ function CsComponent ({
|
||||
if (Object.keys(targetQuestion).length !== 0 && Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) {
|
||||
clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
|
||||
cy?.data('changed', true)
|
||||
cy?.add([
|
||||
const es = cy?.add([
|
||||
{
|
||||
data: {
|
||||
id: targetQuestion.content.id,
|
||||
@ -181,24 +180,39 @@ function CsComponent ({
|
||||
}
|
||||
])
|
||||
cy?.layout(lyopts).run()
|
||||
cy?.fit(es, 200)
|
||||
} else {
|
||||
enqueueSnackbar("Добавляемый вопрос не найден")
|
||||
}
|
||||
}
|
||||
|
||||
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 => {
|
||||
question.content.rule.parentId = parentNodeContentId
|
||||
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 - предупреждаем стор вопросов об открытии модалки ветвления
|
||||
if (parentNodeChildren >= 1) {
|
||||
if (parentQuestion.content.rule.children >= 1) {
|
||||
updateOpenedModalSettingsId(targetQuestion.content.id)
|
||||
} else {
|
||||
//Если ребёнок первый - добавляем его родителю как дефолтный
|
||||
updateQuestion(parentNodeContentId, question => question.content.rule.default = targetQuestion.content.id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,10 +246,18 @@ function CsComponent ({
|
||||
updateQuestion(targetNodeContentId, question => {
|
||||
question.content.rule.parentId = ""
|
||||
question.content.rule.main = []
|
||||
question.content.rule.children = []
|
||||
question.content.rule.default = ""
|
||||
})
|
||||
trashQuestions.forEach(q => {
|
||||
if (q.type === "result") {
|
||||
deleteQuestion(q.id);
|
||||
}
|
||||
});
|
||||
clearRuleForAll()
|
||||
|
||||
} else {
|
||||
|
||||
const parentQuestionContentId = cy?.$('edge[target = "' + targetNodeContentId + '"]')?.toArray()?.[0]?.data()?.source
|
||||
if (targetNodeContentId && parentQuestionContentId) {
|
||||
|
||||
@ -245,7 +267,7 @@ function CsComponent ({
|
||||
|
||||
}
|
||||
|
||||
//После всех манипуляций удаляем грани из CS и ноды из бекенда
|
||||
//После всех манипуляций удаляем грани и ноды из CS Чистим rule потомков на беке
|
||||
|
||||
deleteNodes.forEach((nodeId) => {//Ноды
|
||||
cy?.remove(cy?.$("#" + nodeId))
|
||||
@ -254,7 +276,10 @@ function CsComponent ({
|
||||
question.content.rule.parentId = ""
|
||||
question.content.rule.main = []
|
||||
question.content.rule.default = ""
|
||||
question.content.rule.children = []
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
deleteEdges.forEach((edge: any) => {//Грани
|
||||
@ -264,26 +289,42 @@ function CsComponent ({
|
||||
removeButtons(targetNodeContentId)
|
||||
cy?.data('changed', true)
|
||||
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 }) => {
|
||||
|
||||
console.log("target ", targetQuestionContentId, "parent ", parentQuestionContentId)
|
||||
|
||||
|
||||
updateQuestion(targetQuestionContentId, question => {
|
||||
question.content.rule.parentId = ""
|
||||
question.content.rule.children = []
|
||||
question.content.rule.main = []
|
||||
question.content.rule.default = ""
|
||||
})
|
||||
|
||||
|
||||
//чистим rule родителя
|
||||
const parentQuestion = getQuestionByContentId(parentQuestionContentId)
|
||||
console.log(parentQuestion.content.rule.parentId)
|
||||
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.parentId = parentQuestion.content.rule.parentId
|
||||
newRule.default = questions.filter((q) => {
|
||||
return q.content.rule.parentId === parentQuestionContentId && q.content.id !== targetQuestionContentId
|
||||
})[0]?.content.id || ""
|
||||
//Если этот вопрос был дефолтным у родителя - чистим дефолт
|
||||
//Смотрим можем ли мы заменить id на один из main
|
||||
newRule.default = parentQuestion.content.rule.default === targetQuestionContentId ? "" : parentQuestion.content.rule.default
|
||||
newRule.children = newChildren
|
||||
|
||||
console.log(newRule)
|
||||
updateQuestion(parentQuestionContentId, (PQ) => {
|
||||
PQ.content.rule = newRule
|
||||
})
|
||||
@ -332,7 +373,7 @@ function CsComponent ({
|
||||
positions: (e) => {
|
||||
if (!e.cy().data('changed')) {
|
||||
return e.data('oldPos')
|
||||
} else { e.removeData('oldPos') }
|
||||
}
|
||||
const id = e.id()
|
||||
const incomming = e.cy().edges(`[target="${id}"]`)
|
||||
const layer = 0
|
||||
@ -363,7 +404,7 @@ function CsComponent ({
|
||||
while (queue.length) {
|
||||
const task = queue.pop()
|
||||
if (task.children.length === 0) {
|
||||
task.parent.data('subtreeWidth', task.parent.height())
|
||||
task.parent.data('subtreeWidth', task.parent.height() + 50)
|
||||
continue
|
||||
}
|
||||
const unprocessed = task?.children.filter(e => {
|
||||
@ -379,31 +420,31 @@ function CsComponent ({
|
||||
|
||||
task?.parent.data('subtreeWidth', task.children.reduce((p, n) => p + n.data('subtreeWidth'), 0))
|
||||
}
|
||||
|
||||
const pos = { x: 0, y: 0 }
|
||||
e.data('oldPos', pos)
|
||||
|
||||
queue.push({ task: children, parent: e })
|
||||
while (queue.length) {
|
||||
const task = queue.pop()
|
||||
const oldPos = task.parent.data('oldPos')
|
||||
let yoffset = oldPos.y - task.parent.data('subtreeWidth') / 2
|
||||
task.task.forEach(n => {
|
||||
const width = n.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 })
|
||||
yoffset += width
|
||||
queue.push({ task: n.cy().edges(`[source="${n.id()}"]`).targets(), parent: n })
|
||||
})
|
||||
}
|
||||
e.cy().data('changed', false)
|
||||
return pos
|
||||
} else {
|
||||
if (e.cy().data('firstNode') !== 'root') {
|
||||
e.cy().data('firstNode', 'nonroot')
|
||||
return { x: 0, y: 0 }
|
||||
}
|
||||
if (e.cy().data('firstNode') === undefined)
|
||||
e.cy().data('firstNode', 'nonroot')
|
||||
const parent = e.cy().edges(`[target="${e.id()}"]`)[0].source()
|
||||
const wing = (parent.data('children') === 1) ? 0 : parent.data('subtreeWidth') / 2 + 50
|
||||
const lastOffset = parent.data('lastChild')
|
||||
const step = wing * 2 / (parent.data('children') - 1)
|
||||
//e.removeData('subtreeWidth')
|
||||
if (lastOffset !== undefined) {
|
||||
parent.data('lastChild', lastOffset + step)
|
||||
const pos = { x: 250 * e.data('layer'), y: (lastOffset + step) }
|
||||
e.data('oldPos', pos)
|
||||
return pos
|
||||
} else {
|
||||
parent.data('lastChild', parent.position().y - wing)
|
||||
const pos = { x: 250 * e.data('layer'), y: (parent.position().y - wing) }
|
||||
e.data('oldPos', pos)
|
||||
return pos
|
||||
|
||||
const opos = e.data('oldPos')
|
||||
if (opos) {
|
||||
return opos
|
||||
}
|
||||
}
|
||||
}, // map of (node id) => (position obj); or function(node){ return somPos; }
|
||||
@ -422,7 +463,7 @@ function CsComponent ({
|
||||
useEffect(() => {
|
||||
document.querySelector("#root")?.addEventListener("mouseup", cleardragQuestionContentId);
|
||||
const cy = cyRef.current;
|
||||
const eles = cy?.add(storeToNodes(questions))
|
||||
const eles = cy?.add(storeToNodes(questions.filter((question: AnyTypedQuizQuestion) => (question.type !== "result" && question.type !== null))))
|
||||
cy.data('changed', true)
|
||||
// cy.data('changed', true)
|
||||
const elecs = eles.layout(lyopts).run()
|
||||
@ -690,6 +731,23 @@ function CsComponent ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
sx={{
|
||||
mb: "20px",
|
||||
height: "27px",
|
||||
color: "#7E2AEA",
|
||||
textDecoration: "underline",
|
||||
fontSize: "16px",
|
||||
}}
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
|
||||
//код сюда
|
||||
|
||||
}}
|
||||
>
|
||||
Выровнять
|
||||
</Button>
|
||||
<CytoscapeComponent
|
||||
wheelSensitivity={0.1}
|
||||
elements={[]}
|
||||
@ -700,7 +758,7 @@ function CsComponent ({
|
||||
cy={(cy) => {
|
||||
cyRef.current = cy;
|
||||
}}
|
||||
// autolock
|
||||
autoungrabify={true}
|
||||
/>
|
||||
<button onClick={() => {
|
||||
console.log("NODES____________________________")
|
||||
@ -716,18 +774,18 @@ function CsComponent ({
|
||||
);
|
||||
};
|
||||
|
||||
function Clear () {
|
||||
const quiz = useCurrentQuiz();
|
||||
updateRootContentId(quiz.id, "")
|
||||
clearRuleForAll()
|
||||
return <></>
|
||||
function Clear() {
|
||||
const quiz = useCurrentQuiz();
|
||||
updateRootContentId(quiz.id, "")
|
||||
clearRuleForAll()
|
||||
return <></>
|
||||
}
|
||||
|
||||
export default withErrorBoundary(CsComponent, {
|
||||
fallback: <Clear/>,
|
||||
fallback: <Clear />,
|
||||
onError: (error, info) => {
|
||||
enqueueSnackbar("Дерево порвалось")
|
||||
console.log(info)
|
||||
console.log(error)
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -26,7 +26,7 @@ export const BranchingMap = () => {
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||
marginBottom: "40px",
|
||||
height: "521px",
|
||||
height: "568px",
|
||||
border: dragQuestionContentId === null ? "none" : "#7e2aea 2px dashed"
|
||||
}}
|
||||
>
|
||||
|
||||
@ -46,20 +46,6 @@ export default function ButtonsOptions({
|
||||
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: {
|
||||
icon: JSX.Element;
|
||||
@ -300,7 +286,59 @@ export default function ButtonsOptions({
|
||||
// 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"
|
||||
>
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} 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 { ReallyChangingModal } from "@ui_kit/Modal/ReallyChangingModal/ReallyChangingModal";
|
||||
import { useEffect, useState } from "react";
|
||||
@ -27,6 +27,7 @@ import { updateOpenBranchingPanel, updateDesireToOpenABranchingModal } from "@ro
|
||||
import { useQuestionsStore } from "@root/questions/store";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { updateRootContentId } from "@root/quizes/actions";
|
||||
|
||||
|
||||
interface Props {
|
||||
@ -46,7 +47,7 @@ export default function ButtonsOptionsAndPict({
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
const isIconMobile = useMediaQuery(theme.breakpoints.down(1050));
|
||||
const { openBranchingPanel } = useQuestionsStore.getState()
|
||||
const { questions } = useQuestionsStore.getState()
|
||||
const quiz = useCurrentQuiz();
|
||||
|
||||
useEffect(() => {
|
||||
@ -323,7 +324,59 @@ export default function ButtonsOptionsAndPict({
|
||||
// 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"
|
||||
>
|
||||
|
||||
@ -21,7 +21,6 @@ function DraggableListItem({ question, isDragging, index }: Props) {
|
||||
if (editSomeQuestion !== null) {
|
||||
const setI = setInterval(() => {
|
||||
let comp = document.getElementById(editSomeQuestion)
|
||||
console.log(comp)
|
||||
if(comp !== null) {
|
||||
clearInterval(setI)
|
||||
comp.scrollIntoView({behavior: 'instant'})
|
||||
@ -30,7 +29,6 @@ function DraggableListItem({ question, isDragging, index }: Props) {
|
||||
}, 200)
|
||||
|
||||
}
|
||||
console.log(editSomeQuestion)
|
||||
}, [editSomeQuestion])
|
||||
|
||||
return (
|
||||
|
||||
@ -29,7 +29,8 @@ import {
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} 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 type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
@ -40,6 +41,7 @@ import { ChooseAnswerModal } from "./ChooseAnswerModal";
|
||||
import TypeQuestions from "../TypeQuestions";
|
||||
import { QuestionType } from "@model/question/question";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { useQuestionsStore } from "@root/questions/store";
|
||||
|
||||
interface Props {
|
||||
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
||||
@ -49,6 +51,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function QuestionsPageCard({ question, draggableProps, isDragging, index }: Props) {
|
||||
const { questions } = useQuestionsStore()
|
||||
const [plusVisible, setPlusVisible] = useState<boolean>(false);
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const theme = useTheme();
|
||||
@ -254,8 +257,63 @@ export default function QuestionsPageCard({ question, draggableProps, isDragging
|
||||
// ...question,
|
||||
// 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"
|
||||
>
|
||||
|
||||
@ -9,8 +9,6 @@ import { useQuestionsStore } from "@root/questions/store";
|
||||
export const DraggableList = () => {
|
||||
const { questions } = useQuestionsStore()
|
||||
const filteredQuestions = questions.filter((question) => question.type !== "result")
|
||||
console.log(questions)
|
||||
console.log(filteredQuestions)
|
||||
const onDragEnd = ({ destination, source }: DropResult) => {
|
||||
if (destination) reorderQuestions(source.index, destination.index);
|
||||
};
|
||||
|
||||
@ -9,8 +9,8 @@ import {useQuestionsStore} from "@root/questions/store";
|
||||
|
||||
|
||||
export const QuestionSwitchWindowTool = () => {
|
||||
const {openBranchingPanel} = useQuestionsStore.getState()
|
||||
console.log(openBranchingPanel)
|
||||
const {openBranchingPanel, questions} = useQuestionsStore.getState()
|
||||
console.log("questions ", questions)
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: "20px", flexWrap: "wrap" }}>
|
||||
<Box sx={{ flexBasis: "796px" }}>
|
||||
|
||||
@ -25,7 +25,6 @@ export default function QuestionsPage() {
|
||||
const { openedModalSettingsId, openBranchingPanel } = useQuestionsStore();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
||||
const quiz = useCurrentQuiz();
|
||||
console.log(quiz)
|
||||
useLayoutEffect(() => {
|
||||
updateOpenBranchingPanel(false)
|
||||
updateEditSomeQuestion()
|
||||
|
||||
@ -10,7 +10,6 @@ export const SwitchBranchingPanel = () => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
||||
const {openBranchingPanel} = useQuestionsStore.getState()
|
||||
console.log(openBranchingPanel)
|
||||
const ref = useRef()
|
||||
return (
|
||||
<Box sx={{ userSelect: "none", maxWidth: "350px", width: "100%" }}>
|
||||
@ -28,7 +27,6 @@ export const SwitchBranchingPanel = () => {
|
||||
<Switch
|
||||
value={openBranchingPanel}
|
||||
onChange={(_, value) => {
|
||||
console.log("меняю на " + value)
|
||||
updateOpenBranchingPanel(value)
|
||||
}}
|
||||
sx={{
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
|
||||
|
||||
const priceButtonsArray: { title: string; type: string; sx: SxProps<Theme> }[] =
|
||||
@ -42,38 +43,31 @@ const priceButtonsArray: { title: string; type: string; sx: SxProps<Theme> }[] =
|
||||
];
|
||||
|
||||
type Props = {
|
||||
ButtonsActive: (index: number, type: string) => void;
|
||||
priceButtonsActive: number | undefined;
|
||||
resultData: QuizQuestionResult
|
||||
};
|
||||
|
||||
export default function PriceButtons({
|
||||
ButtonsActive,
|
||||
priceButtonsActive,
|
||||
resultData
|
||||
}: Props) {
|
||||
return (
|
||||
<Box>
|
||||
<Box sx={{ display: "flex", alignItems: "center", mb: "14xp" }}>
|
||||
<Typography component={"h6"} sx={{ weight: "500", fontSize: "18px" }}>
|
||||
Стоимость
|
||||
Заголовок
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
component="div"
|
||||
sx={{ display: "flex", flexWrap: "wrap", gap: "8px", mb: "20px" }}
|
||||
>
|
||||
{priceButtonsArray.map(({ title, type, sx }, index) => (
|
||||
<Button
|
||||
onClick={() => ButtonsActive(index, type)}
|
||||
key={title}
|
||||
sx={{
|
||||
bgcolor: priceButtonsActive === index ? "#7E2AEA" : "#F2F3F7",
|
||||
color: priceButtonsActive === index ? "#FFFF" : "#9A9AAF",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
))}
|
||||
<CustomTextField
|
||||
placeholder={"Вы прошли опрос"}
|
||||
sx={{
|
||||
borderRadius: "8px",
|
||||
height: "48px",
|
||||
width: "100%",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@ -6,6 +6,7 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { Box, Typography, useTheme, useMediaQuery, Button } from "@mui/material";
|
||||
import image from "../../assets/Rectangle 110.png";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||
|
||||
export const FirstEntry = () => {
|
||||
const theme = useTheme();
|
||||
@ -15,17 +16,18 @@ export const FirstEntry = () => {
|
||||
|
||||
const create = () => {
|
||||
if (quiz?.config.haveRoot) {
|
||||
if (questions.length === 0) {
|
||||
enqueueSnackbar("У вас не добавлено ни одного вопроса")
|
||||
return
|
||||
}
|
||||
console.log("createFrontResult")
|
||||
questions
|
||||
.filter((question) => 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 => {
|
||||
createFrontResult(quiz.id, question.content.id)
|
||||
})
|
||||
} else {
|
||||
createFrontResult(quiz.id)
|
||||
console.log("createFrontResult")
|
||||
createFrontResult(quiz.id, "line")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,22 +2,46 @@ import IconPlus from "@icons/IconPlus";
|
||||
import Info from "@icons/Info";
|
||||
import Plus from "@icons/Plus";
|
||||
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 CustomWrapper from "@ui_kit/CustomWrapper";
|
||||
import { DescriptionForm } from "./DescriptionForm/DescriptionForm";
|
||||
import { ResultListForm } from "./ResultListForm";
|
||||
import { SettingForm } from "./SettingForm";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { WhenCard } from "./cards/WhenCard";
|
||||
import { ResultCard } from "./cards/ResultCard";
|
||||
import { ResultCard, checkEmptyData } from "./cards/ResultCard";
|
||||
import { EmailSettingsCard } from "./cards/EmailSettingsCard";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
||||
import { useQuestionsStore } from "@root/questions/store";
|
||||
import { createFrontResult, deleteQuestion } from "@root/questions/actions";
|
||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||
|
||||
export const ResultSettings = () => {
|
||||
const { questions } = useQuestionsStore()
|
||||
const quiz = useCurrentQuiz()
|
||||
const results = useQuestionsStore().questions.filter((q): q is QuizQuestionResult => q.type === "result")
|
||||
console.log("опросник ", quiz)
|
||||
const [quizExpand, setQuizExpand] = useState(true)
|
||||
const [resultContract, setResultContract] = useState(true)
|
||||
const isReadyToLeaveRef = useRef(true);
|
||||
|
||||
useEffect(function calcIsReadyToLeave(){
|
||||
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 (
|
||||
<Box sx={{ maxWidth: "796px" }}>
|
||||
@ -83,8 +107,17 @@ export const ResultSettings = () => {
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
|
||||
<ResultCard resultContract={resultContract} />
|
||||
{
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
||||
|
||||
@ -34,10 +33,7 @@ export const EmailSettingsCard = ({ quizExpand }: Props) => {
|
||||
useEffect(() => {
|
||||
setExpand(false)
|
||||
}, [quizExpand])
|
||||
|
||||
const debouncedCallback = useDebouncedCallback((callback) => {
|
||||
callback();
|
||||
}, 200);
|
||||
|
||||
|
||||
return (
|
||||
<Paper
|
||||
@ -122,9 +118,9 @@ export const EmailSettingsCard = ({ quizExpand }: Props) => {
|
||||
<TextField
|
||||
value={quiz.config.resultInfo.theme}
|
||||
placeholder={"Заголовок вопроса"}
|
||||
onChange={({ target }: { target: HTMLInputElement }) => {debouncedCallback(updateQuiz(quiz.id, (quiz) => {
|
||||
onChange={({ target }: { target: HTMLInputElement }) => {updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.resultInfo.theme = target.value
|
||||
})) }}
|
||||
}) }}
|
||||
sx={{
|
||||
margin: isMobile ? "10px 0" : 0,
|
||||
width:"100%",
|
||||
@ -167,9 +163,9 @@ export const EmailSettingsCard = ({ quizExpand }: Props) => {
|
||||
<TextField
|
||||
value={quiz.config.resultInfo.reply}
|
||||
placeholder={"noreplay@example.ru"}
|
||||
onChange={({ target }: { target: HTMLInputElement }) => {debouncedCallback(updateQuiz(quiz.id, (quiz) => {
|
||||
onChange={({ target }: { target: HTMLInputElement }) => {updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.resultInfo.reply = target.value
|
||||
})) }}
|
||||
}) }}
|
||||
sx={{
|
||||
margin: isMobile ? "10px 0" : 0,
|
||||
width:"100%",
|
||||
@ -212,9 +208,9 @@ export const EmailSettingsCard = ({ quizExpand }: Props) => {
|
||||
<TextField
|
||||
value={quiz.config.resultInfo.replname}
|
||||
placeholder={"Название компании"}
|
||||
onChange={({ target }: { target: HTMLInputElement }) => {debouncedCallback(updateQuiz(quiz.id, (quiz) => {
|
||||
onChange={({ target }: { target: HTMLInputElement }) => {updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.resultInfo.replname = target.value
|
||||
})) }}
|
||||
}) }}
|
||||
sx={{
|
||||
margin: isMobile ? "10px 0" : 0,
|
||||
width:"100%",
|
||||
|
||||
@ -1,135 +1,274 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import * as React from "react";
|
||||
|
||||
import { updateQuiz } from "@root/quizes/actions"
|
||||
import { getQuestionByContentId, updateQuestion, uploadQuestionImage } from "@root/questions/actions"
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
||||
|
||||
import { SwitchSetting } from "../SwichResult";
|
||||
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||
import { UploadImageModal } from "../../Questions/UploadImage/UploadImageModal";
|
||||
import { useDisclosure } from "../../../utils/useDisclosure";
|
||||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
||||
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
Paper,
|
||||
Button,
|
||||
Typography,
|
||||
TextField,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
Box,
|
||||
IconButton,
|
||||
Paper,
|
||||
Button,
|
||||
Typography,
|
||||
TextField,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
FormControl,
|
||||
Popover
|
||||
} from "@mui/material";
|
||||
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
|
||||
|
||||
import ExpandLessIconBG from "@icons/ExpandLessIconBG";
|
||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||
import ShareNetwork from "@icons/ShareNetwork.svg";
|
||||
import ArrowCounterClockWise from "@icons/ArrowCounterClockWise.svg";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import SwitchResult from "../DescriptionForm/SwitchResult";
|
||||
import ButtonsOptionsForm from "../DescriptionForm/ButtinsOptionsForm";
|
||||
import PriceButtons from "../DescriptionForm/PriceButton";
|
||||
import DiscountButtons from "../DescriptionForm/DiscountButtons";
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
import { OneIcon } from "@icons/questionsPage/OneIcon";
|
||||
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
|
||||
import { PointsIcon } from "@icons/questionsPage/PointsIcon";
|
||||
import Trash from "@icons/trash";
|
||||
import Info from "@icons/Info";
|
||||
import ImageAndVideoButtons from "../DescriptionForm/ImageAndVideoButtons";
|
||||
import SettingIcon from "@icons/questionsPage/settingIcon";
|
||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||
import { MutableRefObject } from "react";
|
||||
|
||||
interface Props {
|
||||
resultContract: boolean;
|
||||
resultContract: boolean;
|
||||
resultData: QuizQuestionResult;
|
||||
}
|
||||
|
||||
export const ResultCard = ({ resultContract }:Props) => {
|
||||
const quiz = useCurrentQuiz()
|
||||
const theme = useTheme();
|
||||
export const checkEmptyData = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
||||
let check = true
|
||||
if (
|
||||
resultData.title.length > 0 ||
|
||||
resultData.description.length > 0 ||
|
||||
resultData.content.back.length > 0 ||
|
||||
resultData.content.originalBack.length > 0 ||
|
||||
resultData.content.innerName.length > 0 ||
|
||||
resultData.content.text.length > 0 ||
|
||||
resultData.content.video.length > 0 ||
|
||||
resultData.content.hint.text.length > 0
|
||||
) check = false
|
||||
return check
|
||||
}
|
||||
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1100));
|
||||
const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
||||
const checkEmpty = checkEmptyData({ resultData })
|
||||
const question = getQuestionByContentId(resultData.content.rule.parentId)
|
||||
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const [expand, setExpand] = useState(true)
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setExpand(true)
|
||||
}, [resultContract])
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const id = open ? 'simple-popover' : undefined;
|
||||
|
||||
const [switchState, setSwitchState] = useState<string>("");
|
||||
const [priceButtonsActive, setPriceButtonsActive] = useState<number>(0);
|
||||
const [priceButtonsType, setPriceButtonsType] = useState<string>();
|
||||
const [forwarding, setForwarding] = useState<boolean>(false);
|
||||
|
||||
const buttonsActive = (index: number, type: string) => {
|
||||
setPriceButtonsActive(index);
|
||||
setPriceButtonsType(type);
|
||||
};
|
||||
|
||||
const SSHC = (data: string) => {
|
||||
setSwitchState(data);
|
||||
};
|
||||
|
||||
return(
|
||||
<Paper
|
||||
data-cy="quiz-question-card"
|
||||
return (
|
||||
<>
|
||||
<Info
|
||||
sx={{
|
||||
maxWidth: "796px",
|
||||
width: "100%",
|
||||
borderRadius: "12px",
|
||||
backgroundColor: expand ? "white" : "#EEE4FC",
|
||||
border: expand ? "none" : "1px solid #9A9AAF",
|
||||
boxShadow: "0px 10px 30px #e7e7e7",
|
||||
m: "20px 0"
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: isMobile ? "10px" : "20px",
|
||||
flexDirection: isMobile ? "column" : null,
|
||||
justifyContent: "space-between",
|
||||
minHeight: "40px",
|
||||
"MuiIconButton-root": {
|
||||
|
||||
}}
|
||||
boxShadow: "0 0 10px 10px red"
|
||||
}
|
||||
}}
|
||||
className={checkEmpty ? "blink" : ""}
|
||||
onClick={handleClick}
|
||||
|
||||
/>
|
||||
<Popover
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
sx={{
|
||||
p: '20px',
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
margin: isMobile ? "10px 0" : 0,
|
||||
color: expand ? "#9A9AAF" : "#000000",
|
||||
}}
|
||||
>
|
||||
Заголовок результата
|
||||
<Typography>
|
||||
{resultData?.content.rule.parentId === "line" ? "Единый результат в конце прохождения опросника без ветвления"
|
||||
:
|
||||
`Заголовок вопроса, после которого появится результат: "${question?.title || "нет заголовка"}"`
|
||||
}
|
||||
|
||||
</Typography>
|
||||
{checkEmpty &&
|
||||
<Typography color="red">
|
||||
Вы не заполнили этот результат никакими данными
|
||||
</Typography>
|
||||
<Box
|
||||
}
|
||||
|
||||
</Paper>
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const ResultCard = ({ resultContract, resultData }: Props) => {
|
||||
console.log("resultData", resultData)
|
||||
|
||||
const quizQid = useCurrentQuiz()?.qid;
|
||||
const theme = useTheme();
|
||||
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(800));
|
||||
|
||||
const [expand, setExpand] = React.useState(true)
|
||||
const [resultCardSettings, setResultCardSettings] = React.useState(false)
|
||||
const [buttonPlus, setButtonPlus] = React.useState(true)
|
||||
|
||||
React.useEffect(() => {
|
||||
setExpand(true)
|
||||
}, [resultContract])
|
||||
|
||||
|
||||
const {
|
||||
isCropModalOpen,
|
||||
openCropModal,
|
||||
closeCropModal,
|
||||
imageBlob,
|
||||
originalImageUrl,
|
||||
setCropModalImageBlob,
|
||||
} = useCropModalState();
|
||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||||
|
||||
|
||||
|
||||
async function handleImageUpload(file: File) {
|
||||
const url = await uploadQuestionImage(resultData.id, quizQid, file, (question, url) => {
|
||||
|
||||
question.content.back = url;
|
||||
question.content.originalBack = url;
|
||||
});
|
||||
closeImageUploadModal();
|
||||
openCropModal(file, url);
|
||||
}
|
||||
|
||||
function handleCropModalSaveClick(imageBlob: Blob) {
|
||||
uploadQuestionImage(resultData.id, quizQid, imageBlob, (question, url) => {
|
||||
question.content.back = url;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Paper
|
||||
data-cy="quiz-question-card"
|
||||
sx={{
|
||||
maxWidth: "796px",
|
||||
width: "100%",
|
||||
borderRadius: "12px",
|
||||
backgroundColor: expand ? "white" : "#EEE4FC",
|
||||
border: expand ? "none" : "1px solid #9A9AAF",
|
||||
boxShadow: "0px 10px 30px #e7e7e7",
|
||||
m: "20px 0"
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: expand ? "none" : "flex",
|
||||
alignItems: "center",
|
||||
padding: isMobile ? "10px" : "20px",
|
||||
flexDirection: isMobile ? "column" : null,
|
||||
justifyContent: "space-between",
|
||||
minHeight: "40px",
|
||||
|
||||
}}
|
||||
>
|
||||
<FormControl
|
||||
variant="standard"
|
||||
sx={{
|
||||
p: 0,
|
||||
maxWidth: isTablet ? "549px" : "640px",
|
||||
width: "100%",
|
||||
marginRight: isMobile ? "0px" : "16.1px",
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
value={resultData.title}
|
||||
placeholder={"Заголовок результата"}
|
||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, question => question.title = target.value)}
|
||||
sx={{
|
||||
margin: isMobile ? "10px 0" : 0,
|
||||
"& .MuiInputBase-root": {
|
||||
color: "#000000",
|
||||
backgroundColor: expand
|
||||
? theme.palette.background.default
|
||||
: "transparent",
|
||||
height: "48px",
|
||||
borderRadius: "10px",
|
||||
".MuiOutlinedInput-notchedOutline": {
|
||||
borderWidth: "1px !important",
|
||||
border: !expand ? "none" : null,
|
||||
},
|
||||
"& .MuiInputBase-input::placeholder": {
|
||||
color: "#4D4D4D",
|
||||
opacity: 0.8,
|
||||
},
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: {
|
||||
p: 0,
|
||||
fontSize: "18px",
|
||||
lineHeight: "21px",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
width: isMobile ? "100%" : "auto",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
sx={{ padding: "0", margin: "5px" }}
|
||||
disableRipple
|
||||
data-cy="expand-question"
|
||||
onClick={() => setExpand(!expand)}
|
||||
>
|
||||
{expand ? (
|
||||
<ExpandLessIconBG />
|
||||
) : (
|
||||
<ExpandLessIcon
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
width: isMobile ? "100%" : "auto",
|
||||
position: "relative",
|
||||
boxSizing: "border-box",
|
||||
fill: theme.palette.brightPurple.main,
|
||||
background: "#FFF",
|
||||
borderRadius: "6px",
|
||||
height: "30px",
|
||||
width: "30px",
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
sx={{ padding: "0", margin: "5px" }}
|
||||
disableRipple
|
||||
data-cy="expand-question"
|
||||
onClick={() => setExpand(!expand)}
|
||||
>
|
||||
{expand ? (
|
||||
<ExpandLessIconBG />
|
||||
) : (
|
||||
<ExpandLessIcon
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
fill: theme.palette.brightPurple.main,
|
||||
background: "#FFF",
|
||||
borderRadius: "6px",
|
||||
height: "30px",
|
||||
width: "30px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
</Box>
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
<InfoView resultData={resultData} />
|
||||
</Box>
|
||||
{expand && (
|
||||
<Box
|
||||
</Box>
|
||||
{expand && (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
maxWidth: "796px",
|
||||
@ -150,24 +289,48 @@ export const ResultCard = ({ resultContract }:Props) => {
|
||||
mb: "19px",
|
||||
}}
|
||||
>
|
||||
<CustomTextField placeholder="Заголовок вопроса" text={""} />
|
||||
<IconButton>
|
||||
<ExpandMoreIcon />
|
||||
<CustomTextField
|
||||
value={resultData.title}
|
||||
placeholder={"Заголовок результата"}
|
||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, question => question.title = target.value)} />
|
||||
<IconButton
|
||||
sx={{ padding: "0", margin: "5px" }}
|
||||
disableRipple
|
||||
data-cy="expand-question"
|
||||
onClick={() => setExpand(!expand)}
|
||||
>
|
||||
<ExpandLessIconBG />
|
||||
</IconButton>
|
||||
<OneIcon />
|
||||
<PointsIcon style={{ color: "#9A9AAF" }} />
|
||||
<InfoView resultData={resultData} />
|
||||
|
||||
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: "flex" }}>
|
||||
<PriceButtons
|
||||
ButtonsActive={buttonsActive}
|
||||
priceButtonsActive={priceButtonsActive}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
margin: "20px 0"
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
value={resultData.description}
|
||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.description = target.value)}
|
||||
placeholder={"Заголовок пожирнее"}
|
||||
sx={{
|
||||
borderRadius: "8px",
|
||||
height: "48px",
|
||||
width: "100%",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
<TextField
|
||||
|
||||
value={resultData.content.text}
|
||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.text = target.value)}
|
||||
fullWidth
|
||||
placeholder="Описание"
|
||||
multiline
|
||||
rows={4}
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
backgroundColor: "#F2F3F7",
|
||||
@ -178,6 +341,7 @@ export const ResultCard = ({ resultContract }:Props) => {
|
||||
}}
|
||||
inputProps={{
|
||||
sx: {
|
||||
height: "85px",
|
||||
borderRadius: "10px",
|
||||
fontSize: "18px",
|
||||
lineHeight: "21px",
|
||||
@ -185,35 +349,183 @@ export const ResultCard = ({ resultContract }:Props) => {
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ImageAndVideoButtons />
|
||||
{priceButtonsType === "smooth" ? (
|
||||
<Box sx={{ mb: "20px" }}>
|
||||
<Box sx={{ display: "flex", alignItems: "center", mb: "14xp" }}>
|
||||
<Typography
|
||||
component={"h6"}
|
||||
sx={{ weight: "500", fontSize: "18px" }}
|
||||
>
|
||||
Призыв к действию
|
||||
</Typography>
|
||||
<IconButton sx={{ borderRadius: "6px", padding: "2px" }}>
|
||||
<DeleteIcon style={{ color: "#4D4D4D" }} />
|
||||
</IconButton>
|
||||
|
||||
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
mt: "20px",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
color: resultData.content.useImage ? "#7E2AEA" : "#9A9AAF",
|
||||
fontSize: "16px",
|
||||
"&:hover": {
|
||||
background: "none",
|
||||
},
|
||||
}}
|
||||
variant="text"
|
||||
onClick={() => updateQuestion(resultData.id, (question) => question.content.useImage = true)}
|
||||
>
|
||||
Изображение
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
color: resultData.content.useImage ? "#9A9AAF" : "#7E2AEA",
|
||||
fontSize: "16px",
|
||||
"&:hover": {
|
||||
background: "none",
|
||||
},
|
||||
}}
|
||||
variant="text"
|
||||
onClick={() => updateQuestion(resultData.id, (question) => question.content.useImage = false)}
|
||||
>
|
||||
Видео
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<UploadImageModal
|
||||
isOpen={isImageUploadOpen}
|
||||
onClose={closeImageUploadModal}
|
||||
handleImageChange={handleImageUpload}
|
||||
/>
|
||||
<CropModal
|
||||
isOpen={isCropModalOpen}
|
||||
imageBlob={imageBlob}
|
||||
originalImageUrl={originalImageUrl}
|
||||
setCropModalImageBlob={setCropModalImageBlob}
|
||||
onClose={closeCropModal}
|
||||
onSaveImageClick={handleCropModalSaveClick}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{
|
||||
resultData.content.useImage &&
|
||||
<Box
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
mb: "30px"
|
||||
}}
|
||||
>
|
||||
<AddOrEditImageButton
|
||||
imageSrc={resultData.content.back}
|
||||
onImageClick={() => {
|
||||
if (resultData.content.back) {
|
||||
return openCropModal(
|
||||
resultData.content.back,
|
||||
resultData.content.originalBack
|
||||
);
|
||||
}
|
||||
|
||||
openImageUploadModal();
|
||||
}}
|
||||
onPlusClick={() => {
|
||||
openImageUploadModal();
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box sx={{ display: "flex" }}>
|
||||
}
|
||||
{
|
||||
!resultData.content.useImage &&
|
||||
<Box
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
mb: "30px"
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder="URL видео"
|
||||
text={resultData.content.video ?? ""}
|
||||
onChange={e => updateQuestion(resultData.id, q => {
|
||||
resultData.content.video = e.target.value;
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
</Box>
|
||||
|
||||
|
||||
|
||||
{
|
||||
buttonPlus ?
|
||||
<Button
|
||||
onClick={() => {
|
||||
setButtonPlus(false)
|
||||
}}
|
||||
sx={{
|
||||
display: "inline flex",
|
||||
height: "48px",
|
||||
padding: "10px 20px",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
flexShrink: 0,
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #9A9AAF",
|
||||
background: " #F2F3F7",
|
||||
color: "#9A9AAF",
|
||||
mb: "30px"
|
||||
}}
|
||||
>
|
||||
Кнопка +
|
||||
</Button>
|
||||
:
|
||||
<Box
|
||||
sx={{
|
||||
mb: "30px"
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography component={"span"} sx={{ weight: "500", fontSize: "18px", mb: "10px" }}>
|
||||
Призыв к действию
|
||||
</Typography>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setButtonPlus(true)
|
||||
updateQuestion(resultData.id, (q) => q.content.hint.text = "")
|
||||
}}
|
||||
>
|
||||
<Trash />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<TextField
|
||||
placeholder="Узнать подробнее"
|
||||
value={resultData.content.hint.text}
|
||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.hint.text = target.value)}
|
||||
fullWidth
|
||||
placeholder="Например: узнать подробнее"
|
||||
sx={{
|
||||
width: "410px",
|
||||
"& .MuiInputBase-root": {
|
||||
backgroundColor: "#F2F3F7",
|
||||
width: "410px",
|
||||
width: "409px",
|
||||
height: "48px",
|
||||
borderRadius: "10px",
|
||||
borderRadius: "8px",
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: {
|
||||
height: "85px",
|
||||
borderRadius: "10px",
|
||||
fontSize: "18px",
|
||||
lineHeight: "21px",
|
||||
@ -221,71 +533,78 @@ export const ResultCard = ({ resultContract }:Props) => {
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => setForwarding(true)}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
display: forwarding ? "none" : "",
|
||||
ml: "20px",
|
||||
mb: "20px",
|
||||
}}
|
||||
>
|
||||
Переадресация +
|
||||
</Button>
|
||||
{forwarding ? (
|
||||
<Box sx={{ ml: "20px", mt: "-36px" }}>
|
||||
<Box
|
||||
sx={{ display: "flex", alignItems: "center", mb: "14xp" }}
|
||||
>
|
||||
<Typography
|
||||
component={"h6"}
|
||||
sx={{ weight: "500", fontSize: "18px" }}
|
||||
>
|
||||
Переадресация
|
||||
</Typography>
|
||||
<IconButton sx={{ borderRadius: "6px", padding: "2px" }}>
|
||||
<DeleteIcon style={{ color: "#4D4D4D" }} />
|
||||
</IconButton>
|
||||
<Info />
|
||||
</Box>
|
||||
<Box>
|
||||
<TextField
|
||||
placeholder="https://exemple.ru"
|
||||
fullWidth
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
backgroundColor: "#F2F3F7",
|
||||
width: "326px",
|
||||
height: "48px",
|
||||
borderRadius: "10px",
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: {
|
||||
borderRadius: "10px",
|
||||
fontSize: "18px",
|
||||
lineHeight: "21px",
|
||||
py: 0,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Button variant="outlined" sx={{ mb: "20px" }}>
|
||||
Кнопка +
|
||||
</Button>
|
||||
)}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
background: "#F2F3F7",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
padding: "20px",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<MiniButtonSetting
|
||||
onClick={() => {
|
||||
setResultCardSettings(!resultCardSettings)
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
resultCardSettings
|
||||
? theme.palette.brightPurple.main
|
||||
: "transparent",
|
||||
color:
|
||||
resultCardSettings ? "#ffffff" : theme.palette.grey3.main,
|
||||
"&:hover": {
|
||||
backgroundColor: resultCardSettings ? "#581CA7" : "#7E2AEA",
|
||||
color: "white"
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SettingIcon
|
||||
color={
|
||||
resultCardSettings ? "#ffffff" : theme.palette.grey3.main
|
||||
}
|
||||
/>
|
||||
{!isTablet && "Настройки"}
|
||||
</MiniButtonSetting>
|
||||
</Box>
|
||||
</Box>
|
||||
<ButtonsOptionsForm switchState={switchState} SSHC={SSHC} />
|
||||
<SwitchResult switchState={switchState} totalIndex={0} />
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
{
|
||||
resultCardSettings &&
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "white",
|
||||
p: "20px",
|
||||
borderRadius: "0 0 12px 12px"
|
||||
}}
|
||||
>
|
||||
<CustomTextField
|
||||
placeholder={"Внутреннее описание вопроса"}
|
||||
value={resultData.innerName}
|
||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.innerName = target.value)}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</Paper >
|
||||
)
|
||||
}
|
||||
|
||||
@ -10,8 +10,8 @@ import { nanoid } from "nanoid";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
||||
import { RequestQueue } from "../../utils/requestQueue";
|
||||
import { updateRootContentId } from "@root/quizes/actions"
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
||||
import { updateRootContentId } from "@root/quizes/actions";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { QuestionsStore, useQuestionsStore } from "./store";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
|
||||
@ -90,6 +90,7 @@ const updateQuestionOrders = () => {
|
||||
const questions = useQuestionsStore.getState().questions.filter(
|
||||
(question): question is AnyTypedQuizQuestion => question.type !== null && question.type !== "result"
|
||||
);
|
||||
console.log(questions);
|
||||
|
||||
questions.forEach((question, index) => {
|
||||
updateQuestion(question.id, question => {
|
||||
@ -162,6 +163,10 @@ export const updateQuestion = (
|
||||
try {
|
||||
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);
|
||||
} catch (error) {
|
||||
if (isAxiosCanceledError(error)) return;
|
||||
@ -306,13 +311,15 @@ export const createTypedQuestion = async (
|
||||
if (!question) return;
|
||||
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 {
|
||||
const createdQuestion = await questionApi.create({
|
||||
quiz_id: question.quizId,
|
||||
type,
|
||||
title: question.title,
|
||||
description: question.description,
|
||||
page: questions.length,
|
||||
page: questions.length - untypedOrResultQuestionsLength,
|
||||
required: false,
|
||||
content: JSON.stringify(defaultQuestionByType[type].content),
|
||||
});
|
||||
@ -334,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);
|
||||
if (!question) return;
|
||||
|
||||
|
||||
|
||||
if (question.type === null) {
|
||||
removeQuestion(questionId);
|
||||
return;
|
||||
@ -346,47 +355,10 @@ export const deleteQuestion = async (questionId: string, quizId: string) => requ
|
||||
|
||||
try {
|
||||
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
|
||||
|
||||
console.log(newRule)
|
||||
updateQuestion(question.content.rule.parentId, (PQ) => {
|
||||
PQ.content.rule = newRule
|
||||
})
|
||||
}
|
||||
|
||||
removeQuestion(questionId);
|
||||
|
||||
updateQuestionOrders();
|
||||
} catch (error) {
|
||||
devlog("Error deleting question", error);
|
||||
enqueueSnackbar("Не удалось удалить вопрос");
|
||||
@ -400,8 +372,8 @@ export const copyQuestion = async (questionId: string, quizId: number) => reques
|
||||
const frontId = nanoid();
|
||||
if (question.type === null) {
|
||||
const copiedQuestion = structuredClone(question);
|
||||
copiedQuestion.id = frontId
|
||||
copiedQuestion.content.id = frontId
|
||||
copiedQuestion.id = frontId;
|
||||
copiedQuestion.content.id = frontId;
|
||||
|
||||
setProducedState(state => {
|
||||
state.questions.push(copiedQuestion);
|
||||
@ -421,7 +393,7 @@ export const copyQuestion = async (questionId: string, quizId: number) => reques
|
||||
copiedQuestion.backendId = newQuestionId;
|
||||
copiedQuestion.id = frontId;
|
||||
copiedQuestion.content.id = frontId;
|
||||
copiedQuestion.content.rule = { main: [], parentId: "", default: "" };
|
||||
copiedQuestion.content.rule = { main: [], parentId: "", default: "", children: [] };
|
||||
|
||||
setProducedState(state => {
|
||||
state.questions.push(copiedQuestion);
|
||||
@ -469,41 +441,41 @@ export const updateDragQuestionContentId = (contentId?: string) => {
|
||||
};
|
||||
|
||||
export const clearRuleForAll = () => {
|
||||
const { questions } = useQuestionsStore.getState()
|
||||
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 = ""
|
||||
})
|
||||
question.content.rule.parentId = "";
|
||||
question.content.rule.main = [];
|
||||
question.content.rule.default = "";
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const updateOpenBranchingPanel = (value: boolean) => useQuestionsStore.setState({ openBranchingPanel: value });
|
||||
|
||||
|
||||
let UDTOABM: ReturnType<typeof setTimeout>;
|
||||
export const updateDesireToOpenABranchingModal = (contentId: string) => {
|
||||
useQuestionsStore.setState({ desireToOpenABranchingModal: contentId })
|
||||
clearTimeout(UDTOABM)
|
||||
useQuestionsStore.setState({ desireToOpenABranchingModal: contentId });
|
||||
clearTimeout(UDTOABM);
|
||||
UDTOABM = setTimeout(() => {
|
||||
useQuestionsStore.setState({ desireToOpenABranchingModal: null })
|
||||
}, 7000)
|
||||
}
|
||||
useQuestionsStore.setState({ desireToOpenABranchingModal: null });
|
||||
}, 7000);
|
||||
};
|
||||
export const clearDesireToOpenABranchingModal = () => {
|
||||
useQuestionsStore.setState({ desireToOpenABranchingModal: null })
|
||||
}
|
||||
useQuestionsStore.setState({ desireToOpenABranchingModal: null });
|
||||
};
|
||||
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 => {
|
||||
const frontId = nanoid()
|
||||
const content = JSON.parse(JSON.stringify(defaultQuestionByType["result"].content))
|
||||
content.id = frontId
|
||||
if (parentContentId) content.rule.parentId = parentContentId
|
||||
const frontId = nanoid();
|
||||
const content = JSON.parse(JSON.stringify(defaultQuestionByType["result"].content));
|
||||
content.id = frontId;
|
||||
if (parentContentId) content.rule.parentId = parentContentId;
|
||||
state.questions.push({
|
||||
id: frontId,
|
||||
quizId,
|
||||
|
||||
@ -9,7 +9,6 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
|
||||
|
||||
export function useQuestions() {
|
||||
console.log("вызываю вопросы")
|
||||
const quiz = useCurrentQuiz();
|
||||
const { isLoading, error, isValidating } = useSWR(["questions", quiz?.backendId], ([, id]) => questionApi.getList({ quiz_id: id }), {
|
||||
onSuccess: setQuestions,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user