Merge branch 'dev' into 'staging'
Dev See merge request frontend/squiz!337
This commit is contained in:
commit
40c029da34
@ -40,6 +40,7 @@
|
||||
"notistack": "^3.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-cytoscapejs": "^2.0.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
|
||||
@ -256,7 +256,7 @@ export const getIntegrationRules = async (
|
||||
method: "GET",
|
||||
url: `${API_URL}/rules/${quizID}`,
|
||||
});
|
||||
return [settingsResponse];
|
||||
return [settingsResponse || null];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить настройки интеграции. ${error}`];
|
||||
@ -331,3 +331,18 @@ export const getCustomFields = async (
|
||||
return [null, `Не удалось получить список кастомных полей. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
//Отвязать аккаунт амо от публикации
|
||||
|
||||
export const removeAmoAccount = async (): Promise<[void | null, string?]> => {
|
||||
try {
|
||||
await makeRequest<void>({
|
||||
method: "DELETE",
|
||||
url: `${API_URL}/delete`,
|
||||
});
|
||||
return [null, ""];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось отвязать аккаунт. ${error}`];
|
||||
}
|
||||
};
|
||||
@ -96,6 +96,10 @@ export type AnyTypedQuizQuestion =
|
||||
| QuizQuestionRating
|
||||
| QuizQuestionResult;
|
||||
|
||||
export type AllTypesQuestion =
|
||||
| AnyTypedQuizQuestion
|
||||
| UntypedQuizQuestion;
|
||||
|
||||
type FilterQuestionsWithVariants<T> = T extends {
|
||||
content: { variants: QuestionVariant[] };
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { QuizQuestionBase, QuestionHint, PreviewRule } from "./shared";
|
||||
import type { PreviewRule, QuestionHint, QuizQuestionBase } from "./shared";
|
||||
|
||||
export interface QuizQuestionText extends QuizQuestionBase {
|
||||
type: "text";
|
||||
@ -13,7 +13,7 @@ export interface QuizQuestionText extends QuizQuestionBase {
|
||||
required: boolean;
|
||||
/** Чекбокс "Автозаполнение адреса" */
|
||||
autofill: boolean;
|
||||
answerType: "single" | "multi";
|
||||
answerType: "single" | "multi" | "numberOnly";
|
||||
hint: QuestionHint;
|
||||
rule: PreviewRule;
|
||||
back: string;
|
||||
|
||||
182
src/pages/InstallQuiz/QuizInstallationCard/WidgetSetupByType/BannerWidgetSetup/BannerWidgetSetup.tsx
182
src/pages/InstallQuiz/QuizInstallationCard/WidgetSetupByType/BannerWidgetSetup/BannerWidgetSetup.tsx
@ -37,21 +37,17 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
const quiz = useCurrentQuiz();
|
||||
const [widgetWidth, setWidgetWidth] = useState<string>("");
|
||||
const [widgetHeight, setWidgetHeight] = useState<string>("");
|
||||
const [isAutoopenQuizSettingsOpen, setIsAutoopenQuizSettingsOpen] =
|
||||
useState<boolean>(false);
|
||||
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 [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 [position, setPosition] = useState<BannerWidgetParams["position"]>("bottomleft");
|
||||
const [bannerFullWidth, setBannerFullWidth] = useState<boolean>(false);
|
||||
const [autoShowWidgetTime, setAutoShowWidgetTime] = useState<number>(10);
|
||||
const [appealText, setAppealText] = useState<string>("");
|
||||
@ -88,12 +84,7 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
: undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<WidgetScript
|
||||
scriptText={scriptText}
|
||||
helperText="Установите код внутрь тэга head"
|
||||
/>
|
||||
);
|
||||
return <WidgetScript scriptText={scriptText} helperText="Установите код внутрь тэга head" />;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -129,17 +120,39 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
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 && {
|
||||
position === "topleft" && {
|
||||
top: "calc(12.5 / 262 * 100%)",
|
||||
left: "calc(50.6 / 520 * 100%)",
|
||||
},
|
||||
position === "topright" && {
|
||||
top: "calc(12.5 / 262 * 100%)",
|
||||
left: "calc(190 / 520 * 100%)",
|
||||
},
|
||||
position === "bottomleft" && {
|
||||
top: "calc(178 / 262 * 100%)",
|
||||
left: "calc(50.6 / 520 * 100%)",
|
||||
},
|
||||
position === "bottomright" && {
|
||||
top: "calc(178 / 262 * 100%)",
|
||||
left: "calc(190 / 520 * 100%)",
|
||||
},
|
||||
bannerFullWidth &&
|
||||
position.startsWith("top") && {
|
||||
top: "calc(7 / 262 * 100%)",
|
||||
left: "calc(45 / 520 * 100%)",
|
||||
width: "calc(348 / 520 * 100%)",
|
||||
height: "calc(30 / 262 * 100%)",
|
||||
},
|
||||
bannerFullWidth &&
|
||||
position.startsWith("bottom") && {
|
||||
top: "calc(185 / 262 * 100%)",
|
||||
left: "calc(45 / 520 * 100%)",
|
||||
width: "calc(348 / 520 * 100%)",
|
||||
height: "calc(30 / 262 * 100%)",
|
||||
},
|
||||
pulsation && {
|
||||
":before": {
|
||||
content: "''",
|
||||
@ -196,16 +209,10 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
fontSize="calc(6 / 29.5 * 100cqh)"
|
||||
lineHeight="120%"
|
||||
>
|
||||
<Typography fontSize="calc(6 / 29.5 * 100cqh)" lineHeight="120%">
|
||||
{appealText || "Пройти тест"}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="calc(11 / 29.5 * 100cqh)"
|
||||
lineHeight="120%"
|
||||
>
|
||||
<Typography fontSize="calc(11 / 29.5 * 100cqh)" lineHeight="120%">
|
||||
{quizHeaderText || "Заголовок теста"}
|
||||
</Typography>
|
||||
</Box>
|
||||
@ -220,8 +227,7 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
height: "calc(10 / 29.5 * 100%)",
|
||||
width: "auto",
|
||||
aspectRatio: 1,
|
||||
backgroundColor:
|
||||
rounded || bannerFullWidth ? "#581CA7" : undefined,
|
||||
backgroundColor: rounded || bannerFullWidth ? "#581CA7" : undefined,
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
/>
|
||||
@ -255,9 +261,7 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
Ширина окна (px)
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>Ширина окна (px)</Typography>
|
||||
<PenaTextField
|
||||
type="number"
|
||||
value={widgetWidth}
|
||||
@ -273,9 +277,7 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
Высота окна (px)
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>Высота окна (px)</Typography>
|
||||
<PenaTextField
|
||||
type="number"
|
||||
value={widgetHeight}
|
||||
@ -286,18 +288,14 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
</Box>
|
||||
</Box>
|
||||
</Collapse>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
Текст-призыв
|
||||
</Typography>
|
||||
<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>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>Заголовок quiz</Typography>
|
||||
<PenaTextField
|
||||
placeholder="Заголовок quiz"
|
||||
value={quizHeaderText}
|
||||
@ -311,9 +309,7 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
Показывать через
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>Показывать через</Typography>
|
||||
<PenaTextField
|
||||
type="number"
|
||||
value={autoShowWidgetTime}
|
||||
@ -322,9 +318,7 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
width: "90px",
|
||||
}}
|
||||
/>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
секунд
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>секунд</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
@ -333,20 +327,10 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
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)}
|
||||
/>
|
||||
<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="Баннер на всю ширину экрана"
|
||||
@ -356,9 +340,7 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
if (e.target.checked) setPosition("topleft");
|
||||
}}
|
||||
/>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
Расположение
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>Расположение</Typography>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
value={position}
|
||||
@ -384,17 +366,13 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
<FormControlLabel
|
||||
label="Сверху страницы"
|
||||
value="topleft"
|
||||
control={
|
||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
||||
}
|
||||
control={<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />}
|
||||
sx={{ color: theme.palette.grey2.main }}
|
||||
/>
|
||||
<FormControlLabel
|
||||
label="Снизу страницы"
|
||||
value="bottomleft"
|
||||
control={
|
||||
<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />
|
||||
}
|
||||
control={<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />}
|
||||
sx={{ color: theme.palette.grey2.main }}
|
||||
/>
|
||||
</Box>
|
||||
@ -409,23 +387,13 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
<FormControlLabel
|
||||
label="Слева сверху"
|
||||
value="topleft"
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck />}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
}
|
||||
control={<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />}
|
||||
sx={{ color: theme.palette.grey2.main }}
|
||||
/>
|
||||
<FormControlLabel
|
||||
label="Справа сверху"
|
||||
value="topright"
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck />}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
}
|
||||
label="Слева снизу"
|
||||
value="bottomleft"
|
||||
control={<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />}
|
||||
sx={{ color: theme.palette.grey2.main }}
|
||||
/>
|
||||
</Box>
|
||||
@ -436,25 +404,15 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
}}
|
||||
>
|
||||
<FormControlLabel
|
||||
label="Слева снизу"
|
||||
value="bottomleft"
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck />}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
}
|
||||
label="Справа сверху"
|
||||
value="topright"
|
||||
control={<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />}
|
||||
sx={{ color: theme.palette.grey2.main }}
|
||||
/>
|
||||
<FormControlLabel
|
||||
label="Справа снизу"
|
||||
value="bottomright"
|
||||
control={
|
||||
<Radio
|
||||
checkedIcon={<RadioCheck />}
|
||||
icon={<RadioIcon />}
|
||||
/>
|
||||
}
|
||||
control={<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />}
|
||||
sx={{ color: theme.palette.grey2.main }}
|
||||
/>
|
||||
</Box>
|
||||
@ -462,26 +420,12 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
)}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
Параметры
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>Параметры</Typography>
|
||||
{!bannerFullWidth && (
|
||||
<CustomCheckbox
|
||||
label="Закругленная"
|
||||
checked={rounded}
|
||||
handleChange={(e) => setRounded(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={withShadow} handleChange={(e) => setWithShadow(e.target.checked)} />
|
||||
<CustomCheckbox label="С бликом" checked={buttonFlash} handleChange={(e) => setButtonFlash(e.target.checked)} />
|
||||
<CustomCheckbox
|
||||
label={'Эффект "пульсация"'}
|
||||
checked={pulsation}
|
||||
@ -546,22 +490,16 @@ export default function BannerWidgetSetup({ step, nextButton }: Props) {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
Показывать через
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>Показывать через</Typography>
|
||||
<PenaTextField
|
||||
type="number"
|
||||
value={autoShowQuizTime}
|
||||
onChange={(e) =>
|
||||
setAutoShowQuizTime(parseInt(e.target.value))
|
||||
}
|
||||
onChange={(e) => setAutoShowQuizTime(parseInt(e.target.value))}
|
||||
FormControlSx={{
|
||||
width: "90px",
|
||||
}}
|
||||
/>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
секунд
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>секунд</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Collapse>
|
||||
|
||||
@ -8,24 +8,29 @@ import {
|
||||
import React, { FC, useEffect, useMemo, useState } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { AmoRemoveAccount } from "./AmoRemoveAccount/AmoRemoveAccount";
|
||||
import { AmoLogin } from "./AmoLogin/AmoLogin";
|
||||
import { AmoStep2 } from "./AmoStep2/AmoStep2";
|
||||
import { AmoStep3 } from "./AmoStep3/AmoStep3";
|
||||
import { AmoStep4 } from "./AmoStep4/AmoStep4";
|
||||
import { AmoStep6 } from "./IntegrationStep6/AmoStep6";
|
||||
import { AmoStep7 } from "./IntegrationStep7/AmoStep7";
|
||||
import { AmoModalTitle } from "./AmoModalTitle/AmoModalTitle";
|
||||
import { AmoSettingsBlock } from "./SettingsBlock/AmoSettingsBlock";
|
||||
import { AmoStep7 } from "./IntegrationStep7/AmoStep7";
|
||||
import { AmoAccountInfo } from "./AmoAccountInfo/AmoAccountInfo";
|
||||
import { AccountResponse, getAccount } from "@api/integration";
|
||||
import { AccountResponse, IntegrationRules, getAccount, getIntegrationRules } from "@api/integration";
|
||||
import { useQuestions } from "@/stores/questions/hooks";
|
||||
import { redirect } from "react-router-dom";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
export type TitleKeys = "contacts" | "company" | "deal" | "users" | "buyers";
|
||||
export type TitleKeys = "contacts" | "company" | "deal" | "buyers";
|
||||
|
||||
export type TQuestionEntity = Record<TitleKeys, string[] | []>;
|
||||
type IntegrationsModalProps = {
|
||||
isModalOpen: boolean;
|
||||
handleCloseModal: () => void;
|
||||
companyName: string | null;
|
||||
quizID: number | undefined;
|
||||
};
|
||||
|
||||
export type TagKeys = "contact" | "company" | "deal" | "buyer";
|
||||
@ -35,15 +40,20 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
isModalOpen,
|
||||
handleCloseModal,
|
||||
companyName,
|
||||
quizID,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
|
||||
const { questions } = useQuestions();
|
||||
|
||||
const [step, setStep] = useState<number>(0);
|
||||
const [isSettingsBlock, setIsSettingsBlock] = useState<boolean>(false);
|
||||
const [isRemoveAccount, setIsRemoveAccount] = useState<boolean>(false);
|
||||
|
||||
const [accountInfo, setAccountInfo] = useState<AccountResponse | null>(null);
|
||||
const [integrationRules, setIntegrationRules] = useState<IntegrationRules | null>(null);
|
||||
const [selectedPipelinePerformer, setSelectedPipelinePerformer] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
@ -56,10 +66,9 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
string | null
|
||||
>(null);
|
||||
const [questionEntity, setQuestionEntity] = useState<TQuestionEntity>({
|
||||
deal: [],
|
||||
contacts: [],
|
||||
company: [],
|
||||
deal: [],
|
||||
users: [],
|
||||
buyers: [],
|
||||
});
|
||||
const [tags, setTags] = useState<TTags>({
|
||||
@ -68,21 +77,38 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
company: [],
|
||||
buyer: [],
|
||||
});
|
||||
console.log(accountInfo)
|
||||
console.log(questionEntity)
|
||||
console.log(tags)
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen) {
|
||||
if (isModalOpen && quizID !== undefined && !isRemoveAccount) {
|
||||
const fetchAccount = async () => {
|
||||
const [account, error] = await getAccount();
|
||||
if (account && !error) {
|
||||
setAccountInfo(account);
|
||||
} else {
|
||||
|
||||
if (error) {
|
||||
enqueueSnackbar(error)
|
||||
setAccountInfo(null);
|
||||
}
|
||||
};
|
||||
fetchAccount();
|
||||
if (account) {
|
||||
setAccountInfo(account);
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
};
|
||||
const fetchRules = async () => {
|
||||
const [settingsResponse, error] = await getIntegrationRules(quizID.toString());
|
||||
|
||||
if (error) {
|
||||
enqueueSnackbar(error)
|
||||
setIntegrationRules(null);
|
||||
}
|
||||
if (settingsResponse) {
|
||||
setIntegrationRules(settingsResponse);
|
||||
}
|
||||
};
|
||||
|
||||
fetchAccount();
|
||||
fetchRules();
|
||||
}
|
||||
}, [isModalOpen, isRemoveAccount]);
|
||||
|
||||
const handleNextStep = () => {
|
||||
setStep((prevState) => prevState + 1);
|
||||
@ -152,26 +178,27 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Соотнесение вопросов и сущностей",
|
||||
title: "Добавление тегов",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<AmoStep6
|
||||
questionEntity={questionEntity}
|
||||
setQuestionEntity={setQuestionEntity}
|
||||
tags={tags}
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleNextStep}
|
||||
setTags={setTags}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Добавление тегов",
|
||||
title: "Соотнесение вопросов и сущностей",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<AmoStep7
|
||||
handleSmallBtn={handlePrevStep}
|
||||
handleLargeBtn={handleSave}
|
||||
tags={tags}
|
||||
setTags={setTags}
|
||||
questionEntity={questionEntity}
|
||||
setQuestionEntity={setQuestionEntity}
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleSave}
|
||||
questions={questions}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@ -190,6 +217,9 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
|
||||
const stepTitles = steps.map((step) => step.title);
|
||||
|
||||
//Если нет контекста квиза, то и делать на этой страничке нечего
|
||||
if (quizID === undefined) redirect("/list")
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isModalOpen}
|
||||
@ -252,8 +282,14 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
isSettingsBlock={isSettingsBlock}
|
||||
setIsSettingsBlock={setIsSettingsBlock}
|
||||
setStep={setStep}
|
||||
startRemoveAccount={() => setIsRemoveAccount(true)}
|
||||
/>
|
||||
{isSettingsBlock ? (
|
||||
{isRemoveAccount && (
|
||||
<AmoRemoveAccount
|
||||
stopThisPage={() => setIsRemoveAccount(false)}
|
||||
/>
|
||||
)}
|
||||
{isSettingsBlock && (
|
||||
<Box sx={{ flexGrow: 1, width: "100%" }}>
|
||||
<AmoSettingsBlock
|
||||
stepTitles={stepTitles}
|
||||
@ -268,7 +304,8 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
tags={tags}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
)}
|
||||
{!isSettingsBlock && !isRemoveAccount && (
|
||||
<Box sx={{ flexGrow: 1, width: "100%" }}>{steps[step].component}</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@ -10,6 +10,7 @@ type AmoModalTitleProps = {
|
||||
isSettingsBlock?: boolean;
|
||||
setIsSettingsBlock: (value: boolean) => void;
|
||||
setStep: (value: number) => void;
|
||||
startRemoveAccount: () => void;
|
||||
};
|
||||
|
||||
export const AmoModalTitle: FC<AmoModalTitleProps> = ({
|
||||
@ -18,13 +19,16 @@ export const AmoModalTitle: FC<AmoModalTitleProps> = ({
|
||||
setIsSettingsBlock,
|
||||
isSettingsBlock,
|
||||
setStep,
|
||||
startRemoveAccount
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
console.log(isSettingsBlock)
|
||||
const handleClick = useCallback(async () => {
|
||||
if (isSettingsBlock) {
|
||||
setIsSettingsBlock(false);
|
||||
startRemoveAccount();
|
||||
setIsSettingsBlock(false)
|
||||
setStep(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
import { FC } from "react"
|
||||
import { Button, Typography, useTheme, Box } from "@mui/material"
|
||||
import { removeAmoAccount } from "@/api/integration";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
|
||||
interface Props {
|
||||
stopThisPage: () => void;
|
||||
|
||||
}
|
||||
|
||||
export const AmoRemoveAccount: FC<Props> = ({
|
||||
stopThisPage,
|
||||
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const removeAccount = async () => {
|
||||
const [, error] = await removeAmoAccount()
|
||||
|
||||
if (error) {
|
||||
enqueueSnackbar(error)
|
||||
} else {
|
||||
stopThisPage()
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
mt: "30px"
|
||||
}}
|
||||
>
|
||||
<Typography textAlign="center">
|
||||
Вы хотите сменить аккаунт?
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-evenly",
|
||||
flexWrap: "wrap",
|
||||
margin: "30px auto",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
width: "150px",
|
||||
}}
|
||||
onClick={stopThisPage}
|
||||
>отмена</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
width: "150px",
|
||||
}}
|
||||
onClick={removeAccount}
|
||||
>сменить</Button>
|
||||
</Box>
|
||||
</Box >
|
||||
)
|
||||
}
|
||||
@ -7,23 +7,24 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { ItemsSelectionView } from "./ItemsSelectionView/ItemsSelectionView";
|
||||
import { ItemDetailsView } from "./ItemDetailsView/ItemDetailsView";
|
||||
import { TitleKeys, TQuestionEntity } from "../AmoCRMModal";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
type AmoStep6Props = {
|
||||
handlePrevStep: () => void;
|
||||
import { TagKeys, TTags } from "../AmoCRMModal";
|
||||
import Box from "@mui/material/Box";
|
||||
import { ItemsSelectionView } from "../IntegrationStep7/ItemsSelectionView/ItemsSelectionView";
|
||||
import { TagsDetailsView } from "./TagsDetailsView/TagsDetailsView";
|
||||
|
||||
type Props = {
|
||||
handleNextStep: () => void;
|
||||
questionEntity: TQuestionEntity;
|
||||
setQuestionEntity: Dispatch<SetStateAction<TQuestionEntity>>;
|
||||
handlePrevStep: () => void;
|
||||
tags: TTags;
|
||||
setTags: Dispatch<SetStateAction<TTags>>;
|
||||
};
|
||||
|
||||
export const AmoStep6: FC<AmoStep6Props> = ({
|
||||
handlePrevStep,
|
||||
export const AmoStep6: FC<Props> = ({
|
||||
handleNextStep,
|
||||
questionEntity,
|
||||
setQuestionEntity,
|
||||
handlePrevStep,
|
||||
tags,
|
||||
setTags,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [isSelection, setIsSelection] = useState<boolean>(false);
|
||||
@ -33,14 +34,14 @@ export const AmoStep6: FC<AmoStep6Props> = ({
|
||||
const handleAdd = useCallback(() => {
|
||||
if (!activeItem || !selectedValue) return;
|
||||
|
||||
setQuestionEntity((prevState) => ({
|
||||
setTags((prevState) => ({
|
||||
...prevState,
|
||||
[activeItem]: [...prevState[activeItem as TitleKeys], selectedValue],
|
||||
[activeItem]: [...prevState[activeItem as TagKeys], selectedValue],
|
||||
}));
|
||||
}, [activeItem, setQuestionEntity, selectedValue]);
|
||||
}, [activeItem, setTags, selectedValue]);
|
||||
|
||||
const items = useMemo(
|
||||
() => ["Город", "Имя", "Фамилия", "Отчество", "Контрагент"],
|
||||
() => ["#тег с результатом 1", "#еще один тег с результатом 2", "#тег"],
|
||||
[],
|
||||
);
|
||||
|
||||
@ -58,6 +59,7 @@ export const AmoStep6: FC<AmoStep6Props> = ({
|
||||
items={items}
|
||||
selectedValue={selectedValue}
|
||||
setSelectedValue={setSelectedValue}
|
||||
type={"typeTags"}
|
||||
onSmallBtnClick={() => {
|
||||
setActiveItem(null);
|
||||
setIsSelection(false);
|
||||
@ -69,11 +71,11 @@ export const AmoStep6: FC<AmoStep6Props> = ({
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ItemDetailsView
|
||||
<TagsDetailsView
|
||||
setIsSelection={setIsSelection}
|
||||
handleNextStep={handleNextStep}
|
||||
handlePrevStep={handlePrevStep}
|
||||
questionEntity={questionEntity}
|
||||
handleNextStep={handleNextStep}
|
||||
tags={tags}
|
||||
setActiveItem={setActiveItem}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -2,19 +2,19 @@ import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
||||
import { FC } from "react";
|
||||
import { TagKeys, TTags } from "../../AmoCRMModal";
|
||||
import { Item } from "../../IntegrationStep6/Item/Item";
|
||||
import { Item } from "../../IntegrationStep7/Item/Item";
|
||||
|
||||
type TagsDetailsViewProps = {
|
||||
setIsSelection: (value: boolean) => void;
|
||||
handleSmallBtn: () => void;
|
||||
handleLargeBtn: () => void;
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
tags: TTags;
|
||||
setActiveItem: (value: string | null) => void;
|
||||
};
|
||||
|
||||
export const TagsDetailsView: FC<TagsDetailsViewProps> = ({
|
||||
handleSmallBtn,
|
||||
handleLargeBtn,
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
tags,
|
||||
setActiveItem,
|
||||
setIsSelection,
|
||||
@ -89,9 +89,8 @@ export const TagsDetailsView: FC<TagsDetailsViewProps> = ({
|
||||
}}
|
||||
>
|
||||
<StepButtonsBlock
|
||||
onSmallBtnClick={handleSmallBtn}
|
||||
onLargeBtnClick={handleLargeBtn}
|
||||
largeBtnText={"Сохранить"}
|
||||
onSmallBtnClick={handlePrevStep}
|
||||
onLargeBtnClick={handleNextStep}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -7,24 +7,26 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
import { TagKeys, TTags } from "../AmoCRMModal";
|
||||
import { ItemsSelectionView } from "./ItemsSelectionView/ItemsSelectionView";
|
||||
import { ItemDetailsView } from "./ItemDetailsView/ItemDetailsView";
|
||||
import { TitleKeys, TQuestionEntity } from "../AmoCRMModal";
|
||||
import Box from "@mui/material/Box";
|
||||
import { ItemsSelectionView } from "../IntegrationStep6/ItemsSelectionView/ItemsSelectionView";
|
||||
import { TagsDetailsView } from "./TagsDetailsView/TagsDetailsView";
|
||||
import type { AllTypesQuestion } from "@model/questionTypes/shared"
|
||||
|
||||
type AmoStep7Props = {
|
||||
handleSmallBtn: () => void;
|
||||
handleLargeBtn: () => void;
|
||||
tags: TTags;
|
||||
setTags: Dispatch<SetStateAction<TTags>>;
|
||||
type Props = {
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
questionEntity: TQuestionEntity;
|
||||
setQuestionEntity: Dispatch<SetStateAction<TQuestionEntity>>;
|
||||
questions: AllTypesQuestion[];
|
||||
};
|
||||
|
||||
export const AmoStep7: FC<AmoStep7Props> = ({
|
||||
handleSmallBtn,
|
||||
handleLargeBtn,
|
||||
tags,
|
||||
setTags,
|
||||
export const AmoStep7: FC<Props> = ({
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
questionEntity,
|
||||
setQuestionEntity,
|
||||
questions,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [isSelection, setIsSelection] = useState<boolean>(false);
|
||||
@ -34,14 +36,14 @@ export const AmoStep7: FC<AmoStep7Props> = ({
|
||||
const handleAdd = useCallback(() => {
|
||||
if (!activeItem || !selectedValue) return;
|
||||
|
||||
setTags((prevState) => ({
|
||||
setQuestionEntity((prevState) => ({
|
||||
...prevState,
|
||||
[activeItem]: [...prevState[activeItem as TagKeys], selectedValue],
|
||||
[activeItem]: [...prevState[activeItem as TitleKeys], selectedValue],
|
||||
}));
|
||||
}, [activeItem, setTags, selectedValue]);
|
||||
}, [activeItem, setQuestionEntity, selectedValue]);
|
||||
|
||||
const items = useMemo(
|
||||
() => ["#тег с результатом 1", "#еще один тег с результатом 2", "#тег"],
|
||||
() => ["Город", "Имя", "Фамилия", "Отчество", "Контрагент"],
|
||||
[],
|
||||
);
|
||||
|
||||
@ -59,7 +61,6 @@ export const AmoStep7: FC<AmoStep7Props> = ({
|
||||
items={items}
|
||||
selectedValue={selectedValue}
|
||||
setSelectedValue={setSelectedValue}
|
||||
type={"typeTags"}
|
||||
onSmallBtnClick={() => {
|
||||
setActiveItem(null);
|
||||
setIsSelection(false);
|
||||
@ -69,13 +70,14 @@ export const AmoStep7: FC<AmoStep7Props> = ({
|
||||
setActiveItem(null);
|
||||
setIsSelection(false);
|
||||
}}
|
||||
questions={questions}
|
||||
/>
|
||||
) : (
|
||||
<TagsDetailsView
|
||||
<ItemDetailsView
|
||||
setIsSelection={setIsSelection}
|
||||
handleLargeBtn={handleLargeBtn}
|
||||
handleSmallBtn={handleSmallBtn}
|
||||
tags={tags}
|
||||
handleLargeBtn={handleNextStep}
|
||||
handleSmallBtn={handlePrevStep}
|
||||
questionEntity={questionEntity}
|
||||
setActiveItem={setActiveItem}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -4,19 +4,19 @@ import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
||||
import { FC } from "react";
|
||||
import { TQuestionEntity } from "../../AmoCRMModal";
|
||||
|
||||
type TitleKeys = "contacts" | "company" | "deal" | "users" | "buyers";
|
||||
type TitleKeys = "contacts" | "company" | "deal" | "buyers";
|
||||
|
||||
type ItemDetailsViewProps = {
|
||||
setIsSelection: (value: boolean) => void;
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
handleSmallBtn: () => void;
|
||||
handleLargeBtn: () => void;
|
||||
questionEntity: TQuestionEntity;
|
||||
setActiveItem: (value: string | null) => void;
|
||||
};
|
||||
|
||||
export const ItemDetailsView: FC<ItemDetailsViewProps> = ({
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
handleSmallBtn,
|
||||
handleLargeBtn,
|
||||
questionEntity,
|
||||
setActiveItem,
|
||||
setIsSelection,
|
||||
@ -69,8 +69,9 @@ export const ItemDetailsView: FC<ItemDetailsViewProps> = ({
|
||||
}}
|
||||
>
|
||||
<StepButtonsBlock
|
||||
onSmallBtnClick={handlePrevStep}
|
||||
onLargeBtnClick={handleNextStep}
|
||||
onSmallBtnClick={handleSmallBtn}
|
||||
onLargeBtnClick={handleLargeBtn}
|
||||
largeBtnText={"Сохранить"}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -2,6 +2,7 @@ import { Box } from "@mui/material";
|
||||
import { CustomRadioGroup } from "../../../../../components/CustomRadioGroup/CustomRadioGroup";
|
||||
import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
||||
import { FC } from "react";
|
||||
import { AllTypesQuestion } from "@/model/questionTypes/shared";
|
||||
|
||||
type ItemsSelectionViewProps = {
|
||||
type?: string;
|
||||
@ -6,6 +6,7 @@ import { YandexMetricaLogo } from "../mocks/YandexMetricaLogo";
|
||||
import { VKPixelLogo } from "../mocks/VKPixelLogo";
|
||||
import { QuizMetricType } from "@model/quizSettings";
|
||||
import { AmoCRMLogo } from "../mocks/AmoCRMLogo";
|
||||
import { useCurrentQuiz } from "@/stores/quizes/hooks";
|
||||
|
||||
const AnalyticsModal = lazy(() =>
|
||||
import("./AnalyticsModal/AnalyticsModal").then((module) => ({
|
||||
@ -43,6 +44,8 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
const quiz = useCurrentQuiz();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -125,6 +128,7 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
|
||||
isModalOpen={isAmoCrmModalOpen}
|
||||
handleCloseModal={handleCloseAmoSRMModal}
|
||||
companyName={companyName}
|
||||
quizID={quiz?.backendId}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
@ -4,13 +4,17 @@ import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||
import type { QuizQuestionDate } from "../../../model/questionTypes/date";
|
||||
|
||||
type SettingsDataProps = {
|
||||
question: QuizQuestionDate;
|
||||
questionId: string;
|
||||
isRequired: boolean;
|
||||
isDateRange: boolean;
|
||||
isTime: boolean;
|
||||
};
|
||||
|
||||
export default function SettingsData({ question }: SettingsDataProps) {
|
||||
export default function SettingsData({ questionId, isRequired, isDateRange, isTime }: SettingsDataProps) {
|
||||
const theme = useTheme();
|
||||
const isWrappColumn = useMediaQuery(theme.breakpoints.down(980));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990));
|
||||
|
||||
return (
|
||||
@ -18,19 +22,66 @@ export default function SettingsData({ question }: SettingsDataProps) {
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isWrappColumn ? "column" : null,
|
||||
flexDirection: isTablet ? "column" : "row",
|
||||
marginRight: isFigmaTablte ? (isMobile ? "0" : "0px") : "30px",
|
||||
pb: "20px",
|
||||
pl: "20px",
|
||||
pt: isTablet ? "5px" : "0px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pb: "20px",
|
||||
pl: isFigmaTablte ? (isWrappColumn ? "20px" : "34px") : "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: isMobile ? "13px" : "14px",
|
||||
width: isMobile ? "auto" : "100%",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
height: isMobile ? "18px" : "auto",
|
||||
fontWeight: "500",
|
||||
fontSize: "18px",
|
||||
color: " #4D4D4D",
|
||||
}}
|
||||
>
|
||||
Настройки ответов
|
||||
</Typography>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-dateRange"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={"Выбор диапазона дат"}
|
||||
checked={isDateRange}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionDate>(questionId, (question) => {
|
||||
question.content.dateRange = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-time"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={"Выбор времени"}
|
||||
checked={isTime}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionDate>(questionId, (question) => {
|
||||
question.content.time = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
@ -47,9 +98,9 @@ export default function SettingsData({ question }: SettingsDataProps) {
|
||||
dataCy="checkbox-optional-question"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={"Необязательный вопрос"}
|
||||
checked={!question.content.required}
|
||||
checked={!isRequired}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionDate>(question.id, (question) => {
|
||||
updateQuestion<QuizQuestionDate>(questionId, (question) => {
|
||||
question.content.required = !target.checked;
|
||||
});
|
||||
}}
|
||||
|
||||
@ -7,13 +7,17 @@ interface Props {
|
||||
question: QuizQuestionDate;
|
||||
}
|
||||
|
||||
export default function SwitchData({
|
||||
switchState = "setting",
|
||||
question,
|
||||
}: Props) {
|
||||
export default function SwitchData({ switchState = "setting", question }: Props) {
|
||||
switch (switchState) {
|
||||
case "setting":
|
||||
return <SettingData question={question} />;
|
||||
return (
|
||||
<SettingData
|
||||
questionId={question.id}
|
||||
isRequired={question.content.required}
|
||||
isDateRange={question.content.dateRange}
|
||||
isTime={question.content.time}
|
||||
/>
|
||||
);
|
||||
case "help":
|
||||
return (
|
||||
<HelpQuestions
|
||||
|
||||
@ -3,19 +3,20 @@ import { updateQuestion } from "@root/questions/actions";
|
||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||
import type { QuizQuestionEmoji } from "../../../model/questionTypes/emoji";
|
||||
import { memo } from "react";
|
||||
import type { QuizQuestionVariant } from "@model/questionTypes/variant";
|
||||
|
||||
type SettingEmojiProps = {
|
||||
questionId: string;
|
||||
isRequired: boolean;
|
||||
isMulti: boolean;
|
||||
isOwn: boolean;
|
||||
};
|
||||
|
||||
const SettingEmoji = memo<SettingEmojiProps>(function ({
|
||||
questionId,
|
||||
isRequired,
|
||||
}) {
|
||||
const SettingEmoji = memo<SettingEmojiProps>(function ({ questionId, isRequired, isMulti, isOwn }) {
|
||||
const theme = useTheme();
|
||||
const isWrappColumn = useMediaQuery(theme.breakpoints.down(980));
|
||||
const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(985));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
|
||||
return (
|
||||
@ -24,13 +25,58 @@ const SettingEmoji = memo<SettingEmojiProps>(function ({
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isWrappColumn ? "column" : "none",
|
||||
pb: "20px",
|
||||
pl: "20px",
|
||||
pt: isTablet ? "5px" : "0px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
height: isMobile ? "18px" : "auto",
|
||||
fontWeight: "500",
|
||||
fontSize: "18px",
|
||||
color: " #4D4D4D",
|
||||
}}
|
||||
>
|
||||
Настройки ответов
|
||||
</Typography>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-multiple-answers"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={"Можно несколько"}
|
||||
checked={isMulti}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
|
||||
question.content.multi = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-own-answer"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={'Вариант "свой ответ"'}
|
||||
checked={isOwn}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
|
||||
question.content.own = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
pt: "20px",
|
||||
pb: "20px",
|
||||
pl: "20px",
|
||||
pr: isFigmaTablte ? "30px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
@ -7,16 +7,15 @@ interface Props {
|
||||
question: QuizQuestionEmoji;
|
||||
}
|
||||
|
||||
export default function SwitchEmoji({
|
||||
switchState = "setting",
|
||||
question,
|
||||
}: Props) {
|
||||
export default function SwitchEmoji({ switchState = "setting", question }: Props) {
|
||||
switch (switchState) {
|
||||
case "setting":
|
||||
return (
|
||||
<SettingEmoji
|
||||
questionId={question.id}
|
||||
isRequired={question.content.required}
|
||||
isOwn={question.content.own}
|
||||
isMulti={question.content.multi}
|
||||
/>
|
||||
);
|
||||
case "help":
|
||||
|
||||
@ -4,21 +4,20 @@ import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg";
|
||||
import { memo } from "react";
|
||||
import type { QuizQuestionVariant } from "@model/questionTypes/variant";
|
||||
|
||||
type SettingOptionsAndPictProps = {
|
||||
questionId: string;
|
||||
replText: string;
|
||||
isRequired: boolean;
|
||||
isOwn: boolean;
|
||||
};
|
||||
|
||||
const SettingOptionsAndPict = memo<SettingOptionsAndPictProps>(function ({
|
||||
questionId,
|
||||
replText,
|
||||
isRequired,
|
||||
}) {
|
||||
const SettingOptionsAndPict = memo<SettingOptionsAndPictProps>(function ({ questionId, replText, isRequired, isOwn }) {
|
||||
const theme = useTheme();
|
||||
const isWrappColumn = useMediaQuery(theme.breakpoints.down(980));
|
||||
const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(985));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(680));
|
||||
|
||||
const setReplText = (replText: string) => {
|
||||
@ -36,13 +35,14 @@ const SettingOptionsAndPict = memo<SettingOptionsAndPictProps>(function ({
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isWrappColumn ? "column" : "none",
|
||||
pb: "20px",
|
||||
pl: "20px",
|
||||
pt: isTablet ? "5px" : "0px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
pt: isMobile ? "30px" : "20px",
|
||||
pb: isMobile ? "25px" : "20px",
|
||||
pl: "20px",
|
||||
pt: "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
@ -60,6 +60,17 @@ const SettingOptionsAndPict = memo<SettingOptionsAndPictProps>(function ({
|
||||
>
|
||||
Настройки ответов
|
||||
</Typography>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-own-answer"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={'Вариант "свой ответ"'}
|
||||
checked={isOwn}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
|
||||
question.content.own = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{!isWrappColumn && (
|
||||
<Box sx={{ mt: isMobile ? "11px" : "6px", width: "100%" }}>
|
||||
<Typography
|
||||
@ -90,8 +101,6 @@ const SettingOptionsAndPict = memo<SettingOptionsAndPictProps>(function ({
|
||||
<Box
|
||||
sx={{
|
||||
pt: isMobile ? "0px" : "20px",
|
||||
pb: "20px",
|
||||
pl: isFigmaTablte ? (isWrappColumn ? "20px" : "31px") : "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
@ -123,7 +132,16 @@ const SettingOptionsAndPict = memo<SettingOptionsAndPictProps>(function ({
|
||||
}
|
||||
/>
|
||||
{isWrappColumn && (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
pt: "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
maxWidth: isFigmaTablte ? "297px" : "360px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
height: isMobile ? "18px" : "auto",
|
||||
@ -141,7 +159,7 @@ const SettingOptionsAndPict = memo<SettingOptionsAndPictProps>(function ({
|
||||
maxLength={60}
|
||||
onChange={({ target }) => setReplText(target.value)}
|
||||
/>
|
||||
</>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@ -8,10 +8,7 @@ interface Props {
|
||||
question: QuizQuestionVarImg;
|
||||
}
|
||||
|
||||
export default function SwitchOptionsAndPict({
|
||||
switchState = "setting",
|
||||
question,
|
||||
}: Props) {
|
||||
export default function SwitchOptionsAndPict({ switchState = "setting", question }: Props) {
|
||||
switch (switchState) {
|
||||
case "setting":
|
||||
return (
|
||||
@ -19,6 +16,7 @@ export default function SwitchOptionsAndPict({
|
||||
questionId={question.id}
|
||||
replText={question.content.replText}
|
||||
isRequired={question.content.required}
|
||||
isOwn={question.content.own}
|
||||
/>
|
||||
);
|
||||
case "help":
|
||||
@ -30,12 +28,7 @@ export default function SwitchOptionsAndPict({
|
||||
/>
|
||||
);
|
||||
case "image":
|
||||
return (
|
||||
<UploadImage
|
||||
question={question}
|
||||
cropAspectRatio={{ width: 380, height: 300 }}
|
||||
/>
|
||||
);
|
||||
return <UploadImage question={question} cropAspectRatio={{ width: 380, height: 300 }} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { updateQuestion } from "@root/questions/actions";
|
||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||
import ProportionsIcon11 from "../../../assets/icons/questionsPage/ProportionsIcon11";
|
||||
@ -12,6 +6,9 @@ import ProportionsIcon12 from "../../../assets/icons/questionsPage/ProportionsIc
|
||||
import ProportionsIcon21 from "../../../assets/icons/questionsPage/ProportionsIcon21";
|
||||
import type { QuizQuestionImages } from "../../../model/questionTypes/images";
|
||||
import { memo } from "react";
|
||||
import type { QuizQuestionVariant } from "@model/questionTypes/variant";
|
||||
import FormatIcon1 from "../../../assets/icons/questionsPage/FormatIcon1";
|
||||
import FormatIcon2 from "../../../assets/icons/questionsPage/FormatIcon2";
|
||||
|
||||
type Proportion = "1:1" | "2:1" | "1:2";
|
||||
|
||||
@ -26,14 +23,34 @@ const PROPORTIONS: ProportionItem[] = [
|
||||
{ value: "1:2", icon: ProportionsIcon12 },
|
||||
];
|
||||
|
||||
type Format = "carousel" | "masonry";
|
||||
|
||||
type FormatItem = {
|
||||
value: Format;
|
||||
icon: (props: { color: string }) => JSX.Element;
|
||||
};
|
||||
|
||||
const FORMATS: FormatItem[] = [
|
||||
{ value: "masonry", icon: FormatIcon2 },
|
||||
{ value: "carousel", icon: FormatIcon1 },
|
||||
];
|
||||
|
||||
type SettingOpytionsPictProps = {
|
||||
questionId: string;
|
||||
isRequired: boolean;
|
||||
isMulti: boolean;
|
||||
isOwn: boolean;
|
||||
proportions: Proportion;
|
||||
format: Format;
|
||||
};
|
||||
|
||||
const SettingOptionsPict = memo<SettingOpytionsPictProps>(function ({
|
||||
questionId,
|
||||
isRequired,
|
||||
isMulti,
|
||||
isOwn,
|
||||
proportions,
|
||||
format,
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(985));
|
||||
@ -47,14 +64,17 @@ const SettingOptionsPict = memo<SettingOpytionsPictProps>(function ({
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isTablet ? "column" : null,
|
||||
marginRight: isFigmaTablte ? (isMobile ? "0" : "0px") : "30px",
|
||||
pb: "20px",
|
||||
pl: "20px",
|
||||
pt: isTablet ? "5px" : "0px",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
pt: isMobile ? "25px" : "20px",
|
||||
pb: isMobile ? "25px" : "20px",
|
||||
pl: "20px",
|
||||
pr: isFigmaTablte ? (isMobile ? "20px" : "0px") : "28px",
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
@ -62,10 +82,135 @@ const SettingOptionsPict = memo<SettingOpytionsPictProps>(function ({
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ fontWeight: "500", fontSize: "18px", color: " #4D4D4D" }}
|
||||
sx={{
|
||||
height: isMobile ? "18px" : "auto",
|
||||
fontWeight: "500",
|
||||
fontSize: "18px",
|
||||
color: " #4D4D4D",
|
||||
}}
|
||||
>
|
||||
Настройки вопросов
|
||||
Пропорции
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{PROPORTIONS.map((proportionItem, index) => (
|
||||
<SelectIconButton
|
||||
key={index}
|
||||
Icon={proportionItem.icon}
|
||||
isActive={proportionItem.value === proportions}
|
||||
onClick={() => {
|
||||
updateQuestion<QuizQuestionImages>(questionId, (question) => {
|
||||
if (question.type !== "images") return;
|
||||
question.content.xy = proportionItem.value;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
height: isMobile ? "18px" : "auto",
|
||||
fontWeight: "500",
|
||||
fontSize: "18px",
|
||||
color: " #4D4D4D",
|
||||
}}
|
||||
>
|
||||
Настройки ответов
|
||||
</Typography>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-multiple-answers"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={"Можно несколько"}
|
||||
checked={isMulti}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
|
||||
question.content.multi = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-own-answer"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={'Вариант "свой ответ"'}
|
||||
checked={isOwn}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
|
||||
question.content.own = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
height: isMobile ? "18px" : "auto",
|
||||
fontWeight: "500",
|
||||
fontSize: "18px",
|
||||
color: " #4D4D4D",
|
||||
}}
|
||||
>
|
||||
Формат
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{FORMATS.map((formatItem, index) => (
|
||||
<SelectIconButton
|
||||
key={index}
|
||||
Icon={formatItem.icon}
|
||||
isActive={formatItem.value === format}
|
||||
onClick={() => {
|
||||
updateQuestion<QuizQuestionImages>(questionId, (question) => {
|
||||
if (question.type !== "images") return;
|
||||
question.content.format = formatItem.value;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
pt: "20px",
|
||||
pr: isFigmaTablte ? (isMobile ? "20px" : "0px") : "28px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontWeight: "500", fontSize: "18px", color: " #4D4D4D" }}>Настройки вопросов</Typography>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-optional-question"
|
||||
sx={{ alignItems: isMobile ? "flex-start" : "" }}
|
||||
@ -81,6 +226,7 @@ const SettingOptionsPict = memo<SettingOpytionsPictProps>(function ({
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
@ -101,23 +247,13 @@ export function SelectIconButton({ Icon, isActive = false, onClick }: Props) {
|
||||
<Button
|
||||
onClick={onClick}
|
||||
variant="outlined"
|
||||
startIcon={
|
||||
<Icon
|
||||
color={
|
||||
isActive
|
||||
? theme.palette.navbarbg.main
|
||||
: theme.palette.brightPurple.main
|
||||
}
|
||||
/>
|
||||
}
|
||||
startIcon={<Icon color={isActive ? theme.palette.navbarbg.main : theme.palette.brightPurple.main} />}
|
||||
sx={{
|
||||
backgroundColor: isActive ? theme.palette.brightPurple.main : "#eee4fc",
|
||||
|
||||
borderRadius: 0,
|
||||
border: "none",
|
||||
color: isActive
|
||||
? theme.palette.brightPurple.main
|
||||
: theme.palette.grey2.main,
|
||||
color: isActive ? theme.palette.brightPurple.main : theme.palette.grey2.main,
|
||||
p: "7px",
|
||||
width: "40px",
|
||||
height: "40px",
|
||||
@ -128,9 +264,7 @@ export function SelectIconButton({ Icon, isActive = false, onClick }: Props) {
|
||||
},
|
||||
"&:hover": {
|
||||
border: "none",
|
||||
borderColor: isActive
|
||||
? theme.palette.brightPurple.main
|
||||
: theme.palette.grey2.main,
|
||||
borderColor: isActive ? theme.palette.brightPurple.main : theme.palette.grey2.main,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -7,16 +7,17 @@ interface Props {
|
||||
question: QuizQuestionImages;
|
||||
}
|
||||
|
||||
export default function SwitchAnswerOptionsPict({
|
||||
switchState = "setting",
|
||||
question,
|
||||
}: Props) {
|
||||
export default function SwitchAnswerOptionsPict({ switchState = "setting", question }: Props) {
|
||||
switch (switchState) {
|
||||
case "setting":
|
||||
return (
|
||||
<SettingOpytionsPict
|
||||
questionId={question.id}
|
||||
isRequired={question.content.required}
|
||||
isMulti={question.content.multi}
|
||||
isOwn={question.content.own}
|
||||
proportions={question.content.xy}
|
||||
format={question.content.format}
|
||||
/>
|
||||
);
|
||||
case "help":
|
||||
|
||||
@ -1,31 +1,36 @@
|
||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Box, FormControlLabel, Radio, RadioGroup, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { updateQuestion } from "@root/questions/actions";
|
||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||
import type { QuizQuestionText } from "../../../model/questionTypes/text";
|
||||
import { memo } from "react";
|
||||
import RadioCheck from "@ui_kit/RadioCheck";
|
||||
import RadioIcon from "@ui_kit/RadioIcon";
|
||||
|
||||
type Answer = "single" | "multi" | "numberOnly";
|
||||
|
||||
type AnswerItem = {
|
||||
name: string;
|
||||
value: Answer;
|
||||
};
|
||||
|
||||
const ANSWER_TYPES: AnswerItem[] = [
|
||||
{ name: "Однострочное", value: "single" },
|
||||
{ name: "Многострочное", value: "multi" },
|
||||
{ name: "Только числа", value: "numberOnly" },
|
||||
];
|
||||
|
||||
type SettingTextFieldProps = {
|
||||
questionId: string;
|
||||
isRequired: boolean;
|
||||
isAutofill: boolean;
|
||||
answerType: Answer;
|
||||
};
|
||||
|
||||
type Answer = {
|
||||
name: string;
|
||||
value: "single" | "multi";
|
||||
};
|
||||
|
||||
const ANSWER_TYPES: Answer[] = [
|
||||
{ name: "Однострочное", value: "single" },
|
||||
{ name: "Многострочное", value: "multi" },
|
||||
];
|
||||
|
||||
const SettingTextField = memo<SettingTextFieldProps>(function ({
|
||||
questionId,
|
||||
isRequired,
|
||||
}) {
|
||||
const SettingTextField = memo<SettingTextFieldProps>(function ({ questionId, isRequired, isAutofill, answerType }) {
|
||||
const theme = useTheme();
|
||||
const isWrappColumn = useMediaQuery(theme.breakpoints.down(980));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990));
|
||||
|
||||
return (
|
||||
@ -33,19 +38,67 @@ const SettingTextField = memo<SettingTextFieldProps>(function ({
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isWrappColumn ? "column" : null,
|
||||
marginRight: isFigmaTablte ? "0px" : "32px",
|
||||
flexDirection: isTablet ? "column" : "row",
|
||||
marginRight: isFigmaTablte ? (isMobile ? "0" : "0px") : "30px",
|
||||
pb: "20px",
|
||||
pl: "20px",
|
||||
pt: isTablet ? "5px" : "0px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pb: "20px",
|
||||
pl: isFigmaTablte ? (isWrappColumn ? "20px" : "34px") : "20px",
|
||||
pr: "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
height: isMobile ? "18px" : "auto",
|
||||
fontWeight: "500",
|
||||
fontSize: "18px",
|
||||
color: " #4D4D4D",
|
||||
}}
|
||||
>
|
||||
Настройки ответов
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
value={answerType}
|
||||
onChange={(event) => {
|
||||
updateQuestion<QuizQuestionText>(questionId, (question) => {
|
||||
question.content.answerType = event.target.value as Answer;
|
||||
});
|
||||
}}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
{ANSWER_TYPES.map((answerTypeItem, index) => (
|
||||
<FormControlLabel
|
||||
value={answerTypeItem.value}
|
||||
control={<Radio checkedIcon={<RadioCheck />} icon={<RadioIcon />} />}
|
||||
label={answerTypeItem.name}
|
||||
sx={{
|
||||
color: theme.palette.grey2.main,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
@ -58,6 +111,21 @@ const SettingTextField = memo<SettingTextFieldProps>(function ({
|
||||
>
|
||||
Настройки вопросов
|
||||
</Typography>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-optional-autofill"
|
||||
sx={{
|
||||
display: isMobile ? "flex" : "block",
|
||||
mr: isMobile ? "0px" : "16px",
|
||||
alignItems: isMobile ? "flex-end" : "center",
|
||||
}}
|
||||
label={"Автозаполнение адреса"}
|
||||
checked={isAutofill}
|
||||
handleChange={(e) => {
|
||||
updateQuestion<QuizQuestionText>(questionId, (question) => {
|
||||
question.content.autofill = e.target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-optional-question"
|
||||
sx={{
|
||||
|
||||
@ -8,25 +8,20 @@ interface Props {
|
||||
question: QuizQuestionText;
|
||||
}
|
||||
|
||||
export default function SwitchTextField({
|
||||
switchState = "setting",
|
||||
question,
|
||||
}: Props) {
|
||||
export default function SwitchTextField({ switchState = "setting", question }: Props) {
|
||||
switch (switchState) {
|
||||
case "setting":
|
||||
return (
|
||||
<SettingTextField
|
||||
questionId={question.id}
|
||||
isRequired={question.content.required}
|
||||
isAutofill={question.content.autofill}
|
||||
isOnlyNumbers={question.content.onlyNumbers}
|
||||
answerType={question.content.answerType}
|
||||
/>
|
||||
);
|
||||
case "image":
|
||||
return (
|
||||
<UploadImage
|
||||
question={question}
|
||||
cropAspectRatio={{ width: 400, height: 300 }}
|
||||
/>
|
||||
);
|
||||
return <UploadImage question={question} cropAspectRatio={{ width: 400, height: 300 }} />;
|
||||
case "help":
|
||||
return (
|
||||
<HelpQuestions
|
||||
|
||||
@ -7,9 +7,12 @@ import { memo } from "react";
|
||||
interface Props {
|
||||
questionId: string;
|
||||
isRequired: boolean;
|
||||
isLargeCheck: boolean;
|
||||
isMulti: boolean;
|
||||
isOwn: boolean;
|
||||
}
|
||||
|
||||
const ResponseSettings = memo<Props>(function ({ questionId, isRequired }) {
|
||||
const ResponseSettings = memo<Props>(function ({ questionId, isRequired, isLargeCheck, isMulti, isOwn }) {
|
||||
const theme = useTheme();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
const isFigmaTablte = useMediaQuery(theme.breakpoints.down(990));
|
||||
@ -20,16 +23,72 @@ const ResponseSettings = memo<Props>(function ({ questionId, isRequired }) {
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isTablet ? "column" : "none",
|
||||
flexDirection: isTablet ? "column" : "row",
|
||||
marginRight: isFigmaTablte ? (isMobile ? "0" : "0px") : "30px",
|
||||
pb: "20px",
|
||||
pl: "20px",
|
||||
pt: isTablet ? "5px" : "0px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pb: "20px",
|
||||
pl: isFigmaTablte ? (isTablet ? "20px" : "34px") : "28px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "14px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
height: isMobile ? "18px" : "auto",
|
||||
fontWeight: "500",
|
||||
fontSize: "18px",
|
||||
color: " #4D4D4D",
|
||||
}}
|
||||
>
|
||||
Настройки ответов
|
||||
</Typography>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-long-text-answer"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={"Длинный текстовый ответ"}
|
||||
checked={isLargeCheck}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
|
||||
question.content.largeCheck = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-multiple-answers"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={"Можно несколько"}
|
||||
checked={isMulti}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
|
||||
question.content.multi = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-own-answer"
|
||||
sx={{ mr: isMobile ? "0px" : "16px" }}
|
||||
label={'Вариант "свой ответ"'}
|
||||
checked={isOwn}
|
||||
handleChange={({ target }) => {
|
||||
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
|
||||
question.content.own = target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: "border-box",
|
||||
pt: "20px",
|
||||
pr: isFigmaTablte ? "19px" : "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
@ -8,16 +8,16 @@ interface Props {
|
||||
question: QuizQuestionVariant;
|
||||
}
|
||||
|
||||
export default function SwitchAnswerOptions({
|
||||
switchState = "setting",
|
||||
question,
|
||||
}: Props) {
|
||||
export default function SwitchAnswerOptions({ switchState = "setting", question }: Props) {
|
||||
switch (switchState) {
|
||||
case "setting":
|
||||
return (
|
||||
<ResponseSettings
|
||||
questionId={question.id}
|
||||
isRequired={question.content.required}
|
||||
isLargeCheck={question.content.largeCheck}
|
||||
isMulti={question.content.multi}
|
||||
isOwn={question.content.own}
|
||||
/>
|
||||
);
|
||||
case "help":
|
||||
@ -29,12 +29,7 @@ export default function SwitchAnswerOptions({
|
||||
/>
|
||||
);
|
||||
case "image":
|
||||
return (
|
||||
<UploadImage
|
||||
question={question}
|
||||
cropAspectRatio={{ width: 380, height: 300 }}
|
||||
/>
|
||||
);
|
||||
return <UploadImage question={question} cropAspectRatio={{ width: 380, height: 300 }} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ButtonBase } from "@mui/material";
|
||||
import { startTransition, useRef } from "react";
|
||||
import { ButtonBase, Popover } from "@mui/material";
|
||||
import { startTransition, useState } from "react";
|
||||
import { HexAlphaColorPicker } from "react-colorful";
|
||||
|
||||
interface Props {
|
||||
color: string;
|
||||
@ -7,11 +8,12 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function CircleColorPicker({ color, onChange }: Props) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ButtonBase
|
||||
onClick={() => inputRef.current?.click()}
|
||||
onClick={(event) => setAnchorEl(event.currentTarget)}
|
||||
sx={{
|
||||
aspectRatio: 1,
|
||||
height: "22px",
|
||||
@ -21,21 +23,28 @@ export default function CircleColorPicker({ color, onChange }: Props) {
|
||||
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>
|
||||
<Popover
|
||||
open={Boolean(anchorEl)}
|
||||
anchorEl={anchorEl}
|
||||
onClose={() => setAnchorEl(null)}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "right",
|
||||
}}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
p: "20px",
|
||||
},
|
||||
style: {
|
||||
backgroundColor: color,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<HexAlphaColorPicker color={color} onChange={(color) => startTransition(() => onChange(color))} />
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ const translateMessage: Record<string, string> = {
|
||||
|
||||
export const parseAxiosError = (nativeError: unknown): [string, number?] => {
|
||||
const error = nativeError as AxiosError;
|
||||
|
||||
console.log(error)
|
||||
if (process.env.NODE_ENV !== "production") console.error(error);
|
||||
if (error.message === "Failed to fetch") return ["Ошибка сети"];
|
||||
|
||||
|
||||
@ -9486,6 +9486,11 @@ react-beautiful-dnd@^13.1.1:
|
||||
redux "^4.0.4"
|
||||
use-memo-one "^1.1.1"
|
||||
|
||||
react-colorful@^5.6.1:
|
||||
version "5.6.1"
|
||||
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b"
|
||||
integrity sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==
|
||||
|
||||
react-cytoscapejs@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-cytoscapejs/-/react-cytoscapejs-2.0.0.tgz#fdc2547626df0678acfbb48e73a437ddc1687b01"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user