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