simplify question variant types
This commit is contained in:
parent
9c438de1f9
commit
f39ffb9c23
@ -18,7 +18,8 @@ export const QUIZ_QUESTION_EMOJI: Omit<QuizQuestionEmoji, "id" | "backendId"> =
|
|||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
answer: "",
|
answer: "",
|
||||||
extendedText: "",
|
extendedText: "",
|
||||||
hints: ""
|
hints: "",
|
||||||
|
originalImageUrl: "",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -13,6 +13,6 @@ export const QUIZ_QUESTION_SELECT: Omit<QuizQuestionSelect, "id" | "backendId">
|
|||||||
innerNameCheck: false,
|
innerNameCheck: false,
|
||||||
innerName: "",
|
innerName: "",
|
||||||
default: "",
|
default: "",
|
||||||
variants: [{ id: nanoid(), answer: "", extendedText: "", hints: "" }],
|
variants: [{ id: nanoid(), answer: "", extendedText: "", hints: "", originalImageUrl: "" }],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,6 +14,6 @@ export const QUIZ_QUESTION_VARIANT: Omit<QuizQuestionVariant, "id" | "backendId"
|
|||||||
innerNameCheck: false,
|
innerNameCheck: false,
|
||||||
required: false,
|
required: false,
|
||||||
innerName: "",
|
innerName: "",
|
||||||
variants: [{ id: nanoid(), answer: "", extendedText: "", hints: "" }],
|
variants: [{ id: nanoid(), answer: "", extendedText: "", hints: "", originalImageUrl: "" }],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
ImageQuestionVariant,
|
|
||||||
QuestionBranchingRule,
|
QuestionBranchingRule,
|
||||||
QuestionHint,
|
QuestionHint,
|
||||||
|
QuestionVariant,
|
||||||
QuizQuestionBase
|
QuizQuestionBase
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export interface QuizQuestionImages extends QuizQuestionBase {
|
|||||||
/** Чекбокс "Необязательный вопрос" */
|
/** Чекбокс "Необязательный вопрос" */
|
||||||
required: boolean;
|
required: boolean;
|
||||||
/** Варианты (картинки) */
|
/** Варианты (картинки) */
|
||||||
variants: ImageQuestionVariant[];
|
variants: QuestionVariant[];
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string;
|
||||||
|
|||||||
@ -40,12 +40,9 @@ export type QuestionVariant = {
|
|||||||
hints: string;
|
hints: string;
|
||||||
/** Дополнительное поле для текста, emoji, ссылки на картинку */
|
/** Дополнительное поле для текста, emoji, ссылки на картинку */
|
||||||
extendedText: string;
|
extendedText: string;
|
||||||
};
|
|
||||||
|
|
||||||
export interface ImageQuestionVariant extends QuestionVariant {
|
|
||||||
/** Оригинал изображения (до кропа) */
|
/** Оригинал изображения (до кропа) */
|
||||||
originalImageUrl: string;
|
originalImageUrl: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface QuizQuestionBase {
|
export interface QuizQuestionBase {
|
||||||
backendId: number;
|
backendId: number;
|
||||||
@ -89,7 +86,7 @@ export type AnyQuizQuestion =
|
|||||||
// | QuizQuestionInitial;
|
// | QuizQuestionInitial;
|
||||||
|
|
||||||
type FilterQuestionsWithVariants<T> = T extends {
|
type FilterQuestionsWithVariants<T> = T extends {
|
||||||
content: { variants: QuestionVariant[] | ImageQuestionVariant[]; };
|
content: { variants: QuestionVariant[]; };
|
||||||
} ? T : never;
|
} ? T : never;
|
||||||
|
|
||||||
export type QuizQuestionsWithVariants = FilterQuestionsWithVariants<AnyQuizQuestion>;
|
export type QuizQuestionsWithVariants = FilterQuestionsWithVariants<AnyQuizQuestion>;
|
||||||
@ -100,9 +97,5 @@ export const createQuestionVariant: () => QuestionVariant = () => ({
|
|||||||
answer: "",
|
answer: "",
|
||||||
extendedText: "",
|
extendedText: "",
|
||||||
hints: "",
|
hints: "",
|
||||||
});
|
|
||||||
|
|
||||||
export const createQuestionImageVariant: () => ImageQuestionVariant = () => ({
|
|
||||||
...createQuestionVariant(),
|
|
||||||
originalImageUrl: "",
|
originalImageUrl: "",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
ImageQuestionVariant,
|
|
||||||
QuestionBranchingRule,
|
QuestionBranchingRule,
|
||||||
QuestionHint,
|
QuestionHint,
|
||||||
|
QuestionVariant,
|
||||||
QuizQuestionBase
|
QuizQuestionBase
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export interface QuizQuestionVarImg extends QuizQuestionBase {
|
|||||||
innerName: string;
|
innerName: string;
|
||||||
/** Чекбокс "Необязательный вопрос" */
|
/** Чекбокс "Необязательный вопрос" */
|
||||||
required: boolean;
|
required: boolean;
|
||||||
variants: ImageQuestionVariant[];
|
variants: QuestionVariant[];
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string;
|
||||||
|
|||||||
@ -16,14 +16,13 @@ import { addQuestionVariant, deleteQuestionVariant, setQuestionVariantField } fr
|
|||||||
import type { KeyboardEvent, ReactNode } from "react";
|
import type { KeyboardEvent, ReactNode } from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Draggable } from "react-beautiful-dnd";
|
import { Draggable } from "react-beautiful-dnd";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import type { QuestionVariant } from "../../../model/questionTypes/shared";
|
||||||
import type { ImageQuestionVariant, QuestionVariant } from "../../../model/questionTypes/shared";
|
|
||||||
|
|
||||||
|
|
||||||
type AnswerItemProps = {
|
type AnswerItemProps = {
|
||||||
index: number;
|
index: number;
|
||||||
questionId: string;
|
questionId: string;
|
||||||
variant: QuestionVariant | ImageQuestionVariant;
|
variant: QuestionVariant;
|
||||||
largeCheck: boolean;
|
largeCheck: boolean;
|
||||||
additionalContent?: ReactNode;
|
additionalContent?: ReactNode;
|
||||||
additionalMobile?: ReactNode;
|
additionalMobile?: ReactNode;
|
||||||
@ -73,7 +72,7 @@ export const AnswerItem = ({
|
|||||||
placeholder={"Добавьте ответ"}
|
placeholder={"Добавьте ответ"}
|
||||||
multiline={largeCheck}
|
multiline={largeCheck}
|
||||||
onChange={({ target }) => {
|
onChange={({ target }) => {
|
||||||
setQuestionVariantField(questionId, variant.id, "answer", target.value)
|
setQuestionVariantField(questionId, variant.id, "answer", target.value);
|
||||||
}}
|
}}
|
||||||
onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => {
|
onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (event.code === "Enter" && !largeCheck) {
|
if (event.code === "Enter" && !largeCheck) {
|
||||||
|
|||||||
@ -3,47 +3,47 @@ import { reorderQuestionVariants } from "@root/questions/actions";
|
|||||||
import { type ReactNode } from "react";
|
import { type ReactNode } from "react";
|
||||||
import type { DropResult } from "react-beautiful-dnd";
|
import type { DropResult } from "react-beautiful-dnd";
|
||||||
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
||||||
import type { ImageQuestionVariant, QuestionVariant, QuizQuestionsWithVariants } from "../../../model/questionTypes/shared";
|
import type { QuestionVariant, QuizQuestionsWithVariants } from "../../../model/questionTypes/shared";
|
||||||
import { AnswerItem } from "./AnswerItem";
|
import { AnswerItem } from "./AnswerItem";
|
||||||
|
|
||||||
|
|
||||||
type AnswerDraggableListProps = {
|
type AnswerDraggableListProps = {
|
||||||
question: QuizQuestionsWithVariants;
|
question: QuizQuestionsWithVariants;
|
||||||
additionalContent?: (variant: QuestionVariant | ImageQuestionVariant, index: number) => ReactNode;
|
additionalContent?: (variant: QuestionVariant, index: number) => ReactNode;
|
||||||
additionalMobile?: (variant: QuestionVariant | ImageQuestionVariant, index: number) => ReactNode;
|
additionalMobile?: (variant: QuestionVariant, index: number) => ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AnswerDraggableList = ({
|
export const AnswerDraggableList = ({
|
||||||
question,
|
question,
|
||||||
additionalContent,
|
additionalContent,
|
||||||
additionalMobile,
|
additionalMobile,
|
||||||
}: AnswerDraggableListProps) => {
|
}: AnswerDraggableListProps) => {
|
||||||
const onDragEnd = ({ destination, source }: DropResult) => {
|
const onDragEnd = ({ destination, source }: DropResult) => {
|
||||||
if (destination) {
|
if (destination) {
|
||||||
reorderQuestionVariants(question.id, source.index, destination.index);
|
reorderQuestionVariants(question.id, source.index, destination.index);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<Droppable droppableId="droppable-answer-list">
|
<Droppable droppableId="droppable-answer-list">
|
||||||
{(provided) => (
|
{(provided) => (
|
||||||
<Box ref={provided.innerRef} {...provided.droppableProps}>
|
<Box ref={provided.innerRef} {...provided.droppableProps}>
|
||||||
{question.content.variants.map((variant, index) => (
|
{question.content.variants.map((variant, index) => (
|
||||||
<AnswerItem
|
<AnswerItem
|
||||||
key={variant.id}
|
key={variant.id}
|
||||||
index={index}
|
index={index}
|
||||||
questionId={question.id}
|
questionId={question.id}
|
||||||
largeCheck={("largeCheck" in question.content) ? question.content.largeCheck : false}
|
largeCheck={("largeCheck" in question.content) ? question.content.largeCheck : false}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
additionalContent={additionalContent?.(variant, index)}
|
additionalContent={additionalContent?.(variant, index)}
|
||||||
additionalMobile={additionalMobile?.(variant, index)}
|
additionalMobile={additionalMobile?.(variant, index)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{provided.placeholder}
|
{provided.placeholder}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Droppable>
|
</Droppable>
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,14 +2,13 @@ import { questionApi } from "@api/question";
|
|||||||
import { devlog } from "@frontend/kitui";
|
import { devlog } from "@frontend/kitui";
|
||||||
import { questionToEditQuestionRequest } from "@model/question/edit";
|
import { questionToEditQuestionRequest } from "@model/question/edit";
|
||||||
import { QuestionType, RawQuestion, rawQuestionToQuestion } from "@model/question/question";
|
import { QuestionType, RawQuestion, rawQuestionToQuestion } from "@model/question/question";
|
||||||
import { AnyQuizQuestion, ImageQuestionVariant, QuestionVariant, createQuestionImageVariant, createQuestionVariant } from "@model/questionTypes/shared";
|
import { AnyQuizQuestion, QuestionVariant, createQuestionVariant } from "@model/questionTypes/shared";
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
||||||
import { notReachable } from "../../utils/notReachable";
|
|
||||||
import { RequestQueue } from "../../utils/requestQueue";
|
import { RequestQueue } from "../../utils/requestQueue";
|
||||||
import { QuestionsStore, useQuestionsStore } from "./store";
|
import { QuestionsStore, useQuestionsStore } from "./store";
|
||||||
import { nanoid } from "nanoid";
|
|
||||||
|
|
||||||
|
|
||||||
export const setQuestions = (questions: RawQuestion[] | null) => setProducedState(state => {
|
export const setQuestions = (questions: RawQuestion[] | null) => setProducedState(state => {
|
||||||
@ -76,20 +75,11 @@ export const addQuestionVariant = (questionId: string) => {
|
|||||||
case "variant":
|
case "variant":
|
||||||
case "emoji":
|
case "emoji":
|
||||||
case "select":
|
case "select":
|
||||||
question.content.variants.push(createQuestionVariant());
|
|
||||||
break;
|
|
||||||
case "images":
|
case "images":
|
||||||
case "varimg":
|
case "varimg":
|
||||||
question.content.variants.push(createQuestionImageVariant());
|
question.content.variants.push(createQuestionVariant());
|
||||||
break;
|
break;
|
||||||
case "text":
|
default: throw new Error(`Cannot add variant to question of type "${question.type}"`);
|
||||||
case "date":
|
|
||||||
case "number":
|
|
||||||
case "file":
|
|
||||||
case "page":
|
|
||||||
case "rating":
|
|
||||||
throw new Error(`Cannot add variant to question of type "${question.type}"`);
|
|
||||||
default: notReachable(question);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -173,7 +163,7 @@ export const setVariantImageUrl = (
|
|||||||
if (!("variants" in question.content)) return;
|
if (!("variants" in question.content)) return;
|
||||||
|
|
||||||
const variant = question.content.variants.find(variant => variant.id === variantId);
|
const variant = question.content.variants.find(variant => variant.id === variantId);
|
||||||
if (!variant || !("originalImageUrl" in variant)) return;
|
if (!variant) return;
|
||||||
|
|
||||||
if (variant.extendedText === url) return;
|
if (variant.extendedText === url) return;
|
||||||
|
|
||||||
@ -192,8 +182,8 @@ export const setVariantOriginalImageUrl = (
|
|||||||
|
|
||||||
const variant = question.content.variants.find(
|
const variant = question.content.variants.find(
|
||||||
variant => variant.id === variantId
|
variant => variant.id === variantId
|
||||||
) as ImageQuestionVariant | undefined;
|
) as QuestionVariant | undefined;
|
||||||
if (!variant || !("originalImageUrl" in variant)) return;
|
if (!variant) return;
|
||||||
|
|
||||||
if (variant.originalImageUrl === url) return;
|
if (variant.originalImageUrl === url) return;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user