Merge branch 'dev' into 'staging'

add side widget installation setup

See merge request frontend/squiz!328
This commit is contained in:
Nastya 2024-05-29 18:42:01 +00:00
commit d5f5378f5e
62 changed files with 15169 additions and 14089 deletions

@ -7,7 +7,7 @@
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@frontend/kitui": "^1.0.82",
"@frontend/squzanswerer": "^1.0.38",
"@frontend/squzanswerer": "^1.0.44",
"@mui/icons-material": "^5.10.14",
"@mui/material": "^5.10.14",
"@mui/x-charts": "^6.19.5",
@ -67,7 +67,7 @@
"test": "craco test",
"eject": "craco eject",
"cypress:open": "cypress open",
"code:format": "prettier ./src --write --ignore-unknown",
"code:format": "prettier --write --ignore-unknown",
"prepare": "husky install"
},
"browserslist": {
@ -96,5 +96,17 @@
},
"lint-staged": {
"**/*": "yarn code:format"
},
"prettier": {
"semi": true,
"trailingComma": "es5",
"singleQuote": false,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"endOfLine": "auto",
"bracketSpacing": true,
"arrowParens": "always",
"jsxSingleQuote": false
}
}

@ -1,12 +0,0 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": false,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"endOfLine": "auto",
"bracketSpacing": true,
"arrowParens": "always",
"jsxSingleQuote": false
}

