simplify question variant types

This commit is contained in:
nflnkr 2023-11-28 22:16:00 +03:00
parent 9c438de1f9
commit f39ffb9c23
9 changed files with 56 additions and 73 deletions

@ -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,14 +3,14 @@ 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 = ({

@ -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;