Merge branch 'dev' into 'main'
нельзя добавить больше 1 ребенка неветвящемуся, не отображаются шестерёнки у... See merge request frontend/squiz!93
This commit is contained in:
commit
21f0f395f5
50
src/App.tsx
50
src/App.tsx
@ -16,15 +16,61 @@ import { ResultSettings } from "./pages/ResultPage/ResultSettings";
|
|||||||
import MyQuizzesFull from "./pages/createQuize/MyQuizzesFull";
|
import MyQuizzesFull from "./pages/createQuize/MyQuizzesFull";
|
||||||
import Main from "./pages/main";
|
import Main from "./pages/main";
|
||||||
import EditPage from "./pages/startPage/EditPage";
|
import EditPage from "./pages/startPage/EditPage";
|
||||||
import { clearAuthToken, getMessageFromFetchError, useUserAccountFetcher, useUserFetcher } from "@frontend/kitui";
|
import { clearAuthToken, getMessageFromFetchError, useUserFetcher, UserAccount, makeRequest, devlog, createUserAccount } from "@frontend/kitui";
|
||||||
import { clearUserData, setUser, setUserAccount, useUserStore } from "@root/user";
|
import { clearUserData, setUser, setUserAccount, useUserStore } from "@root/user";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import PrivateRoute from "@ui_kit/PrivateRoute";
|
import PrivateRoute from "@ui_kit/PrivateRoute";
|
||||||
|
|
||||||
import { Restore } from "./pages/startPage/Restore";
|
import { Restore } from "./pages/startPage/Restore";
|
||||||
|
|
||||||
|
import { isAxiosError } from "axios";
|
||||||
|
import { useEffect, useLayoutEffect, useRef } from "react";
|
||||||
|
export function useUserAccountFetcher({ onError, onNewUserAccount, url, userId }: {
|
||||||
|
url: string;
|
||||||
|
userId: string | null;
|
||||||
|
onNewUserAccount: (response: UserAccount) => void;
|
||||||
|
onError?: (error: any) => void;
|
||||||
|
}) {
|
||||||
|
const onNewUserAccountRef = useRef(onNewUserAccount);
|
||||||
|
const onErrorRef = useRef(onError);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
onNewUserAccountRef.current = onNewUserAccount;
|
||||||
|
onErrorRef.current = onError;
|
||||||
|
}, [onError, onNewUserAccount]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!userId) return;
|
||||||
|
const controller = new AbortController();
|
||||||
|
makeRequest<never, UserAccount>({
|
||||||
|
url,
|
||||||
|
contentType: true,
|
||||||
|
method: "GET",
|
||||||
|
useToken: true,
|
||||||
|
withCredentials: false,
|
||||||
|
signal: controller.signal,
|
||||||
|
}).then(result => {
|
||||||
|
devlog("User account", result);
|
||||||
|
onNewUserAccountRef.current(result);
|
||||||
|
}).catch(error => {
|
||||||
|
devlog("Error fetching user account", error);
|
||||||
|
if (isAxiosError(error) && error.response?.status === 404) {
|
||||||
|
createUserAccount(controller.signal, url.replace('get','create')).then(result => {
|
||||||
|
devlog("Created user account", result);
|
||||||
|
onNewUserAccountRef.current(result);
|
||||||
|
}).catch(error => {
|
||||||
|
devlog("Error creating user account", error);
|
||||||
|
onErrorRef.current?.(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
onErrorRef.current?.(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => controller.abort();
|
||||||
|
}, [url, userId]);
|
||||||
|
}
|
||||||
|
|
||||||
dayjs.locale("ru");
|
dayjs.locale("ru");
|
||||||
|
|
||||||
|
|
||||||
const routeslink = [
|
const routeslink = [
|
||||||
{ path: "/list", page: <MyQuizzesFull />, header: false, sidebar: false },
|
{ path: "/list", page: <MyQuizzesFull />, header: false, sidebar: false },
|
||||||
{ path: "/questions/:quizId", page: <QuestionsPage />, header: true, sidebar: true },
|
{ path: "/questions/:quizId", page: <QuestionsPage />, header: true, sidebar: true },
|
||||||
@ -53,7 +99,7 @@ export default function App() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useUserAccountFetcher({
|
useUserAccountFetcher({
|
||||||
url: "https://squiz.pena.digital/customer/account",
|
url: "https://squiz.pena.digital/squiz/account/get",
|
||||||
userId,
|
userId,
|
||||||
onNewUserAccount: setUserAccount,
|
onNewUserAccount: setUserAccount,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { Box, useTheme } from "@mui/material";
|
import {Box, SxProps, Theme, useTheme} from "@mui/material";
|
||||||
|
|
||||||
|
interface Color{
|
||||||
export default function ArrowDownIcon(props: any) {
|
color?: string
|
||||||
|
}
|
||||||
|
export default function ArrowDownIcon(
|
||||||
|
props: any,
|
||||||
|
{color = "#7E2AEA"}: Color
|
||||||
|
) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -17,7 +22,7 @@ export default function ArrowDownIcon(props: any) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
<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={theme.palette.brightPurple.main} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M19.5 9L12 16.5L4.5 9" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
11
src/assets/icons/BackButtonIcon.tsx
Normal file
11
src/assets/icons/BackButtonIcon.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const BackButtonIcon = () => (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path
|
||||||
|
d="M7.86747 18C8.26104 18 9.9253 18 13.4337 18C17.8193 18 19 14.703 19 13.2194C19 11.7358 17.8193 8.93333 13.4337 8.93333C10.1773 8.93333 6.59726 8.93333 5 8.93333M5 8.93333L7.86747 6M5 8.93333L7.86747 11.8182"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
@ -1,6 +1,9 @@
|
|||||||
import { Box } from "@mui/material";
|
import {Box, SxProps, Theme} from "@mui/material";
|
||||||
|
|
||||||
export default function CalendarIcon() {
|
interface Props {
|
||||||
|
sx?: SxProps<Theme>
|
||||||
|
}
|
||||||
|
export default function CalendarIcon({sx}:Props) {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -20,6 +23,7 @@ export default function CalendarIcon() {
|
|||||||
"&:active rect": {
|
"&:active rect": {
|
||||||
stroke: "#FB5607",
|
stroke: "#FB5607",
|
||||||
},
|
},
|
||||||
|
...sx
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg width="20" height="22" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="20" height="22" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Box, useTheme } from "@mui/material";
|
import { Box, useTheme } from "@mui/material";
|
||||||
|
|
||||||
type CheckboxIconProps = {
|
interface CheckboxIconProps {
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
|
color?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CheckboxIcon = ({ checked = false }: CheckboxIconProps) => {
|
export default function CheckboxIcon ({ checked = false, color = "#7E2AEA", }: CheckboxIconProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -17,9 +18,9 @@ export const CheckboxIcon = ({ checked = false }: CheckboxIconProps) => {
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
backgroundColor: checked
|
backgroundColor: checked
|
||||||
? theme.palette.brightPurple.main
|
? color
|
||||||
: theme.palette.background.default,
|
: "#F2F3F7",
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
border: `1px solid #9A9AAF`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{checked && (
|
{checked && (
|
||||||
|
@ -2,14 +2,7 @@ import { FC, SVGProps } from "react";
|
|||||||
|
|
||||||
export const CrossedEyeIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
|
export const CrossedEyeIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg {...props} width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
{...props}
|
|
||||||
width="30"
|
|
||||||
height="30"
|
|
||||||
viewBox="0 0 30 30"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<rect y="0.8125" width="30" height="30" rx="6" fill="#FFF" />
|
<rect y="0.8125" width="30" height="30" rx="6" fill="#FFF" />
|
||||||
<path
|
<path
|
||||||
d="M7.5 7.5625L22.5 24.0625"
|
d="M7.5 7.5625L22.5 24.0625"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Box, useTheme } from "@mui/material";
|
import { Box, useTheme } from "@mui/material";
|
||||||
|
|
||||||
|
|
||||||
export default function EyeIcon() {
|
export default function EyeIcon() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@ -15,8 +14,18 @@ export default function EyeIcon() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||||
<path d="M9 3.9375C3.375 3.9375 1.125 9 1.125 9C1.125 9 3.375 14.0625 9 14.0625C14.625 14.0625 16.875 9 16.875 9C16.875 9 14.625 3.9375 9 3.9375Z" stroke={theme.palette.brightPurple.main} strokeLinecap="round" strokeLinejoin="round" />
|
<path
|
||||||
<path d="M9 11.8125C10.5533 11.8125 11.8125 10.5533 11.8125 9C11.8125 7.4467 10.5533 6.1875 9 6.1875C7.4467 6.1875 6.1875 7.4467 6.1875 9C6.1875 10.5533 7.4467 11.8125 9 11.8125Z" stroke={theme.palette.brightPurple.main} strokeLinecap="round" strokeLinejoin="round" />
|
d="M9 3.9375C3.375 3.9375 1.125 9 1.125 9C1.125 9 3.375 14.0625 9 14.0625C14.625 14.0625 16.875 9 16.875 9C16.875 9 14.625 3.9375 9 3.9375Z"
|
||||||
|
stroke={theme.palette.brightPurple.main}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M9 11.8125C10.5533 11.8125 11.8125 10.5533 11.8125 9C11.8125 7.4467 10.5533 6.1875 9 6.1875C7.4467 6.1875 6.1875 7.4467 6.1875 9C6.1875 10.5533 7.4467 11.8125 9 11.8125Z"
|
||||||
|
stroke={theme.palette.brightPurple.main}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
27
src/assets/icons/LinkSimple.tsx
Normal file
27
src/assets/icons/LinkSimple.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { FC, SVGProps } from "react";
|
||||||
|
|
||||||
|
export const LinkSimple: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||||
|
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path
|
||||||
|
d="M8.82031 15.1781L15.1766 8.8125"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M13.5949 16.7719L10.9418 19.425C10.5238 19.843 10.0276 20.1745 9.48151 20.4007C8.93541 20.6269 8.35009 20.7434 7.75899 20.7434C6.5652 20.7434 5.42031 20.2691 4.57618 19.425C3.73204 18.5809 3.25781 17.436 3.25781 16.2422C3.25781 15.0484 3.73204 13.9035 4.57618 13.0594L7.2293 10.4062"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M16.7719 13.5937L19.425 10.9406C20.2691 10.0965 20.7434 8.95159 20.7434 7.7578C20.7434 6.56401 20.2691 5.41912 19.425 4.57499C18.5809 3.73085 17.436 3.25662 16.2422 3.25662C15.0484 3.25662 13.9035 3.73085 13.0594 4.57499L10.4062 7.22811"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
@ -12,7 +12,7 @@ export const NameplateLogo: FC<SVGProps<SVGSVGElement>> = (props) => (
|
|||||||
<circle cx="54.4046" cy="12.3947" r="2.1546" fill="#7E2AEA" />
|
<circle cx="54.4046" cy="12.3947" r="2.1546" fill="#7E2AEA" />
|
||||||
<path
|
<path
|
||||||
d="M88.866 39.4685C88.2378 33.3607 85.3643 27.7037 80.8025 23.594C76.2408 19.4843 70.3156 17.2146 64.1757 17.2248C63.3039 17.2252 62.4328 17.2708 61.5658 17.3614C55.4608 18.0025 49.8093 20.8814 45.7015 25.443C41.5937 30.0046 39.3205 35.9256 39.3203 42.0642V42.0642V77.549H49.9658V62.468C54.128 65.3636 59.0787 66.9119 64.1491 66.9036C65.0208 66.9033 65.8919 66.8577 66.759 66.767C70.0031 66.426 73.1483 65.4494 76.0151 63.8929C78.8818 62.3364 81.4138 60.2305 83.4667 57.6955C85.5195 55.1604 87.0529 52.2458 87.9793 49.1181C88.9058 45.9904 89.2071 42.7109 88.866 39.4667V39.4685ZM75.1937 51.0011C74.0243 52.4537 72.5783 53.6599 70.9395 54.5498C69.3007 55.4397 67.5017 55.9956 65.6465 56.1854C65.149 56.2371 64.6492 56.2631 64.1491 56.2635C60.9296 56.2605 57.8068 55.1631 55.2932 53.1515C52.7796 51.1398 51.0245 48.3334 50.3161 45.1929C49.6077 42.0523 49.988 38.7642 51.3945 35.8683C52.8011 32.9723 55.1504 30.6406 58.0568 29.2558C60.9632 27.871 64.2541 27.5154 67.3892 28.2473C70.5244 28.9793 73.3176 30.7553 75.3103 33.284C77.303 35.8126 78.3769 38.9436 78.3558 42.1629C78.3346 45.3823 77.2196 48.4989 75.1937 51.0011Z"
|
d="M88.866 39.4685C88.2378 33.3607 85.3643 27.7037 80.8025 23.594C76.2408 19.4843 70.3156 17.2146 64.1757 17.2248C63.3039 17.2252 62.4328 17.2708 61.5658 17.3614C55.4608 18.0025 49.8093 20.8814 45.7015 25.443C41.5937 30.0046 39.3205 35.9256 39.3203 42.0642V42.0642V77.549H49.9658V62.468C54.128 65.3636 59.0787 66.9119 64.1491 66.9036C65.0208 66.9033 65.8919 66.8577 66.759 66.767C70.0031 66.426 73.1483 65.4494 76.0151 63.8929C78.8818 62.3364 81.4138 60.2305 83.4667 57.6955C85.5195 55.1604 87.0529 52.2458 87.9793 49.1181C88.9058 45.9904 89.2071 42.7109 88.866 39.4667V39.4685ZM75.1937 51.0011C74.0243 52.4537 72.5783 53.6599 70.9395 54.5498C69.3007 55.4397 67.5017 55.9956 65.6465 56.1854C65.149 56.2371 64.6492 56.2631 64.1491 56.2635C60.9296 56.2605 57.8068 55.1631 55.2932 53.1515C52.7796 51.1398 51.0245 48.3334 50.3161 45.1929C49.6077 42.0523 49.988 38.7642 51.3945 35.8683C52.8011 32.9723 55.1504 30.6406 58.0568 29.2558C60.9632 27.871 64.2541 27.5154 67.3892 28.2473C70.5244 28.9793 73.3176 30.7553 75.3103 33.284C77.303 35.8126 78.3769 38.9436 78.3558 42.1629C78.3346 45.3823 77.2196 48.4989 75.1937 51.0011Z"
|
||||||
fill="#151515"
|
fill={"currentColor" || "#151515"}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
File diff suppressed because one or more lines are too long
24
src/assets/icons/NameplateLogoFQDark.tsx
Normal file
24
src/assets/icons/NameplateLogoFQDark.tsx
Normal file
File diff suppressed because one or more lines are too long
@ -1,7 +1,10 @@
|
|||||||
import { Box, useTheme } from "@mui/material";
|
import { Box, useTheme } from "@mui/material";
|
||||||
|
|
||||||
|
interface Props{
|
||||||
|
color?: string
|
||||||
|
}
|
||||||
|
|
||||||
export default function UploadIcon() {
|
export default function UploadIcon({color= "#9A9AAF"}: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -15,9 +18,9 @@ export default function UploadIcon() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||||
<path d="M10.75 10.25L16 5L21.25 10.25" stroke={theme.palette.grey2.main} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M10.75 10.25L16 5L21.25 10.25" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
<path d="M16 19V5" stroke={theme.palette.grey2.main} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M16 19V5" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
<path d="M27 19V26C27 26.2652 26.8946 26.5196 26.7071 26.7071C26.5196 26.8946 26.2652 27 26 27H6C5.73478 27 5.48043 26.8946 5.29289 26.7071C5.10536 26.5196 5 26.2652 5 26V19" stroke={theme.palette.grey2.main} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M27 19V26C27 26.2652 26.8946 26.5196 26.7071 26.7071C26.5196 26.8946 26.2652 27 26 27H6C5.73478 27 5.48043 26.8946 5.29289 26.7071C5.10536 26.5196 5 26.2652 5 26V19" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -10,11 +10,11 @@ export const QUIZ_QUESTION_NUMBER: Omit<QuizQuestionNumber, "id" | "backendId">
|
|||||||
required: false,
|
required: false,
|
||||||
innerNameCheck: false,
|
innerNameCheck: false,
|
||||||
innerName: "",
|
innerName: "",
|
||||||
range: "1—100",
|
range: "0—100",
|
||||||
defaultValue: 0,
|
defaultValue: 0,
|
||||||
step: 1,
|
step: 1,
|
||||||
steps: 5,
|
steps: 5,
|
||||||
start: 50,
|
start: 0,
|
||||||
chooseRange: false,
|
chooseRange: false,
|
||||||
form: "star",
|
form: "star",
|
||||||
},
|
},
|
||||||
|
@ -18,12 +18,11 @@ export interface QuestionBranchingRuleMain {
|
|||||||
or: boolean;
|
or: boolean;
|
||||||
rules: {
|
rules: {
|
||||||
question: string; //id родителя (пока что)
|
question: string; //id родителя (пока что)
|
||||||
answers: string[]
|
answers: string[];
|
||||||
}[]
|
}[];
|
||||||
}
|
}
|
||||||
export interface QuestionBranchingRule {
|
export interface QuestionBranchingRule {
|
||||||
|
children: string[];
|
||||||
children: string[],
|
|
||||||
//список условий
|
//список условий
|
||||||
main: QuestionBranchingRuleMain[];
|
main: QuestionBranchingRuleMain[];
|
||||||
parentId: string | null | "root";
|
parentId: string | null | "root";
|
||||||
@ -96,23 +95,27 @@ export type AnyTypedQuizQuestion =
|
|||||||
| QuizQuestionRating
|
| QuizQuestionRating
|
||||||
| QuizQuestionResult;
|
| QuizQuestionResult;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type FilterQuestionsWithVariants<T> = T extends {
|
type FilterQuestionsWithVariants<T> = T extends {
|
||||||
content: { variants: QuestionVariant[]; };
|
content: { variants: QuestionVariant[] };
|
||||||
} ? T : never;
|
}
|
||||||
|
? T
|
||||||
|
: never;
|
||||||
|
|
||||||
export type QuizQuestionsWithVariants = FilterQuestionsWithVariants<AnyTypedQuizQuestion>;
|
export type QuizQuestionsWithVariants = FilterQuestionsWithVariants<AnyTypedQuizQuestion>;
|
||||||
|
|
||||||
|
export const createBranchingRuleMain: (targetId: string, parentId: string) => QuestionBranchingRuleMain = (
|
||||||
export const createBranchingRuleMain: (targetId:string, parentId:string) => QuestionBranchingRuleMain = (targetId, parentId) => ({
|
targetId,
|
||||||
|
parentId
|
||||||
|
) => ({
|
||||||
next: targetId,
|
next: targetId,
|
||||||
or: false,
|
or: false,
|
||||||
rules: [{
|
rules: [
|
||||||
|
{
|
||||||
question: parentId,
|
question: parentId,
|
||||||
answers: [] as string[],
|
answers: [] as string[],
|
||||||
}]
|
},
|
||||||
})
|
],
|
||||||
|
});
|
||||||
export const createQuestionVariant: () => QuestionVariant = () => ({
|
export const createQuestionVariant: () => QuestionVariant = () => ({
|
||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
answer: "",
|
answer: "",
|
||||||
|
@ -33,6 +33,7 @@ export interface QuizConfig {
|
|||||||
startpageType: QuizStartpageType;
|
startpageType: QuizStartpageType;
|
||||||
results: QuizResultsType;
|
results: QuizResultsType;
|
||||||
haveRoot: string | null;
|
haveRoot: string | null;
|
||||||
|
theme: "StandardTheme" | "StandardDarkTheme" | "PinkTheme" | "PinkDarkTheme" | "BlackWhiteTheme" | "OliveTheme" | "YellowTheme" | "GoldDarkTheme" | "PurpleTheme" | "BlueTheme" | "BlueDarkTheme";
|
||||||
resultInfo: {
|
resultInfo: {
|
||||||
when: 'before' | 'after' | 'email',
|
when: 'before' | 'after' | 'email',
|
||||||
share: true | false,
|
share: true | false,
|
||||||
@ -95,6 +96,7 @@ export const defaultQuizConfig: QuizConfig = {
|
|||||||
startpageType: null,
|
startpageType: null,
|
||||||
results: null,
|
results: null,
|
||||||
haveRoot: null,
|
haveRoot: null,
|
||||||
|
theme: "StandardTheme",
|
||||||
resultInfo: {
|
resultInfo: {
|
||||||
when: 'after',
|
when: 'after',
|
||||||
share: false,
|
share: false,
|
||||||
|
@ -56,9 +56,9 @@ function CsComponent({
|
|||||||
}: CsComponentProps) {
|
}: CsComponentProps) {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
const { dragQuestionContentId, desireToOpenABranchingModal, canCreatePublic } = useUiTools()
|
const { dragQuestionContentId, desireToOpenABranchingModal, canCreatePublic, someWorkBackend } = useUiTools()
|
||||||
const trashQuestions = useQuestionsStore().questions
|
const trashQuestions = useQuestionsStore().questions
|
||||||
const questions = trashQuestions.filter((question) => question.type !== "result" && question.type !== null)
|
const questions = trashQuestions.filter((question) => question.type !== "result" && question.type !== null && !question.deleted)
|
||||||
const [startCreate, setStartCreate] = useState("");
|
const [startCreate, setStartCreate] = useState("");
|
||||||
const [startRemove, setStartRemove] = useState("");
|
const [startRemove, setStartRemove] = useState("");
|
||||||
|
|
||||||
@ -87,13 +87,6 @@ function CsComponent({
|
|||||||
gearsContainer,
|
gearsContainer,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
// if (!canCreatePublic) updateModalInfoWhyCantCreate(true)
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const cy = cyRef?.current
|
const cy = cyRef?.current
|
||||||
if (desireToOpenABranchingModal) {
|
if (desireToOpenABranchingModal) {
|
||||||
@ -104,11 +97,14 @@ function CsComponent({
|
|||||||
cy?.elements().data("eroticeyeblink", false)
|
cy?.elements().data("eroticeyeblink", false)
|
||||||
}
|
}
|
||||||
}, [desireToOpenABranchingModal])
|
}, [desireToOpenABranchingModal])
|
||||||
|
//Техническая штучка. Гарантирует не отрисовку модалки по первому входу на страничку. И очистка данных по расскоменчиванию
|
||||||
|
//Быстро просто дешево и сердито :)
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
updateOpenedModalSettingsId()
|
updateOpenedModalSettingsId()
|
||||||
// updateRootContentId(quiz.id, "")
|
// updateRootContentId(quiz.id, "")
|
||||||
// clearRuleForAll()
|
// clearRuleForAll()
|
||||||
}, [])
|
}, [])
|
||||||
|
//Отлов mouseup для отрисовки ноды
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (modalQuestionTargetContentId.length !== 0 && modalQuestionParentContentId.length !== 0) {
|
if (modalQuestionTargetContentId.length !== 0 && modalQuestionParentContentId.length !== 0) {
|
||||||
addNode({ parentNodeContentId: modalQuestionParentContentId, targetNodeContentId: modalQuestionTargetContentId })
|
addNode({ parentNodeContentId: modalQuestionParentContentId, targetNodeContentId: modalQuestionTargetContentId })
|
||||||
@ -119,24 +115,39 @@ function CsComponent({
|
|||||||
|
|
||||||
const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => {
|
const addNode = ({ parentNodeContentId, targetNodeContentId }: { parentNodeContentId: string, targetNodeContentId?: string }) => {
|
||||||
if (quiz) {
|
if (quiz) {
|
||||||
|
|
||||||
//запрещаем работу родителя-ребенка если это один и тот же вопрос
|
//запрещаем работу родителя-ребенка если это один и тот же вопрос
|
||||||
if (parentNodeContentId === targetNodeContentId) return
|
if (parentNodeContentId === targetNodeContentId) return
|
||||||
|
|
||||||
|
|
||||||
const cy = cyRef?.current
|
const cy = cyRef?.current
|
||||||
const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length
|
const parentNodeChildren = cy?.$('edge[source = "' + parentNodeContentId + '"]')?.length
|
||||||
|
|
||||||
|
|
||||||
|
const parentQuestion = getQuestionByContentId(parentNodeContentId)
|
||||||
|
//Нельзя добавлять больше 1 ребёнка вопросам типа страница, ползунок, своё поле для ввода и дата
|
||||||
|
if ((parentQuestion?.type === "date" || parentQuestion?.type === "text" || parentQuestion?.type === "number" || parentQuestion?.type === "page")
|
||||||
|
&& parentQuestion.content.rule.children.length === 1
|
||||||
|
) {
|
||||||
|
console.log(parentQuestion.content.rule.children)
|
||||||
|
console.log("parentQuestion.content.rule.children.length === 1",parentQuestion.content.rule.children.length === 1)
|
||||||
|
enqueueSnackbar("у вопроса этого типа может быть только 1 потомок")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа
|
//если есть инфо о выбранном вопросе из модалки - берём родителя из инфо модалки. Иначе из значения дропа
|
||||||
const targetQuestion = { ...getQuestionByContentId(targetNodeContentId || dragQuestionContentId) } as AnyTypedQuizQuestion
|
const targetQuestion = { ...getQuestionByContentId(targetNodeContentId || dragQuestionContentId) } as AnyTypedQuizQuestion
|
||||||
if (Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) {
|
if (Object.keys(targetQuestion).length !== 0 && parentNodeContentId && parentNodeChildren !== undefined) {
|
||||||
clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
|
clearDataAfterAddNode({ parentNodeContentId, targetQuestion, parentNodeChildren })
|
||||||
cy?.data('changed', true)
|
cy?.data('changed', true)
|
||||||
createResult(quiz.backendId, targetQuestion.content.id)
|
createResult(quiz.backendId, targetQuestion.content.id)
|
||||||
|
console.log("Я собираюсь добавить узел из такого вопроса ", targetQuestion)
|
||||||
|
console.log("Я собираюсь добавить узел у которого папаша вот такой ", parentNodeContentId)
|
||||||
const es = cy?.add([
|
const es = cy?.add([
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
id: targetQuestion.content.id,
|
id: targetQuestion.content.id,
|
||||||
label: targetQuestion.title === "" || targetQuestion.title === " " ? "noname" : targetQuestion.title
|
label: targetQuestion.title === "" || targetQuestion.title === " " ? "noname" : targetQuestion.title,
|
||||||
|
parentType: parentNodeContentId
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -188,14 +199,12 @@ function CsComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!noChild) {//детей больше 1
|
if (!noChild) {//детей больше 1
|
||||||
|
console.log("детей ", noChild, " открываем модалку ветвления")
|
||||||
//- предупреждаем стор вопросов об открытии модалки ветвления
|
//- предупреждаем стор вопросов об открытии модалки ветвления
|
||||||
updateOpenedModalSettingsId(targetQuestion.content.id)
|
updateOpenedModalSettingsId(targetQuestion.content.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (startCreate) {
|
if (startCreate) {
|
||||||
addNode({ parentNodeContentId: startCreate });
|
addNode({ parentNodeContentId: startCreate });
|
||||||
@ -211,11 +220,22 @@ function CsComponent({
|
|||||||
}
|
}
|
||||||
}, [startRemove]);
|
}, [startRemove]);
|
||||||
|
|
||||||
|
//Отработка первичного рендера странички графика
|
||||||
|
const firstRender = useRef(true)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log("____________ПЕРВЧИНЫЙ РЕНДЕР____________")
|
||||||
|
console.log("______someWorkBackend______", someWorkBackend)
|
||||||
|
|
||||||
|
if (!someWorkBackend && firstRender.current) {
|
||||||
|
console.log("цс первично отрабатывает")
|
||||||
document
|
document
|
||||||
.querySelector("#root")
|
.querySelector("#root")
|
||||||
?.addEventListener("mouseup", cleardragQuestionContentId);
|
?.addEventListener("mouseup", cleardragQuestionContentId);
|
||||||
const cy = cyRef.current;
|
const cy = cyRef.current;
|
||||||
|
|
||||||
|
console.log("СПИСОК ЭЛЕМЕНТОВ ЦИТОСКЕЙПА В ПЕРВЧИНЫЙ РЕНДЕР")
|
||||||
|
console.log(cy?.elements())
|
||||||
|
|
||||||
const eles = cy?.add(
|
const eles = cy?.add(
|
||||||
storeToNodes(
|
storeToNodes(
|
||||||
questions.filter(
|
questions.filter(
|
||||||
@ -229,8 +249,11 @@ function CsComponent({
|
|||||||
cy?.on("add", () => cy.data("changed", true));
|
cy?.on("add", () => cy.data("changed", true));
|
||||||
cy?.fit();
|
cy?.fit();
|
||||||
//cy?.layout().run()
|
//cy?.layout().run()
|
||||||
|
firstRender.current = false
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
console.log("разрендер")
|
||||||
document
|
document
|
||||||
.querySelector("#root")
|
.querySelector("#root")
|
||||||
?.removeEventListener("mouseup", cleardragQuestionContentId);
|
?.removeEventListener("mouseup", cleardragQuestionContentId);
|
||||||
@ -239,7 +262,7 @@ function CsComponent({
|
|||||||
crossesContainer.current?.remove();
|
crossesContainer.current?.remove();
|
||||||
gearsContainer.current?.remove();
|
gearsContainer.current?.remove();
|
||||||
};
|
};
|
||||||
}, []);
|
}, [someWorkBackend]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -17,6 +17,7 @@ export const FirstNodeField = ({ setOpenedModalQuestions, modalQuestionTargetCon
|
|||||||
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
console.log("компонент с плюсом")
|
||||||
updateOpenedModalSettingsId()
|
updateOpenedModalSettingsId()
|
||||||
updateRootContentId(quiz.id, "")
|
updateRootContentId(quiz.id, "")
|
||||||
clearRuleForAll()
|
clearRuleForAll()
|
||||||
|
@ -21,7 +21,8 @@ export const storeToNodes = (questions: AnyTypedQuizQuestion[]) => {
|
|||||||
if (question.content.rule.parentId) {
|
if (question.content.rule.parentId) {
|
||||||
nodes.push({data: {
|
nodes.push({data: {
|
||||||
id: question.content.id,
|
id: question.content.id,
|
||||||
label: question.title === "" || question.title === " " ? "noname" : question.title
|
label: question.title === "" || question.title === " " ? "noname" : question.title,
|
||||||
|
parentType: question.content.rule.parentId
|
||||||
}})
|
}})
|
||||||
// nodes.push({
|
// nodes.push({
|
||||||
// data: {
|
// data: {
|
||||||
|
@ -7,6 +7,7 @@ import type {
|
|||||||
NodeSingular,
|
NodeSingular,
|
||||||
AbstractEventObject,
|
AbstractEventObject,
|
||||||
} from "cytoscape";
|
} from "cytoscape";
|
||||||
|
import { getQuestionByContentId } from "@root/questions/actions";
|
||||||
|
|
||||||
type usePopperArgs = {
|
type usePopperArgs = {
|
||||||
layoutsContainer: MutableRefObject<HTMLDivElement | null>;
|
layoutsContainer: MutableRefObject<HTMLDivElement | null>;
|
||||||
@ -111,6 +112,8 @@ export const usePopper = ({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cy?.removeAllListeners()
|
||||||
|
|
||||||
nodesInView.toArray()?.forEach((item) => {
|
nodesInView.toArray()?.forEach((item) => {
|
||||||
const node = item as NodeSingularWithPopper;
|
const node = item as NodeSingularWithPopper;
|
||||||
|
|
||||||
@ -199,6 +202,9 @@ export const usePopper = ({
|
|||||||
});
|
});
|
||||||
let gearsPopper: Popper | null = null;
|
let gearsPopper: Popper | null = null;
|
||||||
if (node.data().root !== true) {
|
if (node.data().root !== true) {
|
||||||
|
console.log(node.data("parentType"))
|
||||||
|
const parentQuestion = getQuestionByContentId(node.data("parentType"))
|
||||||
|
|
||||||
gearsPopper = node.popper({
|
gearsPopper = node.popper({
|
||||||
popper: {
|
popper: {
|
||||||
placement: "left",
|
placement: "left",
|
||||||
@ -223,6 +229,10 @@ export const usePopper = ({
|
|||||||
updateOpenedModalSettingsId(item.id());
|
updateOpenedModalSettingsId(item.id());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (parentQuestion?.type === "date" || parentQuestion?.type === "text" || parentQuestion?.type === "number" || parentQuestion?.type === "page") {
|
||||||
|
gearElement.classList.add("popper-gear-none");
|
||||||
|
}
|
||||||
|
|
||||||
return gearElement;
|
return gearElement;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -8,6 +8,7 @@ import { useUiTools } from "@root/uiTools/store";
|
|||||||
|
|
||||||
export const BranchingMap = () => {
|
export const BranchingMap = () => {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
console.log("рендер странички ветвления")
|
||||||
const { dragQuestionContentId } = useUiTools();
|
const { dragQuestionContentId } = useUiTools();
|
||||||
const [modalQuestionParentContentId, setModalQuestionParentContentId] =
|
const [modalQuestionParentContentId, setModalQuestionParentContentId] =
|
||||||
useState<string>("");
|
useState<string>("");
|
||||||
|
@ -43,3 +43,7 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#popper-gears > .popper-gear-none {
|
||||||
|
display: none
|
||||||
|
}
|
@ -27,6 +27,8 @@ import { updateOpenedModalSettingsId } from "@root/uiTools/actions";
|
|||||||
import { updateRootContentId } from "@root/quizes/actions";
|
import { updateRootContentId } from "@root/quizes/actions";
|
||||||
import { useUiTools } from "@root/uiTools/store";
|
import { useUiTools } from "@root/uiTools/store";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
|
import { updateSomeWorkBackend } from "@root/uiTools/actions";
|
||||||
|
import { DeleteFunction } from "@utils/deleteFunc";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
switchState: string;
|
switchState: string;
|
||||||
@ -52,61 +54,6 @@ export default function ButtonsOptions({
|
|||||||
updateDesireToOpenABranchingModal(question.content.id);
|
updateDesireToOpenABranchingModal(question.content.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteFn = () => {
|
|
||||||
if (question.type !== null) {
|
|
||||||
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
|
|
||||||
updateRootContentId(quiz.id, "");
|
|
||||||
clearRuleForAll();
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
} else if (question.content.rule.parentId.length > 0) { //удалить из стора вопрос из дерева и очистить его потомков
|
|
||||||
const clearQuestions = [] as string[];
|
|
||||||
|
|
||||||
//записываем потомков , а их результаты удаляем
|
|
||||||
const getChildren = (parentQuestion: AnyTypedQuizQuestion) => {
|
|
||||||
questions.forEach((targetQuestion) => {
|
|
||||||
if (targetQuestion.type !== null && targetQuestion.content.rule.parentId === parentQuestion.content.id) {//если у вопроса совпал родитель с родителем => он потомок, в кучу его
|
|
||||||
if (targetQuestion.type !== "result" && targetQuestion.type !== null) {
|
|
||||||
if (!clearQuestions.includes(targetQuestion.content.id)) clearQuestions.push(targetQuestion.content.id);
|
|
||||||
getChildren(targetQuestion); //и ищем его потомков
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
getChildren(question);
|
|
||||||
//чистим потомков от инфы ветвления
|
|
||||||
clearQuestions.forEach((id) => {
|
|
||||||
updateQuestion(id, question => {
|
|
||||||
question.content.rule.parentId = "";
|
|
||||||
question.content.rule.main = [];
|
|
||||||
question.content.rule.default = "";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//чистим rule родителя
|
|
||||||
const parentQuestion = getQuestionByContentId(question.content.rule.parentId);
|
|
||||||
const newRule = {};
|
|
||||||
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== question.content.id); //удаляем условия перехода от родителя к этому вопросу
|
|
||||||
newRule.parentId = parentQuestion.content.rule.parentId;
|
|
||||||
newRule.default = parentQuestion.content.rule.parentId === question.content.id ? "" : parentQuestion.content.rule.parentId;
|
|
||||||
newRule.children = [...parentQuestion.content.rule.children].splice(parentQuestion.content.rule.children.indexOf(question.content.id), 1);
|
|
||||||
|
|
||||||
updateQuestion(question.content.rule.parentId, (PQ) => {
|
|
||||||
PQ.content.rule = newRule;
|
|
||||||
});
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
|
|
||||||
const result = questions.find(q => q.type === "result" && q.content.rule.parentId === question.content.id)
|
|
||||||
if (result) deleteQuestion(result.id);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const buttonSetting: {
|
const buttonSetting: {
|
||||||
icon: JSX.Element;
|
icon: JSX.Element;
|
||||||
title: string;
|
title: string;
|
||||||
@ -171,53 +118,57 @@ export default function ButtonsOptions({
|
|||||||
>
|
>
|
||||||
{buttonSetting.map(({ icon, title, value, myFunc }) => (
|
{buttonSetting.map(({ icon, title, value, myFunc }) => (
|
||||||
<Box key={value}>
|
<Box key={value}>
|
||||||
{value === "branching" ? (
|
{value === "branching" ?
|
||||||
<Tooltip
|
question.type === "page" || question.type === "text" || question.type === "date" || question.type === "number" ?
|
||||||
arrow
|
<></>
|
||||||
placement="right"
|
:
|
||||||
componentsProps={{
|
(
|
||||||
tooltip: {
|
// <Tooltip
|
||||||
sx: {
|
// arrow
|
||||||
background: "#fff",
|
// placement="right"
|
||||||
borderRadius: "6px",
|
// componentsProps={{
|
||||||
color: "#9A9AAF",
|
// tooltip: {
|
||||||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
// sx: {
|
||||||
"& .MuiTooltip-arrow": {
|
// background: "#fff",
|
||||||
color: "#FFF",
|
// borderRadius: "6px",
|
||||||
},
|
// color: "#9A9AAF",
|
||||||
},
|
// boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||||
},
|
// "& .MuiTooltip-arrow": {
|
||||||
}}
|
// color: "#FFF",
|
||||||
title={
|
// },
|
||||||
<Box>
|
// },
|
||||||
<Typography
|
// },
|
||||||
sx={{
|
// }}
|
||||||
color: "#4D4D4D",
|
// title={
|
||||||
fontWeight: "bold",
|
// <Box>
|
||||||
fontSize: "14px",
|
// <Typography
|
||||||
marginBottom: "10px",
|
// sx={{
|
||||||
}}
|
// color: "#4D4D4D",
|
||||||
>
|
// fontWeight: "bold",
|
||||||
Будет показан при условии
|
// fontSize: "14px",
|
||||||
</Typography>
|
// marginBottom: "10px",
|
||||||
<Typography sx={{ fontWeight: "bold", fontSize: "12px" }}>
|
// }}
|
||||||
Название
|
// >
|
||||||
</Typography>
|
// Будет показан при условии
|
||||||
<Typography
|
// </Typography>
|
||||||
sx={{
|
// <Typography sx={{ fontWeight: "bold", fontSize: "12px" }}>
|
||||||
fontWeight: "bold",
|
// Название
|
||||||
fontSize: "12px",
|
// </Typography>
|
||||||
marginBottom: "10px",
|
// <Typography
|
||||||
}}
|
// sx={{
|
||||||
>
|
// fontWeight: "bold",
|
||||||
Условие 1, Условие 2
|
// fontSize: "12px",
|
||||||
</Typography>
|
// marginBottom: "10px",
|
||||||
<Typography sx={{ color: "#7E2AEA", fontSize: "12px" }}>
|
// }}
|
||||||
Все условия обязательны
|
// >
|
||||||
</Typography>
|
// Условие 1, Условие 2
|
||||||
</Box>
|
// </Typography>
|
||||||
}
|
// <Typography sx={{ color: "#7E2AEA", fontSize: "12px" }}>
|
||||||
>
|
// Все условия обязательны
|
||||||
|
// </Typography>
|
||||||
|
// </Box>
|
||||||
|
// }
|
||||||
|
// >
|
||||||
<MiniButtonSetting
|
<MiniButtonSetting
|
||||||
key={title}
|
key={title}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -246,7 +197,7 @@ export default function ButtonsOptions({
|
|||||||
{icon}
|
{icon}
|
||||||
{isWrappMiniButtonSetting ? null : title}
|
{isWrappMiniButtonSetting ? null : title}
|
||||||
</MiniButtonSetting>
|
</MiniButtonSetting>
|
||||||
</Tooltip>
|
// </Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<MiniButtonSetting
|
<MiniButtonSetting
|
||||||
@ -279,7 +230,7 @@ export default function ButtonsOptions({
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<>
|
{/* <>
|
||||||
<MiniButtonSetting
|
<MiniButtonSetting
|
||||||
onClick={undefined} // TODO
|
onClick={undefined} // TODO
|
||||||
sx={{
|
sx={{
|
||||||
@ -310,7 +261,7 @@ export default function ButtonsOptions({
|
|||||||
>
|
>
|
||||||
<VectorQuestions style={{ color: "#FC712F", fontSize: "9px" }} />
|
<VectorQuestions style={{ color: "#FC712F", fontSize: "9px" }} />
|
||||||
</MiniButtonSetting>
|
</MiniButtonSetting>
|
||||||
</>
|
</> */}
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -334,7 +285,7 @@ export default function ButtonsOptions({
|
|||||||
if(question.content.rule.parentId.length !== 0) {
|
if(question.content.rule.parentId.length !== 0) {
|
||||||
setOpenDelete(true)
|
setOpenDelete(true)
|
||||||
} else {
|
} else {
|
||||||
deleteQuestionWithTimeout(question.id, deleteFn);
|
deleteQuestionWithTimeout(question.id, () => DeleteFunction(questions, question, quiz));
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
@ -376,7 +327,7 @@ export default function ButtonsOptions({
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
sx={{ minWidth: "150px" }}
|
sx={{ minWidth: "150px" }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteQuestionWithTimeout(question.id, deleteFn);
|
deleteQuestionWithTimeout(question.id, () => DeleteFunction(questions, question, quiz));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Подтвердить
|
Подтвердить
|
||||||
|
@ -29,6 +29,8 @@ import { enqueueSnackbar } from "notistack";
|
|||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { updateRootContentId } from "@root/quizes/actions";
|
import { updateRootContentId } from "@root/quizes/actions";
|
||||||
import {AnyTypedQuizQuestion} from "@model/questionTypes/shared";
|
import {AnyTypedQuizQuestion} from "@model/questionTypes/shared";
|
||||||
|
import { updateSomeWorkBackend } from "@root/uiTools/actions";
|
||||||
|
import { DeleteFunction } from "@utils/deleteFunc";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -53,60 +55,6 @@ export default function ButtonsOptionsAndPict({
|
|||||||
|
|
||||||
const [openDelete, setOpenDelete] = useState<boolean>(false);
|
const [openDelete, setOpenDelete] = useState<boolean>(false);
|
||||||
|
|
||||||
const deleteFn = () => {
|
|
||||||
if (question.type !== null) {
|
|
||||||
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
|
|
||||||
updateRootContentId(quiz.id, "");
|
|
||||||
clearRuleForAll();
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
} else if (question.content.rule.parentId.length > 0) { //удалить из стора вопрос из дерева и очистить его потомков
|
|
||||||
const clearQuestions = [] as string[];
|
|
||||||
|
|
||||||
//записываем потомков , а их результаты удаляем
|
|
||||||
const getChildren = (parentQuestion: AnyTypedQuizQuestion) => {
|
|
||||||
questions.forEach((targetQuestion) => {
|
|
||||||
if (targetQuestion.content.rule.parentId === parentQuestion.content.id) {//если у вопроса совпал родитель с родителем => он потомок, в кучу его
|
|
||||||
if (targetQuestion.type !== null && targetQuestion.type !== "result") {
|
|
||||||
if (!clearQuestions.includes(targetQuestion.content.id)) clearQuestions.push(targetQuestion.content.id);
|
|
||||||
getChildren(targetQuestion); //и ищем его потомков
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
getChildren(question);
|
|
||||||
//чистим потомков от инфы ветвления
|
|
||||||
clearQuestions.forEach((id) => {
|
|
||||||
updateQuestion(id, question => {
|
|
||||||
question.content.rule.parentId = "";
|
|
||||||
question.content.rule.main = [];
|
|
||||||
question.content.rule.default = "";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//чистим rule родителя
|
|
||||||
const parentQuestion = getQuestionByContentId(question.content.rule.parentId);
|
|
||||||
const newRule = {};
|
|
||||||
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== question.content.id); //удаляем условия перехода от родителя к этому вопросу
|
|
||||||
newRule.parentId = parentQuestion.content.rule.parentId;
|
|
||||||
newRule.default = parentQuestion.content.rule.parentId === question.content.id ? "" : parentQuestion.content.rule.parentId;
|
|
||||||
newRule.children = [...parentQuestion.content.rule.children].splice(parentQuestion.content.rule.children.indexOf(question.content.id), 1);
|
|
||||||
|
|
||||||
updateQuestion(question.content.rule.parentId, (PQ) => {
|
|
||||||
PQ.content.rule = newRule;
|
|
||||||
});
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
|
|
||||||
const result = questions.find(q => q.type === "result" && q.content.rule.parentId === question.content.id)
|
|
||||||
if (result) deleteQuestion(result.id);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (question.deleteTimeoutId) {
|
if (question.deleteTimeoutId) {
|
||||||
clearTimeout(question.deleteTimeoutId);
|
clearTimeout(question.deleteTimeoutId);
|
||||||
@ -199,7 +147,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
{isIconMobile ? null : "Подсказка"}
|
{isIconMobile ? null : "Подсказка"}
|
||||||
</MiniButtonSetting>
|
</MiniButtonSetting>
|
||||||
<>
|
<>
|
||||||
<Tooltip
|
{/* <Tooltip
|
||||||
arrow
|
arrow
|
||||||
placement="right"
|
placement="right"
|
||||||
componentsProps={{
|
componentsProps={{
|
||||||
@ -244,7 +192,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
> */}
|
||||||
<MiniButtonSetting
|
<MiniButtonSetting
|
||||||
onMouseEnter={() => setButtonHover("branching")}
|
onMouseEnter={() => setButtonHover("branching")}
|
||||||
onMouseLeave={() => setButtonHover("")}
|
onMouseLeave={() => setButtonHover("")}
|
||||||
@ -284,7 +232,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
/>
|
/>
|
||||||
{isIconMobile ? null : "Ветвление"}
|
{isIconMobile ? null : "Ветвление"}
|
||||||
</MiniButtonSetting>
|
</MiniButtonSetting>
|
||||||
</Tooltip>
|
{/* </Tooltip> */}
|
||||||
<MiniButtonSetting
|
<MiniButtonSetting
|
||||||
onMouseEnter={() => setButtonHover("image")}
|
onMouseEnter={() => setButtonHover("image")}
|
||||||
onMouseLeave={() => setButtonHover("")}
|
onMouseLeave={() => setButtonHover("")}
|
||||||
@ -318,7 +266,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
/>
|
/>
|
||||||
{isIconMobile ? null : "Изображение"}
|
{isIconMobile ? null : "Изображение"}
|
||||||
</MiniButtonSetting>
|
</MiniButtonSetting>
|
||||||
<MiniButtonSetting
|
{/* <MiniButtonSetting
|
||||||
onClick={() => setOpenedReallyChangingModal(true)}
|
onClick={() => setOpenedReallyChangingModal(true)}
|
||||||
sx={{
|
sx={{
|
||||||
minWidth: "30px",
|
minWidth: "30px",
|
||||||
@ -347,7 +295,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<VectorQuestions style={{ color: "#FC712F", fontSize: "9px" }} />
|
<VectorQuestions style={{ color: "#FC712F", fontSize: "9px" }} />
|
||||||
</MiniButtonSetting>
|
</MiniButtonSetting> */}
|
||||||
</>
|
</>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -370,7 +318,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
if(question.content.rule.parentId.length !== 0) {
|
if(question.content.rule.parentId.length !== 0) {
|
||||||
setOpenDelete(true)
|
setOpenDelete(true)
|
||||||
} else {
|
} else {
|
||||||
deleteQuestionWithTimeout(question.id, deleteFn);
|
deleteQuestionWithTimeout(question.id, () => DeleteFunction(questions, question, quiz));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
data-cy="delete-question"
|
data-cy="delete-question"
|
||||||
@ -411,7 +359,7 @@ export default function ButtonsOptionsAndPict({
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
sx={{ minWidth: "150px" }}
|
sx={{ minWidth: "150px" }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteQuestionWithTimeout(question.id, deleteFn);
|
deleteQuestionWithTimeout(question.id, () => DeleteFunction(questions, question, quiz));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Подтвердить
|
Подтвердить
|
||||||
|
@ -14,7 +14,7 @@ import { getQuestionByContentId } from "@root/questions/actions";
|
|||||||
import { updateDeleteId } from "@root/uiTools/actions";
|
import { updateDeleteId } from "@root/uiTools/actions";
|
||||||
import { useUiTools } from "@root/uiTools/store";
|
import { useUiTools } from "@root/uiTools/store";
|
||||||
|
|
||||||
import { CheckboxIcon } from "@icons/Checkbox";
|
import CheckboxIcon from "@icons/Checkbox";
|
||||||
|
|
||||||
type DeleteNodeModalProps = {
|
type DeleteNodeModalProps = {
|
||||||
removeNode?: (id: string) => void;
|
removeNode?: (id: string) => void;
|
||||||
|
@ -12,11 +12,13 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { changeQuestionType } from "@root/questions/actions";
|
import { changeQuestionType, updateQuestion } from "@root/questions/actions";
|
||||||
import type { RefObject } from "react";
|
import type { RefObject } from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import type { AnyTypedQuizQuestion, UntypedQuizQuestion } from "../../../model/questionTypes/shared";
|
import type { AnyTypedQuizQuestion, UntypedQuizQuestion } from "../../../model/questionTypes/shared";
|
||||||
import { BUTTON_TYPE_QUESTIONS } from "../TypeQuestions";
|
import { BUTTON_TYPE_QUESTIONS } from "../TypeQuestions";
|
||||||
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
|
import { updateSomeWorkBackend } from "@root/uiTools/actions";
|
||||||
|
|
||||||
|
|
||||||
type ChooseAnswerModalProps = {
|
type ChooseAnswerModalProps = {
|
||||||
@ -36,6 +38,7 @@ export const ChooseAnswerModal = ({
|
|||||||
}: ChooseAnswerModalProps) => {
|
}: ChooseAnswerModalProps) => {
|
||||||
const [openModal, setOpenModal] = useState<boolean>(false);
|
const [openModal, setOpenModal] = useState<boolean>(false);
|
||||||
const [selectedValue, setSelectedValue] = useState<QuestionType>("text");
|
const [selectedValue, setSelectedValue] = useState<QuestionType>("text");
|
||||||
|
const { questions } = useQuestionsStore()
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -94,8 +97,8 @@ export const ChooseAnswerModal = ({
|
|||||||
background: "#FFFFFF",
|
background: "#FFFFFF",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h6" sx={{textAlign: "center"}}>
|
<Typography variant="h6" sx={{ textAlign: "center" }}>
|
||||||
Все настройки, кроме заголовка вопроса будут сброшены <br/>
|
Все настройки, кроме заголовка вопроса будут сброшены <br />
|
||||||
(вопрос всё ещё будет участвовать в ветвлении, но его условия будут сброшены)
|
(вопрос всё ещё будет участвовать в ветвлении, но его условия будут сброшены)
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
@ -116,9 +119,44 @@ export const ChooseAnswerModal = ({
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
sx={{ minWidth: "150px" }}
|
sx={{ minWidth: "150px" }}
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
|
|
||||||
|
updateSomeWorkBackend(true)
|
||||||
setOpenModal(false);
|
setOpenModal(false);
|
||||||
|
const clearQuestions = [] as string[];
|
||||||
|
|
||||||
|
//записываем потомков , а их результаты удаляем
|
||||||
|
const getChildren = (parentQuestion: any) => {
|
||||||
|
questions.forEach((targetQuestion) => {
|
||||||
|
if (targetQuestion.type !== null && targetQuestion.content.rule.parentId === parentQuestion.content.id) {//если у вопроса совпал родитель с родителем => он потомок, в кучу его
|
||||||
|
if (targetQuestion.type !== "result" && targetQuestion.type !== null) {
|
||||||
|
if (!clearQuestions.includes(targetQuestion.content.id)) clearQuestions.push(targetQuestion.content.id);
|
||||||
|
getChildren(targetQuestion); //и ищем его потомков
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
getChildren(question);
|
||||||
|
|
||||||
|
updateQuestion(question.id, q => {
|
||||||
|
q.content.rule.parentId = "";
|
||||||
|
q.content.rule.main = [];
|
||||||
|
q.content.rule.children = [];
|
||||||
|
q.content.rule.default = "";
|
||||||
|
});
|
||||||
|
//чистим потомков от инфы ветвления
|
||||||
|
await Promise.allSettled(
|
||||||
|
clearQuestions.map((id) => {
|
||||||
|
updateQuestion(id, question => {
|
||||||
|
question.content.rule.parentId = "";
|
||||||
|
question.content.rule.main = [];
|
||||||
|
question.content.rule.children = [];
|
||||||
|
question.content.rule.default = "";
|
||||||
|
});
|
||||||
|
})
|
||||||
|
)
|
||||||
changeQuestionType(question.id, selectedValue);
|
changeQuestionType(question.id, selectedValue);
|
||||||
|
updateSomeWorkBackend(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Подтвердить
|
Подтвердить
|
||||||
|
@ -29,7 +29,6 @@ function DraggableListItem({ question, isDragging, index }: Props) {
|
|||||||
updateEditSomeQuestion();
|
updateEditSomeQuestion();
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
}
|
}
|
||||||
}, [editSomeQuestion]);
|
}, [editSomeQuestion]);
|
||||||
|
|
||||||
|
@ -55,6 +55,8 @@ import TypeQuestions from "../TypeQuestions";
|
|||||||
import { QuestionType } from "@model/question/question";
|
import { QuestionType } from "@model/question/question";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
|
import { updateSomeWorkBackend } from "@root/uiTools/actions";
|
||||||
|
import { DeleteFunction } from "@utils/deleteFunc";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
|
||||||
@ -87,61 +89,6 @@ const maxLengthTextField = 225;
|
|||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
const deleteFn = () => {
|
|
||||||
if (question.type !== null) {
|
|
||||||
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
|
|
||||||
updateRootContentId(quiz.id, "");
|
|
||||||
clearRuleForAll();
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
} else if (question.content.rule.parentId.length > 0) { //удалить из стора вопрос из дерева и очистить его потомков
|
|
||||||
const clearQuestions = [] as string[];
|
|
||||||
|
|
||||||
//записываем потомков , а их результаты удаляем
|
|
||||||
const getChildren = (parentQuestion: AnyTypedQuizQuestion) => {
|
|
||||||
questions.forEach((targetQuestion) => {
|
|
||||||
if (targetQuestion.type !== null && targetQuestion.content.rule.parentId === parentQuestion.content.id) {//если у вопроса совпал родитель с родителем => он потомок, в кучу его
|
|
||||||
if (targetQuestion.type !== null && targetQuestion.type !== "result") {
|
|
||||||
if (!clearQuestions.includes(targetQuestion.content.id)) clearQuestions.push(targetQuestion.content.id);
|
|
||||||
getChildren(targetQuestion); //и ищем его потомков
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
getChildren(question);
|
|
||||||
//чистим потомков от инфы ветвления
|
|
||||||
clearQuestions.forEach((id) => {
|
|
||||||
updateQuestion(id, question => {
|
|
||||||
question.content.rule.parentId = "";
|
|
||||||
question.content.rule.main = [];
|
|
||||||
question.content.rule.default = "";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//чистим rule родителя
|
|
||||||
const parentQuestion = getQuestionByContentId(question.content.rule.parentId);
|
|
||||||
const newRule = {};
|
|
||||||
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== question.content.id); //удаляем условия перехода от родителя к этому вопросу
|
|
||||||
newRule.parentId = parentQuestion.content.rule.parentId;
|
|
||||||
newRule.default = parentQuestion.content.rule.parentId === question.content.id ? "" : parentQuestion.content.rule.parentId;
|
|
||||||
newRule.children = [...parentQuestion.content.rule.children].splice(parentQuestion.content.rule.children.indexOf(question.content.id), 1);
|
|
||||||
|
|
||||||
updateQuestion(question.content.rule.parentId, (PQ) => {
|
|
||||||
PQ.content.rule = newRule;
|
|
||||||
});
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
|
|
||||||
const result = questions.find(q => q.type === "result" && q.content.rule.parentId === question.content.id)
|
|
||||||
if (result) deleteQuestion(result.id);
|
|
||||||
} else {
|
|
||||||
deleteQuestion(question.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleInputFocus = () => {
|
const handleInputFocus = () => {
|
||||||
setIsTextFieldtActive(true);
|
setIsTextFieldtActive(true);
|
||||||
};
|
};
|
||||||
@ -333,7 +280,7 @@ const maxLengthTextField = 225;
|
|||||||
if (question.content.rule.parentId.length !== 0) {
|
if (question.content.rule.parentId.length !== 0) {
|
||||||
setOpenDelete(true);
|
setOpenDelete(true);
|
||||||
} else {
|
} else {
|
||||||
deleteQuestionWithTimeout(question.id, deleteFn);
|
deleteQuestionWithTimeout(question.id, () => DeleteFunction(questions, question, quiz));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
data-cy="delete-question"
|
data-cy="delete-question"
|
||||||
@ -371,7 +318,7 @@ const maxLengthTextField = 225;
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
sx={{ minWidth: "150px" }}
|
sx={{ minWidth: "150px" }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteQuestionWithTimeout(question.id, deleteFn);
|
deleteQuestionWithTimeout(question.id, () => DeleteFunction(questions, question, quiz));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Подтвердить
|
Подтвердить
|
||||||
|
@ -12,21 +12,24 @@ import Page from "@icons/questionsPage/page";
|
|||||||
import RatingIcon from "@icons/questionsPage/rating";
|
import RatingIcon from "@icons/questionsPage/rating";
|
||||||
import Slider from "@icons/questionsPage/slider";
|
import Slider from "@icons/questionsPage/slider";
|
||||||
import {
|
import {
|
||||||
Box, Checkbox,
|
Box,
|
||||||
|
Checkbox,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
Paper, TextField,
|
Paper,
|
||||||
|
TextField,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import {
|
import {
|
||||||
copyQuestion,
|
copyQuestion,
|
||||||
deleteQuestion, deleteQuestionWithTimeout,
|
deleteQuestion,
|
||||||
|
deleteQuestionWithTimeout,
|
||||||
toggleExpandQuestion,
|
toggleExpandQuestion,
|
||||||
updateQuestion,
|
updateQuestion,
|
||||||
updateUntypedQuestion
|
updateUntypedQuestion,
|
||||||
} from "@root/questions/actions";
|
} from "@root/questions/actions";
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
@ -110,15 +113,13 @@ export default function QuestionsPageCard({ question, questionIndex, draggablePr
|
|||||||
<TextField
|
<TextField
|
||||||
placeholder={`Заголовок ${questionIndex + 1} вопроса`}
|
placeholder={`Заголовок ${questionIndex + 1} вопроса`}
|
||||||
value={question.title}
|
value={question.title}
|
||||||
onChange={({target}) => setTitle(target.value)}
|
onChange={({ target }) => setTitle(target.value)}
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
margin: isMobile ? "10px 0" : 0,
|
margin: isMobile ? "10px 0" : 0,
|
||||||
"& .MuiInputBase-root": {
|
"& .MuiInputBase-root": {
|
||||||
color: "#000000",
|
color: "#000000",
|
||||||
backgroundColor: question.expanded
|
backgroundColor: question.expanded ? theme.palette.background.default : "transparent",
|
||||||
? theme.palette.background.default
|
|
||||||
: "transparent",
|
|
||||||
height: "48px",
|
height: "48px",
|
||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
".MuiOutlinedInput-notchedOutline": {
|
".MuiOutlinedInput-notchedOutline": {
|
||||||
@ -137,7 +138,7 @@ export default function QuestionsPageCard({ question, questionIndex, draggablePr
|
|||||||
<InputAdornment
|
<InputAdornment
|
||||||
ref={anchorRef}
|
ref={anchorRef}
|
||||||
position="start"
|
position="start"
|
||||||
sx={{cursor: "pointer"}}
|
sx={{ cursor: "pointer" }}
|
||||||
onClick={() => setOpen((isOpened) => !isOpened)}
|
onClick={() => setOpen((isOpened) => !isOpened)}
|
||||||
>
|
>
|
||||||
{IconAndrom(question.type)}
|
{IconAndrom(question.type)}
|
||||||
@ -172,7 +173,7 @@ export default function QuestionsPageCard({ question, questionIndex, draggablePr
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{padding: "0", margin: "5px"}}
|
sx={{ padding: "0", margin: "5px" }}
|
||||||
disableRipple
|
disableRipple
|
||||||
data-cy="expand-question"
|
data-cy="expand-question"
|
||||||
onClick={() => toggleExpandQuestion(question.id)}
|
onClick={() => toggleExpandQuestion(question.id)}
|
||||||
@ -235,13 +236,8 @@ export default function QuestionsPageCard({ question, questionIndex, draggablePr
|
|||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton sx={{ padding: "0" }} onClick={() => copyQuestion(question.id, question.quizId)}>
|
||||||
sx={{ padding: "0" }}
|
<CopyIcon style={{ color: theme.palette.brightPurple.main }} />
|
||||||
onClick={() => copyQuestion(question.id, question.quizId)}
|
|
||||||
>
|
|
||||||
<CopyIcon
|
|
||||||
style={{ color: theme.palette.brightPurple.main }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
@ -251,13 +247,10 @@ export default function QuestionsPageCard({ question, questionIndex, draggablePr
|
|||||||
margin: "0 5px 0 10px",
|
margin: "0 5px 0 10px",
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteQuestionWithTimeout(question.id, deleteQuestion(question.id));
|
deleteQuestionWithTimeout(question.id, () => deleteQuestion(question.id));
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeleteIcon
|
<DeleteIcon style={{ color: theme.palette.brightPurple.main }} />
|
||||||
style={{ color: theme.palette.brightPurple.main }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@ -289,16 +282,16 @@ export default function QuestionsPageCard({ question, questionIndex, draggablePr
|
|||||||
}}
|
}}
|
||||||
{...draggableProps}
|
{...draggableProps}
|
||||||
>
|
>
|
||||||
<PointsIcon style={{color: "#4D4D4D", fontSize: "30px"}}/>
|
<PointsIcon style={{ color: "#4D4D4D", fontSize: "30px" }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{question.expanded && (
|
{question.expanded && (
|
||||||
<>
|
<>
|
||||||
{question.type === null ? (
|
{question.type === null ? (
|
||||||
<FormTypeQuestions question={question}/>
|
<FormTypeQuestions question={question} />
|
||||||
) : (
|
) : (
|
||||||
<SwitchQuestionsPage question={question}/>
|
<SwitchQuestionsPage question={question} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
import { VideofileIcon } from "@icons/questionsPage/VideofileIcon";
|
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { updateQuestion } from "@root/questions/actions";
|
||||||
import { updateQuestion, uploadQuestionImage } from "@root/questions/actions";
|
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
|
||||||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
|
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
|
||||||
import ButtonsOptions from "../ButtonsOptions";
|
import ButtonsOptions from "../ButtonsOptions";
|
||||||
import { UploadImageModal } from "../UploadImage/UploadImageModal";
|
|
||||||
import { UploadVideoModal } from "../UploadVideoModal";
|
|
||||||
import SwitchPageOptions from "./switchPageOptions";
|
import SwitchPageOptions from "./switchPageOptions";
|
||||||
import { useDisclosure } from "../../../utils/useDisclosure";
|
import { MediaSelectionAndDisplay } from "@ui_kit/MediaSelectionAndDisplay";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
disableInput?: boolean;
|
disableInput?: boolean;
|
||||||
@ -20,16 +15,11 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function PageOptions({ disableInput, question }: Props) {
|
export default function PageOptions({ disableInput, question }: Props) {
|
||||||
const [openVideoModal, setOpenVideoModal] = useState<boolean>(false);
|
|
||||||
const [switchState, setSwitchState] = useState("setting");
|
const [switchState, setSwitchState] = useState("setting");
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(980));
|
const isTablet = useMediaQuery(theme.breakpoints.down(980));
|
||||||
const isFigmaTablet = useMediaQuery(theme.breakpoints.down(990));
|
const isFigmaTablet = useMediaQuery(theme.breakpoints.down(990));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(780));
|
const isMobile = useMediaQuery(theme.breakpoints.down(780));
|
||||||
const quizQid = useCurrentQuiz()?.qid;
|
|
||||||
const { isCropModalOpen, openCropModal, closeCropModal, imageBlob, originalImageUrl, setCropModalImageBlob } =
|
|
||||||
useCropModalState();
|
|
||||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
|
||||||
|
|
||||||
const setText = useDebouncedCallback((value) => {
|
const setText = useDebouncedCallback((value) => {
|
||||||
updateQuestion(question.id, (question) => {
|
updateQuestion(question.id, (question) => {
|
||||||
@ -43,25 +33,6 @@ export default function PageOptions({ disableInput, question }: Props) {
|
|||||||
setSwitchState(data);
|
setSwitchState(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handleImageUpload(file: File) {
|
|
||||||
const url = await uploadQuestionImage(question.id, quizQid, file, (question, url) => {
|
|
||||||
if (question.type !== "page") return;
|
|
||||||
|
|
||||||
question.content.picture = url;
|
|
||||||
question.content.originalPicture = url;
|
|
||||||
});
|
|
||||||
closeImageUploadModal();
|
|
||||||
openCropModal(file, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCropModalSaveClick(imageBlob: Blob) {
|
|
||||||
uploadQuestionImage(question.id, quizQid, imageBlob, (question, url) => {
|
|
||||||
if (question.type !== "page") return;
|
|
||||||
|
|
||||||
question.content.picture = url;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
@ -71,7 +42,6 @@ export default function PageOptions({ disableInput, question }: Props) {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
px: "20px",
|
px: "20px",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: isMobile ? "25px" : "20px",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: disableInput ? "none" : "", mt: isMobile ? "15px" : "0px" }}>
|
<Box sx={{ display: disableInput ? "none" : "", mt: isMobile ? "15px" : "0px" }}>
|
||||||
@ -82,179 +52,7 @@ export default function PageOptions({ disableInput, question }: Props) {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<MediaSelectionAndDisplay resultData={question} />
|
||||||
sx={{
|
|
||||||
mb: "20px",
|
|
||||||
ml: isTablet ? "0px" : "60px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "28px",
|
|
||||||
justifyContent: isMobile ? "space-between" : null,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AddOrEditImageButton
|
|
||||||
imageSrc={question.content.picture}
|
|
||||||
onImageClick={() => {
|
|
||||||
if (question.content.picture) {
|
|
||||||
return openCropModal(question.content.picture, question.content.originalPicture);
|
|
||||||
}
|
|
||||||
|
|
||||||
openImageUploadModal();
|
|
||||||
}}
|
|
||||||
onPlusClick={() => {
|
|
||||||
openImageUploadModal();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
display: isMobile ? "none" : "block",
|
|
||||||
fontWeight: 400,
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "18.96px",
|
|
||||||
color: question.content.useImage ? "#7E2AEA" : "#9A9AAF",
|
|
||||||
}}
|
|
||||||
onClick={() =>
|
|
||||||
updateQuestion(question.id, (question) => ((question as QuizQuestionPage).content.useImage = true))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Изображение
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<UploadImageModal
|
|
||||||
isOpen={isImageUploadOpen}
|
|
||||||
onClose={closeImageUploadModal}
|
|
||||||
handleImageChange={handleImageUpload}
|
|
||||||
/>
|
|
||||||
<CropModal
|
|
||||||
isOpen={isCropModalOpen}
|
|
||||||
imageBlob={imageBlob}
|
|
||||||
originalImageUrl={originalImageUrl}
|
|
||||||
setCropModalImageBlob={setCropModalImageBlob}
|
|
||||||
onClose={closeCropModal}
|
|
||||||
onSaveImageClick={handleCropModalSaveClick}
|
|
||||||
/>
|
|
||||||
<Typography> или</Typography>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isMobile ? (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
width: "120px",
|
|
||||||
position: "relative",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
background: "#EEE4FC",
|
|
||||||
height: "40px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
borderTopLeftRadius: "4px",
|
|
||||||
borderBottomLeftRadius: "4px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<VideofileIcon
|
|
||||||
style={{
|
|
||||||
color: "#7E2AEA",
|
|
||||||
fontSize: "20px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
width: "20px",
|
|
||||||
background: "#EEE4FC",
|
|
||||||
height: "40px",
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: "#7E2AEA",
|
|
||||||
borderTopRightRadius: "4px",
|
|
||||||
borderBottomRightRadius: "4px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "60px",
|
|
||||||
height: "40px",
|
|
||||||
background: "#EEE4FC",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", width: "100%" }}>
|
|
||||||
<VideofileIcon fontSize="22px" color="#7E2AEA" />
|
|
||||||
</Box>
|
|
||||||
<span
|
|
||||||
onClick={() => setOpenVideoModal(true)}
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
background: "#7E2AEA",
|
|
||||||
height: "100%",
|
|
||||||
width: "25px",
|
|
||||||
color: "white",
|
|
||||||
fontSize: "15px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</span>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
display: isMobile ? "none" : "block",
|
|
||||||
fontWeight: 400,
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "18.96px",
|
|
||||||
color: question.content.useImage ? "#9A9AAF" : "#7E2AEA",
|
|
||||||
}}
|
|
||||||
onClick={() =>
|
|
||||||
updateQuestion(question.id, (question) => ((question as QuizQuestionPage).content.useImage = false))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Видео
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<UploadVideoModal
|
|
||||||
open={openVideoModal}
|
|
||||||
onClose={() => setOpenVideoModal(false)}
|
|
||||||
video={question.content.video}
|
|
||||||
onUpload={(url) => {
|
|
||||||
updateQuestion(question.id, (question) => {
|
|
||||||
if (question.type !== "page") return;
|
|
||||||
|
|
||||||
question.content.video = url;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
<ButtonsOptions switchState={switchState} SSHC={SSHC} question={question} />
|
<ButtonsOptions switchState={switchState} SSHC={SSHC} question={question} />
|
||||||
<SwitchPageOptions switchState={switchState} question={question} />
|
<SwitchPageOptions switchState={switchState} question={question} />
|
||||||
|
@ -1,26 +1,47 @@
|
|||||||
|
import { useEffect, useLayoutEffect } from "react";
|
||||||
import {
|
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||||
Box, useMediaQuery, useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { DraggableList } from "./DraggableList";
|
import { DraggableList } from "./DraggableList";
|
||||||
import { SwitchBranchingPanel } from "./SwitchBranchingPanel";
|
import { SwitchBranchingPanel } from "./SwitchBranchingPanel";
|
||||||
import { BranchingMap } from "./BranchingMap";
|
import { BranchingMap } from "./BranchingMap";
|
||||||
import {useQuestionsStore} from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { useUiTools } from "@root/uiTools/store";
|
import { useUiTools } from "@root/uiTools/store";
|
||||||
|
import { useQuestions } from "@root/questions/hooks";
|
||||||
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
|
||||||
|
import { deleteTimeoutedQuestions } from "@utils/deleteTimeoutedQuestions";
|
||||||
|
|
||||||
export const QuestionSwitchWindowTool = () => {
|
interface Props {
|
||||||
const {questions} = useQuestionsStore.getState()
|
openBranchingPage: boolean;
|
||||||
const {openBranchingPanel} = useUiTools()
|
setOpenBranchingPage: (a: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const QuestionSwitchWindowTool = ({ openBranchingPage, setOpenBranchingPage }: Props) => {
|
||||||
|
const { questions } = useQuestionsStore.getState();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
|
console.log("Я компонент в котором отвечала");
|
||||||
|
|
||||||
|
const openBranchingPageHC = () => {
|
||||||
|
if (!openBranchingPage) {
|
||||||
|
deleteTimeoutedQuestions(questions, quiz);
|
||||||
|
}
|
||||||
|
setOpenBranchingPage(!openBranchingPage);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", gap: "20px", flexWrap: "wrap", marginBottom: isMobile ? "20px" : undefined }}>
|
<Box
|
||||||
<Box sx={{ flexBasis: "796px" }}>
|
sx={{
|
||||||
{openBranchingPanel? <BranchingMap /> : <DraggableList />}
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
marginBottom: isMobile ? "20px" : undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ flexBasis: "796px" }}>{openBranchingPage ? <BranchingMap /> : <DraggableList />}</Box>
|
||||||
|
{openBranchingPage && (
|
||||||
|
<SwitchBranchingPanel openBranchingPage={openBranchingPage} setOpenBranchingPage={openBranchingPageHC} />
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<SwitchBranchingPanel
|
);
|
||||||
/>
|
};
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -13,7 +13,12 @@ import { useQuestionsStore } from "@root/questions/store";
|
|||||||
import { updateOpenBranchingPanel, updateEditSomeQuestion } from "@root/uiTools/actions";
|
import { updateOpenBranchingPanel, updateEditSomeQuestion } from "@root/uiTools/actions";
|
||||||
import { useUiTools } from "@root/uiTools/store";
|
import { useUiTools } from "@root/uiTools/store";
|
||||||
|
|
||||||
export default function QuestionsPage() {
|
interface Props {
|
||||||
|
openBranchingPage: boolean;
|
||||||
|
setOpenBranchingPage: (a: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function QuestionsPage({ openBranchingPage, setOpenBranchingPage }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { openedModalSettingsId, openBranchingPanel } = useUiTools();
|
const { openedModalSettingsId, openBranchingPanel } = useUiTools();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
||||||
@ -55,7 +60,7 @@ export default function QuestionsPage() {
|
|||||||
Свернуть всё
|
Свернуть всё
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<QuestionSwitchWindowTool />
|
<QuestionSwitchWindowTool openBranchingPage={openBranchingPage} setOpenBranchingPage={setOpenBranchingPage} />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
FormControl,
|
FormControl,
|
||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useTheme, Theme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import ArrowDown from "@icons/ArrowDownIcon";
|
import ArrowDown from "@icons/ArrowDownIcon";
|
||||||
@ -18,6 +18,9 @@ type SelectProps = {
|
|||||||
onChange?: (item: string, num: number) => void;
|
onChange?: (item: string, num: number) => void;
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
colorPlaceholder?: string;
|
||||||
|
colorMain?: string;
|
||||||
|
color?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Select = ({
|
export const Select = ({
|
||||||
@ -27,6 +30,9 @@ export const Select = ({
|
|||||||
onChange,
|
onChange,
|
||||||
sx,
|
sx,
|
||||||
placeholder = "",
|
placeholder = "",
|
||||||
|
colorMain = "#7E2AEA",
|
||||||
|
colorPlaceholder = "#9A9AAF",
|
||||||
|
color
|
||||||
}: SelectProps) => {
|
}: SelectProps) => {
|
||||||
const [activeItem, setActiveItem] = useState<number>(
|
const [activeItem, setActiveItem] = useState<number>(
|
||||||
empty ? -1 : activeItemIndex
|
empty ? -1 : activeItemIndex
|
||||||
@ -63,7 +69,7 @@ export const Select = ({
|
|||||||
value ? (
|
value ? (
|
||||||
items[Number(value)]
|
items[Number(value)]
|
||||||
) : (
|
) : (
|
||||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
<Typography sx={{ color: colorPlaceholder }}>
|
||||||
{placeholder}
|
{placeholder}
|
||||||
</Typography>
|
</Typography>
|
||||||
)
|
)
|
||||||
@ -77,7 +83,7 @@ export const Select = ({
|
|||||||
height: "48px",
|
height: "48px",
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
"& .MuiOutlinedInput-notchedOutline": {
|
"& .MuiOutlinedInput-notchedOutline": {
|
||||||
border: `1px solid ${theme.palette.brightPurple.main} !important`,
|
border: `1px solid ${colorMain} !important`,
|
||||||
height: "48px",
|
height: "48px",
|
||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
},
|
},
|
||||||
@ -100,21 +106,21 @@ export const Select = ({
|
|||||||
gap: "8px",
|
gap: "8px",
|
||||||
"& .Mui-selected": {
|
"& .Mui-selected": {
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
color: theme.palette.brightPurple.main,
|
color: colorMain,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
sx: {
|
sx: {
|
||||||
color: theme.palette.brightPurple.main,
|
color: colorMain,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
px: "9px",
|
px: "9px",
|
||||||
gap: "20px",
|
gap: "20px",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
IconComponent={(props) => <ArrowDown {...props} />}
|
IconComponent={(props) => <ArrowDown {...props } color={color}/>}
|
||||||
>
|
>
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -126,7 +132,7 @@ export const Select = ({
|
|||||||
gap: "20px",
|
gap: "20px",
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
color: theme.palette.grey2.main,
|
color: colorPlaceholder,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item}
|
{item}
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import ButtonsOptions from "../ButtonsOptions";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
|
|
||||||
import CustomNumberField from "@ui_kit/CustomNumberField";
|
import CustomNumberField from "@ui_kit/CustomNumberField";
|
||||||
|
|
||||||
|
import ButtonsOptions from "../ButtonsOptions";
|
||||||
import SwitchSlider from "./switchSlider";
|
import SwitchSlider from "./switchSlider";
|
||||||
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
|
|
||||||
import { updateQuestion } from "@root/questions/actions";
|
import { updateQuestion } from "@root/questions/actions";
|
||||||
|
|
||||||
|
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
question: QuizQuestionNumber;
|
question: QuizQuestionNumber;
|
||||||
}
|
}
|
||||||
@ -19,14 +24,31 @@ export default function SliderOptions({ question }: Props) {
|
|||||||
const [startError, setStartError] = useState<boolean>(false);
|
const [startError, setStartError] = useState<boolean>(false);
|
||||||
const [minError, setMinError] = useState<boolean>(false);
|
const [minError, setMinError] = useState<boolean>(false);
|
||||||
const [maxError, setMaxError] = useState<boolean>(false);
|
const [maxError, setMaxError] = useState<boolean>(false);
|
||||||
|
const startValueDebounce = useDebouncedCallback((value) => {
|
||||||
|
updateQuestion(question.id, (question) => {
|
||||||
|
if (question.type !== "number") return;
|
||||||
|
|
||||||
|
question.content.start = value;
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const min = Number(question.content.range.split("—")[0]);
|
const min = Number(question.content.range.split("—")[0]);
|
||||||
const max = Number(question.content.range.split("—")[1]);
|
const max = Number(question.content.range.split("—")[1]);
|
||||||
const start = Number(question.content.start);
|
const start = Number(question.content.start);
|
||||||
|
|
||||||
if (start < min || start > max) {
|
if (start < min) {
|
||||||
setStartError(true);
|
setStartError(true);
|
||||||
|
startValueDebounce(min);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start > max && min < max) {
|
||||||
|
setStartError(true);
|
||||||
|
startValueDebounce(max);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start >= min && start <= max) {
|
if (start >= min && start <= max) {
|
||||||
@ -211,7 +233,11 @@ export default function SliderOptions({ question }: Props) {
|
|||||||
placeholder={"1"}
|
placeholder={"1"}
|
||||||
error={stepError}
|
error={stepError}
|
||||||
value={String(question.content.step)}
|
value={String(question.content.step)}
|
||||||
onChange={({ target }) => {
|
onChange={({ target, type }) => {
|
||||||
|
if (type === "blur") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
updateQuestion(question.id, (question) => {
|
updateQuestion(question.id, (question) => {
|
||||||
if (question.type !== "number") return;
|
if (question.type !== "number") return;
|
||||||
|
|
||||||
@ -219,8 +245,10 @@ export default function SliderOptions({ question }: Props) {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onBlur={({ target }) => {
|
onBlur={({ target }) => {
|
||||||
|
const min = Number(question.content.range.split("—")[0]);
|
||||||
const max = Number(question.content.range.split("—")[1]);
|
const max = Number(question.content.range.split("—")[1]);
|
||||||
const step = Number(target.value);
|
const step = Number(target.value);
|
||||||
|
const range = max - min;
|
||||||
|
|
||||||
if (step > max) {
|
if (step > max) {
|
||||||
updateQuestion(question.id, (question) => {
|
updateQuestion(question.id, (question) => {
|
||||||
@ -229,6 +257,14 @@ export default function SliderOptions({ question }: Props) {
|
|||||||
question.content.step = max;
|
question.content.step = max;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (range % step) {
|
||||||
|
updateQuestion(question.id, (question) => {
|
||||||
|
if (question.type !== "number") return;
|
||||||
|
|
||||||
|
question.content.step = 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,23 +1,32 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
import {Box, Typography, Switch, useTheme, Button, useMediaQuery} from "@mui/material";
|
Typography,
|
||||||
|
Switch,
|
||||||
|
useTheme,
|
||||||
|
Button,
|
||||||
|
useMediaQuery,
|
||||||
|
} from "@mui/material";
|
||||||
|
|
||||||
import { QuestionsList } from "./QuestionsList";
|
import { QuestionsList } from "./QuestionsList";
|
||||||
import { updateOpenBranchingPanel } from "@root/uiTools/actions";
|
import { updateOpenBranchingPanel } from "@root/uiTools/actions";
|
||||||
import {useQuestionsStore} from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import {useRef} from "react";
|
import { useRef } from "react";
|
||||||
import { useUiTools } from "@root/uiTools/store";
|
import { useUiTools } from "@root/uiTools/store";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
openBranchingPage: boolean;
|
||||||
|
setOpenBranchingPage: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
export const SwitchBranchingPanel = () => {
|
export const SwitchBranchingPanel = ({
|
||||||
|
openBranchingPage,
|
||||||
|
setOpenBranchingPage}:Props) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1446));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1446));
|
||||||
|
const ref = useRef();
|
||||||
|
|
||||||
const {openBranchingPanel} = useUiTools()
|
return !isTablet || openBranchingPage ? (
|
||||||
const ref = useRef()
|
|
||||||
|
|
||||||
return ( !isTablet || openBranchingPanel ?
|
|
||||||
<Box sx={{ userSelect: "none", maxWidth: "350px", width: "100%" }}>
|
<Box sx={{ userSelect: "none", maxWidth: "350px", width: "100%" }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -31,10 +40,8 @@ export const SwitchBranchingPanel = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
checked={openBranchingPanel}
|
checked={openBranchingPage}
|
||||||
onChange={
|
onChange={setOpenBranchingPage}
|
||||||
(e) => updateOpenBranchingPanel(e.target.checked)
|
|
||||||
}
|
|
||||||
sx={{
|
sx={{
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 30,
|
height: 30,
|
||||||
@ -87,11 +94,10 @@ export const SwitchBranchingPanel = () => {
|
|||||||
Настройте связи между вопросами
|
Настройте связи между вопросами
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
{ openBranchingPanel && <QuestionsList /> }
|
{openBranchingPage && <QuestionsList />}
|
||||||
</Box>
|
</Box>
|
||||||
:
|
) : (
|
||||||
<></>
|
<></>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import IconPlus from "@icons/IconPlus";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import Info from "@icons/Info";
|
// import { useBlocker } from "react-router-dom";
|
||||||
import Plus from "@icons/Plus";
|
import {
|
||||||
import ArrowLeft from "@icons/questionsPage/arrowLeft";
|
Box,
|
||||||
import { Box, Button, Typography, Paper, Modal, TextField } from "@mui/material";
|
Button,
|
||||||
|
Typography,
|
||||||
|
Paper,
|
||||||
|
Modal,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
import { incrementCurrentStep } from "@root/quizes/actions";
|
import { incrementCurrentStep } from "@root/quizes/actions";
|
||||||
import CustomWrapper from "@ui_kit/CustomWrapper";
|
import CustomWrapper from "@ui_kit/CustomWrapper";
|
||||||
import { DescriptionForm } from "./DescriptionForm/DescriptionForm";
|
import { DescriptionForm } from "./DescriptionForm/DescriptionForm";
|
||||||
import { ResultListForm } from "./ResultListForm";
|
import { ResultListForm } from "./ResultListForm";
|
||||||
import { SettingForm } from "./SettingForm";
|
import { SettingForm } from "./SettingForm";
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
import { WhenCard } from "./cards/WhenCard";
|
import { WhenCard } from "./cards/WhenCard";
|
||||||
import { ResultCard, checkEmptyData } from "./cards/ResultCard";
|
import { ResultCard, checkEmptyData } from "./cards/ResultCard";
|
||||||
import { EmailSettingsCard } from "./cards/EmailSettingsCard";
|
import { EmailSettingsCard } from "./cards/EmailSettingsCard";
|
||||||
@ -17,15 +21,27 @@ import { useQuestionsStore } from "@root/questions/store";
|
|||||||
import { deleteQuestion } from "@root/questions/actions";
|
import { deleteQuestion } from "@root/questions/actions";
|
||||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
|
|
||||||
|
import IconPlus from "@icons/IconPlus";
|
||||||
|
import Info from "@icons/Info";
|
||||||
|
import Plus from "@icons/Plus";
|
||||||
|
import ArrowLeft from "@icons/questionsPage/arrowLeft";
|
||||||
|
|
||||||
export const ResultSettings = () => {
|
export const ResultSettings = () => {
|
||||||
const { questions } = useQuestionsStore();
|
const { questions } = useQuestionsStore();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const results = useQuestionsStore().questions.filter((q): q is QuizQuestionResult => q.type === "result");
|
const results = useQuestionsStore().questions.filter(
|
||||||
|
(q): q is QuizQuestionResult => q.type === "result"
|
||||||
|
);
|
||||||
const [quizExpand, setQuizExpand] = useState(true);
|
const [quizExpand, setQuizExpand] = useState(true);
|
||||||
const [resultContract, setResultContract] = useState(true);
|
const [resultContract, setResultContract] = useState(true);
|
||||||
|
const [triggerExit, setTriggerExit] = useState<{
|
||||||
|
follow: boolean;
|
||||||
|
path: string;
|
||||||
|
}>({ follow: false, path: "" });
|
||||||
|
const [openNotificationModal, setOpenNotificationModal] =
|
||||||
|
useState<boolean>(true);
|
||||||
const isReadyToLeaveRef = useRef(true);
|
const isReadyToLeaveRef = useRef(true);
|
||||||
|
// const blocker = useBlocker(false);
|
||||||
console.log('quiz ', quiz)
|
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
function calcIsReadyToLeave() {
|
function calcIsReadyToLeave() {
|
||||||
@ -42,11 +58,29 @@ console.log('quiz ', quiz)
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
if (isReadyToLeaveRef.current === false) alert("Пожалуйста, проверьте, что вы заполнили все результаты");
|
if (!isReadyToLeaveRef.current && window.location.pathname !== "/edit") {
|
||||||
|
setOpenNotificationModal(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const cnsl = results.filter(q=> q.content.usage)
|
const cnsl = results.filter((q) => q.content.usage);
|
||||||
|
|
||||||
|
const shouldBlock = true; // Replace this
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (shouldBlock) {
|
||||||
|
// blocker.proceed?.()
|
||||||
|
// }
|
||||||
|
// }, [shouldBlock]);
|
||||||
|
|
||||||
|
const leavePage = (leave: boolean) => {
|
||||||
|
if (leave) {
|
||||||
|
console.log("ливаем");
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpenNotificationModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ maxWidth: "796px" }}>
|
<Box sx={{ maxWidth: "796px" }}>
|
||||||
@ -81,9 +115,13 @@ console.log('quiz ', quiz)
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<WhenCard quizExpand={quizExpand} />
|
<WhenCard quizExpand={quizExpand} />
|
||||||
{quiz.config.resultInfo.when === "email" && <EmailSettingsCard quizExpand={quizExpand} />}
|
{quiz.config.resultInfo.when === "email" && (
|
||||||
|
<EmailSettingsCard quizExpand={quizExpand} />
|
||||||
|
)}
|
||||||
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", mb: "15px", mt: "15px" }}>
|
<Box
|
||||||
|
sx={{ display: "flex", alignItems: "center", mb: "15px", mt: "15px" }}
|
||||||
|
>
|
||||||
<Typography variant="p1" sx={{ color: "#4D4D4D", fontSize: "14px" }}>
|
<Typography variant="p1" sx={{ color: "#4D4D4D", fontSize: "14px" }}>
|
||||||
Создайте результат
|
Создайте результат
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -109,7 +147,11 @@ console.log('quiz ', quiz)
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{cnsl.map((resultQuestion) => (
|
{cnsl.map((resultQuestion) => (
|
||||||
<ResultCard resultContract={resultContract} resultData={resultQuestion} key={resultQuestion.id} />
|
<ResultCard
|
||||||
|
resultContract={resultContract}
|
||||||
|
resultData={resultQuestion}
|
||||||
|
key={resultQuestion.id}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
<Modal
|
<Modal
|
||||||
open={false}
|
open={false}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { getQuestionByContentId, updateQuestion, uploadQuestionImage } from "@root/questions/actions"
|
import {
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks"
|
getQuestionByContentId,
|
||||||
|
updateQuestion,
|
||||||
|
} from "@root/questions/actions";
|
||||||
|
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
|
||||||
import { UploadImageModal } from "../../Questions/UploadImage/UploadImageModal";
|
|
||||||
import { useDisclosure } from "../../../utils/useDisclosure";
|
|
||||||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@ -20,7 +17,7 @@ import {
|
|||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
FormControl,
|
FormControl,
|
||||||
Popover
|
Popover,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
|
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
|
||||||
|
|
||||||
@ -30,15 +27,19 @@ import Trash from "@icons/trash";
|
|||||||
import Info from "@icons/Info";
|
import Info from "@icons/Info";
|
||||||
import SettingIcon from "@icons/questionsPage/settingIcon";
|
import SettingIcon from "@icons/questionsPage/settingIcon";
|
||||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
import { QuizQuestionResult } from "@model/questionTypes/result";
|
||||||
import { MutableRefObject } from "react";
|
import { MediaSelectionAndDisplay } from "@ui_kit/MediaSelectionAndDisplay";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
resultContract: boolean;
|
resultContract: boolean;
|
||||||
resultData: QuizQuestionResult;
|
resultData: QuizQuestionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkEmptyData = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
export const checkEmptyData = ({
|
||||||
let check = true
|
resultData,
|
||||||
|
}: {
|
||||||
|
resultData: QuizQuestionResult;
|
||||||
|
}) => {
|
||||||
|
let check = true;
|
||||||
if (
|
if (
|
||||||
resultData.title.length > 0 ||
|
resultData.title.length > 0 ||
|
||||||
resultData.description.length > 0 ||
|
resultData.description.length > 0 ||
|
||||||
@ -48,14 +49,17 @@ export const checkEmptyData = ({ resultData }: { resultData: QuizQuestionResult
|
|||||||
resultData.content.text.length > 0 ||
|
resultData.content.text.length > 0 ||
|
||||||
resultData.content.video.length > 0 ||
|
resultData.content.video.length > 0 ||
|
||||||
resultData.content.hint.text.length > 0
|
resultData.content.hint.text.length > 0
|
||||||
) check = false
|
)
|
||||||
return check
|
check = false;
|
||||||
}
|
return check;
|
||||||
|
};
|
||||||
|
|
||||||
const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
||||||
const checkEmpty = checkEmptyData({ resultData })
|
const checkEmpty = checkEmptyData({ resultData });
|
||||||
const question = getQuestionByContentId(resultData.content.rule.parentId)
|
const question = getQuestionByContentId(resultData.content.rule.parentId);
|
||||||
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
|
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
@ -66,20 +70,18 @@ const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const id = open ? 'simple-popover' : undefined;
|
const id = open ? "simple-popover" : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Info
|
<Info
|
||||||
sx={{
|
sx={{
|
||||||
"MuiIconButton-root": {
|
"MuiIconButton-root": {
|
||||||
|
boxShadow: "0 0 10px 10px red",
|
||||||
boxShadow: "0 0 10px 10px red"
|
},
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
className={checkEmpty ? "blink" : ""}
|
className={checkEmpty ? "blink" : ""}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<Popover
|
<Popover
|
||||||
id={id}
|
id={id}
|
||||||
@ -87,85 +89,51 @@ const InfoView = ({ resultData }: { resultData: QuizQuestionResult }) => {
|
|||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'bottom',
|
vertical: "bottom",
|
||||||
horizontal: 'left',
|
horizontal: "left",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Paper
|
<Paper
|
||||||
sx={{
|
sx={{
|
||||||
p: '20px',
|
p: "20px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexDirection: "column"
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography>
|
||||||
{resultData?.content.rule.parentId === "line" ? "Единый результат в конце прохождения опросника без ветвления"
|
{resultData?.content.rule.parentId === "line"
|
||||||
:
|
? "Единый результат в конце прохождения опросника без ветвления"
|
||||||
`Заголовок вопроса, после которого появится результат: "${question?.title || "нет заголовка"}"`
|
: `Заголовок вопроса, после которого появится результат: "${
|
||||||
}
|
question?.title || "нет заголовка"
|
||||||
|
}"`}
|
||||||
</Typography>
|
</Typography>
|
||||||
{checkEmpty &&
|
{checkEmpty && (
|
||||||
<Typography color="red">
|
<Typography color="red">
|
||||||
Вы не заполнили этот результат никакими данными
|
Вы не заполнили этот результат никакими данными
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
)}
|
||||||
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Popover>
|
</Popover>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const ResultCard = ({ resultContract, resultData }: Props) => {
|
export const ResultCard = ({ resultContract, resultData }: Props) => {
|
||||||
|
|
||||||
const quizQid = useCurrentQuiz()?.qid;
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(800));
|
const isTablet = useMediaQuery(theme.breakpoints.down(800));
|
||||||
|
|
||||||
const [expand, setExpand] = React.useState(true)
|
const [expand, setExpand] = React.useState(true);
|
||||||
const [resultCardSettings, setResultCardSettings] = React.useState(false)
|
const [resultCardSettings, setResultCardSettings] = React.useState(false);
|
||||||
const [buttonPlus, setButtonPlus] = React.useState(true)
|
const [buttonPlus, setButtonPlus] = React.useState(true);
|
||||||
|
const question = getQuestionByContentId(resultData.content.rule.parentId);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setExpand(true)
|
setExpand(true);
|
||||||
}, [resultContract])
|
}, [resultContract]);
|
||||||
|
|
||||||
|
|
||||||
const {
|
|
||||||
isCropModalOpen,
|
|
||||||
openCropModal,
|
|
||||||
closeCropModal,
|
|
||||||
imageBlob,
|
|
||||||
originalImageUrl,
|
|
||||||
setCropModalImageBlob,
|
|
||||||
} = useCropModalState();
|
|
||||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function handleImageUpload(file: File) {
|
|
||||||
const url = await uploadQuestionImage(resultData.id, quizQid, file, (question, url) => {
|
|
||||||
|
|
||||||
question.content.back = url;
|
|
||||||
question.content.originalBack = url;
|
|
||||||
});
|
|
||||||
closeImageUploadModal();
|
|
||||||
openCropModal(file, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCropModalSaveClick(imageBlob: Blob) {
|
|
||||||
uploadQuestionImage(resultData.id, quizQid, imageBlob, (question, url) => {
|
|
||||||
question.content.back = url;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
@ -177,18 +145,24 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
backgroundColor: expand ? "white" : "#EEE4FC",
|
backgroundColor: expand ? "white" : "#EEE4FC",
|
||||||
border: expand ? "none" : "1px solid #9A9AAF",
|
border: expand ? "none" : "1px solid #9A9AAF",
|
||||||
boxShadow: "0px 10px 30px #e7e7e7",
|
boxShadow: "0px 10px 30px #e7e7e7",
|
||||||
m: "20px 0"
|
m: "20px 0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Typography sx={{ color: theme.palette.grey2.main, padding: "5px 20px" }}>
|
||||||
|
{resultData?.content.rule.parentId === "line"
|
||||||
|
? "Единый результат в конце прохождения опросника без ветвления"
|
||||||
|
: `Заголовок вопроса, после которого появится результат: "${
|
||||||
|
question?.title || "нет заголовка"
|
||||||
|
}"`}
|
||||||
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: expand ? "none" : "flex",
|
display: expand ? "none" : "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
padding: isMobile ? "10px" : "20px",
|
padding: isMobile ? "10px" : "0 20px 20px",
|
||||||
flexDirection: isMobile ? "column" : null,
|
flexDirection: isMobile ? "column" : null,
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
minHeight: "40px",
|
minHeight: "40px",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormControl
|
<FormControl
|
||||||
@ -203,7 +177,12 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
<TextField
|
<TextField
|
||||||
value={resultData.title}
|
value={resultData.title}
|
||||||
placeholder={"Заголовок результата"}
|
placeholder={"Заголовок результата"}
|
||||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, question => question.title = target.value)}
|
onChange={({ target }: { target: HTMLInputElement }) =>
|
||||||
|
updateQuestion(
|
||||||
|
resultData.id,
|
||||||
|
(question) => (question.title = target.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
sx={{
|
sx={{
|
||||||
margin: isMobile ? "10px 0" : 0,
|
margin: isMobile ? "10px 0" : 0,
|
||||||
"& .MuiInputBase-root": {
|
"& .MuiInputBase-root": {
|
||||||
@ -277,7 +256,7 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
boxShadow: "0px 10px 30px #e7e7e7",
|
boxShadow: "0px 10px 30px #e7e7e7",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ p: "0 20px", pt: "30px" }}>
|
<Box sx={{ p: "0 20px", pt: "10px" }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@ -291,7 +270,13 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
<CustomTextField
|
<CustomTextField
|
||||||
value={resultData.title}
|
value={resultData.title}
|
||||||
placeholder={"Заголовок результата"}
|
placeholder={"Заголовок результата"}
|
||||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, question => question.title = target.value)} />
|
onChange={({ target }: { target: HTMLInputElement }) =>
|
||||||
|
updateQuestion(
|
||||||
|
resultData.id,
|
||||||
|
(question) => (question.title = target.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{ padding: "0", margin: "5px" }}
|
sx={{ padding: "0", margin: "5px" }}
|
||||||
disableRipple
|
disableRipple
|
||||||
@ -301,18 +286,21 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
<ExpandLessIconBG />
|
<ExpandLessIconBG />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<InfoView resultData={resultData} />
|
<InfoView resultData={resultData} />
|
||||||
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
margin: "20px 0"
|
margin: "20px 0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
value={resultData.description}
|
value={resultData.description}
|
||||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.description = target.value)}
|
onChange={({ target }: { target: HTMLInputElement }) =>
|
||||||
|
updateQuestion(
|
||||||
|
resultData.id,
|
||||||
|
(question) => (question.description = target.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
placeholder={"Заголовок пожирнее"}
|
placeholder={"Заголовок пожирнее"}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
@ -323,9 +311,13 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
|
|
||||||
value={resultData.content.text}
|
value={resultData.content.text}
|
||||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.text = target.value)}
|
onChange={({ target }: { target: HTMLInputElement }) =>
|
||||||
|
updateQuestion(
|
||||||
|
resultData.id,
|
||||||
|
(question) => (question.content.text = target.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
fullWidth
|
fullWidth
|
||||||
placeholder="Описание"
|
placeholder="Описание"
|
||||||
multiline
|
multiline
|
||||||
@ -349,128 +341,12 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<MediaSelectionAndDisplay resultData={resultData} />
|
||||||
|
|
||||||
|
{buttonPlus ? (
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
mt: "20px",
|
|
||||||
display: "flex",
|
|
||||||
gap: "10px",
|
|
||||||
flexDirection: "column"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
sx={{
|
|
||||||
color: resultData.content.useImage ? "#7E2AEA" : "#9A9AAF",
|
|
||||||
fontSize: "16px",
|
|
||||||
"&:hover": {
|
|
||||||
background: "none",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
variant="text"
|
|
||||||
onClick={() => updateQuestion(resultData.id, (question) => question.content.useImage = true)}
|
|
||||||
>
|
|
||||||
Изображение
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
sx={{
|
|
||||||
color: resultData.content.useImage ? "#9A9AAF" : "#7E2AEA",
|
|
||||||
fontSize: "16px",
|
|
||||||
"&:hover": {
|
|
||||||
background: "none",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
variant="text"
|
|
||||||
onClick={() => updateQuestion(resultData.id, (question) => question.content.useImage = false)}
|
|
||||||
>
|
|
||||||
Видео
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<UploadImageModal
|
|
||||||
isOpen={isImageUploadOpen}
|
|
||||||
onClose={closeImageUploadModal}
|
|
||||||
handleImageChange={handleImageUpload}
|
|
||||||
/>
|
|
||||||
<CropModal
|
|
||||||
isOpen={isCropModalOpen}
|
|
||||||
imageBlob={imageBlob}
|
|
||||||
originalImageUrl={originalImageUrl}
|
|
||||||
setCropModalImageBlob={setCropModalImageBlob}
|
|
||||||
onClose={closeCropModal}
|
|
||||||
onSaveImageClick={handleCropModalSaveClick}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{
|
|
||||||
resultData.content.useImage &&
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "20px",
|
|
||||||
mb: "30px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AddOrEditImageButton
|
|
||||||
imageSrc={resultData.content.back}
|
|
||||||
onImageClick={() => {
|
|
||||||
if (resultData.content.back) {
|
|
||||||
return openCropModal(
|
|
||||||
resultData.content.back,
|
|
||||||
resultData.content.originalBack
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
openImageUploadModal();
|
|
||||||
}}
|
|
||||||
onPlusClick={() => {
|
|
||||||
openImageUploadModal();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
!resultData.content.useImage &&
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "20px",
|
|
||||||
mb: "30px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CustomTextField
|
|
||||||
placeholder="URL видео"
|
|
||||||
text={resultData.content.video ?? ""}
|
|
||||||
onChange={e => updateQuestion(resultData.id, q => {
|
|
||||||
q.content.video = e.target.value;
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
buttonPlus ?
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setButtonPlus(false)
|
setButtonPlus(false);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
display: "inline flex",
|
display: "inline flex",
|
||||||
@ -484,25 +360,31 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
border: "1px solid #9A9AAF",
|
border: "1px solid #9A9AAF",
|
||||||
background: " #F2F3F7",
|
background: " #F2F3F7",
|
||||||
color: "#9A9AAF",
|
color: "#9A9AAF",
|
||||||
mb: "30px"
|
mb: "30px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Кнопка +
|
Кнопка +
|
||||||
</Button>
|
</Button>
|
||||||
:
|
) : (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
mb: "30px"
|
mb: "30px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography component={"span"} sx={{ weight: "500", fontSize: "18px", mb: "10px" }}>
|
<Typography
|
||||||
|
component={"span"}
|
||||||
|
sx={{ weight: "500", fontSize: "18px", mb: "10px" }}
|
||||||
|
>
|
||||||
Призыв к действию
|
Призыв к действию
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setButtonPlus(true)
|
setButtonPlus(true);
|
||||||
updateQuestion(resultData.id, (q) => q.content.hint.text = "")
|
updateQuestion(
|
||||||
|
resultData.id,
|
||||||
|
(q) => (q.content.hint.text = "")
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash />
|
<Trash />
|
||||||
@ -511,7 +393,13 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
value={resultData.content.hint.text}
|
value={resultData.content.hint.text}
|
||||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.hint.text = target.value)}
|
onChange={({ target }: { target: HTMLInputElement }) =>
|
||||||
|
updateQuestion(
|
||||||
|
resultData.id,
|
||||||
|
(question) =>
|
||||||
|
(question.content.hint.text = target.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
fullWidth
|
fullWidth
|
||||||
placeholder="Например: узнать подробнее"
|
placeholder="Например: узнать подробнее"
|
||||||
sx={{
|
sx={{
|
||||||
@ -533,14 +421,7 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -560,19 +441,21 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
>
|
>
|
||||||
<MiniButtonSetting
|
<MiniButtonSetting
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setResultCardSettings(!resultCardSettings)
|
setResultCardSettings(!resultCardSettings);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor:
|
backgroundColor: resultCardSettings
|
||||||
resultCardSettings
|
|
||||||
? theme.palette.brightPurple.main
|
? theme.palette.brightPurple.main
|
||||||
: "transparent",
|
: "transparent",
|
||||||
color:
|
color: resultCardSettings
|
||||||
resultCardSettings ? "#ffffff" : theme.palette.grey3.main,
|
? "#ffffff"
|
||||||
|
: theme.palette.grey3.main,
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
backgroundColor: resultCardSettings ? "#581CA7" : "#7E2AEA",
|
backgroundColor: resultCardSettings
|
||||||
color: "white"
|
? "#581CA7"
|
||||||
}
|
: "#7E2AEA",
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SettingIcon
|
<SettingIcon
|
||||||
@ -585,25 +468,28 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{
|
{resultCardSettings && (
|
||||||
resultCardSettings &&
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
p: "20px",
|
p: "20px",
|
||||||
borderRadius: "0 0 12px 12px"
|
borderRadius: "0 0 12px 12px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder={"Внутреннее описание вопроса"}
|
placeholder={"Внутреннее описание вопроса"}
|
||||||
value={resultData.innerName}
|
value={resultData.innerName}
|
||||||
onChange={({ target }: { target: HTMLInputElement; }) => updateQuestion(resultData.id, (question) => question.content.innerName = target.value)}
|
onChange={({ target }: { target: HTMLInputElement }) =>
|
||||||
|
updateQuestion(
|
||||||
|
resultData.id,
|
||||||
|
(question) => (question.content.innerName = target.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)}
|
||||||
}
|
</Paper>
|
||||||
</Paper >
|
);
|
||||||
)
|
};
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box, Typography, Button, Paper, TextField, Link, InputAdornment } from "@mui/material";
|
import {Box, Typography, Button, Paper, TextField, Link, InputAdornment, useTheme} from "@mui/material";
|
||||||
import NameIcon from "@icons/ContactFormIcon/NameIcon";
|
import NameIcon from "@icons/ContactFormIcon/NameIcon";
|
||||||
import EmailIcon from "@icons/ContactFormIcon/EmailIcon";
|
import EmailIcon from "@icons/ContactFormIcon/EmailIcon";
|
||||||
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon";
|
import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon";
|
||||||
@ -13,6 +13,7 @@ import { useQuestionsStore } from "@root/questions/store";
|
|||||||
|
|
||||||
import { checkEmptyData } from "../ResultPage/cards/ResultCard";
|
import { checkEmptyData } from "../ResultPage/cards/ResultCard";
|
||||||
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
||||||
|
import {modes} from "../../utils/themes/Publication/themePublication";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
|
||||||
type ContactFormProps = {
|
type ContactFormProps = {
|
||||||
@ -37,8 +38,9 @@ export const ContactForm = ({
|
|||||||
setShowResultForm,
|
setShowResultForm,
|
||||||
}: ContactFormProps) => {
|
}: ContactFormProps) => {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
const mode = modes;
|
||||||
const { questions } = useQuestionsStore();
|
const { questions } = useQuestionsStore();
|
||||||
|
const theme = useTheme();
|
||||||
const [ready, setReady] = useState(false)
|
const [ready, setReady] = useState(false)
|
||||||
const followNextForm = () => {
|
const followNextForm = () => {
|
||||||
setShowContactForm(false);
|
setShowContactForm(false);
|
||||||
@ -68,6 +70,7 @@ export const ContactForm = ({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
height: "100vh"
|
height: "100vh"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -82,7 +85,8 @@ export const ContactForm = ({
|
|||||||
sx={{
|
sx={{
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
m: "20px 0",
|
m: "20px 0",
|
||||||
fontSize: "28px"
|
fontSize: "28px",
|
||||||
|
color: theme.palette.text.primary
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{quiz?.config.formContact.title || "Заполните форму, чтобы получить результаты теста"}
|
{quiz?.config.formContact.title || "Заполните форму, чтобы получить результаты теста"}
|
||||||
@ -109,6 +113,7 @@ export const ContactForm = ({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
p: "30px"
|
p: "30px"
|
||||||
}}>
|
}}>
|
||||||
|
|
||||||
@ -146,13 +151,13 @@ export const ContactForm = ({
|
|||||||
width: "450px",
|
width: "450px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomCheckbox label="" handleChange={({ target }) => { setReady(target.checked) }} checked={ready} />
|
<CustomCheckbox label="" handleChange={({ target }) => { setReady(target.checked) }} checked={ready} colorIcon={theme.palette.primary.main}/>
|
||||||
<Typography>
|
<Typography>
|
||||||
С
|
С 
|
||||||
<Link> Положением об обработке персональных данных </Link>
|
<Link> Положением об обработке персональных данных </Link>
|
||||||
и
|
 и 
|
||||||
<Link> Политикой конфиденциальности </Link>
|
<Link> Политикой конфиденциальности </Link>
|
||||||
ознакомлен
|
 ознакомлен
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
@ -160,11 +165,14 @@ export const ContactForm = ({
|
|||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
mt: "20px"
|
mt: "20px",
|
||||||
|
gap: "15px"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NameplateLogo style={{ fontSize: "34px" }} />
|
<NameplateLogo style={{ fontSize: "34px", color: mode[quiz.config.theme] ? "#151515" : "#FFFFFF" }} />
|
||||||
<Typography sx={{ fontSize: "20px", color: "#4D4D4D", whiteSpace: "nowrap" }}>Сделано на PenaQuiz</Typography>
|
<Typography sx={{ fontSize: "20px", color: mode[quiz.config.theme] ? "#4D4D4D" : "#F5F7FF", whiteSpace: "nowrap" }}>
|
||||||
|
Сделано на PenaQuiz
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box >
|
</Box >
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Box, Button, Typography, useTheme } from "@mui/material";
|
import {Box, Button, Typography, useMediaQuery, useTheme} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore } from "@root/quizView";
|
import { useQuizViewStore } from "@root/quizView";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
@ -9,6 +9,8 @@ import type { AnyTypedQuizQuestion, QuizQuestionBase } from "../../model/questio
|
|||||||
import { getQuestionByContentId } from "@root/questions/actions";
|
import { getQuestionByContentId } from "@root/questions/actions";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { NameplateLogoFQ } from "@icons/NameplateLogoFQ";
|
import { NameplateLogoFQ } from "@icons/NameplateLogoFQ";
|
||||||
|
import {NameplateLogoFQDark} from "@icons/NameplateLogoFQDark";
|
||||||
|
import {modes} from "../../utils/themes/Publication/themePublication";
|
||||||
import { checkEmptyData } from "../ResultPage/cards/ResultCard";
|
import { checkEmptyData } from "../ResultPage/cards/ResultCard";
|
||||||
|
|
||||||
type FooterProps = {
|
type FooterProps = {
|
||||||
@ -23,9 +25,11 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
const [disableNextButton, setDisableNextButton] = useState<boolean>(false);
|
const [disableNextButton, setDisableNextButton] = useState<boolean>(false);
|
||||||
const [stepNumber, setStepNumber] = useState(1);
|
const [stepNumber, setStepNumber] = useState(1);
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
const mode = modes;
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const questions = useQuestionsStore().questions as AnyTypedQuizQuestion[];
|
const questions = useQuestionsStore().questions as AnyTypedQuizQuestion[];
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const isMobileMini = useMediaQuery(theme.breakpoints.down(382));
|
||||||
const linear = !questions.find(({ content }) => content.rule.parentId === "root");
|
const linear = !questions.find(({ content }) => content.rule.parentId === "root");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -113,10 +117,14 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getNextQuestionId = () => {
|
const getNextQuestionId = () => {
|
||||||
|
console.log("net")
|
||||||
|
console.log(question)
|
||||||
|
let readyBeNextQuestion = "";
|
||||||
|
|
||||||
|
//вопрос обязателен, анализируем ответ и условия ветвления
|
||||||
if (answers.length) {
|
if (answers.length) {
|
||||||
const answer = answers.find(({ questionId }) => questionId === question.content.id);
|
const answer = answers.find(({ questionId }) => questionId === question.content.id);
|
||||||
|
|
||||||
let readyBeNextQuestion = "";
|
|
||||||
|
|
||||||
(question as QuizQuestionBase).content.rule.main.forEach(({ next, rules }) => {
|
(question as QuizQuestionBase).content.rule.main.forEach(({ next, rules }) => {
|
||||||
let longerArray = Math.max(
|
let longerArray = Math.max(
|
||||||
@ -143,8 +151,32 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return readyBeNextQuestion;
|
if (readyBeNextQuestion) return readyBeNextQuestion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!question.required) {//вопрос не обязателен и не нашли совпадений между ответами и условиями ветвления
|
||||||
|
console.log("вопрос не обязателен ищем дальше")
|
||||||
|
const defaultQ = question.content.rule.default
|
||||||
|
if (defaultQ) return defaultQ
|
||||||
|
//Вопросы типа страница, ползунок, своё поле для ввода и дата не могут иметь больше 1 ребёнка. Пользователь не может настроить там дефолт
|
||||||
|
//Кинуть на ребёнка надо даже если там нет дефолта
|
||||||
|
if (
|
||||||
|
(question?.type === "date" ||
|
||||||
|
question?.type === "text" ||
|
||||||
|
question?.type === "number" ||
|
||||||
|
question?.type === "page") && question.content.rule.children.length === 1
|
||||||
|
) return question.content.rule.children[0]
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
//ничё не нашли, ищем резулт
|
||||||
|
console.log("ничё не нашли, ищем резулт ")
|
||||||
|
return questions.find(q => {
|
||||||
|
console.log('q.type === "result"', q.type === "result")
|
||||||
|
console.log('q.content.rule.parentId === question.content.id', q.content.rule.parentId === question.content.id)
|
||||||
|
return q.type === "result" && q.content.rule.parentId === question.content.id
|
||||||
|
})?.content.id
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const followPreviousStep = () => {
|
const followPreviousStep = () => {
|
||||||
@ -192,23 +224,19 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
|
|
||||||
const nextQuestionId = getNextQuestionId();
|
const nextQuestionId = getNextQuestionId();
|
||||||
|
|
||||||
|
console.log(nextQuestionId)
|
||||||
if (nextQuestionId) {
|
if (nextQuestionId) {
|
||||||
const nextQuestion = getQuestionByContentId(nextQuestionId);
|
const nextQuestion = getQuestionByContentId(nextQuestionId);
|
||||||
|
console.log(nextQuestion)
|
||||||
|
|
||||||
if (nextQuestion?.type && nextQuestion.type !== "result") {
|
if (nextQuestion?.type && nextQuestion.type === "result") {
|
||||||
|
showResult(nextQuestion);
|
||||||
|
} else {
|
||||||
setCurrentQuestion(nextQuestion);
|
setCurrentQuestion(nextQuestion);
|
||||||
return;
|
}
|
||||||
} else {
|
} else {
|
||||||
enqueueSnackbar("не могу получить последующий вопрос");
|
enqueueSnackbar("не могу получить последующий вопрос");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const nextQuestion = getQuestionByContentId(question.content.rule.default);
|
|
||||||
if (nextQuestion?.type && nextQuestion.type !== "result") {
|
|
||||||
setCurrentQuestion(nextQuestion);
|
|
||||||
} else {
|
|
||||||
showResult(nextQuestion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -233,7 +261,11 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
gap: "10px",
|
gap: "10px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NameplateLogoFQ style={{ fontSize: "34px", width: "200px", height: "auto" }} />
|
{/*{mode[quiz.config.theme] ? (*/}
|
||||||
|
{/* <NameplateLogoFQ style={{ fontSize: "34px", width:"200px", height:"auto" }} />*/}
|
||||||
|
{/*):(*/}
|
||||||
|
{/* <NameplateLogoFQDark style={{ fontSize: "34px", width:"200px", height:"auto" }} />*/}
|
||||||
|
{/*)}*/}
|
||||||
{linear &&
|
{linear &&
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
@ -242,7 +274,7 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "10px",
|
gap: "10px",
|
||||||
marginRight: "auto",
|
marginRight: "auto",
|
||||||
color: theme.palette.grey1.main,
|
color: theme.palette.text.primary,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>Шаг</Typography>
|
<Typography>Шаг</Typography>
|
||||||
@ -256,7 +288,7 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
width: "30px",
|
width: "30px",
|
||||||
height: "30px",
|
height: "30px",
|
||||||
color: "#FFF",
|
color: "#FFF",
|
||||||
background: theme.palette.brightPurple.main,
|
background: theme.palette.primary.main,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{stepNumber}
|
{stepNumber}
|
||||||
@ -268,13 +300,50 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh
|
|||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
|
marginRight: "auto",
|
||||||
|
// color: theme.palette.grey1.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* <Typography>Шаг</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
fontWeight: "bold",
|
||||||
|
borderRadius: "50%",
|
||||||
|
width: "30px",
|
||||||
|
height: "30px",
|
||||||
|
color: "#FFF",
|
||||||
|
background: theme.palette.brightPurple.main,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{stepNumber} */}
|
||||||
|
{/* </Typography> */}
|
||||||
|
{/* <Typography>Из</Typography>
|
||||||
|
<Typography sx={{ fontWeight: "bold" }}>
|
||||||
|
{questions.length}
|
||||||
|
</Typography> */}
|
||||||
|
</Box>
|
||||||
<Button
|
<Button
|
||||||
disabled={disablePreviousButton}
|
disabled={disablePreviousButton}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
sx={{ fontSize: "16px", padding: "10px 15px" }}
|
sx={{ fontSize: "16px", padding: "10px 15px",}}
|
||||||
onClick={followPreviousStep}
|
onClick={followPreviousStep}
|
||||||
>
|
>
|
||||||
← Назад
|
{isMobileMini ? (
|
||||||
|
"←"
|
||||||
|
) : (
|
||||||
|
"← Назад"
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={disableNextButton}
|
disabled={disableNextButton}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Box } from "@mui/material";
|
import {Box, useTheme} from "@mui/material";
|
||||||
|
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { getQuestionByContentId } from "@root/questions/actions";
|
import { getQuestionByContentId } from "@root/questions/actions";
|
||||||
@ -22,6 +22,9 @@ import { ResultQuestion } from "./ResultQuestion";
|
|||||||
|
|
||||||
import type { QuestionType } from "../../model/question/question";
|
import type { QuestionType } from "../../model/question/question";
|
||||||
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
||||||
|
import {NameplateLogoFQ} from "@icons/NameplateLogoFQ";
|
||||||
|
import {NameplateLogoFQDark} from "@icons/NameplateLogoFQDark";
|
||||||
|
import {modes} from "@utils/themes/Publication/themePublication";
|
||||||
|
|
||||||
type QuestionProps = {
|
type QuestionProps = {
|
||||||
questions: AnyTypedQuizQuestion[];
|
questions: AnyTypedQuizQuestion[];
|
||||||
@ -43,11 +46,10 @@ const QUESTIONS_MAP: any = {
|
|||||||
|
|
||||||
export const Question = ({ questions }: QuestionProps) => {
|
export const Question = ({ questions }: QuestionProps) => {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const [currentQuestion, setCurrentQuestion] =
|
const [currentQuestion, setCurrentQuestion] = useState<AnyTypedQuizQuestion>();
|
||||||
useState<AnyTypedQuizQuestion>();
|
|
||||||
const [showContactForm, setShowContactForm] = useState<boolean>(false);
|
const [showContactForm, setShowContactForm] = useState<boolean>(false);
|
||||||
const [showResultForm, setShowResultForm] = useState<boolean>(false);
|
const [showResultForm, setShowResultForm] = useState<boolean>(false);
|
||||||
|
const mode = modes;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const nextQuestion = getQuestionByContentId(quiz?.config.haveRoot || "");
|
const nextQuestion = getQuestionByContentId(quiz?.config.haveRoot || "");
|
||||||
|
|
||||||
@ -64,9 +66,12 @@ export const Question = ({ questions }: QuestionProps) => {
|
|||||||
|
|
||||||
const QuestionComponent =
|
const QuestionComponent =
|
||||||
QUESTIONS_MAP[currentQuestion.type as Exclude<QuestionType, "nonselected">];
|
QUESTIONS_MAP[currentQuestion.type as Exclude<QuestionType, "nonselected">];
|
||||||
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
sx={{
|
||||||
|
backgroundColor: theme.palette.background.default
|
||||||
|
}}
|
||||||
|
|
||||||
height="100vh"
|
height="100vh"
|
||||||
>
|
>
|
||||||
@ -78,9 +83,18 @@ export const Question = ({ questions }: QuestionProps) => {
|
|||||||
maxWidth: "1440px",
|
maxWidth: "1440px",
|
||||||
padding: "40px 25px 20px",
|
padding: "40px 25px 20px",
|
||||||
margin: "0 auto",
|
margin: "0 auto",
|
||||||
|
overflow: "auto",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<QuestionComponent currentQuestion={currentQuestion} />
|
<QuestionComponent currentQuestion={currentQuestion} />
|
||||||
|
{mode[quiz.config.theme] ? (
|
||||||
|
<NameplateLogoFQ style={{ fontSize: "34px", width:"200px", height:"auto" }} />
|
||||||
|
):(
|
||||||
|
<NameplateLogoFQDark style={{ fontSize: "34px", width:"200px", height:"auto" }} />
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{showResultForm && quiz?.config.resultInfo.when === "before" && (
|
{showResultForm && quiz?.config.resultInfo.when === "before" && (
|
||||||
|
@ -7,6 +7,7 @@ import { useQuestionsStore } from "@root/questions/store";
|
|||||||
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
|
||||||
import YoutubeEmbedIframe from "../../ui_kit/StartPagePreview/YoutubeEmbedIframe.tsx"
|
import YoutubeEmbedIframe from "../../ui_kit/StartPagePreview/YoutubeEmbedIframe.tsx"
|
||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
|
import {modes} from "../../utils/themes/Publication/themePublication";
|
||||||
|
|
||||||
type ResultFormProps = {
|
type ResultFormProps = {
|
||||||
currentQuestion: AnyTypedQuizQuestion;
|
currentQuestion: AnyTypedQuizQuestion;
|
||||||
@ -23,6 +24,7 @@ export const ResultForm = ({
|
|||||||
|
|
||||||
}: ResultFormProps) => {
|
}: ResultFormProps) => {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
const mode = modes;
|
||||||
const { questions } = useQuestionsStore();
|
const { questions } = useQuestionsStore();
|
||||||
const resultQuestion = questions.find(
|
const resultQuestion = questions.find(
|
||||||
(question) =>
|
(question) =>
|
||||||
@ -126,7 +128,7 @@ export const ResultForm = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NameplateLogo style={{ fontSize: "34px" }} />
|
<NameplateLogo style={{ fontSize: "34px" }} />
|
||||||
<Typography sx={{ fontSize: "20px", color: "#4D4D4D", whiteSpace: "nowrap" }}>Сделано на PenaQuiz</Typography>
|
<Typography sx={{ fontSize: "20px", color: mode[quiz.config.theme] ? "#4D4D4D" : "#F5F7FF", whiteSpace: "nowrap" }}>Сделано на PenaQuiz</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -5,6 +5,7 @@ import { QuizStartpageAlignType, QuizStartpageType } from "@model/quizSettings";
|
|||||||
import { notReachable } from "../../utils/notReachable";
|
import { notReachable } from "../../utils/notReachable";
|
||||||
import { useUADevice } from "../../utils/hooks/useUADevice";
|
import { useUADevice } from "../../utils/hooks/useUADevice";
|
||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
|
import {modes} from "../../utils/themes/Publication/themePublication";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setVisualStartPage: (a: boolean) => void;
|
setVisualStartPage: (a: boolean) => void;
|
||||||
@ -13,10 +14,14 @@ interface Props {
|
|||||||
export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
const mode = modes;
|
||||||
const { isMobileDevice } = useUADevice();
|
const { isMobileDevice } = useUADevice();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
|
|
||||||
if (!quiz) return null;
|
if (!quiz) return null;
|
||||||
|
|
||||||
|
console.log(quiz);
|
||||||
|
|
||||||
const handleCopyNumber = () => {
|
const handleCopyNumber = () => {
|
||||||
navigator.clipboard.writeText(quiz.config.info.phonenumber);
|
navigator.clipboard.writeText(quiz.config.info.phonenumber);
|
||||||
};
|
};
|
||||||
@ -76,13 +81,13 @@ export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
|||||||
height: "100vh",
|
height: "100vh",
|
||||||
width: "100vw",
|
width: "100vw",
|
||||||
background:
|
background:
|
||||||
quiz.config.startpageType === "expanded"
|
quiz.config.startpageType === "expanded" && !isMobile
|
||||||
? quiz.config.startpage.position === "left"
|
? quiz.config.startpage.position === "left"
|
||||||
? "linear-gradient(90deg,#272626,transparent)"
|
? "linear-gradient(90deg,#272626,transparent)"
|
||||||
: quiz.config.startpage.position === "center"
|
: quiz.config.startpage.position === "center"
|
||||||
? "linear-gradient(180deg,transparent,#272626)"
|
? "linear-gradient(180deg,transparent,#272626)"
|
||||||
: "linear-gradient(270deg,#272626,transparent)"
|
: "linear-gradient(270deg,#272626,transparent)"
|
||||||
: "",
|
: theme.palette.background.default,
|
||||||
|
|
||||||
color: quiz.config.startpageType === "expanded" ? "white" : "black",
|
color: quiz.config.startpageType === "expanded" ? "white" : "black",
|
||||||
}}
|
}}
|
||||||
@ -109,10 +114,10 @@ export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
|||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Typography sx={{ fontSize: "14px" }}>{quiz.config.info.orgname}</Typography>
|
<Typography sx={{ fontSize: "14px", color: quiz.config.startpageType === "expanded" && !isMobile ? "white" : theme.palette.text.primary}}>{quiz.config.info.orgname}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Link mb="16px" href={quiz.config.info.site}>
|
<Link mb="16px" href={quiz.config.info.site}>
|
||||||
<Typography sx={{ fontSize: "16px", color: theme.palette.brightPurple.main }}>
|
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
||||||
{quiz.config.info.site}
|
{quiz.config.info.site}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
@ -147,6 +152,7 @@ export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
|||||||
overflowWrap: "break-word",
|
overflowWrap: "break-word",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
textAlign: quiz.config.startpageType === "centered" ? "center" : "-moz-initial",
|
textAlign: quiz.config.startpageType === "centered" ? "center" : "-moz-initial",
|
||||||
|
color: quiz.config.startpageType === "expanded" && !isMobile ? "white" : theme.palette.text.primary
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{quiz.name}
|
{quiz.name}
|
||||||
@ -178,25 +184,32 @@ export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{ mt: "46px", display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%" }}
|
sx={{
|
||||||
|
mt: "46px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
flexDirection: isMobile ? "column" : "row"
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ maxWidth: "300px" }}>
|
<Box sx={{ maxWidth: "300px" }}>
|
||||||
{quiz.config.info.clickable ? (
|
{quiz.config.info.clickable ? (
|
||||||
isMobileDevice ? (
|
isMobileDevice ? (
|
||||||
<Link href={`tel:${quiz.config.info.phonenumber}`}>
|
<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}
|
{quiz.config.info.phonenumber}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<ButtonBase onClick={handleCopyNumber}>
|
<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}
|
{quiz.config.info.phonenumber}
|
||||||
</Typography>
|
</Typography>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<Typography sx={{ fontSize: "16px", color: theme.palette.brightPurple.main }}>
|
<Typography sx={{ fontSize: "16px", color: theme.palette.primary.main }}>
|
||||||
{quiz.config.info.phonenumber}
|
{quiz.config.info.phonenumber}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
@ -209,10 +222,11 @@ export const StartPageViewPublication = ({ setVisualStartPage }: Props) => {
|
|||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
gap: "15px"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NameplateLogo style={{ fontSize: "34px" }} />
|
<NameplateLogo style={{ fontSize: "34px", color: quiz.config.startpageType === "expanded" && !isMobile ? "#FFFFFF" : (mode[quiz.config.theme] ? "#151515" : "#FFFFFF") }} />
|
||||||
<Typography sx={{ fontSize: "20px", color: "#4D4D4D", whiteSpace: "nowrap" }}>
|
<Typography sx={{ fontSize: "20px", color: quiz.config.startpageType === "expanded" && !isMobile ? "#F5F7FF" : (mode[quiz.config.theme] ? "#4D4D4D" : "#F5F7FF"), whiteSpace: "nowrap", }}>
|
||||||
Сделано на PenaQuiz
|
Сделано на PenaQuiz
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -241,29 +255,84 @@ function QuizPreviewLayoutByType({
|
|||||||
alignType: QuizStartpageAlignType;
|
alignType: QuizStartpageAlignType;
|
||||||
}) {
|
}) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(630));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
|
function StartPageMobile() {
|
||||||
switch (startpageType) {
|
return(
|
||||||
case null:
|
|
||||||
case "standard": {
|
|
||||||
return (
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: alignType === "left" ? "row" : "row-reverse",
|
flexDirection: "column-reverse",
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
|
justifyContent: "flex-end",
|
||||||
height: "100vh",
|
height: "100vh",
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: !isTablet ? "40%" : "100%",
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: !isTablet ? "flex-start" : "center",
|
alignItems: "flex-start",
|
||||||
p: "25px",
|
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>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch (startpageType) {
|
||||||
|
case null:
|
||||||
|
case "standard": {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile ? (
|
||||||
|
<StartPageMobile/>
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: alignType === "left" ? (isMobile ? "column-reverse" : "row") : "row-reverse",
|
||||||
|
flexGrow: 1,
|
||||||
|
justifyContent: isMobile ? "flex-end" : undefined,
|
||||||
|
height: "100vh",
|
||||||
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: isMobile ? "100%" : "40%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
p: "25px",
|
||||||
|
height: isMobile ? "80%" : undefined
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{quizHeaderBlock}
|
{quizHeaderBlock}
|
||||||
@ -272,17 +341,25 @@ function QuizPreviewLayoutByType({
|
|||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "60%",
|
width: isMobile ? "100%" : "60%",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{backgroundBlock}
|
{backgroundBlock}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case "expanded": {
|
case "expanded": {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile ? (
|
||||||
|
<StartPageMobile/>
|
||||||
|
) : (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: "relative",
|
position: "relative",
|
||||||
@ -322,10 +399,18 @@ function QuizPreviewLayoutByType({
|
|||||||
{backgroundBlock}
|
{backgroundBlock}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case "centered": {
|
case "centered": {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile ? (
|
||||||
|
<StartPageMobile/>
|
||||||
|
) : (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
padding: "16px",
|
padding: "16px",
|
||||||
@ -351,6 +436,10 @@ function QuizPreviewLayoutByType({
|
|||||||
)}
|
)}
|
||||||
{quizMainBlock}
|
{quizMainBlock}
|
||||||
</Box>
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Box } from "@mui/material";
|
import {Box, Button, ThemeProvider, useTheme} from "@mui/material";
|
||||||
|
|
||||||
import { StartPageViewPublication } from "./StartPageViewPublication";
|
import { StartPageViewPublication } from "./StartPageViewPublication";
|
||||||
import { Question } from "./Question";
|
import { Question } from "./Question";
|
||||||
@ -7,7 +7,7 @@ import { useQuestions } from "@root/questions/hooks";
|
|||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { quizApi } from "@api/quiz";
|
import { quizApi } from "@api/quiz";
|
||||||
import { setQuizes } from "@root/quizes/actions";
|
import { setQuizes, updateQuiz } from "@root/quizes/actions";
|
||||||
import { isAxiosError } from "axios";
|
import { isAxiosError } from "axios";
|
||||||
import { devlog } from "@frontend/kitui";
|
import { devlog } from "@frontend/kitui";
|
||||||
import { useQuizStore } from "@root/quizes/store";
|
import { useQuizStore } from "@root/quizes/store";
|
||||||
@ -18,14 +18,14 @@ import { useQuestionsStore } from "@root/questions/store";
|
|||||||
import { setQuestions } from "@root/questions/actions";
|
import { setQuestions } from "@root/questions/actions";
|
||||||
import { questionApi } from "@api/question";
|
import { questionApi } from "@api/question";
|
||||||
import { ApologyPage } from "./ApologyPage"
|
import { ApologyPage } from "./ApologyPage"
|
||||||
|
import {themesPublication} from "../../utils/themes/Publication/themePublication";
|
||||||
|
|
||||||
|
|
||||||
export const ViewPage = () => {
|
export const ViewPage = () => {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const { editQuizId } = useQuizStore();
|
const { editQuizId } = useQuizStore();
|
||||||
const { questions } = useQuestionsStore();
|
const { questions } = useQuestionsStore();
|
||||||
|
const theme = useTheme();
|
||||||
console.log("quiz ", quiz)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const quizes = await quizApi.getList();
|
const quizes = await quizApi.getList();
|
||||||
@ -57,12 +57,50 @@ export const ViewPage = () => {
|
|||||||
if (visualStartPage === undefined) return <></>;
|
if (visualStartPage === undefined) return <></>;
|
||||||
if (questions.length === 0 || (questions.length === 1 && questions[0].type === "result")) return <ApologyPage message="Нет созданных вопросов"/>
|
if (questions.length === 0 || (questions.length === 1 && questions[0].type === "result")) return <ApologyPage message="Нет созданных вопросов"/>
|
||||||
return (
|
return (
|
||||||
<Box>
|
<ThemeProvider theme={themesPublication?.[quiz?.config.theme]}>
|
||||||
|
<Box sx={{backgroundColor: quiz.config.startpageType === "expanded" ? undefined : theme.palette.background.default}}>
|
||||||
{!visualStartPage ? (
|
{!visualStartPage ? (
|
||||||
<StartPageViewPublication setVisualStartPage={setVisualStartPage} />
|
<StartPageViewPublication setVisualStartPage={setVisualStartPage} />
|
||||||
) : (
|
) : (
|
||||||
<Question questions={filteredQuestions} />
|
<Question questions={filteredQuestions} />
|
||||||
)}
|
)}
|
||||||
|
<Box>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "StandardTheme"
|
||||||
|
})}>Standard</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "PinkTheme"
|
||||||
|
})}>Pink</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "BlackWhiteTheme"
|
||||||
|
})}>BlackWhite</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "OliveTheme"
|
||||||
|
})}>Olive</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "YellowTheme"
|
||||||
|
})}>Yellow</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "PurpleTheme"
|
||||||
|
})}>Purple</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "BlueTheme"
|
||||||
|
})}>Blue</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "StandardDarkTheme"
|
||||||
|
})}>StandardDark</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "PinkDarkTheme"
|
||||||
|
})}>PinkDark</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "GoldDarkTheme"
|
||||||
|
})}>GoldDark</Button>
|
||||||
|
<Button onClick={() => updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.theme = "BlueDarkTheme"
|
||||||
|
})}>BlueDark</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</ThemeProvider>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { DatePicker } from "@mui/x-date-pickers";
|
import { DatePicker } from "@mui/x-date-pickers";
|
||||||
import { Box, Typography } from "@mui/material";
|
import {Box, Typography, useTheme} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
||||||
|
|
||||||
import type { QuizQuestionDate } from "../../../model/questionTypes/date";
|
import type { QuizQuestionDate } from "../../../model/questionTypes/date";
|
||||||
import CalendarIcon from "@icons/CalendarIcon";
|
import CalendarIcon from "@icons/CalendarIcon";
|
||||||
|
import {modes} from "../../../utils/themes/Publication/themePublication";
|
||||||
|
import {useCurrentQuiz} from "@root/quizes/hooks";
|
||||||
|
|
||||||
type DateProps = {
|
type DateProps = {
|
||||||
currentQuestion: QuizQuestionDate;
|
currentQuestion: QuizQuestionDate;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Date = ({ currentQuestion }: DateProps) => {
|
export const Date = ({ currentQuestion }: DateProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const mode = modes;
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const answer = answers.find(
|
const answer = answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.content.id
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
@ -20,7 +25,7 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -31,7 +36,10 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
>
|
>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
slots={{
|
slots={{
|
||||||
openPickerIcon: () => <CalendarIcon />,
|
openPickerIcon: () => <CalendarIcon sx={{
|
||||||
|
"& path": {stroke: theme.palette.primary.main},
|
||||||
|
"& rect": {stroke: theme.palette.primary.main}
|
||||||
|
}} />,
|
||||||
}}
|
}}
|
||||||
value={dayjs(
|
value={dayjs(
|
||||||
answer
|
answer
|
||||||
@ -61,10 +69,14 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
},
|
},
|
||||||
"data-cy": "open-datepicker",
|
"data-cy": "open-datepicker",
|
||||||
},
|
},
|
||||||
|
layout: {
|
||||||
|
sx: {backgroundColor: theme.palette.background.default,}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
|
|
||||||
"& .MuiInputBase-root": {
|
"& .MuiInputBase-root": {
|
||||||
backgroundColor: "#F2F3F7",
|
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
|
||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
maxWidth: "250px",
|
maxWidth: "250px",
|
||||||
pr: "22px",
|
pr: "22px",
|
||||||
@ -77,6 +89,7 @@ export const Date = ({ currentQuestion }: DateProps) => {
|
|||||||
borderColor: "#9A9AAF",
|
borderColor: "#9A9AAF",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Radio,
|
Radio,
|
||||||
useTheme,
|
useTheme,
|
||||||
FormControl,
|
FormControl, useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer, deleteAnswer } from "@root/quizView";
|
||||||
@ -22,6 +22,7 @@ type EmojiProps = {
|
|||||||
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.content.id
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
@ -29,7 +30,7 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
value={currentQuestion.content.variants.findIndex(
|
value={currentQuestion.content.variants.findIndex(
|
||||||
@ -49,13 +50,14 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", width: "100%", gap: "42px" }}>
|
<Box sx={{ display: "flex", width: "100%", gap: "42px", flexWrap: "wrap" }}>
|
||||||
{currentQuestion.content.variants.map((variant, index) => (
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
key={variant.id}
|
key={variant.id}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
border: `1px solid`,
|
||||||
|
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
maxWidth: "317px",
|
maxWidth: "317px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@ -89,7 +91,7 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
sx={{
|
sx={{
|
||||||
margin: 0,
|
margin: 0,
|
||||||
padding: "15px",
|
padding: "15px",
|
||||||
color: "#4D4D4D",
|
color: theme.palette.text.primary,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "10px",
|
gap: "10px",
|
||||||
}}
|
}}
|
||||||
@ -107,7 +109,7 @@ export const Emoji = ({ currentQuestion }: EmojiProps) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
control={
|
control={
|
||||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
||||||
}
|
}
|
||||||
label={
|
label={
|
||||||
<Box sx={{ display: "flex", gap: "10px" }}>
|
<Box sx={{ display: "flex", gap: "10px" }}>
|
||||||
|
@ -56,7 +56,7 @@ export const File = ({ currentQuestion }: FileProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -68,11 +68,11 @@ export const File = ({ currentQuestion }: FileProps) => {
|
|||||||
>
|
>
|
||||||
{answer?.split("|")[0] && (
|
{answer?.split("|")[0] && (
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
|
||||||
<Typography>Вы загрузили:</Typography>
|
<Typography color={theme.palette.text.primary}>Вы загрузили:</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
padding: "5px 5px 5px 16px",
|
padding: "5px 5px 5px 16px",
|
||||||
backgroundColor: theme.palette.brightPurple.main,
|
backgroundColor: theme.palette.primary.main,
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
color: "#FFFFFF",
|
color: "#FFFFFF",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -115,7 +115,8 @@ export const File = ({ currentQuestion }: FileProps) => {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
padding: "33px 44px 33px 55px",
|
padding: "33px 44px 33px 55px",
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
border: `1px solid #9A9AAF`,
|
||||||
|
// border: `1px solid ${theme.palette.grey2.main}`,
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -123,7 +124,8 @@ export const File = ({ currentQuestion }: FileProps) => {
|
|||||||
<Box>
|
<Box>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
color: theme.palette.grey2.main,
|
color: "#9A9AAF",
|
||||||
|
// color: theme.palette.grey2.main,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -134,7 +136,8 @@ export const File = ({ currentQuestion }: FileProps) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
color: theme.palette.grey2.main,
|
color: "#9A9AAF",
|
||||||
|
// color: theme.palette.grey2.main,
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
lineHeight: "19px",
|
lineHeight: "19px",
|
||||||
}}
|
}}
|
||||||
|
@ -30,7 +30,7 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
value={currentQuestion.content.variants.findIndex(
|
value={currentQuestion.content.variants.findIndex(
|
||||||
@ -62,7 +62,8 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
|
|||||||
sx={{
|
sx={{
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
border: `1px solid`,
|
||||||
|
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||||
}}
|
}}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -98,12 +99,12 @@ export const Images = ({ currentQuestion }: ImagesProps) => {
|
|||||||
sx={{
|
sx={{
|
||||||
display: "block",
|
display: "block",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
color: theme.palette.grey2.main,
|
color: theme.palette.text.primary,
|
||||||
marginTop: "10px",
|
marginTop: "10px",
|
||||||
}}
|
}}
|
||||||
value={index}
|
value={index}
|
||||||
control={
|
control={
|
||||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
||||||
}
|
}
|
||||||
label={variant.answer}
|
label={variant.answer}
|
||||||
/>
|
/>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Box, Typography, Slider, useTheme } from "@mui/material";
|
import {Box, Typography, Slider, useTheme, useMediaQuery} from "@mui/material";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
|
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
@ -8,6 +8,8 @@ import { CustomSlider } from "@ui_kit/CustomSlider";
|
|||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
||||||
|
|
||||||
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
|
import type { QuizQuestionNumber } from "../../../model/questionTypes/number";
|
||||||
|
import {modes} from "../../../utils/themes/Publication/themePublication";
|
||||||
|
import {useCurrentQuiz} from "@root/quizes/hooks";
|
||||||
|
|
||||||
type NumberProps = {
|
type NumberProps = {
|
||||||
currentQuestion: QuizQuestionNumber;
|
currentQuestion: QuizQuestionNumber;
|
||||||
@ -17,6 +19,9 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
const [minRange, setMinRange] = useState<string>("0");
|
const [minRange, setMinRange] = useState<string>("0");
|
||||||
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
const [maxRange, setMaxRange] = useState<string>("100000000000");
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
|
const mode = modes;
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const updateMinRangeDebounced = useDebouncedCallback(
|
const updateMinRangeDebounced = useDebouncedCallback(
|
||||||
(value, crowded = false) => {
|
(value, crowded = false) => {
|
||||||
@ -26,7 +31,7 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
|
|
||||||
updateAnswer(currentQuestion.content.id, value);
|
updateAnswer(currentQuestion.content.id, value);
|
||||||
},
|
},
|
||||||
1000
|
3000
|
||||||
);
|
);
|
||||||
const updateMaxRangeDebounced = useDebouncedCallback(
|
const updateMaxRangeDebounced = useDebouncedCallback(
|
||||||
(value, crowded = false) => {
|
(value, crowded = false) => {
|
||||||
@ -36,7 +41,7 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
|
|
||||||
updateAnswer(currentQuestion.content.id, value);
|
updateAnswer(currentQuestion.content.id, value);
|
||||||
},
|
},
|
||||||
1000
|
3000
|
||||||
);
|
);
|
||||||
const answer = answers.find(
|
const answer = answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.content.id
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
@ -53,14 +58,14 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
setMinRange(String(currentQuestion.content.start));
|
setMinRange(sliderValue.split("—")[0]);
|
||||||
setMaxRange(String(max));
|
setMaxRange(String(max));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -68,6 +73,7 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
gap: "30px",
|
gap: "30px",
|
||||||
|
paddingRight: isMobile ? "10px" : undefined
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomSlider
|
<CustomSlider
|
||||||
@ -93,6 +99,11 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
setMaxRange(String(range[1]));
|
setMaxRange(String(range[1]));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
"& .MuiSlider-valueLabel": {
|
||||||
|
background: theme.palette.primary.main,}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!currentQuestion.content.chooseRange && (
|
{!currentQuestion.content.chooseRange && (
|
||||||
@ -111,7 +122,11 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "80px",
|
maxWidth: "80px",
|
||||||
"& .MuiInputBase-input": { textAlign: "center" },
|
borderColor: theme.palette.text.primary,
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -141,10 +156,14 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "80px",
|
maxWidth: "80px",
|
||||||
"& .MuiInputBase-input": { textAlign: "center" },
|
borderColor: theme.palette.text.primary,
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography>до</Typography>
|
<Typography color={theme.palette.text.primary}>до</Typography>
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
value={maxRange}
|
value={maxRange}
|
||||||
@ -161,7 +180,11 @@ export const Number = ({ currentQuestion }: NumberProps) => {
|
|||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "80px",
|
maxWidth: "80px",
|
||||||
"& .MuiInputBase-input": { textAlign: "center" },
|
borderColor: theme.palette.text.primary,
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Box, Typography } from "@mui/material";
|
import {Box, Typography, useTheme} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
||||||
|
|
||||||
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
|
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
|
||||||
|
import YoutubeEmbedIframe from "@ui_kit/StartPagePreview/YoutubeEmbedIframe";
|
||||||
|
|
||||||
type PageProps = {
|
type PageProps = {
|
||||||
currentQuestion: QuizQuestionPage;
|
currentQuestion: QuizQuestionPage;
|
||||||
@ -11,11 +12,12 @@ type PageProps = {
|
|||||||
export const Page = ({ currentQuestion }: PageProps) => {
|
export const Page = ({ currentQuestion }: PageProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5" sx={{ paddingBottom: "25px" }}>{currentQuestion.title}</Typography>
|
<Typography variant="h5" sx={{ paddingBottom: "25px", color: theme.palette.text.primary }}>{currentQuestion.title}</Typography>
|
||||||
<Typography>{currentQuestion.content.text}</Typography>
|
<Typography color={theme.palette.text.primary}>{currentQuestion.content.text}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -24,11 +26,10 @@ export const Page = ({ currentQuestion }: PageProps) => {
|
|||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{currentQuestion.content.picture && (
|
{currentQuestion.content.useImage ? (
|
||||||
<Box sx={{borderRadius: "12px",
|
<Box sx={{ borderRadius: "12px", border: "1px solid #9A9AAF", overflow: "hidden" }}>
|
||||||
border: "1px solid #9A9AAF", overflow: "hidden" }}>
|
|
||||||
<img
|
<img
|
||||||
src={currentQuestion.content.picture}
|
src={currentQuestion.content.back}
|
||||||
alt=""
|
alt=""
|
||||||
style={{
|
style={{
|
||||||
display: "block",
|
display: "block",
|
||||||
@ -38,18 +39,10 @@ export const Page = ({ currentQuestion }: PageProps) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
) : (
|
||||||
)}
|
<YoutubeEmbedIframe
|
||||||
{currentQuestion.content.video && (
|
containerSX={{ width: "100%", height: "100%", maxHeight: "80vh", objectFit: "contain" }}
|
||||||
<video
|
videoUrl={currentQuestion.content.video}
|
||||||
src={currentQuestion.content.video}
|
|
||||||
controls
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
maxHeight: "80vh",
|
|
||||||
objectFit: "contain",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -2,7 +2,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
Rating as RatingComponent,
|
Rating as RatingComponent,
|
||||||
useTheme,
|
useTheme, useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
import { useQuizViewStore, updateAnswer } from "@root/quizView";
|
||||||
@ -55,6 +55,7 @@ const buttonRatingForm = [
|
|||||||
export const Rating = ({ currentQuestion }: RatingProps) => {
|
export const Rating = ({ currentQuestion }: RatingProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.content.id
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
@ -65,16 +66,20 @@ export const Rating = ({ currentQuestion }: RatingProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "inline-flex",
|
display: "inline-flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "20px",
|
gap: "20px",
|
||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
|
width: isMobile ? "100%" : undefined
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
<Typography sx={{
|
||||||
|
color: "#9A9AAF"
|
||||||
|
// color: theme.palette.grey2.main
|
||||||
|
}}>
|
||||||
{currentQuestion.content.ratingNegativeDescription}
|
{currentQuestion.content.ratingNegativeDescription}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
@ -88,13 +93,17 @@ export const Rating = ({ currentQuestion }: RatingProps) => {
|
|||||||
onChange={(_, value) =>
|
onChange={(_, value) =>
|
||||||
updateAnswer(currentQuestion.content.id, String(value))
|
updateAnswer(currentQuestion.content.id, String(value))
|
||||||
}
|
}
|
||||||
sx={{ height: "50px", gap: "15px" }}
|
sx={{ height: "50px",
|
||||||
|
gap: isMobile ? undefined : "15px",
|
||||||
|
justifyContent: isMobile ? "space-between" : undefined,
|
||||||
|
width: isMobile ? "100%" : undefined
|
||||||
|
}}
|
||||||
max={currentQuestion.content.steps}
|
max={currentQuestion.content.steps}
|
||||||
icon={form?.icon(theme.palette.brightPurple.main)}
|
icon={form?.icon(theme.palette.primary.main)}
|
||||||
emptyIcon={form?.icon(theme.palette.grey2.main)}
|
emptyIcon={form?.icon("#9A9AAF")}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
<Typography sx={{ color: "#9A9AAF" }}>
|
||||||
{currentQuestion.content.ratingPositiveDescription}
|
{currentQuestion.content.ratingPositiveDescription}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box, Typography } from "@mui/material";
|
import {Box, Typography, useTheme} from "@mui/material";
|
||||||
|
|
||||||
import { Select as SelectComponent } from "../../../pages/Questions/Select";
|
import { Select as SelectComponent } from "../../../pages/Questions/Select";
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ type SelectProps = {
|
|||||||
|
|
||||||
export const Select = ({ currentQuestion }: SelectProps) => {
|
export const Select = ({ currentQuestion }: SelectProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
|
const theme = useTheme();
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.content.id
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
@ -19,7 +20,7 @@ export const Select = ({ currentQuestion }: SelectProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -32,6 +33,8 @@ export const Select = ({ currentQuestion }: SelectProps) => {
|
|||||||
placeholder={currentQuestion.content.default}
|
placeholder={currentQuestion.content.default}
|
||||||
activeItemIndex={answer ? Number(answer) : -1}
|
activeItemIndex={answer ? Number(answer) : -1}
|
||||||
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
items={currentQuestion.content.variants.map(({ answer }) => answer)}
|
||||||
|
colorMain={theme.palette.primary.main}
|
||||||
|
color={theme.palette.primary.main}
|
||||||
onChange={(_, value) => {
|
onChange={(_, value) => {
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
deleteAnswer(currentQuestion.content.id);
|
deleteAnswer(currentQuestion.content.id);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box, Typography } from "@mui/material";
|
import {Box, Typography, useTheme} from "@mui/material";
|
||||||
|
|
||||||
import CustomTextField from "@ui_kit/CustomTextField";
|
import CustomTextField from "@ui_kit/CustomTextField";
|
||||||
|
|
||||||
@ -13,10 +13,10 @@ type TextProps = {
|
|||||||
export const Text = ({ currentQuestion }: TextProps) => {
|
export const Text = ({ currentQuestion }: TextProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
const { answer } = answers.find(({ questionId }) => questionId === currentQuestion.content.id) ?? {};
|
||||||
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -29,6 +29,11 @@ export const Text = ({ currentQuestion }: TextProps) => {
|
|||||||
placeholder={currentQuestion.content.placeholder}
|
placeholder={currentQuestion.content.placeholder}
|
||||||
value={answer || ""}
|
value={answer || ""}
|
||||||
onChange={({ target }) => updateAnswer(currentQuestion.content.id, target.value)}
|
onChange={({ target }) => updateAnswer(currentQuestion.content.id, target.value)}
|
||||||
|
sx={{
|
||||||
|
"&:focus-visible": {
|
||||||
|
borderColor: theme.palette.primary.main
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -21,10 +21,12 @@ import {
|
|||||||
|
|
||||||
import RadioCheck from "@ui_kit/RadioCheck";
|
import RadioCheck from "@ui_kit/RadioCheck";
|
||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
import { CheckboxIcon } from "@icons/Checkbox";
|
import CheckboxIcon from "@icons/Checkbox";
|
||||||
|
import {modes} from "../../../utils/themes/Publication/themePublication";
|
||||||
|
|
||||||
import type { QuizQuestionVariant } from "../../../model/questionTypes/variant";
|
import type { QuizQuestionVariant } from "../../../model/questionTypes/variant";
|
||||||
import type { QuestionVariant } from "../../../model/questionTypes/shared";
|
import type { QuestionVariant } from "../../../model/questionTypes/shared";
|
||||||
|
import {useCurrentQuiz} from "@root/quizes/hooks";
|
||||||
|
|
||||||
type VariantProps = {
|
type VariantProps = {
|
||||||
stepNumber: number;
|
stepNumber: number;
|
||||||
@ -40,6 +42,7 @@ type VariantItemProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Variant = ({ currentQuestion }: VariantProps) => {
|
export const Variant = ({ currentQuestion }: VariantProps) => {
|
||||||
|
const theme = useTheme()
|
||||||
const { answers, ownVariants } = useQuizViewStore();
|
const { answers, ownVariants } = useQuizViewStore();
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
@ -59,7 +62,7 @@ export const Variant = ({ currentQuestion }: VariantProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box sx={{ display: "flex" }}>
|
<Box sx={{ display: "flex" }}>
|
||||||
<Group
|
<Group
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
@ -126,15 +129,19 @@ const VariantItem = ({
|
|||||||
own = false,
|
own = false,
|
||||||
}: VariantItemProps) => {
|
}: VariantItemProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const mode = modes
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
return (
|
return (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
key={variant.id}
|
key={variant.id}
|
||||||
sx={{
|
sx={{
|
||||||
margin: "0",
|
margin: "0",
|
||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
padding: "15px",
|
padding: "15px",
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
border: `1px solid`,
|
||||||
|
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||||
|
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
maxWidth: "685px",
|
maxWidth: "685px",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
@ -150,11 +157,11 @@ const VariantItem = ({
|
|||||||
currentQuestion.content.multi ? (
|
currentQuestion.content.multi ? (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={!!answer?.includes(variant.id)}
|
checked={!!answer?.includes(variant.id)}
|
||||||
checkedIcon={<CheckboxIcon checked />}
|
checkedIcon={<CheckboxIcon checked color={theme.palette.primary.main} />}
|
||||||
icon={<CheckboxIcon />}
|
icon={<CheckboxIcon />}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={own ? <TextField label="Другое..." /> : variant.answer}
|
label={own ? <TextField label="Другое..." /> : variant.answer}
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
RadioGroup,
|
RadioGroup,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Radio,
|
Radio,
|
||||||
useTheme,
|
useTheme, useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import gag from "./gag.png"
|
import gag from "./gag.png"
|
||||||
@ -15,6 +15,8 @@ import RadioCheck from "@ui_kit/RadioCheck";
|
|||||||
import RadioIcon from "@ui_kit/RadioIcon";
|
import RadioIcon from "@ui_kit/RadioIcon";
|
||||||
|
|
||||||
import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
|
import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
|
||||||
|
import {modes} from "../../../utils/themes/Publication/themePublication";
|
||||||
|
import {useCurrentQuiz} from "@root/quizes/hooks";
|
||||||
|
|
||||||
type VarimgProps = {
|
type VarimgProps = {
|
||||||
currentQuestion: QuizQuestionVarImg;
|
currentQuestion: QuizQuestionVarImg;
|
||||||
@ -22,7 +24,10 @@ type VarimgProps = {
|
|||||||
|
|
||||||
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
||||||
const { answers } = useQuizViewStore();
|
const { answers } = useQuizViewStore();
|
||||||
|
const mode = modes;
|
||||||
|
const quiz = useCurrentQuiz();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||||
const { answer } =
|
const { answer } =
|
||||||
answers.find(
|
answers.find(
|
||||||
({ questionId }) => questionId === currentQuestion.content.id
|
({ questionId }) => questionId === currentQuestion.content.id
|
||||||
@ -33,8 +38,14 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5">{currentQuestion.title}</Typography>
|
<Typography variant="h5" color={theme.palette.text.primary}>{currentQuestion.title}</Typography>
|
||||||
<Box sx={{ display: "flex", marginTop: "20px" }}>
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
marginTop: "20px",
|
||||||
|
flexDirection: isMobile ? "column-reverse" : undefined,
|
||||||
|
gap: isMobile ? "30px" : undefined
|
||||||
|
|
||||||
|
}}>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={currentQuestion.id}
|
name={currentQuestion.id}
|
||||||
value={currentQuestion.content.variants.findIndex(
|
value={currentQuestion.content.variants.findIndex(
|
||||||
@ -48,7 +59,7 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
flexBasis: "100%",
|
flexBasis: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%", gap: isMobile ? "20px" : undefined }}>
|
||||||
{currentQuestion.content.variants.map((variant, index) => (
|
{currentQuestion.content.variants.map((variant, index) => (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
key={variant.id}
|
key={variant.id}
|
||||||
@ -56,9 +67,12 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
marginBottom: "15px",
|
marginBottom: "15px",
|
||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
padding: "15px",
|
padding: "15px",
|
||||||
color: "#4D4D4D",
|
color: theme.palette.text.primary,
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
backgroundColor: mode[quiz.config.theme] ? "white" : theme.palette.background.default,
|
||||||
|
border: `1px solid`,
|
||||||
|
borderColor: answer === variant.id ? theme.palette.primary.main : "#9A9AAF",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
margin: isMobile ? 0 : undefined,
|
||||||
}}
|
}}
|
||||||
value={index}
|
value={index}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
@ -74,7 +88,7 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
control={
|
control={
|
||||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
<Radio checkedIcon={<RadioCheck color={theme.palette.primary.main}/>} icon={<RadioIcon />} />
|
||||||
}
|
}
|
||||||
label={variant.answer}
|
label={variant.answer}
|
||||||
/>
|
/>
|
||||||
@ -107,7 +121,7 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => {
|
|||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
:
|
:
|
||||||
(variant?.extendedText || "Выберите вариант ответа слева")
|
(variant?.extendedText || isMobile ? ("Выберите вариант ответа ниже") : ("Выберите вариант ответа слева"))
|
||||||
}
|
}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
72
src/pages/startPage/ConfirmLeaveModal/index.tsx
Normal file
72
src/pages/startPage/ConfirmLeaveModal/index.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { Box, Button, Modal, Typography } from "@mui/material";
|
||||||
|
|
||||||
|
import { useUiTools } from "@root/uiTools/store";
|
||||||
|
|
||||||
|
type ConfirmLeaveModalProps = {
|
||||||
|
open: boolean;
|
||||||
|
follow: () => void;
|
||||||
|
cancel: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ConfirmLeaveModal = ({
|
||||||
|
open,
|
||||||
|
follow,
|
||||||
|
cancel,
|
||||||
|
}: ConfirmLeaveModalProps) => (
|
||||||
|
<Modal open={open} onClose={cancel}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
outline: "none",
|
||||||
|
position: "absolute",
|
||||||
|
overflow: "hidden",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
maxWidth: "620px",
|
||||||
|
width: "100%",
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
borderRadius: "12px",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
boxSizing: "border-box",
|
||||||
|
background: "#F2F3F7",
|
||||||
|
height: "70px",
|
||||||
|
padding: "0 25px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography component="span">
|
||||||
|
Пожалуйста, проверьте, что вы заполнили все результаты
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "end",
|
||||||
|
gap: "10px",
|
||||||
|
margin: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{ width: "100%", maxWidth: "130px" }}
|
||||||
|
onClick={cancel}
|
||||||
|
>
|
||||||
|
Остаться
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
sx={{ width: "100%", maxWidth: "130px" }}
|
||||||
|
onClick={follow}
|
||||||
|
>
|
||||||
|
Покинуть
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
@ -4,6 +4,8 @@ import BackArrowIcon from "@icons/BackArrowIcon";
|
|||||||
import { Burger } from "@icons/Burger";
|
import { Burger } from "@icons/Burger";
|
||||||
import EyeIcon from "@icons/EyeIcon";
|
import EyeIcon from "@icons/EyeIcon";
|
||||||
import { PenaLogoIcon } from "@icons/PenaLogoIcon";
|
import { PenaLogoIcon } from "@icons/PenaLogoIcon";
|
||||||
|
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@ -16,7 +18,7 @@ import {
|
|||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { decrementCurrentStep, resetEditConfig, setQuizes, updateQuiz } from "@root/quizes/actions";
|
import { decrementCurrentStep, resetEditConfig, setQuizes, updateQuiz, setCurrentStep } from "@root/quizes/actions";
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import { useQuizStore } from "@root/quizes/store";
|
import { useQuizStore } from "@root/quizes/store";
|
||||||
import CustomAvatar from "@ui_kit/Header/Avatar";
|
import CustomAvatar from "@ui_kit/Header/Avatar";
|
||||||
@ -33,7 +35,13 @@ import useSWR from "swr";
|
|||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import { SidebarMobile } from "./Sidebar/SidebarMobile";
|
import { SidebarMobile } from "./Sidebar/SidebarMobile";
|
||||||
import { cleanQuestions, createResult, setQuestions } from "@root/questions/actions";
|
import { cleanQuestions, createResult, setQuestions } from "@root/questions/actions";
|
||||||
import { updateOpenBranchingPanel, updateCanCreatePublic, updateModalInfoWhyCantCreate } from "@root/uiTools/actions";
|
import {
|
||||||
|
updateOpenBranchingPanel,
|
||||||
|
updateCanCreatePublic,
|
||||||
|
updateModalInfoWhyCantCreate,
|
||||||
|
setShowConfirmLeaveModal,
|
||||||
|
updateSomeWorkBackend,
|
||||||
|
} from "@root/uiTools/actions";
|
||||||
import { BranchingPanel } from "../Questions/BranchingPanel";
|
import { BranchingPanel } from "../Questions/BranchingPanel";
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { useQuizes } from "@root/quizes/hooks";
|
import { useQuizes } from "@root/quizes/hooks";
|
||||||
@ -46,94 +54,98 @@ import { clearAuthToken } from "@frontend/kitui";
|
|||||||
import { logout } from "@api/auth";
|
import { logout } from "@api/auth";
|
||||||
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
import { ModalInfoWhyCantCreate } from "./ModalInfoWhyCantCreate";
|
import { ModalInfoWhyCantCreate } from "./ModalInfoWhyCantCreate";
|
||||||
import { type } from "os";
|
import { ConfirmLeaveModal } from "./ConfirmLeaveModal";
|
||||||
|
import { checkQuestionHint } from "@utils/checkQuestionHint";
|
||||||
|
import { deleteTimeoutedQuestions } from "@utils/deleteTimeoutedQuestions";
|
||||||
|
import { toggleQuizPreview } from "@root/quizPreview";
|
||||||
|
import { LinkSimple } from "@icons/LinkSimple";
|
||||||
|
import { BackButtonIcon } from "@icons/BackButtonIcon";
|
||||||
|
|
||||||
|
let init: () => void;
|
||||||
export default function EditPage() {
|
export default function EditPage() {
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const { editQuizId } = useQuizStore();
|
const { editQuizId } = useQuizStore();
|
||||||
const { questions } = useQuestionsStore();
|
const { questions } = useQuestionsStore();
|
||||||
|
|
||||||
|
console.log("quiz ", quiz);
|
||||||
|
console.log(questions);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const quizes = await quizApi.getList();
|
const quizes = await quizApi.getList();
|
||||||
setQuizes(quizes);
|
setQuizes(quizes);
|
||||||
|
|
||||||
|
if (editQuizId) {
|
||||||
const questions = await questionApi.getList({ quiz_id: editQuizId });
|
const questions = await questionApi.getList({ quiz_id: editQuizId });
|
||||||
setQuestions(questions);
|
setQuestions(questions);
|
||||||
|
|
||||||
//Всегда должен существовать хоть 1 резулт - "line"
|
//Всегда должен существовать хоть 1 резулт - "line"
|
||||||
console.log(questions)
|
// console.log("сейчас будем ворошиться в этих квешенах ", questions);
|
||||||
|
|
||||||
if (!questions?.find(q=>q.type === "result" && q.content.includes(':"line"') || q.content.includes(":'line'"))) createResult(quiz?.backendId, "line")
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
!questions?.find(
|
||||||
|
(q) => (q.type === "result" && q.content.includes(':"line"')) || q.content.includes(":'line'")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
createResult(quiz?.backendId, "line");
|
||||||
|
console.log("Я не нашёл линейный резулт и собираюсь создать новый");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
getData();
|
getData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { openBranchingPanel, whyCantCreatePublic, canCreatePublic } = useUiTools();
|
const { openBranchingPanel, whyCantCreatePublic, canCreatePublic, showConfirmLeaveModal } = useUiTools();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const currentStep = useQuizStore((state) => state.currentStep);
|
const currentStep = useQuizStore((state) => state.currentStep);
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
const isMobile = useMediaQuery(theme.breakpoints.down(660));
|
||||||
|
const isMobileSm = useMediaQuery(theme.breakpoints.down(370));
|
||||||
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
|
const [mobileSidebar, setMobileSidebar] = useState<boolean>(false);
|
||||||
|
const [nextStep, setNextStep] = useState<number>(0);
|
||||||
const quizConfig = quiz?.config;
|
const quizConfig = quiz?.config;
|
||||||
const disableTest = quiz === undefined ? true : (quiz.config.type === null)
|
const disableTest = quiz === undefined ? true : quiz.config.type === null;
|
||||||
|
const [openBranchingPage, setOpenBranchingPage] = useState<boolean>(false);
|
||||||
|
const [buttonText, setButtonText] = useState("Опубликовать");
|
||||||
|
|
||||||
|
|
||||||
|
const openBranchingPageHC = () => {
|
||||||
|
if (!openBranchingPage) {
|
||||||
|
deleteTimeoutedQuestions(questions, quiz);
|
||||||
|
}
|
||||||
|
setOpenBranchingPage((old) => !old);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editQuizId === null) navigate("/list");
|
if (editQuizId === null) navigate("/list");
|
||||||
}, [navigate, editQuizId]);
|
}, [navigate, editQuizId]);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => () => {
|
||||||
return () => {
|
|
||||||
resetEditConfig();
|
resetEditConfig();
|
||||||
cleanQuestions();
|
cleanQuestions();
|
||||||
updateModalInfoWhyCantCreate(false)
|
updateModalInfoWhyCantCreate(false);
|
||||||
}
|
updateSomeWorkBackend(false)
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updateQuestionHint = useDebouncedCallback((questions: AnyTypedQuizQuestion[]) => {
|
const updateQuestionHint = useDebouncedCallback((questions: AnyTypedQuizQuestion[]) => {
|
||||||
|
const problems = checkQuestionHint(questions);
|
||||||
const problems: any = {}
|
useUiTools.setState({ whyCantCreatePublic: problems });
|
||||||
|
|
||||||
questions.forEach((question) => {
|
|
||||||
//Если не участвует в ветвлении, или безтиповый, или резулт - он нам не интересен
|
|
||||||
if (question.type === null
|
|
||||||
|| question.type === "result"
|
|
||||||
|| question.content.rule.parentId.length === 0) return
|
|
||||||
|
|
||||||
//если есть дети, но нет дефолта - логическая ошибка. Так нельзя
|
|
||||||
if (question.content.rule.children.length > 0 && question.content.rule.default.length === 0) {
|
|
||||||
problems[question.content.id] = {
|
|
||||||
name: question.title,
|
|
||||||
problems: ["Не выбран дефолтный вопрос"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
useUiTools.setState({ whyCantCreatePublic: problems })
|
|
||||||
if (Object.keys(problems).length > 0) {
|
if (Object.keys(problems).length > 0) {
|
||||||
updateQuiz(quiz?.id, (state) => { state.status = "stop" })
|
updateQuiz(quiz?.id, (state) => {
|
||||||
updateCanCreatePublic(false)
|
state.status = "stop";
|
||||||
|
});
|
||||||
|
updateCanCreatePublic(false);
|
||||||
} else {
|
} else {
|
||||||
updateCanCreatePublic(true)
|
updateCanCreatePublic(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateQuestionHint(questions)
|
updateQuestionHint(questions);
|
||||||
}, [questions]);
|
}, [questions]);
|
||||||
|
|
||||||
|
|
||||||
async function handleLogoutClick() {
|
async function handleLogoutClick() {
|
||||||
const [, logoutError] = await logout();
|
const [, logoutError] = await logout();
|
||||||
|
|
||||||
@ -145,9 +157,38 @@ export default function EditPage() {
|
|||||||
clearUserData();
|
clearUserData();
|
||||||
navigate("/");
|
navigate("/");
|
||||||
}
|
}
|
||||||
console.log(questions)
|
|
||||||
|
|
||||||
if (!quizConfig) return <></>
|
const followNewPage = () => {
|
||||||
|
setShowConfirmLeaveModal(false);
|
||||||
|
setCurrentStep(nextStep);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!quizConfig) return <></>;
|
||||||
|
|
||||||
|
const isConditionMet = [1].includes(currentStep) && !openBranchingPanel && quizConfig.type !== "form";
|
||||||
|
|
||||||
|
|
||||||
|
const handleClickStatusQuiz = () => {
|
||||||
|
if (Object.keys(whyCantCreatePublic).length === 0) {
|
||||||
|
if (buttonText === "Опубликовать") {
|
||||||
|
setButtonText("Опубликовано");
|
||||||
|
setTimeout(() => {
|
||||||
|
setButtonText("Отозвать");
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
setButtonText("Опубликовать");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateQuiz(quiz?.id, (state) => {
|
||||||
|
state.status = quiz?.status === "start" ? "stop" : "start";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateModalInfoWhyCantCreate(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(quiz?.status);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/*хедер*/}
|
{/*хедер*/}
|
||||||
@ -276,14 +317,19 @@ export default function EditPage() {
|
|||||||
display: isMobile ? "block" : "flex",
|
display: isMobile ? "block" : "flex",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isMobile ? <SidebarMobile open={mobileSidebar} /> : <Sidebar />}
|
{isMobile ? <SidebarMobile open={mobileSidebar} /> : <Sidebar setNextStep={setNextStep} />}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
padding: isMobile ? "16px 16px 140px 16px" : "25px 25px 140px 25px",
|
overflow: "hidden",
|
||||||
height: "calc(100vh - 80px)",
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
padding: isMobile ? "16px 16px 20px 16px" : "25px 25px 20px 25px",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
|
height: isMobile ? ` calc(100vh - 125px) ` : `calc(100vh - ${isConditionMet ? "186px" : "166px"})`,
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -296,26 +342,25 @@ export default function EditPage() {
|
|||||||
quizType={quizConfig.type}
|
quizType={quizConfig.type}
|
||||||
quizResults={quizConfig.results}
|
quizResults={quizConfig.results}
|
||||||
quizStartPageType={quizConfig.startpageType}
|
quizStartPageType={quizConfig.startpageType}
|
||||||
|
openBranchingPage={openBranchingPage}
|
||||||
|
setOpenBranchingPage={setOpenBranchingPage}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: "absolute",
|
|
||||||
left: 0,
|
|
||||||
bottom: 0,
|
|
||||||
width: "100%",
|
width: "100%",
|
||||||
padding: isMobile ? "20px 16px" : "20px 40px 20px 250px",
|
padding: isMobile ? "20px 16px" : "20px 20px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "flex-start",
|
justifyContent: isMobile ? (isMobileSm ? "center" : "flex-end") : "flex-start",
|
||||||
|
flexDirection: isMobile ? "row-reverse" : "-moz-initial",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "15px",
|
gap: "15px",
|
||||||
background: "#FFF",
|
background: "#FFF",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{[1].includes(currentStep) && !openBranchingPanel && quizConfig.type !== "form" && (
|
{isConditionMet && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -328,8 +373,8 @@ export default function EditPage() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
checked={openBranchingPanel}
|
checked={openBranchingPage}
|
||||||
onChange={(e) => updateOpenBranchingPanel(e.target.checked)}
|
onChange={openBranchingPageHC}
|
||||||
sx={{
|
sx={{
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 30,
|
height: 30,
|
||||||
@ -376,7 +421,8 @@ export default function EditPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!canCreatePublic && quiz.config.type !== "form" ?
|
<Box sx={{ display: isMobile ? "none" : "block" }}>
|
||||||
|
{!canCreatePublic && quiz.config.type !== "form" ? (
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
// disabled
|
// disabled
|
||||||
@ -386,11 +432,13 @@ export default function EditPage() {
|
|||||||
height: "34px",
|
height: "34px",
|
||||||
minWidth: "130px",
|
minWidth: "130px",
|
||||||
}}
|
}}
|
||||||
onClick={() => Object.keys(whyCantCreatePublic).length === 0 ? () => { } : updateModalInfoWhyCantCreate(true)}
|
onClick={() =>
|
||||||
|
Object.keys(whyCantCreatePublic).length === 0 ? () => {} : updateModalInfoWhyCantCreate(true)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Тестовый просмотр
|
Тестовый просмотр
|
||||||
</Button>
|
</Button>
|
||||||
:
|
) : (
|
||||||
<a href={`/view`} target="_blank" rel="noreferrer" style={{ textDecoration: "none" }}>
|
<a href={`/view`} target="_blank" rel="noreferrer" style={{ textDecoration: "none" }}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@ -404,40 +452,105 @@ export default function EditPage() {
|
|||||||
Тестовый просмотр
|
Тестовый просмотр
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
}
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="contained"
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "14px",
|
fontSize: "14px",
|
||||||
lineHeight: "18px",
|
lineHeight: "18px",
|
||||||
height: "34px",
|
height: "34px",
|
||||||
border: `1px solid ${theme.palette.brightPurple.main}`,
|
background: quiz?.status === "start" ? "#7E2AEA" : "#FA5B0E",
|
||||||
backgroundColor: quiz?.status === "start" ? theme.palette.brightPurple.main : "transparent",
|
|
||||||
color: quiz?.status === "start" ? "#FFFFFF" : theme.palette.brightPurple.main,
|
|
||||||
}}
|
}}
|
||||||
onClick={
|
onClick={handleClickStatusQuiz}
|
||||||
Object.keys(whyCantCreatePublic).length === 0 ?
|
|
||||||
() => updateQuiz(quiz?.id, (state) => {
|
|
||||||
state.status = quiz?.status === "start" ? "stop" : "start";
|
|
||||||
})
|
|
||||||
:
|
|
||||||
() => updateModalInfoWhyCantCreate(true)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{quiz?.status === "start" ? "Стоп" : "Старт"}
|
{buttonText === "Отозвать" ? (
|
||||||
|
<Box sx={{ display: "flex", gap: "4px", alignItems: "center" }}>
|
||||||
|
{buttonText} <BackButtonIcon />
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
buttonText
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
{quiz?.status === "start" && <Box
|
{quiz?.status === "start" && (
|
||||||
|
<Box
|
||||||
component={Link}
|
component={Link}
|
||||||
sx={{
|
sx={{
|
||||||
color: "#7e2aea",
|
display: isMobile ? "none" : "block",
|
||||||
fontSize: "14px"
|
color: "#7E2AEA",
|
||||||
|
fontSize: "14px",
|
||||||
}}
|
}}
|
||||||
target="_blank" to={"https://hbpn.link/" + quiz.qid}>https://hbpn.link/{quiz.qid}
|
target="_blank"
|
||||||
</Box>}
|
to={"https://hbpn.link/" + quiz.qid}
|
||||||
|
>
|
||||||
|
https://hbpn.link/{quiz.qid}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isMobile ? (
|
||||||
|
<Button
|
||||||
|
onClick={toggleQuizPreview}
|
||||||
|
variant="outlined"
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "4px",
|
||||||
|
fontSize: "14px",
|
||||||
|
lineHeight: "18px",
|
||||||
|
height: "34px",
|
||||||
|
border: "1px solid #7E2AEA",
|
||||||
|
color: "#7E2AEA",
|
||||||
|
background: "white",
|
||||||
|
p: "8px 14px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EyeIcon />
|
||||||
|
Предпросмотр
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<IconButton
|
||||||
|
onClick={toggleQuizPreview}
|
||||||
|
sx={{
|
||||||
|
pointerEvents: "auto",
|
||||||
|
marginLeft: "auto",
|
||||||
|
position: "relative",
|
||||||
|
zIndex: "999999",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VisibilityIcon sx={{ height: "30px", width: "30px" }} />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isMobile && quiz?.status === "start" && (
|
||||||
|
<Box
|
||||||
|
component={Link}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
width: "34px",
|
||||||
|
height: "34px",
|
||||||
|
color: "#7E2AEA",
|
||||||
|
fontSize: "14px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "Center",
|
||||||
|
background: "#EEE4FC",
|
||||||
|
borderRadius: "8px",
|
||||||
|
}}
|
||||||
|
target="_blank"
|
||||||
|
to={"https://hbpn.link/" + quiz.qid}
|
||||||
|
>
|
||||||
|
<LinkSimple />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box >
|
|
||||||
<ModalInfoWhyCantCreate />
|
<ModalInfoWhyCantCreate />
|
||||||
|
<ConfirmLeaveModal
|
||||||
|
open={showConfirmLeaveModal}
|
||||||
|
follow={followNewPage}
|
||||||
|
cancel={() => setShowConfirmLeaveModal(false)}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,44 @@
|
|||||||
import { Box, Modal, Typography, Divider } from "@mui/material"
|
import { Box, Modal, Typography, Divider } from "@mui/material";
|
||||||
|
|
||||||
import { useUiTools } from "@root/uiTools/store";
|
import { useUiTools } from "@root/uiTools/store";
|
||||||
import { updateModalInfoWhyCantCreate } from "@root/uiTools/actions";
|
import { updateModalInfoWhyCantCreate } from "@root/uiTools/actions";
|
||||||
import { useLayoutEffect } from "react";
|
import { useLayoutEffect } from "react";
|
||||||
|
|
||||||
export const ModalInfoWhyCantCreate = () => {
|
export const ModalInfoWhyCantCreate = () => {
|
||||||
|
|
||||||
const { whyCantCreatePublic, openModalInfoWhyCantCreate } = useUiTools();
|
const { whyCantCreatePublic, openModalInfoWhyCantCreate } = useUiTools();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal open={openModalInfoWhyCantCreate} onClose={() => updateModalInfoWhyCantCreate(false)}>
|
||||||
open={openModalInfoWhyCantCreate}
|
<Box
|
||||||
onClose={() => updateModalInfoWhyCantCreate(false)}
|
sx={{
|
||||||
>
|
position: "absolute" as "absolute",
|
||||||
<Box sx={{
|
top: "50%",
|
||||||
position: 'absolute' as 'absolute',
|
left: "50%",
|
||||||
top: '50%',
|
transform: "translate(-50%, -50%)",
|
||||||
left: '50%',
|
maxWidth: "620px",
|
||||||
transform: 'translate(-50%, -50%)',
|
width: "100%",
|
||||||
maxWidth: '620px',
|
bgcolor: "background.paper",
|
||||||
width: '100%',
|
borderRadius: "12px",
|
||||||
bgcolor: 'background.paper',
|
|
||||||
borderRadius: '12px',
|
|
||||||
|
|
||||||
boxShadow: 24,
|
boxShadow: 24,
|
||||||
p: "25px",
|
p: "25px",
|
||||||
minHeight: "60vh",
|
minHeight: "60vh",
|
||||||
maxHeight: "90vh",
|
maxHeight: "90vh",
|
||||||
overflow: "auto"
|
overflow: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{
|
{Object.values(whyCantCreatePublic).map((data) => {
|
||||||
Object.values(whyCantCreatePublic).map((data) => {
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography color="#7e2aea">У вопроса "{data.name}"</Typography>
|
<Typography color="#7e2aea">У вопроса "{data.name}"</Typography>
|
||||||
{
|
{data.problems.map((problem) => (
|
||||||
data.problems.map((problem) => <Typography p="5px 0">{problem}</Typography>)
|
<Typography p="5px 0">{problem}</Typography>
|
||||||
}
|
))}
|
||||||
<Divider/>
|
<Divider />
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
@ -42,7 +42,7 @@ export default function ModalSizeImage() {
|
|||||||
createData("Варианты и картинка", "380х307 px"),
|
createData("Варианты и картинка", "380х307 px"),
|
||||||
createData("Консультант", "140х140 px"),
|
createData("Консультант", "140х140 px"),
|
||||||
createData("Логотип", "107х37 px"),
|
createData("Логотип", "107х37 px"),
|
||||||
createData("", "1100х600 px"),
|
createData("Результаты", "1100х600 px"),
|
||||||
createData("Бонус", "200х60 px"),
|
createData("Бонус", "200х60 px"),
|
||||||
createData('Картинка для формата вопроса "Страница"', "860х1250 px"),
|
createData('Картинка для формата вопроса "Страница"', "860х1250 px"),
|
||||||
];
|
];
|
||||||
@ -100,12 +100,8 @@ export default function ModalSizeImage() {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ padding: "15px 20px 0px" }}>
|
<Box sx={{ padding: "15px 20px 0px" }}>
|
||||||
<Typography
|
<Typography variant={"body2"} sx={{ color: theme.palette.grey2.main, fontWeight: 400 }}>
|
||||||
variant={"body2"}
|
Рекомендованный размер зависит от того, как вы будете чаще использовать квиз:
|
||||||
sx={{ color: theme.palette.grey2.main, fontWeight: 400 }}
|
|
||||||
>
|
|
||||||
Рекомендованный размер зависит от того, как вы будете чаще
|
|
||||||
использовать квиз:
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ padding: "15px 40px 30px" }}>
|
<Box sx={{ padding: "15px 40px 30px" }}>
|
||||||
@ -115,24 +111,30 @@ export default function ModalSizeImage() {
|
|||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
gap: "6px",
|
||||||
|
position: "relative",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
paddingBottom: "5px",
|
paddingBottom: "5px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "block ruby", height: "20px" }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 18,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
borderBottom: "solid 1px #F2F3F7",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Box sx={{ display: "block ruby", position: "relative", zIndex: 1, background: "white" }}>
|
||||||
<Typography variant={"body2"} fontWeight={400}>
|
<Typography variant={"body2"} fontWeight={400}>
|
||||||
{name}
|
{name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box sx={{ display: "block ruby", position: "relative", zIndex: 1, background: "white" }}>
|
||||||
sx={{
|
<Typography sx={{ whiteSpace: "nowrap" }} variant={"body2"}>
|
||||||
borderBottom: "solid 1px #F2F3F7",
|
{size}
|
||||||
width: "100%",
|
</Typography>
|
||||||
margin: "0 7px",
|
|
||||||
}}
|
|
||||||
></Box>
|
|
||||||
<Box sx={{ display: "block ruby", height: "20px" }}>
|
|
||||||
<Typography variant={"body2"}>{size}</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
@ -153,24 +155,30 @@ export default function ModalSizeImage() {
|
|||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
position: "relative",
|
||||||
|
gap: "6px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
paddingBottom: "5px",
|
paddingBottom: "5px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "block ruby", height: "20px" }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 18,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
borderBottom: "solid 1px #F2F3F7",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Box sx={{ display: "block ruby", position: "relative", zIndex: 1, background: "white" }}>
|
||||||
<Typography variant={"body2"} fontWeight={400}>
|
<Typography variant={"body2"} fontWeight={400}>
|
||||||
{name}
|
{name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box sx={{ display: "block ruby", position: "relative", zIndex: 1, background: "white" }}>
|
||||||
sx={{
|
<Typography sx={{ whiteSpace: "nowrap" }} variant={"body2"}>
|
||||||
borderBottom: "solid 1px #F2F3F7",
|
{size}
|
||||||
width: "100%",
|
</Typography>
|
||||||
margin: "0 7px",
|
|
||||||
}}
|
|
||||||
></Box>
|
|
||||||
<Box sx={{ display: "block ruby", height: "20px" }}>
|
|
||||||
<Typography variant={"body2"}>{size}</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
|
@ -177,7 +177,7 @@ const REQUEST_DEBOUNCE = 200;
|
|||||||
const requestQueue = new RequestQueue();
|
const requestQueue = new RequestQueue();
|
||||||
let requestTimeoutId: ReturnType<typeof setTimeout>;
|
let requestTimeoutId: ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
export const updateQuestion = <T = AnyTypedQuizQuestion>(
|
export const updateQuestion = async <T = AnyTypedQuizQuestion>(
|
||||||
questionId: string,
|
questionId: string,
|
||||||
updateFn: (question: T) => void,
|
updateFn: (question: T) => void,
|
||||||
skipQueue = false,
|
skipQueue = false,
|
||||||
@ -481,20 +481,22 @@ export const getQuestionByContentId = (questionContentId: string | null) => {
|
|||||||
|
|
||||||
export const clearRuleForAll = () => {
|
export const clearRuleForAll = () => {
|
||||||
const { questions } = useQuestionsStore.getState();
|
const { questions } = useQuestionsStore.getState();
|
||||||
questions.forEach(question => {
|
return Promise.allSettled(
|
||||||
|
questions.map(question => {
|
||||||
if (question.type !== null &&
|
if (question.type !== null &&
|
||||||
(question.content.rule.main.length > 0
|
(question.content.rule.main.length > 0
|
||||||
|| question.content.rule.default.length > 0
|
|| question.content.rule.default.length > 0
|
||||||
|| question.content.rule.parentId.length > 0)
|
|| question.content.rule.parentId.length > 0)
|
||||||
&& question.type !== "result") {
|
&& question.type !== "result") {
|
||||||
|
console.log("вызываю очистку рул вопросов")
|
||||||
updateQuestion(question.content.id, question => {
|
updateQuestion(question.content.id, question => {
|
||||||
question.content.rule.parentId = "";
|
question.content.rule.parentId = "";
|
||||||
question.content.rule.main = [];
|
question.content.rule.main = [];
|
||||||
question.content.rule.default = "";
|
question.content.rule.default = "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createResult = async (
|
export const createResult = async (
|
||||||
@ -506,7 +508,7 @@ export const createResult = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Мы получили запрос на создание резулта. Анализируем существует ли такой. Если да - просто делаем его активным
|
//Мы получили запрос на создание резулта. Анализируем существует ли такой. Если да - просто делаем его активным
|
||||||
const question = useQuestionsStore.getState().questions.find(q=> q.type !== null && q?.content.rule.parentId === parentContentId)
|
const question = useQuestionsStore.getState().questions.find(q => q.type !== null && q?.content.rule.parentId === parentContentId)
|
||||||
|
|
||||||
console.log("Получил запрос на создание результа родителю ", parentContentId)
|
console.log("Получил запрос на создание результа родителю ", parentContentId)
|
||||||
console.log("Ищу такой же результ в списке ", question)
|
console.log("Ищу такой же результ в списке ", question)
|
||||||
@ -521,7 +523,7 @@ export const createResult = async (
|
|||||||
content.rule.parentId = parentContentId;
|
content.rule.parentId = parentContentId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const createdQuestion:RawQuestion = await questionApi.create({
|
const createdQuestion: RawQuestion = await questionApi.create({
|
||||||
quiz_id: quizId,
|
quiz_id: quizId,
|
||||||
type: "result",
|
type: "result",
|
||||||
title: "",
|
title: "",
|
||||||
|
@ -38,3 +38,7 @@ export const updateCanCreatePublic = (can: boolean) => useUiTools.setState({ can
|
|||||||
|
|
||||||
export const updateModalInfoWhyCantCreate = (can: boolean) => useUiTools.setState({ openModalInfoWhyCantCreate: can });
|
export const updateModalInfoWhyCantCreate = (can: boolean) => useUiTools.setState({ openModalInfoWhyCantCreate: can });
|
||||||
export const updateDeleteId = (deleteNodeId: string | null = null) => useUiTools.setState({ deleteNodeId });
|
export const updateDeleteId = (deleteNodeId: string | null = null) => useUiTools.setState({ deleteNodeId });
|
||||||
|
export const setShowConfirmLeaveModal = (showConfirmLeaveModal: boolean) => useUiTools.setState({ showConfirmLeaveModal });
|
||||||
|
|
||||||
|
|
||||||
|
export const updateSomeWorkBackend = (someWorkBackend: boolean) => useUiTools.setState({ someWorkBackend });
|
||||||
|
@ -10,8 +10,11 @@ export type UiTools = {
|
|||||||
canCreatePublic: boolean;
|
canCreatePublic: boolean;
|
||||||
whyCantCreatePublic: Record<string, WhyCantCreatePublic>//ид вопроса и список претензий к нему
|
whyCantCreatePublic: Record<string, WhyCantCreatePublic>//ид вопроса и список претензий к нему
|
||||||
openModalInfoWhyCantCreate: boolean;
|
openModalInfoWhyCantCreate: boolean;
|
||||||
deleteNodeId: string | null;
|
deleteNodeId: string | null;
|
||||||
|
showConfirmLeaveModal: boolean;
|
||||||
|
someWorkBackend: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WhyCantCreatePublic = {
|
export type WhyCantCreatePublic = {
|
||||||
name: string;
|
name: string;
|
||||||
problems: string[]
|
problems: string[]
|
||||||
@ -27,7 +30,9 @@ const initialState: UiTools = {
|
|||||||
canCreatePublic: false,
|
canCreatePublic: false,
|
||||||
whyCantCreatePublic: {},
|
whyCantCreatePublic: {},
|
||||||
openModalInfoWhyCantCreate: false,
|
openModalInfoWhyCantCreate: false,
|
||||||
deleteNodeId: null,
|
deleteNodeId: null,
|
||||||
|
showConfirmLeaveModal: false,
|
||||||
|
someWorkBackend: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUiTools = create<UiTools>()(
|
export const useUiTools = create<UiTools>()(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { FormControlLabel, Checkbox, useTheme, Box, useMediaQuery } from "@mui/material";
|
import { FormControlLabel, Checkbox, useTheme, Box, useMediaQuery } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { CheckboxIcon } from "@icons/Checkbox";
|
import CheckboxIcon from "@icons/Checkbox";
|
||||||
|
|
||||||
import type { SxProps } from "@mui/material";
|
import type { SxProps } from "@mui/material";
|
||||||
|
|
||||||
@ -11,9 +11,10 @@ interface Props {
|
|||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
dataCy?: string;
|
dataCy?: string;
|
||||||
|
colorIcon?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CustomCheckbox({ label, handleChange, checked, sx, dataCy }: Props) {
|
export default function CustomCheckbox({ label, handleChange, checked, sx, dataCy, colorIcon }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ export default function CustomCheckbox({ label, handleChange, checked, sx, dataC
|
|||||||
sx={{ padding: "0px 13px 1px 11px" }}
|
sx={{ padding: "0px 13px 1px 11px" }}
|
||||||
disableRipple
|
disableRipple
|
||||||
icon={<CheckboxIcon />}
|
icon={<CheckboxIcon />}
|
||||||
checkedIcon={<CheckboxIcon checked />}
|
checkedIcon={<CheckboxIcon checked color={colorIcon} />}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
data-cy={dataCy}
|
data-cy={dataCy}
|
||||||
@ -32,7 +33,7 @@ export default function CustomCheckbox({ label, handleChange, checked, sx, dataC
|
|||||||
}
|
}
|
||||||
label={label}
|
label={label}
|
||||||
sx={{
|
sx={{
|
||||||
color: theme.palette.grey2.main,
|
color: "#9A9AAF",
|
||||||
height: "26px",
|
height: "26px",
|
||||||
...sx,
|
...sx,
|
||||||
}}
|
}}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Slider, useTheme } from "@mui/material";
|
import {Slider, SxProps, Theme, useTheme} from "@mui/material";
|
||||||
|
|
||||||
type CustomSliderProps = {
|
type CustomSliderProps = {
|
||||||
defaultValue?: number;
|
defaultValue?: number;
|
||||||
@ -6,6 +6,7 @@ type CustomSliderProps = {
|
|||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
|
sx?: SxProps<Theme>
|
||||||
onChange?: (_: Event, value: number | number[]) => void;
|
onChange?: (_: Event, value: number | number[]) => void;
|
||||||
onChangeCommitted?: (_: React.SyntheticEvent | Event, value: number | number[]) => void;
|
onChangeCommitted?: (_: React.SyntheticEvent | Event, value: number | number[]) => void;
|
||||||
};
|
};
|
||||||
@ -18,6 +19,7 @@ export const CustomSlider = ({
|
|||||||
step,
|
step,
|
||||||
onChange,
|
onChange,
|
||||||
onChangeCommitted,
|
onChangeCommitted,
|
||||||
|
sx,
|
||||||
}: CustomSliderProps) => {
|
}: CustomSliderProps) => {
|
||||||
// const handleChange = ({ type }: Event, newValue: number | number[]) => {
|
// const handleChange = ({ type }: Event, newValue: number | number[]) => {
|
||||||
// // Для корректной работы слайдера в FireFox
|
// // Для корректной работы слайдера в FireFox
|
||||||
@ -40,11 +42,11 @@ export const CustomSlider = ({
|
|||||||
onMouseDown={(e) => e.stopPropagation()}
|
onMouseDown={(e) => e.stopPropagation()}
|
||||||
data-cy="slider"
|
data-cy="slider"
|
||||||
sx={{
|
sx={{
|
||||||
color: theme.palette.brightPurple.main,
|
color: "#7E2AEA",
|
||||||
padding: "0",
|
padding: "0",
|
||||||
marginTop: "75px",
|
marginTop: "75px",
|
||||||
"& .MuiSlider-valueLabel": {
|
"& .MuiSlider-valueLabel": {
|
||||||
background: theme.palette.brightPurple.main,
|
background: "#7E2AEA",
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
minWidth: "60px",
|
minWidth: "60px",
|
||||||
width: "auto",
|
width: "auto",
|
||||||
@ -71,6 +73,7 @@ export const CustomSlider = ({
|
|||||||
"& .MuiSlider-track": {
|
"& .MuiSlider-track": {
|
||||||
height: "12px",
|
height: "12px",
|
||||||
},
|
},
|
||||||
|
...sx
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Box, FormControl, TextField, Typography, useTheme } from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
FormControl,
|
||||||
|
TextField,
|
||||||
|
Typography,
|
||||||
|
useTheme,
|
||||||
|
} from "@mui/material";
|
||||||
import type { ChangeEvent, KeyboardEvent, FocusEvent } from "react";
|
import type { ChangeEvent, KeyboardEvent, FocusEvent } from "react";
|
||||||
import type { InputProps, SxProps, Theme } from "@mui/material";
|
import type { InputProps, SxProps, Theme } from "@mui/material";
|
||||||
|
|
||||||
@ -19,7 +25,7 @@ interface CustomTextFieldProps {
|
|||||||
|
|
||||||
export default function CustomTextField({
|
export default function CustomTextField({
|
||||||
placeholder,
|
placeholder,
|
||||||
value,
|
value = "",
|
||||||
onChange,
|
onChange,
|
||||||
onKeyDown,
|
onKeyDown,
|
||||||
onBlur,
|
onBlur,
|
||||||
@ -32,9 +38,13 @@ export default function CustomTextField({
|
|||||||
}: CustomTextFieldProps) {
|
}: CustomTextFieldProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const [inputValue, setInputValue] = useState(value || text || "");
|
const [inputValue, setInputValue] = useState("");
|
||||||
const [isInputActive, setIsInputActive] = useState(false);
|
const [isInputActive, setIsInputActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInputValue(value);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const inputValue = event.target.value;
|
const inputValue = event.target.value;
|
||||||
setInputValue(inputValue);
|
setInputValue(inputValue);
|
||||||
@ -88,8 +98,8 @@ export default function CustomTextField({
|
|||||||
fontSize: "18px",
|
fontSize: "18px",
|
||||||
lineHeight: "21px",
|
lineHeight: "21px",
|
||||||
py: 0,
|
py: 0,
|
||||||
},
|
|
||||||
...sx,
|
...sx,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
data-cy="textfield"
|
data-cy="textfield"
|
||||||
/>
|
/>
|
||||||
|
149
src/ui_kit/MediaSelectionAndDisplay.tsx
Normal file
149
src/ui_kit/MediaSelectionAndDisplay.tsx
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { Box, Button } from "@mui/material";
|
||||||
|
import CustomTextField from "./CustomTextField";
|
||||||
|
import { updateQuestion, uploadQuestionImage } from "@root/questions/actions";
|
||||||
|
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||||
|
|
||||||
|
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
||||||
|
import { UploadImageModal } from "../pages/Questions/UploadImage/UploadImageModal";
|
||||||
|
import { useDisclosure } from "../utils/useDisclosure";
|
||||||
|
import { useCurrentQuiz } from "../stores/quizes/hooks";
|
||||||
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
|
|
||||||
|
interface Iprops {
|
||||||
|
resultData: AnyTypedQuizQuestion;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData }) => {
|
||||||
|
const quizQid = useCurrentQuiz()?.qid;
|
||||||
|
const { isCropModalOpen, openCropModal, closeCropModal, imageBlob, originalImageUrl, setCropModalImageBlob } =
|
||||||
|
useCropModalState();
|
||||||
|
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||||||
|
|
||||||
|
async function handleImageUpload(file: File) {
|
||||||
|
const url = await uploadQuestionImage(resultData.id, quizQid, file, (question, url) => {
|
||||||
|
question.content.back = url;
|
||||||
|
question.content.originalBack = url;
|
||||||
|
});
|
||||||
|
closeImageUploadModal();
|
||||||
|
openCropModal(file, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCropModalSaveClick(imageBlob: Blob) {
|
||||||
|
uploadQuestionImage(resultData.id, quizQid, imageBlob, (question, url) => {
|
||||||
|
question.content.back = url;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
mt: "20px",
|
||||||
|
display: "flex",
|
||||||
|
gap: "10px",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
sx={{
|
||||||
|
color: resultData.content.useImage ? "#7E2AEA" : "#9A9AAF",
|
||||||
|
fontSize: "16px",
|
||||||
|
"&:hover": {
|
||||||
|
background: "none",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
variant="text"
|
||||||
|
onClick={() => updateQuestion(resultData.id, (question) => (question.content.useImage = true))}
|
||||||
|
>
|
||||||
|
Изображение
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
sx={{
|
||||||
|
color: resultData.content.useImage ? "#9A9AAF" : "#7E2AEA",
|
||||||
|
fontSize: "16px",
|
||||||
|
"&:hover": {
|
||||||
|
background: "none",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
variant="text"
|
||||||
|
onClick={() => updateQuestion(resultData.id, (question) => (question.content.useImage = false))}
|
||||||
|
>
|
||||||
|
Видео
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UploadImageModal
|
||||||
|
isOpen={isImageUploadOpen}
|
||||||
|
onClose={closeImageUploadModal}
|
||||||
|
handleImageChange={handleImageUpload}
|
||||||
|
/>
|
||||||
|
<CropModal
|
||||||
|
isOpen={isCropModalOpen}
|
||||||
|
imageBlob={imageBlob}
|
||||||
|
originalImageUrl={originalImageUrl}
|
||||||
|
setCropModalImageBlob={setCropModalImageBlob}
|
||||||
|
onClose={closeCropModal}
|
||||||
|
onSaveImageClick={handleCropModalSaveClick}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{resultData.content.useImage && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "20px",
|
||||||
|
mb: "30px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AddOrEditImageButton
|
||||||
|
imageSrc={resultData.content.back}
|
||||||
|
onImageClick={() => {
|
||||||
|
if (resultData.content.back) {
|
||||||
|
return openCropModal(resultData.content.back, resultData.content.originalBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
openImageUploadModal();
|
||||||
|
}}
|
||||||
|
onPlusClick={() => {
|
||||||
|
openImageUploadModal();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{!resultData.content.useImage && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "20px",
|
||||||
|
mb: "30px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CustomTextField
|
||||||
|
placeholder="URL видео"
|
||||||
|
text={resultData.content.video ?? ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateQuestion(resultData.id, (q) => {
|
||||||
|
q.content.video = e.target.value;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -1,4 +1,3 @@
|
|||||||
import VisibilityIcon from '@mui/icons-material/Visibility';
|
|
||||||
import { Box, IconButton } from "@mui/material";
|
import { Box, IconButton } from "@mui/material";
|
||||||
import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview";
|
import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview";
|
||||||
import { useLayoutEffect, useRef } from "react";
|
import { useLayoutEffect, useRef } from "react";
|
||||||
@ -34,13 +33,7 @@ export default function QuizPreview() {
|
|||||||
function stickPreviewToBottomRight() {
|
function stickPreviewToBottomRight() {
|
||||||
const rnd = rndRef.current;
|
const rnd = rndRef.current;
|
||||||
const rndSelfElement = rnd?.getSelfElement();
|
const rndSelfElement = rnd?.getSelfElement();
|
||||||
if (
|
if (!rnd || !rndSelfElement || !rndParentRef.current || !isFirstShowRef.current) return;
|
||||||
!rnd ||
|
|
||||||
!rndSelfElement ||
|
|
||||||
!rndParentRef.current ||
|
|
||||||
!isFirstShowRef.current
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const rndParentRect = rndParentRef.current.getBoundingClientRect();
|
const rndParentRect = rndParentRef.current.getBoundingClientRect();
|
||||||
const rndRect = rndSelfElement.getBoundingClientRect();
|
const rndRect = rndSelfElement.getBoundingClientRect();
|
||||||
@ -118,18 +111,6 @@ export default function QuizPreview() {
|
|||||||
<QuizPreviewLayout />
|
<QuizPreviewLayout />
|
||||||
</Rnd>
|
</Rnd>
|
||||||
)}
|
)}
|
||||||
<IconButton
|
|
||||||
onClick={toggleQuizPreview}
|
|
||||||
data-cy="toggle-quiz-preview"
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
pointerEvents: "auto",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<VisibilityIcon sx={{ height: "30px", width: "30px" }} />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
import { Box, Button, LinearProgress, Paper, Typography, FormControl, Select as MuiSelect, MenuItem, useTheme } from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
LinearProgress,
|
||||||
|
Paper,
|
||||||
|
Typography,
|
||||||
|
FormControl,
|
||||||
|
Select as MuiSelect,
|
||||||
|
MenuItem,
|
||||||
|
useTheme,
|
||||||
|
} from "@mui/material";
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import {
|
import {
|
||||||
decrementCurrentQuestionIndex,
|
decrementCurrentQuestionIndex,
|
||||||
incrementCurrentQuestionIndex,
|
incrementCurrentQuestionIndex,
|
||||||
useQuizPreviewStore,
|
useQuizPreviewStore,
|
||||||
setCurrentQuestionIndex
|
setCurrentQuestionIndex,
|
||||||
} from "@root/quizPreview";
|
} from "@root/quizPreview";
|
||||||
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "model/questionTypes/shared";
|
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "model/questionTypes/shared";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
@ -25,19 +35,12 @@ import ArrowDownIcon from "@icons/ArrowDownIcon";
|
|||||||
|
|
||||||
export default function QuizPreviewLayout() {
|
export default function QuizPreviewLayout() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const questions = useQuestionsStore(state => state.questions);
|
const questions = useQuestionsStore((state) => state.questions);
|
||||||
const currentQuizStep = useQuizPreviewStore(
|
const currentQuizStep = useQuizPreviewStore((state) => state.currentQuestionIndex);
|
||||||
(state) => state.currentQuestionIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
const nonDeletedQuizQuestions = questions.filter(
|
const nonDeletedQuizQuestions = questions.filter((question) => !question.deleted);
|
||||||
(question) => !question.deleted
|
const maxCurrentQuizStep = nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
|
||||||
);
|
const currentProgress = Math.floor((currentQuizStep / maxCurrentQuizStep) * 100);
|
||||||
const maxCurrentQuizStep =
|
|
||||||
nonDeletedQuizQuestions.length > 0 ? nonDeletedQuizQuestions.length - 1 : 0;
|
|
||||||
const currentProgress = Math.floor(
|
|
||||||
(currentQuizStep / maxCurrentQuizStep) * 100
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
|
const currentQuestion = nonDeletedQuizQuestions[currentQuizStep];
|
||||||
|
|
||||||
@ -84,19 +87,13 @@ export default function QuizPreviewLayout() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ marginBottom: "10px" }}>
|
<Box sx={{ marginBottom: "10px" }}>
|
||||||
<FormControl
|
<FormControl fullWidth size="small" sx={{ width: "100%", minWidth: "200px", height: "48px" }}>
|
||||||
fullWidth
|
|
||||||
size="small"
|
|
||||||
sx={{ width: "100%", minWidth: "200px", height: "48px" }}
|
|
||||||
>
|
|
||||||
<MuiSelect
|
<MuiSelect
|
||||||
id="category-select"
|
id="category-select"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={currentQuizStep}
|
value={currentQuizStep}
|
||||||
placeholder="Заголовок вопроса"
|
placeholder="Заголовок вопроса"
|
||||||
onChange={({ target }) =>
|
onChange={({ target }) => setCurrentQuestionIndex(window.Number(target.value))}
|
||||||
setCurrentQuestionIndex(window.Number(target.value))
|
|
||||||
}
|
|
||||||
sx={{
|
sx={{
|
||||||
height: "48px",
|
height: "48px",
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
@ -138,8 +135,7 @@ export default function QuizPreviewLayout() {
|
|||||||
}}
|
}}
|
||||||
IconComponent={(props) => <ArrowDownIcon {...props} />}
|
IconComponent={(props) => <ArrowDownIcon {...props} />}
|
||||||
>
|
>
|
||||||
{Object.values(questions).map(
|
{Object.values(questions).map(({ id, title }, index) => (
|
||||||
({ id, title }, index) => (
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={id}
|
key={id}
|
||||||
value={index}
|
value={index}
|
||||||
@ -154,8 +150,7 @@ export default function QuizPreviewLayout() {
|
|||||||
>
|
>
|
||||||
{`${index + 1}. ${title}`}
|
{`${index + 1}. ${title}`}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)
|
))}
|
||||||
)}
|
|
||||||
</MuiSelect>
|
</MuiSelect>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
@ -170,8 +165,7 @@ export default function QuizPreviewLayout() {
|
|||||||
>
|
>
|
||||||
<Typography>
|
<Typography>
|
||||||
{nonDeletedQuizQuestions.length > 0
|
{nonDeletedQuizQuestions.length > 0
|
||||||
? `Вопрос ${currentQuizStep + 1} из ${nonDeletedQuizQuestions.length
|
? `Вопрос ${currentQuizStep + 1} из ${nonDeletedQuizQuestions.length}`
|
||||||
}`
|
|
||||||
: "Нет вопросов"}
|
: "Нет вопросов"}
|
||||||
</Typography>
|
</Typography>
|
||||||
{nonDeletedQuizQuestions.length > 0 && (
|
{nonDeletedQuizQuestions.length > 0 && (
|
||||||
@ -218,23 +212,33 @@ export default function QuizPreviewLayout() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function QuestionPreviewComponent({ question }: {
|
function QuestionPreviewComponent({ question }: { question: AnyTypedQuizQuestion | UntypedQuizQuestion | undefined }) {
|
||||||
question: AnyTypedQuizQuestion | UntypedQuizQuestion | undefined;
|
|
||||||
}) {
|
|
||||||
if (!question || question.type === null) return null;
|
if (!question || question.type === null) return null;
|
||||||
|
|
||||||
switch (question.type) {
|
switch (question.type) {
|
||||||
case "variant": return <Variant question={question} />;
|
case "variant":
|
||||||
case "images": return <Images question={question} />;
|
return <Variant question={question} />;
|
||||||
case "varimg": return <Varimg question={question} />;
|
case "images":
|
||||||
case "emoji": return <Emoji question={question} />;
|
return <Images question={question} />;
|
||||||
case "text": return <Text question={question} />;
|
case "varimg":
|
||||||
case "select": return <Select question={question} />;
|
return <Varimg question={question} />;
|
||||||
case "date": return <Date question={question} />;
|
case "emoji":
|
||||||
case "number": return <Number question={question} />;
|
return <Emoji question={question} />;
|
||||||
case "file": return <File question={question} />;
|
case "text":
|
||||||
case "page": return <Page question={question} />;
|
return <Text question={question} />;
|
||||||
case "rating": return <Rating question={question} />;
|
case "select":
|
||||||
default: notReachable(question);
|
return <Select question={question} />;
|
||||||
|
case "date":
|
||||||
|
return <Date question={question} />;
|
||||||
|
case "number":
|
||||||
|
return <Number question={question} />;
|
||||||
|
case "file":
|
||||||
|
return <File question={question} />;
|
||||||
|
case "page":
|
||||||
|
return <Page question={question} />;
|
||||||
|
case "rating":
|
||||||
|
return <Rating question={question} />;
|
||||||
|
default:
|
||||||
|
notReachable(question);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Box, Typography } from "@mui/material";
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import YoutubeEmbedIframe from "@ui_kit/StartPagePreview/YoutubeEmbedIframe";
|
||||||
|
|
||||||
import type { QuizQuestionPage } from "model/questionTypes/page";
|
import type { QuizQuestionPage } from "model/questionTypes/page";
|
||||||
|
|
||||||
@ -7,6 +8,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Page({ question }: Props) {
|
export default function Page({ question }: Props) {
|
||||||
|
console.log(question);
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -16,13 +18,17 @@ export default function Page({ question }: Props) {
|
|||||||
gap: 1,
|
gap: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h6" data-cy="question-title" sx={{ paddingBottom: "25px" }}>{question.title}</Typography>
|
<Typography variant="h6" data-cy="question-title" sx={{ paddingBottom: "25px" }}>
|
||||||
<Typography data-cy="question-text" sx={{ paddingBottom: "20px" }}>{question.content.text}</Typography>
|
{question.title}
|
||||||
{question.content.picture && (
|
</Typography>
|
||||||
<Box sx={{borderRadius: "12px",
|
|
||||||
border: "1px solid #9A9AAF", width: "100%", overflow: "hidden"}}>
|
<Typography data-cy="question-text" sx={{ paddingBottom: "20px" }}>
|
||||||
|
{question.content.text}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{question.content.useImage ? (
|
||||||
<img
|
<img
|
||||||
src={question.content.picture}
|
src={question.content.back}
|
||||||
alt=""
|
alt=""
|
||||||
style={{
|
style={{
|
||||||
display: "block",
|
display: "block",
|
||||||
@ -31,7 +37,8 @@ export default function Page({ question }: Props) {
|
|||||||
objectFit: "contain",
|
objectFit: "contain",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
) : (
|
||||||
|
<YoutubeEmbedIframe containerSX={{ width: "100%", height: "50vh" }} videoUrl={question.content.video} />
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { Box, useTheme } from "@mui/material";
|
import { Box, useTheme } from "@mui/material";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
color?: string
|
||||||
|
}
|
||||||
|
|
||||||
export default function RadioCheck() {
|
export default function RadioCheck( {color = "#7E2AEA"} : Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -16,7 +19,7 @@ export default function RadioCheck() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect x="0.5" y="0.5" width="25" height="25" rx="12.5" fill="#7E2AEA" stroke="#7E2AEA"/>
|
<rect x="0.5" y="0.5" width="25" height="25" rx="12.5" fill={color} stroke={color}/>
|
||||||
<rect x="8" y="8" width="10" height="10" rx="5" fill="white"/>
|
<rect x="8" y="8" width="10" height="10" rx="5" fill="white"/>
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -4,19 +4,13 @@ import PencilCircleIcon from "@icons/PencilCircleIcon";
|
|||||||
import PuzzlePieceIcon from "@icons/PuzzlePieceIcon";
|
import PuzzlePieceIcon from "@icons/PuzzlePieceIcon";
|
||||||
import TagIcon from "@icons/TagIcon";
|
import TagIcon from "@icons/TagIcon";
|
||||||
import { quizSetupSteps } from "@model/quizSettings";
|
import { quizSetupSteps } from "@model/quizSettings";
|
||||||
import {
|
import { Box, IconButton, List, Typography, useTheme } from "@mui/material";
|
||||||
Box,
|
|
||||||
IconButton,
|
|
||||||
List,
|
|
||||||
Typography,
|
|
||||||
useTheme
|
|
||||||
} from "@mui/material";
|
|
||||||
import { setCurrentStep } from "@root/quizes/actions";
|
import { setCurrentStep } from "@root/quizes/actions";
|
||||||
import { useQuizStore } from "@root/quizes/store";
|
import { useQuizStore } from "@root/quizes/store";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import MenuItem from "./MenuItem";
|
import MenuItem from "./MenuItem";
|
||||||
import {useCurrentQuiz} from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
|
import { setShowConfirmLeaveModal } from "@root/uiTools/actions";
|
||||||
|
|
||||||
const quizSettingsMenuItems = [
|
const quizSettingsMenuItems = [
|
||||||
[TagIcon, "Дополнения"],
|
[TagIcon, "Дополнения"],
|
||||||
@ -25,15 +19,29 @@ const quizSettingsMenuItems = [
|
|||||||
[GearIcon, "Настройки"],
|
[GearIcon, "Настройки"],
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export default function Sidebar() {
|
type SidebarProps = {
|
||||||
|
setNextStep: (step: number) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Sidebar({ setNextStep }: SidebarProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
|
const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
|
||||||
const currentStep = useQuizStore(state => state.currentStep);
|
const currentStep = useQuizStore((state) => state.currentStep);
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
|
|
||||||
|
|
||||||
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
|
const handleMenuCollapseToggle = () => setIsMenuCollapsed((prev) => !prev);
|
||||||
|
|
||||||
|
const changePage = (index: number) => {
|
||||||
|
if (currentStep === 2) {
|
||||||
|
setNextStep(index);
|
||||||
|
setShowConfirmLeaveModal(true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentStep(index);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -49,7 +57,7 @@ export default function Sidebar() {
|
|||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
zIndex: 1
|
zIndex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@ -92,12 +100,18 @@ export default function Sidebar() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => setCurrentStep(index)}
|
onClick={() => changePage(index)}
|
||||||
key={index}
|
key={index}
|
||||||
text={menuItem.sidebarText}
|
text={menuItem.sidebarText}
|
||||||
isCollapsed={isMenuCollapsed}
|
isCollapsed={isMenuCollapsed}
|
||||||
isActive={currentStep === index}
|
isActive={currentStep === index}
|
||||||
disabled={index===0 ? false : quiz===undefined ? true : (quiz?.config.type === null)}
|
disabled={
|
||||||
|
index === 0
|
||||||
|
? false
|
||||||
|
: quiz === undefined
|
||||||
|
? true
|
||||||
|
: quiz?.config.type === null
|
||||||
|
}
|
||||||
icon={
|
icon={
|
||||||
<Icon
|
<Icon
|
||||||
color={
|
color={
|
||||||
|
@ -1,20 +1,10 @@
|
|||||||
import {
|
import { Box, Button, ButtonBase, Link, Paper, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
ButtonBase,
|
|
||||||
Link,
|
|
||||||
Paper,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||||
import YoutubeEmbedIframe from "./YoutubeEmbedIframe";
|
import YoutubeEmbedIframe from "./YoutubeEmbedIframe";
|
||||||
import { QuizStartpageAlignType, QuizStartpageType } from "@model/quizSettings";
|
import { QuizStartpageAlignType, QuizStartpageType } from "@model/quizSettings";
|
||||||
import { notReachable } from "../../utils/notReachable";
|
import { notReachable } from "../../utils/notReachable";
|
||||||
import { useUADevice } from "../../utils/hooks/useUADevice";
|
import { useUADevice } from "../../utils/hooks/useUADevice";
|
||||||
|
|
||||||
|
|
||||||
export default function QuizPreviewLayout() {
|
export default function QuizPreviewLayout() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
@ -26,9 +16,9 @@ export default function QuizPreviewLayout() {
|
|||||||
navigator.clipboard.writeText(quiz.config.info.phonenumber);
|
navigator.clipboard.writeText(quiz.config.info.phonenumber);
|
||||||
};
|
};
|
||||||
|
|
||||||
const background = quiz.config.startpage.background.type === "image"
|
const background =
|
||||||
? quiz.config.startpage.background.desktop
|
quiz.config.startpage.background.type === "image" ? (
|
||||||
? (
|
quiz.config.startpage.background.desktop ? (
|
||||||
<img
|
<img
|
||||||
src={quiz.config.startpage.background.desktop}
|
src={quiz.config.startpage.background.desktop}
|
||||||
alt=""
|
alt=""
|
||||||
@ -38,25 +28,25 @@ export default function QuizPreviewLayout() {
|
|||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
) : null
|
||||||
: null
|
) : quiz.config.startpage.background.type === "video" ? (
|
||||||
: quiz.config.startpage.background.type === "video"
|
quiz.config.startpage.background.video ? (
|
||||||
? quiz.config.startpage.background.video
|
|
||||||
? (
|
|
||||||
<YoutubeEmbedIframe videoUrl={quiz.config.startpage.background.video} />
|
<YoutubeEmbedIframe videoUrl={quiz.config.startpage.background.video} />
|
||||||
)
|
) : null
|
||||||
: null
|
) : null;
|
||||||
: null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper className="quiz-preview-draghandle" sx={{ height: "100%" }}>
|
<Paper className="quiz-preview-draghandle" sx={{ height: "100%" }}>
|
||||||
<QuizPreviewLayoutByType
|
<QuizPreviewLayoutByType
|
||||||
quizHeaderBlock={<>
|
quizHeaderBlock={
|
||||||
<Box sx={{
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "20px",
|
gap: "20px",
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{quiz.config.startpage.logo && (
|
{quiz.config.startpage.logo && (
|
||||||
<img
|
<img
|
||||||
src={quiz.config.startpage.logo}
|
src={quiz.config.startpage.logo}
|
||||||
@ -68,25 +58,26 @@ export default function QuizPreviewLayout() {
|
|||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Typography sx={{ fontSize: "12px" }}>
|
<Typography sx={{ fontSize: "12px" }}>{quiz.config.info.orgname}</Typography>
|
||||||
{quiz.config.info.orgname}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
</>}
|
</>
|
||||||
quizMainBlock={<>
|
}
|
||||||
<Box sx={{
|
quizMainBlock={
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "10px",
|
gap: "10px",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: (quiz.config.startpageType === "expanded" && quiz.config.startpage.position === "center")
|
alignItems:
|
||||||
|
quiz.config.startpageType === "expanded" && quiz.config.startpage.position === "center"
|
||||||
? "center"
|
? "center"
|
||||||
: "start",
|
: "start",
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<Typography sx={{ fontWeight: "bold" }}>{quiz.name}</Typography>
|
<Typography sx={{ fontWeight: "bold" }}>{quiz.name}</Typography>
|
||||||
<Typography sx={{ fontSize: "12px" }}>
|
<Typography sx={{ fontSize: "12px" }}>{quiz.config.startpage.description}</Typography>
|
||||||
{quiz.config.startpage.description}
|
|
||||||
</Typography>
|
|
||||||
<Box>
|
<Box>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@ -119,11 +110,10 @@ export default function QuizPreviewLayout() {
|
|||||||
{quiz.config.info.phonenumber}
|
{quiz.config.info.phonenumber}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<Typography sx={{ fontSize: "12px" }}>
|
<Typography sx={{ fontSize: "12px" }}>{quiz.config.info.law}</Typography>
|
||||||
{quiz.config.info.law}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
</>}
|
</>
|
||||||
|
}
|
||||||
backgroundBlock={background}
|
backgroundBlock={background}
|
||||||
startpageType={quiz.config.startpageType}
|
startpageType={quiz.config.startpageType}
|
||||||
alignType={quiz.config.startpage.position}
|
alignType={quiz.config.startpage.position}
|
||||||
@ -132,7 +122,13 @@ export default function QuizPreviewLayout() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function QuizPreviewLayoutByType({ quizHeaderBlock, quizMainBlock, backgroundBlock, startpageType, alignType }: {
|
function QuizPreviewLayoutByType({
|
||||||
|
quizHeaderBlock,
|
||||||
|
quizMainBlock,
|
||||||
|
backgroundBlock,
|
||||||
|
startpageType,
|
||||||
|
alignType,
|
||||||
|
}: {
|
||||||
quizHeaderBlock: JSX.Element;
|
quizHeaderBlock: JSX.Element;
|
||||||
quizMainBlock: JSX.Element;
|
quizMainBlock: JSX.Element;
|
||||||
backgroundBlock: JSX.Element | null;
|
backgroundBlock: JSX.Element | null;
|
||||||
@ -146,27 +142,33 @@ function QuizPreviewLayoutByType({ quizHeaderBlock, quizMainBlock, backgroundBlo
|
|||||||
case null:
|
case null:
|
||||||
case "standard": {
|
case "standard": {
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: alignType === "left" ? "row" : "row-reverse",
|
flexDirection: alignType === "left" ? "row" : "row-reverse",
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
}}>
|
}}
|
||||||
<Box sx={{
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
width: !isTablet ? "40%" : "100%",
|
width: !isTablet ? "40%" : "100%",
|
||||||
padding: "16px",
|
padding: "16px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: !isTablet ? "flex-start" : "center",
|
alignItems: !isTablet ? "flex-start" : "center",
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{quizHeaderBlock}
|
{quizHeaderBlock}
|
||||||
{quizMainBlock}
|
{quizMainBlock}
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{
|
<Box
|
||||||
|
sx={{
|
||||||
width: "60%",
|
width: "60%",
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{backgroundBlock}
|
{backgroundBlock}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -174,15 +176,18 @@ function QuizPreviewLayoutByType({ quizHeaderBlock, quizMainBlock, backgroundBlo
|
|||||||
}
|
}
|
||||||
case "expanded": {
|
case "expanded": {
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
|
sx={{
|
||||||
position: "relative",
|
position: "relative",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: startpageAlignTypeToJustifyContent[alignType],
|
justifyContent: startpageAlignTypeToJustifyContent[alignType],
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
}}>
|
}}
|
||||||
<Box sx={{
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
width: "40%",
|
width: "40%",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
padding: "16px",
|
padding: "16px",
|
||||||
@ -191,18 +196,21 @@ function QuizPreviewLayoutByType({ quizHeaderBlock, quizMainBlock, backgroundBlo
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: alignType === "center" ? "center" : "start",
|
alignItems: alignType === "center" ? "center" : "start",
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{quizHeaderBlock}
|
{quizHeaderBlock}
|
||||||
{quizMainBlock}
|
{quizMainBlock}
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{
|
<Box
|
||||||
|
sx={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{backgroundBlock}
|
{backgroundBlock}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -210,7 +218,8 @@ function QuizPreviewLayoutByType({ quizHeaderBlock, quizMainBlock, backgroundBlo
|
|||||||
}
|
}
|
||||||
case "centered": {
|
case "centered": {
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
|
sx={{
|
||||||
padding: "16px",
|
padding: "16px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
@ -218,18 +227,16 @@ function QuizPreviewLayoutByType({ quizHeaderBlock, quizMainBlock, backgroundBlo
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
"&::-webkit-scrollbar": { width: 0 },
|
"&::-webkit-scrollbar": { width: 0 },
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{quizHeaderBlock}
|
{quizHeaderBlock}
|
||||||
{backgroundBlock &&
|
{backgroundBlock && <Box>{backgroundBlock}</Box>}
|
||||||
<Box>
|
|
||||||
{backgroundBlock}
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
{quizMainBlock}
|
{quizMainBlock}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default: notReachable(startpageType);
|
default:
|
||||||
|
notReachable(startpageType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import VisibilityIcon from "@mui/icons-material/Visibility";
|
|
||||||
import { Box, IconButton, useTheme, useMediaQuery } from "@mui/material";
|
import { Box, IconButton, useTheme, useMediaQuery } from "@mui/material";
|
||||||
import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview";
|
import { toggleQuizPreview, useQuizPreviewStore } from "@root/quizPreview";
|
||||||
import { useLayoutEffect, useRef } from "react";
|
import { useLayoutEffect, useRef } from "react";
|
||||||
@ -35,13 +34,7 @@ export const StartPagePreview = () => {
|
|||||||
function stickPreviewToBottomRight() {
|
function stickPreviewToBottomRight() {
|
||||||
const rnd = rndRef.current;
|
const rnd = rndRef.current;
|
||||||
const rndSelfElement = rnd?.getSelfElement();
|
const rndSelfElement = rnd?.getSelfElement();
|
||||||
if (
|
if (!rnd || !rndSelfElement || !rndParentRef.current || !isFirstShowRef.current) return;
|
||||||
!rnd ||
|
|
||||||
!rndSelfElement ||
|
|
||||||
!rndParentRef.current ||
|
|
||||||
!isFirstShowRef.current
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const rndParentRect = rndParentRef.current.getBoundingClientRect();
|
const rndParentRect = rndParentRef.current.getBoundingClientRect();
|
||||||
const rndRect = rndSelfElement.getBoundingClientRect();
|
const rndRect = rndSelfElement.getBoundingClientRect();
|
||||||
@ -119,17 +112,6 @@ export const StartPagePreview = () => {
|
|||||||
<QuizPreviewLayout />
|
<QuizPreviewLayout />
|
||||||
</Rnd>
|
</Rnd>
|
||||||
)}
|
)}
|
||||||
<IconButton
|
|
||||||
onClick={toggleQuizPreview}
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
pointerEvents: "auto",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<VisibilityIcon sx={{ height: "30px", width: "30px" }} />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,8 @@ interface Props {
|
|||||||
quizType: QuizType;
|
quizType: QuizType;
|
||||||
quizStartPageType: QuizStartpageType;
|
quizStartPageType: QuizStartpageType;
|
||||||
quizResults: QuizResultsType;
|
quizResults: QuizResultsType;
|
||||||
|
openBranchingPage: boolean;
|
||||||
|
setOpenBranchingPage: (a:boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SwitchStepPages({
|
export default function SwitchStepPages({
|
||||||
@ -23,6 +25,8 @@ export default function SwitchStepPages({
|
|||||||
quizType,
|
quizType,
|
||||||
quizStartPageType,
|
quizStartPageType,
|
||||||
quizResults,
|
quizResults,
|
||||||
|
openBranchingPage,
|
||||||
|
setOpenBranchingPage
|
||||||
}: Props) {
|
}: Props) {
|
||||||
switch (activeStep) {
|
switch (activeStep) {
|
||||||
case 0: {
|
case 0: {
|
||||||
@ -30,7 +34,10 @@ export default function SwitchStepPages({
|
|||||||
if (!quizStartPageType) return <Steptwo />;
|
if (!quizStartPageType) return <Steptwo />;
|
||||||
return <StartPageSettings />;
|
return <StartPageSettings />;
|
||||||
}
|
}
|
||||||
case 1: return quizType === "form" ? <FormQuestionsPage /> : <QuestionsPage />;
|
case 1: return quizType === "form" ? <FormQuestionsPage /> : <QuestionsPage
|
||||||
|
openBranchingPage={openBranchingPage}
|
||||||
|
setOpenBranchingPage={setOpenBranchingPage}
|
||||||
|
/>;
|
||||||
case 2: return <ResultPage />;
|
case 2: return <ResultPage />;
|
||||||
case 3: return <ContactFormPage />;
|
case 3: return <ContactFormPage />;
|
||||||
case 4: return <InstallQuiz />;
|
case 4: return <InstallQuiz />;
|
||||||
|
82
src/utils/checkQuestionHint.ts
Normal file
82
src/utils/checkQuestionHint.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { AnyTypedQuizQuestion, QuestionBranchingRuleMain } from "@model/questionTypes/shared";
|
||||||
|
import { WhyCantCreatePublic } from "@root/uiTools/store";
|
||||||
|
import { getQuestionByContentId, updateQuestion } from "@root/questions/actions";
|
||||||
|
|
||||||
|
export const checkQuestionHint = (questions: AnyTypedQuizQuestion): Record<string, WhyCantCreatePublic> => {
|
||||||
|
|
||||||
|
const problems: any = {}
|
||||||
|
|
||||||
|
const pushProblem = (id: string, problem: string, title: string) => {
|
||||||
|
//Если первый вопрос с проблемой - создаём запись. Если не первый - добавляем проблему
|
||||||
|
if (id in problems) {
|
||||||
|
problems[id].problems.push(problem)
|
||||||
|
} else {
|
||||||
|
problems[id] = {
|
||||||
|
name: title,
|
||||||
|
problems: [problem]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
questions.forEach((question: AnyTypedQuizQuestion) => {
|
||||||
|
//Если не участвует в ветвлении, или безтиповый, или резулт - он нам не интересен
|
||||||
|
if (question.type === null
|
||||||
|
|| question.type === "result"
|
||||||
|
|| question.content.rule.parentId.length === 0) return
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
question?.type === "date" ||
|
||||||
|
question?.type === "text" ||
|
||||||
|
question?.type === "number" ||
|
||||||
|
question?.type === "page"
|
||||||
|
) {//Если у вопроса типа страница, ползунок, своё поле для ввода и дата есть ребёнок, но нет дефолта - молча его добавляем.
|
||||||
|
|
||||||
|
if (question.content.rule.children.length === 1 && question.content.rule.default.length === 0)
|
||||||
|
updateQuestion(question.id, (q) => {
|
||||||
|
question.content.rule.default = question.content.rule.children[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//если есть дети, но нет дефолта - логическая ошибка. Так нельзя
|
||||||
|
if (question.content.rule.children.length > 0 && question.content.rule.default.length === 0) {
|
||||||
|
pushProblem(question.content.id, "Не выбран дефолтный вопрос", question.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Rules вопроса не должны совпадать
|
||||||
|
const buffer: QuestionBranchingRuleMain[] = []
|
||||||
|
question.content.rule.main.forEach((condition: QuestionBranchingRuleMain) => {
|
||||||
|
buffer.forEach((oldCondition: QuestionBranchingRuleMain) => {
|
||||||
|
if (areRulesEqual(condition.rules, oldCondition.rules)) {
|
||||||
|
pushProblem(
|
||||||
|
question.content.id,
|
||||||
|
`У вопроса "${getQuestionByContentId(condition.next)?.title || "noname"}" и "${getQuestionByContentId(oldCondition.next)?.title || "noname"}" одинаковые условия ветвления`,
|
||||||
|
question.title
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
buffer.push(condition)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return problems
|
||||||
|
}
|
||||||
|
|
||||||
|
const areRulesEqual = (first: any, second: any) => {
|
||||||
|
const firstArray = first[0].answers
|
||||||
|
const secondArray = second[0].answers
|
||||||
|
if (firstArray.length === secondArray.length) {
|
||||||
|
if (firstArray.length > 1) {
|
||||||
|
if (
|
||||||
|
firstArray.every((element: any, index: number) => element === secondArray[index])
|
||||||
|
&&
|
||||||
|
first.or === second.or
|
||||||
|
) return true;
|
||||||
|
} else {
|
||||||
|
if (firstArray[0] === secondArray[0]) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
67
src/utils/deleteFunc.ts
Normal file
67
src/utils/deleteFunc.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
|
import { clearRuleForAll, deleteQuestion, getQuestionByContentId, updateQuestion } from "@root/questions/actions";
|
||||||
|
import { updateRootContentId } from "@root/quizes/actions";
|
||||||
|
|
||||||
|
//Всё здесь нужно сделать последовательно. И пусть весь мир ждёт.
|
||||||
|
|
||||||
|
export const DeleteFunction = async (questions: any, question: any, quiz: any) => {
|
||||||
|
|
||||||
|
|
||||||
|
if (question.type !== null) {
|
||||||
|
if (question.content.rule.parentId === "root") { //удалить из стора root и очистить rule всем вопросам
|
||||||
|
updateRootContentId(quiz.id, "");
|
||||||
|
await clearRuleForAll();
|
||||||
|
console.log("очистка рулов закончилась")
|
||||||
|
await deleteQuestion(question.id);
|
||||||
|
} else if (question.content.rule.parentId.length > 0) { //удалить из стора вопрос из дерева и очистить его потомков
|
||||||
|
const clearQuestions = [] as string[];
|
||||||
|
|
||||||
|
//записываем потомков , а их результаты удаляем
|
||||||
|
const getChildren = (parentQuestion: AnyTypedQuizQuestion) => {
|
||||||
|
questions.forEach((targetQuestion) => {
|
||||||
|
if (targetQuestion.type !== null && targetQuestion.content.rule.parentId === parentQuestion.content.id) {//если у вопроса совпал родитель с родителем => он потомок, в кучу его
|
||||||
|
if (targetQuestion.type !== "result" && targetQuestion.type !== null) {
|
||||||
|
if (!clearQuestions.includes(targetQuestion.content.id)) clearQuestions.push(targetQuestion.content.id);
|
||||||
|
getChildren(targetQuestion); //и ищем его потомков
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
getChildren(question);
|
||||||
|
//чистим потомков от инфы ветвления
|
||||||
|
await Promise.allSettled(
|
||||||
|
clearQuestions.map((id) => {
|
||||||
|
updateQuestion(id, question => {
|
||||||
|
question.content.rule.parentId = "";
|
||||||
|
question.content.rule.children = [];
|
||||||
|
question.content.rule.main = [];
|
||||||
|
question.content.rule.default = "";
|
||||||
|
});
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
//чистим rule родителя
|
||||||
|
const parentQuestion = getQuestionByContentId(question.content.rule.parentId);
|
||||||
|
const newRule = {};
|
||||||
|
newRule.main = parentQuestion.content.rule.main.filter((data) => data.next !== question.content.id); //удаляем условия перехода от родителя к этому вопросу
|
||||||
|
newRule.parentId = parentQuestion.content.rule.parentId;
|
||||||
|
newRule.default = parentQuestion.content.rule.parentId === question.content.id ? "" : parentQuestion.content.rule.parentId;
|
||||||
|
newRule.children = [...parentQuestion.content.rule.children].splice(parentQuestion.content.rule.children.indexOf(question.content.id), 1);
|
||||||
|
|
||||||
|
await updateQuestion(question.content.rule.parentId, (PQ) => {
|
||||||
|
PQ.content.rule = newRule;
|
||||||
|
});
|
||||||
|
await deleteQuestion(question.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteQuestion(question.id);
|
||||||
|
|
||||||
|
const result = questions.find(q => q.type === "result" && q.content.rule.parentId === question.content.id)
|
||||||
|
if (result) await deleteQuestion(result.id);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
await deleteQuestion(question.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
src/utils/deleteTimeoutedQuestions.ts
Normal file
25
src/utils/deleteTimeoutedQuestions.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { AnyTypedQuizQuestion, UntypedQuizQuestion } from "@model/questionTypes/shared";
|
||||||
|
import { Quiz } from "@model/quiz/quiz";
|
||||||
|
import { updateSomeWorkBackend } from "@root/uiTools/actions";
|
||||||
|
import { DeleteFunction } from "@utils/deleteFunc";
|
||||||
|
|
||||||
|
type allQuestionsTypes = AnyTypedQuizQuestion | UntypedQuizQuestion
|
||||||
|
|
||||||
|
export const deleteTimeoutedQuestions = async (questions: allQuestionsTypes[], quiz: Quiz|undefined) => {
|
||||||
|
console.log("Я отвечаю за удаление неудалёнышей при переключении. Привет, буде знакомы")
|
||||||
|
const questionsForDeletion = questions.filter(
|
||||||
|
({ type, deleted }) => type && type !== "result" && deleted
|
||||||
|
) as AnyTypedQuizQuestion[];
|
||||||
|
if (questionsForDeletion.length > 0) {
|
||||||
|
console.log("меняю занятость беком на true")
|
||||||
|
updateSomeWorkBackend(true)
|
||||||
|
|
||||||
|
|
||||||
|
await Promise.allSettled(
|
||||||
|
questionsForDeletion.map(question => DeleteFunction(questions, question, quiz))
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log("______________меняю на 'можно редактировать дальше'______________")
|
||||||
|
updateSomeWorkBackend(false)
|
||||||
|
}
|
||||||
|
};
|
43
src/utils/themes/Publication/genericPublication.ts
Normal file
43
src/utils/themes/Publication/genericPublication.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { createTheme } from "@mui/material";
|
||||||
|
import theme from "../generic";
|
||||||
|
|
||||||
|
const themePublic = createTheme({
|
||||||
|
...theme,
|
||||||
|
components: {
|
||||||
|
MuiButton: {
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
variant: 'contained'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
padding: '13px 20px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
boxShadow: "none",
|
||||||
|
// "&:hover": {
|
||||||
|
// backgroundColor: "#581CA7"
|
||||||
|
// }
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
variant: 'outlined'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
padding: '10px 20px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "#581CA7",
|
||||||
|
border: '1px solid #581CA7',
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export default themePublic;
|
253
src/utils/themes/Publication/themePublication.ts
Normal file
253
src/utils/themes/Publication/themePublication.ts
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
import { createTheme } from "@mui/material";
|
||||||
|
import themePublic from "./genericPublication";
|
||||||
|
import theme from "../generic";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const StandardTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#7E2AEA",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#333647",
|
||||||
|
secondary: "#7E2AEA",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#FFFFFF",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const StandardDarkTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#7E2AEA",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#FFFFFF",
|
||||||
|
secondary: "#7E2AEA",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#333647",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const PinkTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#D34085",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#333647",
|
||||||
|
secondary: "#D34085",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#FFF9FC",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const PinkDarkTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#D34085",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#FFFFFF",
|
||||||
|
secondary: "#D34085",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#333647",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const BlackWhiteTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#4E4D51",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#333647",
|
||||||
|
secondary: "#FFF9FC",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#FFFFFF",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const OliveTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#758E4F",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#333647",
|
||||||
|
secondary: "#758E4F",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#F9FBF1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const PurpleTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#7E2AEA",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#333647",
|
||||||
|
secondary: "#7E2AEA",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#FBF8FF",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const YellowTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#F2B133",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#333647",
|
||||||
|
secondary: "#F2B133",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#FFFCF6",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const GoldDarkTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#E6AA37",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#FFFCF6",
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#FFFFFF",
|
||||||
|
secondary: "#F2B133",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#333647",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const BlueTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#4964ED",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#333647",
|
||||||
|
secondary: "#4964ED",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#F5F7FF",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const BlueDarkTheme = createTheme({
|
||||||
|
...themePublic,
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#07A0C3",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#252734"
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#FFFFFF",
|
||||||
|
secondary: "#07A0C3",
|
||||||
|
},
|
||||||
|
|
||||||
|
background: {
|
||||||
|
default: "#333647",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const modes = {
|
||||||
|
StandardTheme: true,
|
||||||
|
StandardDarkTheme: false,
|
||||||
|
PinkTheme: true,
|
||||||
|
PinkDarkTheme: false,
|
||||||
|
BlackWhiteTheme: true,
|
||||||
|
OliveTheme: true,
|
||||||
|
YellowTheme: true,
|
||||||
|
GoldDarkTheme: false,
|
||||||
|
PurpleTheme: true,
|
||||||
|
BlueTheme: true,
|
||||||
|
BlueDarkTheme: false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const themesPublication = {
|
||||||
|
StandardTheme,
|
||||||
|
StandardDarkTheme,
|
||||||
|
PinkTheme,
|
||||||
|
PinkDarkTheme,
|
||||||
|
BlackWhiteTheme,
|
||||||
|
OliveTheme,
|
||||||
|
YellowTheme,
|
||||||
|
GoldDarkTheme,
|
||||||
|
PurpleTheme,
|
||||||
|
BlueTheme,
|
||||||
|
BlueDarkTheme,
|
||||||
|
}
|
@ -16,6 +16,9 @@
|
|||||||
],
|
],
|
||||||
"@model/*": [
|
"@model/*": [
|
||||||
"./model/*"
|
"./model/*"
|
||||||
|
],
|
||||||
|
"@utils/*": [
|
||||||
|
"./utils/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user