@ -40,6 +40,7 @@ import { InfoPrivilege } from "./pages/InfoPrivilege";
import OutdatedLink from "./pages/auth/OutdatedLink";
import { useAfterpay } from "@utils/hooks/useAfterpay";
import { useUserAccountFetcher } from "@utils/hooks/useUserAccountFetcher";
import ListPageDummy from "./components/Dummys/pageDummys/listPageDummy";
const MyQuizzesFull = lazy(() => import("./pages/createQuize/MyQuizzesFull"));
@ -208,7 +209,7 @@ export default function App() {
/>
<Route
path="/list"
element={<LazyLoading children={<MyQuizzesFull />} />}
element={<LazyLoading children={<MyQuizzesFull />} fallback={<ListPageDummy/>} />}
/>
<Route
path={"/view/:quizId"}

@ -19,7 +19,7 @@ type AddedQuizImagesResponse = {
};
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz`;
const IMAGES_URL = `${process.env.REACT_APP_DOMAIN}/squizstorer`;
const IMAGES_URL = `${process.env.REACT_APP_DOMAIN}/squizstorer/v1.0.0`;
export const createQuiz = async (
body?: Partial<CreateQuizRequest>,

@ -13,7 +13,7 @@ type SendFileResponse = {
message: string;
};
const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym`;
const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym/v1.0.0`;
export const sendTicketMessage = async (
ticketId: string,
@ -67,7 +67,7 @@ export const sendFile = async (
const sendResponse = await makeRequest<FormData, SendFileResponse>({
method: "POST",
url: `${process.env.REACT_APP_DOMAIN}/heruvym/sendFiles`,
url: `${API_URL}/sendFiles`,
body,
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

@ -1,41 +0,0 @@
import { Box } from "@mui/material";
interface Props {
color: string;
}
export default function NumberThree({ color }: Props) {
return (
<Box
sx={{
height: "30px",
width: "30px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg
width="24"
height="25"
viewBox="0 0 24 25"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21.1875C16.9706 21.1875 21 17.1581 21 12.1875C21 7.21694 16.9706 3.1875 12 3.1875C7.02944 3.1875 3 7.21694 3 12.1875C3 17.1581 7.02944 21.1875 12 21.1875Z"
stroke={color}
strokeWidth="1.5"
strokeMiterlimit="10"
/>
<path
d="M9.75 8.0625H14.25L11.625 11.8125C12.0567 11.8125 12.4817 11.919 12.8624 12.1225C13.243 12.326 13.5677 12.6203 13.8075 12.9792C14.0473 13.3381 14.1949 13.7507 14.2372 14.1803C14.2795 14.6099 14.2152 15.0433 14.05 15.4421C13.8848 15.8409 13.6238 16.1928 13.2901 16.4666C12.9564 16.7405 12.5603 16.9278 12.137 17.0121C11.7136 17.0963 11.276 17.0748 10.8629 16.9495C10.4498 16.8242 10.074 16.599 9.76875 16.2937"
stroke={color}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

@ -1,41 +0,0 @@
import { Box } from "@mui/material";
interface Props {
color: string;
}
export default function NumberTwo({ color }: Props) {
return (
<Box
sx={{
height: "30px",
width: "30px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg
width="24"
height="25"
viewBox="0 0 24 25"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21.1875C16.9706 21.1875 21 17.1581 21 12.1875C21 7.21694 16.9706 3.1875 12 3.1875C7.02944 3.1875 3 7.21694 3 12.1875C3 17.1581 7.02944 21.1875 12 21.1875Z"
stroke={color}
strokeWidth="1.5"
strokeMiterlimit="10"
/>
<path
d="M9.92813 9.06402C10.1303 8.58652 10.4913 8.19352 10.9499 7.9515C11.4085 7.70948 11.9366 7.6333 12.4449 7.73584C12.9533 7.83839 13.4106 8.11336 13.7395 8.51426C14.0684 8.91515 14.2487 9.41735 14.25 9.9359C14.252 10.3839 14.118 10.822 13.8656 11.1921V11.1921L9.75 16.6859H14.25"
stroke={color}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

@ -1,41 +0,0 @@
import { Box } from "@mui/material";
interface Props {
color: string;
}
export default function OneIconBorder({ color }: Props) {
return (
<Box
sx={{
height: "30px",
width: "30px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z"
stroke={color}
strokeWidth="1.5"
strokeMiterlimit="10"
/>
<path
d="M10.125 9.375L12.375 7.875V16.5"
stroke={color}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

@ -0,0 +1,25 @@
import {Box, Skeleton, Typography} from "@mui/material";
import React from "react";
export default function AvailablePrivilegeDummy() {
return (
<Box
sx={{
padding: "20px",
backgroundColor: "#9A9AAF17",
width: "100%",
borderRadius: "12px",
display: "flex",
gap: "20px",
flexWrap: "wrap",
}}
>
<Typography variant={"body1"} sx={{ color: "#9A9AAF" }}>
Вам доступно:
</Typography>
<Skeleton variant="text" sx={{ maxWidth: "260px", width: "100%", height: "30px"}} />
<Skeleton variant="text" sx={{ maxWidth: "130px", width: "100%", height: "30px"}} />
<Skeleton variant="text" sx={{ maxWidth: "340px", width: "100%", height: "30px"}} />
</Box>
);
}

@ -0,0 +1,126 @@
import {Box, Button, IconButton, Skeleton, Typography, useMediaQuery, useTheme} from "@mui/material";
import LinkIcon from "@icons/LinkIcon";
import PencilIcon from "@icons/PencilIcon";
import ChartIcon from "@icons/ChartIcon";
import CopyIcon from "@icons/CopyIcon";
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
import React from "react";
export default function QuizCardDummy() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(600));
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
backgroundColor: "white",
width: isMobile ? "100%" : "560px",
height: "280px",
p: "20px",
borderRadius: "12px",
boxSizing: "border-box",
boxShadow: `0px 100px 309px rgba(210, 208, 225, 0.24),
0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525),
0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066),
0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12),
0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343),
0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)`,
}}
>
<Skeleton variant="text" sx={{ fontSize: "24px", width: "100%", height: "30px" }} />
<Box
sx={{
display: "flex",
alignItems: "center",
mt: "10px",
gap: "10px",
}}
>
<LinkIcon bgcolor="#EEE4FC" color={theme.palette.brightPurple.main} />
<Skeleton variant="text" sx={{ fontSize: "18px", maxWidth: "300px", width: "100%", height: "30px"}} />
</Box>
<Box
sx={{
display: "flex",
mt: "32px",
mr: "22px",
}}
>
<Box sx={{ flex: "1 1 0" }}>
<Skeleton variant="text" sx={{ width: "100%", height: "30px", maxWidth: "100px", }} />
<Typography color={theme.palette.grey3.main}>Открытий</Typography>
</Box>
<Box sx={{ flex: "1 1 0" }}>
<Skeleton variant="text" sx={{ maxWidth: "100px", width: "100%", height: "30px"}} />
<Typography color={theme.palette.grey3.main}>Заявок</Typography>
</Box>
<Box sx={{ flex: "1 1 0" }}>
<Skeleton variant="text" sx={{ maxWidth: "100px", width: "100%", height: "30px"}} />
<Typography color={theme.palette.grey3.main}>Конверсия</Typography>
</Box>
</Box>
<Box
sx={{
mt: "auto",
display: "flex",
gap: isMobile ? "10px" : "20px",
}}
>
<Button
variant="contained"
sx={{
padding: "10px 39px",
}}
>
Заявки
</Button>
<Button
variant="outlined"
startIcon={<PencilIcon />}
sx={{
padding: isMobile ? "10px" : "10px 20px",
minWidth: "unset",
color: theme.palette.brightPurple.main,
"& .MuiButton-startIcon": {
marginRight: isMobile ? 0 : "4px",
marginLeft: 0,
},
}}
>
{isMobile ? "" : "Редактировать"}
</Button>
<IconButton
sx={{
height: "44px",
width: "44px",
border: `${theme.palette.brightPurple.main} 1px solid`,
borderRadius: "6px",
}}
>
<ChartIcon />
</IconButton>
<IconButton
sx={{
height: "44px",
width: "44px",
borderRadius: "6px",
}}
>
<CopyIcon
color={theme.palette.brightPurple.main}
bgcolor={"#EEE4FC"}
/>
</IconButton>
<IconButton
sx={{
color: theme.palette.brightPurple.main,
ml: "auto",
}}
>
<MoreHorizIcon sx={{ transform: "scale(1.75)" }} />
</IconButton>
</Box>
</Box>
);
}

@ -0,0 +1,32 @@
import {
Box,
Button,
SxProps,
Theme,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import SectionWrapper from "@ui_kit/SectionWrapper";
import React from "react";
import HeaderFull from "@ui_kit/Header/HeaderFull";
import QuizCardDummy from "../QuizCardDummy";
import AvailablePrivilegeDummy from "../AvailablePrivilegeDummy";
export default function ListPageDummy() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(500));
return (
<>
<HeaderFull isRequest={false} />
<SectionWrapper
maxWidth="lg"
sx={{ padding: isMobile ? "0 16px" : "20px" }}
>
</SectionWrapper>
</>
);
}

@ -1,180 +0,0 @@
import {
Box,
Checkbox,
FormControl,
FormControlLabel,
TextField,
Typography,
useTheme,
} from "@mui/material";
import Dots from "../../assets/dots.png";
import React from "react";
import InstallQzOnSiteParent from "./InstallQzOnSiteParent";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import InfoIcon from "../../assets/icons/InfoIcon";
export default function AutoOpenInstall() {
const theme = useTheme();
const [checked, setChecked] = React.useState([true, false]);
const handleChange1 = (event: React.ChangeEvent<HTMLInputElement>) => {
setChecked([event.target.checked, checked[1]]);
};
const handleChange2 = (event: React.ChangeEvent<HTMLInputElement>) => {
setChecked([checked[0], event.target.checked]);
};
return (
<InstallQzOnSiteParent>
<Box sx={{ maxWidth: "560px" }}>
<Box
sx={{
border: "1.5px solid #9A9AAF",
borderRadius: "6px",
maxWidth: "560px",
height: "364px",
}}
>
<Box
sx={{
background: theme.palette.background.default,
borderBottom: "1.5px solid #9A9AAF",
borderRadius: "6px 6px 0px 0px",
minHeight: "34px",
display: "flex",
alignItems: "center",
justifyContent: "end",
paddingRight: "10px",
}}
>
{" "}
<img src={Dots} />
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "330px",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
maxWidth: "506px",
minHeight: "280px",
background: "#EEE4FC",
borderRadius: "6px",
border: "1px dashed #7E2AEA",
}}
>
<Typography sx={{ color: theme.palette.brightPurple.main }}>
Quiz будет открываться через указанное время
</Typography>
</Box>
</Box>
</Box>
<Typography sx={{ fontSize: "14px", color: theme.palette.grey2.main }}>
Если quiz уже установлен на сайт, и вы что-то здесь изменили, код на
сайте нужно будет поменять. Настройки в этом конструкторе не
сохраняются.
</Typography>
</Box>
<Box sx={{ display: "flex", flexDirection: "column", gap: "15px" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<CustomCheckbox
label={"Автооткрытие quiz"}
checked={checked[0]}
handleChange={handleChange1}
/>
<InfoIcon />
</Box>
<Box sx={{ display: "flex", alignItems: "center" }}>
<CustomCheckbox
label={"Открывать quiz при попытке уйти с сайта"}
checked={checked[1]}
handleChange={handleChange2}
/>
<InfoIcon />
</Box>
{checked[0] ? (
<Box>
<Box
sx={{
padding: "15px 0",
display: "flex",
gap: "10px",
alignItems: "center",
borderTop: "1px solid #9A9AAF",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<FormControl
fullWidth
variant="standard"
sx={{ p: 0, width: "60px" }}
>
<TextField
fullWidth
placeholder="10"
sx={{
"& .MuiInputBase-root": {
backgroundColor: theme.palette.background.default,
height: "48px",
width: "60px",
borderRadius: "10px",
},
}}
inputProps={{
sx: {
borderRadius: "10px",
fontSize: "18px",
lineHeight: "21px",
py: 0,
},
}}
/>
</FormControl>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<CustomCheckbox label={"Открывать каждый раз"} />
<CustomCheckbox
label={"Отключать автооткрытие на мобильных устройствах"}
/>
</Box>
</Box>
) : (
<></>
)}
{checked[1] ? (
<Box
sx={{
borderTop: "1px solid #9A9AAF",
display: "flex",
flexDirection: "column",
padding: "15px 0",
}}
>
<CustomCheckbox label={"Открывать каждый раз"} />
<CustomCheckbox
label={"Отключать автооткрытие на мобильных устройствах"}
/>
</Box>
) : (
<></>
)}
</Box>
</InstallQzOnSiteParent>
);
}

@ -1,268 +0,0 @@
import {
Box,
FormControl,
FormControlLabel,
Link,
Radio,
RadioGroup,
TextField,
Typography,
useTheme,
} from "@mui/material";
import InstallQzOnSiteParent from "./InstallQzOnSiteParent";
import React, { useState } from "react";
import BannerImg from "../../assets/BannerImg.png";
import CustomTextField from "@ui_kit/CustomTextField";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
import AccordMy from "@ui_kit/Accordion";
type isExpanded = "panel1" | "panel2";
export default function BannerInstall() {
const theme = useTheme();
const [value, setValue] = React.useState("1");
const handleChangeRadio = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue((event.target as HTMLInputElement).value);
};
const [value2, setValue2] = React.useState("1");
const handleChangeRadio2 = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue2((event.target as HTMLInputElement).value);
};
const [isExpanded, setIsExpanded] = useState<isExpanded>("panel1");
return (
<InstallQzOnSiteParent>
<Box sx={{ maxWidth: "560px" }}>
<img src={BannerImg} />
<Typography sx={{ fontSize: "14px", color: theme.palette.grey2.main }}>
Если quiz уже установлен на сайт, и вы что-то здесь изменили, код на
сайте нужно будет поменять. Настройки в этом конструкторе не
сохраняются.
</Typography>
</Box>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Box sx={{ display: "flex", flexDirection: "column", gap: "15px" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Текст-призыв
</Typography>
<CustomTextField placeholder={"Пройти тест"} />
<Typography sx={{ color: theme.palette.grey2.main }}>
Заголовок quiz
</Typography>
<CustomTextField placeholder={"Заголовок quiz"} />
<Box sx={{ display: "flex", gap: "10px", alignItems: "center" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<FormControl
fullWidth
variant="standard"
sx={{ p: 0, width: "60px" }}
>
<TextField
fullWidth
placeholder="20"
sx={{
"& .MuiInputBase-root": {
backgroundColor: theme.palette.background.default,
height: "48px",
width: "60px",
borderRadius: "10px",
},
}}
inputProps={{
sx: {
borderRadius: "10px",
fontSize: "18px",
lineHeight: "21px",
py: 0,
},
}}
/>
</FormControl>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
<Box
sx={{
display: "flex",
gap: "40px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет кнопки
</Typography>
<Box
sx={{
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: theme.palette.brightPurple.main,
border: "1px solid #4D4D4D",
}}
>
{" "}
</Box>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет текста кнопки
</Typography>
<Box
sx={{
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: "#ffffff",
border: "1px solid #4D4D4D",
}}
>
{" "}
</Box>
</Box>
</Box>
<AccordMy
header={"Баннер сбоку экрана"}
isExpanded={isExpanded === "panel1"}
onClick={() => setIsExpanded("panel1")}
sx={{ display: "flex", flexDirection: "column" }}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Расположение
</Typography>
<FormControl>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChangeRadio}
sx={{
maxWidth: "380px",
display: "flex",
flexWrap: "wrap",
flexDirection: "row",
paddingLeft: "5px",
justifyContent: "space-between",
}}
>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<FormControlLabel
sx={{ color: theme.palette.grey2.main }}
value="1"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Слева сверху"
/>
<FormControlLabel
sx={{ color: theme.palette.grey2.main }}
value="2"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Справа сверху"
/>
</Box>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<FormControlLabel
sx={{ color: theme.palette.grey2.main }}
value="3"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Слева снизу"
/>
<FormControlLabel
sx={{ color: theme.palette.grey2.main }}
value="4"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Справа снизу"
/>
</Box>
</RadioGroup>
</FormControl>
<Typography sx={{ color: theme.palette.grey2.main }}>
Параметры
</Typography>
<CustomCheckbox label={"Закругленная"} />
<CustomCheckbox label={"С тенью"} />
<CustomCheckbox label={"С бликом"} />
<CustomCheckbox label={'Эффект "пульсация"'} />
<CustomCheckbox label={"Отключить на мобильных устройствах"} />
</AccordMy>
<AccordMy
header={"Баннер на всю ширину экрана"}
isExpanded={isExpanded === "panel2"}
onClick={() => setIsExpanded("panel2")}
sx={{ display: "flex", flexDirection: "column" }}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Расположение
</Typography>
<FormControl>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value2}
onChange={handleChangeRadio2}
sx={{
maxWidth: "365px",
display: "flex",
flexWrap: "wrap",
flexDirection: "row",
paddingLeft: "5px",
}}
>
<FormControlLabel
sx={{ color: theme.palette.grey2.main }}
value="1"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Сверху страницы"
/>
<FormControlLabel
sx={{ color: theme.palette.grey2.main }}
value="2"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Снизу страницы"
/>
</RadioGroup>
</FormControl>
<Typography sx={{ color: theme.palette.grey2.main }}>
Параметры
</Typography>
<CustomCheckbox label={"С тенью"} />
<CustomCheckbox label={"С бликом"} />
<CustomCheckbox label={'Эффект "пульсация"'} />
<CustomCheckbox label={"Отключить на мобильных устройствах"} />
</AccordMy>
<Link
component="button"
sx={{
fontSize: "16px",
lineHeight: "19px",
color: theme.palette.brightPurple.main,
textDecorationColor: theme.palette.brightPurple.main,
textAlign: "left",
padding: "15px 0",
}}
>
+ Автооткрытие quiz
</Link>
</Box>
</InstallQzOnSiteParent>
);
}

@ -1,169 +0,0 @@
import { Box, Button, Link, Typography, useTheme } from "@mui/material";
import InstallQzOnSiteParent from "./InstallQzOnSiteParent";
import Dots from "../../assets/dots.png";
import React from "react";
import CustomTextField from "@ui_kit/CustomTextField";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
export default function InBodyInstall() {
const theme = useTheme();
return (
<InstallQzOnSiteParent>
<Box sx={{ maxWidth: "560px" }}>
<Box
sx={{
border: "1.5px solid #9A9AAF",
borderRadius: "6px",
maxWidth: "560px",
height: "364px",
}}
>
<Box
sx={{
background: theme.palette.background.default,
borderBottom: "1.5px solid #9A9AAF",
borderRadius: "6px 6px 0px 0px",
minHeight: "34px",
display: "flex",
alignItems: "center",
justifyContent: "end",
paddingRight: "10px",
}}
>
{" "}
<img src={Dots} />
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
gap: "40px",
height: "100%",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
maxWidth: "413px",
minHeight: "133px",
background: "#EEE4FC",
borderRadius: "6px",
border: "1px dashed #7E2AEA",
}}
>
<Typography
sx={{
color: theme.palette.brightPurple.main,
maxWidth: "365px",
}}
>
Quiz будет открыть прямо в том месте, где вы установите код на
сайте
</Typography>
</Box>
<Typography sx={{ maxWidth: "414px", fontSize: "14px" }}>
В мобильной версии будет показана кнопка, открывающая quiz в
модальном окне
</Typography>
</Box>
</Box>
<Typography sx={{ fontSize: "14px", color: theme.palette.grey2.main }}>
Если quiz уже установлен на сайт, и вы что-то здесь изменили, код на
сайте нужно будет поменять. Настройки в этом конструкторе не
сохраняются.
</Typography>
</Box>
<Box sx={{ display: "flex", flexDirection: "column", gap: "15px" }}>
<Typography>1. Задайте размеры (опционально)</Typography>
<Box sx={{ display: "flex", gap: "40px" }}>
<Box sx={{ display: "flex", flexDirection: "column", gap: "20px" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Ширина (px)
</Typography>
<CustomTextField placeholder={"auto"} />
<Typography sx={{ color: theme.palette.grey2.main }}>
Радиус (px)
</Typography>
<CustomTextField placeholder={"0"} />
</Box>
<Box sx={{ display: "flex", flexDirection: "column", gap: "20px" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Высота (px)
</Typography>
<CustomTextField placeholder={"auto"} />
<Typography sx={{ color: theme.palette.grey2.main }}>
Отступ (px)
</Typography>
<CustomTextField placeholder={"0"} />
</Box>
</Box>
<Box sx={{ display: "flex", flexDirection: "column", gap: "15px" }}>
<Typography>2. Настройте кнопку для мобильной версии</Typography>
<Box
sx={{
display: "flex",
gap: "40px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет кнопки
</Typography>
<Box
sx={{
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: theme.palette.brightPurple.main,
border: "1px solid #4D4D4D",
}}
>
{" "}
</Box>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет текста кнопки
</Typography>
<Box
sx={{
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: "#ffffff",
border: "1px solid #4D4D4D",
}}
>
{" "}
</Box>
</Box>
<CustomCheckbox label={"Отключить на мобильных устройствах"} />
<CustomCheckbox label={"Закругленная"} />
<CustomCheckbox label={"С тенью"} />
<CustomCheckbox label={"С бликом"} />
<CustomCheckbox label={"Кнопка на мобильной версии"} />
<Typography sx={{ color: theme.palette.grey2.main }}>
Текст кнопки
</Typography>
<CustomTextField placeholder={"Пройти тест"} />
<Link
component="button"
sx={{
fontSize: "16px",
lineHeight: "19px",
color: theme.palette.brightPurple.main,
textDecorationColor: theme.palette.brightPurple.main,
textAlign: "left",
}}
>
+ Автооткрытие quiz
</Link>
</Box>
</Box>
</InstallQzOnSiteParent>
);
}

@ -1,91 +0,0 @@
import InstallQzOnSiteParent from "./InstallQzOnSiteParent";
import {
Box,
Button,
FormControl,
IconButton,
InputAdornment,
Link,
OutlinedInput,
TextField,
Typography,
useTheme,
} from "@mui/material";
import CopyIcon from "@icons/CopyIcon";
import React from "react";
import InfoIcon from "@icons/InfoIcon";
export default function InstallQzCode() {
const theme = useTheme();
return (
<InstallQzOnSiteParent outerContainerSx={{ flexDirection: "column" }}>
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
<Box
sx={{
maxWidth: "520px",
width: "100%",
display: "flex",
flexDirection: "column",
gap: "20px",
}}
>
<Typography>1. Код вставки опросника</Typography>
<Typography sx={{ color: theme.palette.grey2.main }}>
Установите код в то место, где должен быть опросник
</Typography>
<TextField
id="outlined-multiline-static"
multiline
rows={9}
value="<script>
(function(w, d, s, o){
var j = d.createElement(s); j.async = true; j.src = //script.marquiz.ru/v2.js';j.onload = function() {
if (document.readyState !== 'loading') Marquiz.init(o);
else document.addEventListener('DOMContentLoaded', function() {
Marquiz.init(o);
});"
sx={{
"& .MuiInputBase-root": {
maxWidth: "520px",
width: "100%",
backgroundColor: theme.palette.background.default,
fontSize: "18px",
alignItems: "flex-start",
},
}}
InputProps={{
endAdornment: (
<InputAdornment position="start">
<IconButton edge="end" sx={{ marginTop: "22px" }}>
<CopyIcon
color={"#ffffff"}
bgcolor={theme.palette.brightPurple.main}
/>
</IconButton>
</InputAdornment>
),
}}
/>
</Box>
</Box>
<Box
sx={{
background: "#EEE4FC",
border: "1px solid #7E2AEA",
padding: "20px 50px 20px 20px",
borderRadius: "8px",
display: "flex",
flexDirection: "column",
gap: "20px",
marginBottom: "20px",
}}
>
<Typography>
Код нужно вставить один раз. Изменения в самом quiz будут отображаться
автоматически после сохранения.
</Typography>
</Box>
</InstallQzOnSiteParent>
);
}

@ -1,69 +0,0 @@
import {
Box,
Button,
SxProps,
Theme,
Typography,
useTheme,
} from "@mui/material";
import OneIconBorder from "../../assets/icons/OneIconBorder";
import React from "react";
import NumberTwo from "@icons/NumberTwo";
import NumberThree from "@icons/NumberThree";
interface Props {
outerContainerSx?: SxProps<Theme>;
children?: React.ReactNode;
}
export default function InstallQzOnSiteParent({
outerContainerSx: sx,
children,
}: Props) {
const theme = useTheme();
return (
<>
<Box
sx={{
maxWidth: "1160px",
backgroundColor: "#ffffff",
padding: "20px",
mt: "40px",
borderRadius: "12px",
boxShadow:
"0px 100px 309px rgba(210, 208, 225, 0.24), 0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525), 0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066), 0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12), 0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343), 0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Typography variant="h5" sx={{ paddingRight: "30px" }}>
Установка quiz на сайте
</Typography>
<OneIconBorder color={theme.palette.brightPurple.main} />
<Typography sx={{ color: theme.palette.brightPurple.main }}>
Способ установки
</Typography>
<NumberTwo color={"#FC712F"} />
<Typography sx={{ color: "#FC712F" }}>Настройка кнопки</Typography>
<NumberThree color={theme.palette.grey2.main} />
<Typography sx={{ color: theme.palette.grey2.main }}>
Вставить код на сайт
</Typography>
</Box>
<Box
sx={{
display: "flex",
gap: "40px",
marginTop: "30px",
...sx,
}}
>
{children}
</Box>
<Box sx={{ display: "flex", justifyContent: "end" }}>
<Button variant="contained">Далее</Button>
</Box>
</Box>
</>
);
}

@ -1,162 +0,0 @@
import { Box, Button, Link, Typography, useTheme } from "@mui/material";
import React, { useState } from "react";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import CustomTextField from "@ui_kit/CustomTextField";
import Dots from "../../assets/dots.png";
import InstallQzOnSiteParent from "./InstallQzOnSiteParent";
import AccordMy from "@ui_kit/Accordion";
type isExpanded = "panel1" | "panel2";
export default function OnButtonInstall() {
const theme = useTheme();
const [isExpanded, setIsExpanded] = useState<isExpanded>("panel1");
return (
<>
<InstallQzOnSiteParent>
<Box sx={{ maxWidth: "560px" }}>
<Box
sx={{
border: "1.5px solid #9A9AAF",
borderRadius: "6px",
maxWidth: "560px",
height: "364px",
}}
>
<Box
sx={{
background: theme.palette.background.default,
borderBottom: "1.5px solid #9A9AAF",
borderRadius: "6px 6px 0px 0px",
minHeight: "34px",
display: "flex",
alignItems: "center",
justifyContent: "end",
paddingRight: "10px",
}}
>
{" "}
<img src={Dots} />
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100%",
}}
>
<Button variant="contained">Пройти тест</Button>
</Box>
</Box>
<Typography
sx={{ fontSize: "14px", color: theme.palette.grey2.main }}
>
Если quiz уже установлен на сайт, и вы что-то здесь изменили, код на
сайте нужно будет поменять. Настройки в этом конструкторе не
сохраняются.
</Typography>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<AccordMy
header={"Конструктор кнопки"}
isExpanded={isExpanded === "panel1"}
onClick={() => setIsExpanded("panel1")}
sx={{ display: "flex", flexDirection: "column" }}
>
<Box
sx={{
display: "flex",
gap: "40px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет кнопки
</Typography>
<Box
sx={{
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: theme.palette.brightPurple.main,
border: "1px solid #4D4D4D",
}}
>
{" "}
</Box>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет текста кнопки
</Typography>
<Box
sx={{
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: "#ffffff",
border: "1px solid #4D4D4D",
}}
>
{" "}
</Box>
</Box>
<CustomCheckbox label={"Отключить на мобильных устройствах"} />
<CustomCheckbox label={"Закругленная"} />
<CustomCheckbox label={"С тенью"} />
<CustomCheckbox label={"С бликом"} />
<CustomCheckbox label={"Фиксированная"} />
<Typography
sx={{ color: theme.palette.grey2.main, marginBottom: "14px" }}
>
Текст кнопки
</Typography>
<CustomTextField placeholder={"Пройти тест"} maxLength={30} />
</AccordMy>
<AccordMy
header={"Своя кнопка"}
isExpanded={isExpanded === "panel2"}
onClick={() => setIsExpanded("panel2")}
sx={{ display: "flex", flexDirection: "column", width: "100%" }}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Ссылка для вашей кнопки
</Typography>
<CustomTextField
placeholder={""}
maxLength={200}
text={"#popup:639727c5177be5004f11a0f2"}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Или событие
</Typography>
<CustomTextField
placeholder={""}
text={'onclick=":639727c5177be5004f11a0f2'}
maxLength={20}
/>
</AccordMy>
<Link
component="button"
sx={{
fontSize: "16px",
padding: "15px 0",
lineHeight: "19px",
color: theme.palette.brightPurple.main,
textDecorationColor: theme.palette.brightPurple.main,
textAlign: "left",
}}
>
+ Автооткрытие quiz
</Link>
</Box>
</InstallQzOnSiteParent>
</>
);
}

@ -1,31 +1,58 @@
import { ButtonBase, Typography, useTheme } from "@mui/material";
import NumberIcon from "@icons/NumberIcon";
import { Button, useTheme } from "@mui/material";
import { ReactNode } from "react";
interface Props {
active?: boolean;
state: "active" | "done" | "inactive";
onClick?: () => void;
leftIcon?: ReactNode;
index: number;
children?: ReactNode;
}
export default function InstallationStepButton({
active,
leftIcon,
state = "done",
index,
onClick,
children,
}: Props) {
const theme = useTheme();
const buttonColorByState: Record<Props["state"], string> = {
active: "#FC712F",
done: theme.palette.brightPurple.main,
inactive: theme.palette.grey2.main,
};
const color = buttonColorByState[state];
return (
<ButtonBase onClick={onClick}>
{leftIcon}
<Typography
sx={{
color: active ? "#FC712F" : theme.palette.grey2.main,
}}
>
{children}
</Typography>
</ButtonBase>
<Button
onClick={onClick}
sx={[
{
color,
fontSize: "16px",
"& .MuiButton-startIcon": {
mr: "5px",
},
},
state === "active" && {
textDecoration: "underline",
textDecorationColor: color,
},
]}
startIcon={
<NumberIcon
number={index}
color={color}
sx={{
height: "26px",
width: "26px",
}}
/>
}
>
{children}
</Button>
);
}

@ -1,38 +1,53 @@
import { WidgetType } from "@frontend/squzanswerer";
import {
Box,
ButtonBase,
IconButton,
InputAdornment,
TextField as MuiTextField,
TextFieldProps,
Button,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useDomainDefine } from "@utils/hooks/useDomainDefine";
import { FC, useState } from "react";
import CopyIcon from "../../../assets/icons/CopyIcon";
import OneIconBorder from "../../../assets/icons/OneIconBorder";
import { useState } from "react";
import InstallationStepButton from "./InstallationStepButton";
import BannerWidgetSetup from "./WidgetSetupByType/BannerWidgetSetup/BannerWidgetSetup";
import ButtonWidgetSetup from "./WidgetSetupByType/ButtonWidgetSetup";
import ContainerWidgetSetup from "./WidgetSetupByType/ContainerWidgetSetup";
import PopupWidgetSetup from "./WidgetSetupByType/PopupWidgetSetup";
import SideWidgetSetup from "./WidgetSetupByType/SideWidgetSetup/SideWidgetSetup";
import WidgetTypeButton from "./WidgetTypeButton";
import BannerWidgetPreview from "./previewIcons/BannerWidgetPreview";
import ButtonWidgetPreview from "./previewIcons/ButtonWidgetPreview";
import ContainerWidgetPreview from "./previewIcons/ContainerWidgetPreview";
import PopupWidgetPreview from "./previewIcons/PopupWidgetPreview";
import SideWidgetPreview from "./previewIcons/SideWidgetPreview";
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
type WidgetSetupSettings = {
step: 1 | 2 | 3;
widgetType: WidgetType;
};
export default function QuizInstallationCard() {
const theme = useTheme();
const quiz = useCurrentQuiz();
const { isTestServer } = useDomainDefine();
const [stepState, setStepState] = useState("step1");
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const quiz = useCurrentQuiz();
const [widgetSetupSettings, setWidgetSetupSettings] =
useState<WidgetSetupSettings>({ step: 1, widgetType: "container" });
if (!quiz) return null;
const nextButton = (
<Button
onClick={() => setWidgetSetupSettings((prev) => ({ ...prev, step: 3 }))}
variant="contained"
>
Далее
</Button>
);
return (
<Box
sx={{
backgroundColor: "#ffffff",
padding: "20px",
borderRadius: "12px",
maxWidth: "1160px",
boxShadow:
@ -41,170 +56,168 @@ export default function QuizInstallationCard() {
>
<Box
sx={{
pt: "20px",
pb: "10px",
px: "20px",
display: "flex",
alignItems: isSmallMonitor ? "flex-start" : "center",
gap: "30px",
gap: "20px",
flexDirection: isSmallMonitor ? "column" : "row",
}}
>
<Typography variant="h5" sx={{ paddingRight: "30px" }}>
Установка quiz на сайте
</Typography>
<InstallationStepButton
active={stepState === "step1"}
leftIcon={
<OneIconBorder
color={
stepState === "step1" ? "#FC712F" : theme.palette.grey2.main
}
/>
}
onClick={() => {
setStepState("step1");
<Typography variant="h5">Установка quiz на сайте</Typography>
<Box
sx={{
display: "flex",
gap: isSmallMonitor ? "0" : "11px",
alignItems: isSmallMonitor ? "flex-start" : "center",
flexDirection: isSmallMonitor ? "column" : "row",
}}
>
Способ установки
</InstallationStepButton>
<InstallationStepButton
active={stepState === "step2"}
leftIcon={
<OneIconBorder
color={
stepState === "step2" ? "#FC712F" : theme.palette.grey2.main
}
/>
}
onClick={() => {
setStepState("step2");
}}
>
Вставить код на сайт
</InstallationStepButton>
</Box>
<Box
sx={{
padding: "20px",
display: "flex",
flexDirection: "column",
gap: "30px",
}}
>
{stepState === "step1" ? (
<ButtonBase
<InstallationStepButton
state={widgetSetupSettings.step === 1 ? "active" : "done"}
index={1}
onClick={() => {
setStepState("step2");
}}
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "start",
alignItems: "start",
maxWidth: "205px",
gap: "15px",
setWidgetSetupSettings({
step: 1,
widgetType: "container",
});
}}
>
<ContainerWidgetPreview />
<Typography>В тело сайта</Typography>
<Typography
sx={{
fontSize: "16px",
color: theme.palette.grey2.main,
textAlign: "start",
}}
>
Задайте свои размеры и встройте в сайт
</Typography>
</ButtonBase>
) : (
<>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
flexDirection: "column",
}}
>
<Box
sx={{
maxWidth: "520px",
width: "100%",
display: "flex",
flexDirection: "column",
gap: "20px",
Способ установки
</InstallationStepButton>
{widgetSetupSettings.step !== 1 && (
<>
<InstallationStepButton
state={widgetSetupSettings.step === 2 ? "active" : "done"}
index={2}
onClick={() => {
setWidgetSetupSettings((prev) => ({
...prev,
step: 2,
}));
}}
>
<Typography>1. Код вставки quiz</Typography>
<Typography sx={{ color: theme.palette.grey2.main }}>
Установите код в то место, где должен быть quiz
</Typography>
<TextField
id="outlined-multiline-static"
multiline
rows={9}
value={`<div id="idpena"></div> <script type="module"> import widget from "https://${isTestServer ? "s." : ""}hbpn.link/export/pub.js"; widget.create({ selector: "idpena", quizId: "${quiz.qid}" }) </script>`}
sx={{
"& .MuiInputBase-root": {
maxWidth: "520px",
width: "100%",
backgroundColor: theme.palette.background.default,
fontSize: "18px",
alignItems: "flex-start",
},
}}
InputProps={{
endAdornment: (
<InputAdornment position="start">
<IconButton
edge="end"
sx={{ marginTop: "22px" }}
onClick={() =>
navigator.clipboard.writeText(
// TODO
document.getElementById(
"outlined-multiline-static",
).value,
)
}
>
<CopyIcon
color={"#ffffff"}
bgcolor={theme.palette.brightPurple.main}
/>
</IconButton>
</InputAdornment>
),
}}
/>
</Box>
</Box>
<Box
sx={{
background: "#EEE4FC",
border: "1px solid #7E2AEA",
padding: "20px 50px 20px 20px",
borderRadius: "8px",
display: "flex",
flexDirection: "column",
gap: "20px",
marginBottom: "20px",
}}
>
<Typography>
Код нужно вставить один раз. Изменения в самом quiz будут
отображаться автоматически после сохранения.
</Typography>
<Typography>
Для установки размера добавьте в тег значения высоты и ширины,
например:
</Typography>
<Typography>
{`<div id="idpena" style="width: 600px;height: 600px;"></div>`}
</Typography>
</Box>
</>
)}
Настройка кнопки
</InstallationStepButton>
<InstallationStepButton
state={widgetSetupSettings.step === 3 ? "active" : "inactive"}
index={3}
onClick={() => {
setWidgetSetupSettings((prev) => ({
...prev,
step: 3,
}));
}}
>
Вставить код на сайт
</InstallationStepButton>
</>
)}
</Box>
</Box>
{widgetSetupSettings.step === 1 ? (
<Box
sx={{
px: "12px",
pt: "4px",
pb: "14px",
display: "flex",
flexDirection: isSmallMonitor ? "column" : "row",
flexWrap: "wrap",
gap: "14px",
}}
>
<WidgetTypeButton
image={<ButtonWidgetPreview />}
text1="По кнопке"
text2="Конструктор кнопки или собственная кнопка"
onClick={() => {
setWidgetSetupSettings({
step: 2,
widgetType: "button",
});
}}
/>
<WidgetTypeButton
image={<BannerWidgetPreview />}
text1="Баннером"
text2="Сбоку или на всю ширину экрана"
onClick={() => {
setWidgetSetupSettings({
step: 2,
widgetType: "banner",
});
}}
/>
<WidgetTypeButton
image={<ContainerWidgetPreview />}
text1="В тело сайта"
text2="Задайте свои размеры и встройте в сайт"
onClick={() => {
setWidgetSetupSettings({
step: 2,
widgetType: "container",
});
}}
/>
<WidgetTypeButton
image={<PopupWidgetPreview />}
text1="Автооткрытие"
text2="Автооткрытие поп-ап на сайте"
onClick={() => {
setWidgetSetupSettings({
step: 2,
widgetType: "popup",
});
}}
/>
<WidgetTypeButton
image={<SideWidgetPreview />}
text1="Виджет"
text2="Сбоку страницы, как консультант"
onClick={() => {
setWidgetSetupSettings({
step: 2,
widgetType: "side",
});
}}
/>
</Box>
) : (
<>
{widgetSetupSettings.widgetType === "container" && (
<ContainerWidgetSetup
step={widgetSetupSettings.step}
nextButton={nextButton}
/>
)}
{widgetSetupSettings.widgetType === "button" && (
<ButtonWidgetSetup
step={widgetSetupSettings.step}
nextButton={nextButton}
/>
)}
{widgetSetupSettings.widgetType === "banner" && (
<BannerWidgetSetup
step={widgetSetupSettings.step}
nextButton={nextButton}
/>
)}
{widgetSetupSettings.widgetType === "popup" && (
<PopupWidgetSetup
step={widgetSetupSettings.step}
nextButton={nextButton}
/>
)}
{widgetSetupSettings.widgetType === "side" && (
<SideWidgetSetup
step={widgetSetupSettings.step}
nextButton={nextButton}
/>
)}
</>
)}
</Box>
);
}

@ -0,0 +1,65 @@
import CopyIcon from "@icons/CopyIcon";
import {
Box,
IconButton,
InputAdornment,
TextField as MuiTextField,
TextFieldProps,
Typography,
useTheme,
} from "@mui/material";
import { FC } from "react";
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
interface Props {
scriptText: string;
helperText: string;
}
export default function WidgetScript({ scriptText, helperText }: Props) {
const theme = useTheme();
return (
<Box
sx={{
p: "20px",
}}
>
<Typography color="#9A9AAF">{helperText}</Typography>
<TextField
multiline
value={scriptText}
sx={{
mt: "16px",
width: "100%",
"& .MuiInputBase-root": {
width: "100%",
backgroundColor: theme.palette.background.default,
fontSize: "16px",
color: "#4D4D4D",
alignItems: "flex-start",
},
}}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={() => {
navigator.clipboard.writeText(scriptText);
}}
edge="end"
sx={{ marginTop: "22px" }}
>
<CopyIcon
color={"#ffffff"}
bgcolor={theme.palette.brightPurple.main}
/>
</IconButton>
</InputAdornment>
),
}}
/>
</Box>
);
}

@ -0,0 +1,56 @@
import { Box, SxProps, Theme } from "@mui/material";
interface Props {
sx?: SxProps<Theme>;
}
export default function BannerWidgetPreviewIcon({ sx = [] }: Props) {
return (
<Box
sx={[
{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
"& svg": {
width: "100%",
height: "100%",
},
},
...(Array.isArray(sx) ? sx : [sx]),
]}
>
<svg viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M13.2979 2.94922H15.4949C15.6488 2.94922 15.7964 3.01036 15.9052 3.11919C16.0141 3.22802 16.0752 3.37563 16.0752 3.52954V6.77848M7.21163 2.94922H5.04907C4.89516 2.94922 4.74755 3.01036 4.63872 3.11919C4.52989 3.22802 4.46875 3.37563 4.46875 3.52954V15.7163C4.46875 15.8702 4.52989 16.0178 4.63872 16.1267C4.74755 16.2355 4.89516 16.2966 5.04907 16.2966H8.53802M7.95068 16.2966H15.4949C15.6488 16.2966 15.7964 16.2355 15.9052 16.1267C16.0141 16.0178 16.0752 15.8702 16.0752 15.7163V11.9923"
stroke="white"
strokeWidth="0.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M9.40182 13.7891H7.65735C7.58039 13.7891 7.50659 13.762 7.45217 13.7139C7.39776 13.6659 7.36719 13.6006 7.36719 13.5326V8.14708C7.36719 8.07906 7.39776 8.01383 7.45217 7.96574C7.50659 7.91764 7.58039 7.89062 7.65735 7.89062H9.10815H12.8802C12.9572 7.89062 13.031 7.91764 13.0854 7.96574C13.1398 8.01383 13.1704 8.07906 13.1704 8.14708V9.58283"
stroke="white"
strokeWidth="0.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.36719 1.8125H13.1704V3.39705C13.1704 3.71756 12.9106 3.97737 12.5901 3.97737H7.94751C7.62701 3.97737 7.36719 3.71756 7.36719 3.39705V1.8125Z"
stroke="white"
strokeWidth="0.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M17.0844 8.36719L11.8615 13.5901L9.25 10.9786"
stroke="white"
strokeWidth="0.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

@ -0,0 +1,582 @@
import { BannerWidgetParams } from "@frontend/squzanswerer";
import CloseIcon from "@mui/icons-material/Close";
import {
Box,
Button,
Collapse,
Divider,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useCurrentQuiz } from "@root/quizes/hooks";
import CircleColorPicker from "@ui_kit/CircleColorPicker";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import PenaTextField from "@ui_kit/PenaTextField";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
import RunningStripe from "@ui_kit/RunningStripe";
import { ReactNode, useState } from "react";
import fixedButtonWidgetPreview from "../../../../../assets/banner-widget-preview.png";
import WidgetScript from "../../WidgetScript";
import { createBannerWidgetScriptText } from "../../createWidgetScriptText";
import BannerWidgetPreviewIcon from "./BannerWidgetPreviewIcon";
interface Props {
step: 2 | 3;
nextButton: ReactNode;
}
export default function BannerWidgetSetup({ step, nextButton }: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const quiz = useCurrentQuiz();
const [widgetWidth, setWidgetWidth] = useState<string>("");
const [widgetHeight, setWidgetHeight] = useState<string>("");
const [isAutoopenQuizSettingsOpen, setIsAutoopenQuizSettingsOpen] =
useState<boolean>(false);
const [hideOnMobile, setHideOnMobile] = useState<boolean>(false);
const [rounded, setRounded] = useState<boolean>(false);
const [withShadow, setWithShadow] = useState<boolean>(false);
const [buttonFlash, setButtonFlash] = useState<boolean>(false);
const [buttonBackgroundColor, setButtonBackgroundColor] = useState<string>(
theme.palette.brightPurple.main,
);
const [buttonTextColor, setButtonTextColor] = useState<string>("#FFFFFF");
const [autoShowQuiz, setAutoShowQuiz] = useState<boolean>(false);
const [autoShowQuizTime, setAutoShowQuizTime] = useState<number>(10);
const [openOnLeaveAttempt, setOpenOnLeaveAttempt] = useState<boolean>(false);
const [position, setPosition] =
useState<BannerWidgetParams["position"]>("bottomleft");
const [bannerFullWidth, setBannerFullWidth] = useState<boolean>(false);
const [autoShowWidgetTime, setAutoShowWidgetTime] = useState<number>(10);
const [appealText, setAppealText] = useState<string>("");
const [quizHeaderText, setQuizHeaderText] = useState<string>("");
const [pulsation, setPulsation] = useState<boolean>(false);
const [fullScreen, setFullScreen] = useState<boolean>(false);
if (!quiz) return null;
if (step === 3) {
const scriptText = createBannerWidgetScriptText({
quizId: quiz.qid,
position,
hideOnMobile: hideOnMobile || undefined,
rounded: bannerFullWidth ? undefined : rounded || undefined,
withShadow: withShadow || undefined,
buttonFlash: buttonFlash || undefined,
buttonBackgroundColor,
buttonTextColor,
autoShowQuizTime: autoShowQuiz ? autoShowQuizTime : undefined,
openOnLeaveAttempt: openOnLeaveAttempt || undefined,
bannerFullWidth: bannerFullWidth || undefined,
autoShowWidgetTime,
appealText: appealText || undefined,
quizHeaderText: quizHeaderText || undefined,
pulsation: pulsation || undefined,
fullScreen: fullScreen || undefined,
dialogDimensions:
!fullScreen && (widgetWidth || widgetHeight)
? {
width: widgetWidth ? `${widgetWidth}px` : "100%",
height: widgetHeight ? `${widgetHeight}px` : "100%",
}
: undefined,
});
return (
<WidgetScript
scriptText={scriptText}
helperText="Установите код внутрь тэга head"
/>
);
}
return (
<Box
sx={{
display: "flex",
flexDirection: isSmallMonitor ? "column" : "row",
gap: "40px",
p: "20px",
}}
>
<Box
sx={{
flex: "1 1 0",
p: "20px",
pt: "40px",
}}
>
<Box
sx={{
position: "relative",
}}
>
<img
src={fixedButtonWidgetPreview}
style={{
display: "block",
width: "100%",
height: "100%",
}}
/>
<Box
sx={[
{
position: "absolute",
top: "calc(12.5 / 262 * 100%)",
left: "calc(50.6 / 520 * 100%)",
width: "calc(196 / 520 * 100%)",
height: "calc(30 / 262 * 100%)",
},
bannerFullWidth && {
top: "calc(7 / 262 * 100%)",
left: "calc(45 / 520 * 100%)",
width: "calc(348 / 520 * 100%)",
height: "calc(30 / 262 * 100%)",
},
pulsation && {
":before": {
content: "''",
position: "absolute",
height: "100%",
width: "100%",
pointerEvents: "none",
willChange: "box-shadow",
borderRadius: "30px",
animation: "pena-pulsation linear 5s infinite",
"@keyframes pena-pulsation": {
"0%": {
boxShadow: "0 0 0 0 rgba(126, 42, 234, 0.5)",
},
"30%": {
boxShadow: "0 0 0 15px rgba(0, 0, 0, 0)",
},
"100%": {
boxShadow: "0 0 0 0 rgba(0, 0, 0, 0)",
},
},
},
},
]}
>
<Box
sx={{
width: "100%",
height: "100%",
overflow: "hidden",
display: "flex",
alignItems: "center",
backgroundColor: theme.palette.brightPurple.main,
containerType: "size",
gap: "calc(5 / 196 * 100%)",
borderRadius: rounded && !bannerFullWidth ? "30px" : 0,
boxShadow: withShadow
? "3px 6px 25px 3px rgba(25, 6, 50, 0.4), 0 3px 13px 0 rgba(35, 17, 58, 0.1)"
: "none",
}}
>
<BannerWidgetPreviewIcon
sx={{
height: "calc(18.57 / 29.5 * 100%)",
width: "auto",
aspectRatio: 1,
ml: "calc(7.36 / 196 * 100%)",
}}
/>
<Box
sx={{
display: "flex",
flexDirection: "column",
color: "white",
}}
>
<Typography
fontSize="calc(6 / 29.5 * 100cqh)"
lineHeight="120%"
>
{appealText || "Пройти тест"}
</Typography>
<Typography
fontSize="calc(11 / 29.5 * 100cqh)"
lineHeight="120%"
>
{quizHeaderText || "Заголовок теста"}
</Typography>
</Box>
{buttonFlash && <RunningStripe />}
</Box>
<CloseIcon
sx={{
position: "absolute",
top: 0,
right: 0,
color: "white",
height: "calc(10 / 29.5 * 100%)",
width: "auto",
aspectRatio: 1,
backgroundColor:
rounded || bannerFullWidth ? "#581CA7" : undefined,
borderRadius: "50%",
}}
/>
</Box>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
flex: "1 1 0",
}}
>
<CustomCheckbox
label="Квиз на весь экран"
checked={fullScreen}
handleChange={(e) => setFullScreen(e.target.checked)}
/>
<Collapse in={!fullScreen}>
<Box
sx={{
display: "flex",
gap: "40px",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "20px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Ширина окна (px)
</Typography>
<PenaTextField
type="number"
value={widgetWidth}
onChange={(e) => setWidgetWidth(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "20px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Высота окна (px)
</Typography>
<PenaTextField
type="number"
value={widgetHeight}
onChange={(e) => setWidgetHeight(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
</Box>
</Collapse>
<Typography sx={{ color: theme.palette.grey2.main }}>
Текст-призыв
</Typography>
<PenaTextField
placeholder="Пройти тест"
value={appealText}
onChange={(e) => setAppealText(e.target.value)}
FormControlSx={{ maxWidth: "360px" }}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Заголовок quiz
</Typography>
<PenaTextField
placeholder="Заголовок quiz"
value={quizHeaderText}
onChange={(e) => setQuizHeaderText(e.target.value)}
FormControlSx={{ maxWidth: "360px" }}
/>
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<PenaTextField
type="number"
value={autoShowWidgetTime}
onChange={(e) => setAutoShowWidgetTime(parseInt(e.target.value))}
FormControlSx={{
width: "90px",
}}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
<Box
sx={{
display: "flex",
gap: "40px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет кнопки
</Typography>
<CircleColorPicker
color={buttonBackgroundColor}
onChange={(color) => setButtonBackgroundColor(color)}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет текста кнопки
</Typography>
<CircleColorPicker
color={buttonTextColor}
onChange={(color) => setButtonTextColor(color)}
/>
</Box>
<CustomCheckbox
label="Баннер на всю ширину экрана"
checked={bannerFullWidth}
handleChange={(e) => {
setBannerFullWidth(e.target.checked);
if (e.target.checked) setPosition("topleft");
}}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Расположение
</Typography>
<FormControl>
<RadioGroup
value={position}
onChange={(e) => {
setPosition(e.target.value as BannerWidgetParams["position"]);
}}
sx={{
display: "flex",
flexWrap: "wrap",
flexDirection: "row",
paddingLeft: "5px",
justifyContent: "space-between",
}}
>
{bannerFullWidth ? (
<Box
sx={{
display: "flex",
flexDirection: "row",
gap: "24px",
}}
>
<FormControlLabel
label="Сверху страницы"
value="topleft"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
sx={{ color: theme.palette.grey2.main }}
/>
<FormControlLabel
label="Снизу страницы"
value="bottomleft"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
sx={{ color: theme.palette.grey2.main }}
/>
</Box>
) : (
<>
<Box
sx={{
display: "flex",
flexDirection: "column",
}}
>
<FormControlLabel
label="Слева сверху"
value="topleft"
control={
<Radio
checkedIcon={<RadioCheck />}
icon={<RadioIcon />}
/>
}
sx={{ color: theme.palette.grey2.main }}
/>
<FormControlLabel
label="Справа сверху"
value="topright"
control={
<Radio
checkedIcon={<RadioCheck />}
icon={<RadioIcon />}
/>
}
sx={{ color: theme.palette.grey2.main }}
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
}}
>
<FormControlLabel
label="Слева снизу"
value="bottomleft"
control={
<Radio
checkedIcon={<RadioCheck />}
icon={<RadioIcon />}
/>
}
sx={{ color: theme.palette.grey2.main }}
/>
<FormControlLabel
label="Справа снизу"
value="bottomright"
control={
<Radio
checkedIcon={<RadioCheck />}
icon={<RadioIcon />}
/>
}
sx={{ color: theme.palette.grey2.main }}
/>
</Box>
</>
)}
</RadioGroup>
</FormControl>
<Typography sx={{ color: theme.palette.grey2.main }}>
Параметры
</Typography>
{!bannerFullWidth && (
<CustomCheckbox
label="Закругленная"
checked={rounded}
handleChange={(e) => setRounded(e.target.checked)}
/>
)}
<CustomCheckbox
label="С тенью"
checked={withShadow}
handleChange={(e) => setWithShadow(e.target.checked)}
/>
<CustomCheckbox
label="С бликом"
checked={buttonFlash}
handleChange={(e) => setButtonFlash(e.target.checked)}
/>
<CustomCheckbox
label={'Эффект "пульсация"'}
checked={pulsation}
handleChange={(e) => setPulsation(e.target.checked)}
/>
<CustomCheckbox
label="Отключить на мобильных устройствах"
checked={hideOnMobile}
handleChange={(e) => setHideOnMobile(e.target.checked)}
/>
<Button
variant="text"
onClick={() => setIsAutoopenQuizSettingsOpen((p) => !p)}
sx={{
fontSize: "16px",
lineHeight: "19px",
color: theme.palette.brightPurple.main,
textDecorationColor: theme.palette.brightPurple.main,
textDecoration: "underline",
textAlign: "left",
alignSelf: "start",
px: 0,
}}
>
+ Автооткрытие quiz
</Button>
<Collapse in={isAutoopenQuizSettingsOpen}>
<Box
sx={{
p: "20px",
backgroundColor: "#F2F3F7",
borderRadius: "8px",
}}
>
<CustomCheckbox
label="Автооткрытие квиза"
checked={autoShowQuiz}
handleChange={(e) => setAutoShowQuiz(e.target.checked)}
/>
<CustomCheckbox
label="Открывать квиз при попытке уйти с сайта"
checked={openOnLeaveAttempt}
handleChange={(e) => setOpenOnLeaveAttempt(e.target.checked)}
sx={{
mt: "15px",
}}
/>
<Collapse in={autoShowQuiz}>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
pt: "15px",
}}
>
<Divider color="#9A9AAF" />
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<PenaTextField
type="number"
value={autoShowQuizTime}
onChange={(e) =>
setAutoShowQuizTime(parseInt(e.target.value))
}
FormControlSx={{
width: "90px",
}}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
</Box>
</Collapse>
</Box>
</Collapse>
<Box
sx={{
display: "flex",
justifyContent: "end",
pt: "10px",
}}
>
{nextButton}
</Box>
</Box>
</Box>
);
}

@ -0,0 +1,436 @@
import {
ButtonWidgetFixedParams,
ButtonWidgetParams,
} from "@frontend/squzanswerer";
import {
Box,
Button,
Collapse,
Divider,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useCurrentQuiz } from "@root/quizes/hooks";
import CircleColorPicker from "@ui_kit/CircleColorPicker";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import PenaTextField from "@ui_kit/PenaTextField";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
import RunningStripe from "@ui_kit/RunningStripe";
import { nanoid } from "nanoid";
import { ReactNode, useState } from "react";
import Dots from "../../../../assets/dots.png";
import fixedButtonWidgetPreview from "../../../../assets/fixed-button-widget-preview.png";
import WidgetScript from "../WidgetScript";
import { createButtonWidgetScriptText } from "../createWidgetScriptText";
interface Props {
step: 2 | 3;
nextButton: ReactNode;
}
export default function ButtonWidgetSetup({ step, nextButton }: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const quiz = useCurrentQuiz();
const [widgetWidth, setWidgetWidth] = useState<string>("");
const [widgetHeight, setWidgetHeight] = useState<string>("");
const [isAutoopenQuizSettingsOpen, setIsAutoopenQuizSettingsOpen] =
useState<boolean>(false);
const [hideOnMobile, setHideOnMobile] = useState<boolean>(false);
const [rounded, setRounded] = useState<boolean>(false);
const [withShadow, setWithShadow] = useState<boolean>(false);
const [buttonFlash, setButtonFlash] = useState<boolean>(false);
const [buttonText, setButtonText] = useState<string>("");
const [buttonBackgroundColor, setButtonBackgroundColor] = useState<string>(
theme.palette.brightPurple.main,
);
const [buttonTextColor, setButtonTextColor] = useState<string>("#FFFFFF");
const [autoShowQuiz, setAutoShowQuiz] = useState<boolean>(false);
const [autoShowQuizTime, setAutoShowQuizTime] = useState<number>(10);
const [openOnLeaveAttempt, setOpenOnLeaveAttempt] = useState<boolean>(false);
const [fixedSide, setFixedSide] = useState<
null | ButtonWidgetFixedParams["fixedSide"]
>(null);
const [fullScreen, setFullScreen] = useState<boolean>(false);
if (!quiz) return null;
if (step === 3) {
const genericParams = {
quizId: quiz.qid,
fullScreen: fullScreen || undefined,
dialogDimensions:
!fullScreen && (widgetWidth || widgetHeight)
? {
width: widgetWidth ? `${widgetWidth}px` : "100%",
height: widgetHeight ? `${widgetHeight}px` : "100%",
}
: undefined,
hideOnMobile: hideOnMobile || undefined,
rounded: rounded || undefined,
withShadow: withShadow || undefined,
buttonFlash: buttonFlash || undefined,
buttonText: buttonText || undefined,
buttonBackgroundColor,
buttonTextColor,
autoShowQuizTime: autoShowQuiz ? autoShowQuizTime : undefined,
openOnLeaveAttempt: openOnLeaveAttempt || undefined,
};
let params: ButtonWidgetParams | ButtonWidgetFixedParams;
if (fixedSide === null) {
params = {
...genericParams,
selector: `#pena-quiz-button-container-${nanoid(6)}`,
};
} else {
params = {
...genericParams,
fixedSide,
};
}
const scriptText = createButtonWidgetScriptText(params);
return (
<WidgetScript
scriptText={scriptText}
helperText={
fixedSide === null
? "Установите код в место, в котором должна быть кнопка"
: "Установите код внутрь тэга head"
}
/>
);
}
return (
<Box
sx={{
display: "flex",
flexDirection: isSmallMonitor ? "column" : "row",
gap: "40px",
p: "20px",
}}
>
<Box
sx={{
flex: "1 1 0",
}}
>
{fixedSide === null ? (
<Box
sx={{
border: "1.5px solid #9A9AAF",
borderRadius: "6px",
height: "364px",
display: "flex",
flexDirection: "column",
}}
>
<Box
sx={{
background: theme.palette.background.default,
borderBottom: "1.5px solid #9A9AAF",
borderRadius: "6px 6px 0px 0px",
minHeight: "34px",
display: "flex",
alignItems: "center",
justifyContent: "end",
paddingRight: "10px",
}}
>
{" "}
<img src={Dots} />
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
flexGrow: 1,
}}
>
<Button
variant="contained"
disableFocusRipple
sx={{
overflow: "hidden",
py: "23px",
px: "40px",
fontSize: "20px",
color: buttonTextColor,
backgroundColor: buttonBackgroundColor,
boxShadow: withShadow
? "2px 5px 20px 2px rgba(25, 6, 50, 0.4), 0 2px 10px 0 rgba(35, 17, 58, 0.1)"
: "none",
borderRadius: rounded ? "30px" : "0px",
transition: "all 0.3s ease",
}}
>
{buttonText || "Пройти тест"}
{buttonFlash && <RunningStripe />}
</Button>
</Box>
</Box>
) : (
<Box
sx={{
p: "20px",
pt: "40px",
}}
>
<img
src={fixedButtonWidgetPreview}
style={{
display: "block",
width: "100%",
height: "100%",
}}
/>
</Box>
)}
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
flex: "1 1 0",
}}
>
<Typography fontWeight={500} color="#4D4D4D">
1. Задайте размеры окна квиза (опционально)
</Typography>
<CustomCheckbox
label="Квиз на весь экран"
checked={fullScreen}
handleChange={(e) => setFullScreen(e.target.checked)}
/>
<Collapse in={!fullScreen}>
<Box sx={{ display: "flex", gap: "40px" }}>
<Box sx={{ display: "flex", flexDirection: "column", gap: "20px" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Ширина (px)
</Typography>
<PenaTextField
type="number"
value={widgetWidth}
onChange={(e) => setWidgetWidth(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
<Box sx={{ display: "flex", flexDirection: "column", gap: "20px" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Высота (px)
</Typography>
<PenaTextField
type="number"
value={widgetHeight}
onChange={(e) => setWidgetHeight(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
</Box>
</Collapse>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
alignItems: "start",
}}
>
<Typography fontWeight={500} color="#4D4D4D">
2. Конструктор кнопки
</Typography>
<Box
sx={{
display: "flex",
gap: "40px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет кнопки
</Typography>
<CircleColorPicker
color={buttonBackgroundColor}
onChange={(color) => setButtonBackgroundColor(color)}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет текста кнопки
</Typography>
<CircleColorPicker
color={buttonTextColor}
onChange={(color) => setButtonTextColor(color)}
/>
</Box>
<CustomCheckbox
label="Отключить на мобильных устройствах"
checked={hideOnMobile}
handleChange={(e) => setHideOnMobile(e.target.checked)}
/>
<CustomCheckbox
label="Закругленная"
checked={rounded}
handleChange={(e) => setRounded(e.target.checked)}
/>
<CustomCheckbox
label="С тенью"
checked={withShadow}
handleChange={(e) => setWithShadow(e.target.checked)}
/>
<CustomCheckbox
label="С бликом"
checked={buttonFlash}
handleChange={(e) => setButtonFlash(e.target.checked)}
/>
<CustomCheckbox
label="Фиксированная"
checked={fixedSide !== null}
handleChange={(e) => setFixedSide(e.target.checked ? "left" : null)}
/>
{fixedSide && (
<FormControl>
<RadioGroup
sx={{
flexDirection: "row",
gap: "24px",
pl: "40px",
}}
value={fixedSide}
onChange={(e) => {
setFixedSide(
e.target.value as ButtonWidgetFixedParams["fixedSide"],
);
}}
>
<FormControlLabel
value="left"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Слева"
sx={{
color: theme.palette.grey2.main,
}}
/>
<FormControlLabel
value="right"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Справа"
sx={{
color: theme.palette.grey2.main,
}}
/>
</RadioGroup>
</FormControl>
)}
<Typography sx={{ color: theme.palette.grey2.main }}>
Текст кнопки
</Typography>
<PenaTextField
value={buttonText}
onChange={(e) => setButtonText(e.target.value)}
placeholder="Пройти тест"
FormControlSx={{ maxWidth: "360px" }}
/>
<Button
variant="text"
onClick={() => setIsAutoopenQuizSettingsOpen((p) => !p)}
sx={{
fontSize: "16px",
lineHeight: "19px",
color: theme.palette.brightPurple.main,
textDecorationColor: theme.palette.brightPurple.main,
textDecoration: "underline",
textAlign: "left",
px: 0,
}}
>
+ Автооткрытие quiz
</Button>
<Collapse in={isAutoopenQuizSettingsOpen}>
<Box
sx={{
p: "20px",
backgroundColor: "#F2F3F7",
borderRadius: "8px",
}}
>
<CustomCheckbox
label="Автооткрытие квиза"
checked={autoShowQuiz}
handleChange={(e) => setAutoShowQuiz(e.target.checked)}
/>
<CustomCheckbox
label="Открывать квиз при попытке уйти с сайта"
checked={openOnLeaveAttempt}
handleChange={(e) => setOpenOnLeaveAttempt(e.target.checked)}
sx={{
mt: "15px",
}}
/>
<Collapse in={autoShowQuiz}>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
pt: "15px",
}}
>
<Divider color="#9A9AAF" />
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<PenaTextField
type="number"
value={autoShowQuizTime}
onChange={(e) =>
setAutoShowQuizTime(parseInt(e.target.value))
}
FormControlSx={{
width: "90px",
}}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
</Box>
</Collapse>
</Box>
</Collapse>
</Box>
<Box
sx={{
display: "flex",
justifyContent: "end",
pt: "10px",
}}
>
{nextButton}
</Box>
</Box>
</Box>
);
}

@ -0,0 +1,344 @@
import {
Box,
Button,
Collapse,
Divider,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useCurrentQuiz } from "@root/quizes/hooks";
import CircleColorPicker from "@ui_kit/CircleColorPicker";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import PenaTextField from "@ui_kit/PenaTextField";
import { nanoid } from "nanoid";
import { ReactNode, useState } from "react";
import Dots from "../../../../assets/dots.png";
import WidgetScript from "../WidgetScript";
import { createContainerWidgetScriptText } from "../createWidgetScriptText";
interface Props {
step: 2 | 3;
nextButton: ReactNode;
}
export default function ContainerWidgetSetup({ step, nextButton }: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const quiz = useCurrentQuiz();
const [isAutoopenQuizSettingsOpen, setIsAutoopenQuizSettingsOpen] =
useState<boolean>(false);
const [widgetWidth, setWidgetWidth] = useState<string>("");
const [widgetHeight, setWidgetHeight] = useState<string>("");
const [hideOnMobile, setHideOnMobile] = useState<boolean>(false);
const [showButtonOnMobile, setShowButtonOnMobile] = useState<boolean>(false);
const [rounded, setRounded] = useState<boolean>(false);
const [withShadow, setWithShadow] = useState<boolean>(false);
const [buttonFlash, setButtonFlash] = useState<boolean>(false);
const [buttonText, setButtonText] = useState<string>("");
const [buttonBackgroundColor, setButtonBackgroundColor] = useState<string>(
theme.palette.brightPurple.main,
);
const [buttonTextColor, setButtonTextColor] = useState<string>("#FFFFFF");
const [autoShowQuiz, setAutoShowQuiz] = useState<boolean>(false);
const [autoShowQuizTime, setAutoShowQuizTime] = useState<number>(10);
const [openOnLeaveAttempt, setOpenOnLeaveAttempt] = useState<boolean>(false);
if (!quiz) return null;
if (step === 3) {
const scriptText = createContainerWidgetScriptText({
quizId: quiz.qid,
selector: `#pena-quiz-container-${nanoid(6)}`,
dimensions:
widgetWidth || widgetHeight
? {
width: widgetWidth ? `${widgetWidth}px` : "100%",
height: widgetHeight ? `${widgetHeight}px` : "100%",
}
: undefined,
hideOnMobile: hideOnMobile || undefined,
showButtonOnMobile: showButtonOnMobile || undefined,
rounded: rounded || undefined,
withShadow: withShadow || undefined,
buttonFlash: buttonFlash || undefined,
buttonText: buttonText || undefined,
buttonBackgroundColor,
buttonTextColor,
autoShowQuizTime: autoShowQuiz ? autoShowQuizTime : undefined,
openOnLeaveAttempt: openOnLeaveAttempt || undefined,
});
return (
<WidgetScript
scriptText={scriptText}
helperText="Установите код в место, в котором должен быть квиз"
/>
);
}
return (
<Box
sx={{
display: "flex",
flexDirection: isSmallMonitor ? "column" : "row",
alignItems: "start",
gap: "40px",
p: "20px",
}}
>
<Box
sx={{
border: "1.5px solid #9A9AAF",
borderRadius: "6px",
flex: "1 1 0",
}}
>
<Box
sx={{
background: theme.palette.background.default,
borderBottom: "1.5px solid #9A9AAF",
borderRadius: "6px 6px 0px 0px",
minHeight: "34px",
display: "flex",
alignItems: "center",
justifyContent: "end",
paddingRight: "10px",
}}
>
{" "}
<img src={Dots} />
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
gap: "40px",
height: "100%",
p: "12%",
pb: "7%",
}}
>
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
background: "#EEE4FC",
borderRadius: "6px",
border: "1px dashed #7E2AEA",
p: "40px",
}}
>
<Typography
sx={{
color: theme.palette.brightPurple.main,
maxWidth: "365px",
}}
>
Quiz будет открыть прямо в том месте, где вы установите код на
сайте
</Typography>
</Box>
<Typography
sx={{ maxWidth: "414px", fontSize: "14px", alignSelf: "start" }}
>
В мобильной версии будет показана кнопка, открывающая quiz в
модальном окне
</Typography>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
flex: "1 1 0",
}}
>
<Typography fontWeight={500} color="#4D4D4D">
1. Задайте размеры (опционально)
</Typography>
<Box sx={{ display: "flex", gap: "40px" }}>
<Box sx={{ display: "flex", flexDirection: "column", gap: "20px" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Ширина (px)
</Typography>
<PenaTextField
type="number"
value={widgetWidth}
onChange={(e) => setWidgetWidth(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
<Box sx={{ display: "flex", flexDirection: "column", gap: "20px" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Высота (px)
</Typography>
<PenaTextField
type="number"
value={widgetHeight}
onChange={(e) => setWidgetHeight(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
alignItems: "start",
}}
>
<Typography fontWeight={500} color="#4D4D4D">
2. Настройте кнопку для мобильной версии
</Typography>
<Box
sx={{
display: "flex",
gap: "40px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет кнопки
</Typography>
<CircleColorPicker
color={buttonBackgroundColor}
onChange={(color) => setButtonBackgroundColor(color)}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет текста кнопки
</Typography>
<CircleColorPicker
color={buttonTextColor}
onChange={(color) => setButtonTextColor(color)}
/>
</Box>
<CustomCheckbox
label="Отключить на мобильных устройствах"
checked={hideOnMobile}
handleChange={(e) => setHideOnMobile(e.target.checked)}
/>
<CustomCheckbox
label="Закругленная"
checked={rounded}
handleChange={(e) => setRounded(e.target.checked)}
/>
<CustomCheckbox
label="С тенью"
checked={withShadow}
handleChange={(e) => setWithShadow(e.target.checked)}
/>
<CustomCheckbox
label="С бликом"
checked={buttonFlash}
handleChange={(e) => setButtonFlash(e.target.checked)}
/>
<CustomCheckbox
label="Кнопка на мобильной версии"
checked={showButtonOnMobile}
handleChange={(e) => setShowButtonOnMobile(e.target.checked)}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Текст кнопки
</Typography>
<PenaTextField
value={buttonText}
onChange={(e) => setButtonText(e.target.value)}
placeholder="Пройти тест"
FormControlSx={{ maxWidth: "360px" }}
/>
<Button
variant="text"
onClick={() => setIsAutoopenQuizSettingsOpen((p) => !p)}
sx={{
fontSize: "16px",
lineHeight: "19px",
color: theme.palette.brightPurple.main,
textDecorationColor: theme.palette.brightPurple.main,
textDecoration: "underline",
textAlign: "left",
px: 0,
}}
>
+ Автооткрытие quiz
</Button>
<Collapse in={isAutoopenQuizSettingsOpen}>
<Box
sx={{
p: "20px",
backgroundColor: "#F2F3F7",
borderRadius: "8px",
}}
>
<CustomCheckbox
label="Автооткрытие квиза"
checked={autoShowQuiz}
handleChange={(e) => setAutoShowQuiz(e.target.checked)}
/>
<CustomCheckbox
label="Открывать квиз при попытке уйти с сайта"
checked={openOnLeaveAttempt}
handleChange={(e) => setOpenOnLeaveAttempt(e.target.checked)}
sx={{
mt: "15px",
}}
/>
<Collapse in={autoShowQuiz}>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
pt: "15px",
}}
>
<Divider color="#9A9AAF" />
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<PenaTextField
type="number"
value={autoShowQuizTime}
onChange={(e) =>
setAutoShowQuizTime(parseInt(e.target.value))
}
FormControlSx={{
width: "90px",
}}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
</Box>
</Collapse>
</Box>
</Collapse>
</Box>
<Box
sx={{
display: "flex",
justifyContent: "end",
pt: "10px",
}}
>
{nextButton}
</Box>
</Box>
</Box>
);
}

@ -0,0 +1,225 @@
import {
Box,
Collapse,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useCurrentQuiz } from "@root/quizes/hooks";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import PenaTextField from "@ui_kit/PenaTextField";
import { ReactNode, useState } from "react";
import Dots from "../../../../assets/dots.png";
import WidgetScript from "../WidgetScript";
import { createPopupWidgetScriptText } from "../createWidgetScriptText";
interface Props {
step: 2 | 3;
nextButton: ReactNode;
}
export default function PopupWidgetSetup({ step, nextButton }: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const quiz = useCurrentQuiz();
const [widgetWidth, setWidgetWidth] = useState<string>("");
const [widgetHeight, setWidgetHeight] = useState<string>("");
const [hideOnMobile, setHideOnMobile] = useState<boolean>(false);
const [autoShowQuizTime, setAutoShowQuizTime] = useState<number>(10);
const [openOnLeaveAttempt, setOpenOnLeaveAttempt] = useState<boolean>(false);
const [fullScreen, setFullScreen] = useState<boolean>(false);
if (!quiz) return null;
if (step === 3) {
const scriptText = createPopupWidgetScriptText({
quizId: quiz.qid,
hideOnMobile: hideOnMobile || undefined,
autoShowQuizTime: autoShowQuizTime,
openOnLeaveAttempt: openOnLeaveAttempt || undefined,
fullScreen: fullScreen || undefined,
dialogDimensions:
!fullScreen && (widgetWidth || widgetHeight)
? {
width: widgetWidth ? `${widgetWidth}px` : "100%",
height: widgetHeight ? `${widgetHeight}px` : "100%",
}
: undefined,
});
return (
<WidgetScript
scriptText={scriptText}
helperText="Установите код внутрь тэга head"
/>
);
}
return (
<Box
sx={{
display: "flex",
flexDirection: isSmallMonitor ? "column" : "row",
gap: "40px",
p: "20px",
}}
>
<Box
sx={{
flex: "1 1 0",
}}
>
<Box
sx={{
border: "1.5px solid #9A9AAF",
borderRadius: "6px",
}}
>
<Box
sx={{
background: theme.palette.background.default,
borderBottom: "1.5px solid #9A9AAF",
borderRadius: "6px 6px 0px 0px",
minHeight: "34px",
display: "flex",
alignItems: "center",
justifyContent: "end",
paddingRight: "10px",
}}
>
{" "}
<img src={Dots} />
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "330px",
p: "24px",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
maxWidth: "506px",
minHeight: "280px",
background: "#EEE4FC",
borderRadius: "6px",
border: "1px dashed #7E2AEA",
px: "40px",
}}
>
<Typography sx={{ color: theme.palette.brightPurple.main }}>
Quiz будет открываться через указанное время
</Typography>
</Box>
</Box>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
flex: "1 1 0",
}}
>
<CustomCheckbox
label="Квиз на весь экран"
checked={fullScreen}
handleChange={(e) => setFullScreen(e.target.checked)}
/>
<Collapse in={!fullScreen}>
<Box
sx={{
display: "flex",
gap: "40px",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "20px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Ширина окна (px)
</Typography>
<PenaTextField
type="number"
value={widgetWidth}
onChange={(e) => setWidgetWidth(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "20px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Высота окна (px)
</Typography>
<PenaTextField
type="number"
value={widgetHeight}
onChange={(e) => setWidgetHeight(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
</Box>
</Collapse>
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<PenaTextField
type="number"
value={autoShowQuizTime}
onChange={(e) => setAutoShowQuizTime(parseInt(e.target.value))}
FormControlSx={{
width: "90px",
}}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
<CustomCheckbox
label="Открывать квиз при попытке уйти с сайта"
checked={openOnLeaveAttempt}
handleChange={(e) => setOpenOnLeaveAttempt(e.target.checked)}
/>
<CustomCheckbox
label="Отключать автооткрытие на мобильных устройствах"
checked={hideOnMobile}
handleChange={(e) => setHideOnMobile(e.target.checked)}
/>
<Box
sx={{
display: "flex",
justifyContent: "end",
pt: "10px",
}}
>
{nextButton}
</Box>
</Box>
</Box>
);
}

@ -0,0 +1,61 @@
import { Button } from "@mui/material";
interface Props {
isSelected: boolean;
position: "left" | "right";
onClick: () => void;
}
export default function SideWidgetPositionButton({
isSelected,
position,
onClick,
}: Props) {
return (
<Button
onClick={onClick}
sx={{
aspectRatio: "204 / 134",
width: "100%",
height: "auto",
borderRadius: "6px",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
color: isSelected ? "#7E2AEA" : "#9A9AAF",
p: 0,
}}
>
<svg viewBox="0 0 204 134" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="currentColor"
strokeWidth="1.5"
/>
<rect
x={position === "left" ? "7.5" : "147.5"}
y="62.5"
width="49"
height="64"
rx="3.5"
fill="currentColor"
stroke="currentColor"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="currentColor"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="currentColor" />
<circle cx="177.5" cy="8" r="2.5" fill="currentColor" />
<circle cx="185.5" cy="8" r="2.5" fill="currentColor" />
</svg>
</Button>
);
}

@ -0,0 +1,315 @@
import { SideWidgetParams } from "@frontend/squzanswerer";
import {
Box,
Collapse,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useCurrentQuiz } from "@root/quizes/hooks";
import CircleColorPicker from "@ui_kit/CircleColorPicker";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import PenaTextField from "@ui_kit/PenaTextField";
import { ReactNode, useState } from "react";
import sideWidgetPreviewLeft from "../../../../../assets/side-widget-preview-left.png";
import sideWidgetPreviewRight from "../../../../../assets/side-widget-preview-right.png";
import WidgetScript from "../../WidgetScript";
import { createSideWidgetScriptText } from "../../createWidgetScriptText";
import SideWidgetPositionButton from "./SideWidgetPositionButton";
interface Props {
step: 2 | 3;
nextButton: ReactNode;
}
export default function SideWidgetSetup({ step, nextButton }: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const quiz = useCurrentQuiz();
const [widgetWidth, setWidgetWidth] = useState<string>("");
const [widgetHeight, setWidgetHeight] = useState<string>("");
const [hideOnMobile, setHideOnMobile] = useState<boolean>(false);
const [autoShowQuiz, setAutoShowQuiz] = useState<boolean>(false);
const [autoShowQuizTime, setAutoShowQuizTime] = useState<number>(10);
const [autoShowWidgetTime, setAutoShowWidgetTime] = useState<number>(10);
const [position, setPosition] =
useState<SideWidgetParams["position"]>("left");
const [fullScreen, setFullScreen] = useState<boolean>(false);
const [buttonFlash, setButtonFlash] = useState<boolean>(false);
const [buttonBackgroundColor, setButtonBackgroundColor] = useState<string>(
theme.palette.brightPurple.main,
);
const [buttonTextColor, setButtonTextColor] = useState<string>("#FFFFFF");
if (!quiz) return null;
if (step === 3) {
const scriptText = createSideWidgetScriptText({
quizId: quiz.qid,
position: position,
hideOnMobile: hideOnMobile || undefined,
autoShowQuizTime: autoShowQuiz ? autoShowQuizTime : undefined,
autoShowWidgetTime: autoShowWidgetTime || undefined,
fullScreen: fullScreen || undefined,
buttonFlash: buttonFlash || undefined,
buttonTextColor,
buttonBackgroundColor,
dialogDimensions:
!fullScreen && (widgetWidth || widgetHeight)
? {
width: widgetWidth ? `${widgetWidth}px` : "100%",
height: widgetHeight ? `${widgetHeight}px` : "100%",
}
: undefined,
});
return (
<WidgetScript
scriptText={scriptText}
helperText="Установите код внутрь тэга head"
/>
);
}
return (
<Box
sx={{
display: "flex",
flexDirection: isSmallMonitor ? "column" : "row",
gap: "40px",
p: "20px",
}}
>
<Box
sx={{
flex: "1 1 0",
}}
>
<Box
sx={{
p: "20px",
pt: "40px",
}}
>
<img
src={
position === "left"
? sideWidgetPreviewLeft
: sideWidgetPreviewRight
}
style={{
display: "block",
width: "100%",
height: "100%",
}}
/>
</Box>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
flex: "1 1 0",
}}
>
<Typography color={theme.palette.grey2.main}>Расположение</Typography>
<Box
sx={{
display: "flex",
gap: "40px",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "14px",
flex: "1 0 0",
}}
>
<SideWidgetPositionButton
isSelected={position === "left"}
position="left"
onClick={() => setPosition("left")}
/>
<Typography color={theme.palette.grey2.main}>
Слева снизу
</Typography>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "14px",
flex: "1 0 0",
}}
>
<SideWidgetPositionButton
isSelected={position === "right"}
position="right"
onClick={() => setPosition("right")}
/>
<Typography color={theme.palette.grey2.main}>
Справа снизу
</Typography>
</Box>
</Box>
<CustomCheckbox
label="Квиз на весь экран"
checked={fullScreen}
handleChange={(e) => setFullScreen(e.target.checked)}
/>
<Collapse in={!fullScreen}>
<Box
sx={{
display: "flex",
gap: "40px",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "20px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Ширина окна (px)
</Typography>
<PenaTextField
type="number"
value={widgetWidth}
onChange={(e) => setWidgetWidth(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "20px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Высота окна (px)
</Typography>
<PenaTextField
type="number"
value={widgetHeight}
onChange={(e) => setWidgetHeight(e.target.value)}
FormControlSx={{ maxWidth: "160px" }}
placeholder="auto"
/>
</Box>
</Box>
</Collapse>
<Box
sx={{
display: "flex",
gap: "40px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет кнопки
</Typography>
<CircleColorPicker
color={buttonBackgroundColor}
onChange={(color) => setButtonBackgroundColor(color)}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет текста кнопки
</Typography>
<CircleColorPicker
color={buttonTextColor}
onChange={(color) => setButtonTextColor(color)}
/>
</Box>
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать кнопку через
</Typography>
<PenaTextField
type="number"
value={autoShowWidgetTime}
onChange={(e) => setAutoShowWidgetTime(parseInt(e.target.value))}
FormControlSx={{
width: "90px",
}}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
<CustomCheckbox
label="С бликом"
checked={buttonFlash}
handleChange={(e) => setButtonFlash(e.target.checked)}
/>
<Box>
<CustomCheckbox
label="Автооткрытие виджета при входе на сайт"
checked={autoShowQuiz}
handleChange={(e) => setAutoShowQuiz(e.target.checked)}
/>
<Collapse in={autoShowQuiz}>
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
mt: "14px",
}}
>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<PenaTextField
type="number"
value={autoShowQuizTime}
onChange={(e) => setAutoShowQuizTime(parseInt(e.target.value))}
FormControlSx={{
width: "90px",
}}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
<Typography
sx={{
color: theme.palette.grey2.main,
fontSize: "14px",
mt: "8px",
}}
>
Время, через которое квиз автоматически откроется
</Typography>
</Collapse>
</Box>
<CustomCheckbox
label="Отключить на мобильных устройствах"
checked={hideOnMobile}
handleChange={(e) => setHideOnMobile(e.target.checked)}
/>
<Box
sx={{
display: "flex",
justifyContent: "end",
pt: "10px",
}}
>
{nextButton}
</Box>
</Box>
</Box>
);
}

@ -0,0 +1,63 @@
import {
Box,
Button,
ButtonBase,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { ReactNode } from "react";
interface Props {
onClick?: () => void;
image: ReactNode;
text1: string;
text2: string;
}
export default function WidgetTypeButton({
onClick,
image,
text1,
text2,
}: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
return (
<Button
onClick={onClick}
sx={{
display: "flex",
flexDirection: isSmallMonitor ? "row" : "column",
alignItems: "start",
justifyContent: "start",
width: isSmallMonitor ? undefined : "min-content",
borderRadius: "8px",
columnGap: "18px",
}}
>
{image}
<Box
sx={{
mt: isSmallMonitor ? undefined : "20px",
display: "flex",
flexDirection: "column",
alignItems: "start",
}}
>
<Typography>{text1}</Typography>
<Typography
sx={{
mt: isSmallMonitor ? "10px" : "15px",
fontSize: "16px",
color: (theme) => theme.palette.grey2.main,
textAlign: "start",
}}
>
{text2}
</Typography>
</Box>
</Button>
);
}

@ -0,0 +1,55 @@
import {
BannerWidgetParams,
ButtonWidgetFixedParams,
ButtonWidgetParams,
ContainerWidgetParams,
PopupWidgetParams,
SideWidgetParams,
WidgetType,
} from "@frontend/squzanswerer";
export function createContainerWidgetScriptText(params: ContainerWidgetParams) {
return `<div id="${params.selector.slice(1)}"></div>
<script type="module">
import { ContainerWidget } from "https://hbpn.link/export/pub.js";
new ContainerWidget(${JSON.stringify(params, null, 4)});
</script>`;
}
export function createButtonWidgetScriptText(
params: ButtonWidgetParams | ButtonWidgetFixedParams,
) {
const widgetClassName =
"fixedSide" in params ? "ButtonWidgetFixed" : "ButtonWidget";
return `${"selector" in params ? `<div id="${params.selector.slice(1)}"></div>\n` : ""}<script type="module">
import { ${widgetClassName} } from "https://hbpn.link/export/pub.js";
new ${widgetClassName}(${JSON.stringify(params, null, 4)});
</script>`;
}
export function createPopupWidgetScriptText(params: PopupWidgetParams) {
return `<script type="module">
import { PopupWidget } from "https://hbpn.link/export/pub.js";
new PopupWidget(${JSON.stringify(params, null, 4)});
</script>`;
}
export function createSideWidgetScriptText(params: SideWidgetParams) {
return `<script type="module">
import { SideWidget } from "https://hbpn.link/export/pub.js";
new SideWidget(${JSON.stringify(params, null, 4)});
</script>`;
}
export function createBannerWidgetScriptText(params: BannerWidgetParams) {
return `<script type="module">
import { BannerWidget } from "https://hbpn.link/export/pub.js";
new BannerWidget(${JSON.stringify(params, null, 4)});
</script>`;
}

@ -1,49 +1,34 @@
import { Box } from "@mui/material";
import WidgetPreviewContainer from "./WidgetPreviewContainer";
export default function BannerWidgetPreview() {
return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
}}
>
<svg
width="204"
height="134"
viewBox="0 0 204 134"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<rect
x="7.5"
y="21.0078"
width="95.0002"
height="19"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</svg>
</Box>
<WidgetPreviewContainer>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<rect
x="7.5"
y="21.0078"
width="95.0002"
height="19"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</WidgetPreviewContainer>
);
}

@ -1,50 +1,35 @@
import { Box } from "@mui/material";
import WidgetPreviewContainer from "./WidgetPreviewContainer";
export default function ButtonWidgetPreview() {
return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
}}
>
<svg
width="204"
height="134"
viewBox="0 0 204 134"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<rect
x="16.5"
y="98.5"
width="64"
height="19"
rx="9.5"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</svg>
</Box>
<WidgetPreviewContainer>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<rect
x="16.5"
y="98.5"
width="64"
height="19"
rx="9.5"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</WidgetPreviewContainer>
);
}

@ -1,50 +1,35 @@
import { Box } from "@mui/material";
import WidgetPreviewContainer from "./WidgetPreviewContainer";
export default function ContainerWidgetPreview() {
return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
}}
>
<svg
width="204"
height="134"
viewBox="0 0 204 134"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<rect
x="22.5"
y="50.5"
width="159"
height="76"
rx="3.5"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</svg>
</Box>
<WidgetPreviewContainer>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<rect
x="22.5"
y="50.5"
width="159"
height="76"
rx="3.5"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</WidgetPreviewContainer>
);
}

