WIP
This commit is contained in:
parent
a57de5fe4d
commit
dd46a3833f
@ -1,6 +1,6 @@
|
||||
import { makeRequest } from "@frontend/kitui";
|
||||
import { CreateQuestionRequest } from "model/question/create";
|
||||
import { Question } from "model/question/question";
|
||||
import { RawQuestion } from "model/question/question";
|
||||
import { GetQuestionListRequest, GetQuestionListResponse } from "model/question/getList";
|
||||
import { EditQuestionRequest, EditQuestionResponse } from "model/question/edit";
|
||||
import { DeleteQuestionRequest, DeleteQuestionResponse } from "model/question/delete";
|
||||
@ -9,32 +9,27 @@ import { CopyQuestionRequest, CopyQuestionResponse } from "model/question/copy";
|
||||
|
||||
const baseUrl = process.env.NODE_ENV === "production" ? "/squiz" : "https://squiz.pena.digital/squiz";
|
||||
|
||||
export function createQuestion(body: CreateQuestionRequest = defaultCreateQuestionBody) {
|
||||
return makeRequest<CreateQuestionRequest, Question>({
|
||||
function createQuestion(body?: Partial<CreateQuestionRequest>) {
|
||||
return makeRequest<CreateQuestionRequest, RawQuestion>({
|
||||
url: `${baseUrl}/question/create`,
|
||||
body,
|
||||
body: { ...defaultCreateQuestionBody, ...body },
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
export function getQuestionList(body: GetQuestionListRequest = defaultGetQuestionListBody) {
|
||||
return makeRequest<GetQuestionListRequest, GetQuestionListResponse>({
|
||||
async function getQuestionList(body?: Partial<GetQuestionListRequest>) {
|
||||
if (!body?.quiz_id) return null;
|
||||
|
||||
const response = await makeRequest<GetQuestionListRequest, GetQuestionListResponse>({
|
||||
url: `${baseUrl}/question/getList`,
|
||||
body,
|
||||
method: "GET",
|
||||
body: { ...defaultGetQuestionListBody, ...body },
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
return response.items;
|
||||
}
|
||||
|
||||
export function editQuestion(updatedQuestion: Question, signal?: AbortSignal) {
|
||||
const body: EditQuestionRequest = {
|
||||
id: updatedQuestion.id,
|
||||
title: updatedQuestion.title,
|
||||
desc: updatedQuestion.description,
|
||||
type: updatedQuestion.type,
|
||||
required: updatedQuestion.required,
|
||||
page: updatedQuestion.page,
|
||||
};
|
||||
|
||||
function editQuestion(body: EditQuestionRequest, signal?: AbortSignal) {
|
||||
return makeRequest<EditQuestionRequest, EditQuestionResponse>({
|
||||
url: `${baseUrl}/question/edit`,
|
||||
body,
|
||||
@ -43,7 +38,7 @@ export function editQuestion(updatedQuestion: Question, signal?: AbortSignal) {
|
||||
});
|
||||
}
|
||||
|
||||
export function copyQuestion(copyQuestionBody: CopyQuestionRequest) {
|
||||
function copyQuestion(copyQuestionBody: CopyQuestionRequest) {
|
||||
return makeRequest<CopyQuestionRequest, CopyQuestionResponse>({
|
||||
url: `${baseUrl}/question/copy`,
|
||||
body: copyQuestionBody,
|
||||
@ -51,7 +46,7 @@ export function copyQuestion(copyQuestionBody: CopyQuestionRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteQuestion(id: number) {
|
||||
function deleteQuestion(id: number) {
|
||||
return makeRequest<DeleteQuestionRequest, DeleteQuestionResponse>({
|
||||
url: `${baseUrl}/question/delete`,
|
||||
body: { id },
|
||||
@ -72,7 +67,7 @@ const defaultCreateQuestionBody: CreateQuestionRequest = {
|
||||
"quiz_id": 0,
|
||||
"title": "string",
|
||||
"description": "string",
|
||||
"type": "string",
|
||||
"type": "variant",
|
||||
"required": true,
|
||||
"page": 0,
|
||||
"content": "string",
|
||||
@ -87,14 +82,4 @@ const defaultGetQuestionListBody: GetQuestionListRequest = {
|
||||
"type": "string",
|
||||
"deleted": true,
|
||||
"required": true,
|
||||
"quiz_id": 0
|
||||
};
|
||||
|
||||
const defaultEditQuestionBody: EditQuestionRequest = {
|
||||
"id": 0,
|
||||
"title": "string",
|
||||
"desc": "string",
|
||||
"type": "",
|
||||
"required": true,
|
||||
"page": 0
|
||||
};
|
||||
|
||||
@ -38,7 +38,6 @@ function getQuiz(body?: Partial<GetQuizRequest>) {
|
||||
}
|
||||
|
||||
async function editQuiz(body: EditQuizRequest, signal?: AbortSignal) {
|
||||
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
return makeRequest<EditQuizRequest, EditQuizResponse>({
|
||||
url: `${baseUrl}/quiz/edit`,
|
||||
body,
|
||||
@ -106,26 +105,6 @@ const defaultCreateQuizBody: CreateQuizRequest = {
|
||||
"group_id": 0,
|
||||
};
|
||||
|
||||
const defaultEditQuizBody: EditQuizRequest = {
|
||||
"id": 0,
|
||||
"fp": true,
|
||||
"rep": true,
|
||||
"note_prevented": true,
|
||||
"mailing": true,
|
||||
"uniq": true,
|
||||
"name": "string",
|
||||
"desc": "string",
|
||||
"conf": "string",
|
||||
"status": "string",
|
||||
"limit": 0,
|
||||
"due_to": 0,
|
||||
"time_of_passing": 0,
|
||||
"pausable": true,
|
||||
"question_cnt": 0,
|
||||
"super": true,
|
||||
"group_id": 0,
|
||||
};
|
||||
|
||||
const defaultGetQuizBody: GetQuizRequest = {
|
||||
"quiz_id": "string",
|
||||
"limit": 0,
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import type { QuizQuestionInitial } from "../model/questionTypes/shared";
|
||||
import type { QuizQuestionBase } from "../model/questionTypes/shared";
|
||||
|
||||
export const QUIZ_QUESTION_BASE: Omit<QuizQuestionInitial, "id"> = {
|
||||
|
||||
export const QUIZ_QUESTION_BASE: Omit<QuizQuestionBase, "id"> = {
|
||||
quizId: 0,
|
||||
description: "",
|
||||
page: 0,
|
||||
title: "",
|
||||
type: "nonselected",
|
||||
expanded: true,
|
||||
openedModalSettings: false,
|
||||
required: false,
|
||||
|
||||
28
src/constants/default.ts
Normal file
28
src/constants/default.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { QuestionType } from "@model/question/question";
|
||||
import { QUIZ_QUESTION_DATE } from "./date";
|
||||
import { QUIZ_QUESTION_EMOJI } from "./emoji";
|
||||
import { QUIZ_QUESTION_FILE } from "./file";
|
||||
import { QUIZ_QUESTION_IMAGES } from "./images";
|
||||
import { QUIZ_QUESTION_NUMBER } from "./number";
|
||||
import { QUIZ_QUESTION_PAGE } from "./page";
|
||||
import { QUIZ_QUESTION_RATING } from "./rating";
|
||||
import { QUIZ_QUESTION_SELECT } from "./select";
|
||||
import { QUIZ_QUESTION_TEXT } from "./text";
|
||||
import { QUIZ_QUESTION_VARIANT } from "./variant";
|
||||
import { QUIZ_QUESTION_VARIMG } from "./varimg";
|
||||
import { AnyQuestionContent } from "@model/questionTypes/shared";
|
||||
|
||||
|
||||
export const defaultQuestionContentByType: Record<QuestionType, AnyQuestionContent> = {
|
||||
"date": QUIZ_QUESTION_DATE.content,
|
||||
"emoji": QUIZ_QUESTION_EMOJI.content,
|
||||
"file": QUIZ_QUESTION_FILE.content,
|
||||
"images": QUIZ_QUESTION_IMAGES.content,
|
||||
"number": QUIZ_QUESTION_NUMBER.content,
|
||||
"page": QUIZ_QUESTION_PAGE.content,
|
||||
"rating": QUIZ_QUESTION_RATING.content,
|
||||
"select": QUIZ_QUESTION_SELECT.content,
|
||||
"text": QUIZ_QUESTION_TEXT.content,
|
||||
"variant": QUIZ_QUESTION_VARIANT.content,
|
||||
"varimg": QUIZ_QUESTION_VARIMG.content,
|
||||
} as const;
|
||||
@ -1,12 +1,26 @@
|
||||
import { AnyQuizQuestion, DefiniteQuestionType } from "@model/questionTypes/shared";
|
||||
|
||||
|
||||
export interface EditQuestionRequest {
|
||||
id: number;
|
||||
title: string;
|
||||
desc: string;
|
||||
type: "test" | "button" | "file" | "checkbox" | "select" | "none" | "";
|
||||
required: boolean;
|
||||
page: number;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
type?: DefiniteQuestionType;
|
||||
required?: boolean;
|
||||
page?: number;
|
||||
}
|
||||
|
||||
export interface EditQuestionResponse {
|
||||
updated: number;
|
||||
}
|
||||
|
||||
export function questionToEditQuestionRequest(question: AnyQuizQuestion): EditQuestionRequest {
|
||||
return {
|
||||
id: question.id,
|
||||
title: question.title,
|
||||
desc: question.description,
|
||||
type: question.type,
|
||||
required: question.required,
|
||||
page: question.page,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Question } from "./question";
|
||||
import { RawQuestion } from "./question";
|
||||
|
||||
|
||||
export interface GetQuestionListRequest {
|
||||
@ -24,5 +24,5 @@ export interface GetQuestionListRequest {
|
||||
|
||||
export interface GetQuestionListResponse {
|
||||
count: number;
|
||||
items: Question[];
|
||||
items: RawQuestion[];
|
||||
}
|
||||
|
||||
@ -1,15 +1,69 @@
|
||||
export interface Question {
|
||||
import { AnyQuizQuestion } from "@model/questionTypes/shared";
|
||||
import { defaultQuestionContentByType } from "../../constants/default";
|
||||
|
||||
|
||||
export type QuestionType =
|
||||
| "variant"
|
||||
| "images"
|
||||
| "varimg"
|
||||
| "emoji"
|
||||
| "text"
|
||||
| "select"
|
||||
| "date"
|
||||
| "number"
|
||||
| "file"
|
||||
| "page"
|
||||
| "rating";
|
||||
|
||||
/** Type that comes from server */
|
||||
export interface RawQuestion {
|
||||
/** Id of created question */
|
||||
id: number;
|
||||
/** relation to quiz */
|
||||
quiz_id: number;
|
||||
/** title of question. max 512 length */
|
||||
title: string;
|
||||
/** description of question */
|
||||
description: string;
|
||||
type: "test" | "button" | "file" | "checkbox" | "select" | "none" | "";
|
||||
/** status of question. allow only text, select, file, variant, images, varimg, emoji, date, number, page, rating */
|
||||
type: QuestionType;
|
||||
/** user must pass this question */
|
||||
required: boolean;
|
||||
/** true if question is deleted */
|
||||
deleted: boolean;
|
||||
/** page if question */
|
||||
page: number;
|
||||
/** serialized json of created question */
|
||||
content: string;
|
||||
/** version of quiz */
|
||||
version: number;
|
||||
/** array of previous versions of quiz */
|
||||
parent_ids: number[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export function rawQuestionToQuestion(rawQuestion: RawQuestion): AnyQuizQuestion {
|
||||
let content = defaultQuestionContentByType[rawQuestion.type];
|
||||
|
||||
try {
|
||||
content = JSON.parse(rawQuestion.content);
|
||||
} catch (error) {
|
||||
console.warn("Cannot parse question content from string, using default content", error);
|
||||
}
|
||||
|
||||
return {
|
||||
id: rawQuestion.id,
|
||||
description: rawQuestion.description,
|
||||
page: rawQuestion.page,
|
||||
quizId: rawQuestion.quiz_id,
|
||||
required: rawQuestion.required,
|
||||
title: rawQuestion.title,
|
||||
type: rawQuestion.type,
|
||||
expanded: true,
|
||||
openedModalSettings: false,
|
||||
deleted: false,
|
||||
deleteTimeoutId: 0,
|
||||
content,
|
||||
} as AnyQuizQuestion;
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { QuestionType } from "@model/question/question";
|
||||
import type { QuizQuestionDate } from "./date";
|
||||
import type { QuizQuestionEmoji } from "./emoji";
|
||||
import type { QuizQuestionFile } from "./file";
|
||||
@ -10,6 +11,7 @@ import type { QuizQuestionText } from "./text";
|
||||
import type { QuizQuestionVariant } from "./variant";
|
||||
import type { QuizQuestionVarImg } from "./varimg";
|
||||
|
||||
|
||||
export interface QuestionBranchingRule {
|
||||
/** Радиокнопка "Все условия обязательны" */
|
||||
or: boolean;
|
||||
@ -45,8 +47,11 @@ export interface ImageQuestionVariant extends QuestionVariant {
|
||||
|
||||
export interface QuizQuestionBase {
|
||||
id: number;
|
||||
quizId: number;
|
||||
title: string;
|
||||
type: string;
|
||||
description: string;
|
||||
page: number;
|
||||
type?: QuestionType;
|
||||
expanded: boolean;
|
||||
openedModalSettings: boolean;
|
||||
required: boolean;
|
||||
@ -61,9 +66,9 @@ export interface QuizQuestionBase {
|
||||
};
|
||||
}
|
||||
|
||||
export interface QuizQuestionInitial extends QuizQuestionBase {
|
||||
type: "nonselected";
|
||||
}
|
||||
// export interface QuizQuestionInitial extends QuizQuestionBase {
|
||||
// type: "nonselected";
|
||||
// }
|
||||
|
||||
export type AnyQuizQuestion =
|
||||
| QuizQuestionVariant
|
||||
@ -76,9 +81,11 @@ export type AnyQuizQuestion =
|
||||
| QuizQuestionNumber
|
||||
| QuizQuestionFile
|
||||
| QuizQuestionPage
|
||||
| QuizQuestionRating
|
||||
| QuizQuestionInitial;
|
||||
| QuizQuestionRating;
|
||||
// | QuizQuestionInitial;
|
||||
|
||||
export type QuizQuestionType = AnyQuizQuestion["type"];
|
||||
|
||||
export type AnyQuestionContent = AnyQuizQuestion["content"];
|
||||
|
||||
export type DefiniteQuestionType = Exclude<QuizQuestionType, "nonselected">;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
@ -15,7 +14,6 @@ import {
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
questionStore,
|
||||
updateQuestionsList,
|
||||
removeQuestionForce,
|
||||
createQuestion,
|
||||
@ -26,13 +24,14 @@ import type { RefObject } from "react";
|
||||
import type {
|
||||
QuizQuestionType,
|
||||
QuizQuestionBase,
|
||||
AnyQuizQuestion,
|
||||
} from "../../../model/questionTypes/shared";
|
||||
|
||||
type ChooseAnswerModalProps = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
anchorRef: RefObject<HTMLDivElement>;
|
||||
totalIndex: number;
|
||||
question: AnyQuizQuestion;
|
||||
switchState: string;
|
||||
};
|
||||
|
||||
@ -40,13 +39,11 @@ export const ChooseAnswerModal = ({
|
||||
open,
|
||||
onClose,
|
||||
anchorRef,
|
||||
totalIndex,
|
||||
question,
|
||||
switchState,
|
||||
}: ChooseAnswerModalProps) => {
|
||||
const [openModal, setOpenModal] = useState<boolean>(false);
|
||||
const [selectedValue, setSelectedValue] = useState<QuizQuestionType>("text");
|
||||
const quizId = Number(useParams().quizId);
|
||||
const { listQuestions } = questionStore();
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
@ -126,17 +123,17 @@ export const ChooseAnswerModal = ({
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{ minWidth: "150px" }}
|
||||
onClick={() => {
|
||||
setOpenModal(false);
|
||||
onClick={() => { // TODO
|
||||
// setOpenModal(false);
|
||||
|
||||
const question = { ...listQuestions[quizId][totalIndex] };
|
||||
// const question = { ...listQuestions[quizId][totalIndex] };
|
||||
|
||||
removeQuestionForce(quizId, question.id);
|
||||
createQuestion(quizId, selectedValue, totalIndex);
|
||||
updateQuestionsList<QuizQuestionBase>(quizId, totalIndex, {
|
||||
title: question.title,
|
||||
expanded: question.expanded,
|
||||
});
|
||||
// removeQuestionForce(quizId, question.id);
|
||||
// createQuestion(quizId, selectedValue, totalIndex);
|
||||
// updateQuestionsList<QuizQuestionBase>(quizId, totalIndex, {
|
||||
// title: question.title,
|
||||
// expanded: question.expanded,
|
||||
// });
|
||||
}}
|
||||
>
|
||||
Подтвердить
|
||||
|
||||
@ -1,34 +1,28 @@
|
||||
import { memo } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import { Box, ListItem, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { memo } from "react";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import QuestionsPageCard from "./QuestionPageCard";
|
||||
import { AnyQuizQuestion } from "@model/questionTypes/shared";
|
||||
|
||||
import { updateQuestionsList } from "@root/questions";
|
||||
|
||||
import { QuizQuestionBase } from "../../../model/questionTypes/shared";
|
||||
|
||||
type DraggableListItemProps = {
|
||||
index: number;
|
||||
type Props = {
|
||||
question: AnyQuizQuestion;
|
||||
isDragging: boolean;
|
||||
questionData: QuizQuestionBase;
|
||||
index: number;
|
||||
};
|
||||
|
||||
export default memo(
|
||||
({ index, isDragging, questionData }: DraggableListItemProps) => {
|
||||
const quizId = Number(useParams().quizId);
|
||||
function DraggableListItem({ question, isDragging, index }: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Draggable draggableId={String(index)} index={index}>
|
||||
<Draggable draggableId={question.id.toString()} index={index}>
|
||||
{(provided) => (
|
||||
<ListItem
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
sx={{ userSelect: "none", padding: 0 }}
|
||||
>
|
||||
{questionData.deleted ? (
|
||||
{/* questionData.deleted TODO */ true ? (
|
||||
<Box
|
||||
{...provided.dragHandleProps}
|
||||
sx={{
|
||||
@ -49,11 +43,11 @@ export default memo(
|
||||
Вопрос удалён.
|
||||
</Typography>
|
||||
<Typography
|
||||
onClick={() => {
|
||||
updateQuestionsList<QuizQuestionBase>(quizId, index, {
|
||||
...questionData,
|
||||
deleted: false,
|
||||
});
|
||||
onClick={() => { // TODO
|
||||
// updateQuestionsList<QuizQuestionBase>(quizId, index, {
|
||||
// ...questionData,
|
||||
// deleted: false,
|
||||
// });
|
||||
}}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
@ -69,8 +63,7 @@ export default memo(
|
||||
) : (
|
||||
<Box sx={{ width: "100%", position: "relative" }}>
|
||||
<QuestionsPageCard
|
||||
key={index}
|
||||
totalIndex={index}
|
||||
question={question}
|
||||
draggableProps={provided.dragHandleProps}
|
||||
isDragging={isDragging}
|
||||
/>
|
||||
@ -80,5 +73,8 @@ export default memo(
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const DraggableListItemMemo = memo(DraggableListItem);
|
||||
|
||||
export default DraggableListItemMemo;
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {
|
||||
Box,
|
||||
Checkbox,
|
||||
@ -12,165 +10,67 @@ import {
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { useRef, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
|
||||
import { ChooseAnswerModal } from "./ChooseAnswerModal";
|
||||
import TypeQuestions from "../TypeQuestions";
|
||||
import SwitchQuestionsPage from "../SwitchQuestionsPage";
|
||||
import TypeQuestions from "../TypeQuestions";
|
||||
import { ChooseAnswerModal } from "./ChooseAnswerModal";
|
||||
|
||||
import {
|
||||
questionStore,
|
||||
updateQuestionsList,
|
||||
createQuestion,
|
||||
copyQuestion,
|
||||
createQuestion,
|
||||
removeQuestion,
|
||||
removeQuestionForce,
|
||||
updateQuestionsList
|
||||
} from "@root/questions";
|
||||
|
||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
|
||||
import { CrossedEyeIcon } from "@icons/CrossedEyeIcon";
|
||||
import { ArrowDownIcon } from "@icons/questionsPage/ArrowDownIcon";
|
||||
import { CopyIcon } from "@icons/questionsPage/CopyIcon";
|
||||
import { OneIcon } from "@icons/questionsPage/OneIcon";
|
||||
import { PointsIcon } from "@icons/questionsPage/PointsIcon";
|
||||
import { CopyIcon } from "@icons/questionsPage/CopyIcon";
|
||||
import { CrossedEyeIcon } from "@icons/CrossedEyeIcon";
|
||||
import { HideIcon } from "@icons/questionsPage/hideIcon";
|
||||
import Answer from "@icons/questionsPage/answer";
|
||||
import OptionsPict from "@icons/questionsPage/options_pict";
|
||||
import OptionsAndPict from "@icons/questionsPage/options_and_pict";
|
||||
import Emoji from "@icons/questionsPage/emoji";
|
||||
import Input from "@icons/questionsPage/input";
|
||||
import DropDown from "@icons/questionsPage/drop_down";
|
||||
import Date from "@icons/questionsPage/date";
|
||||
import Slider from "@icons/questionsPage/slider";
|
||||
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
|
||||
import Download from "@icons/questionsPage/download";
|
||||
import DropDown from "@icons/questionsPage/drop_down";
|
||||
import Emoji from "@icons/questionsPage/emoji";
|
||||
import { HideIcon } from "@icons/questionsPage/hideIcon";
|
||||
import Input from "@icons/questionsPage/input";
|
||||
import OptionsAndPict from "@icons/questionsPage/options_and_pict";
|
||||
import OptionsPict from "@icons/questionsPage/options_pict";
|
||||
import Page from "@icons/questionsPage/page";
|
||||
import RatingIcon from "@icons/questionsPage/rating";
|
||||
import { ArrowDownIcon } from "@icons/questionsPage/ArrowDownIcon";
|
||||
import { ReactComponent as PlusIcon } from "../../../assets/icons/plus.svg";
|
||||
|
||||
import Slider from "@icons/questionsPage/slider";
|
||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
||||
import type {
|
||||
AnyQuizQuestion,
|
||||
QuizQuestionInitial,
|
||||
} from "../../../model/questionTypes/shared";
|
||||
import { ReactComponent as PlusIcon } from "../../../assets/icons/plus.svg";
|
||||
import type { AnyQuizQuestion } from "../../../model/questionTypes/shared";
|
||||
|
||||
|
||||
interface Props {
|
||||
totalIndex: number;
|
||||
question: AnyQuizQuestion;
|
||||
draggableProps: DraggableProvidedDragHandleProps | null | undefined;
|
||||
isDragging: boolean;
|
||||
}
|
||||
|
||||
const IconAndrom = (isExpanded: boolean, switchState: string) => {
|
||||
switch (switchState) {
|
||||
case "variant":
|
||||
return (
|
||||
<Answer
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "images":
|
||||
return (
|
||||
<OptionsPict
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "varimg":
|
||||
return (
|
||||
<OptionsAndPict
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "emoji":
|
||||
return (
|
||||
<Emoji
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "text":
|
||||
return (
|
||||
<Input
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "select":
|
||||
return (
|
||||
<DropDown
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "date":
|
||||
return (
|
||||
<Date
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "number":
|
||||
return (
|
||||
<Slider
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "file":
|
||||
return (
|
||||
<Download
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "page":
|
||||
return (
|
||||
<Page
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "rating":
|
||||
return (
|
||||
<RatingIcon
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
export default function QuestionsPageCard({
|
||||
totalIndex,
|
||||
draggableProps,
|
||||
isDragging,
|
||||
}: Props) {
|
||||
export default function QuestionsPageCard({ question, draggableProps, isDragging }: Props) {
|
||||
const [plusVisible, setPlusVisible] = useState<boolean>(false);
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const quizId = Number(useParams().quizId);
|
||||
const theme = useTheme();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
const { listQuestions } = questionStore();
|
||||
const question = listQuestions[quizId][totalIndex];
|
||||
const anchorRef = useRef(null);
|
||||
const debounced = useDebouncedCallback((title) => {
|
||||
updateQuestionsList<QuizQuestionInitial>(quizId, totalIndex, { title });
|
||||
const debounced = useDebouncedCallback((title) => { // TODO update title
|
||||
// updateQuestionsList<QuizQuestionInitial>(quizId, totalIndex, { title });
|
||||
}, 200);
|
||||
|
||||
useEffect(() => {
|
||||
if (question.deleteTimeoutId) {
|
||||
clearTimeout(question.deleteTimeoutId);
|
||||
}
|
||||
}, [question]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper
|
||||
id={String(totalIndex)}
|
||||
data-cy="quiz-question-card"
|
||||
sx={{
|
||||
maxWidth: "796px",
|
||||
@ -217,7 +117,7 @@ export default function QuestionsPageCard({
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
anchorRef={anchorRef}
|
||||
totalIndex={totalIndex}
|
||||
question={question}
|
||||
switchState={question.type}
|
||||
/>
|
||||
</Box>
|
||||
@ -402,9 +302,9 @@ export default function QuestionsPageCard({
|
||||
}}
|
||||
>
|
||||
{question.type === "nonselected" ? (
|
||||
<TypeQuestions totalIndex={totalIndex} />
|
||||
<TypeQuestions question={question} />
|
||||
) : (
|
||||
<SwitchQuestionsPage totalIndex={totalIndex} />
|
||||
<SwitchQuestionsPage question={question} />
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
@ -447,3 +347,87 @@ export default function QuestionsPageCard({
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const IconAndrom = (isExpanded: boolean, switchState: string) => {
|
||||
switch (switchState) {
|
||||
case "variant":
|
||||
return (
|
||||
<Answer
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "images":
|
||||
return (
|
||||
<OptionsPict
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "varimg":
|
||||
return (
|
||||
<OptionsAndPict
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "emoji":
|
||||
return (
|
||||
<Emoji
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "text":
|
||||
return (
|
||||
<Input
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "select":
|
||||
return (
|
||||
<DropDown
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "date":
|
||||
return (
|
||||
<Date
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "number":
|
||||
return (
|
||||
<Slider
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "file":
|
||||
return (
|
||||
<Download
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "page":
|
||||
return (
|
||||
<Page
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
case "rating":
|
||||
return (
|
||||
<RatingIcon
|
||||
color={isExpanded ? "#9A9AAF" : "#7E2AEA"}
|
||||
sx={{ height: "22px", width: "20px" }}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,29 +1,40 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Box } from "@mui/material";
|
||||
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
||||
|
||||
import DraggableListItem from "./DraggableListItem";
|
||||
|
||||
import { questionStore, updateQuestionsListDragAndDrop } from "@root/questions";
|
||||
|
||||
import { reorder } from "./helper";
|
||||
|
||||
import type { DropResult } from "react-beautiful-dnd";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { useQuestionArray } from "@root/questions/hooks";
|
||||
import useSWR from "swr";
|
||||
import { questionApi } from "@api/question";
|
||||
import { setQuestions } from "@root/questions/actions";
|
||||
import { isAxiosError } from "axios";
|
||||
import { devlog } from "@frontend/kitui";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
|
||||
export const DraggableList = () => {
|
||||
const quizId = Number(useParams().quizId);
|
||||
const { listQuestions } = questionStore();
|
||||
const { quiz } = useCurrentQuiz();
|
||||
useSWR(["questions", quiz?.id], ([, id]) => questionApi.getList({ quiz_id: id }), {
|
||||
onSuccess: setQuestions,
|
||||
onError: error => {
|
||||
const message = isAxiosError<string>(error) ? (error.response?.data ?? "") : "";
|
||||
|
||||
const onDragEnd = ({ destination, source }: DropResult) => {
|
||||
if (destination) {
|
||||
const newItems = reorder(
|
||||
listQuestions[quizId],
|
||||
source.index,
|
||||
destination.index
|
||||
);
|
||||
|
||||
updateQuestionsListDragAndDrop(quizId, newItems);
|
||||
devlog("Error getting question list", error);
|
||||
enqueueSnackbar(`Не удалось получить вопросы. ${message}`);
|
||||
}
|
||||
});
|
||||
const questions = useQuestionArray();
|
||||
|
||||
const onDragEnd = ({ destination, source }: DropResult) => { // TODO
|
||||
// if (destination) {
|
||||
// const newItems = reorder(
|
||||
// listQuestions[quizId],
|
||||
// source.index,
|
||||
// destination.index
|
||||
// );
|
||||
|
||||
// updateQuestionsListDragAndDrop(quizId, newItems);
|
||||
// }
|
||||
};
|
||||
|
||||
return (
|
||||
@ -31,12 +42,12 @@ export const DraggableList = () => {
|
||||
<Droppable droppableId="droppable-list">
|
||||
{(provided, snapshot) => (
|
||||
<Box ref={provided.innerRef} {...provided.droppableProps}>
|
||||
{listQuestions[quizId]?.map((_, index) => (
|
||||
{questions.map((question, index) => (
|
||||
<DraggableListItem
|
||||
key={index}
|
||||
index={index}
|
||||
key={question.id}
|
||||
question={question}
|
||||
isDragging={snapshot.isDraggingOver}
|
||||
questionData={_}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
|
||||
@ -6,45 +6,31 @@ import {
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import AddPlus from "../../assets/icons/questionsPage/addPlus";
|
||||
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||
import { quizStore } from "@root/quizes";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {
|
||||
questionStore,
|
||||
createQuestion,
|
||||
updateQuestionsList,
|
||||
} from "@root/questions";
|
||||
import { DraggableList } from "./DraggableList";
|
||||
|
||||
import type { AnyQuizQuestion } from "../../model/questionTypes/shared";
|
||||
import { createQuestion } from "@root/questions/actions";
|
||||
import { incrementCurrentStep } from "@root/quizes/actions";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import QuizPreview from "@ui_kit/QuizPreview/QuizPreview";
|
||||
import { createPortal } from "react-dom";
|
||||
import AddPlus from "../../assets/icons/questionsPage/addPlus";
|
||||
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
|
||||
import { DraggableList } from "./DraggableList";
|
||||
|
||||
|
||||
export default function QuestionsPage() {
|
||||
const { listQuizes, updateQuizesList } = quizStore();
|
||||
const quizId = Number(useParams().quizId);
|
||||
const { listQuestions } = questionStore();
|
||||
const handleNext = () => {
|
||||
updateQuizesList(quizId, { step: listQuizes[quizId].step + 1 });
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
let result = listQuizes[quizId].step - 1;
|
||||
updateQuizesList(quizId, { step: result ? result : 1 });
|
||||
};
|
||||
|
||||
const collapseEverything = () => {
|
||||
listQuestions[quizId].forEach((item, index) => {
|
||||
updateQuestionsList<AnyQuizQuestion>(quizId, index, {
|
||||
...item,
|
||||
expanded: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
||||
const { quiz } = useCurrentQuiz();
|
||||
|
||||
const collapseEverything = () => { // TODO
|
||||
// listQuestions[quizId].forEach((item, index) => {
|
||||
// updateQuestionsList<AnyQuizQuestion>(quizId, index, {
|
||||
// ...item,
|
||||
// expanded: false,
|
||||
// });
|
||||
// });
|
||||
};
|
||||
|
||||
if (!quiz) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -82,7 +68,7 @@ export default function QuestionsPage() {
|
||||
>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
createQuestion(quizId);
|
||||
createQuestion(quiz.id);
|
||||
}}
|
||||
sx={{
|
||||
position: "fixed",
|
||||
@ -108,7 +94,7 @@ export default function QuestionsPage() {
|
||||
background: theme.palette.brightPurple.main,
|
||||
fontSize: "18px",
|
||||
}}
|
||||
onClick={handleNext}
|
||||
onClick={incrementCurrentStep}
|
||||
>
|
||||
Следующий шаг
|
||||
</Button>
|
||||
|
||||
@ -1,33 +1,24 @@
|
||||
import { Box } from "@mui/material";
|
||||
import QuestionsMiniButton from "@ui_kit/QuestionsMiniButton";
|
||||
import Answer from "../../assets/icons/questionsPage/answer";
|
||||
import OptionsPict from "../../assets/icons/questionsPage/options_pict";
|
||||
import OptionsAndPict from "../../assets/icons/questionsPage/options_and_pict";
|
||||
import Date from "../../assets/icons/questionsPage/date";
|
||||
import Download from "../../assets/icons/questionsPage/download";
|
||||
import DropDown from "../../assets/icons/questionsPage/drop_down";
|
||||
import Emoji from "../../assets/icons/questionsPage/emoji";
|
||||
import Input from "../../assets/icons/questionsPage/input";
|
||||
import DropDown from "../../assets/icons/questionsPage/drop_down";
|
||||
import Date from "../../assets/icons/questionsPage/date";
|
||||
import Slider from "../../assets/icons/questionsPage/slider";
|
||||
import Download from "../../assets/icons/questionsPage/download";
|
||||
import OptionsAndPict from "../../assets/icons/questionsPage/options_and_pict";
|
||||
import OptionsPict from "../../assets/icons/questionsPage/options_pict";
|
||||
import Page from "../../assets/icons/questionsPage/page";
|
||||
import RatingIcon from "../../assets/icons/questionsPage/rating";
|
||||
import { Box } from "@mui/material";
|
||||
import React from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import {
|
||||
questionStore,
|
||||
updateQuestionsList,
|
||||
createQuestion,
|
||||
removeQuestionForce,
|
||||
} from "@root/questions";
|
||||
|
||||
import Slider from "../../assets/icons/questionsPage/slider";
|
||||
import { setQuestionFieldOptimistic } from "@root/questions/actions";
|
||||
import type {
|
||||
QuizQuestionType,
|
||||
QuizQuestionBase,
|
||||
AnyQuizQuestion,
|
||||
QuizQuestionType
|
||||
} from "../../model/questionTypes/shared";
|
||||
|
||||
interface Props {
|
||||
totalIndex: number;
|
||||
question: AnyQuizQuestion;
|
||||
}
|
||||
|
||||
type ButtonTypeQuestion = {
|
||||
@ -36,6 +27,30 @@ type ButtonTypeQuestion = {
|
||||
value: QuizQuestionType;
|
||||
};
|
||||
|
||||
export default function TypeQuestions({ question }: Props) {
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
gap: "20px",
|
||||
padding: "8px 20px 20px",
|
||||
}}
|
||||
>
|
||||
{BUTTON_TYPE_QUESTIONS.map(({ icon, title, value }) => (
|
||||
<QuestionsMiniButton
|
||||
key={title}
|
||||
dataCy={`select-questiontype-${value}`}
|
||||
onClick={() => setQuestionFieldOptimistic(question.id, "type", value)}
|
||||
icon={icon}
|
||||
text={title}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export const BUTTON_TYPE_QUESTIONS: ButtonTypeQuestion[] = [
|
||||
{
|
||||
icon: <Answer color="#9A9AAF" />,
|
||||
@ -93,38 +108,3 @@ export const BUTTON_TYPE_QUESTIONS: ButtonTypeQuestion[] = [
|
||||
value: "rating",
|
||||
},
|
||||
];
|
||||
|
||||
export default function TypeQuestions({ totalIndex }: Props) {
|
||||
const quizId = Number(useParams().quizId);
|
||||
const { listQuestions } = questionStore();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
gap: "20px",
|
||||
padding: "8px 20px 20px",
|
||||
}}
|
||||
>
|
||||
{BUTTON_TYPE_QUESTIONS.map(({ icon, title, value }) => (
|
||||
<QuestionsMiniButton
|
||||
key={title}
|
||||
dataCy={`select-questiontype-${value}`}
|
||||
onClick={() => {
|
||||
const question = { ...listQuestions[quizId][totalIndex] };
|
||||
|
||||
removeQuestionForce(quizId, question.id);
|
||||
createQuestion(quizId, value, totalIndex);
|
||||
updateQuestionsList<QuizQuestionBase>(quizId, totalIndex, {
|
||||
expanded: question.expanded,
|
||||
type: value,
|
||||
});
|
||||
}}
|
||||
icon={icon}
|
||||
text={title}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,33 +1,35 @@
|
||||
import { questionApi } from "@api/question";
|
||||
import { devlog } from "@frontend/kitui";
|
||||
import { Question } from "@model/question/question";
|
||||
import { RawQuestion, rawQuestionToQuestion } from "@model/question/question";
|
||||
import { produce } from "immer";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
||||
import { QuestionsStore, useQuestionsStore } from "./store";
|
||||
import { questionToEditQuestionRequest } from "@model/question/edit";
|
||||
import { AnyQuizQuestion } from "@model/questionTypes/shared";
|
||||
|
||||
|
||||
export const setQuestions = (quizes: Question[] | null) => setProducedState(state => {
|
||||
export const setQuestions = (questions: RawQuestion[] | null) => setProducedState(state => {
|
||||
state.questionsById = {};
|
||||
if (quizes === null) return;
|
||||
if (questions === null) return;
|
||||
|
||||
quizes.forEach(question => state.questionsById[question.id] = question);
|
||||
questions.forEach(question => state.questionsById[question.id] = rawQuestionToQuestion(question));
|
||||
}, {
|
||||
type: "setQuizes",
|
||||
quizes,
|
||||
type: "setQuestions",
|
||||
questions,
|
||||
});
|
||||
|
||||
export const setQuestion = (question: Question) => setProducedState(state => {
|
||||
export const setQuestion = (question: AnyQuizQuestion) => setProducedState(state => {
|
||||
state.questionsById[question.id] = question;
|
||||
}, {
|
||||
type: "setQuestion",
|
||||
question,
|
||||
});
|
||||
|
||||
export const setQuestionField = <T extends keyof Question>(
|
||||
export const setQuestionField = <T extends keyof AnyQuizQuestion>(
|
||||
questionId: number,
|
||||
field: T,
|
||||
value: Question[T],
|
||||
value: AnyQuizQuestion[T],
|
||||
) => setProducedState(state => {
|
||||
const question = state.questionsById[questionId];
|
||||
if (!question) return;
|
||||
@ -40,13 +42,13 @@ export const setQuestionField = <T extends keyof Question>(
|
||||
value,
|
||||
});
|
||||
|
||||
let savedOriginalQuestion: Question | null = null;
|
||||
let savedOriginalQuestion: AnyQuizQuestion | null = null;
|
||||
let controller: AbortController | null = null;
|
||||
|
||||
export const setQuestionFieldOptimistic = async <T extends keyof Question>(
|
||||
export const setQuestionFieldOptimistic = async <T extends keyof AnyQuizQuestion>(
|
||||
questionId: number,
|
||||
field: T,
|
||||
value: Question[T],
|
||||
value: AnyQuizQuestion[T],
|
||||
) => {
|
||||
const question = useQuestionsStore.getState().questionsById[questionId] ?? null;
|
||||
if (!question) return;
|
||||
@ -60,9 +62,12 @@ export const setQuestionFieldOptimistic = async <T extends keyof Question>(
|
||||
|
||||
setQuestion(currentUpdatedQuestion);
|
||||
try {
|
||||
const { updated } = await questionApi.edit(currentUpdatedQuestion, controller.signal);
|
||||
const { updated } = await questionApi.edit(
|
||||
questionToEditQuestionRequest(currentUpdatedQuestion),
|
||||
controller.signal,
|
||||
);
|
||||
|
||||
setQuestionField(question.id, "version", updated);
|
||||
setQuestionField(question.id, "id", updated);
|
||||
controller = null;
|
||||
savedOriginalQuestion = null;
|
||||
} catch (error) {
|
||||
@ -83,7 +88,7 @@ export const setQuestionFieldOptimistic = async <T extends keyof Question>(
|
||||
|
||||
export const updateQuestionWithFn = (
|
||||
questionId: number,
|
||||
updateFn: (question: Question) => void,
|
||||
updateFn: (question: AnyQuizQuestion) => void,
|
||||
) => setProducedState(state => {
|
||||
const question = state.questionsById[questionId];
|
||||
if (!question) return;
|
||||
@ -101,7 +106,7 @@ export const createQuestion = async (quizId: number) => {
|
||||
quiz_id: quizId,
|
||||
});
|
||||
|
||||
setQuestion(question);
|
||||
setQuestion(rawQuestionToQuestion(question));
|
||||
} catch (error) {
|
||||
devlog("Error creating question", error);
|
||||
enqueueSnackbar("Не удалось создать вопрос");
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Question } from "@model/question/question";
|
||||
import { AnyQuizQuestion } from "@model/questionTypes/shared";
|
||||
import { create } from "zustand";
|
||||
import { devtools } from "zustand/middleware";
|
||||
|
||||
|
||||
export type QuestionsStore = {
|
||||
questionsById: Record<number, Question | undefined>;
|
||||
questionsById: Record<number, AnyQuizQuestion | undefined>;
|
||||
};
|
||||
|
||||
const initialState: QuestionsStore = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user