Merge branch 'video-upload-modal' into dev
This commit is contained in:
commit
67a5d3c63f
@ -23,9 +23,7 @@ export type AccountResponse = {
|
||||
driveURL: string;
|
||||
};
|
||||
|
||||
export const getAccount = async (): Promise<
|
||||
[AccountResponse | null, string?]
|
||||
> => {
|
||||
export const getAccount = async (): Promise<[AccountResponse | null, string?]> => {
|
||||
try {
|
||||
const response = await makeRequest<void, AccountResponse>({
|
||||
method: "GET",
|
||||
@ -95,10 +93,7 @@ export type TagsResponse = {
|
||||
items: Tag[];
|
||||
};
|
||||
|
||||
export const getTags = async ({
|
||||
page,
|
||||
size,
|
||||
}: PaginationRequest): Promise<[TagsResponse | null, string?]> => {
|
||||
export const getTags = async ({ page, size }: PaginationRequest): Promise<[TagsResponse | null, string?]> => {
|
||||
try {
|
||||
const tagsResponse = await makeRequest<PaginationRequest, TagsResponse>({
|
||||
method: "GET",
|
||||
@ -133,10 +128,7 @@ export type UsersResponse = {
|
||||
items: User[];
|
||||
};
|
||||
|
||||
export const getUsers = async ({
|
||||
page,
|
||||
size,
|
||||
}: PaginationRequest): Promise<[UsersResponse | null, string?]> => {
|
||||
export const getUsers = async ({ page, size }: PaginationRequest): Promise<[UsersResponse | null, string?]> => {
|
||||
try {
|
||||
const usersResponse = await makeRequest<PaginationRequest, UsersResponse>({
|
||||
method: "GET",
|
||||
@ -171,14 +163,9 @@ export const getSteps = async ({
|
||||
page,
|
||||
size,
|
||||
pipelineId,
|
||||
}: PaginationRequest & { pipelineId: number }): Promise<
|
||||
[StepsResponse | null, string?]
|
||||
> => {
|
||||
}: PaginationRequest & { pipelineId: number }): Promise<[StepsResponse | null, string?]> => {
|
||||
try {
|
||||
const stepsResponse = await makeRequest<
|
||||
PaginationRequest & { pipelineId: number },
|
||||
StepsResponse
|
||||
>({
|
||||
const stepsResponse = await makeRequest<PaginationRequest & { pipelineId: number }, StepsResponse>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/steps?page=${page}&size=${size}&pipelineID=${pipelineId}`,
|
||||
});
|
||||
@ -206,15 +193,9 @@ export type PipelinesResponse = {
|
||||
items: Pipeline[];
|
||||
};
|
||||
|
||||
export const getPipelines = async ({
|
||||
page,
|
||||
size,
|
||||
}: PaginationRequest): Promise<[PipelinesResponse | null, string?]> => {
|
||||
export const getPipelines = async ({ page, size }: PaginationRequest): Promise<[PipelinesResponse | null, string?]> => {
|
||||
try {
|
||||
const pipelinesResponse = await makeRequest<
|
||||
PaginationRequest,
|
||||
PipelinesResponse
|
||||
>({
|
||||
const pipelinesResponse = await makeRequest<PaginationRequest, PipelinesResponse>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/pipelines?page=${page}&size=${size}`,
|
||||
});
|
||||
@ -226,7 +207,7 @@ export const getPipelines = async ({
|
||||
};
|
||||
|
||||
//получение настроек интеграции
|
||||
export type QuestionID = Record<string, number>
|
||||
export type QuestionID = Record<string, number>;
|
||||
|
||||
export type IntegrationRules = {
|
||||
PipelineID: number;
|
||||
@ -238,13 +219,11 @@ export type IntegrationRules = {
|
||||
Contact: number[] | null;
|
||||
Company: number[] | null;
|
||||
Customer: number[] | null;
|
||||
}
|
||||
};
|
||||
};
|
||||
export type FieldsRule = Record<Partial<QuestionKeys>, null | [{QuestionID: QuestionID;}]>
|
||||
export type FieldsRule = Record<Partial<QuestionKeys>, null | [{ QuestionID: QuestionID }]>;
|
||||
|
||||
export const getIntegrationRules = async (
|
||||
quizID: string,
|
||||
): Promise<[IntegrationRules | null, string?]> => {
|
||||
export const getIntegrationRules = async (quizID: string): Promise<[IntegrationRules | null, string?]> => {
|
||||
try {
|
||||
const settingsResponse = await makeRequest<void, IntegrationRules>({
|
||||
method: "GET",
|
||||
@ -275,7 +254,7 @@ export type IntegrationRulesUpdate = {
|
||||
|
||||
export const setIntegrationRules = async (
|
||||
quizID: string,
|
||||
settings: IntegrationRulesUpdate,
|
||||
settings: IntegrationRulesUpdate
|
||||
): Promise<[string | null, string?]> => {
|
||||
try {
|
||||
const updateResponse = await makeRequest<IntegrationRulesUpdate, string>({
|
||||
@ -291,7 +270,7 @@ export const setIntegrationRules = async (
|
||||
};
|
||||
export const updateIntegrationRules = async (
|
||||
quizID: string,
|
||||
settings: IntegrationRulesUpdate,
|
||||
settings: IntegrationRulesUpdate
|
||||
): Promise<[string | null, string?]> => {
|
||||
try {
|
||||
const updateResponse = await makeRequest<IntegrationRulesUpdate, string>({
|
||||
@ -326,13 +305,10 @@ export type CustomFieldsResponse = {
|
||||
};
|
||||
|
||||
export const getCustomFields = async (
|
||||
pagination: PaginationRequest,
|
||||
pagination: PaginationRequest
|
||||
): Promise<[CustomFieldsResponse | null, string?]> => {
|
||||
try {
|
||||
const fieldsResponse = await makeRequest<
|
||||
PaginationRequest,
|
||||
CustomFieldsResponse
|
||||
>({
|
||||
const fieldsResponse = await makeRequest<PaginationRequest, CustomFieldsResponse>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/fields?page=${pagination.page}&size=${pagination.size}`,
|
||||
});
|
||||
@ -356,4 +332,4 @@ export const removeAmoAccount = async (): Promise<[void | null, string?]> => {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось отвязать аккаунт. ${error}`];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -1,18 +1,9 @@
|
||||
import * as React from "react";
|
||||
import { FC, useMemo } from "react";
|
||||
import CheckboxIcon from "@icons/Checkbox";
|
||||
import {
|
||||
SelectChangeEvent,
|
||||
Typography,
|
||||
useTheme,
|
||||
Box,
|
||||
FormControlLabel,
|
||||
RadioGroup,
|
||||
Radio,
|
||||
} from "@mui/material";
|
||||
import { SelectChangeEvent, Typography, useTheme, Box, FormControlLabel, RadioGroup, Radio } from "@mui/material";
|
||||
import { MinifiedData, TagKeys } from "@/pages/IntegrationsPage/IntegrationsModal/types";
|
||||
|
||||
|
||||
type CustomRadioGroupProps = {
|
||||
items: MinifiedData[] | [];
|
||||
selectedItemId?: string | null;
|
||||
@ -32,18 +23,19 @@ export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
||||
|
||||
const currentItem = useMemo(() => {
|
||||
if (selectedItemId !== null && selectedItemId.length > 0) {
|
||||
return items.find(item => item.id === selectedItemId) || null
|
||||
return items.find((item) => item.id === selectedItemId) || null;
|
||||
}
|
||||
return null;
|
||||
}, [selectedItemId, items])
|
||||
}, [selectedItemId, items]);
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
let newArray = items
|
||||
if (activeScope !== undefined) newArray =newArray.filter(item => {
|
||||
return item.entity === activeScope
|
||||
})
|
||||
return newArray
|
||||
}, items)
|
||||
let newArray = items;
|
||||
if (activeScope !== undefined)
|
||||
newArray = newArray.filter((item) => {
|
||||
return item.entity === activeScope;
|
||||
});
|
||||
return newArray;
|
||||
}, items);
|
||||
|
||||
const onScroll = React.useCallback((e: React.UIEvent<HTMLDivElement>) => {
|
||||
const scrollHeight = e.currentTarget.scrollHeight;
|
||||
@ -56,10 +48,9 @@ export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
const formControlLabels = useMemo(() => {
|
||||
if (filteredItems.length !== 0) {
|
||||
return filteredItems.map(item =>
|
||||
return filteredItems.map((item) => (
|
||||
<FormControlLabel
|
||||
key={item.id}
|
||||
sx={{
|
||||
@ -71,15 +62,12 @@ export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
||||
borderRadius: "12px",
|
||||
margin: 0,
|
||||
backgroundColor:
|
||||
currentItem?.id === item.id
|
||||
? theme.palette.background.default
|
||||
: theme.palette.common.white,
|
||||
currentItem?.id === item.id ? theme.palette.background.default : theme.palette.common.white,
|
||||
"&.MuiFormControlLabel-root > .MuiTypography-root": {
|
||||
width: "200px",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis"
|
||||
}
|
||||
|
||||
textOverflow: "ellipsis",
|
||||
},
|
||||
}}
|
||||
value={item.id}
|
||||
control={
|
||||
@ -97,7 +85,7 @@ export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
||||
label={item.title}
|
||||
labelPlacement={"start"}
|
||||
/>
|
||||
)
|
||||
));
|
||||
}
|
||||
return (
|
||||
<Box
|
||||
|
@ -1,16 +1,6 @@
|
||||
|
||||
import * as React from "react";
|
||||
import { FC, useCallback, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
Avatar,
|
||||
MenuItem,
|
||||
Select,
|
||||
SelectChangeEvent,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
Box,
|
||||
} from "@mui/material";
|
||||
import { Avatar, MenuItem, Select, SelectChangeEvent, Typography, useMediaQuery, useTheme, Box } from "@mui/material";
|
||||
import arrow_down from "../../assets/icons/arrow_down.svg";
|
||||
import { MinifiedData } from "@/pages/IntegrationsPage/IntegrationsModal/types";
|
||||
|
||||
@ -21,12 +11,7 @@ type CustomSelectProps = {
|
||||
handleScroll: () => void;
|
||||
};
|
||||
|
||||
export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
items,
|
||||
selectedItemId,
|
||||
setSelectedItem,
|
||||
handleScroll,
|
||||
}) => {
|
||||
export const CustomSelect: FC<CustomSelectProps> = ({ items, selectedItemId, setSelectedItem, handleScroll }) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
@ -48,9 +33,7 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const currentItem = useMemo(() => (
|
||||
items.find(item => item.id === selectedItemId) || null
|
||||
), [selectedItemId, items])
|
||||
const currentItem = useMemo(() => items.find((item) => item.id === selectedItemId) || null, [selectedItemId, items]);
|
||||
|
||||
const menuItems = useMemo(() => {
|
||||
if (items.length !== 0) {
|
||||
@ -78,9 +61,7 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
<Typography
|
||||
sx={{
|
||||
width: "33%",
|
||||
borderRight: isMobile
|
||||
? "none"
|
||||
: "1px solid rgba(154, 154, 175, 0.1)",
|
||||
borderRight: isMobile ? "none" : "1px solid rgba(154, 154, 175, 0.1)",
|
||||
padding: isMobile ? "5px 0 5px 20px" : "10px 0 10px 20px",
|
||||
}}
|
||||
>
|
||||
@ -89,9 +70,7 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
<Typography
|
||||
sx={{
|
||||
width: "33%",
|
||||
borderRight: isMobile
|
||||
? "none"
|
||||
: "1px solid rgba(154, 154, 175, 0.1)",
|
||||
borderRight: isMobile ? "none" : "1px solid rgba(154, 154, 175, 0.1)",
|
||||
padding: isMobile ? "5px 0 5px 20px" : "10px 0 10px 20px",
|
||||
color: isMobile ? "#9A9AAF" : "#4D4D4D",
|
||||
}}
|
||||
@ -103,7 +82,11 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
));
|
||||
}
|
||||
return (
|
||||
<MenuItem key={"-1"} disabled sx={{ padding: "12px", zIndex: 2 }}>
|
||||
<MenuItem
|
||||
key={"-1"}
|
||||
disabled
|
||||
sx={{ padding: "12px", zIndex: 2 }}
|
||||
>
|
||||
нет данных
|
||||
</MenuItem>
|
||||
);
|
||||
@ -118,10 +101,7 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
width: "100%",
|
||||
height: "56px",
|
||||
padding: "5px",
|
||||
color:
|
||||
currentItem === null
|
||||
? theme.palette.grey2.main
|
||||
: theme.palette.brightPurple.main,
|
||||
color: currentItem === null ? theme.palette.grey2.main : theme.palette.brightPurple.main,
|
||||
border: `2px solid ${theme.palette.common.white}`,
|
||||
borderRadius: "30px",
|
||||
background: "#EFF0F5",
|
||||
@ -129,7 +109,9 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {if (ref.current !== null) ref.current?.click()}}
|
||||
onClick={() => {
|
||||
if (ref.current !== null) ref.current?.click();
|
||||
}}
|
||||
>
|
||||
<Avatar sx={{ width: 46, height: 46, marginRight: 1 }} />
|
||||
<Typography
|
||||
@ -176,25 +158,23 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
paddingTop: "50px",
|
||||
marginTop: "-50px",
|
||||
borderRadius: "28px",
|
||||
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
display: "block",
|
||||
"& .MuiSelect-select.MuiSelect-outlined.MuiInputBase-input": {
|
||||
display: "none"
|
||||
},
|
||||
"& .MuiSelect-icon":{
|
||||
display: "none"
|
||||
},
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
border: 0
|
||||
},
|
||||
"& .MuiMenu-root.MuiModal-root": {
|
||||
zIndex: 0
|
||||
}
|
||||
|
||||
display: "block",
|
||||
"& .MuiSelect-select.MuiSelect-outlined.MuiInputBase-input": {
|
||||
display: "none",
|
||||
},
|
||||
"& .MuiSelect-icon": {
|
||||
display: "none",
|
||||
},
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
border: 0,
|
||||
},
|
||||
"& .MuiMenu-root.MuiModal-root": {
|
||||
zIndex: 0,
|
||||
},
|
||||
}}
|
||||
onChange={({ target }: SelectChangeEvent<string>) => setSelectedItem(target.value)}
|
||||
onClick={toggleOpened}
|
||||
|
@ -8,10 +8,7 @@ type AmoAccountInfoProps = {
|
||||
accountInfo: AccountResponse;
|
||||
};
|
||||
|
||||
export const AmoAccountInfo: FC<AmoAccountInfoProps> = ({
|
||||
handleNextStep,
|
||||
accountInfo,
|
||||
}) => {
|
||||
export const AmoAccountInfo: FC<AmoAccountInfoProps> = ({ handleNextStep, accountInfo }) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
@ -24,9 +21,7 @@ export const AmoAccountInfo: FC<AmoAccountInfoProps> = ({
|
||||
}}
|
||||
>
|
||||
<Box sx={{ width: isMobile ? "100%" : "45%" }}>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
{title}:
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>{title}:</Typography>
|
||||
</Box>
|
||||
<Box sx={{ width: isMobile ? "100%" : "45%" }}>
|
||||
<Typography>{value || "нет данных"}</Typography>
|
||||
@ -43,12 +38,15 @@ export const AmoAccountInfo: FC<AmoAccountInfoProps> = ({
|
||||
}}
|
||||
>
|
||||
<Box sx={{ width: isMobile ? "100%" : "45%" }}>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||
{title}:
|
||||
</Typography>
|
||||
<Typography sx={{ color: theme.palette.grey2.main }}>{title}:</Typography>
|
||||
</Box>
|
||||
<Box sx={{ width: isMobile ? "100%" : "45%" }}>
|
||||
<a target="_blank" href={link}>{link}</a>
|
||||
<a
|
||||
target="_blank"
|
||||
href={link}
|
||||
>
|
||||
{link}
|
||||
</a>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -1,12 +1,5 @@
|
||||
import React, { FC, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
Box,
|
||||
} from "@mui/material";
|
||||
import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box } from "@mui/material";
|
||||
import { useQuestions } from "@/stores/questions/hooks";
|
||||
import { redirect } from "react-router-dom";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
@ -24,13 +17,26 @@ import { AmoQuestions } from "./AmoQuestions/AmoQuestions";
|
||||
import { AmoModalTitle } from "./AmoModalTitle/AmoModalTitle";
|
||||
import { AmoSettingsBlock } from "./SettingsBlock/AmoSettingsBlock";
|
||||
import { AmoAccountInfo } from "./AmoAccountInfo/AmoAccountInfo";
|
||||
import { AccountResponse, FieldsRule, IntegrationRules, Pipeline, Step, User, getAccount, getIntegrationRules, getPipelines, getSteps, getTags, getUsers, setIntegrationRules, updateIntegrationRules } from "@api/integration";
|
||||
import {
|
||||
AccountResponse,
|
||||
FieldsRule,
|
||||
IntegrationRules,
|
||||
Pipeline,
|
||||
Step,
|
||||
User,
|
||||
getAccount,
|
||||
getIntegrationRules,
|
||||
getPipelines,
|
||||
getSteps,
|
||||
getTags,
|
||||
getUsers,
|
||||
setIntegrationRules,
|
||||
updateIntegrationRules,
|
||||
} from "@api/integration";
|
||||
import type { QuestionID } from "@api/integration";
|
||||
import { useAmoIntegration } from "./useAmoIntegration";
|
||||
import { QuestionKeys, TagKeys, TagQuestionHC } from "./types";
|
||||
|
||||
|
||||
|
||||
type IntegrationsModalProps = {
|
||||
isModalOpen: boolean;
|
||||
handleCloseModal: () => void;
|
||||
@ -38,14 +44,7 @@ type IntegrationsModalProps = {
|
||||
quizID: number | undefined;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
isModalOpen,
|
||||
handleCloseModal,
|
||||
companyName,
|
||||
quizID,
|
||||
}) => {
|
||||
export const AmoCRMModal: FC<IntegrationsModalProps> = ({ isModalOpen, handleCloseModal, companyName, quizID }) => {
|
||||
//Если нет контекста квиза, то и делать на этой страничке нечего
|
||||
if (quizID === undefined) {
|
||||
redirect("/list");
|
||||
@ -57,16 +56,16 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
|
||||
const { questions } = useQuestions();
|
||||
const minifiedQuestions = useMemo(() => questions
|
||||
.filter((q) =>
|
||||
q.type !== "result"
|
||||
&& q.type !== null
|
||||
)
|
||||
.map(({ backendId, title }) => ({
|
||||
id: backendId.toString() as string,
|
||||
title
|
||||
})), [questions])
|
||||
|
||||
const minifiedQuestions = useMemo(
|
||||
() =>
|
||||
questions
|
||||
.filter((q) => q.type !== "result" && q.type !== null)
|
||||
.map(({ backendId, title }) => ({
|
||||
id: backendId.toString() as string,
|
||||
title,
|
||||
})),
|
||||
[questions]
|
||||
);
|
||||
|
||||
const [step, setStep] = useState<number>(0);
|
||||
const [isSettingsBlock, setIsSettingsBlock] = useState<boolean>(false);
|
||||
@ -100,54 +99,54 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
quizID,
|
||||
isModalOpen,
|
||||
isTryRemoveAccount,
|
||||
})
|
||||
});
|
||||
|
||||
const handleAddTagQuestion = useCallback((scope: QuestionKeys | TagKeys, id: string, type: "question" | "tag") => {
|
||||
if (!scope || !id) return;
|
||||
const handleAddTagQuestion = useCallback(
|
||||
(scope: QuestionKeys | TagKeys, id: string, type: "question" | "tag") => {
|
||||
if (!scope || !id) return;
|
||||
|
||||
if (type === "tag") {
|
||||
setSelectedTags((prevState) => ({
|
||||
...prevState,
|
||||
[scope]: [...prevState[scope as TagKeys], id],
|
||||
}));
|
||||
}
|
||||
if (type === "tag") {
|
||||
setSelectedTags((prevState) => ({
|
||||
...prevState,
|
||||
[scope]: [...prevState[scope as TagKeys], id],
|
||||
}));
|
||||
}
|
||||
|
||||
if (type === "question") {
|
||||
setSelectedQuestions((prevState) => ({
|
||||
...prevState,
|
||||
[scope]: [...prevState[scope as QuestionKeys], id],
|
||||
}));
|
||||
}
|
||||
}, [setSelectedQuestions, setSelectedTags]);
|
||||
if (type === "question") {
|
||||
setSelectedQuestions((prevState) => ({
|
||||
...prevState,
|
||||
[scope]: [...prevState[scope as QuestionKeys], id],
|
||||
}));
|
||||
}
|
||||
},
|
||||
[setSelectedQuestions, setSelectedTags]
|
||||
);
|
||||
const handleDeleteTagQuestion = useCallback(() => {
|
||||
if (openDelete === null || !openDelete.scope || !openDelete.id || !openDelete.type) return;
|
||||
if (openDelete.type === "tag") {
|
||||
let newArray = selectedTags[openDelete.scope];
|
||||
const index = newArray.indexOf(openDelete.id)
|
||||
if (index !== -1)
|
||||
newArray.splice(index, 1);
|
||||
const index = newArray.indexOf(openDelete.id);
|
||||
if (index !== -1) newArray.splice(index, 1);
|
||||
|
||||
setSelectedTags((prevState) => ({
|
||||
...prevState,
|
||||
[openDelete.scope]: newArray
|
||||
[openDelete.scope]: newArray,
|
||||
}));
|
||||
}
|
||||
|
||||
if (openDelete.type === "question") {
|
||||
let newArray = selectedQuestions[openDelete.scope as QuestionKeys];
|
||||
const index = newArray.indexOf(openDelete.id)
|
||||
if (index !== -1)
|
||||
newArray.splice(index, 1);
|
||||
const index = newArray.indexOf(openDelete.id);
|
||||
if (index !== -1) newArray.splice(index, 1);
|
||||
|
||||
setSelectedQuestions((prevState) => ({
|
||||
...prevState,
|
||||
[openDelete.scope]: newArray
|
||||
[openDelete.scope]: newArray,
|
||||
}));
|
||||
}
|
||||
setOpenDelete(null)
|
||||
setOpenDelete(null);
|
||||
}, [openDelete]);
|
||||
|
||||
|
||||
const handleNextStep = () => {
|
||||
setStep((prevState) => prevState + 1);
|
||||
};
|
||||
@ -155,45 +154,44 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
setStep((prevState) => prevState - 1);
|
||||
};
|
||||
const handleSave = () => {
|
||||
|
||||
if (quizID === undefined) return
|
||||
if (selectedPipeline === null) return enqueueSnackbar("Выберите воронку")
|
||||
if (selectedPipeline === null) return enqueueSnackbar("Выберите этап воронки")
|
||||
if (quizID === undefined) return;
|
||||
if (selectedPipeline === null) return enqueueSnackbar("Выберите воронку");
|
||||
if (selectedPipeline === null) return enqueueSnackbar("Выберите этап воронки");
|
||||
|
||||
const body = {
|
||||
PipelineID: Number(selectedPipeline),
|
||||
StepID: Number(selectedPipelineStep),
|
||||
PerformerID: Number(selectedDealUser),
|
||||
// FieldsRule: questionsBackend,
|
||||
TagsToAdd: selectedTags
|
||||
}
|
||||
|
||||
const FieldsRule = {
|
||||
Company: [{ "QuestionID": {} }],
|
||||
Lead: [{ "QuestionID": {} }],
|
||||
Customer: [{ "QuestionID": {} }],
|
||||
TagsToAdd: selectedTags,
|
||||
};
|
||||
|
||||
console.log("selectedQuestions")
|
||||
console.log(selectedQuestions)
|
||||
const FieldsRule = {
|
||||
Company: [{ QuestionID: {} }],
|
||||
Lead: [{ QuestionID: {} }],
|
||||
Customer: [{ QuestionID: {} }],
|
||||
};
|
||||
|
||||
console.log("selectedQuestions");
|
||||
console.log(selectedQuestions);
|
||||
for (let key in FieldsRule) {
|
||||
console.log("current key ", key)
|
||||
console.log("current key ", key);
|
||||
selectedQuestions[key as QuestionKeys].forEach((id) => {
|
||||
FieldsRule[key as QuestionKeys][0].QuestionID[id] = 0
|
||||
})
|
||||
FieldsRule[key as QuestionKeys][0].QuestionID[id] = 0;
|
||||
});
|
||||
}
|
||||
for (let key in body.TagsToAdd) {
|
||||
body.TagsToAdd[key as TagKeys] = body.TagsToAdd[key as TagKeys].map(id => Number(id))
|
||||
body.TagsToAdd[key as TagKeys] = body.TagsToAdd[key as TagKeys].map((id) => Number(id));
|
||||
}
|
||||
body.FieldsRule = FieldsRule
|
||||
body.FieldsRule = FieldsRule;
|
||||
|
||||
console.log("На отправку")
|
||||
console.log(body)
|
||||
console.log("На отправку");
|
||||
console.log(body);
|
||||
|
||||
if (firstRules) {
|
||||
setIntegrationRules(quizID.toString(), body)
|
||||
setIntegrationRules(quizID.toString(), body);
|
||||
} else {
|
||||
updateIntegrationRules(quizID.toString(), body)
|
||||
updateIntegrationRules(quizID.toString(), body);
|
||||
}
|
||||
|
||||
handleCloseModal();
|
||||
@ -202,9 +200,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
const steps = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: accountInfo
|
||||
? "Информация об аккаунте"
|
||||
: "Авторизация в аккаунте",
|
||||
title: accountInfo ? "Информация об аккаунте" : "Авторизация в аккаунте",
|
||||
isSettingsAvailable: false,
|
||||
component: accountInfo ? (
|
||||
<AmoAccountInfo
|
||||
@ -239,11 +235,9 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
users={arrayOfUsers}
|
||||
selectedDealUser={selectedDealUser}
|
||||
selectedStep={selectedPipelineStep}
|
||||
|
||||
steps={arrayOfPipelinesSteps}
|
||||
setSelectedDealPerformer={setSelectedDealPerformer}
|
||||
setSelectedStep={setSelectedPipelineStep}
|
||||
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleNextStep}
|
||||
/>
|
||||
@ -270,7 +264,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
tagsItems={arrayOfTags}
|
||||
selectedTags={selectedTags}
|
||||
openDelete={setOpenDelete}
|
||||
handleScroll={() => { }}
|
||||
handleScroll={() => {}}
|
||||
handleAddTag={handleAddTagQuestion}
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleNextStep}
|
||||
@ -307,7 +301,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
arrayOfUsers,
|
||||
minifiedQuestions,
|
||||
arrayOfTags,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
const stepTitles = steps.map((step) => step.title);
|
||||
@ -354,9 +348,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
top: "15px",
|
||||
}}
|
||||
>
|
||||
<CloseIcon
|
||||
sx={{ width: "12px", height: "12px", transform: "scale(1.5)" }}
|
||||
/>
|
||||
<CloseIcon sx={{ width: "12px", height: "12px", transform: "scale(1.5)" }} />
|
||||
</IconButton>
|
||||
<Box
|
||||
sx={{
|
||||
@ -376,29 +368,25 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
setStep={setStep}
|
||||
startRemoveAccount={() => setIsTryRemoveAccount(true)}
|
||||
/>
|
||||
{openDelete !== null ?
|
||||
(
|
||||
<AmoDeleteTagQuestion
|
||||
close={() => setOpenDelete(null)}
|
||||
deleteItem={handleDeleteTagQuestion}
|
||||
/>
|
||||
)
|
||||
:
|
||||
(<>
|
||||
{isTryRemoveAccount && (
|
||||
<AmoRemoveAccount
|
||||
stopThisPage={() => setIsTryRemoveAccount(false)}
|
||||
/>
|
||||
)}
|
||||
{openDelete !== null ? (
|
||||
<AmoDeleteTagQuestion
|
||||
close={() => setOpenDelete(null)}
|
||||
deleteItem={handleDeleteTagQuestion}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{isTryRemoveAccount && <AmoRemoveAccount stopThisPage={() => setIsTryRemoveAccount(false)} />}
|
||||
{isSettingsBlock && (
|
||||
<Box sx={{ flexGrow: 1, width: "100%" }}>
|
||||
<AmoSettingsBlock
|
||||
stepTitles={stepTitles}
|
||||
setIsSettingsBlock={setIsSettingsBlock}
|
||||
setStep={setStep}
|
||||
selectedDealUser={arrayOfUsers.find(u => u.id === selectedDealUser)?.title || "не указан"}
|
||||
selectedFunnel={arrayOfPipelines.find(p => p.id === selectedPipeline)?.title || "нет данных"}
|
||||
selectedStage={arrayOfPipelinesSteps.find(s => s.id === selectedPipelineStep)?.title || "нет данных"}
|
||||
selectedDealUser={arrayOfUsers.find((u) => u.id === selectedDealUser)?.title || "не указан"}
|
||||
selectedFunnel={arrayOfPipelines.find((p) => p.id === selectedPipeline)?.title || "нет данных"}
|
||||
selectedStage={
|
||||
arrayOfPipelinesSteps.find((s) => s.id === selectedPipelineStep)?.title || "нет данных"
|
||||
}
|
||||
selectedQuestions={selectedQuestions}
|
||||
selectedTags={selectedTags}
|
||||
/>
|
||||
@ -407,8 +395,8 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
{!isSettingsBlock && !isTryRemoveAccount && (
|
||||
<Box sx={{ flexGrow: 1, width: "100%" }}>{steps[step].component}</Box>
|
||||
)}
|
||||
</>)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Dialog>
|
||||
);
|
||||
|
@ -130,9 +130,8 @@ export const AmoLogin: FC<IntegrationStep1Props> = ({ handleNextStep }) => {
|
||||
lineHeight: "1",
|
||||
}}
|
||||
>
|
||||
После нажатия на кнопку - "Подключить", вас переадресует на страницу
|
||||
подключения интеграции в ваш аккаунт AmoCRM. Пожалуйста, согласитесь
|
||||
на всё, что мы предлагаем, иначе чуда не случится.
|
||||
После нажатия на кнопку - "Подключить", вас переадресует на страницу подключения интеграции в ваш аккаунт
|
||||
AmoCRM. Пожалуйста, согласитесь на всё, что мы предлагаем, иначе чуда не случится.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ marginTop: "50px" }}>
|
||||
|
@ -19,7 +19,7 @@ export const AmoModalTitle: FC<AmoModalTitleProps> = ({
|
||||
setIsSettingsBlock,
|
||||
isSettingsBlock,
|
||||
setStep,
|
||||
startRemoveAccount
|
||||
startRemoveAccount,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
@ -27,7 +27,7 @@ export const AmoModalTitle: FC<AmoModalTitleProps> = ({
|
||||
const handleClick = useCallback(async () => {
|
||||
if (isSettingsBlock) {
|
||||
startRemoveAccount();
|
||||
setIsSettingsBlock(false)
|
||||
setIsSettingsBlock(false);
|
||||
setStep(0);
|
||||
return;
|
||||
}
|
||||
|
@ -1,7 +1,4 @@
|
||||
import {
|
||||
FC,
|
||||
useState,
|
||||
} from "react";
|
||||
import { FC, useState } from "react";
|
||||
import { ItemsSelectionView } from "./ItemsSelectionView/ItemsSelectionView";
|
||||
import { ItemDetailsView } from "./ItemDetailsView/ItemDetailsView";
|
||||
import { Box } from "@mui/material";
|
||||
@ -27,24 +24,24 @@ export const AmoQuestions: FC<Props> = ({
|
||||
handleAddQuestion,
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
openDelete
|
||||
openDelete,
|
||||
}) => {
|
||||
const [isSelection, setIsSelection] = useState<boolean>(false);
|
||||
const [activeScope, setActiveScope] = useState<QuestionKeys | null>(null);
|
||||
const [selectedQuestion, setSelectedQuestion] = useState<string | null>(null);
|
||||
|
||||
const handleAdd = () => {
|
||||
if (activeScope === null || selectedQuestion === null) return
|
||||
setActiveScope(null)
|
||||
handleAddQuestion(activeScope, selectedQuestion, "question")
|
||||
}
|
||||
const handleDelete = (id: string, scope:QuestionKeys) => {
|
||||
if (activeScope === null || selectedQuestion === null) return;
|
||||
setActiveScope(null);
|
||||
handleAddQuestion(activeScope, selectedQuestion, "question");
|
||||
};
|
||||
const handleDelete = (id: string, scope: QuestionKeys) => {
|
||||
openDelete({
|
||||
id,
|
||||
scope,
|
||||
type: "question"
|
||||
})
|
||||
}
|
||||
type: "question",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -74,7 +71,7 @@ export const AmoQuestions: FC<Props> = ({
|
||||
) : (
|
||||
// Табличка
|
||||
<ItemDetailsView
|
||||
items={questionsItems}
|
||||
items={questionsItems}
|
||||
setActiveScope={setActiveScope}
|
||||
selectedQuestions={selectedQuestions}
|
||||
setIsSelection={setIsSelection}
|
||||
|
@ -19,14 +19,14 @@ export const AnswerItem: FC<AnswerItemProps> = ({ fieldName, fieldValue, deleteH
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between"
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
width: "100%"
|
||||
}}
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
@ -58,7 +58,7 @@ export const AnswerItem: FC<AnswerItemProps> = ({ fieldName, fieldValue, deleteH
|
||||
</Box>
|
||||
<IconButton
|
||||
sx={{
|
||||
m: "auto"
|
||||
m: "auto",
|
||||
}}
|
||||
onClick={deleteHC}
|
||||
>
|
||||
|
@ -11,13 +11,7 @@ type ItemProps = {
|
||||
data: SelectedTags | SelectedQuestions;
|
||||
deleteHC: (id: string, scope: QuestionKeys | TagKeys) => void;
|
||||
};
|
||||
export const Item: FC<ItemProps> = ({
|
||||
items,
|
||||
title,
|
||||
onAddBtnClick,
|
||||
data,
|
||||
deleteHC
|
||||
}) => {
|
||||
export const Item: FC<ItemProps> = ({ items, title, onAddBtnClick, data, deleteHC }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const titleDictionary = {
|
||||
@ -28,7 +22,7 @@ export const Item: FC<ItemProps> = ({
|
||||
};
|
||||
|
||||
const translatedTitle = titleDictionary[title];
|
||||
const selectedOptions = data[title]
|
||||
const selectedOptions = data[title];
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -50,16 +44,14 @@ export const Item: FC<ItemProps> = ({
|
||||
height: "40px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: "16px", fontWeight: 500 }}>
|
||||
{translatedTitle}
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: "16px", fontWeight: 500 }}>{translatedTitle}</Typography>
|
||||
</Box>
|
||||
{selectedOptions &&
|
||||
selectedOptions.map((id, index) => (
|
||||
<AnswerItem
|
||||
key={id + index}
|
||||
fieldValue={"Значение поля"}
|
||||
fieldName={items.find(e => e.id === id)?.title || id}
|
||||
fieldName={items.find((e) => e.id === id)?.title || id}
|
||||
deleteHC={() => deleteHC(selectedOptions[index], title)}
|
||||
/>
|
||||
))}
|
||||
|
@ -13,7 +13,7 @@ type ItemDetailsViewProps = {
|
||||
handleLargeBtn: () => void;
|
||||
selectedQuestions: SelectedQuestions;
|
||||
setActiveScope: (value: QuestionKeys | null) => void;
|
||||
deleteHC: (id: string, scope:QuestionKeys) => void;
|
||||
deleteHC: (id: string, scope: QuestionKeys) => void;
|
||||
};
|
||||
|
||||
export const ItemDetailsView: FC<ItemDetailsViewProps> = ({
|
||||
@ -51,7 +51,7 @@ export const ItemDetailsView: FC<ItemDetailsViewProps> = ({
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "start",
|
||||
}}
|
||||
>
|
||||
>
|
||||
{selectedQuestions &&
|
||||
Object.keys(selectedQuestions).map((item) => (
|
||||
<Item
|
||||
|
@ -1,49 +1,48 @@
|
||||
import { FC } from "react"
|
||||
import { Button, Typography, useTheme, Box } from "@mui/material"
|
||||
import { FC } from "react";
|
||||
import { Button, Typography, useTheme, Box } from "@mui/material";
|
||||
|
||||
interface Props {
|
||||
deleteItem: () => void;
|
||||
close: () => void;
|
||||
deleteItem: () => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export const AmoDeleteTagQuestion: FC<Props> = ({
|
||||
close,
|
||||
deleteItem,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
export const AmoDeleteTagQuestion: FC<Props> = ({ close, deleteItem }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
mt: "30px",
|
||||
}}
|
||||
>
|
||||
<Typography textAlign="center">Вы хотите удалить элемент?</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "30px"
|
||||
display: "flex",
|
||||
justifyContent: "space-evenly",
|
||||
flexWrap: "wrap",
|
||||
margin: "30px auto",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
width: "150px",
|
||||
}}
|
||||
onClick={close}
|
||||
>
|
||||
<Typography textAlign="center">
|
||||
Вы хотите удалить элемент?
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-evenly",
|
||||
flexWrap: "wrap",
|
||||
margin: "30px auto",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
width: "150px",
|
||||
}}
|
||||
onClick={close}
|
||||
>отмена</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
width: "150px",
|
||||
}}
|
||||
onClick={deleteItem}
|
||||
>удалить</Button>
|
||||
</Box>
|
||||
</Box >
|
||||
)
|
||||
}
|
||||
отмена
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
width: "150px",
|
||||
}}
|
||||
onClick={deleteItem}
|
||||
>
|
||||
удалить
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -1,13 +1,9 @@
|
||||
import {
|
||||
FC,
|
||||
useState,
|
||||
} from "react";
|
||||
import { FC, useState } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import { ItemsSelectionView } from "../AmoQuestions/ItemsSelectionView/ItemsSelectionView";
|
||||
import { TagsDetailsView } from "./TagsDetailsView/TagsDetailsView";
|
||||
import { MinifiedData, QuestionKeys, SelectedTags, TagKeys, TagQuestionHC } from "../types";
|
||||
|
||||
|
||||
type Props = {
|
||||
tagsItems: MinifiedData[] | [];
|
||||
selectedTags: SelectedTags;
|
||||
@ -32,17 +28,17 @@ export const AmoTags: FC<Props> = ({
|
||||
const [selectedTag, setSelectedTag] = useState<string | null>(null);
|
||||
|
||||
const handleAdd = () => {
|
||||
if (activeScope === null || selectedTag === null) return
|
||||
setActiveScope(null)
|
||||
handleAddTag(activeScope, selectedTag, "tag")
|
||||
}
|
||||
if (activeScope === null || selectedTag === null) return;
|
||||
setActiveScope(null);
|
||||
handleAddTag(activeScope, selectedTag, "tag");
|
||||
};
|
||||
const handleDelete = (id: string, scope: TagKeys) => {
|
||||
openDelete({
|
||||
id,
|
||||
scope,
|
||||
type: "tag"
|
||||
})
|
||||
}
|
||||
type: "tag",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
@ -11,7 +11,7 @@ type TagsDetailsViewProps = {
|
||||
handleNextStep: () => void;
|
||||
setActiveScope: (value: TagKeys | null) => void;
|
||||
selectedTags: SelectedTags;
|
||||
deleteHC: (id: string, scope:TagKeys) => void;
|
||||
deleteHC: (id: string, scope: TagKeys) => void;
|
||||
};
|
||||
|
||||
export const TagsDetailsView: FC<TagsDetailsViewProps> = ({
|
||||
@ -56,11 +56,7 @@ export const TagsDetailsView: FC<TagsDetailsViewProps> = ({
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ fontSize: "14px", color: theme.palette.grey2.main }}
|
||||
>
|
||||
Результат
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: "14px", color: theme.palette.grey2.main }}>Результат</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -14,7 +14,6 @@ type Props = {
|
||||
setSelectedDealPerformer: (value: string | null) => void;
|
||||
selectedPipeline: string | null;
|
||||
setSelectedPipeline: (value: string | null) => void;
|
||||
|
||||
};
|
||||
|
||||
export const Pipelines: FC<Props> = ({
|
||||
|
@ -64,9 +64,7 @@ export const SettingItem: FC<SettingItemProps> = ({
|
||||
);
|
||||
}
|
||||
if (step === 4) {
|
||||
const isFilled = Object.values(selectedTags).some(
|
||||
(array) => array.length > 0,
|
||||
);
|
||||
const isFilled = Object.values(selectedTags).some((array) => array.length > 0);
|
||||
const status = isFilled ? "Заполнено" : "Не заполнено";
|
||||
|
||||
return (
|
||||
@ -96,9 +94,7 @@ export const SettingItem: FC<SettingItemProps> = ({
|
||||
);
|
||||
}
|
||||
if (step === 5) {
|
||||
const isFilled = Object.values(selectedQuestions).some(
|
||||
(array) => array.length > 0,
|
||||
);
|
||||
const isFilled = Object.values(selectedQuestions).some((array) => array.length > 0);
|
||||
const status = isFilled ? "Заполнено" : "Не заполнено";
|
||||
|
||||
return (
|
||||
|
@ -12,7 +12,7 @@ export type MinifiedData = {
|
||||
};
|
||||
|
||||
export type TagQuestionHC = {
|
||||
scope: QuestionKeys | TagKeys,
|
||||
id: string,
|
||||
type: "question" | "tag"
|
||||
};
|
||||
scope: QuestionKeys | TagKeys;
|
||||
id: string;
|
||||
type: "question" | "tag";
|
||||
};
|
||||
|
@ -1,256 +1,250 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import type {
|
||||
TagKeys,
|
||||
SelectedTags,
|
||||
QuestionKeys,
|
||||
SelectedQuestions,
|
||||
MinifiedData,
|
||||
} from "./types";
|
||||
import type { TagKeys, SelectedTags, QuestionKeys, SelectedQuestions, MinifiedData } from "./types";
|
||||
import {
|
||||
AccountResponse,
|
||||
getIntegrationRules,
|
||||
getPipelines,
|
||||
getSteps,
|
||||
getTags,
|
||||
getUsers,
|
||||
getAccount,
|
||||
FieldsRule,
|
||||
AccountResponse,
|
||||
getIntegrationRules,
|
||||
getPipelines,
|
||||
getSteps,
|
||||
getTags,
|
||||
getUsers,
|
||||
getAccount,
|
||||
FieldsRule,
|
||||
} from "@/api/integration";
|
||||
|
||||
const SIZE = 75;
|
||||
|
||||
interface Props {
|
||||
isModalOpen: boolean;
|
||||
isTryRemoveAccount: boolean;
|
||||
quizID: number;
|
||||
isModalOpen: boolean;
|
||||
isTryRemoveAccount: boolean;
|
||||
quizID: number;
|
||||
}
|
||||
|
||||
export const useAmoIntegration = ({
|
||||
isModalOpen,
|
||||
isTryRemoveAccount,
|
||||
quizID,
|
||||
}: Props) => {
|
||||
const [isloadingPage, setIsLoadingPage] = useState<boolean>(true);
|
||||
const [firstRules, setFirstRules] = useState<boolean>(false);
|
||||
const [accountInfo, setAccountInfo] = useState<AccountResponse | null>(null);
|
||||
export const useAmoIntegration = ({ isModalOpen, isTryRemoveAccount, quizID }: Props) => {
|
||||
const [isloadingPage, setIsLoadingPage] = useState<boolean>(true);
|
||||
const [firstRules, setFirstRules] = useState<boolean>(false);
|
||||
const [accountInfo, setAccountInfo] = useState<AccountResponse | null>(null);
|
||||
|
||||
const [arrayOfPipelines, setArrayOfPipelines] = useState<MinifiedData[]>([]);
|
||||
const [arrayOfPipelinesSteps, setArrayOfPipelinesSteps] = useState<MinifiedData[]>([]);
|
||||
const [arrayOfUsers, setArrayOfUsers] = useState<MinifiedData[]>([]);
|
||||
const [arrayOfTags, setArrayOfTags] = useState<MinifiedData[]>([]);
|
||||
const [arrayOfPipelines, setArrayOfPipelines] = useState<MinifiedData[]>([]);
|
||||
const [arrayOfPipelinesSteps, setArrayOfPipelinesSteps] = useState<MinifiedData[]>([]);
|
||||
const [arrayOfUsers, setArrayOfUsers] = useState<MinifiedData[]>([]);
|
||||
const [arrayOfTags, setArrayOfTags] = useState<MinifiedData[]>([]);
|
||||
|
||||
const [selectedPipeline, setSelectedPipeline] = useState<string | null>(null);
|
||||
const [selectedPipelineStep, setSelectedPipelineStep] = useState<string | null>(null);
|
||||
const [selectedDealUser, setSelectedDealPerformer] = useState<string | null>(null);
|
||||
const [selectedPipeline, setSelectedPipeline] = useState<string | null>(null);
|
||||
const [selectedPipelineStep, setSelectedPipelineStep] = useState<string | null>(null);
|
||||
const [selectedDealUser, setSelectedDealPerformer] = useState<string | null>(null);
|
||||
|
||||
const [questionsBackend, setQuestionsBackend] = useState<FieldsRule>({} as FieldsRule);
|
||||
const [selectedTags, setSelectedTags] = useState<SelectedTags>({
|
||||
const [questionsBackend, setQuestionsBackend] = useState<FieldsRule>({} as FieldsRule);
|
||||
const [selectedTags, setSelectedTags] = useState<SelectedTags>({
|
||||
Lead: [],
|
||||
Contact: [],
|
||||
Company: [],
|
||||
Customer: [],
|
||||
});
|
||||
const [selectedQuestions, setSelectedQuestions] = useState<SelectedQuestions>({
|
||||
Lead: [],
|
||||
Company: [],
|
||||
Customer: [],
|
||||
});
|
||||
|
||||
const [pageOfPipelines, setPageOfPipelines] = useState(1);
|
||||
const [pageOfPipelinesSteps, setPageOfPipelinesSteps] = useState(1);
|
||||
const [pageOfUsers, setPageOfUsers] = useState(1);
|
||||
const [pageOfTags, setPageOfTags] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen && !isTryRemoveAccount) {
|
||||
const fetchAccountRules = async () => {
|
||||
setIsLoadingPage(true);
|
||||
const [account, accountError] = await getAccount();
|
||||
|
||||
if (accountError) {
|
||||
if (!accountError.includes("Not Found")) enqueueSnackbar(accountError);
|
||||
setAccountInfo(null);
|
||||
}
|
||||
if (account) {
|
||||
setAccountInfo(account);
|
||||
}
|
||||
const [settingsResponse, rulesError] = await getIntegrationRules(quizID.toString());
|
||||
|
||||
if (rulesError) {
|
||||
if (rulesError === "first") setFirstRules(true);
|
||||
if (!rulesError.includes("Not Found") && !rulesError.includes("first")) enqueueSnackbar(rulesError);
|
||||
}
|
||||
if (settingsResponse) {
|
||||
if (settingsResponse.PipelineID) setSelectedPipeline(settingsResponse.PipelineID.toString());
|
||||
if (settingsResponse.StepID) setSelectedPipelineStep(settingsResponse.StepID.toString());
|
||||
if (settingsResponse.PerformerID) setSelectedDealPerformer(settingsResponse.PerformerID.toString());
|
||||
|
||||
if (Boolean(settingsResponse.FieldsRule) && Object.keys(settingsResponse?.FieldsRule).length > 0) {
|
||||
const gottenQuestions = { ...selectedQuestions };
|
||||
setQuestionsBackend(settingsResponse.FieldsRule);
|
||||
|
||||
for (let key in settingsResponse.FieldsRule) {
|
||||
if (
|
||||
settingsResponse.FieldsRule[key as QuestionKeys] !== null &&
|
||||
Array.isArray(settingsResponse.FieldsRule[key as QuestionKeys])
|
||||
) {
|
||||
const gottenList = settingsResponse.FieldsRule[key as QuestionKeys];
|
||||
|
||||
if (gottenList !== null) gottenQuestions[key as QuestionKeys] = Object.keys(gottenList[0].QuestionID);
|
||||
}
|
||||
}
|
||||
setSelectedQuestions(gottenQuestions);
|
||||
}
|
||||
|
||||
if (Boolean(settingsResponse.TagsToAdd) && Object.keys(settingsResponse.TagsToAdd).length > 0) {
|
||||
const gottenTags = { ...selectedTags };
|
||||
|
||||
for (let key in settingsResponse.TagsToAdd) {
|
||||
const gottenList = settingsResponse.TagsToAdd[key as TagKeys];
|
||||
if (gottenList !== null && Array.isArray(gottenList)) {
|
||||
gottenTags[key as TagKeys] = gottenList.map((e) => e.toString());
|
||||
}
|
||||
}
|
||||
setSelectedTags(gottenTags);
|
||||
}
|
||||
setFirstRules(false);
|
||||
}
|
||||
setIsLoadingPage(false);
|
||||
};
|
||||
|
||||
fetchAccountRules();
|
||||
} else {
|
||||
//Вот по-хорошему компонент должен размонтироваться и стереть всё. Но это будет сделано позже
|
||||
setArrayOfPipelines([]);
|
||||
setArrayOfPipelinesSteps([]);
|
||||
setArrayOfUsers([]);
|
||||
setArrayOfTags([]);
|
||||
setSelectedPipeline(null);
|
||||
setSelectedPipelineStep(null);
|
||||
setSelectedDealPerformer(null);
|
||||
setQuestionsBackend({} as FieldsRule);
|
||||
setSelectedTags({
|
||||
Lead: [],
|
||||
Contact: [],
|
||||
Company: [],
|
||||
Customer: [],
|
||||
});
|
||||
const [selectedQuestions, setSelectedQuestions] = useState<SelectedQuestions>({
|
||||
});
|
||||
setSelectedQuestions({
|
||||
Lead: [],
|
||||
Company: [],
|
||||
Customer: [],
|
||||
});
|
||||
setPageOfPipelines(1);
|
||||
setPageOfPipelinesSteps(1);
|
||||
setPageOfUsers(1);
|
||||
setPageOfTags(1);
|
||||
}
|
||||
}, [isModalOpen, isTryRemoveAccount]);
|
||||
|
||||
useEffect(() => {
|
||||
getPipelines({
|
||||
page: pageOfPipelines,
|
||||
size: SIZE,
|
||||
}).then(([response]) => {
|
||||
if (response && response.items !== null) {
|
||||
const minifiedPipelines: MinifiedData[] = [];
|
||||
|
||||
response.items.forEach((step) => {
|
||||
minifiedPipelines.push({
|
||||
id: step.AmoID.toString(),
|
||||
title: step.Name,
|
||||
});
|
||||
});
|
||||
setArrayOfPipelines((prevItems) => [...prevItems, ...minifiedPipelines]);
|
||||
setPageOfPipelinesSteps(1);
|
||||
}
|
||||
});
|
||||
}, [pageOfPipelines]);
|
||||
useEffect(() => {
|
||||
const oldData = pageOfPipelinesSteps === 1 ? [] : arrayOfPipelinesSteps;
|
||||
if (selectedPipeline !== null)
|
||||
getSteps({
|
||||
page: pageOfPipelinesSteps,
|
||||
size: SIZE,
|
||||
pipelineId: Number(selectedPipeline),
|
||||
}).then(([response]) => {
|
||||
if (response && response.items !== null) {
|
||||
const minifiedSteps: MinifiedData[] = [];
|
||||
|
||||
const [pageOfPipelines, setPageOfPipelines] = useState(1);
|
||||
const [pageOfPipelinesSteps, setPageOfPipelinesSteps] = useState(1);
|
||||
const [pageOfUsers, setPageOfUsers] = useState(1);
|
||||
const [pageOfTags, setPageOfTags] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen && !isTryRemoveAccount) {
|
||||
const fetchAccountRules = async () => {
|
||||
setIsLoadingPage(true)
|
||||
const [account, accountError] = await getAccount();
|
||||
|
||||
if (accountError) {
|
||||
if (!accountError.includes("Not Found")) enqueueSnackbar(accountError)
|
||||
setAccountInfo(null);
|
||||
}
|
||||
if (account) {
|
||||
setAccountInfo(account);
|
||||
}
|
||||
const [settingsResponse, rulesError] = await getIntegrationRules(quizID.toString());
|
||||
|
||||
if (rulesError) {
|
||||
if (rulesError === "first") setFirstRules(true);
|
||||
if (!rulesError.includes("Not Found") && !rulesError.includes("first")) enqueueSnackbar(rulesError);
|
||||
}
|
||||
if (settingsResponse) {
|
||||
if (settingsResponse.PipelineID) setSelectedPipeline(settingsResponse.PipelineID.toString())
|
||||
if (settingsResponse.StepID) setSelectedPipelineStep(settingsResponse.StepID.toString())
|
||||
if (settingsResponse.PerformerID) setSelectedDealPerformer(settingsResponse.PerformerID.toString())
|
||||
|
||||
if (Boolean(settingsResponse.FieldsRule) &&
|
||||
Object.keys(settingsResponse?.FieldsRule).length > 0) {
|
||||
const gottenQuestions = { ...selectedQuestions }
|
||||
setQuestionsBackend(settingsResponse.FieldsRule)
|
||||
|
||||
for (let key in settingsResponse.FieldsRule) {
|
||||
if (settingsResponse.FieldsRule[key as QuestionKeys] !== null && Array.isArray(settingsResponse.FieldsRule[key as QuestionKeys])) {
|
||||
|
||||
const gottenList = settingsResponse.FieldsRule[key as QuestionKeys]
|
||||
|
||||
if (gottenList !== null)
|
||||
gottenQuestions[key as QuestionKeys] = Object.keys(gottenList[0].QuestionID)
|
||||
}
|
||||
}
|
||||
setSelectedQuestions(gottenQuestions)
|
||||
}
|
||||
|
||||
if (Boolean(settingsResponse.TagsToAdd) &&
|
||||
Object.keys(settingsResponse.TagsToAdd).length > 0) {
|
||||
const gottenTags = { ...selectedTags }
|
||||
|
||||
for (let key in settingsResponse.TagsToAdd) {
|
||||
const gottenList = settingsResponse.TagsToAdd[key as TagKeys]
|
||||
if (gottenList !== null && Array.isArray(gottenList)) {
|
||||
gottenTags[key as TagKeys] = gottenList.map(e => e.toString())
|
||||
}
|
||||
}
|
||||
setSelectedTags(gottenTags)
|
||||
}
|
||||
setFirstRules(false);
|
||||
}
|
||||
setIsLoadingPage(false)
|
||||
};
|
||||
|
||||
fetchAccountRules();
|
||||
} else {
|
||||
//Вот по-хорошему компонент должен размонтироваться и стереть всё. Но это будет сделано позже
|
||||
setArrayOfPipelines([]);
|
||||
setArrayOfPipelinesSteps([]);
|
||||
setArrayOfUsers([]);
|
||||
setArrayOfTags([]);
|
||||
setSelectedPipeline(null);
|
||||
setSelectedPipelineStep(null);
|
||||
setSelectedDealPerformer(null);
|
||||
setQuestionsBackend({} as FieldsRule);
|
||||
setSelectedTags({
|
||||
Lead: [],
|
||||
Contact: [],
|
||||
Company: [],
|
||||
Customer: [],
|
||||
response.items.forEach((step) => {
|
||||
minifiedSteps.push({
|
||||
id: step.AmoID.toString(),
|
||||
title: step.Name,
|
||||
});
|
||||
setSelectedQuestions({
|
||||
Lead: [],
|
||||
Company: [],
|
||||
Customer: [],
|
||||
});
|
||||
setPageOfPipelines(1);
|
||||
setPageOfPipelinesSteps(1);
|
||||
setPageOfUsers(1);
|
||||
setPageOfTags(1);
|
||||
});
|
||||
setArrayOfPipelinesSteps([...oldData, ...minifiedSteps]);
|
||||
}
|
||||
}, [isModalOpen, isTryRemoveAccount]);
|
||||
});
|
||||
}, [selectedPipeline, pageOfPipelinesSteps]);
|
||||
useEffect(() => {
|
||||
getUsers({
|
||||
page: pageOfUsers,
|
||||
size: SIZE,
|
||||
}).then(([response]) => {
|
||||
if (response && response.items !== null) {
|
||||
const minifiedUsers: MinifiedData[] = [];
|
||||
|
||||
useEffect(() => {
|
||||
getPipelines({
|
||||
page: pageOfPipelines,
|
||||
size: SIZE,
|
||||
}).then(([response]) => {
|
||||
if (response && response.items !== null) {
|
||||
const minifiedPipelines: MinifiedData[] = []
|
||||
|
||||
response.items.forEach((step) => {
|
||||
minifiedPipelines.push({
|
||||
id: step.AmoID.toString(),
|
||||
title: step.Name
|
||||
})
|
||||
})
|
||||
setArrayOfPipelines((prevItems) => [...prevItems, ...minifiedPipelines]);
|
||||
setPageOfPipelinesSteps(1)
|
||||
}
|
||||
})
|
||||
}, [pageOfPipelines])
|
||||
useEffect(() => {
|
||||
const oldData = pageOfPipelinesSteps === 1 ? [] : arrayOfPipelinesSteps
|
||||
if (selectedPipeline !== null)
|
||||
getSteps({
|
||||
page: pageOfPipelinesSteps,
|
||||
size: SIZE,
|
||||
pipelineId: Number(selectedPipeline),
|
||||
}).then(([response]) => {
|
||||
if (response && response.items !== null) {
|
||||
const minifiedSteps: MinifiedData[] = []
|
||||
|
||||
response.items.forEach((step) => {
|
||||
minifiedSteps.push({
|
||||
id: step.AmoID.toString(),
|
||||
title: step.Name
|
||||
})
|
||||
})
|
||||
setArrayOfPipelinesSteps([...oldData, ...minifiedSteps]);
|
||||
}
|
||||
});
|
||||
}, [selectedPipeline, pageOfPipelinesSteps])
|
||||
useEffect(() => {
|
||||
getUsers({
|
||||
page: pageOfUsers,
|
||||
size: SIZE,
|
||||
}).then(([response]) => {
|
||||
if (response && response.items !== null) {
|
||||
const minifiedUsers: MinifiedData[] = []
|
||||
|
||||
response.items.forEach((step) => {
|
||||
minifiedUsers.push({
|
||||
id: step.amoUserID.toString(),
|
||||
title: step.name
|
||||
})
|
||||
})
|
||||
setArrayOfUsers((prevItems) => [...prevItems, ...minifiedUsers]);
|
||||
}
|
||||
response.items.forEach((step) => {
|
||||
minifiedUsers.push({
|
||||
id: step.amoUserID.toString(),
|
||||
title: step.name,
|
||||
});
|
||||
});
|
||||
}, [pageOfUsers])
|
||||
useEffect(() => {
|
||||
getTags({
|
||||
page: pageOfTags,
|
||||
size: SIZE,
|
||||
}).then(([response]) => {
|
||||
if (response && response.items !== null) {
|
||||
const minifiedTags: MinifiedData[] = []
|
||||
setArrayOfUsers((prevItems) => [...prevItems, ...minifiedUsers]);
|
||||
}
|
||||
});
|
||||
}, [pageOfUsers]);
|
||||
useEffect(() => {
|
||||
getTags({
|
||||
page: pageOfTags,
|
||||
size: SIZE,
|
||||
}).then(([response]) => {
|
||||
if (response && response.items !== null) {
|
||||
const minifiedTags: MinifiedData[] = [];
|
||||
|
||||
response.items.forEach((step) => {
|
||||
minifiedTags.push({
|
||||
id: step.AmoID.toString(),
|
||||
title: step.Name,
|
||||
entity: step.Entity === "leads" ? "Lead" :
|
||||
step.Entity === "contacts" ? "Contact" :
|
||||
step.Entity === "companies" ? "Company" : "Customer"
|
||||
})
|
||||
})
|
||||
setArrayOfTags((prevItems) => [...prevItems, ...minifiedTags]);
|
||||
}
|
||||
response.items.forEach((step) => {
|
||||
minifiedTags.push({
|
||||
id: step.AmoID.toString(),
|
||||
title: step.Name,
|
||||
entity:
|
||||
step.Entity === "leads"
|
||||
? "Lead"
|
||||
: step.Entity === "contacts"
|
||||
? "Contact"
|
||||
: step.Entity === "companies"
|
||||
? "Company"
|
||||
: "Customer",
|
||||
});
|
||||
});
|
||||
}, [pageOfTags])
|
||||
setArrayOfTags((prevItems) => [...prevItems, ...minifiedTags]);
|
||||
}
|
||||
});
|
||||
}, [pageOfTags]);
|
||||
|
||||
return ({
|
||||
isloadingPage,
|
||||
firstRules,
|
||||
accountInfo,
|
||||
arrayOfPipelines,
|
||||
arrayOfPipelinesSteps,
|
||||
arrayOfUsers,
|
||||
arrayOfTags,
|
||||
selectedPipeline,
|
||||
setSelectedPipeline,
|
||||
selectedPipelineStep,
|
||||
setSelectedPipelineStep,
|
||||
selectedDealUser,
|
||||
setSelectedDealPerformer,
|
||||
questionsBackend,
|
||||
selectedTags,
|
||||
setSelectedTags,
|
||||
selectedQuestions,
|
||||
setSelectedQuestions,
|
||||
setPageOfPipelines,
|
||||
setPageOfPipelinesSteps,
|
||||
setPageOfUsers,
|
||||
setPageOfTags,
|
||||
})
|
||||
}
|
||||
return {
|
||||
isloadingPage,
|
||||
firstRules,
|
||||
accountInfo,
|
||||
arrayOfPipelines,
|
||||
arrayOfPipelinesSteps,
|
||||
arrayOfUsers,
|
||||
arrayOfTags,
|
||||
selectedPipeline,
|
||||
setSelectedPipeline,
|
||||
selectedPipelineStep,
|
||||
setSelectedPipelineStep,
|
||||
selectedDealUser,
|
||||
setSelectedDealPerformer,
|
||||
questionsBackend,
|
||||
selectedTags,
|
||||
setSelectedTags,
|
||||
selectedQuestions,
|
||||
setSelectedQuestions,
|
||||
setPageOfPipelines,
|
||||
setPageOfPipelinesSteps,
|
||||
setPageOfUsers,
|
||||
setPageOfTags,
|
||||
};
|
||||
};
|
||||
|
@ -11,13 +11,13 @@ import { useCurrentQuiz } from "@/stores/quizes/hooks";
|
||||
const AnalyticsModal = lazy(() =>
|
||||
import("./AnalyticsModal/AnalyticsModal").then((module) => ({
|
||||
default: module.AnalyticsModal,
|
||||
})),
|
||||
}))
|
||||
);
|
||||
|
||||
const AmoCRMModal = lazy(() =>
|
||||
import("../IntegrationsModal/AmoCRMModal").then((module) => ({
|
||||
default: module.AmoCRMModal,
|
||||
})),
|
||||
}))
|
||||
);
|
||||
|
||||
type PartnersBoardProps = {
|
||||
|
@ -58,7 +58,7 @@ export default function PageOptions({ disableInput, question }: Props) {
|
||||
</Box>
|
||||
|
||||
<MediaSelectionAndDisplay
|
||||
resultData={question}
|
||||
question={question}
|
||||
cropAspectRatio={{ width: 1388.8, height: 793.2 }}
|
||||
/>
|
||||
</Box>
|
||||
|
@ -1,16 +1,8 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ButtonBase,
|
||||
Modal,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import SelectableButton from "@ui_kit/SelectableButton";
|
||||
import { Box, Button, ButtonBase, Dialog, Typography, useTheme } from "@mui/material";
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
import SelectableButton from "@ui_kit/SelectableButton";
|
||||
import { useState } from "react";
|
||||
import UploadIcon from "../../assets/icons/UploadIcon";
|
||||
|
||||
import type { DragEvent } from "react";
|
||||
|
||||
type BackgroundTypeModal = "linkVideo" | "ownVideo";
|
||||
@ -18,18 +10,12 @@ type BackgroundTypeModal = "linkVideo" | "ownVideo";
|
||||
type HelpQuestionsProps = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
video: string;
|
||||
video: string | null;
|
||||
onUpload: (number: string) => void;
|
||||
};
|
||||
|
||||
export const UploadVideoModal = ({
|
||||
open,
|
||||
onClose,
|
||||
video,
|
||||
onUpload,
|
||||
}: HelpQuestionsProps) => {
|
||||
const [backgroundTypeModal, setBackgroundTypeModal] =
|
||||
useState<BackgroundTypeModal>("linkVideo");
|
||||
export default function UploadVideoModal({ open, onClose, video, onUpload }: HelpQuestionsProps) {
|
||||
const [backgroundTypeModal, setBackgroundTypeModal] = useState<BackgroundTypeModal>("linkVideo");
|
||||
const theme = useTheme();
|
||||
|
||||
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
||||
@ -42,118 +28,102 @@ export const UploadVideoModal = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
maxWidth: "690px",
|
||||
bgcolor: "background.paper",
|
||||
PaperProps={{
|
||||
sx: {
|
||||
maxWidth: "640px",
|
||||
borderRadius: "12px",
|
||||
boxShadow: 24,
|
||||
p: 0,
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
padding: "20px",
|
||||
background: theme.palette.background.default,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
padding: "20px",
|
||||
background: theme.palette.background.default,
|
||||
}}
|
||||
<Typography sx={{ color: "#9A9AAF" }}>
|
||||
Видео можно вставить с любого хостинга: YouTube, Vimeo или загрузить собственное
|
||||
</Typography>
|
||||
<Button
|
||||
onClick={onClose}
|
||||
variant="contained"
|
||||
>
|
||||
<Typography sx={{ color: "#9A9AAF" }}>
|
||||
Видео можно вставить с любого хостинга: YouTube, Vimeo или загрузить
|
||||
собственное
|
||||
</Typography>
|
||||
<Button onClick={onClose} variant="contained">
|
||||
Готово
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ padding: "20px", gap: "10px", display: "flex" }}>
|
||||
<SelectableButton
|
||||
isSelected={backgroundTypeModal === "linkVideo"}
|
||||
onClick={() => setBackgroundTypeModal("linkVideo")}
|
||||
sx={{ maxWidth: "170px", padding: "10px" }}
|
||||
>
|
||||
Ссылка на видео
|
||||
</SelectableButton>
|
||||
<SelectableButton
|
||||
isSelected={backgroundTypeModal === "ownVideo"}
|
||||
onClick={() => setBackgroundTypeModal("ownVideo")}
|
||||
sx={{ maxWidth: "170px", padding: "10px" }}
|
||||
>
|
||||
Загрузить свое
|
||||
</SelectableButton>
|
||||
</Box>
|
||||
{backgroundTypeModal === "linkVideo" ? (
|
||||
<Box sx={{ padding: "20px" }}>
|
||||
<Typography sx={{ paddingBottom: "15px", fontWeight: "500" }}>
|
||||
Ссылка на видео
|
||||
</Typography>
|
||||
<CustomTextField
|
||||
placeholder={"http://example.com"}
|
||||
text={video}
|
||||
onChange={({ target }) => onUpload(target.value || " ")}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box sx={{ padding: "20px" }}>
|
||||
<Typography sx={{ paddingBottom: "15px", fontWeight: "500" }}>
|
||||
Загрузите видео
|
||||
</Typography>
|
||||
<ButtonBase
|
||||
component="label"
|
||||
sx={{ justifyContent: "flex-start", width: "100%" }}
|
||||
>
|
||||
<input
|
||||
onChange={({ target }) => {
|
||||
if (target.files?.length) {
|
||||
onUpload(URL.createObjectURL(target.files[0] || " "));
|
||||
}
|
||||
}}
|
||||
hidden
|
||||
accept="video/*"
|
||||
multiple
|
||||
type="file"
|
||||
/>
|
||||
<Box
|
||||
onDragOver={(event: DragEvent<HTMLDivElement>) =>
|
||||
event.preventDefault()
|
||||
}
|
||||
onDrop={handleDrop}
|
||||
sx={{
|
||||
width: "580px",
|
||||
padding: "33px 33px 33px 50px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
border: `1px solid ${theme.palette.grey2.main}`,
|
||||
borderRadius: "8px",
|
||||
gap: "50px",
|
||||
}}
|
||||
>
|
||||
<UploadIcon />
|
||||
<Box sx={{ color: "#9A9AAF" }}>
|
||||
<Typography sx={{ fontWeight: "500" }}>
|
||||
Добавить видео
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: "16px" }}>
|
||||
Принимает .mp4 и .mov формат — максимум 100мб
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
)}
|
||||
Готово
|
||||
</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
<Box sx={{ padding: "20px", gap: "10px", display: "flex" }}>
|
||||
<SelectableButton
|
||||
isSelected={backgroundTypeModal === "linkVideo"}
|
||||
onClick={() => setBackgroundTypeModal("linkVideo")}
|
||||
sx={{ maxWidth: "170px", padding: "10px" }}
|
||||
>
|
||||
Ссылка на видео
|
||||
</SelectableButton>
|
||||
<SelectableButton
|
||||
isSelected={backgroundTypeModal === "ownVideo"}
|
||||
onClick={() => setBackgroundTypeModal("ownVideo")}
|
||||
sx={{ maxWidth: "170px", padding: "10px" }}
|
||||
>
|
||||
Загрузить свое
|
||||
</SelectableButton>
|
||||
</Box>
|
||||
{backgroundTypeModal === "linkVideo" ? (
|
||||
<Box sx={{ padding: "20px" }}>
|
||||
<Typography sx={{ paddingBottom: "15px", fontWeight: "500" }}>Ссылка на видео</Typography>
|
||||
<CustomTextField
|
||||
placeholder={"http://example.com"}
|
||||
value={video || ""}
|
||||
onChange={({ target }) => onUpload(target.value || " ")}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box sx={{ padding: "20px" }}>
|
||||
<Typography sx={{ paddingBottom: "15px", fontWeight: "500" }}>Загрузите видео</Typography>
|
||||
<ButtonBase
|
||||
component="label"
|
||||
sx={{ justifyContent: "flex-start", width: "100%" }}
|
||||
>
|
||||
<input
|
||||
onChange={({ target }) => {
|
||||
if (target.files?.length) {
|
||||
onUpload(URL.createObjectURL(target.files[0] || " "));
|
||||
}
|
||||
}}
|
||||
hidden
|
||||
accept="video/*"
|
||||
multiple
|
||||
type="file"
|
||||
/>
|
||||
<Box
|
||||
onDragOver={(event: DragEvent<HTMLDivElement>) => event.preventDefault()}
|
||||
onDrop={handleDrop}
|
||||
sx={{
|
||||
width: "580px",
|
||||
padding: "33px 33px 33px 50px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
border: `1px solid ${theme.palette.grey2.main}`,
|
||||
borderRadius: "8px",
|
||||
gap: "50px",
|
||||
}}
|
||||
>
|
||||
<UploadIcon />
|
||||
<Box sx={{ color: "#9A9AAF" }}>
|
||||
<Typography sx={{ fontWeight: "500" }}>Добавить видео</Typography>
|
||||
<Typography sx={{ fontSize: "16px" }}>Принимает .mp4 и .mov формат — максимум 100мб</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import SelectableButton from "@ui_kit/SelectableButton";
|
||||
import UploadBox from "@ui_kit/UploadBox";
|
||||
import { memo, useState } from "react";
|
||||
import UploadIcon from "../../assets/icons/UploadIcon";
|
||||
import { UploadVideoModal } from "./UploadVideoModal";
|
||||
import UploadVideoModal from "./UploadVideoModal";
|
||||
|
||||
type BackgroundType = "text" | "video";
|
||||
|
||||
@ -15,11 +15,7 @@ type HelpQuestionsProps = {
|
||||
hintText: string;
|
||||
};
|
||||
|
||||
const HelpQuestions = memo<HelpQuestionsProps>(function ({
|
||||
questionId,
|
||||
hintVideo,
|
||||
hintText,
|
||||
}) {
|
||||
const HelpQuestions = memo<HelpQuestionsProps>(function ({ questionId, hintVideo, hintText }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [backgroundType, setBackgroundType] = useState<BackgroundType>("text");
|
||||
|
||||
@ -71,15 +67,17 @@ const HelpQuestions = memo<HelpQuestionsProps>(function ({
|
||||
</>
|
||||
) : (
|
||||
<Box>
|
||||
<Typography sx={{ paddingBottom: "15px", fontWeight: "500" }}>
|
||||
Загрузите видео
|
||||
</Typography>
|
||||
<Typography sx={{ paddingBottom: "15px", fontWeight: "500" }}>Загрузите видео</Typography>
|
||||
<ButtonBase
|
||||
onClick={() => setOpen(true)}
|
||||
sx={{ justifyContent: "flex-start" }}
|
||||
>
|
||||
{hintVideo ? (
|
||||
<video src={hintVideo} width="400" controls />
|
||||
<video
|
||||
src={hintVideo}
|
||||
width="400"
|
||||
controls
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<UploadBox
|
||||
|
@ -342,7 +342,7 @@ export const ResultCard = ({ resultContract, resultData }: Props) => {
|
||||
/>
|
||||
|
||||
<MediaSelectionAndDisplay
|
||||
resultData={resultData}
|
||||
question={resultData}
|
||||
cropAspectRatio={{ width: 305.9, height: 305.9 }}
|
||||
/>
|
||||
|
||||
|
@ -6,16 +6,7 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { SwitchSetting } from "../SwichResult";
|
||||
|
||||
import Info from "@icons/Info";
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
Paper,
|
||||
Button,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
Popover,
|
||||
} from "@mui/material";
|
||||
import { Box, IconButton, Paper, Button, Typography, useMediaQuery, useTheme, Popover } from "@mui/material";
|
||||
|
||||
import ExpandLessIconBG from "@icons/ExpandLessIconBG";
|
||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||
@ -88,10 +79,7 @@ const InfoView = () => {
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
Oтправка письма с результатом респонденту после отображения на
|
||||
экране
|
||||
</Typography>
|
||||
<Typography>Oтправка письма с результатом респонденту после отображения на экране</Typography>
|
||||
</Paper>
|
||||
</Popover>
|
||||
</>
|
||||
@ -190,7 +178,10 @@ export const WhenCard = ({ quizExpand }: Props) => {
|
||||
}}
|
||||
>
|
||||
{whenValues.map(({ title, value, id }, index) => (
|
||||
<Box display="flex">
|
||||
<Box
|
||||
display="flex"
|
||||
key={id}
|
||||
>
|
||||
<Button
|
||||
id={id}
|
||||
onClick={() => {
|
||||
@ -201,32 +192,16 @@ export const WhenCard = ({ quizExpand }: Props) => {
|
||||
}}
|
||||
key={title}
|
||||
sx={{
|
||||
bgcolor:
|
||||
quiz?.config.resultInfo.showResultForm === value
|
||||
? " #7E2AEA"
|
||||
: "#F2F3F7",
|
||||
color:
|
||||
quiz?.config.resultInfo.showResultForm === value
|
||||
? " white"
|
||||
: "#9A9AAF",
|
||||
minWidth: isSmallMonitor
|
||||
? isMobile
|
||||
? undefined
|
||||
: "310px"
|
||||
: "auto",
|
||||
bgcolor: quiz?.config.resultInfo.showResultForm === value ? " #7E2AEA" : "#F2F3F7",
|
||||
color: quiz?.config.resultInfo.showResultForm === value ? " white" : "#9A9AAF",
|
||||
minWidth: isSmallMonitor ? (isMobile ? undefined : "310px") : "auto",
|
||||
borderRadius: "8px",
|
||||
width: isMobile ? "100%" : "220px",
|
||||
height: "44px",
|
||||
fontSize: "17px",
|
||||
border:
|
||||
quiz?.config.resultInfo.showResultForm === value
|
||||
? "none"
|
||||
: "1px solid #9A9AAF",
|
||||
border: quiz?.config.resultInfo.showResultForm === value ? "none" : "1px solid #9A9AAF",
|
||||
"&:hover": {
|
||||
backgroundColor:
|
||||
quiz?.config.resultInfo.showResultForm === value
|
||||
? "#581CA7"
|
||||
: "#7E2AEA",
|
||||
backgroundColor: quiz?.config.resultInfo.showResultForm === value ? "#581CA7" : "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
@ -252,32 +227,16 @@ export const WhenCard = ({ quizExpand }: Props) => {
|
||||
});
|
||||
}}
|
||||
sx={{
|
||||
bgcolor:
|
||||
quiz?.config.resultInfo.when === "email"
|
||||
? " #7E2AEA"
|
||||
: "#F2F3F7",
|
||||
color:
|
||||
quiz?.config.resultInfo.when === "email"
|
||||
? " white"
|
||||
: "#9A9AAF",
|
||||
minWidth: isSmallMonitor
|
||||
? isMobile
|
||||
? undefined
|
||||
: "310px"
|
||||
: "auto",
|
||||
bgcolor: quiz?.config.resultInfo.when === "email" ? " #7E2AEA" : "#F2F3F7",
|
||||
color: quiz?.config.resultInfo.when === "email" ? " white" : "#9A9AAF",
|
||||
minWidth: isSmallMonitor ? (isMobile ? undefined : "310px") : "auto",
|
||||
borderRadius: "8px",
|
||||
width: isMobile ? "100%" : "220px",
|
||||
height: "44px",
|
||||
fontSize: "17px",
|
||||
border:
|
||||
quiz?.config.resultInfo.when === "email"
|
||||
? "none"
|
||||
: "1px solid #9A9AAF",
|
||||
border: quiz?.config.resultInfo.when === "email" ? "none" : "1px solid #9A9AAF",
|
||||
"&:hover": {
|
||||
backgroundColor:
|
||||
quiz?.config.resultInfo.when === "email"
|
||||
? "#581CA7"
|
||||
: "#7E2AEA",
|
||||
backgroundColor: quiz?.config.resultInfo.when === "email" ? "#581CA7" : "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
|
@ -1,10 +1,5 @@
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import {
|
||||
clearUserData,
|
||||
OriginalUserAccount,
|
||||
setUserAccount,
|
||||
useUserStore,
|
||||
} from "@root/user";
|
||||
import { clearUserData, OriginalUserAccount, setUserAccount, useUserStore } from "@root/user";
|
||||
import { clearAuthToken, getMessageFromFetchError } from "@frontend/kitui";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useUserAccountFetcher } from "@utils/hooks/useUserAccountFetcher";
|
||||
@ -54,22 +49,10 @@ export default function AvailablePrivilege() {
|
||||
if (created_at.length === 0) return 0;
|
||||
const currentDate = moment();
|
||||
|
||||
return Number(
|
||||
(
|
||||
moment(
|
||||
moment(created_at).add(amount, "days").diff(currentDate),
|
||||
).unix() / 86400
|
||||
).toFixed(1),
|
||||
);
|
||||
return Number((moment(moment(created_at).add(amount, "days").diff(currentDate)).unix() / 86400).toFixed(1));
|
||||
}
|
||||
const quizUnlimDays = getCramps(
|
||||
quizUnlimTime,
|
||||
userPrivileges?.quizUnlimTime?.created_at || "",
|
||||
);
|
||||
const squizBadgeDays = getCramps(
|
||||
squizHideBadge,
|
||||
userPrivileges?.squizHideBadge?.created_at || "",
|
||||
);
|
||||
const quizUnlimDays = getCramps(quizUnlimTime, userPrivileges?.quizUnlimTime?.created_at || "");
|
||||
const squizBadgeDays = getCramps(squizHideBadge, userPrivileges?.squizHideBadge?.created_at || "");
|
||||
|
||||
const currentDate = moment();
|
||||
|
||||
@ -85,10 +68,16 @@ export default function AvailablePrivilege() {
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Typography variant={"body1"} sx={{ color: "#9A9AAF" }}>
|
||||
<Typography
|
||||
variant={"body1"}
|
||||
sx={{ color: "#9A9AAF" }}
|
||||
>
|
||||
Вам доступно:
|
||||
</Typography>
|
||||
<Typography variant={"body1"} sx={{ color: "#4D4D4D" }}>
|
||||
<Typography
|
||||
variant={"body1"}
|
||||
sx={{ color: "#4D4D4D" }}
|
||||
>
|
||||
Безлимитные заявки:{" "}
|
||||
<strong>
|
||||
{quizUnlimDays > 0 && quizUnlimDays < 1
|
||||
@ -97,12 +86,18 @@ export default function AvailablePrivilege() {
|
||||
</strong>
|
||||
</Typography>
|
||||
{quizCnt !== 0 && (
|
||||
<Typography variant={"body1"} sx={{ color: "#4D4D4D" }}>
|
||||
<Typography
|
||||
variant={"body1"}
|
||||
sx={{ color: "#4D4D4D" }}
|
||||
>
|
||||
Заявки: <strong>{quizCnt} шт.</strong>
|
||||
</Typography>
|
||||
)}
|
||||
{squizHideBadge !== 0 && (
|
||||
<Typography variant={"body1"} sx={{ color: "#4D4D4D" }}>
|
||||
<Typography
|
||||
variant={"body1"}
|
||||
sx={{ color: "#4D4D4D" }}
|
||||
>
|
||||
Скрытие логотипа PenaQuiz:{" "}
|
||||
<strong>
|
||||
{squizBadgeDays > 0 && squizBadgeDays < 1
|
||||
|
@ -27,11 +27,7 @@ import {
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
incrementCurrentStep,
|
||||
updateQuiz,
|
||||
uploadQuizImage,
|
||||
} from "@root/quizes/actions";
|
||||
import { incrementCurrentStep, updateQuiz, uploadQuizImage } from "@root/quizes/actions";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||
import CustomTextField from "@ui_kit/CustomTextField";
|
||||
@ -47,24 +43,12 @@ import { DropZone } from "./dropZone";
|
||||
import Extra from "./extra";
|
||||
import TooltipClickInfo from "@ui_kit/Toolbars/TooltipClickInfo";
|
||||
import { VideoElement } from "./VideoElement";
|
||||
import * as React from "react";
|
||||
import UploadVideoModal from "../Questions/UploadVideoModal";
|
||||
|
||||
const designTypes = [
|
||||
[
|
||||
"standard",
|
||||
(color: string) => <LayoutStandartIcon color={color} />,
|
||||
"Standard",
|
||||
],
|
||||
[
|
||||
"expanded",
|
||||
(color: string) => <LayoutExpandedIcon color={color} />,
|
||||
"Expanded",
|
||||
],
|
||||
[
|
||||
"centered",
|
||||
(color: string) => <LayoutCenteredIcon color={color} />,
|
||||
"Centered",
|
||||
],
|
||||
["standard", (color: string) => <LayoutStandartIcon color={color} />, "Standard"],
|
||||
["expanded", (color: string) => <LayoutExpandedIcon color={color} />, "Expanded"],
|
||||
["centered", (color: string) => <LayoutCenteredIcon color={color} />, "Centered"],
|
||||
] as const;
|
||||
|
||||
export default function StartPageSettings() {
|
||||
@ -78,12 +62,28 @@ export default function StartPageSettings() {
|
||||
const [faviconUploding, setFaviconUploading] = useState<boolean>(false);
|
||||
const [backgroundUploding, setBackgroundUploading] = useState<boolean>(false);
|
||||
const [logoUploding, setLogoUploading] = useState<boolean>(false);
|
||||
const [isVideoUploadDialogOpen, setIsVideoUploadDialogOpen] = useState<boolean>(false);
|
||||
|
||||
if (!quiz) return null;
|
||||
|
||||
const MobileVersionHC = (bool: boolean) => {
|
||||
setMobileVersion(bool);
|
||||
};
|
||||
async function handleVideoUpload(videoUrl: string) {
|
||||
if (!quiz) return;
|
||||
|
||||
setBackgroundUploading(true);
|
||||
|
||||
if (videoUrl.startsWith("blob:")) {
|
||||
const videoBlob = await (await fetch(videoUrl)).blob();
|
||||
uploadQuizImage(quiz.id, videoBlob, (quiz, url) => {
|
||||
quiz.config.startpage.background.video = url;
|
||||
});
|
||||
} else {
|
||||
updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.startpage.background.video = videoUrl;
|
||||
});
|
||||
}
|
||||
|
||||
setBackgroundUploading(false);
|
||||
}
|
||||
|
||||
const designType = quiz?.config?.startpageType;
|
||||
let cropAspectRatio:
|
||||
@ -129,6 +129,12 @@ export default function StartPageSettings() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<UploadVideoModal
|
||||
open={isVideoUploadDialogOpen}
|
||||
onClose={() => setIsVideoUploadDialogOpen(false)}
|
||||
onUpload={handleVideoUpload}
|
||||
video={quiz.config.startpage.background.video}
|
||||
/>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{ marginTop: "60px", marginBottom: isSmallMonitor ? "0" : "40px" }}
|
||||
@ -143,25 +149,22 @@ export default function StartPageSettings() {
|
||||
fontWeight: 500,
|
||||
fontSize: "16px",
|
||||
color: formState === "design" ? "#7E2AEA" : "#7D7E86",
|
||||
borderBottom:
|
||||
formState === "design"
|
||||
? "2px solid #7E2AEA"
|
||||
: "1px solid transparent",
|
||||
borderBottom: formState === "design" ? "2px solid #7E2AEA" : "1px solid transparent",
|
||||
}}
|
||||
>
|
||||
Дизайн
|
||||
</Typography>
|
||||
</Button>
|
||||
<Button id="contentButton" onClick={() => setFormState("content")}>
|
||||
<Button
|
||||
id="contentButton"
|
||||
onClick={() => setFormState("content")}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
fontSize: "16px",
|
||||
color: formState === "content" ? "#7E2AEA" : "#7D7E86",
|
||||
borderBottom:
|
||||
formState === "content"
|
||||
? "2px solid #7E2AEA"
|
||||
: "1px solid transparent",
|
||||
borderBottom: formState === "content" ? "2px solid #7E2AEA" : "1px solid transparent",
|
||||
}}
|
||||
>
|
||||
Контент
|
||||
@ -222,8 +225,7 @@ export default function StartPageSettings() {
|
||||
displayEmpty
|
||||
onChange={(e) =>
|
||||
updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.startpageType = e.target
|
||||
.value as QuizStartpageType;
|
||||
quiz.config.startpageType = e.target.value as QuizStartpageType;
|
||||
})
|
||||
}
|
||||
sx={{
|
||||
@ -280,11 +282,7 @@ export default function StartPageSettings() {
|
||||
color: theme.palette.grey2.main,
|
||||
}}
|
||||
>
|
||||
{type[1](
|
||||
type[0] === designType
|
||||
? theme.palette.orange.main
|
||||
: theme.palette.grey2.main,
|
||||
)}
|
||||
{type[1](type[0] === designType ? theme.palette.orange.main : theme.palette.grey2.main)}
|
||||
{type[2]}
|
||||
</MenuItem>
|
||||
))}
|
||||
@ -331,10 +329,7 @@ export default function StartPageSettings() {
|
||||
{quiz.config.startpage.background.type === "image" && (
|
||||
<Box
|
||||
sx={{
|
||||
display:
|
||||
quiz.config.startpage.background.type === "image"
|
||||
? "flex"
|
||||
: "none",
|
||||
display: quiz.config.startpage.background.type === "image" ? "flex" : "none",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
@ -368,15 +363,12 @@ export default function StartPageSettings() {
|
||||
sx={{ maxWidth: "300px" }}
|
||||
cropAspectRatio={cropAspectRatio}
|
||||
imageUrl={quiz.config.startpage.background.desktop}
|
||||
originalImageUrl={
|
||||
quiz.config.startpage.background.originalDesktop
|
||||
}
|
||||
originalImageUrl={quiz.config.startpage.background.originalDesktop}
|
||||
onImageUploadClick={async (file) => {
|
||||
setBackgroundUploading(true);
|
||||
await uploadQuizImage(quiz.id, file, (quiz, url) => {
|
||||
quiz.config.startpage.background.desktop = url;
|
||||
quiz.config.startpage.background.originalDesktop =
|
||||
url;
|
||||
quiz.config.startpage.background.originalDesktop = url;
|
||||
});
|
||||
|
||||
setBackgroundUploading(false);
|
||||
@ -426,7 +418,10 @@ export default function StartPageSettings() {
|
||||
{isMobile ? (
|
||||
<TooltipClickInfo title={"Можно загрузить видео."} />
|
||||
) : (
|
||||
<Tooltip title="Можно загрузить видео." placement="top">
|
||||
<Tooltip
|
||||
title="Можно загрузить видео."
|
||||
placement="top"
|
||||
>
|
||||
<Box>
|
||||
<InfoIcon />
|
||||
</Box>
|
||||
@ -445,7 +440,7 @@ export default function StartPageSettings() {
|
||||
) : (
|
||||
<>
|
||||
<ButtonBase
|
||||
component="label"
|
||||
onClick={() => setIsVideoUploadDialogOpen(true)}
|
||||
sx={{
|
||||
justifyContent: "center",
|
||||
height: "48px",
|
||||
@ -455,29 +450,6 @@ export default function StartPageSettings() {
|
||||
my: "20px",
|
||||
}}
|
||||
>
|
||||
<input
|
||||
onChange={async (event) => {
|
||||
setBackgroundUploading(true);
|
||||
const file = event.target.files?.[0];
|
||||
|
||||
if (file) {
|
||||
await uploadQuizImage(
|
||||
quiz.id,
|
||||
file,
|
||||
(quiz, url) => {
|
||||
quiz.config.startpage.background.video =
|
||||
url;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setBackgroundUploading(false);
|
||||
}}
|
||||
hidden
|
||||
accept=".mp4"
|
||||
multiple
|
||||
type="file"
|
||||
/>
|
||||
<UploadBox
|
||||
icon={<UploadIcon />}
|
||||
sx={{
|
||||
@ -559,10 +531,7 @@ export default function StartPageSettings() {
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display:
|
||||
quiz.config.startpage.background.type === "image"
|
||||
? "flex"
|
||||
: "none",
|
||||
display: quiz.config.startpage.background.type === "image" ? "flex" : "none",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
@ -641,10 +610,7 @@ export default function StartPageSettings() {
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display:
|
||||
quiz.config.startpage.background.type === "image"
|
||||
? "flex"
|
||||
: "none",
|
||||
display: quiz.config.startpage.background.type === "image" ? "flex" : "none",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
@ -870,22 +836,24 @@ export default function StartPageSettings() {
|
||||
maxLength={1000}
|
||||
/>
|
||||
<Extra />
|
||||
<Box sx={{display: "flex", gap: "20px", alignItems: "center"}}>
|
||||
<CustomizedSwitch
|
||||
checked={quiz.config.antifraud}
|
||||
onChange={(e) => {
|
||||
updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.antifraud = e.target.checked;
|
||||
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<Typography sx={{fontWeight: 500,
|
||||
color: theme.palette.grey3.main,}}
|
||||
>
|
||||
Включить антифрод</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: "flex", gap: "20px", alignItems: "center" }}>
|
||||
<CustomizedSwitch
|
||||
checked={quiz.config.antifraud}
|
||||
onChange={(e) => {
|
||||
updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.antifraud = e.target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
Включить антифрод
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
@ -2,6 +2,7 @@ import Box from "@mui/material/Box";
|
||||
import { FC } from "react";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import { IconButton, SxProps, Theme } from "@mui/material";
|
||||
import { QuizVideo } from "@frontend/squzanswerer";
|
||||
|
||||
type VideoElementProps = {
|
||||
videoSrc: string;
|
||||
@ -20,12 +21,7 @@ export const VideoElement: FC<VideoElementProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<Box sx={{ position: "relative", width: `${width}px` }}>
|
||||
<video
|
||||
style={{ borderRadius: "8px" }}
|
||||
src={videoSrc}
|
||||
width={width}
|
||||
controls
|
||||
/>
|
||||
<QuizVideo videoUrl={videoSrc} />
|
||||
<IconButton
|
||||
onClick={onDeleteClick}
|
||||
sx={{
|
||||
|
@ -292,6 +292,6 @@ export const uploadQuizImage = async (quizId: string, blob: Blob, updateFn: (qui
|
||||
});
|
||||
};
|
||||
|
||||
function setProducedState<A extends string | { type: unknown }>(recipe: (state: QuizStore) => void, action?: A) {
|
||||
function setProducedState<A extends string | { type: string }>(recipe: (state: QuizStore) => void, action?: A) {
|
||||
useQuizStore.setState((state) => produce(state, recipe), false, action);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ export const setResults = (results: RawResult | []) =>
|
||||
{
|
||||
type: "setResults",
|
||||
results,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const removeResult = (resultId: number) =>
|
||||
@ -34,9 +34,7 @@ const removeResult = (resultId: number) =>
|
||||
|
||||
export const deleteResult = async (resultId: number) =>
|
||||
requestQueue.enqueue(`deleteResult-${resultId}`, async () => {
|
||||
const result = useResultStore
|
||||
.getState()
|
||||
.results.find((r) => r.id === resultId);
|
||||
const result = useResultStore.getState().results.find((r) => r.id === resultId);
|
||||
if (!result) return;
|
||||
|
||||
const [_, deleteError] = await resultApi.delete(Number(result.id));
|
||||
@ -51,57 +49,46 @@ export const deleteResult = async (resultId: number) =>
|
||||
removeResult(resultId);
|
||||
});
|
||||
|
||||
export const obsolescenceResult = async (
|
||||
resultId: number,
|
||||
editQuizId: number,
|
||||
) => {
|
||||
requestQueue.enqueue(
|
||||
`obsolescenceResult-${resultId}-${editQuizId}`,
|
||||
async () => {
|
||||
const result = useResultStore
|
||||
.getState()
|
||||
.results.find((r) => r.id === resultId);
|
||||
if (!result) return;
|
||||
if (result.new === false) return;
|
||||
let lossDebouncer: null | ReturnType<typeof setTimeout> = null;
|
||||
let lossId: number[] = [];
|
||||
if (!lossId.includes(resultId)) lossId.push(resultId);
|
||||
if (typeof lossDebouncer === "number") clearTimeout(lossDebouncer);
|
||||
lossDebouncer = setTimeout(async () => {
|
||||
//стреляем на лишение новизны
|
||||
const [_, obsolescenceError] = await resultApi.obsolescence(lossId);
|
||||
export const obsolescenceResult = async (resultId: number, editQuizId: number) => {
|
||||
requestQueue.enqueue(`obsolescenceResult-${resultId}-${editQuizId}`, async () => {
|
||||
const result = useResultStore.getState().results.find((r) => r.id === resultId);
|
||||
if (!result) return;
|
||||
if (result.new === false) return;
|
||||
let lossDebouncer: null | ReturnType<typeof setTimeout> = null;
|
||||
let lossId: number[] = [];
|
||||
if (!lossId.includes(resultId)) lossId.push(resultId);
|
||||
if (typeof lossDebouncer === "number") clearTimeout(lossDebouncer);
|
||||
lossDebouncer = setTimeout(async () => {
|
||||
//стреляем на лишение новизны
|
||||
const [_, obsolescenceError] = await resultApi.obsolescence(lossId);
|
||||
|
||||
if (obsolescenceError) {
|
||||
devlog("Error", obsolescenceError);
|
||||
if (obsolescenceError) {
|
||||
devlog("Error", obsolescenceError);
|
||||
|
||||
enqueueSnackbar(obsolescenceError);
|
||||
enqueueSnackbar(obsolescenceError);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lossId = [];
|
||||
}, 3000);
|
||||
const [resultList, resultError] = await resultApi.getList(editQuizId);
|
||||
|
||||
if (resultError || !resultList) {
|
||||
return;
|
||||
}
|
||||
|
||||
setResults(resultList);
|
||||
},
|
||||
);
|
||||
lossId = [];
|
||||
}, 3000);
|
||||
const [resultList, resultError] = await resultApi.getList(editQuizId);
|
||||
|
||||
if (resultError || !resultList) {
|
||||
return;
|
||||
}
|
||||
|
||||
setResults(resultList);
|
||||
});
|
||||
};
|
||||
|
||||
export const ExportResults = async (
|
||||
filterNew: string,
|
||||
filterDate: string,
|
||||
openPrePaymentModal: () => void,
|
||||
editQuizId: number,
|
||||
editQuizId: number
|
||||
) => {
|
||||
const [data, resultError] = await resultApi.export(
|
||||
editQuizId,
|
||||
parseFilters(filterNew, filterDate),
|
||||
);
|
||||
const [data, resultError] = await resultApi.export(editQuizId, parseFilters(filterNew, filterDate));
|
||||
|
||||
if (resultError) {
|
||||
if (resultError?.includes("Payment Required")) {
|
||||
@ -110,7 +97,6 @@ export const ExportResults = async (
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const blob = new Blob([data as BlobPart], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8",
|
||||
});
|
||||
@ -121,9 +107,6 @@ export const ExportResults = async (
|
||||
link.click();
|
||||
};
|
||||
|
||||
function setProducedState<A extends string | { type: string }>(
|
||||
recipe: (state: ResultStore) => void,
|
||||
action?: A,
|
||||
) {
|
||||
function setProducedState<A extends string | { type: string }>(recipe: (state: ResultStore) => void, action?: A) {
|
||||
useResultStore.setState((state) => produce(state, recipe), false, action);
|
||||
}
|
||||
|
@ -6,19 +6,13 @@ import type { SxProps, Theme } from "@mui/material";
|
||||
|
||||
interface Props {
|
||||
sx?: SxProps<Theme>;
|
||||
imageSrc?: string;
|
||||
imageSrc?: string | null;
|
||||
onImageClick?: () => void;
|
||||
onPlusClick?: () => void;
|
||||
uploading: boolean;
|
||||
}
|
||||
|
||||
export default function AddOrEditImageButton({
|
||||
onImageClick,
|
||||
onPlusClick,
|
||||
sx,
|
||||
imageSrc,
|
||||
uploading = false,
|
||||
}: Props) {
|
||||
export default function AddOrEditImageButton({ onImageClick, onPlusClick, sx, imageSrc, uploading = false }: Props) {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
|
||||
|
@ -6,12 +6,7 @@ import { mutate } from "swr";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
import { getDiscounts } from "@api/discounts";
|
||||
|
||||
import {
|
||||
clearUserData,
|
||||
OriginalUserAccount,
|
||||
setUserAccount,
|
||||
useUserStore,
|
||||
} from "@root/user";
|
||||
import { clearUserData, OriginalUserAccount, setUserAccount, useUserStore } from "@root/user";
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
import { useUserAccountFetcher } from "@utils/hooks/useUserAccountFetcher";
|
||||
import type { Discount } from "@model/discounts";
|
||||
@ -55,9 +50,7 @@ export function CheckFastlink() {
|
||||
return;
|
||||
}
|
||||
|
||||
enqueueSnackbar(
|
||||
greetings !== "" ? greetings : "Промокод успешно активирован",
|
||||
);
|
||||
enqueueSnackbar(greetings !== "" ? greetings : "Промокод успешно активирован");
|
||||
localStorage.setItem("fl", "");
|
||||
const [responseAccount, accountError] = await getAccount();
|
||||
|
||||
@ -100,12 +93,7 @@ export function CheckFastlink() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [
|
||||
user.userId,
|
||||
discounts,
|
||||
user.customerAccount?.createdAt,
|
||||
user.userAccount?.created_at,
|
||||
]);
|
||||
}, [user.userId, discounts, user.customerAccount?.createdAt, user.userAccount?.created_at]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -125,7 +113,11 @@ export function CheckFastlink() {
|
||||
borderRadius: 2,
|
||||
}}
|
||||
>
|
||||
<Typography textAlign="center" variant="h6" component="h2">
|
||||
<Typography
|
||||
textAlign="center"
|
||||
variant="h6"
|
||||
component="h2"
|
||||
>
|
||||
Заменить текущий промокод?
|
||||
</Typography>
|
||||
<Box
|
||||
|
@ -1,9 +1,13 @@
|
||||
import { FC, useState } from "react";
|
||||
import { QuizQuestionPage } from "@/model/questionTypes/page";
|
||||
import { QuizQuestionResult } from "@/model/questionTypes/result";
|
||||
import InfoIcon from "@icons/InfoIcon";
|
||||
import UploadIcon from "@icons/UploadIcon";
|
||||
import { Box, Button, ButtonBase, Skeleton, Tooltip, Typography, useTheme } from "@mui/material";
|
||||
import { updateQuestion, uploadQuestionImage } from "@root/questions/actions";
|
||||
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||
|
||||
import AddOrEditImageButton from "@ui_kit/AddOrEditImageButton";
|
||||
import { CropModal, useCropModalState } from "@ui_kit/Modal/CropModal";
|
||||
import UploadBox from "@ui_kit/UploadBox";
|
||||
import { FC, useState } from "react";
|
||||
import { UploadImageModal } from "../pages/Questions/UploadImage/UploadImageModal";
|
||||
import { useDisclosure } from "../utils/useDisclosure";
|
||||
import { useCurrentQuiz } from "../stores/quizes/hooks";
|
||||
@ -12,16 +16,19 @@ import UploadBox from "@ui_kit/UploadBox";
|
||||
import UploadIcon from "@icons/UploadIcon";
|
||||
import InfoIcon from "@icons/InfoIcon";
|
||||
import { VideoElement } from "../pages/startPage/VideoElement";
|
||||
import { useCurrentQuiz } from "../stores/quizes/hooks";
|
||||
import { useDisclosure } from "../utils/useDisclosure";
|
||||
import UploadVideoModal from "@/pages/Questions/UploadVideoModal";
|
||||
|
||||
interface Iprops {
|
||||
resultData: QuizQuestionPage | QuizQuestionResult;
|
||||
interface Props {
|
||||
question: QuizQuestionPage | QuizQuestionResult;
|
||||
cropAspectRatio: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRatio }) => {
|
||||
export const MediaSelectionAndDisplay: FC<Props> = ({ question, cropAspectRatio }) => {
|
||||
const [pictureUploding, setPictureUploading] = useState<boolean>(false);
|
||||
const [backgroundUploding, setBackgroundUploading] = useState<boolean>(false);
|
||||
const quizQid = useCurrentQuiz()?.qid;
|
||||
@ -29,10 +36,12 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRat
|
||||
const { isCropModalOpen, openCropModal, closeCropModal, imageBlob, originalImageUrl, setCropModalImageBlob } =
|
||||
useCropModalState();
|
||||
const [isImageUploadOpen, openImageUploadModal, closeImageUploadModal] = useDisclosure();
|
||||
const [isVideoUploadDialogOpen, setIsVideoUploadDialogOpen] = useState<boolean>(false);
|
||||
|
||||
async function handleImageUpload(file: File) {
|
||||
setPictureUploading(true);
|
||||
|
||||
const url = await uploadQuestionImage(resultData.id, quizQid, file, (question, url) => {
|
||||
const url = await uploadQuestionImage(question.id, quizQid, file, (question, url) => {
|
||||
question.content.back = url;
|
||||
question.content.originalBack = url;
|
||||
});
|
||||
@ -43,11 +52,32 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRat
|
||||
}
|
||||
|
||||
function handleCropModalSaveClick(imageBlob: Blob) {
|
||||
uploadQuestionImage(resultData.id, quizQid, imageBlob, (question, url) => {
|
||||
uploadQuestionImage(question.id, quizQid, imageBlob, (question, url) => {
|
||||
question.content.back = url;
|
||||
});
|
||||
}
|
||||
|
||||
async function handleVideoUpload(videoUrl: string) {
|
||||
setBackgroundUploading(true);
|
||||
|
||||
if (videoUrl.startsWith("blob:")) {
|
||||
const videoBlob = await (await fetch(videoUrl)).blob();
|
||||
uploadQuestionImage(question.id, quizQid, videoBlob, (question, url) => {
|
||||
if (!("video" in question.content)) return;
|
||||
|
||||
question.content.video = url;
|
||||
});
|
||||
} else {
|
||||
updateQuestion(question.id, (question) => {
|
||||
if (!("video" in question.content)) return;
|
||||
|
||||
question.content.video = videoUrl;
|
||||
});
|
||||
}
|
||||
|
||||
setBackgroundUploading(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -64,61 +94,70 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRat
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
color: resultData.content.useImage ? "#7E2AEA" : "#9A9AAF",
|
||||
color: question.content.useImage ? "#7E2AEA" : "#9A9AAF",
|
||||
fontSize: "16px",
|
||||
"&:hover": {
|
||||
background: "none",
|
||||
},
|
||||
}}
|
||||
variant="text"
|
||||
onClick={() => updateQuestion(resultData.id, (question) => (question.content.useImage = true))}
|
||||
onClick={() =>
|
||||
updateQuestion(question.id, (question) => {
|
||||
if (!("useImage" in question.content)) return;
|
||||
|
||||
question.content.useImage = true;
|
||||
})
|
||||
}
|
||||
>
|
||||
Изображение
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
color: resultData.content.useImage ? "#9A9AAF" : "#7E2AEA",
|
||||
color: question.content.useImage ? "#9A9AAF" : "#7E2AEA",
|
||||
fontSize: "16px",
|
||||
"&:hover": {
|
||||
background: "none",
|
||||
},
|
||||
}}
|
||||
variant="text"
|
||||
onClick={() => updateQuestion(resultData.id, (question) => (question.content.useImage = false))}
|
||||
onClick={() =>
|
||||
updateQuestion(question.id, (question) => {
|
||||
if (!("useImage" in question.content)) return;
|
||||
|
||||
question.content.useImage = false;
|
||||
})
|
||||
}
|
||||
>
|
||||
Видео
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
<UploadImageModal
|
||||
isOpen={isImageUploadOpen}
|
||||
onClose={closeImageUploadModal}
|
||||
handleImageChange={handleImageUpload}
|
||||
/>
|
||||
<CropModal
|
||||
isOpen={isCropModalOpen}
|
||||
imageBlob={imageBlob}
|
||||
originalImageUrl={originalImageUrl}
|
||||
setCropModalImageBlob={setCropModalImageBlob}
|
||||
onClose={closeCropModal}
|
||||
onSaveImageClick={handleCropModalSaveClick}
|
||||
onDeleteClick={() => {
|
||||
updateQuestion(question.id, (question) => {
|
||||
question.content.back = null;
|
||||
question.content.originalBack = null;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<UploadImageModal
|
||||
isOpen={isImageUploadOpen}
|
||||
onClose={closeImageUploadModal}
|
||||
handleImageChange={handleImageUpload}
|
||||
/>
|
||||
<CropModal
|
||||
isOpen={isCropModalOpen}
|
||||
imageBlob={imageBlob}
|
||||
originalImageUrl={originalImageUrl}
|
||||
setCropModalImageBlob={setCropModalImageBlob}
|
||||
onClose={closeCropModal}
|
||||
onSaveImageClick={handleCropModalSaveClick}
|
||||
onDeleteClick={() => {
|
||||
updateQuestion(resultData.id, (question) => {
|
||||
question.content.back = null;
|
||||
question.content.originalBack = null;
|
||||
});
|
||||
}}
|
||||
cropAspectRatio={cropAspectRatio}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{resultData.content.useImage && (
|
||||
cropAspectRatio={cropAspectRatio}
|
||||
/>
|
||||
<UploadVideoModal
|
||||
open={isVideoUploadDialogOpen}
|
||||
onClose={() => setIsVideoUploadDialogOpen(false)}
|
||||
onUpload={handleVideoUpload}
|
||||
video={question.content.video}
|
||||
/>
|
||||
{question.content.useImage && (
|
||||
<Box
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
@ -129,11 +168,11 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRat
|
||||
}}
|
||||
>
|
||||
<AddOrEditImageButton
|
||||
imageSrc={resultData.content.back}
|
||||
imageSrc={question.content.back}
|
||||
uploading={pictureUploding}
|
||||
onImageClick={() => {
|
||||
if (resultData.content.back) {
|
||||
return openCropModal(resultData.content.back, resultData.content.originalBack);
|
||||
if (question.content.back) {
|
||||
return openCropModal(question.content.back, question.content.originalBack);
|
||||
}
|
||||
|
||||
openImageUploadModal();
|
||||
@ -144,9 +183,9 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRat
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{!resultData.content.useImage && (
|
||||
{!question.content.useImage && (
|
||||
<>
|
||||
{!resultData.content.video ? (
|
||||
{!question.content.video ? (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
@ -179,7 +218,7 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRat
|
||||
) : (
|
||||
<>
|
||||
<ButtonBase
|
||||
component="label"
|
||||
onClick={() => setIsVideoUploadDialogOpen(true)}
|
||||
sx={{
|
||||
justifyContent: "center",
|
||||
height: "48px",
|
||||
@ -189,22 +228,6 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRat
|
||||
my: "20px",
|
||||
}}
|
||||
>
|
||||
<input
|
||||
onChange={async (event) => {
|
||||
setBackgroundUploading(true);
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
await uploadQuestionImage(resultData.id, quizQid, file, (question, url) => {
|
||||
question.content.video = url;
|
||||
});
|
||||
}
|
||||
setBackgroundUploading(false);
|
||||
}}
|
||||
hidden
|
||||
accept=".mp4"
|
||||
multiple
|
||||
type="file"
|
||||
/>
|
||||
<UploadBox
|
||||
icon={<UploadIcon />}
|
||||
sx={{
|
||||
@ -218,10 +241,12 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({ resultData, cropAspectRat
|
||||
</>
|
||||
) : (
|
||||
<VideoElement
|
||||
videoSrc={resultData.content.video}
|
||||
videoSrc={question.content.video}
|
||||
theme={theme}
|
||||
onDeleteClick={() => {
|
||||
updateQuestion(resultData.id, (question) => {
|
||||
updateQuestion(question.id, (question) => {
|
||||
if (!("video" in question.content)) return;
|
||||
|
||||
question.content.video = null;
|
||||
});
|
||||
}}
|
||||
|
@ -15,8 +15,7 @@ const translateMessage: Record<string, string> = {
|
||||
"field <email> is empty": 'Поле "E-mail" не заполнено',
|
||||
"field <phoneNumber> is empty": 'Поле "Номер телефона" не заполнено',
|
||||
"user with this email or login is exist": "Пользователь уже существует",
|
||||
"user with this login is exist":
|
||||
"Пользователь с таким логином уже существует",
|
||||
"user with this login is exist": "Пользователь с таким логином уже существует",
|
||||
"promocode already activated": "Промокод уже активирован",
|
||||
"promocode not found": "Промокод не найден",
|
||||
"promo code is expired": "Промокод истек",
|
||||
@ -25,7 +24,7 @@ const translateMessage: Record<string, string> = {
|
||||
|
||||
export const parseAxiosError = (nativeError: unknown): [string, number?] => {
|
||||
const error = nativeError as AxiosError;
|
||||
console.error(error)
|
||||
console.error(error);
|
||||
if (process.env.NODE_ENV !== "production") console.error(error);
|
||||
if (error.message === "Failed to fetch") return ["Ошибка сети"];
|
||||
|
||||
@ -40,10 +39,7 @@ console.error(error)
|
||||
if ("statusCode" in serverError) {
|
||||
SEMessage = serverError?.message.toLowerCase() || "";
|
||||
}
|
||||
if (
|
||||
"error" in serverError &&
|
||||
!("statusCode" in (error.response.data as ServerError))
|
||||
) {
|
||||
if ("error" in serverError && !("statusCode" in (error.response.data as ServerError))) {
|
||||
SEMessage = serverError.error.toLowerCase() || "";
|
||||
}
|
||||
const translatedMessage = translateMessage[SEMessage || ""]?.toLowerCase();
|
||||
|
Loading…
Reference in New Issue
Block a user