@ -1,54 +1,39 @@
import { Box } from "@mui/material";
import WidgetPreviewContainer from "./WidgetPreviewContainer";
export default function PopupWidgetPreview() {
return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
}}
>
<svg
width="204"
height="134"
viewBox="0 0 204 134"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<path
d="M2 14H202V126C202 129.314 199.314 132 196 132H7.99999C4.68629 132 2 129.314 2 126V14Z"
fill="#EEE4FC"
/>
<rect
x="18.5"
y="32.0078"
width="167"
height="83"
rx="5.5"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</svg>
</Box>
<WidgetPreviewContainer>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<path
d="M2 14H202V126C202 129.314 199.314 132 196 132H7.99999C4.68629 132 2 129.314 2 126V14Z"
fill="#EEE4FC"
/>
<rect
x="18.5"
y="32.0078"
width="167"
height="83"
rx="5.5"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</WidgetPreviewContainer>
);
}

@ -1,50 +1,35 @@
import { Box } from "@mui/material";
import WidgetPreviewContainer from "./WidgetPreviewContainer";
export default function SideWidgetPreview() {
return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
}}
>
<svg
width="204"
height="134"
viewBox="0 0 204 134"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<rect
x="147.5"
y="62.5"
width="49"
height="64"
rx="3.5"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</svg>
</Box>
<WidgetPreviewContainer>
<rect
x="1.25"
y="1.25"
width="201.5"
height="131.5"
rx="6.75"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<rect
x="147.5"
y="62.5"
width="49"
height="64"
rx="3.5"
fill="#7E2AEA"
stroke="#7E2AEA"
/>
<path
d="M202 14.75H202.75V14V8C202.75 4.27208 199.728 1.25 196 1.25H8C4.27208 1.25 1.25 4.27208 1.25 8V14V14.75H2H202Z"
fill="#F2F3F7"
stroke="#9A9AAF"
strokeWidth="1.5"
/>
<circle cx="169.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="177.5" cy="8" r="2.5" fill="#9A9AAF" />
<circle cx="185.5" cy="8" r="2.5" fill="#9A9AAF" />
</WidgetPreviewContainer>
);
}

