Merge branch 'dev' into 'main'

Dev

See merge request frontend/squiz!33
This commit is contained in:
Nastya 2023-10-18 06:40:09 +00:00
commit 6cf187703e
28 changed files with 1150 additions and 1274 deletions

@ -3,7 +3,7 @@ import type { QuizQuestionInitial } from "../model/questionTypes/shared";
export const QUIZ_QUESTION_BASE: Omit<QuizQuestionInitial, "id"> = { export const QUIZ_QUESTION_BASE: Omit<QuizQuestionInitial, "id"> = {
title: "", title: "",
type: "nonselected", type: "nonselected",
expanded: false, expanded: true,
required: false, required: false,
deleted: false, deleted: false,
deleteTimeoutId: 0, deleteTimeoutId: 0,

@ -15,7 +15,6 @@ export const QUIZ_QUESTION_EMOJI: Omit<QuizQuestionEmoji, "id"> = {
variants: [ variants: [
{ {
answer: "", answer: "",
hints: "",
extendedText: "", extendedText: "",
}, },
], ],

@ -18,7 +18,6 @@ export const QUIZ_QUESTION_IMAGES: Omit<QuizQuestionImages, "id"> = {
variants: [ variants: [
{ {
answer: "", answer: "",
hints: "",
extendedText: "", extendedText: "",
}, },
], ],

@ -12,6 +12,6 @@ export const QUIZ_QUESTION_SELECT: Omit<QuizQuestionSelect, "id"> = {
innerNameCheck: false, innerNameCheck: false,
innerName: "", innerName: "",
default: "", default: "",
variants: [{ answer: "", hints: "", extendedText: "" }], variants: [{ answer: "", extendedText: "" }],
}, },
}; };

@ -13,6 +13,6 @@ export const QUIZ_QUESTION_VARIANT: Omit<QuizQuestionVariant, "id"> = {
innerNameCheck: false, innerNameCheck: false,
required: false, required: false,
innerName: "", innerName: "",
variants: [{ answer: "", hints: "", extendedText: "" }], variants: [{ answer: "", extendedText: "" }],
}, },
}; };

@ -11,7 +11,7 @@ export const QUIZ_QUESTION_VARIMG: Omit<QuizQuestionVarImg, "id"> = {
innerNameCheck: false, innerNameCheck: false,
innerName: "", innerName: "",
required: false, required: false,
variants: [{ answer: "", hints: "", extendedText: "" }], variants: [{ answer: "", extendedText: "" }],
largeCheck: false, largeCheck: false,
replText: "", replText: "",
}, },

@ -32,8 +32,6 @@ export interface QuestionHint {
export type QuestionVariant = { export type QuestionVariant = {
/** Текст */ /** Текст */
answer: string; answer: string;
/** Текст подсказки */
hints: string;
/** Дополнительное поле для текста, emoji, ссылки на картинку */ /** Дополнительное поле для текста, emoji, ссылки на картинку */
extendedText: string; extendedText: string;
}; };

@ -7,7 +7,6 @@ import {
FormControl, FormControl,
InputAdornment, InputAdornment,
IconButton, IconButton,
Popover,
useTheme, useTheme,
useMediaQuery, useMediaQuery,
} from "@mui/material"; } from "@mui/material";
@ -17,10 +16,9 @@ import { questionStore, updateQuestionsList } from "@root/questions";
import { PointsIcon } from "@icons/questionsPage/PointsIcon"; import { PointsIcon } from "@icons/questionsPage/PointsIcon";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon"; import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import { MessageIcon } from "@icons/messagIcon";
import TextareaAutosize from "@mui/base/TextareaAutosize";
import type { ChangeEvent, KeyboardEvent, ReactNode } from "react"; import type { KeyboardEvent, ReactNode } from "react";
import type { DroppableProvided } from "react-beautiful-dnd";
import type { QuizQuestionVariant } from "../../../model/questionTypes/variant"; import type { QuizQuestionVariant } from "../../../model/questionTypes/variant";
import type { QuestionVariant } from "../../../model/questionTypes/shared"; import type { QuestionVariant } from "../../../model/questionTypes/shared";
@ -29,9 +27,9 @@ type AnswerItemProps = {
totalIndex: number; totalIndex: number;
variants: QuestionVariant[]; variants: QuestionVariant[];
variant: QuestionVariant; variant: QuestionVariant;
provided: DroppableProvided;
additionalContent?: ReactNode; additionalContent?: ReactNode;
additionalMobile?: ReactNode; additionalMobile?: ReactNode;
icon?: ReactNode;
}; };
export const AnswerItem = ({ export const AnswerItem = ({
@ -39,6 +37,7 @@ export const AnswerItem = ({
totalIndex, totalIndex,
variants, variants,
variant, variant,
provided,
additionalContent, additionalContent,
additionalMobile, additionalMobile,
}: AnswerItemProps) => { }: AnswerItemProps) => {
@ -59,21 +58,9 @@ export const AnswerItem = ({
}); });
}, 1000); }, 1000);
const [isOpen, setIsOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
setIsOpen(true);
};
const handleClose = () => {
setIsOpen(false);
};
const addNewAnswer = () => { const addNewAnswer = () => {
const answerNew = variants.slice(); const answerNew = variants.slice();
answerNew.push({ answer: "", hints: "", extendedText: "" }); answerNew.push({ answer: "", extendedText: "" });
updateQuestionsList<QuizQuestionVariant>(quizId, totalIndex, { updateQuestionsList<QuizQuestionVariant>(quizId, totalIndex, {
content: { ...question.content, variants: answerNew }, content: { ...question.content, variants: answerNew },
@ -89,123 +76,79 @@ export const AnswerItem = ({
}); });
}; };
const changeAnswerHint = (event: ChangeEvent<HTMLTextAreaElement>) => {
const answerNew = variants.slice();
answerNew[index].hints = event.target.value;
updateQuestionsList<QuizQuestionVariant>(quizId, totalIndex, {
content: { ...question.content, variants: answerNew },
});
};
return ( return (
<Draggable draggableId={String(index)} index={index}> <Box>
{(provided) => ( <FormControl
<Box ref={provided.innerRef} {...provided.draggableProps}> key={index}
<FormControl fullWidth
key={index} variant="standard"
fullWidth sx={{
variant="standard" margin: isTablet ? " 15px 0 20px 0" : "0 0 20px 0",
sx={{ borderRadius: "10px",
margin: isTablet ? " 15px 0 20px 0" : "0 0 20px 0", border: "1px solid rgba(0, 0, 0, 0.23)",
background: "white",
}}
>
<TextField
defaultValue={variant.answer}
fullWidth
focused={false}
placeholder={"Добавьте ответ"}
multiline={question.content.largeCheck}
onChange={({ target }) => debounced(target.value)}
onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => {
if (event.code === "Enter" && !question.content.largeCheck) {
addNewAnswer();
}
}}
InputProps={{
startAdornment: (
<>
<InputAdornment {...provided.droppableProps} position="start">
<PointsIcon style={{ color: "#9A9AAF", fontSize: "30px" }} />
</InputAdornment>
{additionalContent}
</>
),
endAdornment: (
<InputAdornment position="end">
<IconButton sx={{ padding: "0" }} onClick={deleteAnswer}>
<DeleteIcon
style={{
color: theme.palette.grey2.main,
marginRight: "-1px",
}}
/>
</IconButton>
</InputAdornment>
),
}}
sx={{
"& .MuiInputBase-root": {
padding: additionalContent
? isTablet
? "13px"
: "5px 13px"
: "13px",
borderRadius: "10px", borderRadius: "10px",
border: "1px solid rgba(0, 0, 0, 0.23)", background: "#ffffff",
background: "white", "& input.MuiInputBase-input": {
}} height: "22px",
> },
<TextField "& textarea.MuiInputBase-input": {
defaultValue={variant.answer} marginTop: "1px",
fullWidth },
focused={false} "& .MuiOutlinedInput-notchedOutline": {
placeholder={"Добавьте ответ"} border: "none",
multiline={question.content.largeCheck} },
onChange={({ target }) => debounced(target.value)} },
onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => { }}
if (event.code === "Enter" && !question.content.largeCheck) { inputProps={{
addNewAnswer(); sx: { fontSize: "18px", lineHeight: "21px", py: 0, ml: "13px" },
} }}
}} />
InputProps={{ {additionalMobile}
startAdornment: ( </FormControl>
<> </Box>
<InputAdornment
{...provided.dragHandleProps}
position="start"
>
<PointsIcon
style={{ color: "#9A9AAF", fontSize: "30px" }}
/>
</InputAdornment>
{additionalContent}
</>
),
endAdornment: (
<InputAdornment position="end">
<IconButton
sx={{ padding: "0" }}
aria-describedby="my-popover-id"
onClick={handleClick}
>
<MessageIcon
style={{
color: "#9A9AAF",
fontSize: "30px",
marginRight: "6.5px",
}}
/>
</IconButton>
<Popover
id="my-popover-id"
open={isOpen}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
>
<TextareaAutosize
style={{ margin: "10px" }}
placeholder="Подсказка для этого ответа"
value={variant.hints}
onChange={changeAnswerHint}
onKeyDown={(
event: KeyboardEvent<HTMLTextAreaElement>
) => event.stopPropagation()}
/>
</Popover>
<IconButton sx={{ padding: "0" }} onClick={deleteAnswer}>
<DeleteIcon
style={{
color: theme.palette.grey2.main,
marginRight: "-1px",
}}
/>
</IconButton>
</InputAdornment>
),
}}
sx={{
"& .MuiInputBase-root": {
padding: additionalContent ? "5px 13px" : "13px",
borderRadius: "10px",
background: "#ffffff",
"& input.MuiInputBase-input": {
height: "22px",
},
"& textarea.MuiInputBase-input": {
marginTop: "1px",
},
"& .MuiOutlinedInput-notchedOutline": {
border: "none",
},
},
}}
inputProps={{
sx: { fontSize: "18px", lineHeight: "21px", py: 0, ml: "13px" },
}}
/>
{additionalMobile}
</FormControl>
</Box>
)}
</Draggable>
); );
}; };

