Merge branch 'dev' into 'staging'

refactor quiz install page

See merge request frontend/squiz!295
This commit is contained in:
Nastya 2024-05-01 10:40:29 +00:00
commit 09bab17d66
27 changed files with 1232 additions and 748 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

@ -1,7 +1,7 @@
import { Box } from "@mui/material"; import { Box } from "@mui/material";
interface Props { interface Props {
color: string; color?: string;
} }
export default function ArrowLeft({ color = "#7E2AEA" }: Props) { export default function ArrowLeft({ color = "#7E2AEA" }: Props) {

@ -118,6 +118,11 @@ export interface QuizConfig {
vkMetricNumber: number | undefined; vkMetricNumber: number | undefined;
} }
export enum QuizMetricType {
yandex = "yandexMetricNumber",
vk = "vkMetricNumber",
}
export type FormContactFieldName = export type FormContactFieldName =
| "name" | "name"
| "email" | "email"

@ -1,88 +1,10 @@
import React, { useState } from "react"; import { Box, Button } from "@mui/material";
import { import { decrementCurrentStep } from "@root/quizes/actions";
Box,
Button,
ButtonBase,
FormControl,
IconButton,
InputAdornment,
Link,
MenuItem,
Modal,
OutlinedInput,
Paper,
Select,
SelectChangeEvent,
TextField,
Typography,
Tooltip,
useTheme,
useMediaQuery,
} from "@mui/material";
import LinkIcon from "../../assets/icons/LinkIcon";
import InfoIcon from "../../assets/icons/InfoIcon";
import ArrowDown from "../../assets/icons/ArrowDownIcon";
import CopyIcon from "../../assets/icons/CopyIcon";
import VkIcon from "../../assets/icons/VkIcon";
import DomenIcon from "../../assets/icons/DomenIcon";
import OnButton from "../../assets/OnButton.png";
import Bunner from "../../assets/Bunner.png";
import InBidySite from "../../assets/InBodySite.png";
import AutoOpen from "../../assets/AutoOpen.png";
import WidgetImg from "../../assets/Widget.png";
import OneIconBorder from "../../assets/icons/OneIconBorder";
import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft"; import ArrowLeft from "../../assets/icons/questionsPage/arrowLeft";
import CustomTextField from "@ui_kit/CustomTextField"; import QuizInstallationCard from "./QuizInstallationCard/QuizInstallationCard";
import VkIconButton from "../../assets/icons/VkIconButton"; import QuizLinkCard from "./QuizLinkCard";
import SelectableButton from "@ui_kit/SelectableButton";
import ButtonSocial from "./ButtonSocial";
import OnButtonInstall from "./OnButtonInstall";
import BannerInstall from "./BannerInstall";
import InBodyInstall from "./InBodyInstall";
import AutoOpenInstall from "./AutoOpenInstall";
import VidjetInstall from "./VidjetInstall";
import InstallQzCode from "./InstallQzCode";
import {
decrementCurrentStep,
incrementCurrentStep,
updateQuiz,
} from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useDomainDefine } from "@utils/hooks/useDomainDefine";
import NumberTwo from "@icons/NumberTwo";
type BackgroundType = "text" | "video";
export default function InstallQuiz() { export default function InstallQuiz() {
const quiz = useCurrentQuiz();
const { isTestServer } = useDomainDefine();
const [display, setDisplay] = React.useState("1");
const handleChange = (event: SelectChangeEvent) => {
setDisplay(event.target.value);
};
const [openVk, setOpenVk] = React.useState(false);
const [stepState, setStepState] = React.useState("step1");
const handleOpenVk = () => setOpenVk(true);
const handleCloseVk = () => setOpenVk(false);
const [openDom, setOpenDom] = React.useState(false);
const handleOpenDom = () => setOpenDom(true);
const handleCloseDom = () => setOpenDom(false);
const [backgroundType, setBackgroundType] = useState<BackgroundType>("text");
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(600));
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const CopyLink = () => {
let one = (document.getElementById("inputLinkone") as HTMLInputElement)
?.value;
let text = (document.getElementById("inputLink") as HTMLInputElement)
?.value;
navigator.clipboard.writeText(one + text);
};
return ( return (
<> <>
<Box <Box
@ -93,360 +15,8 @@ export default function InstallQuiz() {
flexDirection: "column", flexDirection: "column",
}} }}
> >
<Paper <QuizLinkCard />
sx={{ <QuizInstallationCard />
maxWidth: "522px",
width: "100%",
padding: "20px",
borderRadius: "12px",
display: "flex",
flexDirection: "column",
gap: "30px",
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: isMobile ? "flex-start" : "center",
gap: "10px",
flexDirection: isMobile ? "column" : "row",
}}
>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
<LinkIcon
color={theme.palette.brightPurple.main}
bgcolor={"#EEE4FC"}
/>
<Typography>Ссылка на quiz</Typography>
</Box>
<FormControl
fullWidth
size="small"
sx={{
width: "100%",
maxWidth: "180px",
height: "48px",
}}
>
<Select
id="display-select"
variant="outlined"
value={display}
displayEmpty
onChange={handleChange}
sx={{
height: "48px",
borderRadius: "8px",
"& .MuiOutlinedInput-notchedOutline": {
border: "none !important",
},
}}
MenuProps={{
PaperProps: {
sx: {
mt: "8px",
p: "4px",
borderRadius: "8px",
},
},
MenuListProps: {
sx: {
py: 0,
display: "flex",
flexDirection: "column",
gap: "8px",
"& .Mui-selected": {
backgroundColor: theme.palette.background.default,
color: theme.palette.brightPurple.main,
},
},
},
}}
inputProps={{
sx: {
color: theme.palette.brightPurple.main,
display: "flex",
alignItems: "center",
px: "9px",
gap: "20px",
},
}}
IconComponent={(props) => <ArrowDown {...props} />}
>
<MenuItem
value={1}
sx={{
display: "flex",
alignItems: "center",
gap: "20px",
p: "4px",
borderRadius: "5px",
color: theme.palette.grey2.main,
}}
>
Стандартная
</MenuItem>
</Select>
</FormControl>
</Box>
<Box sx={{ display: "flex", alignItems: "center" }}>
<FormControl fullWidth variant="standard" sx={{ p: 0 }}>
<TextField
disabled
id={"inputLinkone"}
value={
isTestServer ? "https://s.hbpn.link/" : "https://hbpn.link/"
}
sx={{
"& .css-1d3z3hw-MuiOutlinedInput-notchedOutline": {
border: "none",
},
"& .MuiInputBase-root": {
height: "48px",
borderRadius: "10px 0 0 10px",
backgroundColor: "#EEE4FC",
},
}}
/>
</FormControl>
<FormControl fullWidth variant="standard" sx={{ p: 0 }}>
<TextField
value={quiz.qid}
id={"inputLink"}
fullWidth
placeholder="6235840cc71"
sx={{
"& .MuiInputBase-root": {
backgroundColor: theme.palette.background.default,
height: "48px",
borderRadius: "0 10px 10px 0",
},
}}
inputProps={{
sx: {
borderRadius: "0 10px 10px 0",
fontSize: "18px",
lineHeight: "21px",
py: 0,
},
}}
/>
</FormControl>
<IconButton
onClick={CopyLink}
id={"copyLink"}
sx={{ borderRadius: "6px" }}
>
<CopyIcon
color={theme.palette.brightPurple.main}
bgcolor={"#EEE4FC"}
/>
</IconButton>
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
}}
>
<Typography sx={{ color: "#FC712F" }}>
{quiz?.status === "start" ? "Опубликован" : "Не опубликован"}
</Typography>
</Box>
</Paper>
<Box
sx={{
backgroundColor: "#ffffff",
padding: "20px",
mt: "40px",
borderRadius: "12px",
maxWidth: "1160px",
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: isSmallMonitor ? "flex-start" : "center",
gap: "30px",
flexDirection: isSmallMonitor ? "column" : "row",
}}
>
<Typography variant="h5" sx={{ paddingRight: "30px" }}>
Установка quiz на сайте
</Typography>
<ButtonBase
onClick={() => {
setStepState("step1");
}}
>
<OneIconBorder
color={
stepState === "step1" ? "#FC712F" : theme.palette.grey2.main
}
/>
<Typography
sx={{
color:
stepState === "step1"
? "#FC712F"
: theme.palette.grey2.main,
}}
>
Способ установки
</Typography>
</ButtonBase>
<ButtonBase
onClick={() => {
setStepState("step2");
}}
>
<NumberTwo
color={
stepState === "step2" ? "#FC712F" : theme.palette.grey2.main
}
/>
<Typography
sx={{
color:
stepState === "step2"
? "#FC712F"
: theme.palette.grey2.main,
}}
>
Вставить код на сайт
</Typography>
</ButtonBase>
</Box>
<Box
sx={{
padding: "20px",
display: "flex",
flexDirection: "column",
gap: "30px",
}}
>
{stepState === "step1" ? (
<ButtonBase
onClick={() => {
setStepState("step2");
}}
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "start",
alignItems: "start",
maxWidth: "205px",
gap: "15px",
}}
>
<img src={InBidySite} />
<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",
}}
>
<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(
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>
</>
)}
</Box>
</Box>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@ -468,27 +38,3 @@ export default function InstallQuiz() {
</> </>
); );
} }
const buttonInstall: { icon: string; title: string; text: string }[] = [
{
icon: OnButton,
title: "По кнопке",
text: "Конструктор кнопки или собственная кнопка",
},
{ icon: Bunner, title: "Баннером", text: "Сбоку или на всю ширину экрана" },
{
icon: InBidySite,
title: "В тело сайта",
text: "Задайте свои размеры и встройте в сайт",
},
{
icon: AutoOpen,
title: "Автооткрытие",
text: "Автооткрытие поп-ап на сайте",
},
{
icon: WidgetImg,
title: "Виджет",
text: "Сбоку страницы как консультант",
},
];

