4 варианта с фичами работают

This commit is contained in:
Nastya 2024-09-12 18:55:25 +03:00
parent 2fc986c88e
commit 00dace0d3e
11 changed files with 292 additions and 54 deletions

@ -1,6 +1,16 @@
import type { QuestionVariant } from "@/model/questionTypes/shared";
import { useQuizSettings } from "@contexts/QuizDataContext";
import { Box, FormControl, FormControlLabel, Radio, Typography, useTheme } from "@mui/material";
import {
Box,
Checkbox,
FormControl,
FormControlLabel,
Input,
Radio,
TextareaAutosize,
Typography,
useTheme,
} from "@mui/material";
import { useQuizViewStore } from "@stores/quizView";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
@ -14,18 +24,96 @@ type EmojiVariantProps = {
questionId: string;
variant: QuestionVariant;
index: number;
isMulti: boolean;
own: boolean;
questionLargeCheck: boolean;
ownPlaceholder: string;
answer: string | string[] | undefined;
};
export const EmojiVariant = ({ variant, index, questionId }: EmojiVariantProps) => {
interface OwnInputProps {
questionId: string;
variant: QuestionVariant;
largeCheck: boolean;
ownPlaceholder: string;
}
const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputProps) => {
const theme = useTheme();
const ownVariants = useQuizViewStore((state) => state.ownVariants);
const { updateOwnVariant } = useQuizViewStore((state) => state);
const ownAnswer = ownVariants[ownVariants.findIndex((v) => v.id === variant.id)]?.variant.answer || "";
return largeCheck ? (
<Box sx={{ overflow: "auto" }}>
<TextareaAutosize
placeholder={ownPlaceholder}
style={{
resize: "none",
width: "100%",
fontSize: "16px",
color: theme.palette.text.primary,
letterSpacing: "-0.4px",
wordSpacing: "-3px",
border: "none",
outline: "0px none",
}}
value={ownAnswer}
onClick={(e: React.MouseEvent<HTMLTextAreaElement>) => e.stopPropagation()}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
updateOwnVariant(variant.id, e.target.value);
}}
/>
</Box>
) : (
<Input
placeholder={ownPlaceholder}
sx={{
width: "100%",
fontSize: "18px",
color: theme.palette.text.primary,
}}
value={ownAnswer}
disableUnderline
onClick={(e: React.MouseEvent<HTMLElement>) => e.stopPropagation()}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
updateOwnVariant(variant.id, e.target.value);
}}
/>
);
};
export const EmojiVariant = ({
answer,
variant,
index,
questionId,
isMulti,
own,
questionLargeCheck,
ownPlaceholder,
}: EmojiVariantProps) => {
const { settings } = useQuizSettings();
const answers = useQuizViewStore((state) => state.answers);
const { updateAnswer, deleteAnswer } = useQuizViewStore((state) => state);
const theme = useTheme();
const { answer } = answers.find((answer) => answer.questionId === questionId) ?? {};
const onVariantClick = async (event: MouseEvent<HTMLDivElement>) => {
event.preventDefault();
const variantId = variant.id;
if (isMulti) {
const currentAnswer = typeof answer !== "string" ? answer || [] : [];
return updateAnswer(
questionId,
currentAnswer.includes(variantId)
? currentAnswer?.filter((item) => item !== variantId)
: [...currentAnswer, variantId],
variant.points || 0
);
}
updateAnswer(questionId, variant.id, variant.points || 0);
if (answer === variant.id) {
@ -39,7 +127,7 @@ export const EmojiVariant = ({ variant, index, questionId }: EmojiVariantProps)
sx={{
borderRadius: "12px",
border: `1px solid`,
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
borderColor: answer?.includes(variant.id) ? theme.palette.primary.main : "#9A9AAF",
overflow: "hidden",
maxWidth: "317px",
width: "100%",
@ -85,6 +173,7 @@ export const EmojiVariant = ({ variant, index, questionId }: EmojiVariantProps)
alignItems: variant.answer.length <= 60 ? "center" : "flex-start",
position: "relative",
height: "80px",
overflow: "auto",
justifyContent: "center",
"& .MuiFormControlLabel-label": {
wordBreak: "break-word",
@ -101,16 +190,34 @@ export const EmojiVariant = ({ variant, index, questionId }: EmojiVariantProps)
}}
value={index}
control={
<Radio
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
icon={<RadioIcon />}
sx={{ position: "absolute", top: "-162px", right: "12px" }}
/>
isMulti ? (
<Checkbox
checked={!!answer?.includes(variant.id)}
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
icon={<RadioIcon />}
sx={{ position: "absolute", top: "-162px", right: "12px" }}
/>
) : (
<Radio
checkedIcon={<RadioCheck color={theme.palette.primary.main} />}
icon={<RadioIcon />}
sx={{ position: "absolute", top: "-162px", right: "12px" }}
/>
)
}
label={
<Box sx={{ display: "flex", gap: "10px" }}>
<Typography sx={{ wordBreak: "break-word", lineHeight: "normal" }}>{variant.answer}</Typography>
</Box>
own ? (
<OwnInput
questionId={questionId}
variant={variant}
largeCheck={questionLargeCheck}
ownPlaceholder={ownPlaceholder}
/>
) : (
<Box sx={{ display: "flex", gap: "10px" }}>
<Typography sx={{ wordBreak: "break-word", lineHeight: "normal" }}>{variant.answer}</Typography>
</Box>
)
}
/>
</FormControl>

@ -3,6 +3,7 @@ import { Box, RadioGroup, Typography, useTheme } from "@mui/material";
import { useQuizViewStore } from "@stores/quizView";
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
import { EmojiVariant } from "./EmojiVariant";
import moment from "moment";
polyfillCountryFlagEmojis();
@ -16,6 +17,8 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
const theme = useTheme();
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.id) ?? {};
if (moment.isMoment(answer)) throw new Error("Answer is Moment in Variant question");
return (
<Box>
<Typography
@ -44,14 +47,24 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
}}
>
<Box sx={{ display: "flex", width: "100%", gap: "42px", flexWrap: "wrap" }}>
{currentQuestion.content.variants.map((variant, index) => (
<EmojiVariant
key={variant.id}
questionId={currentQuestion.id}
variant={variant}
index={index}
/>
))}
{currentQuestion.content.variants
.filter((v) => {
if (!v.isOwn) return true;
return v.isOwn && currentQuestion.content.own;
})
.map((variant, index) => (
<EmojiVariant
key={variant.id}
questionId={currentQuestion.id}
variant={variant}
index={index}
isMulti={Boolean(currentQuestion.content.multi)}
own={Boolean(variant.isOwn)}
questionLargeCheck={true}
answer={answer}
ownPlaceholder={currentQuestion.content?.ownPlaceholder || ""}
/>
))}
</Box>
</RadioGroup>
</Box>

@ -1,7 +1,7 @@
import { CheckboxIcon } from "@/assets/icons/Checkbox";
import type { QuestionVariant } from "@/model/questionTypes/shared";
import { useQuizSettings } from "@contexts/QuizDataContext";
import { Box, Checkbox, FormControlLabel, Radio, useTheme } from "@mui/material";
import { Box, Checkbox, FormControlLabel, Input, Radio, TextareaAutosize, useTheme } from "@mui/material";
import { useQuizViewStore } from "@stores/quizView";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
@ -12,11 +12,75 @@ type ImagesProps = {
questionId: string;
variant: QuestionVariant;
index: number;
isMulti: boolean;
answer: string | string[] | undefined;
isMulti: boolean;
own: boolean;
questionLargeCheck: boolean;
ownPlaceholder: string;
};
export const ImageVariant = ({ questionId, answer, isMulti, variant, index }: ImagesProps) => {
interface OwnInputProps {
questionId: string;
variant: QuestionVariant;
largeCheck: boolean;
ownPlaceholder: string;
}
const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputProps) => {
const theme = useTheme();
const ownVariants = useQuizViewStore((state) => state.ownVariants);
const { updateOwnVariant } = useQuizViewStore((state) => state);
const ownAnswer = ownVariants[ownVariants.findIndex((v) => v.id === variant.id)]?.variant.answer || "";
return largeCheck ? (
<Box sx={{ overflow: "auto" }}>
<TextareaAutosize
placeholder={ownPlaceholder}
style={{
resize: "none",
width: "100%",
fontSize: "16px",
color: theme.palette.text.primary,
letterSpacing: "-0.4px",
wordSpacing: "-3px",
border: "none",
outline: "0px none",
}}
value={ownAnswer}
onClick={(e: React.MouseEvent<HTMLTextAreaElement>) => e.stopPropagation()}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
updateOwnVariant(variant.id, e.target.value);
}}
/>
</Box>
) : (
<Input
placeholder={ownPlaceholder}
sx={{
width: "100%",
fontSize: "18px",
color: theme.palette.text.primary,
}}
value={ownAnswer}
disableUnderline
onClick={(e: React.MouseEvent<HTMLElement>) => e.stopPropagation()}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
updateOwnVariant(variant.id, e.target.value);
}}
/>
);
};
export const ImageVariant = ({
questionId,
answer,
isMulti,
variant,
index,
own,
questionLargeCheck,
ownPlaceholder,
}: ImagesProps) => {
const { settings } = useQuizSettings();
const { deleteAnswer, updateAnswer } = useQuizViewStore((state) => state);
const theme = useTheme();
@ -92,6 +156,7 @@ export const ImageVariant = ({ questionId, answer, isMulti, variant, index }: Im
justifyContent: "center",
position: "relative",
height: "80px",
overflow: "auto",
"& .MuiFormControlLabel-label": {
wordBreak: "break-word",
height: variant.answer.length <= 60 ? undefined : "60px",
@ -130,7 +195,18 @@ export const ImageVariant = ({ questionId, answer, isMulti, variant, index }: Im
/>
)
}
label={variant.answer}
label={
own ? (
<OwnInput
questionId={questionId}
variant={variant}
largeCheck={questionLargeCheck}
ownPlaceholder={ownPlaceholder}
/>
) : (
variant.answer
)
}
/>
</Box>
);

@ -16,8 +16,6 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
const isTablet = useRootContainerSize() < 1000;
const isMobile = useRootContainerSize() < 500;
console.log(currentQuestion);
if (moment.isMoment(answer)) throw new Error("Answer is Moment in Variant question");
return (
@ -48,16 +46,24 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
width: "100%",
}}
>
{currentQuestion.content.variants.map((variant, index) => (
<ImageVariant
key={variant.id}
questionId={currentQuestion.id}
variant={variant}
index={index}
answer={answer}
isMulti={Boolean(currentQuestion.content.multi)}
/>
))}
{currentQuestion.content.variants
.filter((v) => {
if (!v.isOwn) return true;
return v.isOwn && currentQuestion.content.own;
})
.map((variant, index) => (
<ImageVariant
key={variant.id}
questionId={currentQuestion.id}
variant={variant}
index={index}
answer={answer}
isMulti={Boolean(currentQuestion.content.multi)}
own={Boolean(variant.isOwn)}
questionLargeCheck={true}
ownPlaceholder={currentQuestion.content?.ownPlaceholder || ""}
/>
))}
</Box>
</RadioGroup>
</Box>