@ -47,6 +47,7 @@ export const AnswerDraggableList = ({
totalIndex={totalIndex} totalIndex={totalIndex}
variants={variants} variants={variants}
variant={variant} variant={variant}
provided={provided}
additionalContent={additionalContent?.(variant, index)} additionalContent={additionalContent?.(variant, index)}
additionalMobile={additionalMobile?.(variant, index)} additionalMobile={additionalMobile?.(variant, index)}
/> />

@ -225,7 +225,7 @@ export default function QuestionsPageCard({
sx={{ sx={{
margin: isMobile ? "10px 0" : 0, margin: isMobile ? "10px 0" : 0,
"& .MuiInputBase-root": { "& .MuiInputBase-root": {
color: question.expanded ? "#9A9AAF" : "#4D4D4D", color: question.expanded ? "#000000" : "#4D4D4D",
backgroundColor: question.expanded backgroundColor: question.expanded
? theme.palette.background.default ? theme.palette.background.default
: "transparent", : "transparent",

@ -29,7 +29,7 @@ export default function DropDown({ totalIndex }: Props) {
const addNewAnswer = () => { const addNewAnswer = () => {
const answerNew = question.content.variants.slice(); const answerNew = question.content.variants.slice();
answerNew.push({ answer: "", hints: "", extendedText: "" }); answerNew.push({ answer: "", extendedText: "" });
updateQuestionsList<QuizQuestionSelect>(quizId, totalIndex, { updateQuestionsList<QuizQuestionSelect>(quizId, totalIndex, {
content: { ...question.content, variants: answerNew }, content: { ...question.content, variants: answerNew },
@ -94,12 +94,22 @@ export default function DropDown({ totalIndex }: Props) {
> >
или нажмите Enter или нажмите Enter
</Typography> </Typography>
<EnterIcon style={{ color: "#7E2AEA", fontSize: "24px", marginLeft: "6px" }} /> <EnterIcon
style={{
color: "#7E2AEA",
fontSize: "24px",
marginLeft: "6px",
}}
/>
</> </>
)} )}
</Box> </Box>
</Box> </Box>
<ButtonsOptions switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} /> <ButtonsOptions
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchDropDown switchState={switchState} totalIndex={totalIndex} /> <SwitchDropDown switchState={switchState} totalIndex={totalIndex} />
</> </>
); );

@ -28,7 +28,9 @@ interface Props {
export default function Emoji({ totalIndex }: Props) { export default function Emoji({ totalIndex }: Props) {
const [switchState, setSwitchState] = useState<string>("setting"); const [switchState, setSwitchState] = useState<string>("setting");
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [anchorElement, setAnchorElement] = useState<HTMLDivElement | null>(null); const [anchorElement, setAnchorElement] = useState<HTMLDivElement | null>(
null
);
const [currentIndex, setCurrentIndex] = useState<number>(0); const [currentIndex, setCurrentIndex] = useState<number>(0);
const { listQuestions } = questionStore(); const { listQuestions } = questionStore();
const quizId = Number(useParams().quizId); const quizId = Number(useParams().quizId);
@ -37,7 +39,6 @@ export default function Emoji({ totalIndex }: Props) {
const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isMobile = useMediaQuery(theme.breakpoints.down(790));
const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const SSHC = (data: string) => { const SSHC = (data: string) => {
setSwitchState(data); setSwitchState(data);
}; };
@ -198,14 +199,21 @@ export default function Emoji({ totalIndex }: Props) {
}} }}
/> />
</Popover> </Popover>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px", marginBottom: isMobile ? "17px" : "20px" }}> <Box
sx={{
display: "flex",
alignItems: "center",
gap: "10px",
marginBottom: isMobile ? "17px" : "20px",
}}
>
<Link <Link
component="button" component="button"
variant="body2" variant="body2"
sx={{ color: theme.palette.brightPurple.main }} sx={{ color: theme.palette.brightPurple.main }}
onClick={() => { onClick={() => {
const answerNew = question.content.variants.slice(); const answerNew = question.content.variants.slice();
answerNew.push({ answer: "", hints: "", extendedText: "" }); answerNew.push({ answer: "", extendedText: "" });
updateQuestionsList<QuizQuestionEmoji>(quizId, totalIndex, { updateQuestionsList<QuizQuestionEmoji>(quizId, totalIndex, {
content: { ...question.content, variants: answerNew }, content: { ...question.content, variants: answerNew },
@ -237,7 +245,11 @@ export default function Emoji({ totalIndex }: Props) {
)} )}
</Box> </Box>
</Box> </Box>
<ButtonsOptions switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} /> <ButtonsOptions
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchEmoji switchState={switchState} totalIndex={totalIndex} /> <SwitchEmoji switchState={switchState} totalIndex={totalIndex} />
</> </>
); );

@ -27,7 +27,7 @@ export default memo(
{(provided) => ( {(provided) => (
<ListItem <ListItem
ref={provided.innerRef} ref={provided.innerRef}
{...provided.draggableProps} {...(index !== 0 ? provided.draggableProps : {})}
sx={{ userSelect: "none", padding: 0 }} sx={{ userSelect: "none", padding: 0 }}
> >
{questionData.deleted ? ( {questionData.deleted ? (

@ -147,11 +147,13 @@ export default function QuestionsPageCard({
), ),
endAdornment: ( endAdornment: (
<> <>
<InputAdornment {...draggableProps} position="start"> {totalIndex !== 0 && (
<PointsIcon <InputAdornment {...draggableProps} position="start">
style={{ color: "#9A9AAF", fontSize: "30px" }} <PointsIcon
/> style={{ color: "#9A9AAF", fontSize: "30px" }}
</InputAdornment> />
</InputAdornment>
)}
</> </>
), ),
}} }}

@ -15,6 +15,10 @@ export const FormDraggableList = () => {
const { listQuestions } = questionStore(); const { listQuestions } = questionStore();
const onDragEnd = ({ destination, source }: DropResult) => { const onDragEnd = ({ destination, source }: DropResult) => {
if (destination?.index === 0) {
return;
}
if (destination) { if (destination) {
const newItems = reorder( const newItems = reorder(
listQuestions[quizId], listQuestions[quizId],

@ -93,13 +93,6 @@ export default function FormQuestionsPage() {
}} }}
onClick={() => { onClick={() => {
createQuestion(quizId); createQuestion(quizId);
updateQuestionsList<QuizQuestionBase>(
quizId,
listQuestions[quizId].length - 1 || 0,
{
expanded: true,
}
);
}} }}
> >
<AddAnswer color="#EEE4FC" /> <AddAnswer color="#EEE4FC" />

@ -5,18 +5,14 @@ import { Box } from "@mui/material";
import QuestionsMiniButton from "@ui_kit/QuestionsMiniButton"; import QuestionsMiniButton from "@ui_kit/QuestionsMiniButton";
import ButtonsOptions from "../ButtonsOptions"; import ButtonsOptions from "../ButtonsOptions";
import SwitchAnswerOptions from "../answerOptions/switchAnswerOptions"; import SwitchAnswerOptions from "../answerOptions/switchAnswerOptions";
import { BUTTON_TYPE_QUESTIONS } from "../TypeQuestions";
import Answer from "../../../assets/icons/questionsPage/answer"; 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 Emoji from "../../../assets/icons/questionsPage/emoji";
import Input from "../../../assets/icons/questionsPage/input"; import Input from "../../../assets/icons/questionsPage/input";
import DropDown from "../../../assets/icons/questionsPage/drop_down"; import DropDown from "../../../assets/icons/questionsPage/drop_down";
import Date from "../../../assets/icons/questionsPage/date"; import Date from "../../../assets/icons/questionsPage/date";
import Slider from "../../../assets/icons/questionsPage/slider"; import Slider from "../../../assets/icons/questionsPage/slider";
import Download from "../../../assets/icons/questionsPage/download"; import Download from "../../../assets/icons/questionsPage/download";
import Page from "../../../assets/icons/questionsPage/page";
import RatingIcon from "../../../assets/icons/questionsPage/rating";
import { import {
questionStore, questionStore,
@ -40,27 +36,12 @@ type ButtonTypeQuestion = {
value: QuizQuestionType; value: QuizQuestionType;
}; };
export const BUTTON_TYPE_QUESTIONS: ButtonTypeQuestion[] = [ const BUTTON_TYPE_SHORT_QUESTIONS: ButtonTypeQuestion[] = [
{ {
icon: <Answer color="#9A9AAF" />, icon: <Answer color="#9A9AAF" />,
title: "Варианты ответов", title: "Варианты ответов",
value: "variant", value: "variant",
}, },
{
icon: <OptionsPict color="#9A9AAF" />,
title: "Варианты с картинками",
value: "images",
},
{
icon: <OptionsAndPict color="#9A9AAF" />,
title: "Варианты и картинка",
value: "varimg",
},
{
icon: <Emoji color="#9A9AAF" />,
title: "Эмоджи",
value: "emoji",
},
{ {
icon: <Input color="#9A9AAF" />, icon: <Input color="#9A9AAF" />,
title: "Своё поле для ввода", title: "Своё поле для ввода",
@ -86,16 +67,6 @@ export const BUTTON_TYPE_QUESTIONS: ButtonTypeQuestion[] = [
title: "Загрузка файла", title: "Загрузка файла",
value: "file", value: "file",
}, },
{
icon: <Page color="#9A9AAF" />,
title: "Страница",
value: "page",
},
{
icon: <RatingIcon color="#9A9AAF" />,
title: "Рейтинг",
value: "rating",
},
]; ];
export default function FormTypeQuestions({ totalIndex }: Props) { export default function FormTypeQuestions({ totalIndex }: Props) {
@ -114,7 +85,10 @@ export default function FormTypeQuestions({ totalIndex }: Props) {
margin: "20px", margin: "20px",
}} }}
> >
{BUTTON_TYPE_QUESTIONS.map(({ icon, title, value }) => ( {(totalIndex === 0
? BUTTON_TYPE_QUESTIONS
: BUTTON_TYPE_SHORT_QUESTIONS
).map(({ icon, title, value }) => (
<QuestionsMiniButton <QuestionsMiniButton
key={title} key={title}
onClick={() => { onClick={() => {

@ -1,16 +1,16 @@
import { useState } from "react"; import { useState } from "react";
import { import {
Box, Box,
Link, Link,
Typography, Typography,
useTheme, useTheme,
useMediaQuery, useMediaQuery,
InputAdornment, InputAdornment,
IconButton, IconButton,
Button, Button,
Popover, Popover,
TextareaAutosize, TextareaAutosize,
TextField, TextField,
} from "@mui/material"; } from "@mui/material";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@ -35,423 +35,247 @@ import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
import { produce } from "immer"; import { produce } from "immer";
interface Props { interface Props {
totalIndex: number; totalIndex: number;
} }
export default function OptionsAndPicture({ totalIndex }: Props) { export default function OptionsAndPicture({ totalIndex }: Props) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [opened, setOpened] = useState<boolean>(false); const [opened, setOpened] = useState<boolean>(false);
const [switchState, setSwitchState] = useState("setting"); const [switchState, setSwitchState] = useState("setting");
const [currentIndex, setCurrentIndex] = useState<number>(0); const [currentIndex, setCurrentIndex] = useState<number>(0);
const quizId = Number(useParams().quizId); const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore(); const { listQuestions } = questionStore();
const theme = useTheme(); const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.down(1000)); const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isMobile = useMediaQuery(theme.breakpoints.down(790));
const question = listQuestions[quizId][totalIndex] as QuizQuestionVarImg; const question = listQuestions[quizId][totalIndex] as QuizQuestionVarImg;
const SSHC = (data: string) => { const SSHC = (data: string) => {
setSwitchState(data); setSwitchState(data);
}; };
const uploadImage = (files: FileList | null) => { const uploadImage = (files: FileList | null) => {
if (files?.length) { if (files?.length) {
const [file] = Array.from(files); const [file] = Array.from(files);
const clonedContent = { ...question.content }; const clonedContent = { ...question.content };
clonedContent.variants[currentIndex].extendedText = clonedContent.variants[currentIndex].extendedText =
URL.createObjectURL(file); URL.createObjectURL(file);
updateQuestionsList<QuizQuestionVarImg>(quizId, totalIndex, { updateQuestionsList<QuizQuestionVarImg>(quizId, totalIndex, {
content: clonedContent, content: clonedContent,
}); });
setOpen(false); setOpen(false);
setOpened(true); setOpened(true);
} }
}; };
return ( return (
<> <>
<Box sx={{ pl: "20px", pr: "20px" }}> <Box sx={{ pl: "20px", pr: "20px" }}>
<AnswerDraggableList <AnswerDraggableList
variants={question.content.variants} variants={question.content.variants}
totalIndex={totalIndex} totalIndex={totalIndex}
additionalContent={(variant, index) => ( additionalContent={(variant, index) => (
<> <>
{!isMobile && ( {!isMobile && (
<Box
sx={{ cursor: "pointer" }}
onClick={() => {
setCurrentIndex(index);
setOpen(true);
}}
>
{variant.extendedText ? (
<Box
sx={{
overflow: "hidden",
width: "60px",
display: "flex",
alignItems: "center",
background: "#EEE4FC",
borderRadius: "3px",
margin: "0 10px",
height: "40px",
}}
>
<Box sx={{ display: "flex", width: "40px" }}>
<img
src={variant.extendedText}
alt=""
style={{ width: "100%" }}
/>
</Box>
<PlusImage />
</Box>
) : (
<Button component="label" sx={{ padding: "0px" }}>
<AddImage
sx={{
height: "40px",
width: "60px",
margin: "0 10px",
}}
/>
</Button>
)}
</Box>
)}
</>
)}
additionalMobile={(variant, index) => (
<>
{isMobile && (
<Box
onClick={() => {
setCurrentIndex(index);
setOpen(true);
}}
sx={{
overflow: "hidden",
display: "flex",
alignItems: "center",
m: "8px",
position: "relative",
borderRadius: "3px",
}}
>
<Box
sx={{
width: "100%",
background: "#EEE4FC",
height: "40px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{variant.extendedText ? (
<Box
sx={{
overflow: "hidden",
width: "40px",
display: "flex",
alignItems: "center",
background: "#EEE4FC",
height: "30px",
borderRadius: "3px",
}}
>
<img
src={variant.extendedText}
alt=""
style={{ width: "100%" }}
/>
</Box>
) : (
<Button component="label" sx={{ padding: "0px" }}>
<Image
sx={{
height: "40px",
width: "60px",
margin: "0 10px",
}}
/>
</Button>
)}
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "20px",
background: "#EEE4FC",
height: "40px",
color: "white",
backgroundColor: "#7E2AEA",
}}
>
+
</Box>
</Box>
)}
</>
)}
/>
<UploadImageModal
open={open}
onClose={() => setOpen(false)}
imgHC={uploadImage}
/>
<CropModal
opened={opened}
onClose={() => setOpened(false)}
picture={question.content.variants[currentIndex]?.extendedText}
onCropPress={url => {
const content = produce(question.content, draft => {
draft.variants[currentIndex].extendedText = url;
});
updateQuestionsList<QuizQuestionVarImg>(quizId, totalIndex, {
content,
});
}}
/>
<Box <Box
sx={{ sx={{ cursor: "pointer" }}
width: "100%", onClick={() => {
border: "1px solid #9A9AAF", setCurrentIndex(index);
borderRadius: "8px", setOpen(true);
display: isTablet ? "block" : "none", }}
}}
> >
<TextField {variant.extendedText ? (
fullWidth <Box
focused={false} sx={{
placeholder={"Добавьте ответ"} overflow: "hidden",
multiline={question.content.largeCheck} width: "60px",
InputProps={{
startAdornment: (
<>
<InputAdornment position="start">
<PointsIcon
style={{ color: "#9A9AAF", fontSize: "30px" }}
/>
</InputAdornment>
{!isMobile && (
<Box
sx={{
width: "60px",
height: "40px",
background: "#EEE4FC",
display: "flex",
justifyContent: "space-between",
marginRight: "20px",
marginLeft: "12px",
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
}}
>
<ImageAddIcons fontSize="22px" color="#7E2AEA" />
</Box>
<span
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
background: "#7E2AEA",
height: "100%",
width: "25px",
color: "white",
fontSize: "15px",
}}
>
+
</span>
</Box>
)}
</>
),
endAdornment: (
<InputAdornment position="end">
<IconButton
sx={{ padding: "0" }}
aria-describedby="my-popover-id"
>
<MessageIcon
style={{
color: "#9A9AAF",
fontSize: "30px",
marginRight: "6.5px",
}}
/>
</IconButton>
<Popover
id="my-popover-id"
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
open={false}
>
<TextareaAutosize
style={{ margin: "10px" }}
placeholder="Подсказка для этого ответа"
/>
</Popover>
<IconButton sx={{ padding: "0" }}>
<DeleteIcon
style={{
color: theme.palette.grey2.main,
marginRight: "-1px",
}}
/>
</IconButton>
</InputAdornment>
),
}}
sx={{
"& .MuiInputBase-root": {
padding: "13.5px",
borderRadius: "10px",
background: "#ffffff",
height: "48px",
},
"& .MuiOutlinedInput-notchedOutline": {
border: "none",
},
}}
inputProps={{
sx: { fontSize: "18px", lineHeight: "21px", py: 0 },
}}
/>
{isMobile && (
<Box
sx={{
display: "flex",
alignItems: "center",
m: "8px",
position: "relative",
}}
>
<Box
sx={{ width: "100%", background: "#EEE4FC", height: "40px" }}
/>
<ImageAddIcons
style={{
position: "absolute",
color: "#7E2AEA",
fontSize: "20px",
left: "45%",
right: "55%",
}}
/>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "20px",
background: "#EEE4FC",
height: "40px",
color: "white",
backgroundColor: "#7E2AEA",
}}
>
<Box
sx={{ width: "100%", background: "#EEE4FC", height: "40px" }}
/>
<ImageAddIcons
style={{
position: "absolute",
color: "#7E2AEA",
fontSize: "20px",
left: "45%",
right: "55%",
}}
/>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "20px",
background: "#EEE4FC",
height: "40px",
color: "white",
backgroundColor: "#7E2AEA",
}}
>
+
</Box>
</Box>
</Box>
)}
</Box>
<Box
sx={{
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
marginBottom: "17px", background: "#EEE4FC",
}} borderRadius: "3px",
> margin: "0 10px",
<Link height: "40px",
component="button" }}
variant="body2"
sx={{
color: theme.palette.brightPurple.main,
fontWeight: "400",
fontSize: "16px",
mr: "4px",
height: "19px",
}}
onClick={() => {
const clonedContent = { ...question.content };
clonedContent.variants.push({
answer: "",
hints: "",
extendedText: "",
});
updateQuestionsList<QuizQuestionVarImg>(quizId, totalIndex, {
content: clonedContent,
});
}}
> >
Добавьте ответ <Box sx={{ display: "flex", width: "40px" }}>
</Link> <img
{isMobile ? null : ( src={variant.extendedText}
<> alt=""
<Typography style={{ width: "100%" }}
sx={{ />
fontWeight: 400, </Box>
lineHeight: "21.33px", <PlusImage />
color: theme.palette.grey2.main, </Box>
fontSize: "16px", ) : (
}} <Button component="label" sx={{ padding: "0px" }}>
> <AddImage
или нажмите Enter sx={{
</Typography> height: "40px",
<EnterIcon width: "60px",
style={{ margin: "0 10px",
color: "#7E2AEA", }}
fontSize: "24px", />
marginLeft: "6px", </Button>
}} )}
/>
</>
)}
</Box> </Box>
</Box> )}
<ButtonsOptionsAndPict </>
switchState={switchState} )}
SSHC={SSHC} additionalMobile={(variant, index) => (
totalIndex={totalIndex} <>
/> {isMobile && (
<SwitchOptionsAndPict switchState={switchState} totalIndex={totalIndex} /> <Box
</> onClick={() => {
); setCurrentIndex(index);
setOpen(true);
}}
sx={{
overflow: "hidden",
display: "flex",
alignItems: "center",
m: "8px",
position: "relative",
borderRadius: "3px",
}}
>
<Box
sx={{
width: "100%",
background: "#EEE4FC",
height: "40px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{variant.extendedText ? (
<Box
sx={{
overflow: "hidden",
width: "40px",
display: "flex",
alignItems: "center",
background: "#EEE4FC",
height: "30px",
borderRadius: "3px",
}}
>
<img
src={variant.extendedText}
alt=""
style={{ width: "100%" }}
/>
</Box>
) : (
<Button component="label" sx={{ padding: "0px" }}>
<Image
sx={{
height: "40px",
width: "60px",
margin: "0 10px",
}}
/>
</Button>
)}
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "20px",
background: "#EEE4FC",
height: "40px",
color: "white",
backgroundColor: "#7E2AEA",
}}
>
+
</Box>
</Box>
)}
</>
)}
/>
<UploadImageModal
open={open}
onClose={() => setOpen(false)}
imgHC={uploadImage}
/>
<CropModal
opened={opened}
onClose={() => setOpened(false)}
picture={question.content.variants[currentIndex]?.extendedText}
onCropPress={(url) => {
const content = produce(question.content, (draft) => {
draft.variants[currentIndex].extendedText = url;
});
updateQuestionsList<QuizQuestionVarImg>(quizId, totalIndex, {
content,
});
}}
/>
<Box
sx={{
display: "flex",
alignItems: "center",
marginBottom: "17px",
}}
>
<Link
component="button"
variant="body2"
sx={{
color: theme.palette.brightPurple.main,
fontWeight: "400",
fontSize: "16px",
mr: "4px",
height: "19px",
}}
onClick={() => {
const clonedContent = { ...question.content };
clonedContent.variants.push({
answer: "",
extendedText: "",
});
updateQuestionsList<QuizQuestionVarImg>(quizId, totalIndex, {
content: clonedContent,
});
}}
>
Добавьте ответ
</Link>
{isMobile ? null : (
<>
<Typography
sx={{
fontWeight: 400,
lineHeight: "21.33px",
color: theme.palette.grey2.main,
fontSize: "16px",
}}
>
или нажмите Enter
</Typography>
<EnterIcon
style={{
color: "#7E2AEA",
fontSize: "24px",
marginLeft: "6px",
}}
/>
</>
)}
</Box>
</Box>
<ButtonsOptionsAndPict
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchOptionsAndPict switchState={switchState} totalIndex={totalIndex} />
</>
);
} }

@ -1,12 +1,12 @@
import { useState } from "react"; import { useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { import {
Box, Box,
Link, Link,
Typography, Typography,
Button, Button,
useTheme, useTheme,
useMediaQuery useMediaQuery,
} from "@mui/material"; } from "@mui/material";
import ButtonsOptions from "../ButtonsOptions"; import ButtonsOptions from "../ButtonsOptions";
@ -25,227 +25,234 @@ import type { QuizQuestionImages } from "../../../model/questionTypes/images";
import { produce } from "immer"; import { produce } from "immer";
interface Props { interface Props {
totalIndex: number; totalIndex: number;
} }
export default function OptionsPicture({ totalIndex }: Props) { export default function OptionsPicture({ totalIndex }: Props) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [opened, setOpened] = useState<boolean>(false); const [opened, setOpened] = useState<boolean>(false);
const [currentIndex, setCurrentIndex] = useState<number>(0); const [currentIndex, setCurrentIndex] = useState<number>(0);
const theme = useTheme(); const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790)); const isMobile = useMediaQuery(theme.breakpoints.down(790));
const isTablet = useMediaQuery(theme.breakpoints.down(790)); const isTablet = useMediaQuery(theme.breakpoints.down(790));
const quizId = Number(useParams().quizId); const quizId = Number(useParams().quizId);
const [switchState, setSwitchState] = useState("setting"); const [switchState, setSwitchState] = useState("setting");
const { listQuestions } = questionStore(); const { listQuestions } = questionStore();
const question = listQuestions[quizId][totalIndex] as QuizQuestionImages; const question = listQuestions[quizId][totalIndex] as QuizQuestionImages;
const SSHC = (data: string) => { const SSHC = (data: string) => {
setSwitchState(data); setSwitchState(data);
}; };
const uploadImage = (files: FileList | null) => { const uploadImage = (files: FileList | null) => {
if (files?.length) { if (files?.length) {
const [file] = Array.from(files); const [file] = Array.from(files);
const clonedContent = { ...question.content }; const clonedContent = { ...question.content };
clonedContent.variants[currentIndex].extendedText = clonedContent.variants[currentIndex].extendedText =
URL.createObjectURL(file); URL.createObjectURL(file);
updateQuestionsList<QuizQuestionImages>(quizId, totalIndex, { updateQuestionsList<QuizQuestionImages>(quizId, totalIndex, {
content: clonedContent, content: clonedContent,
}); });
setOpen(false); setOpen(false);
setOpened(true); setOpened(true);
} }
}; };
const addNewAnswer = () => { const addNewAnswer = () => {
const answerNew = question.content.variants.slice(); const answerNew = question.content.variants.slice();
answerNew.push({ answer: "", hints: "", extendedText: "" }); answerNew.push({ answer: "", extendedText: "" });
updateQuestionsList<QuizQuestionImages>(quizId, totalIndex, { updateQuestionsList<QuizQuestionImages>(quizId, totalIndex, {
content: { ...question.content, variants: answerNew }, content: { ...question.content, variants: answerNew },
}); });
}; };
return ( return (
<> <>
<Box sx={{ padding: "20px" }}> <Box sx={{ padding: "20px" }}>
<AnswerDraggableList <AnswerDraggableList
variants={question.content.variants} variants={question.content.variants}
totalIndex={totalIndex} totalIndex={totalIndex}
additionalContent={(variant, index) => ( additionalContent={(variant, index) => (
<> <>
{!isMobile && ( {!isMobile && (
<Box <Box
sx={{ cursor: "pointer" }} sx={{ cursor: "pointer" }}
onClick={() => { onClick={() => {
setCurrentIndex(index); setCurrentIndex(index);
setOpen(true); setOpen(true);
}} }}
> >
{variant.extendedText ? ( {variant.extendedText ? (
<Box <Box
sx={{ sx={{
overflow: "hidden", overflow: "hidden",
width: "60px", width: "60px",
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
background: "#EEE4FC", background: "#EEE4FC",
borderRadius: "3px", borderRadius: "3px",
margin: "0 10px", margin: "0 10px",
height: "40px", height: "40px",
}} }}
>
<Box sx={{ display: "flex", width: "40px" }}>
<img
src={variant.extendedText}
alt=""
style={{ width: "100%" }}
/>
</Box>
<PlusImage />
</Box>
) : (
<Button component="label" sx={{ padding: "0px" }}>
<AddImage
sx={{ height: "40px", width: "60px", margin: "0 10px" }}
/>
</Button>
)}
</Box>
)}
</>
)}
additionalMobile={(variant, index) => (
<>
{isMobile && (
<Box
onClick={() => {
setCurrentIndex(index);
setOpen(true);
}}
sx={{
overflow: "hidden",
display: "flex",
alignItems: "center",
m: "8px",
position: "relative",
borderRadius: "3px",
}}
>
<Box
sx={{
width: "100%",
background: "#EEE4FC",
height: "40px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{variant.extendedText ? (
<Box
sx={{
overflow: "hidden",
width: "40px",
display: "flex",
alignItems: "center",
background: "#EEE4FC",
height: "30px",
borderRadius: "3px",
}}
>
<img
src={variant.extendedText}
alt=""
style={{ width: "100%" }}
/>
</Box>
) : (
<Button component="label" sx={{ padding: "0px" }}>
<Image
sx={{
height: "40px",
width: "60px",
margin: "0 10px",
}}
/>
</Button>
)}
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "20px",
background: "#EEE4FC",
height: "40px",
color: "white",
backgroundColor: "#7E2AEA",
}}
>
+
</Box>
</Box>
)}
</>
)}
/>
<UploadImageModal
open={open}
onClose={() => setOpen(false)}
imgHC={uploadImage}
/>
<CropModal
opened={opened}
onClose={() => setOpened(false)}
picture={question.content.variants[currentIndex]?.extendedText}
onCropPress={url => {
const content = produce(question.content, draft => {
draft.variants[currentIndex].extendedText = url;
});
updateQuestionsList<QuizQuestionImages>(quizId, totalIndex, {
content,
});
}}
/>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
<Link
component="button"
variant="body2"
sx={{ color: theme.palette.brightPurple.main }}
onClick={addNewAnswer}
> >
Добавьте ответ <Box sx={{ display: "flex", width: "40px" }}>
</Link> <img
{isMobile ? null : ( src={variant.extendedText}
<> alt=""
<Typography style={{ width: "100%" }}
sx={{ />
fontWeight: 400, </Box>
lineHeight: "21.33px", <PlusImage />
color: theme.palette.grey2.main, </Box>
fontSize: "16px", ) : (
}} <Button component="label" sx={{ padding: "0px" }}>
> <AddImage
или нажмите Enter sx={{ height: "40px", width: "60px", margin: "0 10px" }}
</Typography> />
<EnterIcon </Button>
style={{ )}
color: "#7E2AEA",
fontSize: "24px",
marginLeft: "6px",
}}
/>
</>
)}
</Box> </Box>
</Box> )}
<ButtonsOptions switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} /> </>
<SwitchAnswerOptionsPict switchState={switchState} totalIndex={totalIndex} /> )}
</> additionalMobile={(variant, index) => (
); <>
{isMobile && (
<Box
onClick={() => {
setCurrentIndex(index);
setOpen(true);
}}
sx={{
overflow: "hidden",
display: "flex",
alignItems: "center",
m: "8px",
position: "relative",
borderRadius: "3px",
}}
>
<Box
sx={{
width: "100%",
background: "#EEE4FC",
height: "40px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{variant.extendedText ? (
<Box
sx={{
overflow: "hidden",
width: "40px",
display: "flex",
alignItems: "center",
background: "#EEE4FC",
height: "30px",
borderRadius: "3px",
}}
>
<img
src={variant.extendedText}
alt=""
style={{ width: "100%" }}
/>
</Box>
) : (
<Button component="label" sx={{ padding: "0px" }}>
<Image
sx={{
height: "40px",
width: "60px",
margin: "0 10px",
}}
/>
</Button>
)}
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "20px",
background: "#EEE4FC",
height: "40px",
color: "white",
backgroundColor: "#7E2AEA",
}}
>
+
</Box>
</Box>
)}
</>
)}
/>
<UploadImageModal
open={open}
onClose={() => setOpen(false)}
imgHC={uploadImage}
/>
<CropModal
opened={opened}
onClose={() => setOpened(false)}
picture={question.content.variants[currentIndex]?.extendedText}
onCropPress={(url) => {
const content = produce(question.content, (draft) => {
draft.variants[currentIndex].extendedText = url;
});
updateQuestionsList<QuizQuestionImages>(quizId, totalIndex, {
content,
});
}}
/>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
<Link
component="button"
variant="body2"
sx={{ color: theme.palette.brightPurple.main }}
onClick={addNewAnswer}
>
Добавьте ответ
</Link>
{isMobile ? null : (
<>
<Typography
sx={{
fontWeight: 400,
lineHeight: "21.33px",
color: theme.palette.grey2.main,
fontSize: "16px",
}}
>
или нажмите Enter
</Typography>
<EnterIcon
style={{
color: "#7E2AEA",
fontSize: "24px",
marginLeft: "6px",
}}
/>
</>
)}
</Box>
</Box>
<ButtonsOptions
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchAnswerOptionsPict
switchState={switchState}
totalIndex={totalIndex}
/>
</>
);
} }

@ -1,19 +1,19 @@
import { import {
Box, Box,
Button, Button,
IconButton, IconButton,
Typography, Typography,
useMediaQuery, useMediaQuery,
useTheme, useTheme,
} from "@mui/material"; } from "@mui/material";
import AddPlus from "../../assets/icons/questionsPage/addPlus"; import AddPlus from "../../assets/icons/questionsPage/addPlus";
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft"; import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
import { quizStore } from "@root/quizes"; import { quizStore } from "@root/quizes";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { import {
questionStore, questionStore,
createQuestion, createQuestion,
updateQuestionsList, updateQuestionsList,
} from "@root/questions"; } from "@root/questions";
import { DraggableList } from "./DraggableList"; import { DraggableList } from "./DraggableList";
@ -22,17 +22,17 @@ import QuizPreview from "@ui_kit/QuizPreview/QuizPreview";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
export default function QuestionsPage() { export default function QuestionsPage() {
const { listQuizes, updateQuizesList } = quizStore(); const { listQuizes, updateQuizesList } = quizStore();
const quizId = Number(useParams().quizId); const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore(); const { listQuestions } = questionStore();
const handleNext = () => { const handleNext = () => {
updateQuizesList(quizId, { step: listQuizes[quizId].step + 1 }); updateQuizesList(quizId, { step: listQuizes[quizId].step + 1 });
}; };
const handleBack = () => { const handleBack = () => {
let result = listQuizes[quizId].step - 1; let result = listQuizes[quizId].step - 1;
updateQuizesList(quizId, { step: result ? result : 1 }); updateQuizesList(quizId, { step: result ? result : 1 });
}; };
const collapseEverything = () => { const collapseEverything = () => {
listQuestions[quizId].forEach((item, index) => { listQuestions[quizId].forEach((item, index) => {
@ -43,12 +43,11 @@ export default function QuestionsPage() {
}); });
}; };
const theme = useTheme(); const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.up(1000)); const isMobile = useMediaQuery(theme.breakpoints.down(660));
return ( return (
<> <>
{/* <Stepper activeStep={activeStep} desc={"Задайте вопросы"} /> */}
<Box <Box
sx={{ sx={{
maxWidth: "796px", maxWidth: "796px",
@ -85,10 +84,15 @@ export default function QuestionsPage() {
onClick={() => { onClick={() => {
createQuestion(quizId); createQuestion(quizId);
}} }}
sx={{
position: "fixed",
left: isMobile ? "20px" : "250px",
bottom: "20px",
}}
> >
<AddPlus /> <AddPlus />
</IconButton> </IconButton>
<Box sx={{ display: "flex", gap: "8px" }}> <Box sx={{ display: "flex", gap: "8px", marginLeft: "auto" }}>
<Button <Button
variant="outlined" variant="outlined"
sx={{ padding: "10px 20px", borderRadius: "8px", height: "44px" }} sx={{ padding: "10px 20px", borderRadius: "8px", height: "44px" }}

@ -27,7 +27,7 @@ export default function AnswerOptions({ totalIndex }: Props) {
const addNewAnswer = () => { const addNewAnswer = () => {
const answerNew = question.content.variants.slice(); const answerNew = question.content.variants.slice();
answerNew.push({ answer: "", hints: "", extendedText: "" }); answerNew.push({ answer: "", extendedText: "" });
updateQuestionsList<QuizQuestionVariant>(quizId, totalIndex, { updateQuestionsList<QuizQuestionVariant>(quizId, totalIndex, {
content: { ...question.content, variants: answerNew }, content: { ...question.content, variants: answerNew },
@ -89,12 +89,22 @@ export default function AnswerOptions({ totalIndex }: Props) {
> >
или нажмите Enter или нажмите Enter
</Typography> </Typography>
<EnterIcon style={{ color: "#7E2AEA", fontSize: "24px", marginLeft: "6px" }} /> <EnterIcon
style={{
color: "#7E2AEA",
fontSize: "24px",
marginLeft: "6px",
}}
/>
</> </>
)} )}
</Box> </Box>
</Box> </Box>
<ButtonsOptionsAndPict switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} /> <ButtonsOptionsAndPict
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchAnswerOptions switchState={switchState} totalIndex={totalIndex} /> <SwitchAnswerOptions switchState={switchState} totalIndex={totalIndex} />
</> </>
); );

@ -126,7 +126,7 @@ export const quizStore = create<QuizStore>()(
"logo": "hub.pena.digital/img/logo", "logo": "hub.pena.digital/img/logo",
"startpage": { "startpage": {
"description": "",// приветственный текст опроса "description": "",// приветственный текст опроса
"button": "Начать", // текст на кнопке начала опроса "button": "", // текст на кнопке начала опроса
"position": "ltr", // ltr или rtl. отображение элементов поверх фона "position": "ltr", // ltr или rtl. отображение элементов поверх фона
"background": { "background": {
"type": "image", //image или video "type": "image", //image или video
@ -137,11 +137,11 @@ export const quizStore = create<QuizStore>()(
}, },
}, },
"info": { "info": {
"phonenumber": "+79885895677", "phonenumber": "",
"clickable": true, "clickable": true,
"orgname": "ООО \"Пена\"", "orgname": "",
"site": "hub.pena.digital", "site": "",
"law": "юридическая информация" "law": ""
}, },
"meta": "что-то" "meta": "что-то"
} }

@ -1,130 +1,132 @@
import { PointsIcon } from "@icons/questionsPage/PointsIcon";
import { Box, IconButton } from "@mui/material"; import { Box, IconButton } from "@mui/material";
import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview"; import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview";
import { useLayoutEffect, useRef } from "react"; import { useLayoutEffect, useRef } from "react";
import { Rnd } from "react-rnd"; import { Rnd } from "react-rnd";
import QuizPreviewLayout from "./QuizPreviewLayout"; import QuizPreviewLayout from "./QuizPreviewLayout";
import ResizeIcon from "./ResizeIcon"; import ResizeIcon from "./ResizeIcon";
import VisibilityIcon from '@mui/icons-material/Visibility'; import VisibilityIcon from "@mui/icons-material/Visibility";
const DRAG_PARENT_MARGIN = 0;
const DRAG_PARENT_MARGIN = 25; const NAVBAR_HEIGHT = 0;
const NAVBAR_HEIGHT = 81; const DRAG_PARENT_BOTTOM_MARGIN = 0;
const DRAG_PARENT_BOTTOM_MARGIN = 65;
interface RndPositionAndSize { interface RndPositionAndSize {
x: number; x: number;
y: number; y: number;
width: string; width: string;
height: string; height: string;
} }
export default function QuizPreview() { export default function QuizPreview() {
const isPreviewShown = useQuizPreviewStore(state => state.isPreviewShown); const isPreviewShown = useQuizPreviewStore((state) => state.isPreviewShown);
const rndParentRef = useRef<HTMLDivElement>(null); const rndParentRef = useRef<HTMLDivElement>(null);
const rndRef = useRef<Rnd | null>(null); const rndRef = useRef<Rnd | null>(null);
const rndPositionAndSizeRef = useRef<RndPositionAndSize>({ x: 0, y: 0, width: "340", height: "480" }); const rndPositionAndSizeRef = useRef<RndPositionAndSize>({
const isFirstShowRef = useRef<boolean>(true); x: 0,
y: 0,
width: "340",
height: "480",
});
const isFirstShowRef = useRef<boolean>(true);
useLayoutEffect(function stickPreviewToBottomRight() { useLayoutEffect(
const rnd = rndRef.current; function stickPreviewToBottomRight() {
const rndSelfElement = rnd?.getSelfElement(); const rnd = rndRef.current;
if (!rnd || !rndSelfElement || !rndParentRef.current || !isFirstShowRef.current) return; const rndSelfElement = rnd?.getSelfElement();
if (
!rnd ||
!rndSelfElement ||
!rndParentRef.current ||
!isFirstShowRef.current
)
return;
const rndParentRect = rndParentRef.current.getBoundingClientRect(); const rndParentRect = rndParentRef.current.getBoundingClientRect();
const rndRect = rndSelfElement.getBoundingClientRect(); const rndRect = rndSelfElement.getBoundingClientRect();
const x = rndParentRect.width - rndRect.width; const x = rndParentRect.width - rndRect.width;
const y = rndParentRect.height - rndRect.height; const y = rndParentRect.height - rndRect.height;
rnd.updatePosition({ x, y }); rnd.updatePosition({ x, y });
rndPositionAndSizeRef.current.x = x; rndPositionAndSizeRef.current.x = x;
rndPositionAndSizeRef.current.y = y; rndPositionAndSizeRef.current.y = y;
isFirstShowRef.current = false; isFirstShowRef.current = false;
}, [isPreviewShown]); },
[isPreviewShown]
);
return ( return (
<Box <Box
ref={rndParentRef} ref={rndParentRef}
sx={{ sx={{
position: "fixed", position: "fixed",
top: NAVBAR_HEIGHT + DRAG_PARENT_MARGIN, top: NAVBAR_HEIGHT + DRAG_PARENT_MARGIN,
left: DRAG_PARENT_MARGIN, left: DRAG_PARENT_MARGIN,
bottom: DRAG_PARENT_BOTTOM_MARGIN, bottom: DRAG_PARENT_BOTTOM_MARGIN,
right: DRAG_PARENT_MARGIN, right: DRAG_PARENT_MARGIN,
// backgroundColor: "rgba(0, 100, 0, 0.2)", // backgroundColor: "rgba(0, 100, 0, 0.2)",
pointerEvents: "none", pointerEvents: "none",
zIndex: 100, zIndex: 100,
}} }}
>
{isPreviewShown && (
<Rnd
minHeight={20}
minWidth={20}
bounds="parent"
ref={rndRef}
dragHandleClassName="quiz-preview-draghandle"
default={{
x: rndPositionAndSizeRef.current.x,
y: rndPositionAndSizeRef.current.y,
width: rndPositionAndSizeRef.current.width,
height: rndPositionAndSizeRef.current.height,
}}
onResizeStop={(e, direction, ref, delta, position) => {
rndPositionAndSizeRef.current.x = position.x;
rndPositionAndSizeRef.current.y = position.y;
rndPositionAndSizeRef.current.width = ref.style.width;
rndPositionAndSizeRef.current.height = ref.style.height;
}}
onDragStop={(e, d) => {
rndPositionAndSizeRef.current.x = d.x;
rndPositionAndSizeRef.current.y = d.y;
}}
onDragStart={(e, d) => {
e.preventDefault();
}}
enableResizing={{
topLeft: isPreviewShown,
}}
resizeHandleComponent={{
topLeft: <ResizeIcon />,
}}
resizeHandleStyles={{
topLeft: {
top: "-1px",
left: "-1px",
},
}}
style={{
overflow: "hidden",
pointerEvents: "auto",
}}
> >
{isPreviewShown && <QuizPreviewLayout />
<Rnd </Rnd>
minHeight={300} )}
minWidth={340} <IconButton
bounds="parent" onClick={toggleQuizPreview}
ref={rndRef} sx={{
dragHandleClassName="quiz-preview-draghandle" position: "absolute",
default={{ right: 0,
x: rndPositionAndSizeRef.current.x, bottom: -54,
y: rndPositionAndSizeRef.current.y, pointerEvents: "auto",
width: rndPositionAndSizeRef.current.width, }}
height: rndPositionAndSizeRef.current.height >
}} <VisibilityIcon sx={{ height: "30px", width: "30px" }} />
onResizeStop={(e, direction, ref, delta, position) => { </IconButton>
rndPositionAndSizeRef.current.x = position.x; </Box>
rndPositionAndSizeRef.current.y = position.y; );
rndPositionAndSizeRef.current.width = ref.style.width;
rndPositionAndSizeRef.current.height = ref.style.height;
}}
onDragStop={(e, d) => {
rndPositionAndSizeRef.current.x = d.x;
rndPositionAndSizeRef.current.y = d.y;
}}
onDragStart={(e, d) => {
e.preventDefault();
}}
enableResizing={{
topLeft: isPreviewShown,
}}
resizeHandleComponent={{
topLeft: <ResizeIcon />
}}
resizeHandleStyles={{
topLeft: {
top: "-1px",
left: "-1px",
}
}}
style={{
pointerEvents: "auto",
}}
>
<QuizPreviewLayout />
<IconButton
className="quiz-preview-draghandle"
sx={{
position: "absolute",
bottom: -54,
right: 46,
cursor: "move",
}}
>
<PointsIcon style={{ color: "#4D4D4D", fontSize: "30px" }} />
</IconButton>
</Rnd>
}
<IconButton
onClick={toggleQuizPreview}
sx={{
position: "absolute",
right: 0,
bottom: -54,
pointerEvents: "auto",
}}
>
<VisibilityIcon sx={{ height: "30px", width: "30px" }} />
</IconButton>
</Box>
);
} }

@ -1,6 +1,10 @@
import { Box, Button, LinearProgress, Paper, Typography } from "@mui/material"; import { Box, Button, LinearProgress, Paper, Typography } from "@mui/material";
import { questionStore } from "@root/questions"; import { questionStore } from "@root/questions";
import { decrementCurrentQuestionIndex, incrementCurrentQuestionIndex, useQuizPreviewStore } from "@root/quizPreview"; import {
decrementCurrentQuestionIndex,
incrementCurrentQuestionIndex,
useQuizPreviewStore,
} from "@root/quizPreview";
import { DefiniteQuestionType } from "model/questionTypes/shared"; import { DefiniteQuestionType } from "model/questionTypes/shared";
import { FC, useEffect } from "react"; import { FC, useEffect } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@ -17,117 +21,143 @@ import Text from "./QuizPreviewQuestionTypes/Text";
import Variant from "./QuizPreviewQuestionTypes/Variant"; import Variant from "./QuizPreviewQuestionTypes/Variant";
import Varimg from "./QuizPreviewQuestionTypes/Varimg"; import Varimg from "./QuizPreviewQuestionTypes/Varimg";
const QuestionPreviewComponentByType: Record<DefiniteQuestionType, FC<any>> = { const QuestionPreviewComponentByType: Record<DefiniteQuestionType, FC<any>> = {
variant: Variant, variant: Variant,
images: Images, images: Images,
varimg: Varimg, varimg: Varimg,
emoji: Emoji, emoji: Emoji,
text: Text, text: Text,
select: Select, select: Select,
date: Date, date: Date,
number: Number, number: Number,
file: File, file: File,
page: Page, page: Page,
rating: Rating, rating: Rating,
}; };
export default function QuizPreviewLayout() { export default function QuizPreviewLayout() {
const quizId = useParams().quizId ?? 0; const quizId = useParams().quizId ?? 0;
const listQuestions = questionStore(state => state.listQuestions); const listQuestions = questionStore((state) => state.listQuestions);
const currentQuizStep = useQuizPreviewStore(state => state.currentQuestionIndex); const currentQuizStep = useQuizPreviewStore(
(state) => state.currentQuestionIndex
);
const quizQuestions = listQuestions[quizId] ?? []; const quizQuestions = listQuestions[quizId] ?? [];
const nonDeletedQuizQuestions = quizQuestions.filter(question => !question.deleted); const nonDeletedQuizQuestions = quizQuestions.filter(
const maxCurrentQuizStep = nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0; (question) => !question.deleted
const currentProgress = Math.floor((currentQuizStep / maxCurrentQuizStep) * 100); );
const maxCurrentQuizStep =
nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
const currentProgress = Math.floor(
(currentQuizStep / maxCurrentQuizStep) * 100
);
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep]; const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
const QuestionComponent = currentQuestion const QuestionComponent = currentQuestion
? QuestionPreviewComponentByType[currentQuestion.type as DefiniteQuestionType] ? QuestionPreviewComponentByType[
: null; currentQuestion.type as DefiniteQuestionType
]
: null;
const questionElement = QuestionComponent const questionElement = QuestionComponent ? (
? <QuestionComponent key={currentQuestion.id} question={currentQuestion} /> <QuestionComponent key={currentQuestion.id} question={currentQuestion} />
: null; ) : null;
useEffect(function resetCurrentQuizStep() { useEffect(
if (currentQuizStep > maxCurrentQuizStep) { function resetCurrentQuizStep() {
decrementCurrentQuestionIndex(); if (currentQuizStep > maxCurrentQuizStep) {
} decrementCurrentQuestionIndex();
}, [currentQuizStep, maxCurrentQuizStep]); }
},
[currentQuizStep, maxCurrentQuizStep]
);
return ( return (
<Paper sx={{ <Paper
height: "100%", className="quiz-preview-draghandle"
sx={{
height: "100%",
display: "flex",
flexDirection: "column",
flexGrow: 1,
borderRadius: "12px",
pointerEvents: "auto",
}}
>
<Box
sx={{
p: "16px",
whiteSpace: "break-spaces",
overflowY: "auto",
flexGrow: 1,
"&::-webkit-scrollbar": { width: 0 },
}}
>
{questionElement}
</Box>
<Box
sx={{
mt: "auto",
p: "16px",
display: "flex",
borderTop: "1px solid #E3E3E3",
alignItems: "center",
}}
>
<Box
sx={{
flexGrow: 1,
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
flexGrow: 1, gap: 1,
borderRadius: "12px", }}
pointerEvents: "auto", >
}}> <Typography>
<Box sx={{ {nonDeletedQuizQuestions.length > 0
p: "16px", ? `Вопрос ${currentQuizStep + 1} из ${
whiteSpace: "break-spaces", nonDeletedQuizQuestions.length
overflowY: "auto", }`
flexGrow: 1, : "Нет вопросов"}
}}> </Typography>
{questionElement} {nonDeletedQuizQuestions.length > 0 && (
</Box> <LinearProgress
<Box sx={{ variant="determinate"
mt: "auto", value={currentProgress}
p: "16px", sx={{
display: "flex", "&.MuiLinearProgress-colorPrimary": {
borderTop: "1px solid #E3E3E3", backgroundColor: "fadePurple.main",
alignItems: "center", },
}}> "& .MuiLinearProgress-barColorPrimary": {
<Box sx={{ backgroundColor: "brightPurple.main",
flexGrow: 1, },
display: "flex", }}
flexDirection: "column", />
gap: 1, )}
}}> </Box>
<Typography> <Box
{nonDeletedQuizQuestions.length > 0 sx={{
? `Вопрос ${currentQuizStep + 1} из ${nonDeletedQuizQuestions.length}` ml: 2,
: "Нет вопросов" display: "flex",
} gap: 1,
</Typography> }}
{nonDeletedQuizQuestions.length > 0 && >
<LinearProgress <Button
variant="determinate" variant="outlined"
value={currentProgress} onClick={decrementCurrentQuestionIndex}
sx={{ disabled={currentQuizStep === 0}
"&.MuiLinearProgress-colorPrimary": { sx={{ px: 1, minWidth: 0 }}
backgroundColor: "fadePurple.main", >
}, <ArrowLeft />
"& .MuiLinearProgress-barColorPrimary": { </Button>
backgroundColor: "brightPurple.main", <Button
}, variant="contained"
}} onClick={() => incrementCurrentQuestionIndex(maxCurrentQuizStep)}
/> disabled={currentQuizStep >= maxCurrentQuizStep}
} >
</Box> Далее
<Box sx={{ </Button>
ml: 2, </Box>
display: "flex", </Box>
gap: 1, </Paper>
}}> );
<Button
variant="outlined"
onClick={decrementCurrentQuestionIndex}
disabled={currentQuizStep === 0}
sx={{ px: 1, minWidth: 0 }}
>
<ArrowLeft />
</Button>
<Button
variant="contained"
onClick={() => incrementCurrentQuestionIndex(maxCurrentQuizStep)}
disabled={currentQuizStep >= maxCurrentQuizStep}
>Далее</Button>
</Box>
</Box>
</Paper>
);
} }

@ -1,39 +1,54 @@
import InfoIcon from "@icons/InfoIcon"; import InfoIcon from "@icons/InfoIcon";
import { Box, FormControl, FormControlLabel, FormLabel, Radio, RadioGroup, Tooltip, Typography } from "@mui/material"; import {
Box,
FormControl,
FormControlLabel,
FormLabel,
Radio,
RadioGroup,
Tooltip,
Typography,
} from "@mui/material";
import { QuizQuestionEmoji } from "model/questionTypes/emoji"; import { QuizQuestionEmoji } from "model/questionTypes/emoji";
import { useState, ChangeEvent } from "react"; import { useState, ChangeEvent } from "react";
interface Props { interface Props {
question: QuizQuestionEmoji; question: QuizQuestionEmoji;
} }
export default function Emoji({ question }: Props) { export default function Emoji({ question }: Props) {
const [value, setValue] = useState<string | null>(null); const [value, setValue] = useState<string | null>(null);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => { const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setValue((event.target as HTMLInputElement).value); setValue((event.target as HTMLInputElement).value);
}; };
return ( return (
<FormControl fullWidth> <FormControl fullWidth>
<FormLabel id="quiz-question-radio-group">{question.title}</FormLabel> <FormLabel id="quiz-question-radio-group">{question.title}</FormLabel>
<RadioGroup <RadioGroup
aria-labelledby="quiz-question-radio-group" aria-labelledby="quiz-question-radio-group"
value={value} value={value}
onChange={handleChange} onChange={handleChange}
> >
{question.content.variants.map((variant, index) => ( {question.content.variants.map((variant, index) => (
<FormControlLabel key={index} value={variant.answer} control={<Radio />} label={ <FormControlLabel
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}> key={index}
<Typography>{`${variant.extendedText} ${variant.answer}`}</Typography> value={variant.answer}
<Tooltip title={variant.hints} placement="right"> control={<Radio />}
<Box><InfoIcon /></Box> label={
</Tooltip> <Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
</Box> <Typography>{`${variant.extendedText} ${variant.answer}`}</Typography>
} /> <Tooltip title="Подсказка" placement="right">
))} <Box>
</RadioGroup> <InfoIcon />
</FormControl> </Box>
); </Tooltip>
</Box>
}
/>
))}
</RadioGroup>
</FormControl>
);
} }

@ -1,90 +1,107 @@
import InfoIcon from "@icons/InfoIcon"; import InfoIcon from "@icons/InfoIcon";
import { Box, ButtonBase, Divider, Tooltip, Typography, useTheme } from "@mui/material"; import {
Box,
ButtonBase,
Divider,
Tooltip,
Typography,
useTheme,
} from "@mui/material";
import { QuizQuestionImages } from "model/questionTypes/images"; import { QuizQuestionImages } from "model/questionTypes/images";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
interface Props { interface Props {
question: QuizQuestionImages; question: QuizQuestionImages;
} }
export default function Images({ question }: Props) { export default function Images({ question }: Props) {
const theme = useTheme(); const theme = useTheme();
const [selectedVariants, setSelectedVariants] = useState<number[]>([]); const [selectedVariants, setSelectedVariants] = useState<number[]>([]);
function handleVariantClick(index: number) { function handleVariantClick(index: number) {
if (!question.content.multi) return setSelectedVariants([index]); if (!question.content.multi) return setSelectedVariants([index]);
const newSelectedVariants = [...selectedVariants]; const newSelectedVariants = [...selectedVariants];
if (newSelectedVariants.includes(index)) { if (newSelectedVariants.includes(index)) {
newSelectedVariants.splice(newSelectedVariants.indexOf(index), 1); newSelectedVariants.splice(newSelectedVariants.indexOf(index), 1);
} else { } else {
newSelectedVariants.push(index); newSelectedVariants.push(index);
}
setSelectedVariants(newSelectedVariants);
} }
setSelectedVariants(newSelectedVariants);
}
useEffect(function resetSelectedVariants() { useEffect(
setSelectedVariants([]); function resetSelectedVariants() {
}, [question.content.multi]); setSelectedVariants([]);
},
[question.content.multi]
);
return ( return (
<Box sx={{ <Box
display: "flex", sx={{
flexDirection: "column", display: "flex",
gap: 1, flexDirection: "column",
}}> gap: 1,
<Typography variant="h6">{question.title}</Typography> }}
<Box sx={{ >
display: "grid", <Typography variant="h6">{question.title}</Typography>
gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", <Box
gap: 1, sx={{
}}> display: "grid",
{question.content.variants.map((variant, index) => ( gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
<ButtonBase gap: 1,
key={index} }}
onClick={() => handleVariantClick(index)} >
sx={{ {question.content.variants.map((variant, index) => (
display: "flex", <ButtonBase
flexDirection: "column", key={index}
borderRadius: "8px", onClick={() => handleVariantClick(index)}
overflow: "hidden", sx={{
border: "1px solid", display: "flex",
borderColor: selectedVariants.includes(index) ? theme.palette.brightPurple.main : "#E3E3E3", flexDirection: "column",
}} borderRadius: "8px",
> overflow: "hidden",
{variant.extendedText ? border: "1px solid",
<img borderColor: selectedVariants.includes(index)
src={variant.extendedText} ? theme.palette.brightPurple.main
alt="question variant" : "#E3E3E3",
style={{ }}
width: "100%", >
display: "block", {variant.extendedText ? (
objectFit: "scale-down", <img
flexGrow: 1, src={variant.extendedText}
}} alt="question variant"
/> style={{
: width: "100%",
<Typography p={2}>Картинка отсутствует</Typography> display: "block",
} objectFit: "scale-down",
<Divider sx={{ width: "100%" }} /> flexGrow: 1,
<Box sx={{ }}
display: "flex", />
alignItems: "center", ) : (
justifyContent: "space-between", <Typography p={2}>Картинка отсутствует</Typography>
gap: 2, )}
p: 1, <Divider sx={{ width: "100%" }} />
}}> <Box
<Typography>{variant.answer}</Typography> sx={{
<Tooltip title={variant.hints} placement="right"> display: "flex",
<Box> alignItems: "center",
<InfoIcon /> justifyContent: "space-between",
</Box> gap: 2,
</Tooltip> p: 1,
</Box> }}
</ButtonBase> >
))} <Typography>{variant.answer}</Typography>
<Tooltip title="Подсказка" placement="right">
<Box>
<InfoIcon />
</Box>
</Tooltip>
</Box> </Box>
</Box> </ButtonBase>
); ))}
</Box>
</Box>
);
} }

@ -1,41 +1,54 @@
import InfoIcon from "@icons/InfoIcon"; import InfoIcon from "@icons/InfoIcon";
import { Box, FormControl, FormControlLabel, FormLabel, Radio, RadioGroup, Tooltip, Typography } from "@mui/material"; import {
Box,
FormControl,
FormControlLabel,
FormLabel,
Radio,
RadioGroup,
Tooltip,
Typography,
} from "@mui/material";
import { QuizQuestionVariant } from "model/questionTypes/variant"; import { QuizQuestionVariant } from "model/questionTypes/variant";
import { ChangeEvent, useState } from "react"; import { ChangeEvent, useState } from "react";
interface Props { interface Props {
question: QuizQuestionVariant; question: QuizQuestionVariant;
} }
export default function Variant({ question }: Props) { export default function Variant({ question }: Props) {
const [value, setValue] = useState<string | null>(null); const [value, setValue] = useState<string | null>(null);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => { const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setValue((event.target as HTMLInputElement).value); setValue((event.target as HTMLInputElement).value);
}; };
return ( return (
<FormControl fullWidth> <FormControl fullWidth>
<FormLabel id="quiz-question-radio-group">{question.title}</FormLabel> <FormLabel id="quiz-question-radio-group">{question.title}</FormLabel>
<RadioGroup <RadioGroup
aria-labelledby="quiz-question-radio-group" aria-labelledby="quiz-question-radio-group"
value={value} value={value}
onChange={handleChange} onChange={handleChange}
> >
{question.content.variants.map((variant, index) => ( {question.content.variants.map((variant, index) => (
<FormControlLabel key={index} value={variant.answer} control={<Radio />} label={ <FormControlLabel
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}> key={index}
<Typography>{variant.answer}</Typography> value={variant.answer}
<Tooltip title={variant.hints} placement="right"> control={<Radio />}
<Box> label={
<InfoIcon /> <Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
</Box> <Typography>{variant.answer}</Typography>
</Tooltip> <Tooltip title="Подсказка" placement="right">
</Box> <Box>
} /> <InfoIcon />
))} </Box>
</RadioGroup> </Tooltip>
</FormControl> </Box>
); }
/>
))}
</RadioGroup>
</FormControl>
);
} }

@ -1,80 +1,99 @@
import InfoIcon from "@icons/InfoIcon"; import InfoIcon from "@icons/InfoIcon";
import { Box, FormControl, FormControlLabel, FormLabel, Radio, RadioGroup, Tooltip, Typography } from "@mui/material"; import {
Box,
FormControl,
FormControlLabel,
FormLabel,
Radio,
RadioGroup,
Tooltip,
Typography,
} from "@mui/material";
import { QuestionVariant } from "model/questionTypes/shared"; import { QuestionVariant } from "model/questionTypes/shared";
import { QuizQuestionVarImg } from "model/questionTypes/varimg"; import { QuizQuestionVarImg } from "model/questionTypes/varimg";
import { useState, ChangeEvent } from "react"; import { useState, ChangeEvent } from "react";
interface Props { interface Props {
question: QuizQuestionVarImg; question: QuizQuestionVarImg;
} }
export default function Varimg({ question }: Props) { export default function Varimg({ question }: Props) {
const [selectedVariantIndex, setSelectedVariantIndex] = useState<number>(-1); const [selectedVariantIndex, setSelectedVariantIndex] = useState<number>(-1);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => { const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setSelectedVariantIndex(question.content.variants.findIndex( setSelectedVariantIndex(
variant => variant.answer === event.target.value question.content.variants.findIndex(
)); (variant) => variant.answer === event.target.value
}; )
const currentVariant: QuestionVariant | undefined = question.content.variants[selectedVariantIndex];
return (
<Box sx={{
display: "flex",
flexWrap: "wrap",
gap: 2,
}}>
<FormControl>
<FormLabel id="quiz-question-radio-group">{question.title}</FormLabel>
<RadioGroup
aria-labelledby="quiz-question-radio-group"
value={currentVariant?.answer ?? ""}
onChange={handleChange}
>
{question.content.variants.map((variant, index) => (
<FormControlLabel
key={index}
value={variant.answer}
control={<Radio />}
label={
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
<Typography>{variant.answer}</Typography>
<Tooltip title={variant.hints} placement="right">
<Box>
<InfoIcon />
</Box>
</Tooltip>
</Box>
}
/>
))}
</RadioGroup>
</FormControl>
<Box sx={{
border: "1px solid #E3E3E3",
maxWidth: "400px",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
}}>
{currentVariant?.extendedText ?
<img
src={currentVariant.extendedText}
alt="question variant"
style={{
width: "100%",
display: "block",
objectFit: "scale-down",
flexGrow: 1,
}}
/>
:
<Typography p={2}>{selectedVariantIndex === -1 ? "Выберите вариант" : "Картинка отсутствует"}</Typography>
}
</Box>
</Box>
); );
};
const currentVariant: QuestionVariant | undefined =
question.content.variants[selectedVariantIndex];
return (
<Box
sx={{
display: "flex",
flexWrap: "wrap",
gap: 2,
}}
>
<FormControl>
<FormLabel id="quiz-question-radio-group">{question.title}</FormLabel>
<RadioGroup
aria-labelledby="quiz-question-radio-group"
value={currentVariant?.answer ?? ""}
onChange={handleChange}
>
{question.content.variants.map((variant, index) => (
<FormControlLabel
key={index}
value={variant.answer}
control={<Radio />}
label={
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
<Typography>{variant.answer}</Typography>
<Tooltip title="Подсказка" placement="right">
<Box>
<InfoIcon />
</Box>
</Tooltip>
</Box>
}
/>
))}
</RadioGroup>
</FormControl>
<Box
sx={{
border: "1px solid #E3E3E3",
maxWidth: "400px",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
}}
>
{currentVariant?.extendedText ? (
<img
src={currentVariant.extendedText}
alt="question variant"
style={{
width: "100%",
display: "block",
objectFit: "scale-down",
flexGrow: 1,
}}
/>
) : (
<Typography p={2}>
{selectedVariantIndex === -1
? "Выберите вариант"
: "Картинка отсутствует"}
</Typography>
)}
</Box>
</Box>
);
} }