@ -0,0 +1,31 @@
import { ButtonBase, Typography, useTheme } from "@mui/material";
import { ReactNode } from "react";
interface Props {
active?: boolean;
onClick?: () => void;
leftIcon?: ReactNode;
children?: ReactNode;
}
export default function InstallationStepButton({
active,
leftIcon,
onClick,
children,
}: Props) {
const theme = useTheme();
return (
<ButtonBase onClick={onClick}>
{leftIcon}
<Typography
sx={{
color: active ? "#FC712F" : theme.palette.grey2.main,
}}
>
{children}
</Typography>
</ButtonBase>
);
}

@ -0,0 +1,207 @@
import {
Box,
ButtonBase,
IconButton,
InputAdornment,
TextField as MuiTextField,
TextFieldProps,
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 InstallationStepButton from "./InstallationStepButton";
import ContainerWidgetPreview from "./previewIcons/ContainerWidgetPreview";
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
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));
if (!quiz) return null;
return (
<Box
sx={{
backgroundColor: "#ffffff",
padding: "20px",
borderRadius: "12px",
maxWidth: "1160px",
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: isSmallMonitor ? "flex-start" : "center",
gap: "30px",
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");
}}
>
Способ установки
</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
onClick={() => {
setStepState("step2");
}}
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "start",
alignItems: "start",
maxWidth: "205px",
gap: "15px",
}}
>
<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",
}}
>
<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>
</>
)}
</Box>
</Box>
);
}

@ -0,0 +1,49 @@
import { Box } from "@mui/material";
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>
);
}

@ -0,0 +1,50 @@
import { Box } from "@mui/material";
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>
);
}

@ -0,0 +1,50 @@
import { Box } from "@mui/material";
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>
);
}

@ -0,0 +1,54 @@
import { Box } from "@mui/material";
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>
);
}

@ -0,0 +1,50 @@
import { Box } from "@mui/material";
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>
);
}

@ -0,0 +1,209 @@
import {
Box,
FormControl,
IconButton,
MenuItem,
TextField as MuiTextField,
Paper,
Select,
SelectChangeEvent,
TextFieldProps,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useDomainDefine } from "@utils/hooks/useDomainDefine";
import { FC, useState } from "react";
import ArrowDown from "../../assets/icons/ArrowDownIcon";
import CopyIcon from "../../assets/icons/CopyIcon";
import LinkIcon from "../../assets/icons/LinkIcon";
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
export default function QuizLinkCard() {
const theme = useTheme();
const quiz = useCurrentQuiz();
const { isTestServer } = useDomainDefine();
const [display, setDisplay] = useState("1");
const isMobile = useMediaQuery(theme.breakpoints.down(600));
const CopyLink = () => {
let one = (document.getElementById("inputLinkone") as HTMLInputElement)
?.value;
let text = (document.getElementById("inputLink") as HTMLInputElement)
?.value;
navigator.clipboard.writeText(one + text);
};
const handleChange = (event: SelectChangeEvent) => {
setDisplay(event.target.value);
};
if (!quiz) return null;
return (
<Paper
sx={{
maxWidth: "522px",
width: "100%",
padding: "20px",
borderRadius: "12px",
display: "flex",
flexDirection: "column",
gap: "30px",
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: isMobile ? "flex-start" : "center",
gap: "10px",
flexDirection: isMobile ? "column" : "row",
}}
>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
<LinkIcon
color={theme.palette.brightPurple.main}
bgcolor={"#EEE4FC"}
/>
<Typography>Ссылка на quiz</Typography>
</Box>
<FormControl
fullWidth
size="small"
sx={{
width: "100%",
maxWidth: "180px",
height: "48px",
}}
>
<Select
id="display-select"
variant="outlined"
value={display}
displayEmpty
onChange={handleChange}
sx={{
height: "48px",
borderRadius: "8px",
"& .MuiOutlinedInput-notchedOutline": {
border: "none !important",
},
}}
MenuProps={{
PaperProps: {
sx: {
mt: "8px",
p: "4px",
borderRadius: "8px",
},
},
MenuListProps: {
sx: {
py: 0,
display: "flex",
flexDirection: "column",
gap: "8px",
"& .Mui-selected": {
backgroundColor: theme.palette.background.default,
color: theme.palette.brightPurple.main,
},
},
},
}}
inputProps={{
sx: {
color: theme.palette.brightPurple.main,
display: "flex",
alignItems: "center",
px: "9px",
gap: "20px",
},
}}
IconComponent={(props) => <ArrowDown {...props} />}
>
<MenuItem
value={1}
sx={{
display: "flex",
alignItems: "center",
gap: "20px",
p: "4px",
borderRadius: "5px",
color: theme.palette.grey2.main,
}}
>
Стандартная
</MenuItem>
</Select>
</FormControl>
</Box>
<Box sx={{ display: "flex", alignItems: "center" }}>
<FormControl fullWidth variant="standard" sx={{ p: 0 }}>
<TextField
disabled
id={"inputLinkone"}
value={isTestServer ? "https://s.hbpn.link/" : "https://hbpn.link/"}
sx={{
"& .css-1d3z3hw-MuiOutlinedInput-notchedOutline": {
border: "none",
},
"& .MuiInputBase-root": {
height: "48px",
borderRadius: "10px 0 0 10px",
backgroundColor: "#EEE4FC",
},
}}
/>
</FormControl>
<FormControl fullWidth variant="standard" sx={{ p: 0 }}>
<TextField
value={quiz.qid}
id={"inputLink"}
fullWidth
placeholder="6235840cc71"
sx={{
"& .MuiInputBase-root": {
backgroundColor: theme.palette.background.default,
height: "48px",
borderRadius: "0 10px 10px 0",
},
}}
inputProps={{
sx: {
borderRadius: "0 10px 10px 0",
fontSize: "18px",
lineHeight: "21px",
py: 0,
},
}}
/>
</FormControl>
<IconButton
onClick={CopyLink}
id={"copyLink"}
sx={{ borderRadius: "6px" }}
>
<CopyIcon
color={theme.palette.brightPurple.main}
bgcolor={"#EEE4FC"}
/>
</IconButton>
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
}}
>
<Typography sx={{ color: "#FC712F" }}>
{quiz?.status === "start" ? "Опубликован" : "Не опубликован"}
</Typography>
</Box>
</Paper>
);
}

