добавление иконок, модалка выбора вопроса по клику
This commit is contained in:
parent
2b38c15e3e
commit
7dc083907f
@ -12,6 +12,7 @@ export const QUIZ_QUESTION_BASE: Omit<QuizQuestionBase, "id" | "backendId"> = {
|
|||||||
deleted: false,
|
deleted: false,
|
||||||
deleteTimeoutId: 0,
|
deleteTimeoutId: 0,
|
||||||
content: {
|
content: {
|
||||||
|
id: "",
|
||||||
hint: {
|
hint: {
|
||||||
text: "",
|
text: "",
|
||||||
video: "",
|
video: "",
|
||||||
|
|||||||
@ -46,16 +46,18 @@ export interface RawQuestion {
|
|||||||
|
|
||||||
export function rawQuestionToQuestion(rawQuestion: RawQuestion): AnyTypedQuizQuestion {
|
export function rawQuestionToQuestion(rawQuestion: RawQuestion): AnyTypedQuizQuestion {
|
||||||
let content = defaultQuestionByType[rawQuestion.type].content;
|
let content = defaultQuestionByType[rawQuestion.type].content;
|
||||||
|
const frontId = nanoid()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
content = JSON.parse(rawQuestion.content);
|
content = JSON.parse(rawQuestion.content);
|
||||||
|
if (content.id.length === 0 || content.id.length === undefined) content.id = frontId
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("Cannot parse question content from string, using default content", error);
|
console.warn("Cannot parse question content from string, using default content", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
backendId: rawQuestion.id,
|
backendId: rawQuestion.id,
|
||||||
id: nanoid(),
|
id: frontId,
|
||||||
description: rawQuestion.description,
|
description: rawQuestion.description,
|
||||||
page: rawQuestion.page,
|
page: rawQuestion.page,
|
||||||
quizId: rawQuestion.quiz_id,
|
quizId: rawQuestion.quiz_id,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type {
|
|||||||
export interface QuizQuestionDate extends QuizQuestionBase {
|
export interface QuizQuestionDate extends QuizQuestionBase {
|
||||||
type: "date";
|
type: "date";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Необязательный вопрос" */
|
/** Чекбокс "Необязательный вопрос" */
|
||||||
required: boolean;
|
required: boolean;
|
||||||
/** Чекбокс "Внутреннее название вопроса" */
|
/** Чекбокс "Внутреннее название вопроса" */
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
export interface QuizQuestionEmoji extends QuizQuestionBase {
|
export interface QuizQuestionEmoji extends QuizQuestionBase {
|
||||||
type: "emoji";
|
type: "emoji";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Можно несколько" */
|
/** Чекбокс "Можно несколько" */
|
||||||
multi: boolean;
|
multi: boolean;
|
||||||
/** Чекбокс "Вариант "свой ответ"" */
|
/** Чекбокс "Вариант "свой ответ"" */
|
||||||
|
|||||||
@ -17,6 +17,7 @@ export type UploadFileType = keyof typeof UPLOAD_FILE_TYPES_MAP;
|
|||||||
export interface QuizQuestionFile extends QuizQuestionBase {
|
export interface QuizQuestionFile extends QuizQuestionBase {
|
||||||
type: "file";
|
type: "file";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Необязательный вопрос" */
|
/** Чекбокс "Необязательный вопрос" */
|
||||||
required: boolean;
|
required: boolean;
|
||||||
/** Чекбокс "Внутреннее название вопроса" */
|
/** Чекбокс "Внутреннее название вопроса" */
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
export interface QuizQuestionImages extends QuizQuestionBase {
|
export interface QuizQuestionImages extends QuizQuestionBase {
|
||||||
type: "images";
|
type: "images";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Вариант "свой ответ"" */
|
/** Чекбокс "Вариант "свой ответ"" */
|
||||||
own: boolean;
|
own: boolean;
|
||||||
/** Чекбокс "Можно несколько" */
|
/** Чекбокс "Можно несколько" */
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type {
|
|||||||
export interface QuizQuestionNumber extends QuizQuestionBase {
|
export interface QuizQuestionNumber extends QuizQuestionBase {
|
||||||
type: "number";
|
type: "number";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Необязательный вопрос" */
|
/** Чекбокс "Необязательный вопрос" */
|
||||||
required: boolean;
|
required: boolean;
|
||||||
/** Чекбокс "Внутреннее название вопроса" */
|
/** Чекбокс "Внутреннее название вопроса" */
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type {
|
|||||||
export interface QuizQuestionPage extends QuizQuestionBase {
|
export interface QuizQuestionPage extends QuizQuestionBase {
|
||||||
type: "page";
|
type: "page";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Внутреннее название вопроса" */
|
/** Чекбокс "Внутреннее название вопроса" */
|
||||||
innerNameCheck: boolean;
|
innerNameCheck: boolean;
|
||||||
/** Поле "Внутреннее название вопроса" */
|
/** Поле "Внутреннее название вопроса" */
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type {
|
|||||||
export interface QuizQuestionRating extends QuizQuestionBase {
|
export interface QuizQuestionRating extends QuizQuestionBase {
|
||||||
type: "rating";
|
type: "rating";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Необязательный вопрос" */
|
/** Чекбокс "Необязательный вопрос" */
|
||||||
required: boolean;
|
required: boolean;
|
||||||
/** Чекбокс "Внутреннее название вопроса" */
|
/** Чекбокс "Внутреннее название вопроса" */
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
export interface QuizQuestionSelect extends QuizQuestionBase {
|
export interface QuizQuestionSelect extends QuizQuestionBase {
|
||||||
type: "select";
|
type: "select";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Можно несколько" */
|
/** Чекбокс "Можно несколько" */
|
||||||
multi: boolean;
|
multi: boolean;
|
||||||
/** Чекбокс "Необязательный вопрос" */
|
/** Чекбокс "Необязательный вопрос" */
|
||||||
|
|||||||
@ -62,6 +62,7 @@ export interface QuizQuestionBase {
|
|||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
deleteTimeoutId: number;
|
deleteTimeoutId: number;
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type {
|
|||||||
export interface QuizQuestionText extends QuizQuestionBase {
|
export interface QuizQuestionText extends QuizQuestionBase {
|
||||||
type: "text";
|
type: "text";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
/** Чекбокс "Внутреннее название вопроса" */
|
/** Чекбокс "Внутреннее название вопроса" */
|
||||||
innerNameCheck: boolean;
|
innerNameCheck: boolean;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
export interface QuizQuestionVariant extends QuizQuestionBase {
|
export interface QuizQuestionVariant extends QuizQuestionBase {
|
||||||
type: "variant";
|
type: "variant";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Длинный текстовый ответ" */
|
/** Чекбокс "Длинный текстовый ответ" */
|
||||||
largeCheck: boolean;
|
largeCheck: boolean;
|
||||||
/** Чекбокс "Можно несколько" */
|
/** Чекбокс "Можно несколько" */
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
export interface QuizQuestionVarImg extends QuizQuestionBase {
|
export interface QuizQuestionVarImg extends QuizQuestionBase {
|
||||||
type: "varimg";
|
type: "varimg";
|
||||||
content: {
|
content: {
|
||||||
|
id: string;
|
||||||
/** Чекбокс "Вариант "свой ответ"" */
|
/** Чекбокс "Вариант "свой ответ"" */
|
||||||
own: boolean;
|
own: boolean;
|
||||||
/** Чекбокс "Внутреннее название вопроса" */
|
/** Чекбокс "Внутреннее название вопроса" */
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import popper from "cytoscape-popper";
|
|||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { AnyQuizQuestion } from "@model/questionTypes/shared"
|
import { AnyQuizQuestion } from "@model/questionTypes/shared"
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { clearDragQuestionId, getQuestionById, updateQuestion, updateOpenedModalSettingsId } from "@root/questions/actions";
|
import { cleardragQuestionContentId, getQuestionById, updateQuestion, updateOpenedModalSettingsId, getQuestionByContentId } from "@root/questions/actions";
|
||||||
|
|
||||||
import { storeToNodes } from "./helper";
|
import { storeToNodes } from "./helper";
|
||||||
|
|
||||||
@ -92,11 +92,26 @@ const stylesheet: Stylesheet[] = [
|
|||||||
|
|
||||||
Cytoscape.use(popper);
|
Cytoscape.use(popper);
|
||||||
|
|
||||||
export const CsComponent = () => {
|
interface Props {
|
||||||
|
modalQuestionParentContentId: string;
|
||||||
|
modalQuestionTargetContentId: string;
|
||||||
|
setOpenedModalQuestions: (open:boolean) => void;
|
||||||
|
setModalQuestionParentContentId: (id:string) => void;
|
||||||
|
setModalQuestionTargetContentId: (id:string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const CsComponent = ({
|
||||||
|
modalQuestionParentContentId,
|
||||||
|
modalQuestionTargetContentId,
|
||||||
|
setOpenedModalQuestions,
|
||||||
|
setModalQuestionParentContentId,
|
||||||
|
setModalQuestionTargetContentId
|
||||||
|
}:Props) => {
|
||||||
console.log("Я существую")
|
console.log("Я существую")
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
const { dragQuestionId, questions } = useQuestionsStore()
|
const { dragQuestionContentId, questions } = useQuestionsStore()
|
||||||
const [startCreate, setStartCreate] = useState("");
|
const [startCreate, setStartCreate] = useState("");
|
||||||
const [startRemove, setStartRemove] = useState("");
|
const [startRemove, setStartRemove] = useState("");
|
||||||
|
|
||||||
@ -106,73 +121,251 @@ 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);
|
||||||
|
|
||||||
|
useEffect(() =>{
|
||||||
|
if (modalQuestionTargetContentId.length !== 0 && modalQuestionParentContentId.length !== 0) {
|
||||||
|
console.log("был выбран вопрос " + modalQuestionTargetContentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [modalQuestionTargetContentId])
|
||||||
|
|
||||||
const addNode = ({ parentNodeId }: { parentNodeId: string }) => {
|
const addNode = ({ parentNodeContentId }: { parentNodeContentId: string }) => {
|
||||||
const cy = cyRef?.current
|
const cy = cyRef?.current
|
||||||
const parentNodeChildren = cy?.$('edge[source = "' + parentNodeId + '"]').length
|
const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length
|
||||||
const targetQuestion = { ...getQuestionById(dragQuestionId) } as AnyQuizQuestion
|
const targetQuestion = { ...getQuestionByContentId(dragQuestionContentId) } as AnyQuizQuestion
|
||||||
console.log(parentNodeChildren, parentNodeId)
|
console.log(parentNodeChildren, parentNodeContentId)
|
||||||
|
|
||||||
if (targetQuestion && targetQuestion && parentNodeId && parentNodeChildren !== undefined) {
|
if (targetQuestion && targetQuestion && parentNodeContentId && parentNodeChildren !== undefined) {
|
||||||
|
console.log(targetQuestion, targetQuestion, parentNodeContentId, parentNodeChildren)
|
||||||
cy?.add([
|
cy?.add([
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
id: targetQuestion.id,
|
id: targetQuestion.content.id,
|
||||||
label: targetQuestion.title || "noname"
|
label: targetQuestion.title || "noname"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
source: parentNodeId,
|
source: parentNodeContentId,
|
||||||
target: targetQuestion.id
|
target: targetQuestion.content.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
// clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
|
||||||
|
|
||||||
//Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления
|
|
||||||
if (parentNodeChildren > 1) {
|
|
||||||
updateOpenedModalSettingsId(parentNodeId)
|
|
||||||
} else {
|
|
||||||
//Если ребёнок первый - добавляем его родителю как дефолтный
|
|
||||||
updateQuestion(parentNodeId, question => question.content.rule.default = targetQuestion.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
//предупреждаем добавленный вопрос о том, кто его родитель
|
|
||||||
updateQuestion(targetQuestion.id, question => question.content.rule.parentId = parentNodeId)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log(dragQuestionId)
|
console.log(dragQuestionContentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clearDataAfterAddNode = ({ parentNodeContentId, targetQuestion, parentNodeChildren }: { parentNodeContentId: string, targetQuestion: AnyQuizQuestion, parentNodeChildren:number }) => {
|
||||||
|
//предупреждаем добавленный вопрос о том, кто его родитель
|
||||||
|
updateQuestion(targetQuestion.id, question => {
|
||||||
|
question.content.rule.parentId = parentNodeContentId
|
||||||
|
question.content.rule.main = []
|
||||||
|
})
|
||||||
|
|
||||||
|
//Если детей больше 1 - предупреждаем стор вопросов об открытии модалки ветвления
|
||||||
|
if (parentNodeChildren > 1) {
|
||||||
|
updateOpenedModalSettingsId(parentNodeContentId)
|
||||||
|
} else {
|
||||||
|
//Если ребёнок первый - добавляем его родителю как дефолтный
|
||||||
|
updateQuestion(parentNodeContentId, question => question.content.rule.default = targetQuestion.content.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const removeNode = ({ targetNodeContentId }: { targetNodeContentId: string }) => {
|
||||||
|
const cy = cyRef?.current
|
||||||
|
//получить можно только дочерние ноды
|
||||||
|
console.log(cy?.$('#'+targetNodeContentId))
|
||||||
|
console.log(cy?.$('#'+targetNodeContentId)?.data())
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
const clearDataAfterRemoveNode = () => {
|
||||||
|
|
||||||
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (startCreate) {
|
if (startCreate) {
|
||||||
addNode({ parentNodeId: startCreate });
|
addNode({ parentNodeContentId: startCreate });
|
||||||
clearDragQuestionId()
|
cleardragQuestionContentId()
|
||||||
setStartCreate("");
|
setStartCreate("");
|
||||||
}
|
}
|
||||||
}, [startCreate]);
|
}, [startCreate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (startRemove) {
|
if (startRemove) {
|
||||||
// removeNode(quiz, startRemove);
|
// removeNode({ targetNodeContentId: startRemove });
|
||||||
setStartRemove("");
|
setStartRemove("");
|
||||||
}
|
}
|
||||||
}, [startRemove]);
|
}, [startRemove]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.querySelector("#root")?.addEventListener("mouseup", clearDragQuestionId);
|
document.querySelector("#root")?.addEventListener("mouseup", cleardragQuestionContentId);
|
||||||
const cy = cyRef.current;
|
const cy = cyRef.current;
|
||||||
cy?.add(storeToNodes(questions))
|
// cy?.add(storeToNodes(questions))
|
||||||
cy?.layout(ly).run()
|
cy?.add(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 250,
|
||||||
|
"y": 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 2",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 500,
|
||||||
|
"y": 300
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 3",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 500,
|
||||||
|
"y": 400
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 2 4",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 750,
|
||||||
|
"y": 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 2 6",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 750,
|
||||||
|
"y": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 3 5",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 750,
|
||||||
|
"y": 300
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 3 7",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 750,
|
||||||
|
"y": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 2 6 9867874",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 1000,
|
||||||
|
"y": 300
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 2 6 7398789",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 1000,
|
||||||
|
"y": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"id": "1 2 6 9484789",
|
||||||
|
"label": "нет имени"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": 1000,
|
||||||
|
"y": 700
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1",
|
||||||
|
"target": "1 2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1",
|
||||||
|
"target": "1 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1 2",
|
||||||
|
"target": "1 2 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1 2",
|
||||||
|
"target": "1 2 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1 3",
|
||||||
|
"target": "1 3 5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1 3",
|
||||||
|
"target": "1 3 7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1 2 6",
|
||||||
|
"target": "1 2 6 9867874"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1 2 6",
|
||||||
|
"target": "1 2 6 7398789"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"source": "1 2 6",
|
||||||
|
"target": "1 2 6 9484789"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
//cy?.layout(ly).run()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.querySelector("#root")?.removeEventListener("mouseup", clearDragQuestionId);
|
document.querySelector("#root")?.removeEventListener("mouseup", cleardragQuestionContentId);
|
||||||
layoutsContainer.current?.remove();
|
layoutsContainer.current?.remove();
|
||||||
plusesContainer.current?.remove();
|
plusesContainer.current?.remove();
|
||||||
crossesContainer.current?.remove();
|
crossesContainer.current?.remove();
|
||||||
@ -206,85 +399,11 @@ export const CsComponent = () => {
|
|||||||
data.id && removeButtons(data.id);
|
data.id && removeButtons(data.id);
|
||||||
})
|
})
|
||||||
initialPopperIcons(e)
|
initialPopperIcons(e)
|
||||||
console.log('ready', e)
|
// console.log('ready', e)
|
||||||
console.log(e.cy.nodes())
|
// console.log(e.cy.nodes())
|
||||||
console.log(e.cy.nodes().data())
|
// console.log(e.cy.nodes().data())
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ly, setly] = useState<any>({
|
|
||||||
name: 'preset',
|
|
||||||
|
|
||||||
positions: (e) => {
|
|
||||||
console.log("идёт расчёт позиции")
|
|
||||||
const id = e.id()
|
|
||||||
const incomming = e.cy().edges(`[target="${id}"]`)
|
|
||||||
const layer = 0
|
|
||||||
e.removeData('lastChild')
|
|
||||||
|
|
||||||
if (incomming.length === 0) {
|
|
||||||
const children = e.cy().edges(`[source="${id}"]`)
|
|
||||||
e.data('layer', layer)
|
|
||||||
e.data('children', children.targets().length)
|
|
||||||
const queue = []
|
|
||||||
children.forEach(n => {
|
|
||||||
queue.push({ task: n.target(), layer: layer + 1 })
|
|
||||||
})
|
|
||||||
while (queue.length) {
|
|
||||||
const task = queue.pop()
|
|
||||||
task.task.data('layer', task.layer)
|
|
||||||
const children = e.cy().edges(`[source="${task.task.id()}"]`)
|
|
||||||
task.task.data('children', children.targets().length)
|
|
||||||
if (children.length !== 0) {
|
|
||||||
children.forEach(n => queue.push({ task: n.target(), layer: task.layer + 1 }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queue.push({ parent: e, children: children.targets() })
|
|
||||||
while (queue.length) {
|
|
||||||
const task = queue.pop()
|
|
||||||
if (task.children.length === 0) {
|
|
||||||
task.parent.data('subtreeWidth', task.parent.height())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const unprocessed = task?.children.filter(e => e.data('subtreeWidth') === undefined)
|
|
||||||
if (unprocessed.length !== 0) {
|
|
||||||
unprocessed.forEach(t => queue.push({ parent: t, children: t.cy().edges(`[source="${t.id()}"]`).targets() }))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
task?.parent.data('subtreeWidth', task.children.reduce((p, n) => p + n.data('subtreeWidth'), 0))
|
|
||||||
}
|
|
||||||
return { x: 200 * e.data('layer'), y: 0 }
|
|
||||||
} else {
|
|
||||||
const parent = e.cy().edges(`[target="${e.id()}"]`)[0].source()
|
|
||||||
// console.log(e.data('subtreeWidth'), e.id(),(parent.data('children')-1) )
|
|
||||||
const wing = parent.data('subtreeWidth') / 2
|
|
||||||
const lastOffset = parent.data('lastChild')
|
|
||||||
const step = wing * 2 / (parent.data('children') - 1)
|
|
||||||
if (e.data('layer') === 1)
|
|
||||||
// console.log(e.data('subtreeWidth'), e.id(), (parent.data('children') - 1), step, wing, e.data('layer'), parent.id(), lastOffset)
|
|
||||||
//e.removeData('subtreeWidth')
|
|
||||||
//console.log('poss', e.id(), 'children', parent.data('children'),'lo', lastOffset, 'v', wing)
|
|
||||||
if (lastOffset !== undefined) {
|
|
||||||
parent.data('lastChild', lastOffset + step)
|
|
||||||
return { x: 200 * e.data('layer'), y: (lastOffset + step) || 1 }
|
|
||||||
} else {
|
|
||||||
parent.data('lastChild', parent.position().y - wing)
|
|
||||||
return { x: 200 * e.data('layer'), y: (parent.position().y - wing) || 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, // map of (node id) => (position obj); or function(node){ return somPos; }
|
|
||||||
zoom: undefined, // the zoom level to set (prob want fit = false if set)
|
|
||||||
pan: undefined, // the pan level to set (prob want fit = false if set)
|
|
||||||
fit: false, // whether to fit to viewport
|
|
||||||
padding: 30, // padding on fit
|
|
||||||
animate: false, // whether to transition the node positions
|
|
||||||
animationDuration: 500, // duration of animation in ms if enabled
|
|
||||||
animationEasing: undefined, // easing of animation if enabled
|
|
||||||
animateFilter: function (node, i) { return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
|
|
||||||
ready: readyLO, // callback on layoutready
|
|
||||||
// stop: (e) => console.log('stop', e), // callback on layoutstop
|
|
||||||
transform: function (node, position) { return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const initialPopperIcons = (e) => {
|
const initialPopperIcons = (e) => {
|
||||||
const cy = e.cy
|
const cy = e.cy
|
||||||
@ -325,7 +444,7 @@ export const CsComponent = () => {
|
|||||||
.toArray()
|
.toArray()
|
||||||
?.forEach((item) => {
|
?.forEach((item) => {
|
||||||
const node = item as NodeSingularWithPopper;
|
const node = item as NodeSingularWithPopper;
|
||||||
console.log(node)
|
//console.log(node)
|
||||||
|
|
||||||
const layoutsPopper = node.popper({
|
const layoutsPopper = node.popper({
|
||||||
popper: {
|
popper: {
|
||||||
@ -347,7 +466,7 @@ export const CsComponent = () => {
|
|||||||
// layoutElement.addEventListener("mouseup", () =>{}
|
// layoutElement.addEventListener("mouseup", () =>{}
|
||||||
// // setStartCreate(node.id())
|
// // setStartCreate(node.id())
|
||||||
// );
|
// );
|
||||||
console.log(layoutsContainer.current)
|
//console.log(layoutsContainer.current)
|
||||||
layoutsContainer.current?.appendChild(layoutElement);
|
layoutsContainer.current?.appendChild(layoutElement);
|
||||||
|
|
||||||
return layoutElement;
|
return layoutElement;
|
||||||
@ -402,7 +521,7 @@ export const CsComponent = () => {
|
|||||||
// setStartRemove(node.id())
|
// setStartRemove(node.id())
|
||||||
// );
|
// );
|
||||||
|
|
||||||
console.log(crossElement)
|
//console.log(crossElement)
|
||||||
return crossElement;
|
return crossElement;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -413,10 +532,10 @@ export const CsComponent = () => {
|
|||||||
modifiers: [{ name: "flip", options: { boundary: node } }],
|
modifiers: [{ name: "flip", options: { boundary: node } }],
|
||||||
},
|
},
|
||||||
content: ([item]) => {
|
content: ([item]) => {
|
||||||
console.log(item.data())
|
//console.log(item.data())
|
||||||
const itemId = item.id();
|
const itemId = item.id();
|
||||||
console.log(item.data())
|
//console.log(item.data())
|
||||||
console.log(item.data().lastChild)
|
//console.log(item.data().lastChild)
|
||||||
if (item.data().lastChild === NaN || item.data().lastChild === undefined) {
|
if (item.data().lastChild === NaN || item.data().lastChild === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -517,7 +636,85 @@ export const CsComponent = () => {
|
|||||||
// elements={createGraphElements(tree, quiz)}
|
// elements={createGraphElements(tree, quiz)}
|
||||||
style={{ height: "480px", background: "#F2F3F7" }}
|
style={{ height: "480px", background: "#F2F3F7" }}
|
||||||
stylesheet={stylesheet}
|
stylesheet={stylesheet}
|
||||||
layout={ly}
|
layout={{
|
||||||
|
name: 'preset',
|
||||||
|
|
||||||
|
positions: (e) => {
|
||||||
|
//console.log("идёт расчёт позиции")
|
||||||
|
const id = e.id()
|
||||||
|
const incomming = e.cy().edges(`[target="${id}"]`)
|
||||||
|
const layer = 0
|
||||||
|
e.removeData('lastChild')
|
||||||
|
|
||||||
|
if (incomming.length === 0) {
|
||||||
|
const children = e.cy().edges(`[source="${id}"]`)
|
||||||
|
e.data('layer', layer)
|
||||||
|
e.data('children', children.targets().length)
|
||||||
|
const queue = []
|
||||||
|
children.forEach(n => {
|
||||||
|
queue.push({ task: n.target(), layer: layer + 1 })
|
||||||
|
})
|
||||||
|
while (queue.length) {
|
||||||
|
const task = queue.pop()
|
||||||
|
task.task.data('layer', task.layer)
|
||||||
|
const children = e.cy().edges(`[source="${task.task.id()}"]`)
|
||||||
|
task.task.data('children', children.targets().length)
|
||||||
|
if (children.length !== 0) {
|
||||||
|
children.forEach(n => queue.push({ task: n.target(), layer: task.layer + 1 }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queue.push({ parent: e, children: children.targets() })
|
||||||
|
while (queue.length) {
|
||||||
|
const task = queue.pop()
|
||||||
|
if (task.children.length === 0) {
|
||||||
|
task.parent.data('subtreeWidth', task.parent.height())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const unprocessed = task?.children.filter(e => e.data('subtreeWidth') === undefined)
|
||||||
|
if (unprocessed.length !== 0) {
|
||||||
|
unprocessed.forEach(t => queue.push({ parent: t, children: t.cy().edges(`[source="${t.id()}"]`).targets() }))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
task?.parent.data('subtreeWidth', task.children.reduce((p, n) => p + n.data('subtreeWidth'), 0))
|
||||||
|
}
|
||||||
|
return { x: 200 * e.data('layer'), y: 0 }
|
||||||
|
} else {
|
||||||
|
const parent = e.cy().edges(`[target="${e.id()}"]`)[0].source()
|
||||||
|
//console.log('batya', parent)
|
||||||
|
// console.log(e.data('subtreeWidth'), e.id(),(parent.data('children')-1) )
|
||||||
|
const wing = parent.data('subtreeWidth') / 2
|
||||||
|
const lastOffset = parent.data('lastChild')
|
||||||
|
const step = wing * 2 / (parent.data('children') - 1)
|
||||||
|
console.log(parent.data('subtreeWidth'), e.id(), (parent.data('children') - 1), step, wing, e.data('layer'), parent.id(), lastOffset)
|
||||||
|
//e.removeData('subtreeWidth')
|
||||||
|
//console.log('poss', e.id(), 'children', parent.data('children'),'lo', lastOffset, 'v', wing)
|
||||||
|
if (lastOffset !== undefined) {
|
||||||
|
parent.data('lastChild', lastOffset + step)
|
||||||
|
//console.log('lastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChild')
|
||||||
|
// console.log('lastChild', lastOffset + step)
|
||||||
|
// return { x: 200 * e.data('layer'), y: (lastOffset + step) }
|
||||||
|
return { x: 200 * e.data('layer'), y: (lastOffset + step) }
|
||||||
|
} else {
|
||||||
|
parent.data('lastChild', parent.position().y - wing)
|
||||||
|
// console.log('lastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChildlastChild')
|
||||||
|
// console.log('lastChild', parent.position().y - wing)
|
||||||
|
// return { x: 200 * e.data('layer'), y: (parent.position().y - wing)}
|
||||||
|
return { x: 200 * e.data('layer'), y: (parent.position().y - wing) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, // map of (node id) => (position obj); or function(node){ return somPos; }
|
||||||
|
zoom: undefined, // the zoom level to set (prob want fit = false if set)
|
||||||
|
pan: undefined, // the pan level to set (prob want fit = false if set)
|
||||||
|
fit: false, // whether to fit to viewport
|
||||||
|
padding: 30, // padding on fit
|
||||||
|
animate: false, // whether to transition the node positions
|
||||||
|
animationDuration: 500, // duration of animation in ms if enabled
|
||||||
|
animationEasing: undefined, // easing of animation if enabled
|
||||||
|
animateFilter: function (node, i) { return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
|
||||||
|
ready: readyLO, // callback on layoutready
|
||||||
|
// stop: (e) => console.log('stop', e), // callback on layoutstop
|
||||||
|
transform: function (node, position) { return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
|
||||||
|
}}
|
||||||
cy={(cy) => {
|
cy={(cy) => {
|
||||||
cyRef.current = cy;
|
cyRef.current = cy;
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -1,28 +1,30 @@
|
|||||||
import { Box } from "@mui/material"
|
import { Box } from "@mui/material"
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { updateDragQuestionId, updateQuestion } from "@root/questions/actions"
|
import { updateDragQuestionContentId, updateQuestion } from "@root/questions/actions"
|
||||||
import { updateRootInfo } from "@root/quizes/actions"
|
import { updateRootInfo } from "@root/quizes/actions"
|
||||||
import { useQuestionsStore } from "@root/questions/store"
|
import { useQuestionsStore } from "@root/questions/store"
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setOpenedModalQuestionsId:(str: string|null) => void
|
setOpenedModalQuestions: (open: boolean) => void;
|
||||||
|
modalQuestionTargetContentId: string;
|
||||||
}
|
}
|
||||||
export const FirstNodeField = ({setOpenedModalQuestionsId}:Props) => {
|
export const FirstNodeField = ({ setOpenedModalQuestions, modalQuestionTargetContentId }: Props) => {
|
||||||
const { dragQuestionId } = useQuestionsStore()
|
const { dragQuestionContentId } = useQuestionsStore()
|
||||||
const Container = useRef<HTMLDivElement | null>(null);
|
const Container = useRef<HTMLDivElement | null>(null);
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
console.log(dragQuestionId)
|
console.log(dragQuestionContentId)
|
||||||
|
|
||||||
|
const modalOpen = () => setOpenedModalQuestions(true)
|
||||||
|
|
||||||
const newRootNode = () => {
|
const newRootNode = () => {
|
||||||
if (quiz) {
|
if (quiz) {
|
||||||
console.log(dragQuestionId)
|
console.log(dragQuestionContentId)
|
||||||
|
|
||||||
if (dragQuestionId) {
|
if (dragQuestionContentId) {
|
||||||
updateRootInfo(quiz?.id, true)
|
updateRootInfo(quiz?.id, true)
|
||||||
updateQuestion(dragQuestionId, (question) => question.content.rule.parentId = "root")
|
updateQuestion(dragQuestionContentId, (question) => question.content.rule.parentId = "root")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
enqueueSnackbar("Нет информации о взятом опроснике")
|
enqueueSnackbar("Нет информации о взятом опроснике")
|
||||||
@ -31,12 +33,28 @@ console.log(dragQuestionId)
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Container.current?.addEventListener("mouseup", newRootNode)
|
Container.current?.addEventListener("mouseup", newRootNode)
|
||||||
return () => { Container.current?.removeEventListener("mouseup", newRootNode) }
|
Container.current?.addEventListener("click", modalOpen)
|
||||||
|
return () => {
|
||||||
|
Container.current?.removeEventListener("mouseup", newRootNode)
|
||||||
|
Container.current?.removeEventListener("click", modalOpen)
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (quiz) {
|
||||||
|
|
||||||
|
if (dragQuestionContentId) {
|
||||||
|
updateRootInfo(quiz?.id, true)
|
||||||
|
updateQuestion(modalQuestionTargetContentId, (question) => question.content.rule.parentId = "root")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
enqueueSnackbar("Нет информации о взятом опроснике")
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [modalQuestionTargetContentId])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
onclick
|
|
||||||
ref={Container}
|
ref={Container}
|
||||||
sx={{
|
sx={{
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
|||||||
@ -21,12 +21,12 @@ export const storeToNodes = (questions: AnyQuizQuestion[]) => {
|
|||||||
console.log(question)
|
console.log(question)
|
||||||
if (question.content.rule.parentId) {
|
if (question.content.rule.parentId) {
|
||||||
nodes.push({data: {
|
nodes.push({data: {
|
||||||
id: question.id,
|
id: question.content.id,
|
||||||
label: question.title ? question.title : "noname"
|
label: question.title ? question.title : "noname"
|
||||||
}})
|
}})
|
||||||
if (question.content.rule.parentId !== "root") edges.push({data: {
|
if (question.content.rule.parentId !== "root") edges.push({data: {
|
||||||
source: question.content.rule.parentId,
|
source: question.content.rule.parentId,
|
||||||
target: question.id
|
target: question.content.id
|
||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,12 +4,15 @@ import { CsComponent } from "./CsComponent";
|
|||||||
import { useQuestionsStore } from "@root/questions/store"
|
import { useQuestionsStore } from "@root/questions/store"
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import {BranchingQuestionsModal} from "../BranchingQuestionsModal"
|
||||||
|
|
||||||
|
|
||||||
export const BranchingMap = () => {
|
export const BranchingMap = () => {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const { dragQuestionId } = useQuestionsStore()
|
const { dragQuestionContentId } = useQuestionsStore()
|
||||||
const [openedModalQuestionsId, setOpenedModalQuestionsId] = useState<string>()
|
const [modalQuestionParentContentId, setModalQuestionParentContentId] = useState<string>("")
|
||||||
|
const [modalQuestionTargetContentId, setModalQuestionTargetContentId] = useState<string>("")
|
||||||
|
const [openedModalQuestions, setOpenedModalQuestions] = useState<boolean>(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -22,17 +25,17 @@ export const BranchingMap = () => {
|
|||||||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||||
marginBottom: "40px",
|
marginBottom: "40px",
|
||||||
height: "521px",
|
height: "521px",
|
||||||
border: dragQuestionId === null ? "none" : "#7e2aea 2px dashed"
|
border: dragQuestionContentId === null ? "none" : "#7e2aea 2px dashed"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
{
|
{
|
||||||
quiz?.config.haveRoot ?
|
quiz?.config.haveRoot ?
|
||||||
<CsComponent />
|
<CsComponent modalQuestionParentContentId={modalQuestionParentContentId} modalQuestionTargetContentId={modalQuestionTargetContentId} setOpenedModalQuestions={setOpenedModalQuestions} setModalQuestionParentContentId={setModalQuestionParentContentId} setModalQuestionTargetContentId={setModalQuestionTargetContentId}/>
|
||||||
:
|
:
|
||||||
<FirstNodeField setOpenedModalQuestionsId={setOpenedModalQuestionsId} />
|
<FirstNodeField setOpenedModalQuestions={setOpenedModalQuestions} modalQuestionTargetContentId={modalQuestionTargetContentId}/>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
<BranchingQuestionsModal openedModalQuestions={openedModalQuestions} setOpenedModalQuestions={setOpenedModalQuestions} setModalQuestionTargetContentId={setModalQuestionTargetContentId} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useParams } from "react-router-dom";
|
|||||||
import { Box, Button, Typography } from "@mui/material";
|
import { Box, Button, Typography } from "@mui/material";
|
||||||
import { ReactComponent as CheckedIcon } from "@icons/checked.svg";
|
import { ReactComponent as CheckedIcon } from "@icons/checked.svg";
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { updateDragQuestionId } from "@root/questions/actions";
|
import { updateDragQuestionContentId } from "@root/questions/actions";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "@model/questionTypes/shared";
|
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ 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" }}>
|
||||||
@ -54,7 +55,7 @@ export const QuestionsList = () => {
|
|||||||
{questions.filter((q:AnyQuestion) => q.type).map(({ title, id, content }, index) => (
|
{questions.filter((q:AnyQuestion) => q.type).map(({ title, id, content }, index) => (
|
||||||
<Button
|
<Button
|
||||||
onMouseDown={() => {//Разрешаем добавить этот вопрос если у него нет родителя (не добавляли ещё в дерево)
|
onMouseDown={() => {//Разрешаем добавить этот вопрос если у него нет родителя (не добавляли ещё в дерево)
|
||||||
if (!content.rule.parentId) updateDragQuestionId(id)
|
if (!content.rule.parentId) updateDragQuestionContentId(id)
|
||||||
}}
|
}}
|
||||||
key={index}
|
key={index}
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
77
src/pages/Questions/BranchingQuestionsModal/index.tsx
Normal file
77
src/pages/Questions/BranchingQuestionsModal/index.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { Box, Modal, Button, Typography } from "@mui/material";
|
||||||
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
|
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
|
|
||||||
|
type AnyQuestion = UntypedQuizQuestion | AnyTypedQuizQuestion
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
openedModalQuestions: boolean;
|
||||||
|
setModalQuestionTargetContentId: (contentId:string) => void;
|
||||||
|
setOpenedModalQuestions: (open:boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BranchingQuestionsModal = ({ openedModalQuestions, setOpenedModalQuestions, setModalQuestionTargetContentId}:Props) => {
|
||||||
|
const quizId = Number(useParams().quizId);
|
||||||
|
const { questions } = useQuestionsStore();
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpenedModalQuestions(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={openedModalQuestions} onClose={handleClose}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
overflow: "hidden",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
maxWidth: "620px",
|
||||||
|
width: "100%",
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
borderRadius: "12px",
|
||||||
|
boxShadow: 24,
|
||||||
|
padding: "30px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ margin: "0 auto", maxWidth: "350px" }}>
|
||||||
|
{questions.filter((q:AnyQuestion) => (q.type && !q.content.rule.parentId)).map((question: AnyTypedQuizQuestion, index:number) => (
|
||||||
|
<Button
|
||||||
|
key={question.content.id}
|
||||||
|
onClick={() => {
|
||||||
|
setModalQuestionTargetContentId(question.content.id)
|
||||||
|
handleClose();
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
padding: "12px",
|
||||||
|
background: "#FFFFFF",
|
||||||
|
borderRadius: "8px",
|
||||||
|
marginBottom: "20px",
|
||||||
|
boxShadow: "0px 10px 30px #e7e7e7",
|
||||||
|
backgroundImage: `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='rgb(154, 154, 175)' stroke-width='2' stroke-dasharray='8 8' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
|
||||||
|
border-radius: 8px;`,
|
||||||
|
"&:last-child": { marginBottom: 0 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
color: "#000",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{question.title || "нет заголовка"}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -122,7 +122,10 @@ export const updateQuestion = (
|
|||||||
updateFn: (question: AnyTypedQuizQuestion) => void,
|
updateFn: (question: AnyTypedQuizQuestion) => void,
|
||||||
) => {
|
) => {
|
||||||
setProducedState(state => {
|
setProducedState(state => {
|
||||||
const question = state.questions.find(q => q.id === questionId);
|
const question = state.questions.find(q => q.id === questionId) || state.questions.find(q => {
|
||||||
|
console.log(q)
|
||||||
|
return q.content.id === questionId
|
||||||
|
});
|
||||||
if (!question) return;
|
if (!question) return;
|
||||||
if (question.type === null) throw new Error("Cannot update untyped question, use 'updateUntypedQuestion' instead");
|
if (question.type === null) throw new Error("Cannot update untyped question, use 'updateUntypedQuestion' instead");
|
||||||
|
|
||||||
@ -331,7 +334,7 @@ export const createTypedQuestion = async (
|
|||||||
const question = useQuestionsStore.getState().questions.find(q => q.id === questionId);
|
const question = useQuestionsStore.getState().questions.find(q => q.id === questionId);
|
||||||
if (!question) return;
|
if (!question) return;
|
||||||
if (question.type !== null) throw new Error("Cannot upgrade already typed question");
|
if (question.type !== null) throw new Error("Cannot upgrade already typed question");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const createdQuestion = await questionApi.create({
|
const createdQuestion = await questionApi.create({
|
||||||
quiz_id: question.quizId,
|
quiz_id: question.quizId,
|
||||||
@ -426,16 +429,20 @@ function setProducedState<A extends string | { type: unknown; }>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const clearDragQuestionId = () => {
|
export const cleardragQuestionContentId = () => {
|
||||||
console.log("чищу чищу")
|
console.log("чищу чищу")
|
||||||
useQuestionsStore.setState({dragQuestionId: null});
|
useQuestionsStore.setState({dragQuestionContentId: null});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getQuestionById = (questionId: string | null) => {
|
export const getQuestionById = (questionId: string | null) => {
|
||||||
if (questionId === null) return null;
|
if (questionId === null) return 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) => {
|
||||||
|
if (questionContentId === null) return null;
|
||||||
|
return useQuestionsStore.getState().questions.find(q => q.content.id === questionContentId) || 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 updateDragQuestionId = (id?: string) => useQuestionsStore.setState({dragQuestionId: id ? id : null});
|
export const updateDragQuestionContentId = (contentId?: string) => useQuestionsStore.setState({dragQuestionContentId: contentId ? contentId : null});
|
||||||
|
|
||||||
|
|||||||
@ -6,13 +6,13 @@ import { devtools } from "zustand/middleware";
|
|||||||
export type QuestionsStore = {
|
export type QuestionsStore = {
|
||||||
questions: (AnyTypedQuizQuestion | UntypedQuizQuestion);
|
questions: (AnyTypedQuizQuestion | UntypedQuizQuestion);
|
||||||
openedModalSettingsId: string | null;
|
openedModalSettingsId: string | null;
|
||||||
dragQuestionId: string | null;
|
dragQuestionContentId: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: QuestionsStore = {
|
const initialState: QuestionsStore = {
|
||||||
questions: [],
|
questions: [],
|
||||||
openedModalSettingsId: null as null,
|
openedModalSettingsId: null as null,
|
||||||
dragQuestionId: null,
|
dragQuestionContentId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useQuestionsStore = create<QuestionsStore>()(
|
export const useQuestionsStore = create<QuestionsStore>()(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user