add container widget installation setup

This commit is contained in:
nflnkr 2024-05-17 16:18:11 +03:00
parent 5c5890cfb6
commit a92f04a3b4
14 changed files with 734 additions and 399 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.41",
"@mui/icons-material": "^5.10.14",
"@mui/material": "^5.10.14",
"@mui/x-charts": "^6.19.5",

@ -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>
);
}

@ -1,21 +1,94 @@
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 {
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 { useState } from "react";
import Dots from "../../assets/dots.png";
import WidgetScript from "./QuizInstallationCard/WidgetScript";
import { createContainerWidgetScriptText } from "./QuizInstallationCard/createWidgetScriptText";
export default function InBodyInstall() {
interface Props {
step: 2 | 3;
}
export default function InBodyInstall({ step }: 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} />;
}
return (
<InstallQzOnSiteParent>
<Box sx={{ maxWidth: "560px" }}>
<Box
sx={{
display: "flex",
flexDirection: isSmallMonitor ? "column" : "row",
gap: "40px",
p: "20px",
}}
>
<Box
sx={{
flexGrow: 1,
}}
>
<Box
sx={{
border: "1.5px solid #9A9AAF",
borderRadius: "6px",
maxWidth: "560px",
height: "364px",
}}
>
<Box
@ -41,19 +114,20 @@ export default function InBodyInstall() {
alignItems: "center",
gap: "40px",
height: "100%",
p: "12%",
pb: "7%",
}}
>
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
maxWidth: "413px",
minHeight: "133px",
background: "#EEE4FC",
borderRadius: "6px",
border: "1px dashed #7E2AEA",
p: "40px",
}}
>
<Typography
@ -67,45 +141,64 @@ export default function InBodyInstall() {
</Typography>
</Box>
<Typography sx={{ maxWidth: "414px", fontSize: "14px" }}>
<Typography
sx={{ maxWidth: "414px", fontSize: "14px", alignSelf: "start" }}
>
В мобильной версии будет показана кнопка, открывающая 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",
flexDirection: "column",
gap: "15px",
flexGrow: 1,
}}
>
<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>
<CustomTextField placeholder={"auto"} />
<Typography sx={{ color: theme.palette.grey2.main }}>
Радиус (px)
</Typography>
<CustomTextField placeholder={"0"} />
<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>
<CustomTextField placeholder={"auto"} />
<Typography sx={{ color: theme.palette.grey2.main }}>
Отступ (px)
</Typography>
<CustomTextField placeholder={"0"} />
<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" }}>
<Typography>2. Настройте кнопку для мобильной версии</Typography>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
alignItems: "start",
}}
>
<Typography fontWeight={500} color="#4D4D4D">
2. Настройте кнопку для мобильной версии
</Typography>
<Box
sx={{
display: "flex",
@ -115,55 +208,128 @@ export default function InBodyInstall() {
<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>
<CircleColorPicker
color={buttonBackgroundColor}
onChange={(color) => setButtonBackgroundColor(color)}
/>
<Typography sx={{ color: theme.palette.grey2.main }}>
Цвет текста кнопки
</Typography>
<Box
sx={{
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: "#ffffff",
border: "1px solid #4D4D4D",
}}
>
{" "}
</Box>
<CircleColorPicker
color={buttonTextColor}
onChange={(color) => setButtonTextColor(color)}
/>
</Box>
<CustomCheckbox label={"Отключить на мобильных устройствах"} />
<CustomCheckbox label={"Закругленная"} />
<CustomCheckbox label={"С тенью"} />
<CustomCheckbox label={"С бликом"} />
<CustomCheckbox label={"Кнопка на мобильной версии"} />
<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>
<CustomTextField placeholder={"Пройти тест"} />
<Link
component="button"
<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
</Link>
</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>
</InstallQzOnSiteParent>
</Box>
);
}

@ -17,7 +17,7 @@ export default function InstallationStepButton({
}: Props) {
const theme = useTheme();
const buttonColorByState: Record<string, string> = {
const buttonColorByState: Record<Props["state"], string> = {
active: "#FC712F",
done: theme.palette.brightPurple.main,
inactive: theme.palette.grey2.main,
@ -28,13 +28,29 @@ export default function InstallationStepButton({
return (
<Button
onClick={onClick}
startIcon={<NumberIcon number={index} color={color} />}
sx={{
color: buttonColorByState[state],
"& .MuiButton-startIcon": {
mr: "5px",
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,17 +1,8 @@
import {
Box,
IconButton,
InputAdornment,
TextField as MuiTextField,
TextFieldProps,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { WidgetType } from "@frontend/squzanswerer";
import { Box, 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 { useState } from "react";
import InBodyInstall from "../InBodyInstall";
import InstallationStepButton from "./InstallationStepButton";
import WidgetTypeButton from "./WidgetTypeButton";
import BannerWidgetPreview from "./previewIcons/BannerWidgetPreview";
@ -20,19 +11,20 @@ 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<1 | 2 | 3>(1);
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const quiz = useCurrentQuiz();
const [widgetSetupSettings, setWidgetSetupSettings] =
useState<WidgetSetupSettings>({ step: 1, widgetType: "container" });
if (!quiz) return null;
const scriptText = `<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>`;
return (
<Box
sx={{
@ -45,183 +37,141 @@ export default function QuizInstallationCard() {
>
<Box
sx={{
py: "10px",
pt: "20px",
pb: "10px",
px: "20px",
display: "flex",
alignItems: isSmallMonitor ? "flex-start" : "center",
gap: "11px",
gap: "20px",
flexDirection: isSmallMonitor ? "column" : "row",
}}
>
<Typography variant="h5" mr="9px">
Установка quiz на сайте
</Typography>
<InstallationStepButton
state={stepState === 1 ? "active" : "done"}
index={1}
onClick={() => {
setStepState(1);
<Typography variant="h5">Установка quiz на сайте</Typography>
<Box
sx={{
display: "flex",
gap: isSmallMonitor ? "0" : "11px",
alignItems: isSmallMonitor ? "flex-start" : "center",
flexDirection: isSmallMonitor ? "column" : "row",
}}
>
Способ установки
</InstallationStepButton>
<InstallationStepButton
state={
stepState === 1 ? "inactive" : stepState === 2 ? "active" : "done"
}
index={2}
onClick={() => {
setStepState(2);
}}
>
Настройка кнопки
</InstallationStepButton>
<InstallationStepButton
state={stepState === 3 ? "active" : "inactive"}
index={3}
onClick={() => {
setStepState(3);
}}
>
Вставить код на сайт
</InstallationStepButton>
</Box>
<Box
sx={{
px: "12px",
pt: "4px",
pb: "20px",
display: "flex",
flexWrap: "wrap",
gap: "14px",
}}
>
{stepState === 1 ? (
<>
<WidgetTypeButton
image={<ButtonWidgetPreview />}
text1="По кнопке"
text2="Конструктор кнопки или собственная кнопка"
onClick={() => {
setStepState(2);
}}
/>
<WidgetTypeButton
image={<BannerWidgetPreview />}
text1="Баннером"
text2="Сбоку или на всю ширину экрана"
onClick={() => {
setStepState(2);
}}
/>
<WidgetTypeButton
image={<ContainerWidgetPreview />}
text1="В тело сайта"
text2="Задайте свои размеры и встройте в сайт"
onClick={() => {
setStepState(2);
}}
/>
<WidgetTypeButton
image={<PopupWidgetPreview />}
text1="Автооткрытие"
text2="Автооткрытие поп-ап на сайте"
onClick={() => {
setStepState(2);
}}
/>
<WidgetTypeButton
image={<SideWidgetPreview />}
text1="Виджет"
text2="Сбоку страницы, как консультант"
onClick={() => {
setStepState(2);
}}
/>
</>
) : (
<>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
flexDirection: "column",
}}
>
<Box
sx={{
maxWidth: "520px",
width: "100%",
display: "flex",
flexDirection: "column",
gap: "20px",
<InstallationStepButton
state={widgetSetupSettings.step === 1 ? "active" : "done"}
index={1}
onClick={() => {
setWidgetSetupSettings({
step: 1,
widgetType: "container",
});
}}
>
Способ установки
</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
multiline
rows={9}
value={scriptText}
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(scriptText)
}
>
<CopyIcon
color={"#ffffff"}
bgcolor={theme.palette.brightPurple.main}
/>
</IconButton>
</InputAdornment>
),
}}
/>
</Box>
</Box>
<Box
sx={{
background: "#EEE4FC",
border: "1px solid #7E2AEA",
p: "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" && (
<InBodyInstall step={widgetSetupSettings.step} />
)}
</>
)}
</Box>
);
}

@ -0,0 +1,66 @@
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;
}
export default function WidgetScript({ scriptText }: Props) {
const theme = useTheme();
return (
<Box
sx={{
p: "20px",
}}
>
<Typography color="#9A9AAF">
Установите код в место, в котором должен быть квиз
</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>
);
}

@ -1,4 +1,11 @@
import { Button, ButtonBase, Typography } from "@mui/material";
import {
Box,
Button,
ButtonBase,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { ReactNode } from "react";
interface Props {
@ -14,29 +21,43 @@ export default function WidgetTypeButton({
text1,
text2,
}: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
return (
<Button
onClick={onClick}
sx={{
display: "flex",
flexDirection: "column",
flexDirection: isSmallMonitor ? "row" : "column",
alignItems: "start",
width: "min-content",
justifyContent: "start",
width: isSmallMonitor ? undefined : "min-content",
borderRadius: "8px",
columnGap: "18px",
}}
>
{image}
<Typography mt="20px">{text1}</Typography>
<Typography
<Box
sx={{
mt: "15px",
fontSize: "16px",
color: (theme) => theme.palette.grey2.main,
textAlign: "start",
mt: isSmallMonitor ? undefined : "20px",
display: "flex",
flexDirection: "column",
alignItems: "start",
}}
>
{text2}
</Typography>
<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}"></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 `<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,4 +1,4 @@
import { Box } from "@mui/material";
import { Box, useMediaQuery, useTheme } from "@mui/material";
import { ReactNode } from "react";
interface Props {
@ -6,10 +6,13 @@ interface Props {
}
export default function WidgetPreviewContainer({ children }: Props) {
const theme = useTheme();
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
return (
<Box
sx={{
width: "200px",
width: isSmallMonitor ? "90px" : "200px",
aspectRatio: "204 / 134",
display: "flex",
alignItems: "center",
@ -17,7 +20,12 @@ export default function WidgetPreviewContainer({ children }: Props) {
flexShrink: 0,
}}
>
<svg viewBox="0 0 204 134" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
viewBox="0 0 204 134"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ width: "100%" }}
>
{children}
</svg>
</Box>

@ -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: "20px",
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>
);
}

@ -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>
);
}

@ -1527,10 +1527,10 @@
immer "^10.0.2"
reconnecting-eventsource "^1.6.2"
"@frontend/squzanswerer@^1.0.38":
version "1.0.38"
resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/43/packages/npm/@frontend/squzanswerer/-/@frontend/squzanswerer-1.0.38.tgz#51f382368d73f9ad36c4f7f3357dedb32d5453e8"
integrity sha1-UfOCNo1z+a02xPfzNX3tsy1UU+g=
"@frontend/squzanswerer@^1.0.41":
version "1.0.41"
resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/43/packages/npm/@frontend/squzanswerer/-/@frontend/squzanswerer-1.0.41.tgz#d535b03c0a432a2c427c99083a6ec9b1e64e060e"
integrity sha1-1TWwPApDKixCfJkIOm7JseZOBg4=
dependencies:
bowser "1.9.4"
country-flag-emoji-polyfill "^0.1.8"