@ -1,41 +0,0 @@
import { Box, useTheme } from "@mui/material";
import { FC } from "react";
import { YandexMetricaLogo } from "../mocks/YandexMetricaLogo";
type PartnerItemProps = {
setIsModalOpen: (value: boolean) => void;
setCompanyName?: (value: string) => void;
};
export const YandexButton: FC<PartnerItemProps> = ({
setIsModalOpen,
setCompanyName,
}) => {
const theme = useTheme();
const handleClick = () => {
setIsModalOpen(true);
};
return (
<>
<Box
sx={{
width: 250,
height: 60,
backgroundColor: "white",
borderRadius: "8px",
padding: "0 20px",
display: "flex",
alignItems: "center",
marginBottom: "2%",
marginRight: "2%",
cursor: "pointer",
}}
onClick={() => setIsModalOpen(true)}
>
<YandexMetricaLogo />
</Box>
</>
);
};

@ -1,197 +0,0 @@
import {
Button,
Dialog,
IconButton,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import Box from "@mui/material/Box";
import CloseIcon from "@mui/icons-material/Close";
import React, { useState } from "react";
import CustomTextField from "@ui_kit/CustomTextField";
import EditPencil from "@icons/EditPencil";
import Trash from "@icons/trash";
import { updateQuiz } from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
interface Props {
isModalOpen: boolean;
handleCloseModal: () => void;
}
export default function YandexModal({ isModalOpen, handleCloseModal }: Props) {
const theme = useTheme();
const quiz = useCurrentQuiz();
const isMobile = useMediaQuery(theme.breakpoints.down(600));
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const yandexNumber = quiz?.config.yandexMetricNumber;
const [isSave, setIsSave] = useState<boolean>(!!yandexNumber);
const [currentValue, setCurrentValue] = useState<string>(
yandexNumber ? yandexNumber.toString() : "",
);
const handleClose = () => {
handleCloseModal();
if (!yandexNumber) {
setIsSave(false);
setCurrentValue("");
return;
}
setIsSave(true);
setCurrentValue(yandexNumber.toString());
};
const handleSave = () => {
handleCloseModal();
updateQuiz(quiz?.id, (quiz) => {
quiz.config.yandexMetricNumber = currentValue
? Number(currentValue)
: undefined;
});
if (!currentValue) {
setIsSave(false);
return;
}
setIsSave(true);
};
const handleEdit = () => {
setIsSave(false);
};
const handleClear = () => {
setCurrentValue("");
setIsSave(false);
};
return (
<Dialog
open={isModalOpen}
onClose={handleClose}
fullWidth
PaperProps={{
sx: {
maxWidth: isTablet ? "100%" : "580px",
maxHeight: isTablet ? "100%" : "251px",
borderRadius: "12px",
},
}}
>
<Box
sx={{
width: "100%",
height: "68px",
backgroundColor: theme.palette.background.default,
}}
>
<Typography
sx={{
fontSize: isMobile ? "20px" : "24px",
fontWeight: "500",
padding: "20px",
}}
>
Аналитика с Яндекс.Метрикой
</Typography>
</Box>
<IconButton
onClick={handleClose}
sx={{
width: "12px",
height: "12px",
position: "absolute",
right: "15px",
top: "15px",
}}
>
<CloseIcon
sx={{ width: "12px", height: "12px", transform: "scale(1.5)" }}
/>
</IconButton>
<Box
sx={{
display: "flex",
flexDirection: "column",
padding: "15px 20px 15px",
flexGrow: 1,
gap: "20px",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "10px",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Typography fontWeight={500}>
{isSave ? "Ваш номер счетчика" : "Введите номер счетчика"}
</Typography>
{isSave && (
<Box>
<IconButton onClick={handleEdit}>
<EditPencil
color={theme.palette.brightPurple.main}
width={"18px"}
height={"18px"}
/>
</IconButton>
<IconButton onClick={handleClear}>
<Trash
sx={{
width: "24px",
"& path": {
stroke: theme.palette.brightPurple.main,
},
}}
/>
</IconButton>
</Box>
)}
</Box>
<CustomTextField
placeholder={isSave ? currentValue : "в формате ХХХХХХХХ"}
type={"number"}
value={currentValue}
disabled={isSave}
onChange={(e) => {
const onlyNums = e.target.value.replace(/[^0-9]/g, "");
setCurrentValue(onlyNums);
}}
/>
</Box>
{!isSave && (
<Box
sx={{
display: "flex",
justifyContent: isMobile ? "space-between" : "end",
gap: "10px",
}}
>
<Button
sx={{ width: isMobile ? "100%" : "130px" }}
onClick={handleClose}
variant={"outlined"}
>
Отмена
</Button>
<Button
sx={{ width: isMobile ? "100%" : "130px" }}
variant={"contained"}
onClick={handleSave}
>
Сохранить
</Button>
</Box>
)}
</Box>
</Dialog>
);
}

@ -6,6 +6,7 @@ import { useQuizStore } from "@root/quizes/store";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { PartnersBoard } from "./PartnersBoard/PartnersBoard"; import { PartnersBoard } from "./PartnersBoard/PartnersBoard";
import { partnersMock } from "./mocks/MockData"; import { partnersMock } from "./mocks/MockData";
import { QuizMetricType } from "@model/quizSettings";
interface IntegrationsPageProps { interface IntegrationsPageProps {
heightSidebar: number; heightSidebar: number;
@ -22,7 +23,9 @@ export const IntegrationsPage = ({
const navigate = useNavigate(); const navigate = useNavigate();
const isMobile = useMediaQuery(theme.breakpoints.down(660)); const isMobile = useMediaQuery(theme.breakpoints.down(660));
const [isModalOpen, setIsModalOpen] = useState<boolean>(false); const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [companyName, setCompanyName] = useState<string | null>(null); const [companyName, setCompanyName] = useState<
keyof typeof QuizMetricType | null
>(null);
useEffect(() => { useEffect(() => {
if (editQuizId === null) navigate("/list"); if (editQuizId === null) navigate("/list");
}, [navigate, editQuizId]); }, [navigate, editQuizId]);
@ -62,6 +65,7 @@ export const IntegrationsPage = ({
<PartnersBoard <PartnersBoard
partners={partnersMock} partners={partnersMock}
setIsModalOpen={setIsModalOpen} setIsModalOpen={setIsModalOpen}
companyName={companyName}
setCompanyName={setCompanyName} setCompanyName={setCompanyName}
isModalOpen={isModalOpen} isModalOpen={isModalOpen}
handleCloseModal={handleCloseModal} handleCloseModal={handleCloseModal}

@ -0,0 +1,263 @@
import {
Button,
Dialog,
IconButton,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import Box from "@mui/material/Box";
import CloseIcon from "@mui/icons-material/Close";
import React, { useEffect, useMemo, useState } from "react";
import CustomTextField from "@ui_kit/CustomTextField";
import EditPencil from "@icons/EditPencil";
import Trash from "@icons/trash";
import { updateQuiz } from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { QuizMetricType } from "@model/quizSettings";
import { InstructionsBlock } from "./IntsructionsBlock/InstructionsBlock";
interface Props {
isModalOpen: boolean;
handleCloseModal: () => void;
companyName: keyof typeof QuizMetricType;
}
export default function AnalyticsModal({
isModalOpen,
handleCloseModal,
companyName,
}: Props) {
const theme = useTheme();
const quiz = useCurrentQuiz();
const isMobile = useMediaQuery(theme.breakpoints.down(600));
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const configName = QuizMetricType[companyName];
const meterNumber = quiz?.config[configName];
const [isSave, setIsSave] = useState<boolean>(!!meterNumber);
const [currentValue, setCurrentValue] = useState<string>(
meterNumber ? meterNumber.toString() : "",
);
const analyticTexts = useMemo(() => {
return {
yandex: {
header: "Яндекс.Метрикой",
counterName: "счетчика",
instructionTitle: "Как установить Яндекс Метрику в квиз?",
instructionSubTitle: "Инструкция по настройке Яндекс Метрики",
instructionHeader: "Настройка счётчика и интеграции",
instructionText:
"Повседневная практика показывает, что дальнейшее развитие различных форм деятельности требуют определения и уточнения соответствующий условий активизации.Повседневная практика показывает, что дальнейшее развитие различных форм деятельности требуют определения и уточнения соответствующий условий активизации.Повседневная практика показывает, что дальнейшее развитие различных форм деятельности требуют определения и уточнения соответствующий условий активизации.",
},
vk: {
header: "VK Пиксель",
counterName: "пикселя",
instructionTitle: "Как установить VK Пиксель в квиз?",
instructionSubTitle: "Инструкция по настройке VK Пиксель",
instructionHeader: "Настройка счётчика и интеграции",
instructionText:
"Повседневная практика показывает, что дальнейшее развитие различных форм деятельности требуют определения и уточнения соответствующий условий активизации.",
},
};
}, []);
const handleClose = () => {
handleCloseModal();
if (!meterNumber) {
setIsSave(false);
setCurrentValue("");
return;
}
setIsSave(true);
setCurrentValue(meterNumber.toString());
};
const handleSave = () => {
handleCloseModal();
updateQuiz(quiz?.id, (quiz) => {
quiz.config[configName] = currentValue ? Number(currentValue) : undefined;
});
if (!currentValue) {
setIsSave(false);
return;
}
setIsSave(true);
};
const handleEdit = () => {
setIsSave(false);
};
const handleClear = () => {
setCurrentValue("");
setIsSave(false);
};
useEffect(() => {
const configName =
QuizMetricType[companyName as keyof typeof QuizMetricType];
const meterNumber = quiz?.config[configName];
setCurrentValue(meterNumber ? meterNumber.toString() : "");
setIsSave(!!meterNumber);
}, [companyName]);
return (
<Dialog
open={isModalOpen}
onClose={handleClose}
fullWidth
PaperProps={{
sx: {
maxWidth: isTablet ? "100%" : "920px",
borderRadius: "12px",
},
}}
>
<Box
sx={{
width: "100%",
height: "68px",
backgroundColor: theme.palette.background.default,
}}
>
<Typography
sx={{
fontSize: isMobile ? "20px" : "24px",
fontWeight: "500",
padding: "20px",
color: theme.palette.grey2.main,
}}
>
Аналитика с {analyticTexts[companyName]?.header}
</Typography>
</Box>
<IconButton
onClick={handleClose}
sx={{
width: "12px",
height: "12px",
position: "absolute",
right: "15px",
top: "15px",
}}
>
<CloseIcon
sx={{ width: "12px", height: "12px", transform: "scale(1.5)" }}
/>
</IconButton>
<Box
sx={{
display: "flex",
flexDirection: "column",
padding: "20px 20px",
flexGrow: 1,
gap: "20px",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "10px",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
maxWidth: isMobile ? "100%" : "590px",
position: "relative",
}}
>
<Typography fontWeight={500}>
{isSave
? `Ваш номер ${analyticTexts[companyName]?.counterName}`
: `Введите номер ${analyticTexts[companyName]?.counterName}`}
</Typography>
{isSave && (
<Box
sx={{
display: "flex",
gap: "10px",
position: "absolute",
right: "0",
top: "0",
}}
>
<IconButton onClick={handleEdit} sx={{ padding: "0" }}>
<EditPencil
color={theme.palette.brightPurple.main}
width={"24px"}
height={"24px"}
/>
</IconButton>
<IconButton onClick={handleClear} sx={{ padding: "0" }}>
<Trash
sx={{
width: "24px",
"& path": {
stroke: theme.palette.brightPurple.main,
},
}}
/>
</IconButton>
</Box>
)}
</Box>
<Box
sx={{
display: "flex",
flexDirection: isMobile ? "column" : "row",
gap: "20px",
alignItems: "center",
}}
>
<CustomTextField
sxForm={{ maxWidth: isMobile ? "100%" : "590px" }}
placeholder={isSave ? currentValue : "в формате ХХХХХХХХ"}
type={"number"}
value={currentValue}
disabled={isSave}
onChange={(e) => {
const onlyNums = e.target.value.replace(/[^0-9]/g, "");
setCurrentValue(onlyNums);
}}
/>
{!isSave && (
<Box
sx={{
width: isMobile ? "100%" : "auto",
display: "flex",
justifyContent: isMobile ? "space-between" : "end",
gap: "10px",
}}
>
{meterNumber && !isSave && (
<Button
sx={{ width: isMobile ? "100%" : "130px", height: "44px" }}
onClick={handleClose}
variant={"outlined"}
>
Отмена
</Button>
)}
<Button
sx={{ width: isMobile ? "100%" : "130px", height: "44px" }}
variant={"contained"}
onClick={handleSave}
>
Сохранить
</Button>
</Box>
)}
</Box>
</Box>
<InstructionsBlock
headerText={analyticTexts[companyName]?.instructionTitle}
subHeaderText={analyticTexts[companyName]?.instructionSubTitle}
instructionTitle={analyticTexts[companyName]?.instructionHeader}
instructionsText={analyticTexts[companyName]?.instructionText}
/>
</Box>
</Dialog>
);
}

@ -0,0 +1,88 @@
import Box from "@mui/material/Box";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import { Typography, useMediaQuery, useTheme } from "@mui/material";
import React, { FC, useState } from "react";
type InstructionsBlockProps = {
headerText: string;
subHeaderText: string;
instructionTitle: string;
instructionsText: string;
};
export const InstructionsBlock: FC<InstructionsBlockProps> = ({
headerText,
instructionsText,
subHeaderText,
instructionTitle,
}) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(600));
const [isInstructionOpen, setIsInstructionOpen] = useState(false);
return (
<Box>
<Box
onClick={() => setIsInstructionOpen(!isInstructionOpen)}
sx={{
cursor: "pointer",
backgroundColor: theme.palette.background.default,
borderRadius: isInstructionOpen ? "12px 12px 0 0" : "12px",
padding: "20px",
border: "1px solid #E5E5E5",
borderBottom: isInstructionOpen ? "none" : "1px solid #E5E5E5",
position: "relative",
}}
>
{isMobile && (
<KeyboardArrowUpIcon
fontSize="medium"
sx={{
position: "absolute",
top: "20px",
right: "20px",
transition: "transform 0.3s",
transform: isInstructionOpen ? "rotate(0deg" : "rotate(180deg)",
}}
/>
)}
<Typography
sx={{
marginBottom: "4px",
fontWeight: "500",
width: isMobile ? "90%" : "100%",
}}
>
{headerText}
</Typography>
<Typography sx={{ fontSize: "16px", color: theme.palette.grey2.main }}>
{subHeaderText}
</Typography>
</Box>
{isInstructionOpen && (
<Box
sx={{
borderRadius: " 0 0 12px 12px",
padding: "20px",
border: "1px solid #E5E5E5",
borderTop: isInstructionOpen ? "none" : "1px solid #E5E5E5",
maxHeight: isMobile ? "240px" : "300px",
overflowY: "auto",
}}
>
<Typography
sx={{
fontWeight: "500",
color: theme.palette.grey3.main,
marginBottom: "15px",
}}
>
{instructionTitle}
</Typography>
<Typography sx={{ color: theme.palette.grey3.main }}>
{instructionsText}
</Typography>
</Box>
)}
</Box>
);
};

@ -1,7 +1,10 @@
import { Box, Typography, useTheme } from "@mui/material"; import { Box, Typography, useTheme } from "@mui/material";
import { FC } from "react"; import { FC } from "react";
import { YandexButton } from "../IntegrationYandex/YandexButton"; import { ServiceButton } from "./ServiceButton/ServiceButton";
import YandexModal from "../IntegrationYandex/YandexModal"; import { YandexMetricaLogo } from "../mocks/YandexMetricaLogo";
import AnalyticsModal from "./AnalyticsModal/AnalyticsModal";
import { VKPixelLogo } from "../mocks/VKPixelLogo";
import { QuizMetricType } from "@model/quizSettings";
export type Partner = { export type Partner = {
name: string; name: string;
@ -12,7 +15,8 @@ export type Partner = {
type PartnersBoardProps = { type PartnersBoardProps = {
partners: Partner[]; partners: Partner[];
setIsModalOpen: (value: boolean) => void; setIsModalOpen: (value: boolean) => void;
setCompanyName: (value: string) => void; companyName: keyof typeof QuizMetricType | null;
setCompanyName: (value: keyof typeof QuizMetricType) => void;
isModalOpen: boolean; isModalOpen: boolean;
handleCloseModal: () => void; handleCloseModal: () => void;
}; };
@ -22,17 +26,18 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
setIsModalOpen, setIsModalOpen,
isModalOpen, isModalOpen,
handleCloseModal, handleCloseModal,
companyName,
setCompanyName, setCompanyName,
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const partnersByCategory = partners.reduce( // const partnersByCategory = partners.reduce(
(acc, partner) => { // (acc, partner) => {
(acc[partner.category] = acc[partner.category] || []).push(partner); // (acc[partner.category] = acc[partner.category] || []).push(partner);
return acc; // return acc;
}, // },
{} as Record<string, Partner[]>, // {} as Record<string, Partner[]>,
); // );
return ( return (
<Box <Box
@ -74,21 +79,47 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
{/* </Box>*/} {/* </Box>*/}
{/* </Box>*/} {/* </Box>*/}
{/*))}*/} {/*))}*/}
<Typography
variant="h6" <Box>
sx={{ <Typography
textAlign: { xs: "center", sm: "start", md: "start" }, variant="h6"
lineHeight: "1", sx={{
marginBottom: "12px", textAlign: { xs: "start", sm: "start", md: "start" },
}} lineHeight: "1",
> marginBottom: "12px",
Аналитика }}
</Typography> >
<YandexButton setIsModalOpen={setIsModalOpen} /> Аналитика
<YandexModal </Typography>
isModalOpen={isModalOpen} <Box
handleCloseModal={handleCloseModal} sx={{
/> display: "flex",
flexWrap: "wrap",
justifyContent: { xs: "start", sm: "start", md: "start" },
}}
>
<ServiceButton
logo={<YandexMetricaLogo />}
setIsModalOpen={setIsModalOpen}
name={"yandex"}
setCompanyName={setCompanyName}
/>
<ServiceButton
logo={<VKPixelLogo />}
title={"VK Пиксель"}
name={"vk"}
setIsModalOpen={setIsModalOpen}
setCompanyName={setCompanyName}
></ServiceButton>
</Box>
</Box>
{companyName && (
<AnalyticsModal
isModalOpen={isModalOpen}
handleCloseModal={handleCloseModal}
companyName={companyName}
/>
)}
</Box> </Box>
); );
}; };

@ -0,0 +1,57 @@
import { Box, Typography, useTheme } from "@mui/material";
import { FC } from "react";
import { QuizMetricType } from "@model/quizSettings";
type PartnerItemProps = {
setIsModalOpen: (value: boolean) => void;
setCompanyName: (value: keyof typeof QuizMetricType) => void;
logo?: JSX.Element;
title?: string;
name: string;
};
export const ServiceButton: FC<PartnerItemProps> = ({
setIsModalOpen,
logo,
title,
name,
setCompanyName,
}) => {
const theme = useTheme();
const handleClick = () => {
setCompanyName(name as keyof typeof QuizMetricType);
setIsModalOpen(true);
};
return (
<>
<Box
sx={{
width: 250,
height: 60,
backgroundColor: "white",
borderRadius: "8px",
padding: "0 20px",
display: "flex",
alignItems: "center",
marginBottom: "2%",
marginRight: "2%",
cursor: "pointer",
}}
onClick={handleClick}
>
{logo && logo}
<Typography
sx={{
fontSize: "18px",
fontWeight: "400",
marginLeft: "15px",
}}
>
{title && title}
</Typography>
</Box>
</>
);
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 65 KiB

@ -0,0 +1,6 @@
import React from "react";
import { ReactComponent as VKLogo } from "./VKLogo.svg";
export const VKPixelLogo = () => {
return <VKLogo />;
};

@ -50,19 +50,38 @@ export default function AvailablePrivilege() {
const squizHideBadge = userPrivileges?.squizHideBadge?.amount || 0; const squizHideBadge = userPrivileges?.squizHideBadge?.amount || 0;
//Где дни - amount - это на сколько дней выдан безлимит. т.е. не сколько осталось, а на сколько дней выдано //Где дни - amount - это на сколько дней выдан безлимит. т.е. не сколько осталось, а на сколько дней выдано
function getCramps (amount: number, created_at: string) { function getCramps(amount: number, created_at: string) {
if (created_at.length === 0) return 0 if (created_at.length === 0) return 0;
const currentDate = moment() const currentDate = moment();
return Number((moment(moment(created_at).add(amount, "days").diff(currentDate)).unix() / 86400).toFixed(1)) return Number(
(
moment(
moment(created_at).add(amount, "days").diff(currentDate),
).unix() / 86400
).toFixed(1),
);
} }
const quizUnlimDays = getCramps(quizUnlimTime, userPrivileges?.quizUnlimTime?.created_at || "") const quizUnlimDays = getCramps(
const squizBadgeDays = getCramps(squizHideBadge, userPrivileges?.squizHideBadge?.created_at || "") quizUnlimTime,
userPrivileges?.quizUnlimTime?.created_at || "",
);
const squizBadgeDays = getCramps(
squizHideBadge,
userPrivileges?.squizHideBadge?.created_at || "",
);
const currentDate = moment() const currentDate = moment();
console.log(quizUnlimDays) console.log(quizUnlimDays);
console.log(moment()) console.log(moment());
console.log(moment(moment(userPrivileges?.quizUnlimTime?.created_at).add(quizUnlimTime, "days"))) console.log(
moment(
moment(userPrivileges?.quizUnlimTime?.created_at).add(
quizUnlimTime,
"days",
),
),
);
return ( return (
<Box <Box
@ -82,12 +101,9 @@ export default function AvailablePrivilege() {
<Typography variant={"body1"} sx={{ color: "#4D4D4D" }}> <Typography variant={"body1"} sx={{ color: "#4D4D4D" }}>
Безлимитные заявки:{" "} Безлимитные заявки:{" "}
<strong> <strong>
{ {quizUnlimDays > 0 && quizUnlimDays < 1
quizUnlimDays > 0 && quizUnlimDays < 1 ? ? "последний день"
"последний день" : `${Math.trunc(quizUnlimDays)} ${declOfNum(Math.trunc(quizUnlimDays), DayForm)}`}
:
`${Math.trunc(quizUnlimDays)} ${declOfNum(Math.trunc(quizUnlimDays), DayForm)}`
}
</strong> </strong>
</Typography> </Typography>
{quizCnt !== 0 && ( {quizCnt !== 0 && (
@ -99,12 +115,9 @@ export default function AvailablePrivilege() {
<Typography variant={"body1"} sx={{ color: "#4D4D4D" }}> <Typography variant={"body1"} sx={{ color: "#4D4D4D" }}>
Скрытие логотипа PenaQuiz:{" "} Скрытие логотипа PenaQuiz:{" "}
<strong> <strong>
{ {squizBadgeDays > 0 && squizBadgeDays < 1
squizBadgeDays > 0 && squizBadgeDays < 1 ? ? "последний день"
"последний день" : `${Math.trunc(squizBadgeDays)} ${declOfNum(Math.trunc(squizBadgeDays), DayForm)}`}
:
`${Math.trunc(squizBadgeDays)} ${declOfNum(Math.trunc(squizBadgeDays), DayForm)}`
}
</strong> </strong>
</Typography> </Typography>
)} )}

@ -72,7 +72,6 @@ export const SidebarMobile: FC<Iprops> = ({
}; };
const clickInput = (event: MouseEvent) => { const clickInput = (event: MouseEvent) => {
debugger;
if (ref.current && !ref.current?.contains(event.target as Node)) if (ref.current && !ref.current?.contains(event.target as Node))
setInputOpen(false); setInputOpen(false);
}; };