merge style in fixed

This commit is contained in:
Nastya 2023-09-29 07:24:06 +03:00
commit ed600b94a5
47 changed files with 1206 additions and 467 deletions

@ -17,6 +17,7 @@
"@types/react": "^18.0.0",
"@types/react-dnd": "^3.0.2",
"@types/react-dom": "^18.0.0",
"emoji-mart": "^5.5.2",
"file-saver": "^2.0.5",
"html-to-image": "^1.11.11",
"jszip": "^3.10.1",
@ -71,6 +72,8 @@
]
},
"devDependencies": {
"@emoji-mart/data": "^1.1.2",
"@emoji-mart/react": "^1.1.1",
"@types/react-beautiful-dnd": "^13.1.4",
"craco-alias": "^3.0.1"
}

@ -1,26 +1,26 @@
import { Box } from "@mui/material";
export default function BackArrowIcon({ color = "black" }: { color?: string }) {
return (
<Box
sx={{
height: "24px",
width: "24px",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M20.25 12H3.75" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M10.5 5.25L3.75 12L10.5 18.75"
stroke={color}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}
return (
<Box
sx={{
height: "24px",
width: "24px",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M20.25 12H3.75" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M10.5 5.25L3.75 12L10.5 18.75"
stroke={color}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

@ -6,4 +6,4 @@ export const Burger: FC<SVGProps<SVGSVGElement>> = (props) => (
<path d="M28 16.1504H3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
<path d="M28 24.1504H3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</svg>
);
);

@ -1,30 +1,40 @@
import { Box } from "@mui/material";
interface Props {
height: string;
width: string;
color: string;
height: string;
width: string;
color: string;
}
export default function ChartPieIcon({ height, width, color }: Props) {
return (
<Box
sx={{
borderRadius: "6px",
height,
width,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" viewBox="0 0 35 35" fill="none">
<path d="M17.5 30.625C24.7487 30.625 30.625 24.7487 30.625 17.5C30.625 10.2513 24.7487 4.375 17.5 4.375C10.2513 4.375 4.375 10.2513 4.375 17.5C4.375 24.7487 10.2513 30.625 17.5 30.625Z" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M17.5 17.5V4.375" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M28.8613 10.9375L6.13867 24.0625" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</Box>
);
}
return (
<Box
sx={{
borderRadius: "6px",
height,
width,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" viewBox="0 0 35 35" fill="none">
<path
d="M17.5 30.625C24.7487 30.625 30.625 24.7487 30.625 17.5C30.625 10.2513 24.7487 4.375 17.5 4.375C10.2513 4.375 4.375 10.2513 4.375 17.5C4.375 24.7487 10.2513 30.625 17.5 30.625Z"
stroke={color}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M17.5 17.5V4.375" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M28.8613 10.9375L6.13867 24.0625"
stroke={color}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

@ -17,6 +17,7 @@ const AddImage: FC<Iprops> = ({ onClick, sx }) => {
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
...sx,
}}
>
<svg width="60" height="40" viewBox="0 0 60 40" fill="none" xmlns="http://www.w3.org/2000/svg">

@ -0,0 +1,32 @@
import { Box } from "@mui/material";
export default function Plus() {
return (
<Box
sx={{
height: "30px",
width: "20px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg
width="20"
height="30"
viewBox="0 0 15 40"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M18 0H0V40H18C18.4602 40 20 39.1046 20 38V2C20 0.89543 18.4602 0 18 0Z"
fill="#7E2AEA"
/>
<path
d="M9.518 24.612C9.398 24.612 9.296 24.576 9.212 24.504C9.14 24.42 9.104 24.318 9.104 24.198V20.454H5.414C5.294 20.454 5.192 20.418 5.108 20.346C5.036 20.262 5 20.16 5 20.04V19.464C5 19.344 5.036 19.248 5.108 19.176C5.192 19.092 5.294 19.05 5.414 19.05H9.104V15.414C9.104 15.294 9.14 15.198 9.212 15.126C9.296 15.042 9.398 15 9.518 15H10.148C10.268 15 10.364 15.042 10.436 15.126C10.52 15.198 10.562 15.294 10.562 15.414V19.05H14.27C14.39 19.05 14.486 19.092 14.558 19.176C14.642 19.248 14.684 19.344 14.684 19.464V20.04C14.684 20.16 14.642 20.262 14.558 20.346C14.486 20.418 14.39 20.454 14.27 20.454H10.562V24.198C10.562 24.318 10.52 24.42 10.436 24.504C10.364 24.576 10.268 24.612 10.148 24.612H9.518Z"
fill="white"
/>
</svg>
</Box>
);
}

@ -1,7 +1,16 @@
import { useState } from "react";
import { useState, useRef } from "react";
import { useParams } from "react-router-dom";
import { Draggable } from "react-beautiful-dnd";
import { Box, TextField, FormControl, InputAdornment, IconButton, useTheme, useMediaQuery } from "@mui/material";
import {
Box,
TextField,
FormControl,
InputAdornment,
IconButton,
Popover,
useTheme,
useMediaQuery,
} from "@mui/material";
import { useDebouncedCallback } from "use-debounce";
import { questionStore, updateQuestionsList } from "@root/questions";
@ -9,26 +18,34 @@ import { questionStore, updateQuestionsList } from "@root/questions";
import { PointsIcon } from "@icons/questionsPage/PointsIcon";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import { MessageIcon } from "@icons/messagIcon";
import Popover from "@mui/material/Popover";
import TextareaAutosize from "@mui/base/TextareaAutosize";
import type { ChangeEvent, KeyboardEvent } from "react";
import type { ChangeEvent, KeyboardEvent, ReactNode } from "react";
import type { Variants } from "@root/questions";
import { ReactNode } from "react";
type AnswerItemProps = {
index: number;
totalIndex: number;
variants: Variants[];
variant: Variants;
additionalContent?: ReactNode;
additionalMobile?: ReactNode;
icon?: ReactNode;
};
export const AnswerItem = ({ index, totalIndex, variants, variant, icon }: AnswerItemProps) => {
export const AnswerItem = ({
index,
totalIndex,
variants,
variant,
additionalContent,
additionalMobile,
icon
}: AnswerItemProps) => {
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const isTablet = useMediaQuery(theme.breakpoints.down(790));
const debounced = useDebouncedCallback((value) => {
const answerNew = variants.slice();
answerNew[index].answer = value;
@ -55,7 +72,7 @@ export const AnswerItem = ({ index, totalIndex, variants, variant, icon }: Answe
const addNewAnswer = () => {
const answerNew = variants.slice();
answerNew.push({ answer: "", hints: "" });
answerNew.push({ answer: "", hints: "", emoji: "" });
updateQuestionsList(quizId, totalIndex, {
content: {
@ -97,7 +114,13 @@ export const AnswerItem = ({ index, totalIndex, variants, variant, icon }: Answe
key={index}
fullWidth
variant="standard"
sx={{ padding: isMobile ? " 20px 0 20px 0" : "0 0 18px 0" }}
sx={{ padding: isTablet ? " 20px 0 20px 0" : "0 0 18px 0" }}
// sx={{
// margin: isTablet ? " 15px 0 20px 0" : "0 0 20px 0",
// borderRadius: "10px",
// border: "1px solid rgba(0, 0, 0, 0.23)",
// background: "white",
// }}
>
<TextField
defaultValue={variant.answer}
@ -117,7 +140,7 @@ export const AnswerItem = ({ index, totalIndex, variants, variant, icon }: Answe
<InputAdornment {...provided.dragHandleProps} position="start">
<PointsIcon style={{ color: "#9A9AAF", fontSize: "30px" }} />
</InputAdornment>
{icon && icon}
{additionalContent}
</>
),
endAdornment: (
@ -126,11 +149,13 @@ export const AnswerItem = ({ index, totalIndex, variants, variant, icon }: Answe
<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" }}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
>
<TextareaAutosize
style={{ margin: "10px" }}
@ -147,17 +172,22 @@ export const AnswerItem = ({ index, totalIndex, variants, variant, icon }: Answe
),
}}
sx={{
padding: isTablet ? "10px 0px" : 0,
"& .MuiInputBase-root": {
padding: icon ? "5px 13.5px" : "13.5px 13.5px 13.5px 9px",
borderRadius: "8px",
height: "48px",
background: "#ffffff",
"& .MuiOutlinedInput-notchedOutline": {
border: "none",
},
},
}}
inputProps={{
sx: { fontSize: "18px", lineHeight: "21px", py: 0, ml: "13px" },
}}
/>
{additionalMobile}
</FormControl>
</Box>
)}

@ -8,20 +8,22 @@ import { updateVariants } from "@root/questions";
import { reorder } from "./helper";
import { ReactNode } from "react";
import type { ReactNode } from "react";
import type { DropResult } from "react-beautiful-dnd";
import type { Variants } from "@root/questions";
type AnswerDraggableListProps = {
variants: Variants[];
totalIndex: number;
icon?: ReactNode;
additionalContent?: (variant: Variants, index: number) => ReactNode;
additionalMobile?: (variant: Variants, index: number) => ReactNode;
};
export const AnswerDraggableList = ({
variants,
totalIndex,
icon,
additionalContent,
additionalMobile,
}: AnswerDraggableListProps) => {
const quizId = Number(useParams().quizId);
@ -45,7 +47,8 @@ export const AnswerDraggableList = ({
totalIndex={totalIndex}
variants={variants}
variant={variant}
icon={icon}
additionalContent={additionalContent?.(variant, index)}
additionalMobile={additionalMobile?.(variant, index)}
/>
))}
{provided.placeholder}

@ -1,6 +1,6 @@
import { useState, useEffect } from "react";
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
import { useParams } from "react-router-dom";
import React from "react";
import SettingIcon from "../../assets/icons/questionsPage/settingIcon";
import Clue from "../../assets/icons/questionsPage/clue";
import Branching from "../../assets/icons/questionsPage/branching";
@ -8,7 +8,7 @@ import { Box, Typography, Tooltip, IconButton, useTheme, useMediaQuery } from "@
import { HideIcon } from "../../assets/icons/questionsPage/hideIcon";
import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon";
import { questionStore, resetSomeField, copyQuestion, removeQuestion } from "@root/questions";
import { questionStore, resetSomeField, copyQuestion, removeQuestionForce, updateQuestionsList, removeQuestion } from "@root/questions";
import { DoubleTick } from "@icons/questionsPage/DoubleTick";
import { DoubleArrowRight } from "@icons/questionsPage/DoubleArrowRight";
import { VectorQuestions } from "@icons/questionsPage/VectorQuestions";
@ -20,15 +20,26 @@ interface Props {
totalIndex: number;
}
export default function ButtonsOptions({ SSHC, switchState, totalIndex }: Props) {
export default function ButtonsOptions({
SSHC,
switchState,
totalIndex,
}: Props) {
const quizId = Number(useParams().quizId);
const { openedModalSettings } = questionStore();
const [openedReallyChangingModal, setOpenedReallyChangingModal] = React.useState<boolean>(false);
const { openedModalSettings, listQuestions } = questionStore();
const [openedReallyChangingModal, setOpenedReallyChangingModal] =
useState<boolean>(false);
useEffect(() => {
if (listQuestions[quizId][totalIndex].deleteTimeoutId) {
clearTimeout(listQuestions[quizId][totalIndex].deleteTimeoutId);
}
}, [listQuestions]);
const openedModal = () => {
resetSomeField({ openedModalSettings: "open" });
console.log(openedModalSettings);
};
const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(790));
@ -41,17 +52,33 @@ export default function ButtonsOptions({ SSHC, switchState, totalIndex }: Props)
myFunc?: any;
}[] = [
{
icon: <SettingIcon color={switchState === "setting" ? "#ffffff" : theme.palette.grey3.main} />,
icon: (
<SettingIcon
color={
switchState === "setting" ? "#ffffff" : theme.palette.grey3.main
}
/>
),
title: "Настройки",
value: "setting",
},
{
icon: <Clue color={switchState === "help" ? "#ffffff" : theme.palette.grey3.main} />,
icon: (
<Clue
color={switchState === "help" ? "#ffffff" : theme.palette.grey3.main}
/>
),
title: "Подсказка",
value: "help",
},
{
icon: <Branching color={switchState === "branching" ? "#ffffff" : theme.palette.grey3.main} />,
icon: (
<Branching
color={
switchState === "branching" ? "#ffffff" : theme.palette.grey3.main
}
/>
),
title: "Ветвление",
value: "branching",
myFunc: openedModal,
@ -78,7 +105,7 @@ export default function ButtonsOptions({ SSHC, switchState, totalIndex }: Props)
}}
>
{buttonSetting.map(({ icon, title, value, myFunc }) => (
<>
<Box key={value}>
{value === "branching" ? (
<Tooltip
arrow
@ -95,7 +122,9 @@ export default function ButtonsOptions({ SSHC, switchState, totalIndex }: Props)
>
Будет показан при условии
</Typography>
<Typography sx={{ fontWeight: "bold", fontSize: "12px" }}>Название</Typography>
<Typography sx={{ fontWeight: "bold", fontSize: "12px" }}>
Название
</Typography>
<Typography
sx={{
fontWeight: "bold",
@ -105,7 +134,9 @@ export default function ButtonsOptions({ SSHC, switchState, totalIndex }: Props)
>
Условие 1, Условие 2
</Typography>
<Typography sx={{ color: "#7E2AEA", fontSize: "12px" }}>Все условия обязательны</Typography>
<Typography sx={{ color: "#7E2AEA", fontSize: "12px" }}>
Все условия обязательны
</Typography>
</Box>
}
>
@ -116,10 +147,15 @@ export default function ButtonsOptions({ SSHC, switchState, totalIndex }: Props)
myFunc();
}}
sx={{
backgroundColor: switchState === value ? theme.palette.brightPurple.main : "transparent",
color: switchState === value ? "#ffffff" : theme.palette.grey3.main,
backgroundColor:
switchState === value
? theme.palette.brightPurple.main
: "transparent",
color:
switchState === value
? "#ffffff"
: theme.palette.grey3.main,
minWidth: isWrappMiniButtonSetting ? "30px" : "64px",
height: "30px",
}}
>
@ -136,17 +172,23 @@ export default function ButtonsOptions({ SSHC, switchState, totalIndex }: Props)
myFunc();
}}
sx={{
backgroundColor: switchState === value ? theme.palette.brightPurple.main : "transparent",
color: switchState === value ? "#ffffff" : theme.palette.grey3.main,
minWidth: isMobile ? "30px" : "64px",
backgroundColor:
switchState === value
? theme.palette.brightPurple.main
: "transparent",
color:
switchState === value
? "#ffffff"
: theme.palette.grey3.main,
minWidth: isWrappMiniButtonSetting ? "30px" : "64px",
height: "30px",
}}
>
{icon}
{isMobile ? null : title}
{isWrappMiniButtonSetting ? null : title}
</MiniButtonSetting>
)}
</>
</Box>
))}
<MiniButtonSetting
onClick={() => setOpenedReallyChangingModal(true)}
@ -188,13 +230,35 @@ export default function ButtonsOptions({ SSHC, switchState, totalIndex }: Props)
<IconButton sx={{ borderRadius: "6px", padding: "2px" }}>
<HideIcon style={{ color: "#4D4D4D" }} />
</IconButton>
<IconButton sx={{ borderRadius: "6px", padding: "2px" }} onClick={() => copyQuestion(quizId, totalIndex)}>
<CopyIcon style={{ color: "#4D4D4D" }} />
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => copyQuestion(quizId, totalIndex)}
>
<CopyIcon color={"#4D4D4D"} />
</IconButton>
<IconButton sx={{ borderRadius: "6px", padding: "2px" }} onClick={() => removeQuestion(quizId, totalIndex)}>
<DeleteIcon style={{ color: "#4D4D4D" }} />
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
const removedId = listQuestions[quizId][totalIndex].id;
if (listQuestions[quizId][totalIndex].deleteTimeoutId) {
clearTimeout(listQuestions[quizId][totalIndex].deleteTimeoutId);
}
removeQuestion(quizId, totalIndex);
const newTimeoutId = window.setTimeout(() => {
removeQuestionForce(quizId, removedId);
}, 5000);
updateQuestionsList(quizId, totalIndex, {
...listQuestions[quizId][totalIndex],
deleteTimeoutId: newTimeoutId,
});
}}
>
<DeleteIcon color={"#4D4D4D"} />
</IconButton>
</Box>
</Box>
);
}
}

@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
import SettingIcon from "../../assets/icons/questionsPage/settingIcon";
import Clue from "../../assets/icons/questionsPage/clue";
@ -9,7 +9,14 @@ import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon";
import { DeleteIcon } from "../../assets/icons/questionsPage/deleteIcon";
import ImgIcon from "../../assets/icons/questionsPage/imgIcon";
import { useParams } from "react-router-dom";
import { questionStore, copyQuestion, removeQuestion, resetSomeField } from "@root/questions";
import {
questionStore,
copyQuestion,
removeQuestion,
resetSomeField,
removeQuestionForce,
updateQuestionsList,
} from "@root/questions";
import { DoubleArrowRight } from "@icons/questionsPage/DoubleArrowRight";
import { DoubleTick } from "@icons/questionsPage/DoubleTick";
import { VectorQuestions } from "@icons/questionsPage/VectorQuestions";
@ -26,12 +33,19 @@ interface Props {
export default function ButtonsOptionsAndPict({ SSHC, switchState, totalIndex }: Props) {
const [buttonHover, setButtonHover] = useState<string>("");
const quizId = Number(useParams().quizId);
const { openedModalSettings } = questionStore();
const { listQuestions } = questionStore();
const [openedReallyChangingModal, setOpenedReallyChangingModal] =
useState<boolean>(false);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const isIconMobile = useMediaQuery(theme.breakpoints.down(1050));
const [openedReallyChangingModal, setOpenedReallyChangingModal] = useState<boolean>(false);
useEffect(() => {
if (listQuestions[quizId][totalIndex].deleteTimeoutId) {
clearTimeout(listQuestions[quizId][totalIndex].deleteTimeoutId);
}
}, [listQuestions]);
const openedModal = () => {
resetSomeField({ openedModalSettings: "open" });
};
@ -198,7 +212,6 @@ export default function ButtonsOptionsAndPict({ SSHC, switchState, totalIndex }:
/>
{isIconMobile ? null : "Изображение"}
</MiniButtonSetting>
<MiniButtonSetting
onClick={() => setOpenedReallyChangingModal(true)}
sx={{
@ -243,12 +256,37 @@ export default function ButtonsOptionsAndPict({ SSHC, switchState, totalIndex }:
</IconButton>
<IconButton
sx={{ borderRadius: "6px", padding: " 0px 2px" }}
onClick={() => removeQuestion(quizId, totalIndex)}
onClick={() => copyQuestion(quizId, totalIndex)}
>
<CopyIcon color={"#4D4D4D"} />
</IconButton>
<IconButton
sx={{ borderRadius: "6px", padding: "2px" }}
onClick={() => {
const removedId = listQuestions[quizId][totalIndex].id;
if (listQuestions[quizId][totalIndex].deleteTimeoutId) {
clearTimeout(listQuestions[quizId][totalIndex].deleteTimeoutId);
}
removeQuestion(quizId, totalIndex);
const newTimeoutId = window.setTimeout(() => {
removeQuestionForce(quizId, removedId);
}, 5000);
updateQuestionsList(quizId, totalIndex, {
...listQuestions[quizId][totalIndex],
deleteTimeoutId: newTimeoutId,
});
}}
>
<DeleteIcon style={{ color: "#4D4D4D" }} />
</IconButton>
</Box>
<ReallyChangingModal opened={openedReallyChangingModal} onClose={() => setOpenedReallyChangingModal(false)} />
<ReallyChangingModal
opened={openedReallyChangingModal}
onClose={() => setOpenedReallyChangingModal(false)}
/>
</Box>
);
}

@ -57,7 +57,7 @@ export default function SettingsData({ totalIndex }: SettingsDataProps) {
}}
/>
<CustomCheckbox
sx={{ display: "block" }}
sx={{ display: "block", mr: isMobile ? "0px" : "16px" }}
label={"Выбор времени"}
checked={listQuestions[quizId][totalIndex].content.time}
handleChange={({ target }) => {
@ -92,8 +92,9 @@ export default function SettingsData({ totalIndex }: SettingsDataProps) {
});
}}
/>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ width: isMobile ? "93%" : "auto", display: "flex", alignItems: "center" }}>
<CustomCheckbox
sx={{ mr: isMobile ? "0px" : "16px" }}
label={"Внутреннее название вопроса"}
checked={listQuestions[quizId][totalIndex].content.innerNameCheck}
handleChange={(e) => {

@ -11,6 +11,7 @@ import {
ClickAwayListener,
Modal,
Button,
useTheme,
} from "@mui/material";
import {
@ -27,6 +28,7 @@ type ChooseAnswerModalProps = {
onClose: () => void;
anchorRef: RefObject<HTMLDivElement>;
totalIndex: number;
switchState: string;
};
export const ChooseAnswerModal = ({
@ -34,11 +36,13 @@ export const ChooseAnswerModal = ({
onClose,
anchorRef,
totalIndex,
switchState,
}: ChooseAnswerModalProps) => {
const [openModal, setOpenModal] = useState<boolean>(false);
const [selectedValue, setSelectedValue] = useState<string>("");
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const theme = useTheme();
return (
<>
@ -56,15 +60,26 @@ export const ChooseAnswerModal = ({
{BUTTON_TYPE_QUESTIONS.map(({ icon, title, value }) => (
<MenuItem
key={value}
onClick={() => {
onClose();
setOpenModal(true);
setSelectedValue(value);
}}
sx={{ display: "flex", gap: "10px" }}
{...(value !== switchState && {
onClick: () => {
onClose();
setOpenModal(true);
setSelectedValue(value);
},
})}
>
<Box>{icon}</Box>
<Typography>{title}</Typography>
<Typography
sx={{
color:
value === switchState
? theme.palette.brightPurple.main
: theme.palette.grey2.main,
}}
>
{title}
</Typography>
</MenuItem>
))}
</MenuList>

@ -1,33 +1,88 @@
import { memo } from "react";
import { useParams } from "react-router-dom";
import { Draggable } from "react-beautiful-dnd";
import { Box, ListItem } from "@mui/material";
import { Box, ListItem, Typography, useTheme } from "@mui/material";
import QuestionsPageCard from "./QuestionPageCard";
import { questionStore, updateQuestionsList } from "@root/questions";
import {Question} from "@root/questions"
type DraggableListItemProps = {
index: number;
isDragging: boolean;
questionData: Question;
};
export const DraggableListItem = ({
export default memo(({
index,
isDragging,
}: DraggableListItemProps) => (
<Draggable draggableId={String(index)} index={index}>
{(provided) => (
<ListItem
ref={provided.innerRef}
{...provided.draggableProps}
sx={{ userSelect: "none", padding: 0 }}
>
<Box sx={{ width: "100%", position: "relative" }}>
<QuestionsPageCard
key={index}
totalIndex={index}
draggableProps={provided.dragHandleProps}
isDragging={isDragging}
/>
</Box>
</ListItem>
)}
</Draggable>
);
questionData
}: DraggableListItemProps) => {
const quizId = Number(useParams().quizId);
const theme = useTheme();
console.log("Мой индекс "+index)
console.log(questionData)
return (
<Draggable draggableId={String(index)} index={index}>
{(provided) => (
<ListItem
ref={provided.innerRef}
{...provided.draggableProps}
sx={{ userSelect: "none", padding: 0 }}
>
{questionData.deleted ? (
<Box
{...provided.dragHandleProps}
sx={{
width: "100%",
maxWidth: "800px",
display: "flex",
justifyContent: "center",
marginBottom: "40px",
gap: "5px",
}}
>
<Typography
sx={{
fontSize: "16px",
color: theme.palette.grey2.main,
}}
>
Вопрос удалён.
</Typography>
<Typography
onClick={() => {
updateQuestionsList(quizId, index, {
...questionData,
deleted: false,
});
}}
sx={{
cursor: "pointer",
fontSize: "16px",
textDecoration: "underline",
color: theme.palette.brightPurple.main,
textDecorationColor: theme.palette.brightPurple.main,
}}
>
Восстановить?
</Typography>
</Box>
) : (
<Box sx={{ width: "100%", position: "relative" }}>
<QuestionsPageCard
key={index}
totalIndex={index}
draggableProps={provided.dragHandleProps}
isDragging={isDragging}
/>
</Box>
)}
</ListItem>
)}
</Draggable>
);
});

@ -1,4 +1,4 @@
import { useState, useRef } from "react";
import { useState, useRef, useEffect } from "react";
import { useParams } from "react-router-dom";
import {
Box,
@ -18,7 +18,14 @@ import { ChooseAnswerModal } from "./ChooseAnswerModal";
import TypeQuestions from "../TypeQuestions";
import SwitchQuestionsPage from "../SwitchQuestionsPage";
import { questionStore, updateQuestionsList, createQuestion, copyQuestion, removeQuestion } from "@root/questions";
import {
questionStore,
updateQuestionsList,
createQuestion,
copyQuestion,
removeQuestion,
removeQuestionForce,
} from "@root/questions";
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
@ -91,6 +98,12 @@ export default function QuestionsPageCard({ totalIndex, draggableProps, isDraggi
updateQuestionsList(quizId, totalIndex, { title });
}, 1000);
useEffect(() => {
if (listQuestions[quizId][totalIndex].deleteTimeoutId) {
clearTimeout(listQuestions[quizId][totalIndex].deleteTimeoutId);
}
}, [listQuestions]);
return (
<>
<Paper
@ -140,6 +153,7 @@ export default function QuestionsPageCard({ totalIndex, draggableProps, isDraggi
onClose={() => setOpen(false)}
anchorRef={anchorRef}
totalIndex={totalIndex}
switchState={switchState}
/>
</Box>
),
@ -226,7 +240,25 @@ export default function QuestionsPageCard({ totalIndex, draggableProps, isDraggi
borderRadius: "6px",
padding: "2px",
}}
onClick={() => removeQuestion(quizId, totalIndex)}
onClick={() => {
const removedId = listQuestions[quizId][totalIndex].id;
if (listQuestions[quizId][totalIndex].deleteTimeoutId) {
clearTimeout(
listQuestions[quizId][totalIndex].deleteTimeoutId
);
}
removeQuestion(quizId, totalIndex);
const newTimeoutId = window.setTimeout(() => {
removeQuestionForce(quizId, removedId);
}, 5000);
updateQuestionsList(quizId, totalIndex, {
...listQuestions[quizId][totalIndex],
deleteTimeoutId: newTimeoutId,
});
}}
>
<DeleteIcon style={{ color: "white" }} />
</IconButton>

@ -1,9 +1,8 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import { Box } from "@mui/material";
import { DragDropContext, Droppable } from "react-beautiful-dnd";
import { DraggableListItem } from "./DraggableListItem";
import DraggableListItem from "./DraggableListItem";
import { questionStore, updateQuestionsListDragAndDrop } from "@root/questions";
@ -17,7 +16,11 @@ export const DraggableList = () => {
const onDragEnd = ({ destination, source }: DropResult) => {
if (destination) {
const newItems = reorder(listQuestions[quizId], source.index, destination.index);
const newItems = reorder(
listQuestions[quizId],
source.index,
destination.index
);
updateQuestionsListDragAndDrop(quizId, newItems);
}
@ -29,7 +32,12 @@ export const DraggableList = () => {
{(provided, snapshot) => (
<Box ref={provided.innerRef} {...provided.droppableProps}>
{listQuestions[quizId]?.map((_, index) => (
<DraggableListItem key={index} index={index} isDragging={snapshot.isDraggingOver} />
<DraggableListItem
key={index}
index={index}
isDragging={snapshot.isDraggingOver}
questionData={_}
/>
))}
{provided.placeholder}
</Box>

@ -27,7 +27,7 @@ export default function DropDown({ totalIndex }: Props) {
const addNewAnswer = () => {
const answerNew = variants.slice();
answerNew.push({ answer: "", hints: "" });
answerNew.push({ answer: "", hints: "", emoji: "" });
updateQuestionsList(quizId, totalIndex, {
content: {
@ -93,7 +93,11 @@ export default function DropDown({ totalIndex }: Props) {
)}
</Box>
</Box>
<ButtonsOptions switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} />
<ButtonsOptions
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchDropDown switchState={switchState} totalIndex={totalIndex} />
</>
);

@ -1,3 +1,4 @@
import { useState } from "react";
import { useParams } from "react-router-dom";
import {
Box,
@ -14,22 +15,29 @@ import {
import { EnterIcon } from "../../../assets/icons/questionsPage/enterIcon";
import ButtonsOptions from "../ButtonsOptions";
import SwitchEmoji from "./switchEmoji";
import React from "react";
import AddEmoji from "../../../assets/icons/questionsPage/addEmoji";
import { AnswerDraggableList } from "../AnswerDraggableList";
import { EmojiPicker } from "@ui_kit/EmojiPicker";
import { EmojiIcons } from "@icons/EmojiIocns";
import { questionStore, updateQuestionsList } from "@root/questions";
import { PointsIcon } from "@icons/questionsPage/PointsIcon";
import { MessageIcon } from "@icons/messagIcon";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import { ImageAddIcons } from "@icons/ImageAddIcons";
import { EmojiIcons } from "@icons/EmojiIocns";
import AddEmoji from "../../../assets/icons/questionsPage/addEmoji";
import PlusImage from "../../../assets/icons/questionsPage/plus";
interface Props {
totalIndex: number;
}
export default function Emoji({ totalIndex }: Props) {
const [switchState, setSwitchState] = React.useState("setting");
const [switchState, setSwitchState] = useState<string>("setting");
const [open, setOpen] = useState<boolean>(false);
const [anchorElement, setAnchorElement] = useState<HTMLDivElement | null>(
null
);
const [currentIndex, setCurrentIndex] = useState<number>(0);
const { listQuestions } = questionStore();
const quizId = Number(useParams().quizId);
const theme = useTheme();
@ -42,7 +50,7 @@ export default function Emoji({ totalIndex }: Props) {
return (
<>
<Box sx={{ padding: "0 20px 0px 20px" }}>
{/* <Box sx={{ padding: "0 20px 0px 20px" }}>
<Box
sx={{
width: "100%",
@ -148,14 +156,165 @@ export default function Emoji({ totalIndex }: Props) {
</Box>
)}
</Box>
<Box sx={{ display: "flex", alignItems: "center", marginBottom: "20px" }}>
<Box sx={{ display: "flex", alignItems: "center", marginBottom: "20px" }}> */}
<Box sx={{ padding: "20px" }}>
<AnswerDraggableList
variants={listQuestions[quizId][totalIndex].content.variants}
totalIndex={totalIndex}
additionalContent={(variant, index) => (
<>
{!isTablet && (
<Box sx={{ cursor: "pointer", margin: "0 15px 0 5px" }}>
<Box
onClick={({ currentTarget }) => {
setAnchorElement(currentTarget);
setCurrentIndex(index);
setOpen(true);
}}
>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
gap: "5px",
}}
>
{variant.emoji ? (
<Box
sx={{
width: "30px",
display: "flex",
alignItems: "center",
background: "#EEE4FC",
borderRadius: "3px",
marginRight: "15px",
}}
>
<Box sx={{ marginLeft: "3px" }}>{variant.emoji}</Box>
<Box sx={{ marginLeft: "-3px" }}>
<PlusImage />
</Box>
</Box>
) : (
<AddEmoji />
)}
</Box>
</Box>
</Box>
)}
</>
)}
additionalMobile={(variant, index) => (
<>
{isTablet && (
<Box
onClick={({ currentTarget }) => {
setAnchorElement(currentTarget);
setCurrentIndex(index);
setOpen(true);
}}
sx={{
display: "flex",
alignItems: "center",
m: "8px",
position: "relative",
}}
>
<Box
sx={{
width: "100%",
background: "#EEE4FC",
height: "40px",
}}
/>
{variant.emoji ? (
<Box
sx={{
position: "absolute",
color: "#7E2AEA",
fontSize: "20px",
left: "45%",
right: "55%",
}}
>
{variant.emoji}
</Box>
) : (
<EmojiIcons
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>
)}
</>
)}
/>
<Popover
open={open}
anchorEl={anchorElement}
onClick={(event) => event.stopPropagation()}
onClose={() => setOpen(false)}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
sx={{
".MuiPaper-root.MuiPaper-rounded": {
borderRadius: "10px",
},
}}
>
<EmojiPicker
onEmojiSelect={({ native }) => {
setOpen(false);
const cloneVariants = [
...listQuestions[quizId][totalIndex].content.variants,
];
cloneVariants[currentIndex] = {
...cloneVariants[currentIndex],
emoji: native,
};
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
variants: cloneVariants,
},
});
}}
/>
</Popover>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
<Link
component="button"
variant="body2"
sx={{ color: theme.palette.brightPurple.main }}
onClick={() => {
const answerNew = listQuestions[quizId][totalIndex].content.variants.slice();
answerNew.push({ answer: "", hints: "" });
const answerNew =
listQuestions[quizId][totalIndex].content.variants.slice();
answerNew.push({ answer: "", hints: "", emoji: "" });
updateQuestionsList(quizId, totalIndex, {
content: {
@ -167,7 +326,7 @@ export default function Emoji({ totalIndex }: Props) {
>
Добавьте ответ
</Link>
{isMobile ? null : (
{!isTablet && (
<>
<Typography
sx={{
@ -184,7 +343,11 @@ export default function Emoji({ totalIndex }: Props) {
)}
</Box>
</Box>
<ButtonsOptions switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} />
<ButtonsOptions
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchEmoji switchState={switchState} totalIndex={totalIndex} />
</>
);

@ -59,7 +59,7 @@ export default function SettingEmoji({ totalIndex }: SettingEmojiProps) {
}}
/>
<CustomCheckbox
sx={{ mr: isMobile ? "0px" : "16px" }}
sx={{ display: "block", mr: isMobile ? "0px" : "16px" }}
label={'Вариант "свой ответ"'}
checked={listQuestions[quizId][totalIndex].content.own}
handleChange={(e) => {
@ -85,6 +85,7 @@ export default function SettingEmoji({ totalIndex }: SettingEmojiProps) {
Настройки вопросов
</Typography>
<CustomCheckbox
sx={{ mr: isMobile ? "0px" : "16px" }}
label={"Необязательный вопрос"}
checked={!listQuestions[quizId][totalIndex].required}
handleChange={(e) => {
@ -95,6 +96,7 @@ export default function SettingEmoji({ totalIndex }: SettingEmojiProps) {
/>
<Box sx={{ width: isMobile ? "90%" : "auto", display: "flex", alignItems: "center" }}>
<CustomCheckbox
sx={{ mr: isMobile ? "0px" : "16px" }}
label={"Внутреннее название вопроса"}
checked={listQuestions[quizId][totalIndex].content.innerNameCheck}
handleChange={(e) => {

@ -54,6 +54,7 @@ export default function OptionsAndPicture({ totalIndex }: Props) {
>
{listQuestions[quizId][totalIndex].content.variants.map((_, index) => (
<ButtonBase
key={index}
component="label"
sx={{
display: isTablet ? "none" : "flex",
@ -107,7 +108,6 @@ export default function OptionsAndPicture({ totalIndex }: Props) {
+
</span>
</Box>
<Typography
sx={{
padding: "0 0 0 20px",
@ -179,8 +179,15 @@ export default function OptionsAndPicture({ totalIndex }: Props) {
<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
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" }} />
@ -205,10 +212,25 @@ export default function OptionsAndPicture({ totalIndex }: Props) {
/>
{isMobile && (
<Box sx={{ display: "flex", alignItems: "center", m: "8px", position: "relative" }}>
<Box sx={{ width: "100%", background: "#EEE4FC", height: "40px" }} />
<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%" }}
style={{
position: "absolute",
color: "#7E2AEA",
fontSize: "20px",
left: "45%",
right: "55%",
}}
/>
<Box
sx={{
@ -245,6 +267,12 @@ export default function OptionsAndPicture({ totalIndex }: Props) {
mr: "4px",
height: "19px",
}}
onClick={() => {
const clonContent = listQuestions[quizId][totalIndex].content;
clonContent.variants.push({ answer: "", hints: "", emoji: "" });
updateQuestionsList(quizId, totalIndex, { content: clonContent });
}}
>
Добавьте ответ
</Link>
@ -265,7 +293,11 @@ export default function OptionsAndPicture({ totalIndex }: Props) {
)}
</Box>
</Box>
<ButtonsOptionsAndPict switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} />
<ButtonsOptionsAndPict
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchOptionsAndPict switchState={switchState} totalIndex={totalIndex} />
</>
);

@ -53,6 +53,7 @@ export default function OptionsPicture({ totalIndex }: Props) {
}
};
return (
<>
<Box sx={{ pl: "20px", pr: "20px" }}>
@ -95,7 +96,6 @@ export default function OptionsPicture({ totalIndex }: Props) {
+
</span>
</Box>
<Typography
sx={{
padding: "0 0 0 20px",
@ -166,8 +166,15 @@ export default function OptionsPicture({ totalIndex }: Props) {
<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
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" }} />
@ -192,10 +199,25 @@ export default function OptionsPicture({ totalIndex }: Props) {
/>
{isMobile && (
<Box sx={{ display: "flex", alignItems: "center", m: "8px", position: "relative" }}>
<Box sx={{ width: "100%", background: "#EEE4FC", height: "40px" }} />
<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%" }}
style={{
position: "absolute",
color: "#7E2AEA",
fontSize: "20px",
left: "45%",
right: "55%",
}}
/>
<Box
sx={{
@ -253,8 +275,15 @@ export default function OptionsPicture({ totalIndex }: Props) {
)}
</Box>
</Box>
<ButtonsOptions switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} />
<SwitchAnswerOptionsPict switchState={switchState} totalIndex={totalIndex} />
<ButtonsOptions
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchAnswerOptionsPict
switchState={switchState}
totalIndex={totalIndex}
/>
</>
);
}

@ -147,19 +147,22 @@ export default function SettingOpytionsPict({ totalIndex }: SettingOpytionsPictP
})
}
/>
<CustomCheckbox
sx={{ display: "block", mr: isMobile ? "0px" : "16px" }}
label={"Большие картинки"}
checked={listQuestions[quizId][totalIndex].content.largeCheck}
handleChange={({ target }) =>
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
largeCheck: target.checked,
},
})
}
/>
{listQuestions[quizId][totalIndex].content.xy !== "1:1" &&
listQuestions[quizId][totalIndex].content.format !== "masonry" && (
<CustomCheckbox
sx={{ display: "block", mr: isMobile ? "0px" : "16px" }}
label={"Большие картинки"}
checked={listQuestions[quizId][totalIndex].content.largeCheck}
handleChange={({ target }) =>
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
largeCheck: target.checked,
},
})
}
/>
)}
<CustomCheckbox
sx={{ display: "block", mr: isMobile ? "0px" : "16px" }}
label={'Вариант "свой ответ"'}
@ -174,7 +177,6 @@ export default function SettingOpytionsPict({ totalIndex }: SettingOpytionsPictP
}
/>
</Box>
<Box
sx={{
pt: isMobile ? "0px" : "20px",
@ -236,7 +238,13 @@ export default function SettingOpytionsPict({ totalIndex }: SettingOpytionsPictP
})
}
/>
<Box sx={{ width: isMobile ? "90%" : "auto", display: "flex", alignItems: "center" }}>
<Box
sx={{
width: isMobile ? "90%" : "auto",
display: "flex",
alignItems: "center",
}}
>
<CustomCheckbox
label={"Внутреннее название вопроса"}
checked={listQuestions[quizId][totalIndex].content.innerNameCheck}

@ -1,5 +1,11 @@
import { useParams } from "react-router-dom";
import { Box, Typography, useMediaQuery, useTheme, Tooltip } from "@mui/material";
import {
Box,
Typography,
Tooltip,
useMediaQuery,
useTheme,
} from "@mui/material";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import CustomTextField from "@ui_kit/CustomTextField";
import { useDebouncedCallback } from "use-debounce";
@ -15,7 +21,6 @@ type SettingPageOptionsProps = {
export default function SettingPageOptions({ totalIndex }: SettingPageOptionsProps) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const debounced = useDebouncedCallback((value) => {

@ -77,7 +77,6 @@ export default function QuestionsPage() {
<IconButton
onClick={() => {
createQuestion(quizId);
console.log(listQuestions);
}}
>
<AddPlus />

@ -1,10 +1,16 @@
import { useState } from "react";
import { useState, useEffect, useRef } from "react";
import { useParams } from "react-router-dom";
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
import {
Box,
Typography,
TextField,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useDebouncedCallback } from "use-debounce";
import { questionStore, updateQuestionsList } from "@root/questions";
import ButtonsOptions from "../ButtonsOptions";
import SwitchRating from "./switchRating";
import { questionStore, updateQuestionsList } from "@root/questions";
import CustomTextField from "@ui_kit/CustomTextField";
import TropfyIcon from "../../../assets/icons/questionsPage/tropfyIcon";
import FlagIcon from "../../../assets/icons/questionsPage/FlagIcon";
@ -25,10 +31,49 @@ export type ButtonRatingFrom = {
export default function RatingOptions({ totalIndex }: Props) {
const [switchState, setSwitchState] = useState("setting");
const [negativeText, setNegativeText] = useState<string>("");
const [positiveText, setPositiveText] = useState<string>("");
const [negativeTextWidth, setNegativeTextWidth] = useState<number>(0);
const [positiveTextWidth, setPositiveTextWidth] = useState<number>(0);
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const negativeRef = useRef<HTMLDivElement>(null);
const positiveRef = useRef<HTMLDivElement>(null);
const debounceNegativeDescription = useDebouncedCallback((value) => {
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingNegativeDescription: value.substring(0, 15),
},
});
}, 500);
const debouncePositiveDescription = useDebouncedCallback((value) => {
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingPositiveDescription: value.substring(0, 15),
},
});
}, 500);
useEffect(() => {
setNegativeText(
listQuestions[quizId][totalIndex].content.ratingNegativeDescription
);
setPositiveText(
listQuestions[quizId][totalIndex].content.ratingPositiveDescription
);
}, []);
useEffect(() => {
setNegativeTextWidth(negativeRef.current?.offsetWidth || 0);
}, [negativeText]);
useEffect(() => {
setPositiveTextWidth(positiveRef.current?.offsetWidth || 0);
}, [positiveText]);
const buttonRatingForm: ButtonRatingFrom[] = [
{ name: "star", icon: <StarIconMini width={"50px"} color={theme.palette.grey2.main} /> },
@ -51,42 +96,43 @@ export default function RatingOptions({ totalIndex }: Props) {
<>
<Box
sx={{
width: isMobile ? "auto" : "100%",
width: isMobile
? "auto"
: `${
listQuestions[quizId][totalIndex].content.steps * 44 >
negativeTextWidth + positiveTextWidth
? listQuestions[quizId][totalIndex].content.steps * 44
: negativeTextWidth + positiveTextWidth + 20
}px`,
minWidth: "300px",
maxWidth: "440px",
display: "flex",
px: "20px",
flexDirection: "column",
gap: "20px",
}}
>
<Box sx={{ display: "flex", gap: isMobile ? "10px" : "15px" }}>
{Array.from({ length: listQuestions[quizId][totalIndex].content.steps }, (_, index) => index).map(
(itemNumber) => (
<Box
{...(itemNumber === 0 || itemNumber === listQuestions[quizId][totalIndex].content.steps - 1
? {
onClick: () => {
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingExpanded: true,
},
});
},
sx: {
cursor: "pointer",
transform: "scale(1.5)",
":hover": {
transform: "scale(1.7)",
transition: "0.2s",
},
},
}
: { sx: { transform: "scale(1.5)" } })}
>
{buttonRatingForm.find(({ name }) => listQuestions[quizId][totalIndex].content.form === name)?.icon}
</Box>
)
)}
<Box
sx={{
display: "flex",
justifyContent: "space-between",
padding: "0 10px",
gap: isMobile ? "10px" : "15px",
}}
>
{Array.from(
{ length: listQuestions[quizId][totalIndex].content.steps },
(_, index) => index
).map((itemNumber) => (
<Box key={itemNumber} sx={{ transform: "scale(1.5)" }}>
{
buttonRatingForm.find(
({ name }) =>
listQuestions[quizId][totalIndex].content.form === name
)?.icon
}
</Box>
))}
</Box>
<Box
sx={{
@ -99,53 +145,106 @@ export default function RatingOptions({ totalIndex }: Props) {
}}
>
<Typography
ref={negativeRef}
sx={{
color: theme.palette.grey2.main,
position: "absolute",
opacity: 0,
zIndex: "-100",
whiteSpace: "nowrap",
fontSize: "16px",
fontWeight: 400,
}}
>
Негативно
{negativeText}
</Typography>
<Typography
sx={{
color: theme.palette.grey2.main,
fontSize: "16px",
fontWeight: 400,
<TextField
defaultValue={
listQuestions[quizId][totalIndex].content
.ratingNegativeDescription
}
value={negativeText}
placeholder="Негативно"
onChange={({ target }) => {
if (target.value.length <= 15) {
setNegativeText(target.value);
debounceNegativeDescription(target.value);
}
}}
>
Позитивно
</Typography>
</Box>
{listQuestions[quizId][totalIndex].content.ratingExpanded &&
(listQuestions[quizId][totalIndex].content.ratingDescription ? (
<Typography>{listQuestions[quizId][totalIndex].content.ratingDescription}</Typography>
) : (
<CustomTextField
placeholder={"Описание"}
text={listQuestions[quizId][totalIndex].content.ratingDescription}
onKeyDown={({ target, key }) => {
if (key === "Enter") {
const currentTarget = target as HTMLInputElement;
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingDescription: currentTarget.value.substring(0, 20),
},
});
}
}}
onBlur={({ target }) => {
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingDescription: target.value.substring(0, 20),
onBlur={({ target }) => debounceNegativeDescription(target.value)}
sx={{
width: negativeTextWidth + 10 + "px",
background: "transparent",
fontSize: "18px",
minWidth: "95px",
maxWidth: "230px",
transition: "0.2s",
"& .MuiInputBase-root": {
"& .MuiInputBase-input": {
color: theme.palette.grey2.main,
fontSize: "16px",
padding: "0 3px",
borderRadius: "3px",
border: "1px solid",
borderColor: "transparent",
"&:hover, &:focus": {
borderColor: theme.palette.grey2.main,
},
});
}}
/>
))}
},
"& .MuiOutlinedInput-notchedOutline": {
outline: "none",
border: "none",
},
},
}}
/>
<Typography
ref={positiveRef}
sx={{
position: "absolute",
opacity: 0,
zIndex: "-100",
whiteSpace: "nowrap",
fontSize: "16px",
}}
>
{positiveText}
</Typography>
<TextField
value={positiveText}
placeholder="Позитивно"
onChange={({ target }) => {
if (target.value.length <= 15) {
setPositiveText(target.value);
debouncePositiveDescription(target.value);
}
}}
onBlur={({ target }) => debouncePositiveDescription(target.value)}
sx={{
width: positiveTextWidth + 10 + "px",
background: "transparent",
fontSize: "18px",
minWidth: "95px",
maxWidth: "230px",
transition: "0.2s",
"& .MuiInputBase-root": {
"& .MuiInputBase-input": {
color: theme.palette.grey2.main,
fontSize: "16px",
padding: "0 3px",
borderRadius: "3px",
border: "1px solid",
borderColor: "transparent",
"&:hover, &:focus": {
borderColor: theme.palette.grey2.main,
},
},
"& .MuiOutlinedInput-notchedOutline": {
outline: "none",
border: "none",
},
},
}}
/>
</Box>
</Box>
<ButtonsOptions switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} />
<SwitchRating switchState={switchState} totalIndex={totalIndex} />

@ -120,15 +120,18 @@ export default function SettingSlider({ totalIndex }: SettingSliderProps) {
</Typography>
<Slider
value={listQuestions[quizId][totalIndex].content.steps}
min={1}
min={2}
max={10}
aria-label="Default"
valueLabelDisplay="auto"
sx={{ color: theme.palette.brightPurple.main, padding: "0" }}
onChange={(_, value) => {
const clonContent = listQuestions[quizId][totalIndex].content;
clonContent.steps = Number(value) || 1;
updateQuestionsList(quizId, totalIndex, { content: clonContent });
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
steps: Number(value) || 1,
},
});
}}
/>
</Box>
@ -157,7 +160,13 @@ export default function SettingSlider({ totalIndex }: SettingSliderProps) {
});
}}
/>
<Box sx={{ width: isMobile ? "90%" : "auto", display: "flex", alignItems: "center" }}>
<Box
sx={{
width: isMobile ? "90%" : "auto",
display: "flex",
alignItems: "center",
}}
>
<CustomCheckbox
sx={{ mr: isMobile ? "0px" : "16px" }}
label={"Внутреннее название вопроса"}

@ -47,10 +47,14 @@ export default function SliderOptions({ totalIndex }: Props) {
placeholder={"0"}
min={0}
max={99}
value={listQuestions[quizId][totalIndex].content.range.split("—")[0]}
value={
listQuestions[quizId][totalIndex].content.range.split("—")[0]
}
onChange={({ target }) => {
const clonContent = listQuestions[quizId][totalIndex].content;
clonContent.range = `${target.value}${listQuestions[quizId][totalIndex].content.range.split("—")[1]}`;
clonContent.range = `${target.value}${
listQuestions[quizId][totalIndex].content.range.split("—")[1]
}`;
updateQuestionsList(quizId, totalIndex, {
content: clonContent,
});
@ -58,12 +62,16 @@ export default function SliderOptions({ totalIndex }: Props) {
onBlur={({ target }) => {
const start = listQuestions[quizId][totalIndex].content.start;
const min = Number(target.value);
const max = Number(listQuestions[quizId][totalIndex].content.range.split("—")[1]);
const max = Number(
listQuestions[quizId][totalIndex].content.range.split("—")[1]
);
if (min >= max) {
const clonContent = listQuestions[quizId][totalIndex].content;
clonContent.range = `${max - 1 >= 0 ? max - 1 : 0}${
listQuestions[quizId][totalIndex].content.range.split("—")[1]
listQuestions[quizId][totalIndex].content.range.split(
"—"
)[1]
}`;
updateQuestionsList(quizId, totalIndex, {
content: clonContent,
@ -86,10 +94,14 @@ export default function SliderOptions({ totalIndex }: Props) {
placeholder={"100"}
min={0}
max={100}
value={listQuestions[quizId][totalIndex].content.range.split("—")[1]}
value={
listQuestions[quizId][totalIndex].content.range.split("—")[1]
}
onChange={({ target }) => {
const clonContent = listQuestions[quizId][totalIndex].content;
clonContent.range = `${listQuestions[quizId][totalIndex].content.range.split("—")[0]}${target.value}`;
clonContent.range = `${
listQuestions[quizId][totalIndex].content.range.split("—")[0]
}${target.value}`;
updateQuestionsList(quizId, totalIndex, {
content: clonContent,
});
@ -97,15 +109,19 @@ export default function SliderOptions({ totalIndex }: Props) {
onBlur={({ target }) => {
const start = listQuestions[quizId][totalIndex].content.start;
const step = listQuestions[quizId][totalIndex].content.step;
const min = Number(listQuestions[quizId][totalIndex].content.range.split("—")[0]);
const min = Number(
listQuestions[quizId][totalIndex].content.range.split("—")[0]
);
const max = Number(target.value);
const range = max - min;
if (max <= min) {
const clonContent = listQuestions[quizId][totalIndex].content;
clonContent.range = `${listQuestions[quizId][totalIndex].content.range.split("—")[0]}${
min + 1 >= 100 ? 100 : min + 1
}`;
clonContent.range = `${
listQuestions[quizId][totalIndex].content.range.split(
"—"
)[0]
}${min + 1 >= 100 ? 100 : min + 1}`;
updateQuestionsList(quizId, totalIndex, {
content: clonContent,
});
@ -129,7 +145,11 @@ export default function SliderOptions({ totalIndex }: Props) {
});
if (range % step) {
setStepError(`Шаг должен делить без остатка диапазон ${max} - ${min} = ${max - min}`);
setStepError(
`Шаг должен делить без остатка диапазон ${max} - ${min} = ${
max - min
}`
);
} else {
setStepError("");
}
@ -154,8 +174,12 @@ export default function SliderOptions({ totalIndex }: Props) {
<CustomNumberField
sx={{ maxWidth: "310px", width: "100%" }}
placeholder={"50"}
min={Number(listQuestions[quizId][totalIndex].content.range.split("—")[0])}
max={Number(listQuestions[quizId][totalIndex].content.range.split("—")[1])}
min={Number(
listQuestions[quizId][totalIndex].content.range.split("—")[0]
)}
max={Number(
listQuestions[quizId][totalIndex].content.range.split("—")[1]
)}
value={String(listQuestions[quizId][totalIndex].content.start)}
onChange={({ target }) => {
const clonContent = listQuestions[quizId][totalIndex].content;
@ -167,7 +191,16 @@ export default function SliderOptions({ totalIndex }: Props) {
/>
</Box>
<Box sx={{ width: "100%" }}>
<Typography sx={{ fontWeight: "500", fontSize: "18px", color: "#4D4D4D", mb: "10px" }}>Шаг</Typography>
<Typography
sx={{
fontWeight: "500",
fontSize: "18px",
color: "#4D4D4D",
mb: "10px",
}}
>
Шаг
</Typography>
<CustomNumberField
sx={{ maxWidth: "310px", width: "100%" }}
min={0}
@ -183,8 +216,12 @@ export default function SliderOptions({ totalIndex }: Props) {
});
}}
onBlur={({ target }) => {
const min = Number(listQuestions[quizId][totalIndex].content.range.split("—")[0]);
const max = Number(listQuestions[quizId][totalIndex].content.range.split("—")[1]);
const min = Number(
listQuestions[quizId][totalIndex].content.range.split("—")[0]
);
const max = Number(
listQuestions[quizId][totalIndex].content.range.split("—")[1]
);
const range = max - min;
const step = Number(target.value);
@ -198,7 +235,11 @@ export default function SliderOptions({ totalIndex }: Props) {
}
if (range % step) {
setStepError(`Шаг должен делить без остатка диапазон ${max} - ${min} = ${max - min}`);
setStepError(
`Шаг должен делить без остатка диапазон ${max} - ${min} = ${
max - min
}`
);
} else {
setStepError("");
}
@ -207,7 +248,11 @@ export default function SliderOptions({ totalIndex }: Props) {
</Box>
</Box>
</Box>
<ButtonsOptions switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} />
<ButtonsOptions
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchSlider switchState={switchState} totalIndex={totalIndex} />
</>
);

@ -81,7 +81,13 @@ export default function SettingSlider({ totalIndex }: SettingSliderProps) {
});
}}
/>
<Box sx={{ width: isMobile ? "90%" : "auto", display: "flex", alignItems: "center" }}>
<Box
sx={{
width: isMobile ? "90%" : "auto",
display: "flex",
alignItems: "center",
}}
>
<CustomCheckbox
sx={{ mr: isMobile ? "0px" : "16px", whiteSpace: isFigmaTablte ? "nowrap" : "" }}
label={"Внутреннее название вопроса"}

@ -101,7 +101,6 @@ export default function TypeQuestions({ totalIndex }: Props) {
<QuestionsMiniButton
key={title}
onClick={() => {
console.log(value);
updateQuestionsList(quizId, totalIndex, { type: value });
}}
icon={icon}

@ -25,7 +25,7 @@ export default function AnswerOptions({ totalIndex }: Props) {
const addNewAnswer = () => {
const answerNew = variants.slice();
answerNew.push({ answer: "", hints: "" });
answerNew.push({ answer: "", hints: "", emoji: "" });
updateQuestionsList(quizId, totalIndex, {
content: {
@ -92,7 +92,11 @@ export default function AnswerOptions({ totalIndex }: Props) {
)}
</Box>
</Box>
<ButtonsOptionsAndPict switchState={switchState} SSHC={SSHC} totalIndex={totalIndex} />
<ButtonsOptionsAndPict
switchState={switchState}
SSHC={SSHC}
totalIndex={totalIndex}
/>
<SwitchAnswerOptions switchState={switchState} totalIndex={totalIndex} />
</>
);

@ -32,12 +32,12 @@ const ANSWERS = ["Ответ 1", "Ответ 2", "Ответ 3"];
const CONDITIONS = ["Все условия обязательны", "Обязательно хотя бы одно условие"];
export default function BranchingQuestions({ totalIndex }: BranchingQuestionsProps) {
const [title, setTitle] = useState<string>("Заголовок вопроса");
const theme = useTheme();
const [titleInputWidth, setTitleInputWidth] = useState<number>(0);
const quizId = Number(useParams().quizId);
const { openedModalSettings, listQuestions } = questionStore();
const theme = useTheme();
const titleRef = useRef<HTMLDivElement>(null);
const [title, setTitle] = useState<string>(listQuestions[quizId][totalIndex].title)
useEffect(() => {
setTitleInputWidth(titleRef.current?.offsetWidth || 0);
@ -96,15 +96,18 @@ export default function BranchingQuestions({ totalIndex }: BranchingQuestionsPro
<input
type="text"
value={title}
placeholder="Заголовок вопроса"
onChange={({ target }) => setTitle(target.value)}
style={{
width: titleInputWidth,
width: titleInputWidth ? titleInputWidth : 170,
outline: "none",
background: "transparent",
border: "none",
fontSize: "18px",
minWidth: "160px",
minWidth: "50px",
maxWidth: "500px",
fontFamily: "Rubik",
transition: ".2s",
}}
/>
</Box>

@ -12,4 +12,4 @@ export const ChartLineUp: FC<SVGProps<SVGSVGElement>> = (props) => (
/>
<path d="M19.5 9.75V6H15.75" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
);

@ -13,4 +13,4 @@ export const Pencil: FC<SVGProps<SVGSVGElement>> = (props) => (
<path d="M8.95312 20.2031L3.79688 15.0469" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
<path d="M3.5 23.5H21" stroke="currentColor" strokeLinecap="round" />
</svg>
);
);

@ -7,4 +7,4 @@ export const People: FC<SVGProps<SVGSVGElement>> = (props) => (
fill="currentColor"
/>
</svg>
);
);

@ -11,4 +11,4 @@ export const Question: FC<SVGProps<SVGSVGElement>> = (props) => (
/>
<circle cx="10.8367" cy="15.5652" r="0.825" fill="currentColor" />
</svg>
);
);

@ -10,4 +10,4 @@ export const ReturnTime: FC<SVGProps<SVGSVGElement>> = (props) => (
stroke-linejoin="round"
/>
</svg>
);
);

@ -21,4 +21,4 @@ export const Settings: FC<SVGProps<SVGSVGElement>> = (props) => (
strokeLinejoin="round"
/>
</svg>
);
);

@ -1,6 +1,6 @@
import Stepper from "@ui_kit/Stepper";
import SwitchStepPages from "@ui_kit/switchStepPages";
import React from "react";
import React, { useState } from "react";
import PenaLogo from "@ui_kit/PenaLogo";
import { Box, Button, Container, FormControl, IconButton, TextField, useMediaQuery, useTheme } from "@mui/material";
import BackArrowIcon from "@icons/BackArrowIcon";
@ -22,7 +22,7 @@ export default function StartPage() {
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(660));
const [mobileSidebar, setMobileSidebar] = React.useState<boolean>(false);
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
const handleNext = () => {
updateQuizesList(params, { step: listQuizes[params].step + 1 });

@ -68,7 +68,6 @@ export default function StartPageSettings({ handleNext }: HandleNext) {
const theme = useTheme();
const designType = listQuizes[params].startpage;
const StartPageClone = listQuizes[params];
console.log(StartPageClone);
const [backgroundType, setBackgroundType] = useState(
listQuizes[params].config.startpage.background.type
);
@ -616,7 +615,6 @@ export default function StartPageSettings({ handleNext }: HandleNext) {
label="Кликабельный"
checked={listQuizes[params].config.info.clickable}
handleChange={(e) => {
console.log(e.target.checked);
let SPageClone = listQuizes[params].config;
SPageClone.info.clickable = e.target.checked;
updateQuizesList(params, { config: SPageClone });

@ -28,22 +28,18 @@ export default ({ text, sx, heightImg, widthImg }: Props) => {
const [ready, setReady] = useState(false);
const dragenterHC = () => {
// console.log("onDragEnter")
setReady(true);
};
const dragexitHC = () => {
// console.log("onDragExit")
setReady(false);
};
const dropHC = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
// console.log("onDrop")
setReady(false);
const file = event.dataTransfer.files[0];
console.log(event.dataTransfer.files[0]);
if (file.size < 5242880) {
setData(URL.createObjectURL(file));
} else {
@ -53,7 +49,6 @@ export default ({ text, sx, heightImg, widthImg }: Props) => {
const dragOverHC = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
// console.log("onDragOver")
};
return (

@ -146,14 +146,11 @@ export default ({ text, sx, heightImg, widthImg }: Props) => {
const [favList, setFavList] = useState<string[]>([]);
useEffect(() => {
console.log(favList);
if (favList.length === 18) {
console.log("сейчас я сработаю");
const zip = new JSZip(); //создание зип архива
favList.forEach((uri, i) => {
console.log(i);
const idx = uri.indexOf("base64,") + "base64,".length; //обработка строки картинки
const content = uri.substring(idx); //обработка строки картинки
zip.file(`fav${i}.jpg`, content, { base64: true }); //сохранение картинки в архив с именем "fav.jpg"
@ -201,46 +198,26 @@ export default ({ text, sx, heightImg, widthImg }: Props) => {
const [ready, setReady] = useState(false);
const dragenterHC = () => {
// console.log("onDragEnter")
setReady(true);
};
const dragexitHC = () => {
// console.log("onDragExit")
setReady(false);
};
const dropHC = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
// console.log("onDrop")
setReady(false);
const file = event.dataTransfer.files[0];
console.log(event.dataTransfer.files[0]);
if (file.size < 5242880) {
setData(URL.createObjectURL(file));
} else {
enqueueSnackbar("Размер картинки слишком велик");
}
// try {
// Resizer.imageFileResizer(
// file,
// 50,
// 50,
// "JPEG",
// 100,
// 0,
// callback,
// );
//
// } catch (err) {
// console.log(err);
// }
//
};
const dragOverHC = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
// console.log("onDragOver")
};
return (

@ -13,10 +13,8 @@ export default function StepOne({ handleNext }: HandleNext) {
const theme = useTheme();
const params = Number(useParams().quizId);
console.log(params)
const {listQuizes, updateQuizesList,} = quizStore()
console.log(listQuizes)
return (
<>
<Box

@ -4,6 +4,7 @@ import { persist } from "zustand/middleware";
export type Variants = {
answer: string;
hints: string;
emoji: string;
};
type Hint = {
@ -28,7 +29,8 @@ export interface Question {
description: string;
type: string;
required: boolean;
deleted: true;
deleted: boolean;
deleteTimeoutId: number;
page: number;
content: {
variants: Variants[];
@ -63,8 +65,8 @@ export interface Question {
chooseRange: boolean;
required: boolean;
replText: string;
ratingExpanded: boolean;
ratingDescription: string;
ratingNegativeDescription: string;
ratingPositiveDescription: string;
};
version: number;
parent_ids: number[];
@ -83,7 +85,8 @@ export const DEFAULT_QUESTION: Omit<Question, "id"> = {
description: "",
type: "",
required: true,
deleted: true,
deleted: false,
deleteTimeoutId: 0,
page: 0,
content: {
largeCheck: false,
@ -115,12 +118,13 @@ export const DEFAULT_QUESTION: Omit<Question, "id"> = {
chooseRange: false,
required: false,
replText: "",
ratingExpanded: false,
ratingDescription: "",
ratingNegativeDescription: "",
ratingPositiveDescription: "",
variants: [
{
answer: "",
hints: "",
emoji: "",
},
],
hint: {
@ -145,15 +149,35 @@ export const DEFAULT_QUESTION: Omit<Question, "id"> = {
expanded: false,
};
let isFirstPartialize = true;
export const questionStore = create<QuestionStore>()(
persist<QuestionStore>(
() => ({
listQuestions: {},
openedModalSettings: "",
}),
{
name: "question",
partialize: (state: QuestionStore) => {
if (isFirstPartialize) {
isFirstPartialize = false;
Object.keys(state.listQuestions).forEach((quizId) => {
[...state.listQuestions[quizId]].forEach(({ id, deleted }) => {
if (deleted) {
const removedItemIndex = state.listQuestions[quizId].findIndex(
(item) => item.id === id
);
state.listQuestions[quizId].splice(removedItemIndex, 1);
}
});
});
}
return state;
},
}
)
);
@ -220,11 +244,18 @@ export const copyQuestion = (quizId: number, copiedQuestionIndex: number) => {
questionStore.setState({ listQuestions });
};
export const removeQuestionForce = (quizId: number, removedId: number) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
const removedItemIndex = questionListClone[quizId].findIndex(
({ id }) => id === removedId
);
questionListClone[quizId].splice(removedItemIndex, 1);
questionStore.setState({ listQuestions: questionListClone });
};
export const removeQuestion = (quizId: number, index: number) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
questionListClone[quizId].splice(index, 1);
questionListClone[quizId][index].deleted = true;
questionStore.setState({ listQuestions: questionListClone });
};

@ -0,0 +1,26 @@
import EmojiPickerOriginal from "@emoji-mart/react";
import { Box } from "@mui/material";
type Emoji = {
emoticons: string[];
id: string;
keywords: string[];
name: string;
native: string;
shortcodes: string;
unified: string;
};
type EmojiPickerProps = {
onEmojiSelect: (emoji: Emoji) => void;
};
export const EmojiPicker = ({ onEmojiSelect }: EmojiPickerProps) => (
<Box sx={{ minWidth: "352px" }}>
<EmojiPickerOriginal
onEmojiSelect={onEmojiSelect}
theme="light"
locale="ru"
/>
</Box>
);

@ -155,9 +155,6 @@ export const CropModal: FC<Iprops> = ({ opened, onClose, picture }) => {
const aspect = imageWidth / imageHeight;
console.log(aspect);
console.log(width);
if (aspect <= 1.333) {
setWidth(240);

@ -15,139 +15,143 @@ import LayoutIcon from "../assets/icons/LayoutIcon";
import MenuItem from "./MenuItem";
const createQuizMenuItems = [
[LayoutIcon, "Стартовая страница"],
[QuestionIcon, "Вопросы"],
[ChartPieIcon, "Результаты"],
[ContactBookIcon, "Форма контактов"],
[FlowArrowIcon, "Установка квиза"],
[MegaphoneIcon, "Запуск рекламы"],
[LayoutIcon, "Стартовая страница"],
[QuestionIcon, "Вопросы"],
[ChartPieIcon, "Результаты"],
[ContactBookIcon, "Форма контактов"],
[FlowArrowIcon, "Установка квиза"],
[MegaphoneIcon, "Запуск рекламы"],
] as const;
const quizSettingsMenuItems = [
[TagIcon, "Дополнения"],
[PencilCircleIcon, "Дизайн"],
[PuzzlePieceIcon, "Интеграции"],
[GearIcon, "Настройки"],
[TagIcon, "Дополнения"],
[PencilCircleIcon, "Дизайн"],
[PuzzlePieceIcon, "Интеграции"],
[GearIcon, "Настройки"],
] as const;
export default function Sidebar() {
const theme = useTheme();
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
const [activeMenuItemIndex, setActiveMenuItemIndex] = useState<number>(0);
const [progress, setProgress] = useState<number>(1 / 6);
const theme = useTheme();
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
const [activeMenuItemIndex, setActiveMenuItemIndex] = useState<number>(0);
const [progress, setProgress] = useState<number>(1 / 6);
const handleMenuCollapseToggle = () => setIsMenuCollapsed(prev => !prev);
return (
<Box
sx={{
backgroundColor: theme.palette.lightPurple.main,
minWidth: isMenuCollapsed ? "80px" : "230px",
width: isMenuCollapsed ? "80px" : "230px",
height: 'calc(100vh - 80px)',
display: "flex",
flexDirection: "column",
py: "19px",
transitionProperty: "width, min-width",
transitionDuration: "200ms",
overflow: "hidden",
whiteSpace: "nowrap",
boxSizing: "border-box"
}}
>
<Box
sx={{
display: "flex",
pl: isMenuCollapsed ? undefined : "16px",
pr: isMenuCollapsed ? undefined : "8px",
mb: isMenuCollapsed ? "5px" : undefined,
alignItems: "center",
justifyContent: isMenuCollapsed ? "center" : undefined,
}}
>
{!isMenuCollapsed &&
<Typography
sx={{
fontSize: "14px",
lineHeight: "20px",
fontWeight: 500,
color: theme.palette.grey2.main,
}}
>Создание квиза</Typography>
}
<IconButton onClick={handleMenuCollapseToggle} sx={{ ml: isMenuCollapsed ? undefined : "auto" }}>
<CollapseMenuIcon height="16px" width="16px" color={theme.palette.grey2.main} transform={isMenuCollapsed ? "rotate(180deg)" : ""} />
</IconButton>
</Box>
<List disablePadding>
{createQuizMenuItems.map((menuItem, index) => {
const Icon = menuItem[0];
return (
<MenuItem
onClick={() => setActiveMenuItemIndex(index)}
key={menuItem[1]}
text={menuItem[1]}
isCollapsed={isMenuCollapsed}
isActive={activeMenuItemIndex === index}
icon={<Icon
color={activeMenuItemIndex === index ?
theme.palette.brightPurple.main
:
isMenuCollapsed ?
"white"
:
theme.palette.grey2.main
}
height={isMenuCollapsed ? "35px" : "24px"}
width={isMenuCollapsed ? "35px" : "24px"}
/>}
/>
);
})}
</List>
{!isMenuCollapsed &&
<Typography
sx={{
px: "16px",
mt: "16px",
mb: "11px",
fontSize: "14px",
lineHeight: "20px",
fontWeight: 500,
color: theme.palette.grey2.main,
}}
>Настройки квиза</Typography>
}
<List disablePadding>
{quizSettingsMenuItems.map((menuItem, index) => {
const Icon = menuItem[0];
const totalIndex = index + createQuizMenuItems.length;
const isActive = activeMenuItemIndex === totalIndex;
return (
<MenuItem
onClick={() => setActiveMenuItemIndex(totalIndex)}
key={menuItem[1]}
text={menuItem[1]}
isActive={isActive}
isCollapsed={isMenuCollapsed}
icon={<Icon
color={isActive ?
theme.palette.brightPurple.main
:
isMenuCollapsed ?
"white"
:
theme.palette.grey2.main
}
height={isMenuCollapsed ? "35px" : "24px"}
width={isMenuCollapsed ? "35px" : "24px"}
/>}
/>
);
})}
</List>
</Box>
)
}
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
return (
<Box
sx={{
backgroundColor: theme.palette.lightPurple.main,
minWidth: isMenuCollapsed ? "80px" : "230px",
width: isMenuCollapsed ? "80px" : "230px",
height: "calc(100vh - 80px)",
display: "flex",
flexDirection: "column",
py: "19px",
transitionProperty: "width, min-width",
transitionDuration: "200ms",
overflow: "hidden",
whiteSpace: "nowrap",
boxSizing: "border-box",
}}
>
<Box
sx={{
display: "flex",
pl: isMenuCollapsed ? undefined : "16px",
pr: isMenuCollapsed ? undefined : "8px",
mb: isMenuCollapsed ? "5px" : undefined,
alignItems: "center",
justifyContent: isMenuCollapsed ? "center" : undefined,
}}
>
{!isMenuCollapsed && (
<Typography
sx={{
fontSize: "14px",
lineHeight: "20px",
fontWeight: 500,
color: theme.palette.grey2.main,
}}
>
Создание квиза
</Typography>
)}
<IconButton onClick={handleMenuCollapseToggle} sx={{ ml: isMenuCollapsed ? undefined : "auto" }}>
<CollapseMenuIcon
height="16px"
width="16px"
color={theme.palette.grey2.main}
transform={isMenuCollapsed ? "rotate(180deg)" : ""}
/>
</IconButton>
</Box>
<List disablePadding>
{createQuizMenuItems.map((menuItem, index) => {
const Icon = menuItem[0];
return (
<MenuItem
onClick={() => setActiveMenuItemIndex(index)}
key={menuItem[1]}
text={menuItem[1]}
isCollapsed={isMenuCollapsed}
isActive={activeMenuItemIndex === index}
icon={
<Icon
color={
activeMenuItemIndex === index
? theme.palette.brightPurple.main
: isMenuCollapsed
? "white"
: theme.palette.grey2.main
}
height={isMenuCollapsed ? "35px" : "24px"}
width={isMenuCollapsed ? "35px" : "24px"}
/>
}
/>
);
})}
</List>
{!isMenuCollapsed && (
<Typography
sx={{
px: "16px",
mt: "16px",
mb: "11px",
fontSize: "14px",
lineHeight: "20px",
fontWeight: 500,
color: theme.palette.grey2.main,
}}
>
Настройки квиза
</Typography>
)}
<List disablePadding>
{quizSettingsMenuItems.map((menuItem, index) => {
const Icon = menuItem[0];
const totalIndex = index + createQuizMenuItems.length;
const isActive = activeMenuItemIndex === totalIndex;
return (
<MenuItem
onClick={() => setActiveMenuItemIndex(totalIndex)}
key={menuItem[1]}
text={menuItem[1]}
isActive={isActive}
isCollapsed={isMenuCollapsed}
icon={
<Icon
color={
isActive ? theme.palette.brightPurple.main : isMenuCollapsed ? "white" : theme.palette.grey2.main
}
height={isMenuCollapsed ? "35px" : "24px"}
width={isMenuCollapsed ? "35px" : "24px"}
/>
}
/>
);
})}
</List>
</Box>
);
}

@ -51,7 +51,6 @@ class Resizer {
Yoffset = Math.round((size - height) / 2)
}
console.log(height, width)
ctx.drawImage(image, Xoffset, Yoffset, width, height);

@ -1218,6 +1218,16 @@
resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz"
integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==
"@emoji-mart/data@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@emoji-mart/data/-/data-1.1.2.tgz#777c976f8f143df47cbb23a7077c9ca9fe5fc513"
integrity sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg==
"@emoji-mart/react@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@emoji-mart/react/-/react-1.1.1.tgz#ddad52f93a25baf31c5383c3e7e4c6e05554312a"
integrity sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==
"@emotion/babel-plugin@^11.10.5":
version "11.10.5"
resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz"
@ -4163,6 +4173,11 @@ emittery@^0.8.1:
resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz"
integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==
emoji-mart@^5.5.2:
version "5.5.2"
resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.5.2.tgz#3ddbaf053139cf4aa217650078bc1c50ca8381af"
integrity sha512-Sqc/nso4cjxhOwWJsp9xkVm8OF5c+mJLZJFoFfzRuKO+yWiN7K8c96xmtughYb0d/fZ8UC6cLIQ/p4BR6Pv3/A==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz"