Merge branch 'dev' into 'main'

Dev

See merge request frontend/squiz!99
This commit is contained in:
Nastya 2023-12-29 17:47:09 +00:00
commit c36b51c174
34 changed files with 1400 additions and 735 deletions

@ -5,6 +5,7 @@ import "dayjs/locale/ru";
import SigninDialog from "./pages/auth/Signin";
import SignupDialog from "./pages/auth/Signup";
import { ViewPage } from "./pages/ViewPublicationPage";
import { DesignPage } from "./pages/startPage/DesignPage";
import { Route, Routes, useLocation, useNavigate, Navigate } from "react-router-dom";
import "./index.css";
import ContactFormPage from "./pages/ContactFormPage/ContactFormPage";
@ -139,6 +140,7 @@ export default function App() {
<Route path="edit" element={<EditPage />} />
<Route path="crop" element={<ImageCrop />} />
<Route path="/view" element={<ViewPage />} />
<Route path="/design" element={<DesignPage />} />
</Route>
</Routes>
</>

@ -0,0 +1,33 @@
export const TreeStructure = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 19 19" fill="none">
<path
d="M4.72656 7.53125H1.91406C1.6034 7.53125 1.35156 7.78309 1.35156 8.09375V10.9062C1.35156 11.2169 1.6034 11.4688 1.91406 11.4688H4.72656C5.03722 11.4688 5.28906 11.2169 5.28906 10.9062V8.09375C5.28906 7.78309 5.03722 7.53125 4.72656 7.53125Z"
stroke="#9A9AAF"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.8516 2.36513H11.4766C11.1659 2.36513 10.9141 2.61697 10.9141 2.92763V6.30263C10.9141 6.61329 11.1659 6.86513 11.4766 6.86513H14.8516C15.1622 6.86513 15.4141 6.61329 15.4141 6.30263V2.92763C15.4141 2.61697 15.1622 2.36513 14.8516 2.36513Z"
stroke="#9A9AAF"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.8516 12.1349H11.4766C11.1659 12.1349 10.9141 12.3867 10.9141 12.6974V16.0724C10.9141 16.383 11.1659 16.6349 11.4766 16.6349H14.8516C15.1622 16.6349 15.4141 16.383 15.4141 16.0724V12.6974C15.4141 12.3867 15.1622 12.1349 14.8516 12.1349Z"
stroke="#9A9AAF"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path d="M5.28906 9.5H8.10156" stroke="#9A9AAF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
<path
d="M10.9141 14.3849H9.78908C9.56721 14.3858 9.34736 14.3428 9.1422 14.2583C8.93704 14.1738 8.75064 14.0496 8.59376 13.8927C8.43688 13.7358 8.31261 13.5494 8.22814 13.3443C8.14366 13.1391 8.10065 12.9192 8.10158 12.6974V6.30264C8.10065 6.08078 8.14366 5.86092 8.22814 5.65576C8.31261 5.45061 8.43688 5.26421 8.59376 5.10733C8.75064 4.95044 8.93704 4.82618 9.1422 4.7417C9.34736 4.65723 9.56721 4.61421 9.78908 4.61514H10.9141"
stroke="#9A9AAF"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);

@ -1,11 +1,11 @@
import { Box } from "@mui/material";
// interface Props {
// color: string;
// }
interface Props {
color: string;
}
export default function ArrowLeft() {
export default function ArrowLeft({color = "#7E2AEA"}: Props) {
return (
<Box
@ -16,8 +16,8 @@ export default function ArrowLeft() {
}}
>
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.75 12H4.25" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M11 5.25L4.25 12L11 18.75" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M20.75 12H4.25" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M11 5.25L4.25 12L11 18.75" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</Box>

@ -2,19 +2,14 @@ import { useTheme } from "@mui/material";
interface Props {
width: number;
color?: string;
}
export default function QuizLogo({ width }: Props) {
export default function QuizLogo({ width, color = "#151515" }: Props) {
const theme = useTheme();
return (
<svg
style={{ minWidth: width }}
width={width}
viewBox="0 0 149 58"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<svg style={{ minWidth: width }} width={width} viewBox="0 0 149 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_3258_71703)">
<path
fillRule="evenodd"
@ -26,23 +21,23 @@ export default function QuizLogo({ width }: Props) {
<circle cx="33.2733" cy="10.0479" r="1.27718" fill="#7E2AEA" />
<path
d="M53.705 26.1072C53.3326 22.4867 51.6293 19.1334 48.9253 16.6973C46.2212 14.2612 42.709 12.9158 39.0694 12.9218C38.5526 12.9221 38.0363 12.9491 37.5223 13.0028C33.9035 13.3828 30.5535 15.0893 28.1185 17.7933C25.6835 20.4973 24.3361 24.0071 24.3359 27.6458V27.6458V48.6801H30.6462V39.7405C33.1134 41.457 36.0481 42.3747 39.0536 42.3698C39.5704 42.3696 40.0867 42.3426 40.6007 42.2888C42.5237 42.0867 44.3881 41.5078 46.0874 40.5852C47.7867 39.6626 49.2876 38.4142 50.5045 36.9115C51.7213 35.4088 52.6303 33.6811 53.1794 31.8271C53.7286 29.9731 53.9072 28.0291 53.705 26.1061V26.1072ZM45.6005 32.9433C44.9074 33.8044 44.0502 34.5194 43.0788 35.0469C42.1074 35.5744 41.0409 35.9039 39.9413 36.0164C39.6464 36.047 39.3501 36.0625 39.0536 36.0627C37.1452 36.0609 35.2941 35.4104 33.8041 34.218C32.3142 33.0256 31.2738 31.362 30.8539 29.5004C30.434 27.6388 30.6594 25.6897 31.4931 23.9731C32.3269 22.2564 33.7195 20.8743 35.4423 20.0534C37.1652 19.2325 39.1159 19.0217 40.9743 19.4556C42.8327 19.8895 44.4884 20.9423 45.6696 22.4412C46.8509 23.9401 47.4875 25.796 47.4749 27.7044C47.4623 29.6127 46.8014 31.4601 45.6005 32.9433Z"
fill="#151515"
fill={color}
/>
<path
d="M70.1665 12.9218C69.6497 12.922 69.1333 12.9491 68.6194 13.0028C64.8697 13.3977 61.4144 15.216 58.9656 18.083C56.5168 20.95 55.2612 24.6472 55.4575 28.4125C55.6538 32.1779 57.2872 35.7244 60.0209 38.3212C62.7545 40.9179 66.3803 42.367 70.1507 42.3698C70.6674 42.3696 71.1838 42.3426 71.6978 42.2888C74.4013 42.001 76.973 40.9725 79.1295 39.3167C81.286 37.6609 82.9436 35.442 83.9197 32.9044H76.7271L76.6976 32.9412C75.7763 34.08 74.5737 34.9585 73.2086 35.4899C71.8436 36.0212 70.3635 36.187 68.9147 35.9708C67.4659 35.7546 66.0987 35.1639 64.9483 34.2572C63.7979 33.3505 62.9041 32.1591 62.3554 30.8009H84.5466C85.0138 28.6512 84.9941 26.4241 84.489 24.2829C83.9839 22.1417 83.0061 20.1407 81.6273 18.4264C80.2485 16.7121 78.5037 15.328 76.5206 14.3756C74.5375 13.4231 72.3664 12.9264 70.1665 12.9218V12.9218ZM62.3554 24.4907C62.9298 23.0767 63.8772 21.8452 65.0964 20.9272C66.3157 20.0093 67.7612 19.4394 69.2788 19.2784C69.5737 19.2477 69.87 19.2323 70.1665 19.2321C71.8425 19.2293 73.481 19.7288 74.8705 20.6661C76.2599 21.6034 77.3367 22.9355 77.9618 24.4907H62.3554Z"
fill="#151515"
fill={color}
/>
<path
d="M100.135 12.9218C97.7229 12.918 95.357 13.5812 93.2986 14.8381V12.9218H86.9883V42.3698H93.2986V26.0683C93.2986 24.2552 94.0188 22.5164 95.3008 21.2344C96.5829 19.9523 98.3216 19.2321 100.135 19.2321C101.948 19.2321 103.687 19.9523 104.969 21.2344C106.251 22.5164 106.971 24.2552 106.971 26.0683V42.3698H113.281V26.0683C113.281 22.5816 111.896 19.2378 109.431 16.7723C106.965 14.3069 103.621 12.9218 100.135 12.9218Z"
fill="#151515"
fill={color}
/>
<path
d="M144.833 29.5273V12.9218H138.523V15.5511C136.056 13.834 133.121 12.9161 130.116 12.9218C129.599 12.9221 129.083 12.9491 128.569 13.0028C124.819 13.3977 121.364 15.2161 118.915 18.083C116.466 20.95 115.21 24.6472 115.407 28.4126C115.603 32.1779 117.236 35.7244 119.97 38.3212C122.704 40.918 126.329 42.3671 130.1 42.3698C130.617 42.3696 131.133 42.3426 131.647 42.2888C135.314 41.9135 138.705 40.1663 141.139 37.3973C143.03 39.9275 145.769 41.6925 148.854 42.3698V35.7545C147.657 35.2122 146.641 34.3366 145.928 33.2323C145.215 32.128 144.835 30.8418 144.833 29.5273ZM136.647 32.9412C135.954 33.8021 135.096 34.517 134.125 35.0445C133.154 35.572 132.087 35.9016 130.988 36.0143C130.693 36.0449 130.396 36.0604 130.1 36.0606C127.944 36.0605 125.87 35.2328 124.307 33.7484C122.744 32.2639 121.81 30.2359 121.698 28.0829C121.586 25.9299 122.305 23.8161 123.707 22.1778C125.108 20.5395 127.085 19.5016 129.229 19.2784C129.524 19.2477 129.82 19.2323 130.116 19.2321C131.704 19.2335 133.26 19.6845 134.603 20.5331C135.946 21.3816 137.021 22.593 137.704 24.0271C138.388 25.4613 138.651 27.0595 138.464 28.637C138.277 30.2145 137.647 31.7067 136.647 32.9412Z"
fill="#151515"
fill={color}
/>
<path
d="M123.338 57.5645V54.1748C123.106 54.4753 122.816 54.7144 122.469 54.8921C122.122 55.0656 121.747 55.1523 121.345 55.1523C120.579 55.1523 119.949 54.8646 119.454 54.2891C118.87 53.6162 118.578 52.7043 118.578 51.5532C118.578 50.4699 118.851 49.6257 119.396 49.0205C119.947 48.4111 120.628 48.1064 121.44 48.1064C121.889 48.1064 122.276 48.2017 122.602 48.3921C122.932 48.5825 123.224 48.8703 123.478 49.2554V48.2588H125.122V57.5645H123.338ZM123.396 51.5469C123.396 50.8571 123.254 50.3451 122.97 50.0107C122.691 49.6722 122.34 49.5029 121.917 49.5029C121.485 49.5029 121.123 49.6743 120.831 50.0171C120.543 50.3599 120.399 50.9036 120.399 51.6484C120.399 52.389 120.539 52.9243 120.818 53.2544C121.098 53.5802 121.443 53.7432 121.853 53.7432C122.264 53.7432 122.623 53.5591 122.932 53.1909C123.241 52.8228 123.396 52.2747 123.396 51.5469ZM131.317 55V53.9907C131.072 54.3504 130.748 54.634 130.346 54.8413C129.948 55.0487 129.527 55.1523 129.083 55.1523C128.63 55.1523 128.224 55.0529 127.864 54.854C127.505 54.6551 127.244 54.3758 127.083 54.0161C126.923 53.6564 126.842 53.1592 126.842 52.5244V48.2588H128.626V51.3564C128.626 52.3044 128.658 52.8862 128.721 53.1021C128.789 53.3136 128.91 53.4829 129.083 53.6099C129.257 53.7326 129.477 53.7939 129.743 53.7939C130.048 53.7939 130.321 53.7114 130.562 53.5464C130.803 53.3771 130.968 53.1698 131.057 52.9243C131.146 52.6746 131.19 52.0674 131.19 51.1025V48.2588H132.974V55H131.317ZM134.828 47.3447V45.6943H136.611V47.3447H134.828ZM134.828 55V48.2588H136.611V55H134.828ZM137.729 55V53.6099L140.255 50.709C140.67 50.235 140.976 49.8986 141.175 49.6997C140.968 49.7124 140.695 49.7209 140.356 49.7251L137.976 49.7378V48.2588H143.549V49.522L140.972 52.4927L140.064 53.4766C140.56 53.4469 140.866 53.4321 140.985 53.4321H143.746V55H137.729Z"
fill="black"
fill={color}
/>
</g>
<defs>

@ -12,8 +12,12 @@ import {
useMediaQuery,
useTheme,
} from "@mui/material";
import { addQuestionVariant, deleteQuestionVariant, setQuestionVariantField } from "@root/questions/actions";
import type { KeyboardEvent, ReactNode } from "react";
import {
addQuestionVariant,
deleteQuestionVariant,
setQuestionVariantField,
} from "@root/questions/actions";
import type { ChangeEvent, KeyboardEvent, ReactNode } from "react";
import { useState } from "react";
import { Draggable } from "react-beautiful-dnd";
import type { QuestionVariant } from "../../../model/questionTypes/shared";
@ -78,28 +82,37 @@ export const AnswerItem = ({
focused={false}
placeholder={"Добавьте ответ"}
multiline={largeCheck}
onChange={({ target }) => {
onChange={({ target }: ChangeEvent<HTMLInputElement>) => {
setQuestionVariantAnswer(target.value || " ");
}}
onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => {
// if (disableKeyDown) {
// enqueueSnackbar("100 максимальное количество вопросов");
// } else if (event.code === "Enter" && !largeCheck) {
if (disableKeyDown) {
enqueueSnackbar("100 максимальное количество вопросов");
} else if (event.code === "Enter" && !largeCheck) {
addQuestionVariant(questionId);
// }
}
}}
InputProps={{
startAdornment: (
<>
<InputAdornment {...provided.dragHandleProps} position="start">
<PointsIcon style={{ color: "#9A9AAF", fontSize: "30px" }} />
<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}>
<IconButton
sx={{ padding: "0" }}
aria-describedby="my-popover-id"
onClick={handleClick}
>
<MessageIcon
style={{
color: "#9A9AAF",
@ -119,11 +132,20 @@ export const AnswerItem = ({
style={{ margin: "10px" }}
placeholder="Подсказка для этого ответа"
value={variant.hints}
onChange={(e) => setQuestionVariantAnswer(e.target.value || " ")}
onKeyDown={(event: KeyboardEvent<HTMLTextAreaElement>) => event.stopPropagation()}
onChange={(e) =>
setQuestionVariantAnswer(e.target.value || " ")
}
onKeyDown={(
event: KeyboardEvent<HTMLTextAreaElement>
) => event.stopPropagation()}
/>
</Popover>
<IconButton sx={{ padding: "0" }} onClick={() => deleteQuestionVariant(questionId, variant.id)}>
<IconButton
sx={{ padding: "0" }}
onClick={() =>
deleteQuestionVariant(questionId, variant.id)
}
>
<DeleteIcon
style={{
color: theme.palette.grey2.main,

@ -32,7 +32,7 @@ export const Select = ({
placeholder = "",
colorMain = "#7E2AEA",
colorPlaceholder = "#9A9AAF",
color
color,
}: SelectProps) => {
const [activeItem, setActiveItem] = useState<number>(
empty ? -1 : activeItemIndex
@ -94,6 +94,7 @@ export const Select = ({
mt: "8px",
p: "4px",
borderRadius: "8px",
backgroundColor: theme.palette.background.default,
border: "1px solid #EEE4FC",
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
},
@ -120,7 +121,7 @@ export const Select = ({
gap: "20px",
},
}}
IconComponent={(props) => <ArrowDown {...props } color={color}/>}
IconComponent={(props) => <ArrowDown {...props } />}
>
{items.map((item, index) => (
<MenuItem

@ -41,14 +41,14 @@ export const checkEmptyData = ({
}) => {
let check = true;
if (
resultData.title.length > 0 ||
resultData.description.length > 0 ||
resultData.content.back.length > 0 ||
resultData.content.originalBack.length > 0 ||
resultData.content.innerName.length > 0 ||
resultData.content.text.length > 0 ||
resultData.content.video.length > 0 ||
resultData.content.hint.text.length > 0
resultData.title?.length > 0 ||
resultData.description?.length > 0 ||
resultData.content.back?.length > 0 ||
resultData.content.originalBack?.length > 0 ||
resultData.content.innerName?.length > 0 ||
resultData.content.text?.length > 0 ||
resultData.content.video?.length > 0 ||
resultData.content.hint.text?.length > 0
)
check = false;
return check;

@ -429,6 +429,8 @@ function QuizPreviewLayoutByType({
sx={{
width: "60%",
overflow: "hidden",
display: "flex",
justifyContent: "center"
}}
>
{backgroundBlock}

@ -41,7 +41,7 @@ export const Page = ({ currentQuestion }: PageProps) => {
</Box>
) : (
<YoutubeEmbedIframe
containerSX={{ width: "100%", height: "100%", maxHeight: "80vh", objectFit: "contain" }}
containerSX={{ width: "100%", height: "calc( 100vh - 270px)", maxHeight: "80vh", objectFit: "contain" }}
videoUrl={currentQuestion.content.video}
/>
)}

@ -73,15 +73,10 @@ export const Rating = ({ currentQuestion }: RatingProps) => {
alignItems: "center",
gap: "20px",
marginTop: "20px",
flexDirection: "column",
width: isMobile ? "100%" : undefined
}}
>
<Typography sx={{
color: "#9A9AAF"
// color: theme.palette.grey2.main
}}>
{currentQuestion.content.ratingNegativeDescription}
</Typography>
<Box
sx={{
display: "inline-block",
@ -103,9 +98,17 @@ export const Rating = ({ currentQuestion }: RatingProps) => {
emptyIcon={form?.icon("#9A9AAF")}
/>
</Box>
<Typography sx={{ color: "#9A9AAF" }}>
{currentQuestion.content.ratingPositiveDescription}
</Typography>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
gap: 2,
width: "100%"
}}
>
<Typography sx={{ color: "#9A9AAF" }}>{currentQuestion.content.ratingNegativeDescription}</Typography>
<Typography sx={{ color: "#9A9AAF" }}>{currentQuestion.content.ratingPositiveDescription}</Typography>
</Box>
</Box>
</Box>
);

@ -0,0 +1,114 @@
import { quizApi } from "@api/quiz";
import { Box, useMediaQuery, useTheme } from "@mui/material";
import {
resetEditConfig,
setQuizes,
updateQuiz,
setCurrentStep,
} from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuizStore } from "@root/quizes/store";
import Sidebar from "@ui_kit/Sidebar";
import { enqueueSnackbar } from "notistack";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useDebouncedCallback } from "use-debounce";
import { SidebarMobile } from "./Sidebar/SidebarMobile";
import {
cleanQuestions,
createResult,
setQuestions,
} from "@root/questions/actions";
import {
updateCanCreatePublic,
updateModalInfoWhyCantCreate,
setShowConfirmLeaveModal,
updateSomeWorkBackend,
} from "@root/uiTools/actions";
import { useQuestionsStore } from "@root/questions/store";
import { questionApi } from "@api/question";
import { useUiTools } from "@root/uiTools/store";
import { clearUserData } from "@root/user";
import { clearAuthToken } from "@frontend/kitui";
import { logout } from "@api/auth";
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import { ModalInfoWhyCantCreate } from "./ModalInfoWhyCantCreate";
import { ConfirmLeaveModal } from "./ConfirmLeaveModal";
import { checkQuestionHint } from "@utils/checkQuestionHint";
import { Header } from "./Header";
export const DesignPage = () => {
const quiz = useCurrentQuiz();
const { editQuizId } = useQuizStore();
const { questions } = useQuestionsStore();
useEffect(() => {
const getData = async () => {
const quizes = await quizApi.getList();
setQuizes(quizes);
};
getData();
}, []);
const { showConfirmLeaveModal } = useUiTools();
const theme = useTheme();
const navigate = useNavigate();
const currentStep = useQuizStore((state) => state.currentStep);
const isMobile = useMediaQuery(theme.breakpoints.down(660));
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
const [nextStep, setNextStep] = useState<number>(0);
useEffect(
() => () => {
cleanQuestions();
updateModalInfoWhyCantCreate(false);
updateSomeWorkBackend(false);
},
[]
);
useEffect(() => {
if (editQuizId === null) navigate("/list");
}, [navigate, editQuizId]);
const followNewPage = () => {
setShowConfirmLeaveModal(false);
setCurrentStep(nextStep);
};
const changePage = (index: number) => {
if (currentStep === 2) {
setNextStep(index);
setShowConfirmLeaveModal(true);
return;
}
setCurrentStep(index);
};
console.log(quiz)
if (!quiz) return <></>;
return (
<>
<Header setMobileSidebar={setMobileSidebar} />
<Box sx={{ display: "flex" }}>
{isMobile ? (
<SidebarMobile open={mobileSidebar} changePage={changePage} />
) : (
<Sidebar changePage={changePage} />
)}
<Box>Страница дизайна</Box>
</Box>
<ModalInfoWhyCantCreate />
<ConfirmLeaveModal
open={showConfirmLeaveModal}
follow={followNewPage}
cancel={() => setShowConfirmLeaveModal(false)}
/>
</>
);
};

@ -18,7 +18,12 @@ import {
useMediaQuery,
useTheme,
} from "@mui/material";
import { decrementCurrentStep, resetEditConfig, setQuizes, updateQuiz, setCurrentStep } from "@root/quizes/actions";
import {
resetEditConfig,
setQuizes,
updateQuiz,
setCurrentStep,
} from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuizStore } from "@root/quizes/store";
import CustomAvatar from "@ui_kit/Header/Avatar";
@ -34,7 +39,11 @@ import { Link, useNavigate } from "react-router-dom";
import useSWR from "swr";
import { useDebouncedCallback } from "use-debounce";
import { SidebarMobile } from "./Sidebar/SidebarMobile";
import { cleanQuestions, createResult, setQuestions } from "@root/questions/actions";
import {
cleanQuestions,
createResult,
setQuestions,
} from "@root/questions/actions";
import {
updateOpenBranchingPanel,
updateCanCreatePublic,
@ -43,6 +52,7 @@ import {
updateSomeWorkBackend,
} from "@root/uiTools/actions";
import { BranchingPanel } from "../Questions/BranchingPanel";
import { Header } from "./Header";
import { useQuestionsStore } from "@root/questions/store";
import { useQuizes } from "@root/quizes/hooks";
import { questionApi } from "@api/question";
@ -60,6 +70,7 @@ import { deleteTimeoutedQuestions } from "@utils/deleteTimeoutedQuestions";
import { toggleQuizPreview } from "@root/quizPreview";
import { LinkSimple } from "@icons/LinkSimple";
import { BackButtonIcon } from "@icons/BackButtonIcon";
import { TreeStructure } from "@icons/TreeStructure";
let init: () => void;
export default function EditPage() {
@ -67,8 +78,6 @@ export default function EditPage() {
const { editQuizId } = useQuizStore();
const { questions } = useQuestionsStore();
console.log("quiz ", quiz);
console.log(questions);
useEffect(() => {
const getData = async () => {
const quizes = await quizApi.getList();
@ -82,7 +91,9 @@ export default function EditPage() {
if (
!questions?.find(
(q) => (q.type === "result" && q.content.includes(':"line"')) || q.content.includes(":'line'")
(q) =>
(q.type === "result" && q.content.includes(':"line"')) ||
q.content.includes(":'line'")
)
) {
createResult(quiz?.backendId, "line");
@ -93,20 +104,26 @@ export default function EditPage() {
getData();
}, []);
const { openBranchingPanel, whyCantCreatePublic, canCreatePublic, showConfirmLeaveModal } = useUiTools();
const {
openBranchingPanel,
whyCantCreatePublic,
canCreatePublic,
showConfirmLeaveModal,
} = useUiTools();
const theme = useTheme();
const navigate = useNavigate();
const currentStep = useQuizStore((state) => state.currentStep);
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isBranchingLogic = useMediaQuery(theme.breakpoints.down(1100));
const isMobile = useMediaQuery(theme.breakpoints.down(660));
const isLinkButton = useMediaQuery(theme.breakpoints.down(708));
const isMobileSm = useMediaQuery(theme.breakpoints.down(370));
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
const [nextStep, setNextStep] = useState<number>(0);
const quizConfig = quiz?.config;
const disableTest = quiz === undefined ? true : quiz.config.type === null;
const [openBranchingPage, setOpenBranchingPage] = useState<boolean>(false);
const [buttonText, setButtonText] = useState("Опубликовать");
const [buttonText, setButtonText] = useState(quiz?.status === "stop" ? "Опубликовать" : "Отозвать");
const openBranchingPageHC = () => {
if (!openBranchingPage) {
@ -121,15 +138,16 @@ export default function EditPage() {
useEffect(
() => () => {
resetEditConfig();
// resetEditConfig();
cleanQuestions();
updateModalInfoWhyCantCreate(false);
updateSomeWorkBackend(false)
updateSomeWorkBackend(false);
},
[]
);
const updateQuestionHint = useDebouncedCallback((questions: AnyTypedQuizQuestion[]) => {
const updateQuestionHint = useDebouncedCallback(
(questions: AnyTypedQuizQuestion[]) => {
const problems = checkQuestionHint(questions, quiz);
useUiTools.setState({ whyCantCreatePublic: problems });
if (Object.keys(problems).length > 0) {
@ -140,24 +158,14 @@ export default function EditPage() {
} else {
updateCanCreatePublic(true);
}
}, 600);
},
600
);
useEffect(() => {
updateQuestionHint(questions);
}, [questions]);
async function handleLogoutClick() {
const [, logoutError] = await logout();
if (logoutError) {
return enqueueSnackbar(logoutError);
}
clearAuthToken();
clearUserData();
navigate("/");
}
const followNewPage = () => {
setShowConfirmLeaveModal(false);
setCurrentStep(nextStep);
@ -165,8 +173,10 @@ export default function EditPage() {
if (!quizConfig) return <></>;
const isConditionMet = [1].includes(currentStep) && !openBranchingPanel && quizConfig.type !== "form";
const isConditionMet =
[1].includes(currentStep) &&
!openBranchingPanel &&
quizConfig.type !== "form";
const handleClickStatusQuiz = () => {
if (Object.keys(whyCantCreatePublic).length === 0) {
@ -174,7 +184,7 @@ export default function EditPage() {
setButtonText("Опубликовано");
setTimeout(() => {
setButtonText("Отозвать");
}, 3000);
}, 1500);
} else {
setButtonText("Опубликовать");
}
@ -187,141 +197,31 @@ export default function EditPage() {
}
};
const changePage = (index: number) => {
if (currentStep === 2) {
setNextStep(index);
setShowConfirmLeaveModal(true);
return;
}
setCurrentStep(index);
};
return (
<>
{/*хедер*/}
<Container
component="nav"
maxWidth={false}
disableGutters
sx={{
px: "16px",
display: "flex",
height: isMobile ? "51px" : "80px",
alignItems: "center",
bgcolor: isMobile ? "#333647" : "white",
borderBottom: "1px solid #E3E3E3",
zIndex: theme.zIndex.drawer + 1,
}}
>
<Link to="/" style={{ display: "flex" }}>
{isMobile ? <Logotip width={100} /> : <Logotip width={124} />}
</Link>
<Box
sx={{
display: isMobile ? "none" : "flex",
alignItems: "center",
ml: "37px",
}}
>
<Link to="/list">
<IconButton sx={{ p: "6px" }}>
<BackArrowIcon />
</IconButton>
</Link>
<FormControl fullWidth variant="standard">
<TextField
value={quiz.name}
onChange={(e) =>
updateQuiz(quiz.id, (quiz) => {
quiz.name = e.target.value;
})
}
fullWidth
id="project-name"
placeholder="Название проекта окно"
sx={{
width: "270px",
"& .MuiInputBase-root": {
height: "34px",
borderRadius: "8px",
p: 0,
},
}}
inputProps={{
sx: {
height: "20px",
borderRadius: "8px",
fontSize: "16px",
lineHeight: "20px",
p: "7px",
color: "black",
"&::placeholder": {
opacity: 1,
},
},
}}
/>
</FormControl>
</Box>
{isTablet ? (
<Box
sx={{
display: "flex",
ml: "auto",
}}
>
{isMobile ? (
<Burger
onClick={() => setMobileSidebar(!mobileSidebar)}
style={{ fontSize: "30px", color: "white", cursor: "pointer" }}
/>
) : (
<Box>
<CustomAvatar
sx={{
ml: "11px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/>
</Box>
)}
</Box>
) : (
<>
<Box
sx={{
display: "flex",
gap: "30px",
overflow: "hidden",
ml: "20px",
}}
>
<NavMenuItem text="Редактировать" isActive />
<NavMenuItem text="Заявки" />
<NavMenuItem text="Аналитика" />
<NavMenuItem text="История" />
<NavMenuItem text="Помощь" />
</Box>
<Box
sx={{
display: "flex",
ml: "auto",
gap: "15px",
}}
>
<CustomAvatar
sx={{
ml: "11px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/>
<LogoutButton onClick={handleLogoutClick} />
</Box>
</>
)}
</Container>
<Header setMobileSidebar={setMobileSidebar} />
<Box
sx={{
display: isMobile ? "block" : "flex",
position: "relative",
}}
>
{isMobile ? <SidebarMobile open={mobileSidebar} /> : <Sidebar setNextStep={setNextStep} />}
{isMobile ? (
<SidebarMobile open={mobileSidebar} changePage={changePage} />
) : (
<Sidebar changePage={changePage} />
)}
<Box
sx={{
background: theme.palette.background.default,
@ -333,7 +233,13 @@ export default function EditPage() {
sx={{
padding: isMobile ? "16px 16px 20px 16px" : "25px 25px 20px 25px",
overflow: "auto",
height: isMobile ? ` calc(100vh - 125px) ` : `calc(100vh - ${isConditionMet ? "186px" : "166px"})`,
height: isMobile
? `calc(100vh - 125px)`
: isConditionMet
? isBranchingLogic
? `calc(100vh - 166px)`
: `calc(100vh - 186px)`
: `calc(100vh - 166px)`,
boxSizing: "border-box",
}}
>
@ -357,14 +263,91 @@ export default function EditPage() {
width: "100%",
padding: isMobile ? "20px 16px" : "20px 20px",
display: "flex",
justifyContent: isMobile ? (isMobileSm ? "center" : "flex-end") : "flex-start",
justifyContent: isMobile
? isMobileSm
? "center"
: "flex-end"
: "flex-start",
flexDirection: isMobile ? "row-reverse" : "-moz-initial",
alignItems: "center",
gap: "15px",
background: "#FFF",
}}
>
{isConditionMet && (
{isConditionMet &&
(isBranchingLogic ? (
<Box
sx={{
width: "77px",
height: "51px",
position: "fixed",
zIndex: "99999",
right: "0",
top: "20%",
background: "#333647",
borderTopLeftRadius: "8px",
borderBottomLeftRadius: "8px",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<Switch
icon={<TreeStructure />}
checkedIcon={<TreeStructure />}
checked={openBranchingPage}
onChange={openBranchingPageHC}
sx={{
width: 50,
height: 30,
padding: 0,
"& .MuiSwitch-switchBase": {
padding: 0,
margin: "2px",
width: "26px",
height: "26px",
background: "#fff",
transitionDuration: "300ms",
"&:hover": {
backgroundColor: "#fff !important",
},
"&:active": {
background: "#fff",
},
"&.Mui-checked": {
transform: "translateX(20px)",
color: theme.palette.brightPurple.main,
"& + .MuiSwitch-track": {
backgroundColor: "#7E2AEA",
opacity: 1,
border: 0,
},
"&.Mui-disabled + .MuiSwitch-track": { opacity: 0.5 },
},
"&.Mui-disabled .MuiSwitch-thumb": {
color: theme.palette.mode === "light" ? theme.palette.grey[100] : theme.palette.grey[600],
},
"&.Mui-disabled + .MuiSwitch-track": {
opacity: theme.palette.mode === "light" ? 0.7 : 0.3,
},
},
"& .MuiSwitch-thumb": {
boxSizing: "border-box",
width: 25,
height: 25,
},
"& .MuiSwitch-track": {
backgroundColor: "rgba(217, 188, 255, 0.16)",
borderRadius: 13,
opacity: 1,
transition: theme.transitions.create(["background-color"], {
duration: 500,
}),
},
}}
/>
</Box>
) : (
<Box
sx={{
display: "flex",
@ -420,10 +403,12 @@ export default function EditPage() {
}}
/>
<Box>
<Typography sx={{ fontWeight: "bold", color: "#4D4D4D" }}>Логика ветвления</Typography>
<Typography sx={{ fontWeight: "bold", color: "#4D4D4D", whiteSpace: "nowrap" }}>
Логика ветвления
</Typography>
</Box>
</Box>
)}
))}
<Box sx={{ display: isMobile ? "none" : "block" }}>
{!canCreatePublic && quiz.config.type !== "form" ? (
@ -434,23 +419,31 @@ export default function EditPage() {
fontSize: "14px",
lineHeight: "18px",
height: "34px",
whiteSpace: "nowrap",
minWidth: "130px",
}}
onClick={() =>
Object.keys(whyCantCreatePublic).length === 0 ? () => {} : updateModalInfoWhyCantCreate(true)
Object.keys(whyCantCreatePublic).length === 0
? () => {}
: updateModalInfoWhyCantCreate(true)
}
>
Тестовый просмотр
</Button>
) : (
<a href={`/view`} target="_blank" rel="noreferrer" style={{ textDecoration: "none" }}>
<a
href={`/view`}
target="_blank"
rel="noreferrer"
style={{ textDecoration: "none" }}
>
<Button
variant="contained"
sx={{
fontSize: "14px",
whiteSpace: "nowrap",
lineHeight: "18px",
height: "34px",
minWidth: "130px",
}}
>
Тестовый просмотр
@ -466,6 +459,8 @@ export default function EditPage() {
lineHeight: "18px",
height: "34px",
background: buttonText === "Опубликовано" ? "#FA5B0E" : "#7E2AEA",
p: "0 18px",
minWidth: "120px",
}}
onClick={handleClickStatusQuiz}
>
@ -477,10 +472,14 @@ export default function EditPage() {
buttonText
)}
</Button>
{quiz?.status === "start" && (
{quiz?.status === "start" &&
(!isLinkButton ? (
<Box
component={Link}
sx={{
whiteSpace: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
display: isMobile ? "none" : "block",
color: "#7E2AEA",
fontSize: "14px",
@ -490,7 +489,27 @@ export default function EditPage() {
>
https://hbpn.link/{quiz.qid}
</Box>
)}
) : (
<Box
component={Link}
sx={{
cursor: "pointer",
minWidth: "34px",
height: "34px",
color: "#7E2AEA",
fontSize: "14px",
display: isMobile ? "none" : "flex",
justifyContent: "center",
alignItems: "Center",
background: "#EEE4FC",
borderRadius: "8px",
}}
target="_blank"
to={"https://hbpn.link/" + quiz.qid}
>
<LinkSimple />
</Box>
))}
{isMobile ? (
<Button

@ -0,0 +1,178 @@
import { LogoutButton } from "@ui_kit/LogoutButton";
import BackArrowIcon from "@icons/BackArrowIcon";
import { Burger } from "@icons/Burger";
import {
Box,
Container,
FormControl,
IconButton,
TextField,
useMediaQuery,
useTheme,
} from "@mui/material";
import { updateQuiz } from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import CustomAvatar from "@ui_kit/Header/Avatar";
import NavMenuItem from "@ui_kit/Header/NavMenuItem";
import { enqueueSnackbar } from "notistack";
import { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import Logotip from "../Landing/images/icons/QuizLogo";
import { clearUserData } from "@root/user";
import { clearAuthToken } from "@frontend/kitui";
import { logout } from "@api/auth";
type HeaderProps = {
setMobileSidebar: (callback: (visible: boolean) => boolean) => void;
};
export const Header = ({ setMobileSidebar }: HeaderProps) => {
const quiz = useCurrentQuiz();
const theme = useTheme();
const navigate = useNavigate();
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(660));
async function handleLogoutClick() {
const [, logoutError] = await logout();
if (logoutError) {
return enqueueSnackbar(logoutError);
}
clearAuthToken();
clearUserData();
navigate("/");
}
return (
<Container
component="nav"
maxWidth={false}
disableGutters
sx={{
px: "16px",
display: "flex",
height: isMobile ? "51px" : "80px",
alignItems: "center",
bgcolor: isMobile ? "#333647" : "white",
borderBottom: "1px solid #E3E3E3",
zIndex: theme.zIndex.drawer + 1,
}}
>
<Link to="/" style={{ display: "flex" }}>
{isMobile ? <Logotip width={100} /> : <Logotip width={124} />}
</Link>
<Box
sx={{
display: isMobile ? "none" : "flex",
alignItems: "center",
ml: "37px",
}}
>
<Link to="/list">
<IconButton sx={{ p: "6px" }}>
<BackArrowIcon />
</IconButton>
</Link>
<FormControl fullWidth variant="standard">
<TextField
value={quiz.name}
onChange={(e) =>
updateQuiz(quiz.id, (quiz) => {
quiz.name = e.target.value;
})
}
fullWidth
id="project-name"
placeholder="Название проекта окно"
sx={{
width: "270px",
"& .MuiInputBase-root": {
height: "34px",
borderRadius: "8px",
p: 0,
},
}}
inputProps={{
sx: {
height: "20px",
borderRadius: "8px",
fontSize: "16px",
lineHeight: "20px",
p: "7px",
color: "black",
"&::placeholder": {
opacity: 1,
},
},
}}
/>
</FormControl>
</Box>
{isTablet ? (
<Box
sx={{
display: "flex",
ml: "auto",
}}
>
{isMobile ? (
<Burger
onClick={() => setMobileSidebar((visible: boolean) => !visible)}
style={{ fontSize: "30px", color: "white", cursor: "pointer" }}
/>
) : (
<Box>
{/* <CustomAvatar
sx={{
ml: "11px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/> */}
</Box>
)}
</Box>
) : (
<>
<Box
sx={{
display: "flex",
gap: "30px",
overflow: "hidden",
ml: "20px",
}}
>
{/* <NavMenuItem text="Редактировать" isActive /> */}
{/* <NavMenuItem text="Заявки" />
<NavMenuItem text="Аналитика" />
<NavMenuItem text="История" />
<NavMenuItem text="Помощь" /> */}
</Box>
<Box
sx={{
display: "flex",
ml: "auto",
gap: "15px",
}}
>
{/* <CustomAvatar
sx={{
ml: "11px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/> */}
<LogoutButton onClick={handleLogoutClick} />
</Box>
</>
)}
</Container>
);
};

@ -1,7 +1,10 @@
import BackArrowIcon from "@icons/BackArrowIcon";
import { People } from "@mui/icons-material";
import { FC, useState } from "react";
import { Box, Typography } from "@mui/material";
import { FC } from "react";
import { People } from "@mui/icons-material";
import { SidebarModal } from "./SidebarModal";
import BackArrowIcon from "@icons/BackArrowIcon";
import { ChartLineUp } from "./icons/ChartLineUp";
import { ReturnTime } from "./icons/ReturnTime";
import { Question } from "./icons/Question";
@ -11,9 +14,29 @@ import { ArrowDown } from "./icons/ArrowDown";
interface Iprops {
open: boolean;
changePage: (step: number) => void;
}
export const SidebarMobile: FC<Iprops> = ({ open }) => (
const quizSetupSteps = [
{ sidebarIcon: <Pencil style={{ color: "#974BFA", fontSize: "24px" }} /> },
{ sidebarIcon: <People style={{ color: "#974BFA", fontSize: "24px" }} /> },
{
sidebarIcon: <ChartLineUp style={{ color: "#974BFA", fontSize: "24px" }} />,
},
{
sidebarIcon: <ReturnTime style={{ color: "#974BFA", fontSize: "24px" }} />,
},
{ sidebarIcon: <Question style={{ color: "#974BFA", fontSize: "24px" }} /> },
] as const;
export const SidebarMobile: FC<Iprops> = ({ open, changePage }) => {
const [openModal, setOpenModal] = useState<boolean>(false);
const onClose = () => {
setOpenModal(false);
};
return (
<Box
sx={{
display: open ? "block" : "none",
@ -31,16 +54,38 @@ export const SidebarMobile: FC<Iprops> = ({ open }) => (
<Box sx={{ ml: "15px", display: "flex", alignItems: "end" }}>
<Box>
<Typography sx={{ fontSize: "12px", color: "#9A9AAF" }}>Название</Typography>
<Typography sx={{ color: "#FFF", fontSize: "18px", fontWeight: "500" }}>Название проекта</Typography>
</Box>
<Pencil style={{ position: "absolute", right: "0", color: "white", fontSize: "24px" }} />
</Box>
</Box>
<Box
sx={{ width: "100%", justifyContent: "center", display: "flex", marginTop: "20px", flexWrap: "wrap", gap: "5px" }}
<Typography sx={{ fontSize: "12px", color: "#9A9AAF" }}>
Название
</Typography>
<Typography
sx={{ color: "#FFF", fontSize: "18px", fontWeight: "500" }}
>
Название проекта
</Typography>
</Box>
<Pencil
style={{
position: "absolute",
right: "0",
color: "white",
fontSize: "24px",
}}
/>
</Box>
</Box>
<Box
sx={{
width: "100%",
justifyContent: "center",
display: "flex",
marginTop: "20px",
flexWrap: "wrap",
gap: "5px",
}}
>
{quizSetupSteps.map(({ sidebarIcon }, index) => (
<Box
onClick={() => changePage(index)}
sx={{
cursor: "pointer",
width: "44px",
@ -52,70 +97,11 @@ export const SidebarMobile: FC<Iprops> = ({ open }) => (
borderRadius: "8px",
}}
>
<Pencil style={{ color: "#974BFA", fontSize: "24px" }} />
{sidebarIcon}
</Box>
<Box
sx={{
cursor: "pointer",
width: "44px",
height: "44px",
background: "#262835",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
}}
>
<People style={{ color: "#974BFA", fontSize: "24px" }} />
</Box>
<Box
sx={{
cursor: "pointer",
width: "44px",
height: "44px",
background: "#262835",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
}}
>
<ChartLineUp style={{ color: "#974BFA", fontSize: "24px" }} />
</Box>
<Box
sx={{
cursor: "pointer",
width: "44px",
height: "44px",
background: "#262835",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
}}
>
<ReturnTime style={{ color: "#974BFA", fontSize: "24px" }} />
</Box>
<Box
sx={{
cursor: "pointer",
width: "44px",
height: "44px",
background: "#262835",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
}}
>
<Question style={{ color: "#974BFA", fontSize: "24px" }} />
</Box>
))}
<Box
onClick={() => setOpenModal(true)}
sx={{
px: "10px",
width: "70px",
@ -129,9 +115,13 @@ export const SidebarMobile: FC<Iprops> = ({ open }) => (
marginLeft: "28px",
}}
>
<Settings style={{ color: "#974BFA", fontSize: "24px", marginLeft: "10px" }} />
<Settings
style={{ color: "#974BFA", fontSize: "24px", marginLeft: "10px" }}
/>
<ArrowDown style={{ color: "#F2F3F7" }} />
</Box>
</Box>
<SidebarModal open={openModal} onClose={onClose} changePage={changePage} />
</Box>
);
);
};

@ -0,0 +1,39 @@
import { Box, Modal } from "@mui/material";
import Sidebar from "@ui_kit/Sidebar";
type SidebarModalProps = {
open: boolean;
onClose: () => void;
changePage: (step: number) => void;
};
export const SidebarModal = ({
open,
onClose,
changePage,
}: SidebarModalProps) => {
return (
<Modal open={open} onClose={onClose}>
<Box
onClick={onClose}
sx={{
outline: "none",
position: "absolute",
overflow: "hidden",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
maxWidth: "230px",
maxHeight: "400px",
width: "100%",
bgcolor: "background.paper",
borderRadius: "5px",
boxShadow: 24,
p: 0,
}}
>
<Sidebar changePage={changePage} disableCollapse />
</Box>
</Modal>
);
};

@ -79,11 +79,11 @@ export default function Header() {
ml: "20px",
}}
>
<NavMenuItem text="Редактировать" isActive />
<NavMenuItem text="Заявки" />
{/* <NavMenuItem text="Редактировать" isActive /> */}
{/* <NavMenuItem text="Заявки" />
<NavMenuItem text="Аналитика" />
<NavMenuItem text="История" />
<NavMenuItem text="Помощь" />
<NavMenuItem text="Помощь" /> */}
</Box>
<Box
sx={{
@ -92,9 +92,9 @@ export default function Header() {
gap: "15px",
}}
>
<CustomAvatar
{/* <CustomAvatar
sx={{ ml: "11px", backgroundColor: theme.palette.orange.main, height: "36px", width: "36px" }}
/>
/> */}
</Box>
</>
)}

@ -63,17 +63,17 @@ export default function HeaderFull() {
overflow: "hidden",
}}
>
<NavMenuItem text="Квизы" />
{/* <NavMenuItem text="Квизы" />
<NavMenuItem text="Меню 2" isActive />
<NavMenuItem text="Меню 3" />
<NavMenuItem text="Меню 4" />
<NavMenuItem text="Меню 5" />
<NavMenuItem text="Меню 1" />
<NavMenuItem text="Меню 2" />
<NavMenuItem text="Меню 2" /> */}
</Box>
)}
<Box sx={{ display: isMobile ? "none" : "flex", ml: "auto" }}>
{!isTablet && (
{/* {!isTablet && (
<>
<IconButton sx={{ p: 0 }}>
<WalletIcon color={theme.palette.grey2.main} bgcolor="#F2F3F7" />
@ -93,17 +93,17 @@ export default function HeaderFull() {
</Typography>
</Box>
</>
)}
)} */}
{!isMobile && (
<>
<CustomAvatar
{/* <CustomAvatar
sx={{
ml: "27px",
backgroundColor: theme.palette.orange.main,
height: "36px",
width: "36px",
}}
/>
/> */}
<LogoutButton
onClick={handleLogoutClick}
sx={{

@ -7,11 +7,13 @@ import { Dayjs } from "dayjs";
interface Props {
label?: string;
sx?: SxProps<Theme>;
sxIcon?: SxProps<Theme>;
sxDate?: SxProps<Theme>;
value?: Dayjs | null;
onChange?: (value: Dayjs | null) => void;
}
export default function LabeledDatePicker({ label, value, onChange, sx }: Props) {
export default function LabeledDatePicker({ label, value, onChange, sx, sxIcon, sxDate }: Props) {
const theme = useTheme();
const upLg = useMediaQuery(theme.breakpoints.up("md"));
@ -38,7 +40,7 @@ export default function LabeledDatePicker({ label, value, onChange, sx }: Props)
value={value}
onChange={onChange}
slots={{
openPickerIcon: () => <CalendarIcon />,
openPickerIcon: () => <CalendarIcon sx={sxIcon}/>,
}}
slotProps={{
openPickerButton: {
@ -47,6 +49,9 @@ export default function LabeledDatePicker({ label, value, onChange, sx }: Props)
},
"data-cy": "open-datepicker",
},
layout: {
sx: {backgroundColor: theme.palette.background.default,}
}
}}
sx={{
"& .MuiInputBase-root": {
@ -55,13 +60,14 @@ export default function LabeledDatePicker({ label, value, onChange, sx }: Props)
pr: "22px",
"& input": {
py: "11px",
pl: upLg ? "20px" : "13px",
pl: "20px",
lineHeight: "19px",
},
"& fieldset": {
borderColor: "#9A9AAF",
},
},
...sxDate
}}
/>
</Box>

@ -1,10 +1,12 @@
import { Box, IconButton } from "@mui/material";
import {Box, IconButton, ThemeProvider} from "@mui/material";
import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview";
import { useLayoutEffect, useRef } from "react";
import { Rnd } from "react-rnd";
import { useWindowSize } from "../../utils/hooks/useWindowSize";
import QuizPreviewLayout from "./QuizPreviewLayout";
import ResizeIcon from "@icons/ResizeIcon";
import {themesPublication} from "@utils/themes/Publication/themePublication";
import {useCurrentQuiz} from "@root/quizes/hooks";
const DRAG_PARENT_MARGIN = 0;
const NAVBAR_HEIGHT = 0;
@ -20,6 +22,7 @@ interface RndPositionAndSize {
export default function QuizPreview() {
const isPreviewShown = useQuizPreviewStore((state) => state.isPreviewShown);
const rndParentRef = useRef<HTMLDivElement>(null);
const quiz = useCurrentQuiz();
const rndRef = useRef<Rnd | null>(null);
const rndPositionAndSizeRef = useRef<RndPositionAndSize>({
x: 0,
@ -51,6 +54,7 @@ export default function QuizPreview() {
);
return (
<ThemeProvider theme={themesPublication?.[quiz?.config.theme]}>
<Box
ref={rndParentRef}
data-cy="quiz-preview-container"
@ -112,5 +116,7 @@ export default function QuizPreview() {
</Rnd>
)}
</Box>
</ThemeProvider>
);
}

@ -17,7 +17,7 @@ import {
setCurrentQuestionIndex,
} from "@root/quizPreview";
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "model/questionTypes/shared";
import { useEffect } from "react";
import {useEffect, useRef, useState} from "react";
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
import Date from "./QuizPreviewQuestionTypes/Date";
import Emoji from "./QuizPreviewQuestionTypes/Emoji";
@ -26,7 +26,7 @@ import Images from "./QuizPreviewQuestionTypes/Images";
import Number from "./QuizPreviewQuestionTypes/Number";
import Page from "./QuizPreviewQuestionTypes/Page";
import Rating from "./QuizPreviewQuestionTypes/Rating";
import Select from "./QuizPreviewQuestionTypes/Select";
import Select, {ArrowDownTheme} from "./QuizPreviewQuestionTypes/Select";
import Text from "./QuizPreviewQuestionTypes/Text";
import Variant from "./QuizPreviewQuestionTypes/Variant";
import Varimg from "./QuizPreviewQuestionTypes/Varimg";
@ -37,13 +37,14 @@ export default function QuizPreviewLayout() {
const theme = useTheme();
const questions = useQuestionsStore((state) => state.questions);
const currentQuizStep = useQuizPreviewStore((state) => state.currentQuestionIndex);
const [widthPreview, setWidthPreview] = useState(null)
const nonDeletedQuizQuestions = questions.filter((question) => !question.deleted && question.type !== "result" );
const maxCurrentQuizStep = nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
const currentProgress = Math.floor((currentQuizStep / maxCurrentQuizStep) * 100);
const PreviewWin = useRef(0);
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
useEffect(
function resetCurrentQuizStep() {
if (currentQuizStep > maxCurrentQuizStep) {
@ -53,8 +54,22 @@ export default function QuizPreviewLayout() {
[currentQuizStep, maxCurrentQuizStep]
);
const observer = useRef(
new ResizeObserver((entries) => {
const { width } = entries[0].contentRect;
setWidthPreview(width)
})
);
useEffect(() => {
observer.current.observe(PreviewWin.current);
},
[PreviewWin, observer]);
console.log('current width state: ', widthPreview);
return (
<Paper
ref={PreviewWin}
className="quiz-preview-draghandle"
data-cy="quiz-preview-layout"
sx={{
@ -64,6 +79,7 @@ export default function QuizPreviewLayout() {
flexGrow: 1,
borderRadius: "12px",
pointerEvents: "auto",
backgroundColor: theme.palette.background.default
}}
>
<Box
@ -77,7 +93,7 @@ export default function QuizPreviewLayout() {
scrollbarWidth: "none",
}}
>
<QuestionPreviewComponent question={currentQuestion} />
<QuestionPreviewComponent question={currentQuestion} widthPreview={widthPreview} />
</Box>
<Box
sx={{
@ -98,8 +114,11 @@ export default function QuizPreviewLayout() {
height: "48px",
borderRadius: "8px",
"& .MuiOutlinedInput-notchedOutline": {
border: `1px solid ${theme.palette.brightPurple.main} !important`,
border: `1px solid ${theme.palette.primary.main} !important`,
},
"& .MuiSelect-icon": {
color: theme.palette.primary.main
}
}}
MenuProps={{
PaperProps: {
@ -109,6 +128,7 @@ export default function QuizPreviewLayout() {
borderRadius: "8px",
border: "1px solid #EEE4FC",
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
backgroundColor: theme.palette.background.default,
},
},
MenuListProps: {
@ -119,21 +139,21 @@ export default function QuizPreviewLayout() {
gap: "8px",
"& .Mui-selected": {
backgroundColor: theme.palette.background.default,
color: theme.palette.brightPurple.main,
color: theme.palette.primary.main,
},
},
},
}}
inputProps={{
sx: {
color: theme.palette.brightPurple.main,
color: theme.palette.primary.main,
display: "flex",
alignItems: "center",
px: "9px",
gap: "20px",
},
}}
IconComponent={(props) => <ArrowDownIcon {...props} />}
IconComponent={ArrowDownTheme}
>
{Object.values(questions.filter(q=>q.type!=="result")).map(({ id, title }, index) => (
<MenuItem
@ -145,7 +165,7 @@ export default function QuizPreviewLayout() {
gap: "20px",
p: "4px",
borderRadius: "5px",
color: theme.palette.grey2.main,
color: "#9A9AAF",
}}
>
{`${index + 1}. ${title}`}
@ -196,7 +216,7 @@ export default function QuizPreviewLayout() {
disabled={currentQuizStep === 0}
sx={{ px: 1, minWidth: 0 }}
>
<ArrowLeft />
<ArrowLeft color={theme.palette.primary.main}/>
</Button>
<Button
variant="contained"
@ -212,32 +232,32 @@ export default function QuizPreviewLayout() {
);
}
function QuestionPreviewComponent({ question }: { question: AnyTypedQuizQuestion | UntypedQuizQuestion | undefined }) {
function QuestionPreviewComponent({ question, widthPreview }: { question: AnyTypedQuizQuestion | UntypedQuizQuestion | undefined, widthPreview?: number }) {
if (!question || question.type === null) return null;
switch (question.type) {
case "variant":
return <Variant question={question} />;
return <Variant question={question} widthPreview={widthPreview}/>;
case "images":
return <Images question={question} />;
return <Images question={question} widthPreview={widthPreview}/>;
case "varimg":
return <Varimg question={question} />;
return <Varimg question={question} widthPreview={widthPreview}/>;
case "emoji":
return <Emoji question={question} />;
return <Emoji question={question} widthPreview={widthPreview}/>;
case "text":
return <Text question={question} />;
return <Text question={question} widthPreview={widthPreview}/>;
case "select":
return <Select question={question} />;
return <Select question={question} widthPreview={widthPreview}/>;
case "date":
return <Date question={question} />;
return <Date question={question} widthPreview={widthPreview}/>;
case "number":
return <Number question={question} />;
return <Number question={question} widthPreview={widthPreview}/>;
case "file":
return <File question={question} />;
return <File question={question} widthPreview={widthPreview}/>;
case "page":
return <Page question={question} />;
return <Page question={question} widthPreview={widthPreview}/>;
case "rating":
return <Rating question={question} />;
return <Rating question={question} widthPreview={widthPreview}/>;
default:
notReachable(question);
}

@ -1,13 +1,18 @@
import { Box, Typography } from "@mui/material";
import {Box, Typography, useTheme} from "@mui/material";
import LabeledDatePicker from "@ui_kit/LabeledDatePicker";
import type { QuizQuestionDate } from "model/questionTypes/date";
import {modes} from "@utils/themes/Publication/themePublication";
import {useCurrentQuiz} from "@root/quizes/hooks";
interface Props {
question: QuizQuestionDate;
}
export default function Date({ question }: Props) {
const theme = useTheme();
const mode = modes;
const quiz = useCurrentQuiz();
return (
<Box
sx={{
@ -17,7 +22,20 @@ export default function Date({ question }: Props) {
}}
>
<Typography variant="h6" data-cy="question-title">{question.title}</Typography>
<LabeledDatePicker />
<LabeledDatePicker sxIcon={{
"& path": {stroke: theme.palette.primary.main},
"& rect": {stroke: theme.palette.primary.main}
}}
sxDate={{
"& .MuiInputBase-root": {
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
borderRadius: "10px",
maxWidth: "250px",
pr: "22px",
}
}}
/>
</Box>
);
}

@ -44,7 +44,8 @@ export default function Emoji({ question }: Props) {
key={index}
sx={{
borderRadius: "12px",
border: `1px solid ${theme.palette.grey2.main}`,
border: `1px solid`,
borderColor: value === variant.answer ? theme.palette.primary.main : "#9A9AAF",
overflow: "hidden",
maxWidth: "317px",
width: "100%",
@ -66,17 +67,17 @@ export default function Emoji({ question }: Props) {
sx={{
margin: 0,
padding: "15px",
color: "#4D4D4D",
color: theme.palette.text.primary,
display: "flex",
gap: "10px",
background: "#f2f3f7"
background: theme.palette.background.default
}}
control={
<Radio
inputProps={{
"data-cy": "variant-radio",
}}
checkedIcon={<RadioCheck />} icon={<RadioIcon />}
checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />}
/>
}

@ -58,7 +58,7 @@ export default function File({ question }: Props) {
}}
/>
</Button>
{file && <Typography data-cy="chosen-file-name">Выбран файл: {file.name}</Typography>}
{file && <Typography data-cy="chosen-file-name">Вы загрузили: {file.name}</Typography>}
</Box>
);
}

@ -14,9 +14,10 @@ import type { QuizQuestionImages } from "model/questionTypes/images";
interface Props {
question: QuizQuestionImages;
widthPreview: number
}
export default function Images({ question }: Props) {
export default function Images({ question, widthPreview }: Props) {
const theme = useTheme();
const [selectedVariants, setSelectedVariants] = useState<number[]>([]);
@ -47,12 +48,13 @@ export default function Images({ question }: Props) {
gap: 1,
}}
>
<Typography variant="h6">{question.title}</Typography>
<Typography variant="h6" color={theme.palette.text.primary}>{question.title}</Typography>
<Box
sx={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
gap: 1,
gap: "15px",
maxWidth: "1050px"
}}
>
{question.content.variants
@ -70,8 +72,10 @@ export default function Images({ question }: Props) {
overflow: "hidden",
border: "1px solid",
borderColor: selectedVariants.includes(index)
? theme.palette.brightPurple.main
? theme.palette.primary.main
: "#E3E3E3",
maxWidth: "300px",
maxHeight: "340px"
}}
>
{variant.extendedText ? (

@ -1,9 +1,13 @@
import { useLayoutEffect, useState } from "react";
import { Box, Typography } from "@mui/material";
import {Box, Typography, useTheme} from "@mui/material";
import { CustomSlider } from "@ui_kit/CustomSlider";
import type { QuizQuestionNumber } from "model/questionTypes/number";
import CustomTextField from "@ui_kit/CustomTextField";
import {updateAnswer} from "@root/quizView";
import {modes} from "@utils/themes/Publication/themePublication";
import {useCurrentQuiz} from "@root/quizes/hooks";
interface Props {
question: QuizQuestionNumber;
@ -11,7 +15,9 @@ interface Props {
export default function Number({ question }: Props) {
const [sliderValues, setSliderValues] = useState<number | number[]>(0);
const theme = useTheme();
const mode = modes;
const quiz = useCurrentQuiz();
const start = question.content.start;
const min = parseInt(question.content.range.split("—")[0]);
const max = parseInt(question.content.range.split("—")[1]);
@ -50,7 +56,66 @@ export default function Number({ question }: Props) {
defaultValue={start}
valueLabelDisplay={"on"}
step={question.content.step}
sx={{
color: theme.palette.primary.main,
"& .MuiSlider-valueLabel": {
background: theme.palette.primary.main,}
}}
/>
{!question.content.chooseRange && (
<Box sx={{mt: "30px", maxWidth: "80px",}}>
<CustomTextField
placeholder="0"
value={sliderValues}
sx={{
borderColor: theme.palette.text.primary,
"& .MuiInputBase-input": {
textAlign: "center",
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
},
}}
/>
</Box>
)}
{question.content.chooseRange && (
<Box
sx={{
mt: "30px",
display: "flex",
gap: "15px",
alignItems: "center",
"& .MuiFormControl-root": { width: "auto" },
}}
>
<CustomTextField
placeholder="0"
value={sliderValues[0]}
sx={{
maxWidth: "80px",
borderColor: theme.palette.text.primary,
"& .MuiInputBase-input": {
textAlign: "center",
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
},
}}
/>
<Typography color={theme.palette.text.primary}>до</Typography>
<CustomTextField
placeholder="0"
value={sliderValues[1]}
sx={{
maxWidth: "80px",
borderColor: theme.palette.text.primary,
"& .MuiInputBase-input": {
textAlign: "center",
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
},
}}
/>
</Box>
)}
</Box>
</Box>
);

@ -53,7 +53,7 @@ export default function Rating({ question }: Props) {
gap: 1,
}}
>
<Typography variant="h6" data-cy="question-title">{question.title}</Typography>
<Typography variant="h6" data-cy="question-title" color={theme.palette.text.primary} >{question.title}</Typography>
<Box
sx={{
display: "flex",
@ -67,7 +67,7 @@ export default function Rating({ question }: Props) {
data-rating={selectedRating}
sx={{
display: "flex",
gap: isMobile ? "10px" : "15px",
gap: "25px",
flexWrap: "wrap",
}}
>
@ -89,11 +89,7 @@ export default function Rating({ question }: Props) {
}}
>
<RatingIconComponent
color={
selectedRating > itemNumber
? theme.palette.brightPurple.main
: theme.palette.grey2.main
}
color={selectedRating > itemNumber ? theme.palette.primary.main : "#9A9AAF"}
/>
</Box>
))}

@ -19,7 +19,7 @@ interface Props {
export default function Text({ question }: Props) {
const theme = useTheme();
const [selectValue, setSelectValue] = useState<string>("");
const arrowDown = <ArrowDownIcon color = {"currentColor"}/>
function handleChange(event: SelectChangeEvent<string | null>) {
setSelectValue((event.target as HTMLInputElement).value);
}
@ -54,8 +54,11 @@ export default function Text({ question }: Props) {
height: "48px",
borderRadius: "8px",
"& .MuiOutlinedInput-notchedOutline": {
border: `1px solid ${theme.palette.brightPurple.main} !important`,
border: `1px solid ${theme.palette.primary.main} !important`,
},
"& .MuiSelect-icon": {
color: theme.palette.primary.main
}
}}
MenuProps={{
PaperProps: {
@ -64,6 +67,7 @@ export default function Text({ question }: Props) {
p: "4px",
borderRadius: "8px",
border: "1px solid #EEE4FC",
backgroundColor: theme.palette.background.default,
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
},
},
@ -75,19 +79,20 @@ export default function Text({ question }: Props) {
gap: "8px",
"& .Mui-selected": {
backgroundColor: theme.palette.background.default,
color: theme.palette.brightPurple.main,
color: theme.palette.primary.main,
},
},
},
}}
inputProps={{
sx: {
color: theme.palette.brightPurple.main,
color: theme.palette.primary.main,
display: "flex",
alignItems: "center",
px: "9px",
gap: "20px",
"& + input": !selectValue && {
backgroundColor: theme.palette.background.default,
border: "none",
transform: "translateY(-50%)",
top: "50%",
@ -96,9 +101,10 @@ export default function Text({ question }: Props) {
color: "#333",
fontSize: "16px",
},
},
}}
IconComponent={(props) => <ArrowDownIcon {...props} />}
IconComponent={ArrowDownTheme}
>
{question.content.variants
.filter(({ answer }) => answer)
@ -113,7 +119,7 @@ export default function Text({ question }: Props) {
gap: "20px",
p: "4px",
borderRadius: "5px",
color: theme.palette.grey2.main,
color: "#9A9AAF",
}}
>
{variant.answer}
@ -124,3 +130,23 @@ export default function Text({ question }: Props) {
</Box>
);
}
export function ArrowDownTheme (props: any) {
return (
<Box
{...props}
sx={{
top: "25% !important",
height: "24px",
width: "24px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M19.5 9L12 16.5L4.5 9" stroke={"currentColor"} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</Box>
)
}

@ -8,7 +8,7 @@ import {
RadioGroup,
useRadioGroup,
Tooltip,
Typography,
Typography, useTheme, Checkbox,
} from "@mui/material";
import InfoIcon from "@icons/InfoIcon";
@ -17,6 +17,9 @@ import type { QuizQuestionVariant } from "model/questionTypes/variant";
import CustomRadio from "@ui_kit/CustomRadio";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
import {modes} from "@utils/themes/Publication/themePublication";
import {useCurrentQuiz} from "@root/quizes/hooks";
import CheckboxIcon from "@icons/Checkbox";
interface Props {
question: QuizQuestionVariant;
@ -24,14 +27,16 @@ interface Props {
export default function Variant({ question }: Props) {
const [value, setValue] = useState<string | null>(null);
const theme = useTheme();
const mode = modes;
const quiz = useCurrentQuiz();
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setValue((event.target as HTMLInputElement).value);
};
return (
<FormControl fullWidth>
<FormLabel id="quiz-question-radio-group" data-cy="question-title" sx={{color: "#000000", marginBottom: "20px", fontSize: "24px", fontWeight: 500}}>
<FormLabel id="quiz-question-radio-group" data-cy="question-title" sx={{color: theme.palette.text.primary, marginBottom: "20px", fontSize: "24px", fontWeight: 500}}>
{question.title}
</FormLabel>
<RadioGroup
@ -52,24 +57,32 @@ export default function Variant({ question }: Props) {
data-cy="variant-answer"
labelPlacement="start"
sx={{borderRadius: "12px",
border: value === value ? "1px solid #7E2AEA" : "1px solid #9A9AAF",
border: value == variant.answer ? `1px solid ${theme.palette.primary.main}` : "1px solid #9A9AAF",
padding: "20px",
justifyContent: "space-between",
maxWidth: "685px",
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
width: "100%",
margin: 0
}}
control={
control={question.content.multi ? (
<Checkbox
checkedIcon={<CheckboxIcon checked color={theme.palette.primary.main} />}
icon={<CheckboxIcon />}
/>
) : (
<Radio
inputProps={{
"data-cy": "variant-radio",
}}
checkedIcon={<RadioCheck />} icon={<RadioIcon />}
checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />}
/>
)
}
label={
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
<Typography>
<Typography color={theme.palette.text.primary}>
{variant.answer}
</Typography>
{variant.hints && (

@ -7,7 +7,7 @@ import {
Radio,
RadioGroup,
Tooltip,
Typography, useTheme,
Typography, useMediaQuery, useTheme,
} from "@mui/material";
import InfoIcon from "@icons/InfoIcon";
@ -16,14 +16,20 @@ import type { QuestionVariant } from "model/questionTypes/shared";
import type { QuizQuestionVarImg } from "model/questionTypes/varimg";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
import {modes} from "@utils/themes/Publication/themePublication";
import {useCurrentQuiz} from "@root/quizes/hooks";
interface Props {
question: QuizQuestionVarImg;
widthPreview: number;
}
export default function Varimg({ question }: Props) {
export default function Varimg({ question, widthPreview }: Props) {
const [selectedVariantIndex, setSelectedVariantIndex] = useState<number>(-1);
const theme = useTheme();
const mode = modes;
const quiz = useCurrentQuiz();
const isMobile = useMediaQuery(theme.breakpoints.down(650));
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setSelectedVariantIndex(
question.content.variants.findIndex(
@ -43,20 +49,32 @@ export default function Varimg({ question }: Props) {
gap: "40px",
}}
>
<FormControl sx={{maxWidth: "900px",
<FormControl sx={{
//maxWidth: "900px",
width: "100%", gap: "25px"}}>
<FormLabel
id="quiz-question-radio-group"
data-cy="question-title"
sx={{fontSize: "24px", fontWeight: 500}}
sx={{fontSize: "24px", fontWeight: 500, color: theme.palette.text.primary}}
>
{question.title}
</FormLabel>
<Box sx={{
display: "flex",
alignItems: "center",
flexDirection: widthPreview < 650 ? "column-reverse" : undefined,
gap: "30px"
}}>
<RadioGroup
aria-labelledby="quiz-question-radio-group"
value={currentVariant?.answer ?? ""}
onChange={handleChange}
sx={{gap: "20px"}}
sx={{
gap: "20px",
display: "flex",
width: "100%",
}}
>
{question.content.variants
.filter(({ answer }) => answer)
@ -69,16 +87,20 @@ export default function Varimg({ question }: Props) {
margin: 0,
borderRadius: "5px",
padding: "15px",
color: "#4D4D4D",
border: `1px solid ${theme.palette.grey2.main}`,
display: "flex",
color: theme.palette.text.primary,
border: `1px solid`,
borderColor: selectedVariantIndex == index
? theme.palette.primary.main
: "#E3E3E3",
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
/*display: "flex",*/
}}
control={
<Radio
inputProps={{
"data-cy": "variant-radio",
}}
checkedIcon={<RadioCheck />} icon={<RadioIcon />}
checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />}
/>
}
label={
@ -96,7 +118,6 @@ export default function Varimg({ question }: Props) {
/>
))}
</RadioGroup>
</FormControl>
<Box
sx={{
border: "1px solid #E3E3E3",
@ -106,7 +127,7 @@ export default function Varimg({ question }: Props) {
justifyContent: "center",
alignItems: "center",
borderRadius: "12px",
marginTop: "50px",
marginTop: widthPreview < 650 ? 0 : "50px",
overflow: "hidden",
}}
>
@ -117,19 +138,24 @@ export default function Varimg({ question }: Props) {
alt="question variant"
style={{
width: "100%",
height: "100%",
display: "block",
objectFit: "scale-down",
flexGrow: 1,
objectFit: "cover"
}}
/>
) : (
<Typography p={2}>
{selectedVariantIndex === -1
? "Выберите вариант"
? widthPreview < 650 ? "Выберите вариант ниже" : "Выберите вариант"
: "Картинка отсутствует"}
</Typography>
)}
</Box>
</Box>
</FormControl>
</Box>
);
}

@ -5,12 +5,11 @@ import PuzzlePieceIcon from "@icons/PuzzlePieceIcon";
import TagIcon from "@icons/TagIcon";
import { quizSetupSteps } from "@model/quizSettings";
import { Box, IconButton, List, Typography, useTheme } from "@mui/material";
import { setCurrentStep } from "@root/quizes/actions";
import { useQuizStore } from "@root/quizes/store";
import { useState } from "react";
import MenuItem from "./MenuItem";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { setShowConfirmLeaveModal } from "@root/uiTools/actions";
import { useLocation, useNavigate } from "react-router-dom";
const quizSettingsMenuItems = [
[TagIcon, "Дополнения"],
@ -20,28 +19,28 @@ const quizSettingsMenuItems = [
] as const;
type SidebarProps = {
setNextStep: (step: number) => void;
changePage: (step: number) => void;
disableCollapse?: boolean;
};
export default function Sidebar({ setNextStep }: SidebarProps) {
export default function Sidebar({ changePage, disableCollapse }: SidebarProps) {
const theme = useTheme();
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
const currentStep = useQuizStore((state) => state.currentStep);
const quiz = useCurrentQuiz();
const { pathname } = useLocation();
const navigate = useNavigate();
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
const changePage = (index: number) => {
if (currentStep === 2) {
setNextStep(index);
setShowConfirmLeaveModal(true);
return;
const changeMenuItem = (index: number) => {
if (!pathname.startsWith("/edit")) {
navigate("/edit");
}
setCurrentStep(index);
changePage(index);
};
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
return (
<Box
sx={{
@ -82,6 +81,7 @@ export default function Sidebar({ setNextStep }: SidebarProps) {
Создание квиза
</Typography>
)}
{!disableCollapse && (
<IconButton
onClick={handleMenuCollapseToggle}
sx={{ ml: isMenuCollapsed ? undefined : "auto" }}
@ -93,6 +93,7 @@ export default function Sidebar({ setNextStep }: SidebarProps) {
transform={isMenuCollapsed ? "rotate(180deg)" : ""}
/>
</IconButton>
)}
</Box>
<List disablePadding>
{quizSetupSteps.map((menuItem, index) => {
@ -100,7 +101,7 @@ export default function Sidebar({ setNextStep }: SidebarProps) {
return (
<MenuItem
onClick={() => changePage(index)}
onClick={() => changeMenuItem(index)}
key={index}
text={menuItem.sidebarText}
isCollapsed={isMenuCollapsed}
@ -129,7 +130,7 @@ export default function Sidebar({ setNextStep }: SidebarProps) {
);
})}
</List>
{/* {!isMenuCollapsed && (
{!isMenuCollapsed && (
<Typography
sx={{
px: "16px",
@ -143,8 +144,36 @@ export default function Sidebar({ setNextStep }: SidebarProps) {
>
Настройки квиза
</Typography>
)} */}
)}
<List disablePadding>
<MenuItem
onClick={()=>navigate("/design")}
text={"Дизайн"}
isCollapsed={isMenuCollapsed}
isActive={currentStep === 6}
disabled={
currentStep === 6
? false
: quiz === undefined
? true
: quiz?.config.type === null
}
icon={
<PencilCircleIcon
color={
currentStep === 6
? theme.palette.brightPurple.main
: isMenuCollapsed
? "white"
: theme.palette.grey2.main
}
height={isMenuCollapsed ? "35px" : "24px"}
width={isMenuCollapsed ? "35px" : "24px"}
/>
}
/>
{/* {quizSettingsMenuItems.map((menuItem, index) => {
const Icon = menuItem[0];
const totalIndex = index + quizSetupSteps.length;

@ -4,6 +4,7 @@ import YoutubeEmbedIframe from "./YoutubeEmbedIframe";
import { QuizStartpageAlignType, QuizStartpageType } from "@model/quizSettings";
import { notReachable } from "../../utils/notReachable";
import { useUADevice } from "../../utils/hooks/useUADevice";
import {useEffect, useRef, useState} from "react";
export default function QuizPreviewLayout() {
const theme = useTheme();
@ -36,7 +37,18 @@ export default function QuizPreviewLayout() {
) : null;
return (
<Paper className="quiz-preview-draghandle" sx={{ height: "100%" }}>
<Paper className="quiz-preview-draghandle"
sx={{
height: "100%",
background:
quiz.config.startpageType === "expanded"
? quiz.config.startpage.position === "left"
? "linear-gradient(90deg,#272626,transparent)"
: quiz.config.startpage.position === "center"
? "linear-gradient(180deg,transparent,#272626)"
: "linear-gradient(270deg,#272626,transparent)"
: theme.palette.background.default,
}}>
<QuizPreviewLayoutByType
quizHeaderBlock={
<>
@ -94,19 +106,19 @@ export default function QuizPreviewLayout() {
{quiz.config.info.clickable ? (
isMobileDevice ? (
<Link href={`tel:${quiz.config.info.phonenumber}`}>
<Typography sx={{ fontSize: "16px", color: theme.palette.brightPurple.main }}>
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
{quiz.config.info.phonenumber}
</Typography>
</Link>
) : (
<ButtonBase onClick={handleCopyNumber}>
<Typography sx={{ fontSize: "16px", color: theme.palette.brightPurple.main }}>
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
{quiz.config.info.phonenumber}
</Typography>
</ButtonBase>
)
) : (
<Typography sx={{ fontSize: "16px", color: theme.palette.brightPurple.main }}>
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
{quiz.config.info.phonenumber}
</Typography>
)}
@ -138,6 +150,46 @@ function QuizPreviewLayoutByType({
const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.down(630));
function StartPageMobile() {
return(
<>
<Box
sx={{
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "flex-start",
p: "25px",
height: "80%"
}}
>
{quizHeaderBlock}
<Box
sx={{
height: "80%",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
width: "100%"
}}
>
{quizMainBlock}
</Box>
</Box>
<Box
sx={{
width: "100%",
overflow: "hidden",
}}
>
{backgroundBlock}
</Box>
</>
)
}
switch (startpageType) {
case null:
case "standard": {
@ -191,7 +243,7 @@ function QuizPreviewLayoutByType({
width: "40%",
position: "relative",
padding: "16px",
zIndex: 2,
zIndex: 3,
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
@ -208,7 +260,7 @@ function QuizPreviewLayoutByType({
top: 0,
height: "100%",
width: "100%",
zIndex: 1,
zIndex: -1,
}}
>
{backgroundBlock}
@ -230,7 +282,15 @@ function QuizPreviewLayoutByType({
}}
>
{quizHeaderBlock}
{backgroundBlock && <Box>{backgroundBlock}</Box>}
{backgroundBlock &&
<Box
sx={{
width: "60%",
overflow: "hidden",
display: "flex",
justifyContent: "center"
}}
>{backgroundBlock}</Box>}
{quizMainBlock}
</Box>
);

@ -1,9 +1,11 @@
import { Box, IconButton, useTheme, useMediaQuery } from "@mui/material";
import {Box, IconButton, useTheme, useMediaQuery, ThemeProvider} from "@mui/material";
import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview";
import { useLayoutEffect, useRef } from "react";
import {useEffect, useLayoutEffect, useRef, useState} from "react";
import { Rnd } from "react-rnd";
import QuizPreviewLayout from "./QuizPreviewLayout";
import ResizeIcon from "@icons/ResizeIcon";
import {themesPublication} from "@utils/themes/Publication/themePublication";
import {useCurrentQuiz} from "@root/quizes/hooks";
const DRAG_PARENT_MARGIN = 0;
const NAVBAR_HEIGHT = 0;
@ -21,6 +23,7 @@ export const StartPagePreview = () => {
const rndParentRef = useRef<HTMLDivElement>(null);
const rndRef = useRef<Rnd | null>(null);
const theme = useTheme();
const quiz = useCurrentQuiz();
const isTablet = useMediaQuery(theme.breakpoints.down(630));
const rndPositionAndSizeRef = useRef<RndPositionAndSize>({
x: 0,
@ -52,6 +55,7 @@ export const StartPagePreview = () => {
);
return (
<ThemeProvider theme={themesPublication?.[quiz?.config.theme]}>
<Box
ref={rndParentRef}
sx={{
@ -113,5 +117,7 @@ export const StartPagePreview = () => {
</Rnd>
)}
</Box>
</ThemeProvider>
);
};

@ -35,12 +35,12 @@ export default function ProgressMobileStepper({
width: "100%",
flexGrow: 1,
padding: "8px 0",
"& .css-1ej0n1q-MuiLinearProgress-root-MuiMobileStepper-progress": {
"& .MuiLinearProgress-root": {
height: "10px",
background: "#ffffff",
width: "100%",
},
"& .css-1v0msyf-MuiLinearProgress-bar1": {
"& .MuiLinearProgress-bar": {
background: "#7e2aea",
},
}}

@ -14,10 +14,6 @@ const themePublic = createTheme({
padding: '13px 20px',
borderRadius: '8px',
boxShadow: "none",
// "&:hover": {
// backgroundColor: "#581CA7"
// }
},
},
{
@ -27,11 +23,6 @@ const themePublic = createTheme({
style: {
padding: '10px 20px',
borderRadius: '8px',
"&:hover": {
backgroundColor: "#581CA7",
border: '1px solid #581CA7',
}
},
},
],