@ -0,0 +1,33 @@
import { Box, useMediaQuery, useTheme } from "@mui/material";
import { ReactNode } from "react";
interface Props {
children?: ReactNode;
}
export default function WidgetPreviewContainer({ children }: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
return (
<Box
sx={{
width: isSmallMonitor ? "90px" : "200px",
aspectRatio: "204 / 134",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
}}
>
<svg
viewBox="0 0 204 134"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ width: "100%" }}
>
{children}
</svg>
</Box>
);
}

@ -1,219 +0,0 @@
import {
Box,
Button,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
TextField,
Typography,
useTheme,
} from "@mui/material";
import React, { useState } from "react";
import InstallQzOnSiteParent from "./InstallQzOnSiteParent";
import CustomCheckbox from "@ui_kit/CustomCheckbox";
import VidjetImg from "../../assets/VidjetImg.png";
import LDownButton from "@icons/InstallQuizIcon/LDownButton";
import RDownButton from "@icons/InstallQuizIcon/RDownButton";
import RadioCheck from "@ui_kit/RadioCheck";
import RadioIcon from "@ui_kit/RadioIcon";
export default function VidjetInstall() {
const [value, setValue] = React.useState("1");
const handleChangeRadio = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue((event.target as HTMLInputElement).value);
};
const [position, setPosition] = useState<"left" | "right">("left");
const theme = useTheme();
return (
<InstallQzOnSiteParent>
<Box sx={{ maxWidth: "560px" }}>
<img src={VidjetImg} />
<Typography sx={{ fontSize: "14px", color: theme.palette.grey2.main }}>
Если quiz уже установлен на сайт, и вы что-то здесь изменили, код на
сайте нужно будет поменять. Настройки в этом конструкторе не
сохраняются.
</Typography>
</Box>
<Box sx={{ display: "flex", flexDirection: "column", gap: "15px" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Расположение
</Typography>
<Box sx={{ display: "flex" }}>
<SelectButtonPosition
onClick={() => setPosition("left")}
isActive={position === "left"}
Icon={LDownButton}
/>
<SelectButtonPosition
onClick={() => setPosition("right")}
isActive={position === "right"}
Icon={RDownButton}
/>
</Box>
<Box sx={{ display: "flex", gap: "10px", alignItems: "center" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать виджет
</Typography>
<FormControl
fullWidth
variant="standard"
sx={{ p: 0, width: "60px" }}
>
<TextField
fullWidth
placeholder="10"
sx={{
"& .MuiInputBase-root": {
backgroundColor: theme.palette.background.default,
height: "48px",
width: "60px",
borderRadius: "10px",
},
}}
inputProps={{
sx: {
borderRadius: "10px",
fontSize: "18px",
lineHeight: "21px",
py: 0,
},
}}
/>
</FormControl>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
<Typography sx={{ color: theme.palette.grey2.main }}>
Автооткрытие виджета при входе на сайт
</Typography>
<FormControl>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChangeRadio}
sx={{
maxWidth: "365px",
display: "flex",
gap: "40px",
flexDirection: "row",
}}
>
<FormControlLabel
sx={{ color: theme.palette.grey2.main }}
value="1"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Да"
/>
<FormControlLabel
sx={{ color: theme.palette.grey2.main }}
value="2"
control={
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
}
label="Нет"
/>
</RadioGroup>
</FormControl>
{value === "1" ? (
<Box>
<Box sx={{ display: "flex", gap: "10px", alignItems: "center" }}>
<Typography sx={{ color: theme.palette.grey2.main }}>
Показывать через
</Typography>
<FormControl
fullWidth
variant="standard"
sx={{ p: 0, width: "60px" }}
>
<TextField
fullWidth
placeholder="10"
sx={{
"& .MuiInputBase-root": {
backgroundColor: theme.palette.background.default,
height: "48px",
width: "60px",
borderRadius: "10px",
},
}}
inputProps={{
sx: {
borderRadius: "10px",
fontSize: "18px",
lineHeight: "21px",
py: 0,
},
}}
/>
</FormControl>
<Typography sx={{ color: theme.palette.grey2.main }}>
секунд
</Typography>
</Box>
<Typography
sx={{ color: theme.palette.grey2.main, fontSize: "14px" }}
>
Время, через которое quiz автоматически откроется
</Typography>
</Box>
) : (
<></>
)}
<CustomCheckbox label={"Отключить на мобильных устройствах"} />
<CustomCheckbox label={"Полностью скрывать виджет при закрытии"} />
</Box>
</InstallQzOnSiteParent>
);
}
interface Props {
Icon: React.ElementType;
isActive?: boolean;
onClick: () => void;
}
function SelectButtonPosition({ Icon, isActive = false, onClick }: Props) {
const theme = useTheme();
return (
<Button
onClick={onClick}
variant="text"
startIcon={
<Icon
color={
isActive
? theme.palette.brightPurple.main
: theme.palette.grey2.main
}
/>
}
sx={{
borderRadius: "6px",
border: "none",
p: "7px",
width: "auto",
minWidth: 0,
"& .MuiButton-startIcon": {
mr: 0,
ml: 0,
},
"&:hover": {
border: "none",
borderColor: isActive
? theme.palette.brightPurple.main
: theme.palette.grey2.main,
},
}}
/>
);
}

@ -2,10 +2,6 @@ import React from "react";
import Box from "@mui/material/Box";
import { Typography, useMediaQuery, useTheme } from "@mui/material";
import SectionStyled from "./SectionStyled";
import OneIconBorder from "@icons/OneIconBorder";
import TwoIcon from "./images/icons/IconNumber2";
import ThreeIcon from "./images/icons/IconNumber3";
import FourIcon from "./images/icons/IconNumber4";
import Firstblock1 from "./images/firstblock1.png";
import Firstblock2 from "./images/firstblock2.png";
import Firstblock3 from "./images/firstblock3.png";
@ -28,6 +24,7 @@ import Icon16 from "./images/icons/Group149";
import Icon17 from "./images/icons/Group151";
import Icon19 from "./images/icons/Group153";
import Icon21 from "./images/icons/Network";
import NumberIcon from "@icons/NumberIcon";
export default function HowItWorks() {
const theme = useTheme();
@ -88,19 +85,7 @@ export default function HowItWorks() {
boxSizing: "border-box",
}}
>
<Box
sx={{
height: "36px",
width: "36px",
justifyContent: "center",
alignItems: "center",
display: "flex",
borderRadius: "6px",
background: "#EEE4FC",
}}
>
<OneIconBorder color={"#7E2AEA"} />
</Box>
<NumberIcon number={1} color="#7E2AEA" backgroundColor="#EEE4FC" />
<Box
sx={{
minHeight: isTablet ? "121px" : "162px",
@ -252,20 +237,7 @@ export default function HowItWorks() {
boxSizing: "border-box",
}}
>
<Box
sx={{
height: "36px",
width: "36px",
justifyContent: "center",
alignItems: "center",
display: "flex",
borderRadius: "6px",
background: "#EEE4FC",
}}
>
<TwoIcon />
</Box>
<NumberIcon number={2} color="#7E2AEA" backgroundColor="#EEE4FC" />
<Box
sx={{
minHeight: "40%",
@ -372,19 +344,7 @@ export default function HowItWorks() {
boxSizing: "border-box",
}}
>
<Box
sx={{
height: "36px",
width: "36px",
justifyContent: "center",
alignItems: "center",
display: "flex",
borderRadius: "6px",
background: "#EEE4FC",
}}
>
<ThreeIcon />
</Box>
<NumberIcon number={3} color="#7E2AEA" backgroundColor="#EEE4FC" />
<Box
sx={{
minHeight: "40%",
@ -526,19 +486,7 @@ export default function HowItWorks() {
flexDirection: "column",
}}
>
<Box
sx={{
height: "36px",
width: "36px",
justifyContent: "center",
alignItems: "center",
display: "flex",
borderRadius: "6px",
background: "#EEE4FC",
}}
>
<FourIcon />
</Box>
<NumberIcon number={4} color="#7E2AEA" backgroundColor="#EEE4FC" />
<Box
sx={{
minHeight: "40%",

@ -1,42 +0,0 @@
import { Box } from "@mui/material";
interface Props {
color?: string;
}
export default function OneIconBorder({ color }: Props) {
return (
<Box
sx={{
height: "36px",
width: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="36" height="36" rx="6" fill="#EEE4FC" />
<path
d="M18 27C22.9706 27 27 22.9706 27 18C27 13.0294 22.9706 9 18 9C13.0294 9 9 13.0294 9 18C9 22.9706 13.0294 27 18 27Z"
stroke="#7E2AEA"
strokeWidth="1.5"
strokeMiterlimit="10"
/>
<path
d="M16.2696 15.3945C16.4382 14.9964 16.7392 14.6687 17.1216 14.4669C17.504 14.2651 17.9444 14.2015 18.3683 14.2871C18.7921 14.3726 19.1734 14.6018 19.4477 14.9361C19.7219 15.2704 19.8723 15.6892 19.8734 16.1216C19.8751 16.4951 19.7633 16.8604 19.5529 17.1691V17.1691L16.1211 21.75H19.8734"
stroke="#7E2AEA"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

@ -1,42 +0,0 @@
import { Box } from "@mui/material";
interface Props {
color?: string;
}
export default function OneIconBorder({ color }: Props) {
return (
<Box
sx={{
height: "36px",
width: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="36" height="36" rx="6" fill="#EEE4FC" />
<path
d="M18 27C22.9706 27 27 22.9706 27 18C27 13.0294 22.9706 9 18 9C13.0294 9 9 13.0294 9 18C9 22.9706 13.0294 27 18 27Z"
stroke="#7E2AEA"
strokeWidth="1.5"
strokeMiterlimit="10"
/>
<path
d="M16.125 14.25H19.875L17.6875 17.375C18.0472 17.375 18.4014 17.4638 18.7186 17.6334C19.0359 17.8029 19.3064 18.0482 19.5062 18.3473C19.7061 18.6464 19.8291 18.9901 19.8643 19.3481C19.8996 19.7061 19.846 20.0673 19.7083 20.3996C19.5707 20.732 19.3532 21.0252 19.0751 21.2535C18.797 21.4817 18.467 21.6378 18.1141 21.708C17.7613 21.7781 17.3967 21.7602 17.0524 21.6558C16.7082 21.5514 16.395 21.3637 16.1406 21.1094"
stroke="#7E2AEA"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

@ -1,49 +0,0 @@
import { Box } from "@mui/material";
interface Props {
color?: string;
}
export default function OneIconBorder({ color }: Props) {
return (
<Box
sx={{
height: "36px",
width: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="36" height="36" rx="6" fill="#EEE4FC" />
<path
d="M18 27C22.9706 27 27 22.9706 27 18C27 13.0294 22.9706 9 18 9C13.0294 9 9 13.0294 9 18C9 22.9706 13.0294 27 18 27Z"
stroke="#7E2AEA"
strokeWidth="1.5"
strokeMiterlimit="10"
/>
<path
d="M17.6251 14.0779L15.75 19.3906H19.5002"
stroke="#7E2AEA"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M19.5 16.8904V21.8906"
stroke="#7E2AEA"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Box>
);
}

@ -0,0 +1,58 @@
import {Link, useNavigate} from "react-router-dom";
import {Box, Button, Typography, useMediaQuery, useTheme} from "@mui/material";
import {ReactComponent as TemplateIcon} from "@/assets/quiz-templates/template.svg";
import {createQuiz, resetEditConfig} from "@root/quizes/actions";
import React from "react";
export default function CreateButtons ({mt}: string) {
const navigate = useNavigate();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(500));
return(
<Box
sx={{
display: "flex",
gap: "20px",
flexWrap: "wrap",
marginTop: mt,
}}
>
<Link to="/gallery" style={{ textDecoration: "none" }}>
<Button
sx={{
display: "flex",
gap: "10px",
padding: "13px",
borderRadius: "8px",
background: "#9A9AAF17",
color: theme.palette.brightPurple.main,
transition: ".2s",
"&:hover": {
color: theme.palette.background.paper,
background: theme.palette.brightPurple.main,
"& svg path": { stroke: theme.palette.background.paper },
},
}}
>
<TemplateIcon />
<Typography>Посмотреть шаблоны</Typography>
</Button>
</Link>
<Button
variant="contained"
sx={{
padding: isMobile ? "10px" : "10px 47px",
minWidth: "44px",
}}
onClick={() => {
resetEditConfig();
createQuiz(navigate);
}}
data-cy="create-quiz"
>
{isMobile ? "+" : "Создать +"}
</Button>
</Box>
)
}

@ -1,66 +0,0 @@
import { Box, Button, Typography, useTheme } from "@mui/material";
import SectionWrapper from "@ui_kit/SectionWrapper";
import ComplexNavText from "./ComplexNavText";
import { createQuiz } from "@root/quizes/actions";
import { Link, useNavigate } from "react-router-dom";
import { resetEditConfig } from "@root/quizes/actions";
import AvailablePrivilege from "./AvailablePrivilege";
import { ReactComponent as TemplateIcon } from "@/assets/quiz-templates/template.svg";
export default function FirstQuiz() {
const navigate = useNavigate();
const theme = useTheme();
return (
<SectionWrapper
maxWidth="lg"
sx={{ mt: "25px", mb: "40px", padding: "10px" }}
>
<Typography variant="h4" sx={{ mt: "20px", mb: "30px" }}>
Создайте свой первый quiz
</Typography>
<AvailablePrivilege />
<Box
sx={{
display: "flex",
gap: "20px",
flexWrap: "wrap",
marginTop: "20px",
}}
>
<Button
variant="contained"
data-cy="create-quiz"
onClick={() => {
resetEditConfig();
createQuiz(navigate);
}}
>
Создать +
</Button>
<Link to="/gallery" style={{ textDecoration: "none" }}>
<Button
sx={{
display: "flex",
gap: "10px",
padding: "13px",
borderRadius: "8px",
background: "#9A9AAF17",
color: theme.palette.brightPurple.main,
transition: ".2s",
"&:hover": {
color: theme.palette.background.paper,
background: theme.palette.brightPurple.main,
"& svg path": { stroke: theme.palette.background.paper },
},
}}
>
<TemplateIcon />
<Typography>Посмотреть шаблоны</Typography>
</Button>
</Link>
</Box>
</SectionWrapper>
);
}

@ -1,22 +0,0 @@
import { useTheme, Box } from "@mui/material";
import ExpandableQuizBlock from "./ExpandableQuizBlock";
import MyQuizzesFull from "./MyQuizzesFull";
export default function MyQuizzes() {
const theme = useTheme();
return (
<MyQuizzesFull>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "40px",
}}
>
<ExpandableQuizBlock name="Название" />
<ExpandableQuizBlock name="Название" />
</Box>
</MyQuizzesFull>
);
}

@ -1,31 +1,28 @@
import {
Box,
Button,
Skeleton,
SxProps,
Theme,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { Link } from "react-router-dom";
import {
copyQuiz,
createQuiz,
resetEditConfig,
updateQuiz,
} from "@root/quizes/actions";
import { mutate } from "swr";
import { useQuizes } from "@root/quizes/hooks";
import SectionWrapper from "@ui_kit/SectionWrapper";
import React from "react";
import { useNavigate } from "react-router-dom";
import FirstQuiz from "./FirstQuiz";
import QuizCard from "./QuizCard";
import HeaderFull from "@ui_kit/Header/HeaderFull";
import QuizgenegationName from "@utils/quizgenegationName";
import AvailablePrivilege from "./AvailablePrivilege";
import { ReactComponent as TemplateIcon } from "@/assets/quiz-templates/template.svg";
import AvailablePrivilegeDummy from "@/components/Dummys/AvailablePrivilegeDummy";
import QuizCardDummy from "@/components/Dummys/QuizCardDummy";
import CreateButtons from "@/pages/createQuize/CreateButtons";
interface Props {
outerContainerSx?: SxProps<Theme>;
@ -36,8 +33,7 @@ export default function MyQuizzesFull({
outerContainerSx: sx,
children,
}: Props) {
const { quizes } = useQuizes();
const navigate = useNavigate();
const { quizes, isLoading } = useQuizes();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(500));
@ -49,9 +45,6 @@ export default function MyQuizzesFull({
return (
<>
<HeaderFull isRequest={false} />
{quizes.length === 0 ? (
<FirstQuiz />
) : (
<SectionWrapper
maxWidth="lg"
sx={{ padding: isMobile ? "0 16px" : "20px" }}
@ -65,51 +58,12 @@ export default function MyQuizzesFull({
mb: "30px",
}}
>
<Typography variant="h4">Мои quiz</Typography>
<Box
sx={{
display: "flex",
gap: "20px",
flexWrap: "wrap",
marginTop: "20px",
}}
>
<Link to="/gallery" style={{ textDecoration: "none" }}>
<Button
sx={{
display: "flex",
gap: "10px",
padding: "13px",
borderRadius: "8px",
background: "#9A9AAF17",
color: theme.palette.brightPurple.main,
transition: ".2s",
"&:hover": {
color: theme.palette.background.paper,
background: theme.palette.brightPurple.main,
"& svg path": { stroke: theme.palette.background.paper },
},
}}
>
<TemplateIcon />
<Typography>Посмотреть шаблоны</Typography>
</Button>
</Link>
<Button
variant="contained"
sx={{
padding: isMobile ? "10px" : "10px 47px",
minWidth: "44px",
}}
onClick={() => {
resetEditConfig();
createQuiz(navigate);
}}
data-cy="create-quiz"
>
{isMobile ? "+" : "Создать +"}
</Button>
</Box>
{isLoading ? <Skeleton variant="text" sx={{ fontSize: "24px", width: "300px", height: "50px" }} /> :
<Typography variant="h4">{quizes.length === 0 ? "Создайте свой первый quiz" : "Мои quiz"}</Typography>
}
{quizes.length !== 0 &&
<CreateButtons mt={"20px"}/>
}
</Box>
<Box
sx={{
@ -121,38 +75,49 @@ export default function MyQuizzesFull({
width: isMobile ? "100%" : undefined,
}}
>
<AvailablePrivilege />
{quizes.map((quiz) => {
if (quiz.name.length === 0 || quiz.name === " ") {
updateQuiz(quiz.id, (quiz) => {
quiz.name = QuizgenegationName({ quiz });
});
}
{isLoading ?
<>
<AvailablePrivilegeDummy/>
<QuizCardDummy/>
<QuizCardDummy/>
<QuizCardDummy/>
</>
:
<>
<AvailablePrivilege />
{quizes.length === 0 &&
<CreateButtons mt={"0px"}/>}
{quizes.map((quiz) => {
if (quiz.name.length === 0 || quiz.name === " ") {
updateQuiz(quiz.id, (quiz) => {
quiz.name = QuizgenegationName({ quiz });
});
}
return (
<QuizCard
key={quiz.id}
quiz={quiz}
openCount={quiz.session_count}
applicationCount={quiz.passed_count}
conversionPercent={
quiz.session_count
? parseFloat(
(
(quiz.passed_count / quiz.session_count) *
100
).toFixed(2),
)
: 0
}
onClickCopy={onClickCopy}
/>
);
})}
return (
<QuizCard
key={quiz.id}
quiz={quiz}
openCount={quiz.session_count}
applicationCount={quiz.passed_count}
conversionPercent={
quiz.session_count
? parseFloat(
(
(quiz.passed_count / quiz.session_count) *
100
).toFixed(2),
)
: 0
}
onClickCopy={onClickCopy}
/>
);
})}
</>}
</Box>
{children}
</SectionWrapper>
)}
</>
);
}

@ -1,8 +1,4 @@
import {
Box,
useMediaQuery,
useTheme,
} from "@mui/material";
import { Box, useMediaQuery, useTheme } from "@mui/material";
import { updateQuiz, setCurrentStep } from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuizStore } from "@root/quizes/store";

@ -450,7 +450,7 @@ export const uploadQuestionImage = async (
}
const imageId = values[0];
const imageUrl = `https://storage.yandexcloud.net/squizimages/${quizQid}/${imageId}`;
const imageUrl = `https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/squizimages/${quizQid}/${imageId}`;
updateQuestion(questionId, (question) => {
updateFn(question, imageUrl);

@ -305,7 +305,7 @@ export const uploadQuizImage = async (
updateQuiz(quizId, (quiz) => {
updateFn(
quiz,
`https://storage.yandexcloud.net/squizimages/${quiz.qid}/${imageId}`,
`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/squizimages/${quiz.qid}/${imageId}`,
);
});
};

@ -0,0 +1,41 @@
import { ButtonBase } from "@mui/material";
import { startTransition, useRef } from "react";
interface Props {
color: string;
onChange: (color: string) => void;
}
export default function CircleColorPicker({ color, onChange }: Props) {
const inputRef = useRef<HTMLInputElement>(null);
return (
<ButtonBase
onClick={() => inputRef.current?.click()}
sx={{
aspectRatio: 1,
height: "22px",
width: "22px",
minWidth: "22px",
borderRadius: "50%",
backgroundColor: color,
border: "1px solid #4D4D4D",
}}
>
<input
ref={inputRef}
type="color"
value={color}
onChange={(e) => {
startTransition(() => {
onChange(e.target.value);
});
}}
style={{
opacity: 0,
cursor: "pointer",
}}
/>
</ButtonBase>
);
}

@ -98,7 +98,7 @@ export default function ChatDocument({
</svg>
<Link
download
href={`https://storage.yandexcloud.net/pair/${file}`}
href={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/pair/${file}`}
style={{
color: "#7E2AEA",
display: "flex",

@ -110,7 +110,7 @@ export default function ChatImage({
height: "217px",
width: "217px",
}}
src={`https://storage.yandexcloud.net/pair/${file}`}
src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/pair/${file}`}
/>
</ButtonBase>
</Box>

@ -12,7 +12,7 @@ export default function ChatImageNewWindow() {
maxHeight: "100vh",
maxWidth: "100vw",
}}
src={`https://storage.yandexcloud.net/pair/${srcImage}`}
src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/pair/${srcImage}`}
/>
</>
);

@ -113,7 +113,9 @@ export default function ChatImage({
}}
controls
>
<source src={`https://storage.yandexcloud.net/pair/${file}`} />
<source
src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/angesight/pair/${file}`}
/>
</Box>
</Box>
</Box>

@ -0,0 +1,135 @@
import {
FormControl,
InputLabel,
TextField as MuiTextField,
SxProps,
TextFieldProps,
Theme,
useMediaQuery,
useTheme,
} from "@mui/material";
import { FC } from "react";
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
interface Props {
id?: string;
label?: string;
labelSx?: SxProps<Theme>;
bold?: boolean;
gap?: string;
backgroundColor?: string;
FormControlSx?: SxProps<Theme>;
TextFieldSx?: SxProps<Theme>;
placeholder?: TextFieldProps["placeholder"];
value?: TextFieldProps["value"];
helperText?: TextFieldProps["helperText"];
error?: TextFieldProps["error"];
type?: TextFieldProps["type"];
onBlur?: TextFieldProps["onBlur"];
onChange?: TextFieldProps["onChange"];
fullWidth?: boolean;
}
export default function PenaTextField({
id,
label,
labelSx,
bold = false,
gap = "10px",
onChange,
error,
helperText,
onBlur,
placeholder,
type,
value,
backgroundColor,
FormControlSx,
TextFieldSx,
fullWidth = true,
}: Props) {
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const labelFont = upMd
? bold
? theme.typography.p1
: { ...theme.typography.body1, fontWeight: 500 }
: theme.typography.body2;
const placeholderFont = upMd
? undefined
: { fontWeight: 400, fontSize: "16px", lineHeight: "19px" };
return (
<FormControl
fullWidth={fullWidth}
variant="standard"
sx={{
gap,
...FormControlSx,
}}
>
{label && (
<InputLabel
shrink
htmlFor={id}
sx={{
position: "inherit",
color: "black",
transform: "none",
...labelFont,
...labelSx,
}}
>
{label}
</InputLabel>
)}
<TextField
fullWidth
id={id}
error={error}
helperText={helperText}
onBlur={onBlur}
placeholder={placeholder}
type={type}
value={value}
sx={{
"& .MuiInputBase-root": {
height: "48px",
borderRadius: "8px",
"& fieldset": {
border: `1px solid ${theme.palette.grey2.main}`,
},
"&:hover fieldset": {
border: `1px solid ${theme.palette.grey3.main}`,
},
"&.Mui-focused fieldset": {
border: `2px solid ${theme.palette.brightPurple.main}`,
},
},
"& .MuiFormHelperText-root.MuiFormHelperText-contained.MuiFormHelperText-filled.Mui-error":
{
position: "absolute",
top: "45px",
},
...TextFieldSx,
}}
inputProps={{
sx: {
boxSizing: "border-box",
backgroundColor:
backgroundColor ?? theme.palette.background.default,
borderRadius: "8px",
height: "48px",
py: 0,
color: theme.palette.grey3.main,
...placeholderFont,
},
}}
onChange={onChange}
/>
</FormControl>
);
}

@ -0,0 +1,35 @@
import { Box, SxProps, Theme } from "@mui/material";
interface Props {
sx?: SxProps<Theme>;
}
export default function RunningStripe({ sx = [] }: Props) {
return (
<Box
component="span"
sx={[
{
position: "absolute",
height: "70px",
width: "140px",
background:
"linear-gradient(0deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.1) 100%)",
animation: "runningStripe linear 3s infinite",
transform: "rotate(-60deg)",
"@keyframes runningStripe": {
"0%": {
left: "-150px",
opacity: 1,
},
"25%, 100%": {
left: "100%",
opacity: 0,
},
},
},
...(Array.isArray(sx) ? sx : [sx]),
]}
/>
);
}

@ -6,12 +6,18 @@ import {
QuizType,
} from "@model/quizSettings";
const ContactFormPage = lazy(() => import("../pages/ContactFormPage/ContactFormPage"));
const ContactFormPage = lazy(
() => import("../pages/ContactFormPage/ContactFormPage"),
);
const InstallQuiz = lazy(() => import("../pages/InstallQuiz/InstallQuiz"));
const FormQuestionsPage = lazy(() => import("../pages/Questions/Form/FormQuestionsPage"));
const FormQuestionsPage = lazy(
() => import("../pages/Questions/Form/FormQuestionsPage"),
);
const QuestionsPage = lazy(() => import("../pages/Questions/QuestionsPage"));
const ResultPage = lazy(() => import("../pages/ResultPage/ResultPage"));
const StartPageSettings = lazy(() => import("../pages/startPage/StartPageSettings"));
const StartPageSettings = lazy(
() => import("../pages/startPage/StartPageSettings"),
);
const StepOne = lazy(() => import("../pages/startPage/stepOne"));
const Steptwo = lazy(() => import("../pages/startPage/steptwo"));
@ -38,26 +44,56 @@ export default function SwitchStepPages({
}: Props) {
switch (activeStep) {
case 0: {
if (!quizType) return <LazyLoading><StepOne /></LazyLoading>;
if (!quizStartPageType) return <LazyLoading><Steptwo /></LazyLoading>;
return <LazyLoading><StartPageSettings /></LazyLoading>;
if (!quizType)
return (
<LazyLoading>
<StepOne />
</LazyLoading>
);
if (!quizStartPageType)
return (
<LazyLoading>
<Steptwo />
</LazyLoading>
);
return (
<LazyLoading>
<StartPageSettings />
</LazyLoading>
);
}
case 1:
return quizType === "form" ? (
<LazyLoading><FormQuestionsPage /></LazyLoading>
<LazyLoading>
<FormQuestionsPage />
</LazyLoading>
) : (
<LazyLoading><QuestionsPage
openBranchingPage={openBranchingPage}
setOpenBranchingPage={setOpenBranchingPage}
widthMain={widthMain}
/></LazyLoading>
<LazyLoading>
<QuestionsPage
openBranchingPage={openBranchingPage}
setOpenBranchingPage={setOpenBranchingPage}
widthMain={widthMain}
/>
</LazyLoading>
);
case 2:
return <LazyLoading><ResultPage /></LazyLoading>;
return (
<LazyLoading>
<ResultPage />
</LazyLoading>
);
case 3:
return <LazyLoading><ContactFormPage /></LazyLoading>;
return (
<LazyLoading>
<ContactFormPage />
</LazyLoading>
);
case 4:
return <LazyLoading><InstallQuiz /></LazyLoading>;
return (
<LazyLoading>
<InstallQuiz />
</LazyLoading>
);
default:
throw new Error(`Invalid quiz setup step: ${activeStep}`);
}

23968
yarn.lock

File diff suppressed because it is too large Load Diff