@ -25,7 +25,7 @@ interface OwnInputProps {
largeCheck: boolean;
ownPlaceholder: string;
}
const OwnInput = ({ questionId, variant, largeCheck }: OwnInputProps) => {
const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputProps) => {
const theme = useTheme();
const ownVariants = useQuizViewStore((state) => state.ownVariants);
const { updateOwnVariant } = useQuizViewStore((state) => state);
@ -34,7 +34,7 @@ const OwnInput = ({ questionId, variant, largeCheck }: OwnInputProps) => {
return largeCheck ? (
<TextareaAutosize
placeholder=""
placeholder={ownPlaceholder}
style={{
resize: "none",
width: "100%",
@ -53,6 +53,7 @@ const OwnInput = ({ questionId, variant, largeCheck }: OwnInputProps) => {
/>
) : (
<Input
placeholder={ownPlaceholder}
sx={{
width: "100%",
fontSize: "18px",
@ -83,7 +84,7 @@ export const VariantItem = ({
variant: QuestionVariant;
answer: string | string[] | undefined;
index: number;
own?: boolean;
own: boolean;
questionLargeCheck: boolean;
ownPlaceholder: string;
}) => {

@ -86,7 +86,7 @@ export const Variant = ({ currentQuestion }: VariantProps) => {
variant={variant}
answer={answer}
index={index}
own={variant.isOwn}
own={Boolean(variant.isOwn)}
questionLargeCheck={currentQuestion.content.largeCheck}
ownPlaceholder={currentQuestion.content?.ownPlaceholder || ""}
/>

@ -26,7 +26,7 @@ interface OwnInputProps {
largeCheck: boolean;
ownPlaceholder: string;
}
const OwnInput = ({ questionId, variant, largeCheck }: OwnInputProps) => {
const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputProps) => {
const theme = useTheme();
const ownVariants = useQuizViewStore((state) => state.ownVariants);
const { updateOwnVariant } = useQuizViewStore((state) => state);
@ -35,7 +35,7 @@ const OwnInput = ({ questionId, variant, largeCheck }: OwnInputProps) => {
return largeCheck ? (
<TextareaAutosize
placeholder=""
placeholder={ownPlaceholder}
style={{
resize: "none",
width: "100%",
@ -54,6 +54,7 @@ const OwnInput = ({ questionId, variant, largeCheck }: OwnInputProps) => {
/>
) : (
<Input
placeholder={ownPlaceholder}
sx={{
width: "100%",
fontSize: "18px",

@ -28,8 +28,6 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
const ownVariant = ownVariants.find((variant) => variant.id === currentQuestion.id);
const variant = currentQuestion.content.variants.find(({ id }) => answer === id);
console.log(variant);
useEffect(() => {
if (!ownVariant) {
updateOwnVariant(currentQuestion.id, "");

@ -95,7 +95,7 @@ export const createQuizViewStore = () =>
(state) => {
const index = state.ownVariants.findIndex((variant) => variant.id === id);
if (index < 0 || !index) {
if (index < 0) {
state.ownVariants.push({
id,
variant: {

@ -28,6 +28,37 @@ export function sendQuestionAnswer(
});
}
case "emoji": {
if (question.content.multi) {
const answer = questionAnswer.answer;
const ownVariant = Array.isArray(answer)
? ownVariants[ownVariants.findIndex((variant) => answer.some((a: string) => a === variant.id))]?.variant || ""
: ownVariants[ownVariants.findIndex((variant) => variant.id === questionAnswer.answer)]?.variant || "";
if (moment.isMoment(answer)) throw new Error("Answer is Moment in Variant question");
//Оставляем только выбранные варианты
const selectedVariants = question.content.variants.filter((v) => answer.includes(v.id));
let answerString = ``;
selectedVariants.forEach((e) => {
if (e.isOwn) {
if (question.content.own && selectedVariants.some((v) => v.isOwn)) {
answerString += `\`${e.extendedText} ${ownVariant?.answer ?? ""}\`,`;
}
} else {
answerString += `\`${e.extendedText} ${e.answer ?? ""}\`,`;
}
});
answerString = answerString.slice(0, -1);
return sendAnswer({
questionId: question.id,
body: answerString,
qid: quizId,
});
}
const variant = question.content.variants.find((v) => v.id === questionAnswer.answer);
if (!variant) throw new Error(`Cannot find variant with id ${questionAnswer.answer} in question ${question.id}`);
@ -43,19 +74,25 @@ export function sendQuestionAnswer(
case "images": {
if (question.content.multi) {
const answer = questionAnswer.answer;
if (!Array.isArray(answer)) throw new Error("Cannot send answer in select question");
const ownAnswer = Array.isArray(answer)
? ownVariants[ownVariants.findIndex((variant) => answer.some((a: string) => a === variant.id))]?.variant
?.answer || ""
: ownVariants[ownVariants.findIndex((variant) => variant.id === questionAnswer.answer)]?.variant?.answer ||
"";
if (moment.isMoment(answer)) throw new Error("Answer is Moment in Variant question");
//Оставляем только выбранные варианты
const selectedVariants = question.content.variants.filter((v) => answer.includes(v.id));
let answerString = ``;
selectedVariants.forEach((variant) => {
const body = JSON.stringify({
Image: variant.extendedText,
Description: variant.answer,
});
answerString += `\`${body}\`,`;
selectedVariants.forEach((e) => {
if (!e.isOwn) answerString += `\`${e.answer}\`,`;
});
if (question.content.own && selectedVariants.some((v) => v.isOwn)) {
answerString += `\`${ownAnswer}\`,`;
}
answerString = answerString.slice(0, -1);
return sendAnswer({
@ -133,8 +170,6 @@ export function sendQuestionAnswer(
: ownVariants[ownVariants.findIndex((variant) => variant.id === questionAnswer.answer)]?.variant?.answer ||
"";
if (!Array.isArray(answer)) throw new Error("Cannot send answer in select question");
//Оставляем только выбранные варианты
const selectedVariants = question.content.variants.filter((v) => answer.includes(v.id));

1
src/ CHANGELOG.md Normal file

@ -0,0 +1 @@
1.0.0 Добавлены фичи "мультиответ", "перенос строки в своём ответе", "свой ответ", "плейсхолдер своего ответа"