Merge branch '26' into dev
This commit is contained in:
commit
eb3c8b960a
332
src/api/integration.ts
Normal file
332
src/api/integration.ts
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
import { makeRequest } from "@api/makeRequest";
|
||||||
|
import { parseAxiosError } from "@utils/parse-error";
|
||||||
|
|
||||||
|
export type PaginationRequest = {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz/amocrm`;
|
||||||
|
|
||||||
|
// получение информации об аккаунте
|
||||||
|
|
||||||
|
export type AccountResponse = {
|
||||||
|
ID: number;
|
||||||
|
AccountID: string;
|
||||||
|
AmoID: number;
|
||||||
|
Name: string;
|
||||||
|
Email: string;
|
||||||
|
Role: string;
|
||||||
|
Group: number;
|
||||||
|
Deleted: boolean;
|
||||||
|
CreatedAt: number;
|
||||||
|
Subdomain: string;
|
||||||
|
Amoiserid: number;
|
||||||
|
Country: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAccount = async (): Promise<
|
||||||
|
[AccountResponse | null, string?]
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await makeRequest<void, AccountResponse>({
|
||||||
|
method: "GET",
|
||||||
|
url: `${API_URL}/account`,
|
||||||
|
useToken: true,
|
||||||
|
});
|
||||||
|
return [response];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Не удалось получить информацию об аккаунте. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// подключить Amo
|
||||||
|
|
||||||
|
export const connectAmo = async (): Promise<[string | null, string?]> => {
|
||||||
|
try {
|
||||||
|
const response = await makeRequest<void, { link: string }>({
|
||||||
|
method: "POST",
|
||||||
|
url: `${API_URL}/account`,
|
||||||
|
useToken: true,
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
return [response.link];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Не удалось подключить аккаунт. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// получение токена
|
||||||
|
|
||||||
|
export type TokenPair = {
|
||||||
|
accessToken: string;
|
||||||
|
refreshToken: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTokens = async (): Promise<[TokenPair | null, string?]> => {
|
||||||
|
try {
|
||||||
|
const response = await makeRequest<void, TokenPair>({
|
||||||
|
method: "GET",
|
||||||
|
url: `${API_URL}/webhook/create`,
|
||||||
|
useToken: true,
|
||||||
|
});
|
||||||
|
return [response];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Failed to get tokens. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//получение списка тегов
|
||||||
|
|
||||||
|
export type Tag = {
|
||||||
|
ID: number;
|
||||||
|
AmoID: number;
|
||||||
|
AccountID: number;
|
||||||
|
Entity: string;
|
||||||
|
Name: string;
|
||||||
|
Color: string;
|
||||||
|
Deleted: boolean;
|
||||||
|
CreatedAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TagsResponse = {
|
||||||
|
count: number;
|
||||||
|
items: Tag[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTags = async ({
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
}: PaginationRequest): Promise<[TagsResponse | null, string?]> => {
|
||||||
|
try {
|
||||||
|
const tagsResponse = await makeRequest<PaginationRequest, TagsResponse>({
|
||||||
|
method: "GET",
|
||||||
|
url: `${API_URL}/tags?page=${page}&size=${size}`,
|
||||||
|
});
|
||||||
|
return [tagsResponse];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Не удалось получить список тегов. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//получение списка пользователей
|
||||||
|
|
||||||
|
export type User = {
|
||||||
|
ID: number;
|
||||||
|
AccountID: string;
|
||||||
|
AmoID: number;
|
||||||
|
Name: string;
|
||||||
|
Email: string;
|
||||||
|
Role: string;
|
||||||
|
Group: number;
|
||||||
|
Deleted: boolean;
|
||||||
|
CreatedAt: number;
|
||||||
|
Subdomain: string;
|
||||||
|
Amoiserid: number;
|
||||||
|
Country: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UsersResponse = {
|
||||||
|
count: number;
|
||||||
|
items: User[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUsers = async ({
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
}: PaginationRequest): Promise<[UsersResponse | null, string?]> => {
|
||||||
|
try {
|
||||||
|
const usersResponse = await makeRequest<PaginationRequest, UsersResponse>({
|
||||||
|
method: "GET",
|
||||||
|
url: `${API_URL}/users?page=${page}&size=${size}`,
|
||||||
|
});
|
||||||
|
return [usersResponse];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Не удалось получить список пользователей. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//получение списка шагов
|
||||||
|
|
||||||
|
export type Step = {
|
||||||
|
ID: number;
|
||||||
|
AmoID: number;
|
||||||
|
PipelineID: number;
|
||||||
|
AccountID: number;
|
||||||
|
Name: string;
|
||||||
|
Color: string;
|
||||||
|
Deleted: boolean;
|
||||||
|
CreatedAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StepsResponse = {
|
||||||
|
count: number;
|
||||||
|
items: Step[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSteps = async ({
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
pipelineId,
|
||||||
|
}: PaginationRequest & { pipelineId: number }): Promise<
|
||||||
|
[StepsResponse | null, string?]
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const stepsResponse = await makeRequest<
|
||||||
|
PaginationRequest & { pipelineId: number },
|
||||||
|
StepsResponse
|
||||||
|
>({
|
||||||
|
method: "GET",
|
||||||
|
url: `${API_URL}/steps?page=${page}&size=${size}&pipelineID=${pipelineId}`,
|
||||||
|
});
|
||||||
|
return [stepsResponse];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Не удалось получить список шагов. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//получение списка воронок
|
||||||
|
|
||||||
|
export type Pipeline = {
|
||||||
|
ID: number;
|
||||||
|
AmoID: number;
|
||||||
|
AccountID: number;
|
||||||
|
Name: string;
|
||||||
|
IsArchive: boolean;
|
||||||
|
Deleted: boolean;
|
||||||
|
CreatedAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PipelinesResponse = {
|
||||||
|
count: number;
|
||||||
|
items: Pipeline[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPipelines = async ({
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
}: PaginationRequest): Promise<[PipelinesResponse | null, string?]> => {
|
||||||
|
try {
|
||||||
|
const pipelinesResponse = await makeRequest<
|
||||||
|
PaginationRequest,
|
||||||
|
PipelinesResponse
|
||||||
|
>({
|
||||||
|
method: "GET",
|
||||||
|
url: `${API_URL}/pipelines?page=${page}&size=${size}`,
|
||||||
|
});
|
||||||
|
return [pipelinesResponse];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Не удалось получить список воронок. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//получение настроек интеграции
|
||||||
|
|
||||||
|
export type IntegrationRules = {
|
||||||
|
ID: number;
|
||||||
|
AccountID: number;
|
||||||
|
QuizID: number;
|
||||||
|
PerformerID: number;
|
||||||
|
PipelineID: number;
|
||||||
|
StepID: number;
|
||||||
|
UTMs: number[];
|
||||||
|
FieldsRule: {
|
||||||
|
lead: { QuestionID: number }[];
|
||||||
|
contact: { ContactRuleMap: string }[];
|
||||||
|
company: { QuestionID: number }[];
|
||||||
|
customer: { QuestionID: number }[];
|
||||||
|
};
|
||||||
|
Deleted: boolean;
|
||||||
|
CreatedAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getIntegrationRules = async (
|
||||||
|
quizID: string,
|
||||||
|
): Promise<[IntegrationRules | null, string?]> => {
|
||||||
|
try {
|
||||||
|
const settingsResponse = await makeRequest<void, IntegrationRules>({
|
||||||
|
method: "GET",
|
||||||
|
url: `${API_URL}/rules/${quizID}`,
|
||||||
|
});
|
||||||
|
return [settingsResponse];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Не удалось получить настройки интеграции. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//обновление настроек интеграции
|
||||||
|
|
||||||
|
export type IntegrationRulesUpdate = {
|
||||||
|
PerformerID: number;
|
||||||
|
PipelineID: number;
|
||||||
|
StepID: number;
|
||||||
|
Utms: number[];
|
||||||
|
Fieldsrule: {
|
||||||
|
Lead: { QuestionID: number }[];
|
||||||
|
Contact: { ContactRuleMap: string }[];
|
||||||
|
Company: { QuestionID: number }[];
|
||||||
|
Customer: { QuestionID: number }[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateIntegrationRules = async (
|
||||||
|
quizID: string,
|
||||||
|
settings: IntegrationRulesUpdate,
|
||||||
|
): Promise<[string | null, string?]> => {
|
||||||
|
try {
|
||||||
|
const updateResponse = await makeRequest<IntegrationRulesUpdate, string>({
|
||||||
|
method: "PATCH",
|
||||||
|
url: `${API_URL}/rules/${quizID}`,
|
||||||
|
body: settings,
|
||||||
|
});
|
||||||
|
return [updateResponse];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Failed to update integration settings. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Получение кастомных полей
|
||||||
|
|
||||||
|
export type CustomField = {
|
||||||
|
ID: number;
|
||||||
|
AmoID: number;
|
||||||
|
Code: string;
|
||||||
|
AccountID: number;
|
||||||
|
Name: string;
|
||||||
|
EntityType: string;
|
||||||
|
Type: string;
|
||||||
|
Deleted: boolean;
|
||||||
|
CreatedAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CustomFieldsResponse = {
|
||||||
|
count: number;
|
||||||
|
items: CustomField[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCustomFields = async (
|
||||||
|
pagination: PaginationRequest,
|
||||||
|
): Promise<[CustomFieldsResponse | null, string?]> => {
|
||||||
|
try {
|
||||||
|
const fieldsResponse = await makeRequest<
|
||||||
|
PaginationRequest,
|
||||||
|
CustomFieldsResponse
|
||||||
|
>({
|
||||||
|
method: "GET",
|
||||||
|
url: `${API_URL}/fields?page=${pagination.page}&size=${pagination.size}`,
|
||||||
|
});
|
||||||
|
return [fieldsResponse];
|
||||||
|
} catch (nativeError) {
|
||||||
|
const [error] = parseAxiosError(nativeError);
|
||||||
|
return [null, `Не удалось получить список кастомных полей. ${error}`];
|
||||||
|
}
|
||||||
|
};
|
||||||
BIN
src/assets/icons/Amologo.png
Normal file
BIN
src/assets/icons/Amologo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
51
src/components/AmoButton/AmoButton.tsx
Normal file
51
src/components/AmoButton/AmoButton.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { Button, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
|
import AmoLogo from "../../assets/icons/Amologo.png";
|
||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
type AmoButtonProps = {
|
||||||
|
onClick?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AmoButton: FC<AmoButtonProps> = ({ onClick }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#329dc9",
|
||||||
|
width: isMobile ? "270px" : "310px",
|
||||||
|
height: "47px",
|
||||||
|
display: "inline-flex",
|
||||||
|
justifyContent: "start",
|
||||||
|
border: "#316a88 1px solid",
|
||||||
|
color: "white",
|
||||||
|
textDecoration: "none",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={AmoLogo}
|
||||||
|
style={{
|
||||||
|
height: "100%",
|
||||||
|
maxWidth: "339px",
|
||||||
|
objectFit: "scale-down",
|
||||||
|
userSelect: "none",
|
||||||
|
pointerEvents: "none",
|
||||||
|
}}
|
||||||
|
alt={"AmoCRM"}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
margin: "auto",
|
||||||
|
letterSpacing: "1px",
|
||||||
|
fontSize: "14px",
|
||||||
|
fontWeight: 600,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Подключить
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,33 +1,248 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { FC } from "react";
|
import { FC, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import Radio from "@mui/material/Radio";
|
import Radio from "@mui/material/Radio";
|
||||||
import RadioGroup from "@mui/material/RadioGroup";
|
import RadioGroup from "@mui/material/RadioGroup";
|
||||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import CheckboxIcon from "@icons/Checkbox";
|
import CheckboxIcon from "@icons/Checkbox";
|
||||||
import { useTheme } from "@mui/material";
|
import { Typography, useTheme } from "@mui/material";
|
||||||
|
import {
|
||||||
|
getPipelines,
|
||||||
|
getSteps,
|
||||||
|
getTags,
|
||||||
|
PaginationRequest,
|
||||||
|
Pipeline,
|
||||||
|
Step,
|
||||||
|
Tag,
|
||||||
|
} from "@api/integration";
|
||||||
|
|
||||||
type CustomRadioGroupProps = {
|
type CustomRadioGroupProps = {
|
||||||
items: string[];
|
type?: string;
|
||||||
selectedValue: string | null;
|
selectedValue: string | null;
|
||||||
setSelectedValue: (value: string | null) => void;
|
setSelectedValue: (value: string | null) => void;
|
||||||
|
pipelineId?: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SIZE = 25;
|
||||||
|
|
||||||
export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
||||||
items,
|
type,
|
||||||
selectedValue,
|
selectedValue,
|
||||||
setSelectedValue,
|
setSelectedValue,
|
||||||
|
pipelineId,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [currentValue, setCurrentValue] = React.useState<string | null>(
|
const [currentValue, setCurrentValue] = useState<string | null>(
|
||||||
selectedValue,
|
selectedValue,
|
||||||
);
|
);
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [tags, setTags] = useState<Tag[]>([]);
|
||||||
|
const [steps, setSteps] = useState<Step[]>([]);
|
||||||
|
const [pipelines, setPipelines] = useState<Pipeline[]>([]);
|
||||||
|
const [hasMoreItems, setHasMoreItems] = useState(true);
|
||||||
|
const boxRef = useRef(null);
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSelectedValue((event.target as HTMLInputElement).value);
|
setSelectedValue((event.target as HTMLInputElement).value);
|
||||||
setCurrentValue((event.target as HTMLInputElement).value);
|
setCurrentValue((event.target as HTMLInputElement).value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
||||||
|
const scrollHeight = e.currentTarget.scrollHeight;
|
||||||
|
const scrollTop = e.currentTarget.scrollTop;
|
||||||
|
const clientHeight = e.currentTarget.clientHeight;
|
||||||
|
const scrolledToBottom = scrollTop / (scrollHeight - clientHeight) > 0.9;
|
||||||
|
|
||||||
|
if (scrolledToBottom && !isLoading && hasMoreItems) {
|
||||||
|
setPage((prevPage) => prevPage + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type === "typeTags" && hasMoreItems) {
|
||||||
|
setIsLoading(true);
|
||||||
|
const pagination: PaginationRequest = {
|
||||||
|
page: page,
|
||||||
|
size: SIZE,
|
||||||
|
};
|
||||||
|
getTags(pagination).then(([response]) => {
|
||||||
|
if (response && response.items !== null) {
|
||||||
|
setTags((prevItems) => [...prevItems, ...response.items]);
|
||||||
|
if (response.items.length < SIZE) {
|
||||||
|
setHasMoreItems(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (type === "typeSteps" && hasMoreItems && pipelineId) {
|
||||||
|
setIsLoading(true);
|
||||||
|
const pagination: PaginationRequest & { pipelineId: number } = {
|
||||||
|
page: page,
|
||||||
|
size: SIZE,
|
||||||
|
pipelineId: pipelineId,
|
||||||
|
};
|
||||||
|
getSteps(pagination).then(([response]) => {
|
||||||
|
if (response && response.items !== null) {
|
||||||
|
setSteps((prevItems) => [...prevItems, ...response.items]);
|
||||||
|
if (response.items.length < SIZE) {
|
||||||
|
setHasMoreItems(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (type === "typePipelines" && hasMoreItems) {
|
||||||
|
setIsLoading(true);
|
||||||
|
const pagination: PaginationRequest = {
|
||||||
|
page: page,
|
||||||
|
size: SIZE,
|
||||||
|
};
|
||||||
|
getPipelines(pagination).then(([response]) => {
|
||||||
|
if (response && response.items !== null) {
|
||||||
|
setPipelines((prevItems) => [...prevItems, ...response.items]);
|
||||||
|
if (response.items.length < SIZE) {
|
||||||
|
setHasMoreItems(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [page, type, hasMoreItems, pipelineId]);
|
||||||
|
|
||||||
|
const formControlLabels = useMemo(() => {
|
||||||
|
if (type === "typeTags" && tags && tags.length !== 0) {
|
||||||
|
return tags.map((item) => (
|
||||||
|
<FormControlLabel
|
||||||
|
key={item.ID}
|
||||||
|
sx={{
|
||||||
|
color: "black",
|
||||||
|
padding: "15px",
|
||||||
|
borderBottom: `1px solid ${theme.palette.background.default}`,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
borderRadius: "12px",
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor:
|
||||||
|
currentValue === item.Name
|
||||||
|
? theme.palette.background.default
|
||||||
|
: theme.palette.common.white,
|
||||||
|
}}
|
||||||
|
value={item.Name}
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
checkedIcon={
|
||||||
|
<CheckboxIcon
|
||||||
|
checked
|
||||||
|
isRounded
|
||||||
|
color={theme.palette.brightPurple.main}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
icon={<CheckboxIcon isRounded />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
||||||
|
<Typography sx={{ color: `${item.Color}` }}>
|
||||||
|
{item.Name}
|
||||||
|
</Typography>
|
||||||
|
<Typography>{item.Entity}</Typography>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
labelPlacement={"start"}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (type === "typeSteps" && steps && steps.length !== 0) {
|
||||||
|
return steps.map((step) => (
|
||||||
|
<FormControlLabel
|
||||||
|
key={step.ID}
|
||||||
|
sx={{
|
||||||
|
color: "black",
|
||||||
|
padding: "15px",
|
||||||
|
borderBottom: `1px solid ${theme.palette.background.default}`,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
borderRadius: "12px",
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor:
|
||||||
|
currentValue === step.Name
|
||||||
|
? theme.palette.background.default
|
||||||
|
: theme.palette.common.white,
|
||||||
|
}}
|
||||||
|
value={step.Name}
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
checkedIcon={
|
||||||
|
<CheckboxIcon
|
||||||
|
checked
|
||||||
|
isRounded
|
||||||
|
color={theme.palette.brightPurple.main}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
icon={<CheckboxIcon isRounded />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={step.Name}
|
||||||
|
labelPlacement={"start"}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (type === "typePipelines" && pipelines && pipelines.length !== 0) {
|
||||||
|
return pipelines.map((pipeline) => (
|
||||||
|
<FormControlLabel
|
||||||
|
key={pipeline.ID}
|
||||||
|
sx={{
|
||||||
|
color: "black",
|
||||||
|
padding: "15px",
|
||||||
|
borderBottom: `1px solid ${theme.palette.background.default}`,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
borderRadius: "12px",
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor:
|
||||||
|
currentValue === pipeline.Name
|
||||||
|
? theme.palette.background.default
|
||||||
|
: theme.palette.common.white,
|
||||||
|
}}
|
||||||
|
value={pipeline.Name}
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
checkedIcon={
|
||||||
|
<CheckboxIcon
|
||||||
|
checked
|
||||||
|
isRounded
|
||||||
|
color={theme.palette.brightPurple.main}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
icon={<CheckboxIcon isRounded />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={pipeline.Name}
|
||||||
|
labelPlacement={"start"}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100%",
|
||||||
|
padding: "15px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography>Нет элементов</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}, [tags, steps, currentValue, type, pipelines]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
ref={boxRef}
|
||||||
|
onScroll={handleScroll}
|
||||||
sx={{
|
sx={{
|
||||||
border: `1px solid ${theme.palette.grey2.main}`,
|
border: `1px solid ${theme.palette.grey2.main}`,
|
||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
@ -42,39 +257,7 @@ export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
|||||||
value={currentValue}
|
value={currentValue}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
>
|
>
|
||||||
{items.map((item) => (
|
{formControlLabels}
|
||||||
<FormControlLabel
|
|
||||||
key={item}
|
|
||||||
sx={{
|
|
||||||
color: "black",
|
|
||||||
padding: "15px",
|
|
||||||
borderBottom: `1px solid ${theme.palette.background.default}`,
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
borderRadius: "12px",
|
|
||||||
margin: 0,
|
|
||||||
backgroundColor:
|
|
||||||
currentValue === item
|
|
||||||
? theme.palette.background.default
|
|
||||||
: theme.palette.common.white,
|
|
||||||
}}
|
|
||||||
value={item}
|
|
||||||
control={
|
|
||||||
<Radio
|
|
||||||
checkedIcon={
|
|
||||||
<CheckboxIcon
|
|
||||||
checked
|
|
||||||
isRounded
|
|
||||||
color={theme.palette.brightPurple.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
icon={<CheckboxIcon isRounded />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={item}
|
|
||||||
labelPlacement={"start"}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,40 +8,151 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import { FC, useCallback, useRef, useState } from "react";
|
import * as React from "react";
|
||||||
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import "./CustomSelect.css";
|
import "./CustomSelect.css";
|
||||||
import arrow_down from "../../assets/icons/arrow_down.svg";
|
import arrow_down from "../../assets/icons/arrow_down.svg";
|
||||||
|
import { getUsers, PaginationRequest, User } from "@api/integration";
|
||||||
|
|
||||||
type CustomSelectProps = {
|
type CustomSelectProps = {
|
||||||
items: string[];
|
|
||||||
selectedItem: string | null;
|
selectedItem: string | null;
|
||||||
setSelectedItem: (value: string | null) => void;
|
setSelectedItem: (value: string | null) => void;
|
||||||
|
type?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CustomSelect: FC<CustomSelectProps> = ({
|
export const CustomSelect: FC<CustomSelectProps> = ({
|
||||||
items,
|
|
||||||
selectedItem,
|
selectedItem,
|
||||||
setSelectedItem,
|
setSelectedItem,
|
||||||
|
type,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
const [opened, setOpened] = useState<boolean>(false);
|
const [opened, setOpened] = useState<boolean>(false);
|
||||||
const [currentValue, setCurrentValue] = useState<string | null>(selectedItem);
|
const [currentValue, setCurrentValue] = useState<string | null>(selectedItem);
|
||||||
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
const [hasMoreItems, setHasMoreItems] = useState(true);
|
||||||
|
const SIZE = 25;
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
|
const selectWidth = ref.current ? ref.current.offsetWidth : undefined;
|
||||||
|
|
||||||
|
const [savedValue, setSavedValue] = useState<number | null>(null);
|
||||||
|
|
||||||
const onSelectItem = useCallback(
|
const onSelectItem = useCallback(
|
||||||
(event: SelectChangeEvent<HTMLDivElement>) => {
|
(event: SelectChangeEvent<HTMLDivElement>) => {
|
||||||
const newValue = event.target.value.toString();
|
const newValue = event.target.value.toString();
|
||||||
|
const selectedUser = users.find((user) => user.Name === newValue);
|
||||||
|
if (selectedUser) {
|
||||||
|
//для сохранения ID выбранного пользователя в стейт или конфиг...
|
||||||
|
setSavedValue(selectedUser.ID);
|
||||||
|
}
|
||||||
setCurrentValue(newValue);
|
setCurrentValue(newValue);
|
||||||
setSelectedItem(newValue);
|
setSelectedItem(newValue);
|
||||||
},
|
},
|
||||||
[setSelectedItem, setCurrentValue],
|
[setSelectedItem, setCurrentValue, setSavedValue, users],
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleOpened = useCallback(() => {
|
const toggleOpened = useCallback(() => {
|
||||||
setOpened((isOpened) => !isOpened);
|
setOpened((isOpened) => !isOpened);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
||||||
|
const scrollHeight = e.currentTarget.scrollHeight;
|
||||||
|
const scrollTop = e.currentTarget.scrollTop;
|
||||||
|
const clientHeight = e.currentTarget.clientHeight;
|
||||||
|
const scrolledToBottom = scrollTop / (scrollHeight - clientHeight) > 0.9;
|
||||||
|
|
||||||
|
if (scrolledToBottom && !isLoading && hasMoreItems) {
|
||||||
|
setPage((prevPage) => prevPage + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type === "typeUsers" && hasMoreItems) {
|
||||||
|
setIsLoading(true);
|
||||||
|
const pagination: PaginationRequest = {
|
||||||
|
page: page,
|
||||||
|
size: SIZE,
|
||||||
|
};
|
||||||
|
getUsers(pagination).then(([response]) => {
|
||||||
|
if (response && response.items !== null) {
|
||||||
|
setUsers((prevItems) => [...prevItems, ...response.items]);
|
||||||
|
if (response.items.length < SIZE) {
|
||||||
|
setHasMoreItems(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [page, type, hasMoreItems]);
|
||||||
|
|
||||||
|
const menuItems = useMemo(() => {
|
||||||
|
if (type === "typeUsers" && users && users.length !== 0) {
|
||||||
|
return users.map((user) => (
|
||||||
|
<MenuItem
|
||||||
|
key={user.ID}
|
||||||
|
value={user.Name}
|
||||||
|
sx={{
|
||||||
|
padding: "6px 0",
|
||||||
|
zIndex: 2,
|
||||||
|
borderTop: "1px solid rgba(154, 154, 175, 0.1)",
|
||||||
|
width: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
color: "#4D4D4D",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: isMobile ? "column" : "row",
|
||||||
|
justifyContent: "space-evenly",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
width: "33%",
|
||||||
|
borderRight: isMobile
|
||||||
|
? "none"
|
||||||
|
: "1px solid rgba(154, 154, 175, 0.1)",
|
||||||
|
padding: isMobile ? "5px 0 5px 20px" : "10px 0 10px 20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{user.Name}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
width: "33%",
|
||||||
|
borderRight: isMobile
|
||||||
|
? "none"
|
||||||
|
: "1px solid rgba(154, 154, 175, 0.1)",
|
||||||
|
padding: isMobile ? "5px 0 5px 20px" : "10px 0 10px 20px",
|
||||||
|
color: isMobile ? "#9A9AAF" : "#4D4D4D",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{user.Email}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
width: "33%",
|
||||||
|
padding: isMobile ? "5px 0 5px 20px" : "10px 0 10px 20px",
|
||||||
|
color: isMobile ? "#9A9AAF" : "#4D4D4D",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{user.Role}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<MenuItem key={"-1"} disabled sx={{ padding: "12px", zIndex: 2 }}>
|
||||||
|
нет данных
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}, [users, type]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box
|
<Box
|
||||||
@ -100,29 +211,21 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
|||||||
MenuProps={{
|
MenuProps={{
|
||||||
disablePortal: true,
|
disablePortal: true,
|
||||||
PaperProps: {
|
PaperProps: {
|
||||||
|
onScroll: handleScroll,
|
||||||
style: {
|
style: {
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
maxHeight: "300px",
|
maxHeight: "300px",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
|
overflowX: "auto",
|
||||||
|
maxWidth: selectWidth,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
sx={{ width: "100%" }}
|
sx={{}}
|
||||||
onChange={onSelectItem}
|
onChange={onSelectItem}
|
||||||
onClick={toggleOpened}
|
onClick={toggleOpened}
|
||||||
>
|
>
|
||||||
{items.map((item) => {
|
{menuItems}
|
||||||
const uniqueKey = `${item}-${Date.now()}`;
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
key={uniqueKey}
|
|
||||||
value={item}
|
|
||||||
sx={{ padding: "12px", zIndex: 2 }}
|
|
||||||
>
|
|
||||||
{item}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
</Select>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -114,6 +114,8 @@ export interface QuizConfig {
|
|||||||
law?: string;
|
law?: string;
|
||||||
};
|
};
|
||||||
meta: string;
|
meta: string;
|
||||||
|
antifraud: boolean;
|
||||||
|
showfc: boolean;
|
||||||
yandexMetricsNumber: number | undefined;
|
yandexMetricsNumber: number | undefined;
|
||||||
vkMetricsNumber: number | undefined;
|
vkMetricsNumber: number | undefined;
|
||||||
}
|
}
|
||||||
@ -228,6 +230,8 @@ export const defaultQuizConfig: QuizConfig = {
|
|||||||
button: "",
|
button: "",
|
||||||
},
|
},
|
||||||
meta: "",
|
meta: "",
|
||||||
|
antifraud: true,
|
||||||
|
showfc: true,
|
||||||
yandexMetricsNumber: undefined,
|
yandexMetricsNumber: undefined,
|
||||||
vkMetricsNumber: undefined,
|
vkMetricsNumber: undefined,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import {
|
|||||||
FieldSettingsDrawerState,
|
FieldSettingsDrawerState,
|
||||||
FormContactFieldName,
|
FormContactFieldName,
|
||||||
} from "@model/quizSettings";
|
} from "@model/quizSettings";
|
||||||
|
import CustomizedSwitch from "@ui_kit/CustomSwitch";
|
||||||
|
|
||||||
const buttons: { key: FormContactFieldName; name: string; desc: string }[] = [
|
const buttons: { key: FormContactFieldName; name: string; desc: string }[] = [
|
||||||
{ name: "Имя", desc: "Дмитрий", key: "name" },
|
{ name: "Имя", desc: "Дмитрий", key: "name" },
|
||||||
@ -63,6 +64,21 @@ export default function ContactFormPage() {
|
|||||||
mt: "67px",
|
mt: "67px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Box sx={{display: "flex", gap: "20px", alignItems: "center"}}>
|
||||||
|
<CustomizedSwitch
|
||||||
|
checked={quiz.config.showfc}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.showfc = e.target.checked;
|
||||||
|
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography sx={{fontWeight: 500,
|
||||||
|
color: theme.palette.grey3.main,}}
|
||||||
|
>
|
||||||
|
Показывать форму контактов</Typography>
|
||||||
|
</Box>
|
||||||
{!quiz?.config.formContact.fields.name.used &&
|
{!quiz?.config.formContact.fields.name.used &&
|
||||||
!quiz?.config.formContact.fields.email.used &&
|
!quiz?.config.formContact.fields.email.used &&
|
||||||
!quiz?.config.formContact.fields.phone.used &&
|
!quiz?.config.formContact.fields.phone.used &&
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
|
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||||
|
import { FC } from "react";
|
||||||
|
import { AccountResponse } from "@api/integration";
|
||||||
|
|
||||||
|
type AmoAccountInfoProps = {
|
||||||
|
handleNextStep: () => void;
|
||||||
|
accountInfo: AccountResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AmoAccountInfo: FC<AmoAccountInfoProps> = ({
|
||||||
|
handleNextStep,
|
||||||
|
accountInfo,
|
||||||
|
}) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
|
|
||||||
|
const infoItem = (title: string, value: string | number) => (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: isMobile ? "column" : "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ width: isMobile ? "100%" : "45%" }}>
|
||||||
|
<Typography sx={{ color: theme.palette.grey2.main }}>
|
||||||
|
{title}:
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ width: isMobile ? "100%" : "45%" }}>
|
||||||
|
<Typography>{value}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100%",
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
marginTop: isMobile ? "20px" : "60px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: isMobile ? "10px" : "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{infoItem("Amo ID", accountInfo.AmoID)}
|
||||||
|
{infoItem("Имя аккаунта", accountInfo.Name)}
|
||||||
|
{infoItem("Email аккаунта", accountInfo.Email)}
|
||||||
|
{infoItem("Роль", accountInfo.Role)}
|
||||||
|
{infoItem("Группа пользователя", accountInfo.Group)}
|
||||||
|
{infoItem("URL профиля пользователя в Amo", accountInfo.Subdomain)}
|
||||||
|
{infoItem("Страна пользователя", accountInfo.Country)}
|
||||||
|
</Box>
|
||||||
|
<StepButtonsBlock
|
||||||
|
isSmallBtnDisabled={true}
|
||||||
|
onLargeBtnClick={handleNextStep}
|
||||||
|
largeBtnText={"Далее"}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -5,20 +5,19 @@ import {
|
|||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React, { FC, useMemo, useState } from "react";
|
import React, { FC, useEffect, useMemo, useState } from "react";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { IntegrationStep1 } from "./IntegrationStep1/IntegrationStep1";
|
import { AmoLogin } from "./AmoLogin/AmoLogin";
|
||||||
import { IntegrationStep2 } from "./IntegrationStep2/IntegrationStep2";
|
import { AmoStep2 } from "./AmoStep2/AmoStep2";
|
||||||
import { IntegrationStep3 } from "./IntegrationStep3/IntegrationStep3";
|
import { AmoStep3 } from "./AmoStep3/AmoStep3";
|
||||||
import { IntegrationStep4 } from "./IntegrationStep4/IntegrationStep4";
|
import { AmoStep4 } from "./AmoStep4/AmoStep4";
|
||||||
import { IntegrationStep5 } from "./IntegrationStep5/IntegrationStep5";
|
import { AmoStep6 } from "./IntegrationStep6/AmoStep6";
|
||||||
import { IntegrationStep6 } from "./IntegrationStep6/IntegrationStep6";
|
import { AmoModalTitle } from "./AmoModalTitle/AmoModalTitle";
|
||||||
import { funnelsMock, performersMock, stagesMock } from "../mocks/MockData";
|
import { AmoSettingsBlock } from "./SettingsBlock/AmoSettingsBlock";
|
||||||
import File from "@ui_kit/QuizPreview/QuizPreviewQuestionTypes/File";
|
import { AmoStep7 } from "./IntegrationStep7/AmoStep7";
|
||||||
import { IntegrationsModalTitle } from "./IntegrationsModalTitle/IntegrationsModalTitle";
|
import { AmoAccountInfo } from "./AmoAccountInfo/AmoAccountInfo";
|
||||||
import { SettingsBlock } from "./SettingsBlock/SettingsBlock";
|
import { AccountResponse, getAccount } from "@api/integration";
|
||||||
import { IntegrationStep7 } from "./IntegrationStep7/IntegrationStep7";
|
|
||||||
|
|
||||||
export type TitleKeys = "contacts" | "company" | "deal" | "users" | "buyers";
|
export type TitleKeys = "contacts" | "company" | "deal" | "users" | "buyers";
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ type IntegrationsModalProps = {
|
|||||||
export type TagKeys = "contact" | "company" | "deal" | "buyer";
|
export type TagKeys = "contact" | "company" | "deal" | "buyer";
|
||||||
export type TTags = Record<TagKeys, string[] | []>;
|
export type TTags = Record<TagKeys, string[] | []>;
|
||||||
|
|
||||||
export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||||
isModalOpen,
|
isModalOpen,
|
||||||
handleCloseModal,
|
handleCloseModal,
|
||||||
companyName,
|
companyName,
|
||||||
@ -43,18 +42,19 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
|
|
||||||
const [step, setStep] = useState<number>(0);
|
const [step, setStep] = useState<number>(0);
|
||||||
const [isSettingsBlock, setIsSettingsBlock] = useState<boolean>(false);
|
const [isSettingsBlock, setIsSettingsBlock] = useState<boolean>(false);
|
||||||
const [selectedFunnelPerformer, setSelectedFunnelPerformer] = useState<
|
|
||||||
|
const [accountInfo, setAccountInfo] = useState<AccountResponse | null>(null);
|
||||||
|
const [selectedPipelinePerformer, setSelectedPipelinePerformer] = useState<
|
||||||
string | null
|
string | null
|
||||||
>(null);
|
>(null);
|
||||||
const [selectedFunnel, setSelectedFunnel] = useState<string | null>(null);
|
const [selectedPipeline, setSelectedPipeline] = useState<string | null>(null);
|
||||||
const [selectedStagePerformer, setSelectedStagePerformer] = useState<
|
const [selectedStepsPerformer, setSelectedStepsPerformer] = useState<
|
||||||
string | null
|
string | null
|
||||||
>(null);
|
>(null);
|
||||||
const [selectedStage, setSelectedStage] = useState<string | null>(null);
|
const [selectedStep, setSelectedStep] = useState<string | null>(null);
|
||||||
const [selectedDealPerformer, setSelectedDealPerformer] = useState<
|
const [selectedDealPerformer, setSelectedDealPerformer] = useState<
|
||||||
string | null
|
string | null
|
||||||
>(null);
|
>(null);
|
||||||
const [utmFile, setUtmFile] = useState<File | null>(null);
|
|
||||||
const [questionEntity, setQuestionEntity] = useState<TQuestionEntity>({
|
const [questionEntity, setQuestionEntity] = useState<TQuestionEntity>({
|
||||||
contacts: [],
|
contacts: [],
|
||||||
company: [],
|
company: [],
|
||||||
@ -69,6 +69,20 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
buyer: [],
|
buyer: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isModalOpen) {
|
||||||
|
const fetchAccount = async () => {
|
||||||
|
const [account, error] = await getAccount();
|
||||||
|
if (account && !error) {
|
||||||
|
setAccountInfo(account);
|
||||||
|
} else {
|
||||||
|
setAccountInfo(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchAccount();
|
||||||
|
}
|
||||||
|
}, [isModalOpen]);
|
||||||
|
|
||||||
const handleNextStep = () => {
|
const handleNextStep = () => {
|
||||||
setStep((prevState) => prevState + 1);
|
setStep((prevState) => prevState + 1);
|
||||||
};
|
};
|
||||||
@ -79,27 +93,33 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
handleCloseModal();
|
handleCloseModal();
|
||||||
setStep(1);
|
setStep(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const steps = useMemo(
|
const steps = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
title: "Авторизация в аккаунте",
|
title: accountInfo
|
||||||
|
? "Информация об аккаунте"
|
||||||
|
: "Авторизация в аккаунте",
|
||||||
isSettingsAvailable: false,
|
isSettingsAvailable: false,
|
||||||
component: <IntegrationStep1 handleNextStep={handleNextStep} />,
|
component: accountInfo ? (
|
||||||
|
<AmoAccountInfo
|
||||||
|
handleNextStep={handleNextStep}
|
||||||
|
accountInfo={accountInfo}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<AmoLogin handleNextStep={handleNextStep} />
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Выбор воронки",
|
title: "Выбор воронки",
|
||||||
isSettingsAvailable: true,
|
isSettingsAvailable: true,
|
||||||
component: (
|
component: (
|
||||||
<IntegrationStep2
|
<AmoStep2
|
||||||
handlePrevStep={handlePrevStep}
|
handlePrevStep={handlePrevStep}
|
||||||
handleNextStep={handleNextStep}
|
handleNextStep={handleNextStep}
|
||||||
selectedFunnelPerformer={selectedFunnelPerformer}
|
selectedPipelinePerformer={selectedPipelinePerformer}
|
||||||
setSelectedFunnelPerformer={setSelectedFunnelPerformer}
|
setSelectedPipelinePerformer={setSelectedPipelinePerformer}
|
||||||
selectedFunnel={selectedFunnel}
|
selectedPipeline={selectedPipeline}
|
||||||
setSelectedFunnel={setSelectedFunnel}
|
setSelectedPipeline={setSelectedPipeline}
|
||||||
performers={performersMock}
|
|
||||||
funnels={funnelsMock}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -107,15 +127,14 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
title: "Выбор этапа воронки",
|
title: "Выбор этапа воронки",
|
||||||
isSettingsAvailable: true,
|
isSettingsAvailable: true,
|
||||||
component: (
|
component: (
|
||||||
<IntegrationStep3
|
<AmoStep3
|
||||||
handlePrevStep={handlePrevStep}
|
handlePrevStep={handlePrevStep}
|
||||||
handleNextStep={handleNextStep}
|
handleNextStep={handleNextStep}
|
||||||
selectedStagePerformer={selectedStagePerformer}
|
selectedStepsPerformer={selectedStepsPerformer}
|
||||||
setSelectedStagePerformer={setSelectedStagePerformer}
|
setSelectedStepsPerformer={setSelectedStepsPerformer}
|
||||||
selectedStage={selectedStage}
|
selectedStep={selectedStep}
|
||||||
setSelectedStage={setSelectedStage}
|
setSelectedStep={setSelectedStep}
|
||||||
performers={performersMock}
|
pipelineId={selectedPipeline}
|
||||||
stages={stagesMock}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -123,24 +142,11 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
title: "Сделка",
|
title: "Сделка",
|
||||||
isSettingsAvailable: true,
|
isSettingsAvailable: true,
|
||||||
component: (
|
component: (
|
||||||
<IntegrationStep4
|
<AmoStep4
|
||||||
handlePrevStep={handlePrevStep}
|
handlePrevStep={handlePrevStep}
|
||||||
handleNextStep={handleNextStep}
|
handleNextStep={handleNextStep}
|
||||||
selectedDealPerformer={selectedDealPerformer}
|
selectedDealPerformer={selectedDealPerformer}
|
||||||
setSelectedDealPerformer={setSelectedDealPerformer}
|
setSelectedDealPerformer={setSelectedDealPerformer}
|
||||||
performers={performersMock}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Добавление utm-меток",
|
|
||||||
isSettingsAvailable: false,
|
|
||||||
component: (
|
|
||||||
<IntegrationStep5
|
|
||||||
handlePrevStep={handlePrevStep}
|
|
||||||
handleNextStep={handleNextStep}
|
|
||||||
utmFile={utmFile}
|
|
||||||
setUtmFile={setUtmFile}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -148,7 +154,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
title: "Соотнесение вопросов и сущностей",
|
title: "Соотнесение вопросов и сущностей",
|
||||||
isSettingsAvailable: true,
|
isSettingsAvailable: true,
|
||||||
component: (
|
component: (
|
||||||
<IntegrationStep6
|
<AmoStep6
|
||||||
questionEntity={questionEntity}
|
questionEntity={questionEntity}
|
||||||
setQuestionEntity={setQuestionEntity}
|
setQuestionEntity={setQuestionEntity}
|
||||||
handlePrevStep={handlePrevStep}
|
handlePrevStep={handlePrevStep}
|
||||||
@ -160,7 +166,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
title: "Добавление тегов",
|
title: "Добавление тегов",
|
||||||
isSettingsAvailable: true,
|
isSettingsAvailable: true,
|
||||||
component: (
|
component: (
|
||||||
<IntegrationStep7
|
<AmoStep7
|
||||||
handleSmallBtn={handlePrevStep}
|
handleSmallBtn={handlePrevStep}
|
||||||
handleLargeBtn={handleSave}
|
handleLargeBtn={handleSave}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
@ -171,11 +177,10 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
questionEntity,
|
questionEntity,
|
||||||
utmFile,
|
selectedPipelinePerformer,
|
||||||
selectedFunnelPerformer,
|
selectedPipeline,
|
||||||
selectedFunnel,
|
selectedStepsPerformer,
|
||||||
selectedStagePerformer,
|
selectedStep,
|
||||||
selectedStage,
|
|
||||||
selectedDealPerformer,
|
selectedDealPerformer,
|
||||||
tags,
|
tags,
|
||||||
],
|
],
|
||||||
@ -188,7 +193,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
open={isModalOpen}
|
open={isModalOpen}
|
||||||
onClose={handleCloseModal}
|
onClose={handleCloseModal}
|
||||||
fullWidth
|
fullWidth
|
||||||
fullScreen={isMobile}
|
// fullScreen={isMobile}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
sx: {
|
sx: {
|
||||||
maxWidth: isTablet ? "100%" : "920px",
|
maxWidth: isTablet ? "100%" : "920px",
|
||||||
@ -209,6 +214,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
fontSize: isMobile ? "20px" : "24px",
|
fontSize: isMobile ? "20px" : "24px",
|
||||||
fontWeight: "500",
|
fontWeight: "500",
|
||||||
padding: "20px",
|
padding: "20px",
|
||||||
|
color: theme.palette.grey2.main,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Интеграция с {companyName ? companyName : "партнером"}
|
Интеграция с {companyName ? companyName : "партнером"}
|
||||||
@ -238,7 +244,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IntegrationsModalTitle
|
<AmoModalTitle
|
||||||
step={step}
|
step={step}
|
||||||
steps={steps}
|
steps={steps}
|
||||||
isSettingsBlock={isSettingsBlock}
|
isSettingsBlock={isSettingsBlock}
|
||||||
@ -247,16 +253,15 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
|||||||
/>
|
/>
|
||||||
{isSettingsBlock ? (
|
{isSettingsBlock ? (
|
||||||
<Box sx={{ flexGrow: 1, width: "100%" }}>
|
<Box sx={{ flexGrow: 1, width: "100%" }}>
|
||||||
<SettingsBlock
|
<AmoSettingsBlock
|
||||||
stepTitles={stepTitles}
|
stepTitles={stepTitles}
|
||||||
setIsSettingsBlock={setIsSettingsBlock}
|
setIsSettingsBlock={setIsSettingsBlock}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
selectedDealPerformer={selectedDealPerformer}
|
selectedDealPerformer={selectedDealPerformer}
|
||||||
selectedFunnelPerformer={selectedFunnelPerformer}
|
selectedFunnelPerformer={selectedPipelinePerformer}
|
||||||
selectedFunnel={selectedFunnel}
|
selectedFunnel={selectedPipeline}
|
||||||
selectedStagePerformer={selectedStagePerformer}
|
selectedStagePerformer={selectedStepsPerformer}
|
||||||
selectedStage={selectedStage}
|
selectedStage={selectedStep}
|
||||||
utmFile={utmFile}
|
|
||||||
questionEntity={questionEntity}
|
questionEntity={questionEntity}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
/>
|
/>
|
||||||
@ -0,0 +1,150 @@
|
|||||||
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
|
import { FC } from "react";
|
||||||
|
import { AmoButton } from "../../../../components/AmoButton/AmoButton";
|
||||||
|
import { connectAmo } from "@api/integration";
|
||||||
|
|
||||||
|
type IntegrationStep1Props = {
|
||||||
|
handleNextStep: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
// interface Values {
|
||||||
|
// login: string;
|
||||||
|
// password: string;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const initialValues: Values = {
|
||||||
|
// login: "",
|
||||||
|
// password: "",
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const validationSchema = object({
|
||||||
|
// login: string().required("Поле обязательно"),
|
||||||
|
// password: string().required("Поле обязательно").min(8, "Минимум 8 символов"),
|
||||||
|
// });
|
||||||
|
|
||||||
|
export const AmoLogin: FC<IntegrationStep1Props> = ({ handleNextStep }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
|
|
||||||
|
const onAmoClick = async () => {
|
||||||
|
console.log("Amo click");
|
||||||
|
const [url, error] = await connectAmo();
|
||||||
|
if (url && !error) {
|
||||||
|
window.open(url, "_blank");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// const formik = useFormik<Values>({
|
||||||
|
// initialValues,
|
||||||
|
// validationSchema,
|
||||||
|
// onSubmit: async (values, formikHelpers) => {
|
||||||
|
// const loginTrimmed = values.login.trim();
|
||||||
|
// const passwordTrimmed = values.password.trim();
|
||||||
|
// try {
|
||||||
|
// // Simulate a network request
|
||||||
|
// await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
// handleNextStep();
|
||||||
|
// } catch (error) {
|
||||||
|
// formikHelpers.setSubmitting(false);
|
||||||
|
// if (error instanceof Error) {
|
||||||
|
// formikHelpers.setErrors({
|
||||||
|
// login: error.message,
|
||||||
|
// password: error.message,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
// component="form"
|
||||||
|
// onSubmit={formik.handleSubmit}
|
||||||
|
// noValidate
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100%",
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/*<Box*/}
|
||||||
|
{/* sx={{*/}
|
||||||
|
{/* marginTop: "68px",*/}
|
||||||
|
{/* width: isMobile ? "100%" : "500px",*/}
|
||||||
|
{/* display: "flex",*/}
|
||||||
|
{/* flexDirection: "column",*/}
|
||||||
|
{/* alignItems: "center",*/}
|
||||||
|
{/* gap: "15px",*/}
|
||||||
|
{/* }}*/}
|
||||||
|
{/*>*/}
|
||||||
|
{/* <InputTextfield*/}
|
||||||
|
{/* TextfieldProps={{*/}
|
||||||
|
{/* value: formik.values.login,*/}
|
||||||
|
{/* placeholder: "+7 900 000 00 00 или username@penahaub.com",*/}
|
||||||
|
{/* onBlur: formik.handleBlur,*/}
|
||||||
|
{/* error: formik.touched.login && Boolean(formik.errors.login),*/}
|
||||||
|
{/* helperText: formik.touched.login && formik.errors.login,*/}
|
||||||
|
{/* "data-cy": "login",*/}
|
||||||
|
{/* }}*/}
|
||||||
|
{/* onChange={formik.handleChange}*/}
|
||||||
|
{/* color={theme.palette.background.default}*/}
|
||||||
|
{/* id="login"*/}
|
||||||
|
{/* label="Телефон или E-mail"*/}
|
||||||
|
{/* gap="10px"*/}
|
||||||
|
{/* />*/}
|
||||||
|
{/* <PasswordInput*/}
|
||||||
|
{/* TextfieldProps={{*/}
|
||||||
|
{/* value: formik.values.password,*/}
|
||||||
|
{/* placeholder: "Не менее 8 символов",*/}
|
||||||
|
{/* onBlur: formik.handleBlur,*/}
|
||||||
|
{/* error: formik.touched.password && Boolean(formik.errors.password),*/}
|
||||||
|
{/* helperText: formik.touched.password && formik.errors.password,*/}
|
||||||
|
{/* type: "password",*/}
|
||||||
|
{/* "data-cy": "password",*/}
|
||||||
|
{/* }}*/}
|
||||||
|
{/* onChange={formik.handleChange}*/}
|
||||||
|
{/* color={theme.palette.background.default}*/}
|
||||||
|
{/* id="password"*/}
|
||||||
|
{/* label="Пароль"*/}
|
||||||
|
{/* gap="10px"*/}
|
||||||
|
{/* />*/}
|
||||||
|
{/*</Box>*/}
|
||||||
|
<Box sx={{ marginTop: "70px", width: isMobile ? "100%" : "500px" }}>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
fontWeight: "400",
|
||||||
|
color: theme.palette.grey2.main,
|
||||||
|
lineHeight: "1",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Инструкция
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
marginTop: "12px",
|
||||||
|
fontSize: "18px",
|
||||||
|
fontWeight: "400",
|
||||||
|
color: theme.palette.grey3.main,
|
||||||
|
lineHeight: "1",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
После нажатия на кнопку - "Подключить", вас переадресует на страницу
|
||||||
|
подключения интеграции в ваш аккаунт AmoCRM. Пожалуйста, согласитесь
|
||||||
|
на всё, что мы предлагаем, иначе чуда не случится.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ marginTop: "50px" }}>
|
||||||
|
<AmoButton onClick={onAmoClick} />
|
||||||
|
</Box>
|
||||||
|
{/*<StepButtonsBlock*/}
|
||||||
|
{/* isSmallBtnDisabled={true}*/}
|
||||||
|
{/* largeBtnType={"submit"}*/}
|
||||||
|
{/* // isLargeBtnDisabled={formik.isSubmitting}*/}
|
||||||
|
{/* largeBtnText={"Войти"}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -4,7 +4,7 @@ import GearIcon from "@icons/GearIcon";
|
|||||||
import React, { FC, useCallback, useMemo } from "react";
|
import React, { FC, useCallback, useMemo } from "react";
|
||||||
import AccountSetting from "@icons/AccountSetting";
|
import AccountSetting from "@icons/AccountSetting";
|
||||||
|
|
||||||
type IntegrationsModalTitleProps = {
|
type AmoModalTitleProps = {
|
||||||
step: number;
|
step: number;
|
||||||
steps: { title: string; isSettingsAvailable: boolean }[];
|
steps: { title: string; isSettingsAvailable: boolean }[];
|
||||||
isSettingsBlock?: boolean;
|
isSettingsBlock?: boolean;
|
||||||
@ -12,7 +12,7 @@ type IntegrationsModalTitleProps = {
|
|||||||
setStep: (value: number) => void;
|
setStep: (value: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntegrationsModalTitle: FC<IntegrationsModalTitleProps> = ({
|
export const AmoModalTitle: FC<AmoModalTitleProps> = ({
|
||||||
step,
|
step,
|
||||||
steps,
|
steps,
|
||||||
setIsSettingsBlock,
|
setIsSettingsBlock,
|
||||||
@ -4,31 +4,26 @@ import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
|||||||
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
||||||
import { CustomRadioGroup } from "../../../../components/CustomRadioGroup/CustomRadioGroup";
|
import { CustomRadioGroup } from "../../../../components/CustomRadioGroup/CustomRadioGroup";
|
||||||
|
|
||||||
type IntegrationStep3Props = {
|
type AmoStep2Props = {
|
||||||
handlePrevStep: () => void;
|
handlePrevStep: () => void;
|
||||||
handleNextStep: () => void;
|
handleNextStep: () => void;
|
||||||
selectedStagePerformer: string | null;
|
selectedPipelinePerformer: string | null;
|
||||||
setSelectedStagePerformer: (value: string | null) => void;
|
setSelectedPipelinePerformer: (value: string | null) => void;
|
||||||
selectedStage: string | null;
|
selectedPipeline: string | null;
|
||||||
setSelectedStage: (value: string | null) => void;
|
setSelectedPipeline: (value: string | null) => void;
|
||||||
performers: string[];
|
|
||||||
stages: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntegrationStep3: FC<IntegrationStep3Props> = ({
|
export const AmoStep2: FC<AmoStep2Props> = ({
|
||||||
handlePrevStep,
|
handlePrevStep,
|
||||||
handleNextStep,
|
handleNextStep,
|
||||||
selectedStagePerformer,
|
selectedPipelinePerformer,
|
||||||
setSelectedStagePerformer,
|
setSelectedPipelinePerformer,
|
||||||
selectedStage,
|
selectedPipeline,
|
||||||
setSelectedStage,
|
setSelectedPipeline,
|
||||||
performers,
|
|
||||||
stages,
|
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -41,9 +36,9 @@ export const IntegrationStep3: FC<IntegrationStep3Props> = ({
|
|||||||
>
|
>
|
||||||
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
selectedItem={selectedStagePerformer}
|
selectedItem={selectedPipelinePerformer}
|
||||||
items={performers}
|
setSelectedItem={setSelectedPipelinePerformer}
|
||||||
setSelectedItem={setSelectedStagePerformer}
|
type={"typeUsers"}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -55,9 +50,9 @@ export const IntegrationStep3: FC<IntegrationStep3Props> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomRadioGroup
|
<CustomRadioGroup
|
||||||
items={stages}
|
selectedValue={selectedPipeline}
|
||||||
selectedValue={selectedStage}
|
setSelectedValue={setSelectedPipeline}
|
||||||
setSelectedValue={setSelectedStage}
|
type={"typePipelines"}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -4,30 +4,29 @@ import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
|||||||
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
||||||
import { CustomRadioGroup } from "../../../../components/CustomRadioGroup/CustomRadioGroup";
|
import { CustomRadioGroup } from "../../../../components/CustomRadioGroup/CustomRadioGroup";
|
||||||
|
|
||||||
type IntegrationStep2Props = {
|
type AmoStep3Props = {
|
||||||
handlePrevStep: () => void;
|
handlePrevStep: () => void;
|
||||||
handleNextStep: () => void;
|
handleNextStep: () => void;
|
||||||
selectedFunnelPerformer: string | null;
|
selectedStepsPerformer: string | null;
|
||||||
setSelectedFunnelPerformer: (value: string | null) => void;
|
setSelectedStepsPerformer: (value: string | null) => void;
|
||||||
selectedFunnel: string | null;
|
selectedStep: string | null;
|
||||||
setSelectedFunnel: (value: string | null) => void;
|
setSelectedStep: (value: string | null) => void;
|
||||||
performers: string[];
|
pipelineId: string | null;
|
||||||
funnels: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntegrationStep2: FC<IntegrationStep2Props> = ({
|
export const AmoStep3: FC<AmoStep3Props> = ({
|
||||||
handlePrevStep,
|
handlePrevStep,
|
||||||
handleNextStep,
|
handleNextStep,
|
||||||
selectedFunnelPerformer,
|
selectedStepsPerformer,
|
||||||
setSelectedFunnelPerformer,
|
setSelectedStepsPerformer,
|
||||||
selectedFunnel,
|
selectedStep,
|
||||||
setSelectedFunnel,
|
setSelectedStep,
|
||||||
performers,
|
pipelineId,
|
||||||
funnels,
|
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -40,9 +39,9 @@ export const IntegrationStep2: FC<IntegrationStep2Props> = ({
|
|||||||
>
|
>
|
||||||
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
selectedItem={selectedFunnelPerformer}
|
selectedItem={selectedStepsPerformer}
|
||||||
items={performers}
|
type={"typeUsers"}
|
||||||
setSelectedItem={setSelectedFunnelPerformer}
|
setSelectedItem={setSelectedStepsPerformer}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -54,9 +53,11 @@ export const IntegrationStep2: FC<IntegrationStep2Props> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomRadioGroup
|
<CustomRadioGroup
|
||||||
items={funnels}
|
// @ts-ignore
|
||||||
selectedValue={selectedFunnel}
|
pipelineId={pipelineId}
|
||||||
setSelectedValue={setSelectedFunnel}
|
type={"typeSteps"}
|
||||||
|
selectedValue={selectedStep}
|
||||||
|
setSelectedValue={setSelectedStep}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -3,20 +3,18 @@ import { FC } from "react";
|
|||||||
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||||
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
||||||
|
|
||||||
type IntegrationStep4Props = {
|
type AmoStep4Props = {
|
||||||
handlePrevStep: () => void;
|
handlePrevStep: () => void;
|
||||||
handleNextStep: () => void;
|
handleNextStep: () => void;
|
||||||
selectedDealPerformer: string | null;
|
selectedDealPerformer: string | null;
|
||||||
setSelectedDealPerformer: (value: string | null) => void;
|
setSelectedDealPerformer: (value: string | null) => void;
|
||||||
performers: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntegrationStep4: FC<IntegrationStep4Props> = ({
|
export const AmoStep4: FC<AmoStep4Props> = ({
|
||||||
handlePrevStep,
|
handlePrevStep,
|
||||||
handleNextStep,
|
handleNextStep,
|
||||||
selectedDealPerformer,
|
selectedDealPerformer,
|
||||||
setSelectedDealPerformer,
|
setSelectedDealPerformer,
|
||||||
performers,
|
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
@ -35,8 +33,8 @@ export const IntegrationStep4: FC<IntegrationStep4Props> = ({
|
|||||||
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
selectedItem={selectedDealPerformer}
|
selectedItem={selectedDealPerformer}
|
||||||
items={performers}
|
|
||||||
setSelectedItem={setSelectedDealPerformer}
|
setSelectedItem={setSelectedDealPerformer}
|
||||||
|
type={"typeUsers"}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -1,144 +0,0 @@
|
|||||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
|
||||||
import { FC } from "react";
|
|
||||||
import { object, string } from "yup";
|
|
||||||
import InputTextfield from "@ui_kit/InputTextfield";
|
|
||||||
import PasswordInput from "@ui_kit/passwordInput";
|
|
||||||
import { useFormik } from "formik";
|
|
||||||
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
|
||||||
|
|
||||||
type IntegrationStep1Props = {
|
|
||||||
handleNextStep: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Values {
|
|
||||||
login: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialValues: Values = {
|
|
||||||
login: "",
|
|
||||||
password: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
const validationSchema = object({
|
|
||||||
login: string().required("Поле обязательно"),
|
|
||||||
password: string().required("Поле обязательно").min(8, "Минимум 8 символов"),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const IntegrationStep1: FC<IntegrationStep1Props> = ({
|
|
||||||
handleNextStep,
|
|
||||||
}) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
|
||||||
|
|
||||||
const formik = useFormik<Values>({
|
|
||||||
initialValues,
|
|
||||||
validationSchema,
|
|
||||||
onSubmit: async (values, formikHelpers) => {
|
|
||||||
const loginTrimmed = values.login.trim();
|
|
||||||
const passwordTrimmed = values.password.trim();
|
|
||||||
try {
|
|
||||||
// Simulate a network request
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
||||||
handleNextStep();
|
|
||||||
} catch (error) {
|
|
||||||
formikHelpers.setSubmitting(false);
|
|
||||||
if (error instanceof Error) {
|
|
||||||
formikHelpers.setErrors({
|
|
||||||
login: error.message,
|
|
||||||
password: error.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
component="form"
|
|
||||||
onSubmit={formik.handleSubmit}
|
|
||||||
noValidate
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: isMobile ? "start" : "center",
|
|
||||||
height: "100%",
|
|
||||||
flexGrow: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
marginTop: "68px",
|
|
||||||
width: isMobile ? "100%" : "500px",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "15px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<InputTextfield
|
|
||||||
TextfieldProps={{
|
|
||||||
value: formik.values.login,
|
|
||||||
placeholder: "+7 900 000 00 00 или username@penahaub.com",
|
|
||||||
onBlur: formik.handleBlur,
|
|
||||||
error: formik.touched.login && Boolean(formik.errors.login),
|
|
||||||
helperText: formik.touched.login && formik.errors.login,
|
|
||||||
"data-cy": "login",
|
|
||||||
}}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
color={theme.palette.background.default}
|
|
||||||
id="login"
|
|
||||||
label="Телефон или E-mail"
|
|
||||||
gap="10px"
|
|
||||||
/>
|
|
||||||
<PasswordInput
|
|
||||||
TextfieldProps={{
|
|
||||||
value: formik.values.password,
|
|
||||||
placeholder: "Не менее 8 символов",
|
|
||||||
onBlur: formik.handleBlur,
|
|
||||||
error: formik.touched.password && Boolean(formik.errors.password),
|
|
||||||
helperText: formik.touched.password && formik.errors.password,
|
|
||||||
type: "password",
|
|
||||||
"data-cy": "password",
|
|
||||||
}}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
color={theme.palette.background.default}
|
|
||||||
id="password"
|
|
||||||
label="Пароль"
|
|
||||||
gap="10px"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ marginTop: "30px", width: isMobile ? "100%" : "500px" }}>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
fontWeight: "400",
|
|
||||||
color: theme.palette.grey2.main,
|
|
||||||
lineHeight: "1",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Инструкция
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
marginTop: "12px",
|
|
||||||
fontSize: "18px",
|
|
||||||
fontWeight: "400",
|
|
||||||
color: theme.palette.grey3.main,
|
|
||||||
lineHeight: "1",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Повседневная практика показывает, что постоянный количественный рост и
|
|
||||||
сфера нашей активности способствует подготовки и реализации систем
|
|
||||||
массового участия
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<StepButtonsBlock
|
|
||||||
isSmallBtnDisabled={true}
|
|
||||||
largeBtnType={"submit"}
|
|
||||||
isLargeBtnDisabled={formik.isSubmitting}
|
|
||||||
largeBtnText={"Войти"}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
import {
|
|
||||||
Box,
|
|
||||||
ButtonBase,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import UploadIcon from "@icons/UploadIcon";
|
|
||||||
import { type DragEvent, FC, useRef, useState } from "react";
|
|
||||||
|
|
||||||
type TextFormat = "txt" | "docx";
|
|
||||||
|
|
||||||
interface CustomFileUploaderProps {
|
|
||||||
description?: string;
|
|
||||||
accept?: TextFormat[];
|
|
||||||
handleImageChange: (file: File) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CustomFileUploader: FC<CustomFileUploaderProps> = ({
|
|
||||||
accept,
|
|
||||||
description,
|
|
||||||
handleImageChange,
|
|
||||||
}) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const dropZone = useRef<HTMLDivElement>(null);
|
|
||||||
const [ready, setReady] = useState(false);
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(700));
|
|
||||||
|
|
||||||
const handleDragEnter = (event: DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setReady(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
const file = event.dataTransfer.files[0];
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
handleImageChange(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
const acceptedFormats = accept
|
|
||||||
? accept.map((format) => "." + format).join(", ")
|
|
||||||
: "";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ButtonBase component="label" sx={{ justifyContent: "flex-start" }}>
|
|
||||||
<input
|
|
||||||
onChange={(event) => {
|
|
||||||
const file = event.target.files?.[0];
|
|
||||||
if (file) handleImageChange(file);
|
|
||||||
}}
|
|
||||||
hidden
|
|
||||||
accept={acceptedFormats || ".jpg, .jpeg, .png , .gif"}
|
|
||||||
multiple
|
|
||||||
type="file"
|
|
||||||
data-cy="upload-image-input"
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
onDragOver={(event: DragEvent<HTMLDivElement>) =>
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
onDrop={handleDrop}
|
|
||||||
ref={dropZone}
|
|
||||||
sx={{
|
|
||||||
width: isMobile ? "100%" : "580px",
|
|
||||||
padding: isMobile ? "33px" : "33px 10px 33px 55px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
border: `1px solid ${ready ? "red" : theme.palette.grey2.main}`,
|
|
||||||
borderRadius: "8px",
|
|
||||||
gap: "55px",
|
|
||||||
flexDirection: isMobile ? "column" : "row",
|
|
||||||
}}
|
|
||||||
onDragEnter={handleDragEnter}
|
|
||||||
>
|
|
||||||
<UploadIcon />
|
|
||||||
<Box>
|
|
||||||
<Typography sx={{ color: "#9A9AAF", fontWeight: "bold" }}>
|
|
||||||
Добавить файл
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
sx={{ color: theme.palette.grey2.main, fontSize: "16px" }}
|
|
||||||
>
|
|
||||||
{description || "Принимает JPG, PNG, и GIF формат — максимум 5mb"}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</ButtonBase>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
import React, { FC } from "react";
|
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import { IconButton, Typography, useTheme } from "@mui/material";
|
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
|
||||||
|
|
||||||
type FileBlockProps = {
|
|
||||||
file: File | null;
|
|
||||||
setFile?: (file: File | null) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FileBlock: FC<FileBlockProps> = ({ setFile, file }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
return (
|
|
||||||
<Box sx={{ display: "flex", gap: "15px", alignItems: "center" }}>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "18px",
|
|
||||||
fontWeight: "400",
|
|
||||||
color: theme.palette.grey3.main,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Вы загрузили:
|
|
||||||
</Typography>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
backgroundColor: theme.palette.brightPurple.main,
|
|
||||||
borderRadius: "8px",
|
|
||||||
padding: setFile ? "5px 5px 5px 14px" : "5px 14px",
|
|
||||||
gap: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
sx={{ color: "white", fontSize: "14px", fontWeight: "400" }}
|
|
||||||
>
|
|
||||||
{file?.name}
|
|
||||||
</Typography>
|
|
||||||
{setFile && (
|
|
||||||
<IconButton
|
|
||||||
onClick={() => setFile(null)}
|
|
||||||
sx={{
|
|
||||||
backgroundColor: "#864BD9",
|
|
||||||
borderRadius: "50%",
|
|
||||||
width: "24px",
|
|
||||||
height: "24px",
|
|
||||||
color: "white",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CloseIcon
|
|
||||||
sx={{ width: "14px", height: "14px", transform: "scale(1.5)" }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
|
||||||
import React, { FC } from "react";
|
|
||||||
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
|
||||||
import File from "@ui_kit/QuizPreview/QuizPreviewQuestionTypes/File";
|
|
||||||
import { FileBlock } from "./FileBlock/FileBlock";
|
|
||||||
import { CustomFileUploader } from "./CustomFileUploader/CustomFileUploader";
|
|
||||||
|
|
||||||
type IntegrationStep5Props = {
|
|
||||||
handlePrevStep: () => void;
|
|
||||||
handleNextStep: () => void;
|
|
||||||
setUtmFile: (file: File | null) => void;
|
|
||||||
utmFile: File | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const IntegrationStep5: FC<IntegrationStep5Props> = ({
|
|
||||||
handlePrevStep,
|
|
||||||
handleNextStep,
|
|
||||||
utmFile,
|
|
||||||
setUtmFile,
|
|
||||||
}) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100%",
|
|
||||||
flexGrow: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ alignSelf: "start", marginTop: "20px" }}>
|
|
||||||
{utmFile ? (
|
|
||||||
<FileBlock file={utmFile} setFile={setUtmFile} />
|
|
||||||
) : (
|
|
||||||
<CustomFileUploader
|
|
||||||
description={"Принимает .txt и .docx формат — максимум 100мб"}
|
|
||||||
accept={["txt", "docx"]}
|
|
||||||
handleImageChange={setUtmFile}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<StepButtonsBlock
|
|
||||||
onLargeBtnClick={handleNextStep}
|
|
||||||
onSmallBtnClick={handlePrevStep}
|
|
||||||
isLargeBtnDisabled={!utmFile}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -9,17 +9,17 @@ import {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import { ItemsSelectionView } from "./ItemsSelectionView/ItemsSelectionView";
|
import { ItemsSelectionView } from "./ItemsSelectionView/ItemsSelectionView";
|
||||||
import { ItemDetailsView } from "./ItemDetailsView/ItemDetailsView";
|
import { ItemDetailsView } from "./ItemDetailsView/ItemDetailsView";
|
||||||
import { TitleKeys, TQuestionEntity } from "../IntegrationsModal";
|
import { TitleKeys, TQuestionEntity } from "../AmoCRMModal";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
type IntegrationStep6Props = {
|
type AmoStep6Props = {
|
||||||
handlePrevStep: () => void;
|
handlePrevStep: () => void;
|
||||||
handleNextStep: () => void;
|
handleNextStep: () => void;
|
||||||
questionEntity: TQuestionEntity;
|
questionEntity: TQuestionEntity;
|
||||||
setQuestionEntity: Dispatch<SetStateAction<TQuestionEntity>>;
|
setQuestionEntity: Dispatch<SetStateAction<TQuestionEntity>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntegrationStep6: FC<IntegrationStep6Props> = ({
|
export const AmoStep6: FC<AmoStep6Props> = ({
|
||||||
handlePrevStep,
|
handlePrevStep,
|
||||||
handleNextStep,
|
handleNextStep,
|
||||||
questionEntity,
|
questionEntity,
|
||||||
@ -2,12 +2,7 @@ import { Box, Typography, useTheme } from "@mui/material";
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { IconBtnAdd } from "./IconBtnAdd/IconBtnAdd";
|
import { IconBtnAdd } from "./IconBtnAdd/IconBtnAdd";
|
||||||
import { AnswerItem } from "./AnswerItem/AnswerItem";
|
import { AnswerItem } from "./AnswerItem/AnswerItem";
|
||||||
import {
|
import { TagKeys, TitleKeys, TQuestionEntity, TTags } from "../../AmoCRMModal";
|
||||||
TagKeys,
|
|
||||||
TitleKeys,
|
|
||||||
TQuestionEntity,
|
|
||||||
TTags,
|
|
||||||
} from "../../IntegrationsModal";
|
|
||||||
|
|
||||||
type ItemProps = {
|
type ItemProps = {
|
||||||
title: TitleKeys | TagKeys;
|
title: TitleKeys | TagKeys;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Box, useTheme } from "@mui/material";
|
|||||||
import { Item } from "../Item/Item";
|
import { Item } from "../Item/Item";
|
||||||
import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { TQuestionEntity } from "../../IntegrationsModal";
|
import { TQuestionEntity } from "../../AmoCRMModal";
|
||||||
|
|
||||||
type TitleKeys = "contacts" | "company" | "deal" | "users" | "buyers";
|
type TitleKeys = "contacts" | "company" | "deal" | "users" | "buyers";
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
|
||||||
type ItemsSelectionViewProps = {
|
type ItemsSelectionViewProps = {
|
||||||
|
type?: string;
|
||||||
items: string[];
|
items: string[];
|
||||||
selectedValue: string | null;
|
selectedValue: string | null;
|
||||||
setSelectedValue: (value: string | null) => void;
|
setSelectedValue: (value: string | null) => void;
|
||||||
@ -17,6 +18,7 @@ export const ItemsSelectionView: FC<ItemsSelectionViewProps> = ({
|
|||||||
setSelectedValue,
|
setSelectedValue,
|
||||||
onLargeBtnClick,
|
onLargeBtnClick,
|
||||||
onSmallBtnClick,
|
onSmallBtnClick,
|
||||||
|
type,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -37,6 +39,7 @@ export const ItemsSelectionView: FC<ItemsSelectionViewProps> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomRadioGroup
|
<CustomRadioGroup
|
||||||
|
type={type}
|
||||||
items={items}
|
items={items}
|
||||||
selectedValue={selectedValue}
|
selectedValue={selectedValue}
|
||||||
setSelectedValue={setSelectedValue}
|
setSelectedValue={setSelectedValue}
|
||||||
|
|||||||
@ -8,19 +8,19 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
import { TagKeys, TTags } from "../IntegrationsModal";
|
import { TagKeys, TTags } from "../AmoCRMModal";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import { ItemsSelectionView } from "../IntegrationStep6/ItemsSelectionView/ItemsSelectionView";
|
import { ItemsSelectionView } from "../IntegrationStep6/ItemsSelectionView/ItemsSelectionView";
|
||||||
import { TagsDetailsView } from "./TagsDetailsView/TagsDetailsView";
|
import { TagsDetailsView } from "./TagsDetailsView/TagsDetailsView";
|
||||||
|
|
||||||
type IntegrationStep7Props = {
|
type AmoStep7Props = {
|
||||||
handleSmallBtn: () => void;
|
handleSmallBtn: () => void;
|
||||||
handleLargeBtn: () => void;
|
handleLargeBtn: () => void;
|
||||||
tags: TTags;
|
tags: TTags;
|
||||||
setTags: Dispatch<SetStateAction<TTags>>;
|
setTags: Dispatch<SetStateAction<TTags>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntegrationStep7: FC<IntegrationStep7Props> = ({
|
export const AmoStep7: FC<AmoStep7Props> = ({
|
||||||
handleSmallBtn,
|
handleSmallBtn,
|
||||||
handleLargeBtn,
|
handleLargeBtn,
|
||||||
tags,
|
tags,
|
||||||
@ -59,6 +59,7 @@ export const IntegrationStep7: FC<IntegrationStep7Props> = ({
|
|||||||
items={items}
|
items={items}
|
||||||
selectedValue={selectedValue}
|
selectedValue={selectedValue}
|
||||||
setSelectedValue={setSelectedValue}
|
setSelectedValue={setSelectedValue}
|
||||||
|
type={"typeTags"}
|
||||||
onSmallBtnClick={() => {
|
onSmallBtnClick={() => {
|
||||||
setActiveItem(null);
|
setActiveItem(null);
|
||||||
setIsSelection(false);
|
setIsSelection(false);
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { Box, Typography, useTheme } from "@mui/material";
|
import { Box, Typography, useTheme } from "@mui/material";
|
||||||
import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { TagKeys, TTags } from "../../IntegrationsModal";
|
import { TagKeys, TTags } from "../../AmoCRMModal";
|
||||||
import { Item } from "../../IntegrationStep6/Item/Item";
|
import { Item } from "../../IntegrationStep6/Item/Item";
|
||||||
|
|
||||||
type TagsDetailsViewProps = {
|
type TagsDetailsViewProps = {
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import { FC } from "react";
|
|||||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||||
import { SettingItem } from "./SettingItem/SettingItem";
|
import { SettingItem } from "./SettingItem/SettingItem";
|
||||||
import { TQuestionEntity, TTags } from "../IntegrationsModal";
|
import { TQuestionEntity, TTags } from "../AmoCRMModal";
|
||||||
|
|
||||||
type SettingsBlockProps = {
|
type AmoSettingsBlockProps = {
|
||||||
stepTitles: string[];
|
stepTitles: string[];
|
||||||
setStep: (value: number) => void;
|
setStep: (value: number) => void;
|
||||||
setIsSettingsBlock: (value: boolean) => void;
|
setIsSettingsBlock: (value: boolean) => void;
|
||||||
@ -13,12 +13,11 @@ type SettingsBlockProps = {
|
|||||||
selectedStagePerformer: string | null;
|
selectedStagePerformer: string | null;
|
||||||
selectedStage: string | null;
|
selectedStage: string | null;
|
||||||
selectedDealPerformer: string | null;
|
selectedDealPerformer: string | null;
|
||||||
utmFile: File | null;
|
|
||||||
questionEntity: TQuestionEntity;
|
questionEntity: TQuestionEntity;
|
||||||
tags: TTags;
|
tags: TTags;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsBlock: FC<SettingsBlockProps> = ({
|
export const AmoSettingsBlock: FC<AmoSettingsBlockProps> = ({
|
||||||
stepTitles,
|
stepTitles,
|
||||||
setStep,
|
setStep,
|
||||||
setIsSettingsBlock,
|
setIsSettingsBlock,
|
||||||
@ -27,7 +26,6 @@ export const SettingsBlock: FC<SettingsBlockProps> = ({
|
|||||||
selectedStagePerformer,
|
selectedStagePerformer,
|
||||||
selectedDealPerformer,
|
selectedDealPerformer,
|
||||||
selectedStage,
|
selectedStage,
|
||||||
utmFile,
|
|
||||||
questionEntity,
|
questionEntity,
|
||||||
tags,
|
tags,
|
||||||
}) => {
|
}) => {
|
||||||
@ -68,7 +66,6 @@ export const SettingsBlock: FC<SettingsBlockProps> = ({
|
|||||||
selectedStagePerformer={selectedStagePerformer}
|
selectedStagePerformer={selectedStagePerformer}
|
||||||
selectedDealPerformer={selectedDealPerformer}
|
selectedDealPerformer={selectedDealPerformer}
|
||||||
selectedStage={selectedStage}
|
selectedStage={selectedStage}
|
||||||
utmFile={utmFile}
|
|
||||||
questionEntity={questionEntity}
|
questionEntity={questionEntity}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
/>
|
/>
|
||||||
@ -4,8 +4,7 @@ import { Typography, useMediaQuery, useTheme } from "@mui/material";
|
|||||||
import { SettingItemHeader } from "./SettingItemHeader/SettingItemHeader";
|
import { SettingItemHeader } from "./SettingItemHeader/SettingItemHeader";
|
||||||
import { ResponsiblePerson } from "./ResponsiblePerson/ResponsiblePerson";
|
import { ResponsiblePerson } from "./ResponsiblePerson/ResponsiblePerson";
|
||||||
import { SelectedParameter } from "./SelectedParameter/SelectedParameter";
|
import { SelectedParameter } from "./SelectedParameter/SelectedParameter";
|
||||||
import { FileBlock } from "../../IntegrationStep5/FileBlock/FileBlock";
|
import { TQuestionEntity, TTags } from "../../AmoCRMModal";
|
||||||
import { TQuestionEntity, TTags } from "../../IntegrationsModal";
|
|
||||||
|
|
||||||
type SettingItemProps = {
|
type SettingItemProps = {
|
||||||
step: number;
|
step: number;
|
||||||
@ -17,7 +16,6 @@ type SettingItemProps = {
|
|||||||
selectedStagePerformer: string | null;
|
selectedStagePerformer: string | null;
|
||||||
selectedDealPerformer: string | null;
|
selectedDealPerformer: string | null;
|
||||||
selectedStage: string | null;
|
selectedStage: string | null;
|
||||||
utmFile: File | null;
|
|
||||||
questionEntity: TQuestionEntity;
|
questionEntity: TQuestionEntity;
|
||||||
tags: TTags;
|
tags: TTags;
|
||||||
};
|
};
|
||||||
@ -32,7 +30,6 @@ export const SettingItem: FC<SettingItemProps> = ({
|
|||||||
selectedStagePerformer,
|
selectedStagePerformer,
|
||||||
selectedDealPerformer,
|
selectedDealPerformer,
|
||||||
selectedStage,
|
selectedStage,
|
||||||
utmFile,
|
|
||||||
questionEntity,
|
questionEntity,
|
||||||
tags,
|
tags,
|
||||||
}) => {
|
}) => {
|
||||||
@ -68,17 +65,6 @@ export const SettingItem: FC<SettingItemProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (step === 4) {
|
if (step === 4) {
|
||||||
return (
|
|
||||||
<Box sx={{ display: "flex", gap: "15px", marginTop: "20px" }}>
|
|
||||||
{utmFile ? (
|
|
||||||
<FileBlock file={utmFile} />
|
|
||||||
) : (
|
|
||||||
<Typography>Файл не загружен</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (step === 5) {
|
|
||||||
const isFilled = Object.values(questionEntity).some(
|
const isFilled = Object.values(questionEntity).some(
|
||||||
(array) => array.length > 0,
|
(array) => array.length > 0,
|
||||||
);
|
);
|
||||||
@ -110,7 +96,7 @@ export const SettingItem: FC<SettingItemProps> = ({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (step === 6) {
|
if (step === 5) {
|
||||||
const isFilled = Object.values(tags).some((array) => array.length > 0);
|
const isFilled = Object.values(tags).some((array) => array.length > 0);
|
||||||
const status = isFilled ? "Заполнено" : "Не заполнено";
|
const status = isFilled ? "Заполнено" : "Не заполнено";
|
||||||
|
|
||||||
@ -148,7 +134,6 @@ export const SettingItem: FC<SettingItemProps> = ({
|
|||||||
selectedStagePerformer,
|
selectedStagePerformer,
|
||||||
selectedDealPerformer,
|
selectedDealPerformer,
|
||||||
selectedStage,
|
selectedStage,
|
||||||
utmFile,
|
|
||||||
questionEntity,
|
questionEntity,
|
||||||
tags,
|
tags,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
|||||||
import { useQuizStore } from "@root/quizes/store";
|
import { useQuizStore } from "@root/quizes/store";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { PartnersBoard } from "./PartnersBoard/PartnersBoard";
|
import { PartnersBoard } from "./PartnersBoard/PartnersBoard";
|
||||||
import { partnersMock } from "./mocks/MockData";
|
|
||||||
import { QuizMetricType } from "@model/quizSettings";
|
import { QuizMetricType } from "@model/quizSettings";
|
||||||
|
|
||||||
interface IntegrationsPageProps {
|
interface IntegrationsPageProps {
|
||||||
@ -26,6 +25,9 @@ export const IntegrationsPage = ({
|
|||||||
const [companyName, setCompanyName] = useState<
|
const [companyName, setCompanyName] = useState<
|
||||||
keyof typeof QuizMetricType | null
|
keyof typeof QuizMetricType | null
|
||||||
>(null);
|
>(null);
|
||||||
|
|
||||||
|
const [isAmoCrmModalOpen, setIsAmoCrmModalOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editQuizId === null) navigate("/list");
|
if (editQuizId === null) navigate("/list");
|
||||||
}, [navigate, editQuizId]);
|
}, [navigate, editQuizId]);
|
||||||
@ -38,9 +40,9 @@ export const IntegrationsPage = ({
|
|||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
// setTimeout(() => {
|
};
|
||||||
// setCompanyName(null);
|
const handleCloseAmoSRMModal = () => {
|
||||||
// }, 300);
|
setIsAmoCrmModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -63,18 +65,15 @@ export const IntegrationsPage = ({
|
|||||||
Интеграции
|
Интеграции
|
||||||
</Typography>
|
</Typography>
|
||||||
<PartnersBoard
|
<PartnersBoard
|
||||||
partners={partnersMock}
|
|
||||||
setIsModalOpen={setIsModalOpen}
|
setIsModalOpen={setIsModalOpen}
|
||||||
companyName={companyName}
|
companyName={companyName}
|
||||||
setCompanyName={setCompanyName}
|
setCompanyName={setCompanyName}
|
||||||
isModalOpen={isModalOpen}
|
isModalOpen={isModalOpen}
|
||||||
handleCloseModal={handleCloseModal}
|
handleCloseModal={handleCloseModal}
|
||||||
|
setIsAmoCrmModalOpen={setIsAmoCrmModalOpen}
|
||||||
|
isAmoCrmModalOpen={isAmoCrmModalOpen}
|
||||||
|
handleCloseAmoSRMModal={handleCloseAmoSRMModal}
|
||||||
/>
|
/>
|
||||||
{/*<IntegrationsModal*/}
|
|
||||||
{/* isModalOpen={isModalOpen}*/}
|
|
||||||
{/* handleCloseModal={handleCloseModal}*/}
|
|
||||||
{/* companyName={companyName}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -25,11 +25,11 @@ interface Props {
|
|||||||
companyName: keyof typeof QuizMetricType;
|
companyName: keyof typeof QuizMetricType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AnalyticsModal({
|
export const AnalyticsModal = ({
|
||||||
isModalOpen,
|
isModalOpen,
|
||||||
handleCloseModal,
|
handleCloseModal,
|
||||||
companyName,
|
companyName,
|
||||||
}: Props) {
|
}: Props) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
@ -260,4 +260,4 @@ export default function AnalyticsModal({
|
|||||||
</Box>
|
</Box>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@ -1,50 +0,0 @@
|
|||||||
import { Box, Typography, useTheme } from "@mui/material";
|
|
||||||
import { FC } from "react";
|
|
||||||
import { Partner } from "../PartnersBoard";
|
|
||||||
|
|
||||||
type PartnerItemProps = {
|
|
||||||
partner: Partner;
|
|
||||||
setIsModalOpen: (value: boolean) => void;
|
|
||||||
setCompanyName: (value: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PartnerItem: FC<PartnerItemProps> = ({
|
|
||||||
partner,
|
|
||||||
setIsModalOpen,
|
|
||||||
setCompanyName,
|
|
||||||
}) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
setCompanyName(partner.name);
|
|
||||||
setIsModalOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{partner && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: 250,
|
|
||||||
height: 60,
|
|
||||||
backgroundColor: "white",
|
|
||||||
borderRadius: "8px",
|
|
||||||
padding: "0 20px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
marginBottom: "2%",
|
|
||||||
marginRight: "2%",
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
{partner.logo ? (
|
|
||||||
<img height={"100%"} src={partner.logo} alt={partner.name} />
|
|
||||||
) : (
|
|
||||||
<Typography>{partner.name}</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,43 +1,47 @@
|
|||||||
import { Box, Typography, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { FC } from "react";
|
import React, { FC, lazy, Suspense } from "react";
|
||||||
import { ServiceButton } from "./ServiceButton/ServiceButton";
|
import { ServiceButton } from "./ServiceButton/ServiceButton";
|
||||||
import { YandexMetricaLogo } from "../mocks/YandexMetricaLogo";
|
import { YandexMetricaLogo } from "../mocks/YandexMetricaLogo";
|
||||||
import AnalyticsModal from "./AnalyticsModal/AnalyticsModal";
|
// import AnalyticsModal from "./AnalyticsModal/AnalyticsModal";
|
||||||
import { VKPixelLogo } from "../mocks/VKPixelLogo";
|
import { VKPixelLogo } from "../mocks/VKPixelLogo";
|
||||||
import { QuizMetricType } from "@model/quizSettings";
|
import { QuizMetricType } from "@model/quizSettings";
|
||||||
|
import { AmoCRMLogo } from "../mocks/AmoCRMLogo";
|
||||||
|
|
||||||
export type Partner = {
|
const AnalyticsModal = lazy(() =>
|
||||||
name: string;
|
import("./AnalyticsModal/AnalyticsModal").then((module) => ({
|
||||||
logo?: string;
|
default: module.AnalyticsModal,
|
||||||
category: string;
|
})),
|
||||||
};
|
);
|
||||||
|
|
||||||
|
const AmoCRMModal = lazy(() =>
|
||||||
|
import("../IntegrationsModal/AmoCRMModal").then((module) => ({
|
||||||
|
default: module.AmoCRMModal,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
type PartnersBoardProps = {
|
type PartnersBoardProps = {
|
||||||
partners: Partner[];
|
|
||||||
setIsModalOpen: (value: boolean) => void;
|
setIsModalOpen: (value: boolean) => void;
|
||||||
companyName: keyof typeof QuizMetricType | null;
|
companyName: keyof typeof QuizMetricType | null;
|
||||||
setCompanyName: (value: keyof typeof QuizMetricType) => void;
|
setCompanyName: (value: keyof typeof QuizMetricType) => void;
|
||||||
isModalOpen: boolean;
|
isModalOpen: boolean;
|
||||||
handleCloseModal: () => void;
|
handleCloseModal: () => void;
|
||||||
|
setIsAmoCrmModalOpen: (value: boolean) => void;
|
||||||
|
isAmoCrmModalOpen: boolean;
|
||||||
|
handleCloseAmoSRMModal: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PartnersBoard: FC<PartnersBoardProps> = ({
|
export const PartnersBoard: FC<PartnersBoardProps> = ({
|
||||||
partners,
|
|
||||||
setIsModalOpen,
|
setIsModalOpen,
|
||||||
isModalOpen,
|
isModalOpen,
|
||||||
handleCloseModal,
|
handleCloseModal,
|
||||||
companyName,
|
companyName,
|
||||||
setCompanyName,
|
setCompanyName,
|
||||||
|
setIsAmoCrmModalOpen,
|
||||||
|
isAmoCrmModalOpen,
|
||||||
|
handleCloseAmoSRMModal,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
// const partnersByCategory = partners.reduce(
|
|
||||||
// (acc, partner) => {
|
|
||||||
// (acc[partner.category] = acc[partner.category] || []).push(partner);
|
|
||||||
// return acc;
|
|
||||||
// },
|
|
||||||
// {} as Record<string, Partner[]>,
|
|
||||||
// );
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -48,39 +52,32 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
|
|||||||
justifyContent: { xs: "center", sm: "center", md: "start" },
|
justifyContent: { xs: "center", sm: "center", md: "start" },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/*{Object.entries(partnersByCategory).map(([category, partners]) => (*/}
|
|
||||||
{/* <Box key={category}>*/}
|
|
||||||
{/* <Typography*/}
|
|
||||||
{/* variant="h6"*/}
|
|
||||||
{/* sx={{*/}
|
|
||||||
{/* textAlign: { xs: "center", sm: "start", md: "start" },*/}
|
|
||||||
{/* lineHeight: "1",*/}
|
|
||||||
{/* marginBottom: "12px",*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/* >*/}
|
|
||||||
{/* {category}*/}
|
|
||||||
{/* </Typography>*/}
|
|
||||||
{/* <Box*/}
|
|
||||||
{/* sx={{*/}
|
|
||||||
{/* display: "flex",*/}
|
|
||||||
{/* flexWrap: "wrap",*/}
|
|
||||||
{/* justifyContent: { xs: "center", sm: "start", md: "start" },*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/* >*/}
|
|
||||||
{/* {partners.map((partner) => (*/}
|
|
||||||
{/* <PartnerItem*/}
|
|
||||||
{/* key={partner.name}*/}
|
|
||||||
{/* partner={partner}*/}
|
|
||||||
{/* setIsModalOpen={setIsModalOpen}*/}
|
|
||||||
{/* setCompanyName={setCompanyName}*/}
|
|
||||||
{/* />*/}
|
|
||||||
{/* ))}*/}
|
|
||||||
|
|
||||||
{/* </Box>*/}
|
|
||||||
{/* </Box>*/}
|
|
||||||
{/*))}*/}
|
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
|
<Typography
|
||||||
|
variant="h6"
|
||||||
|
sx={{
|
||||||
|
textAlign: { xs: "start", sm: "start", md: "start" },
|
||||||
|
lineHeight: "1",
|
||||||
|
marginBottom: "12px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
CRM
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
justifyContent: { xs: "start", sm: "start", md: "start" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ServiceButton
|
||||||
|
logo={<AmoCRMLogo />}
|
||||||
|
setIsModalOpen={setIsAmoCrmModalOpen}
|
||||||
|
setCompanyName={setCompanyName}
|
||||||
|
name={"amoCRM"}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h6"
|
||||||
sx={{
|
sx={{
|
||||||
@ -114,11 +111,22 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{companyName && (
|
{companyName && (
|
||||||
<AnalyticsModal
|
<Suspense>
|
||||||
isModalOpen={isModalOpen}
|
<AnalyticsModal
|
||||||
handleCloseModal={handleCloseModal}
|
isModalOpen={isModalOpen}
|
||||||
companyName={companyName}
|
handleCloseModal={handleCloseModal}
|
||||||
/>
|
companyName={companyName}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
|
{companyName && (
|
||||||
|
<Suspense>
|
||||||
|
<AmoCRMModal
|
||||||
|
isModalOpen={isAmoCrmModalOpen}
|
||||||
|
handleCloseModal={handleCloseAmoSRMModal}
|
||||||
|
companyName={companyName}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
6
src/pages/IntegrationsPage/mocks/AmoCRMLogo.tsx
Normal file
6
src/pages/IntegrationsPage/mocks/AmoCRMLogo.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { ReactComponent as AmoLogo } from "./amoCRMLogo.svg";
|
||||||
|
|
||||||
|
export const AmoCRMLogo = () => {
|
||||||
|
return <AmoLogo />;
|
||||||
|
};
|
||||||
@ -1,9 +1,3 @@
|
|||||||
import amoCrmLogo from "./amoCrmLogo.png";
|
|
||||||
|
|
||||||
export const partnersMock = [
|
|
||||||
{ category: "CRM", name: "amoCRM", logo: amoCrmLogo },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const performersMock = [
|
export const performersMock = [
|
||||||
"Ангелина Полякова",
|
"Ангелина Полякова",
|
||||||
"Петр Иванов",
|
"Петр Иванов",
|
||||||
|
|||||||
9
src/pages/IntegrationsPage/mocks/amoCRMLogo.svg
Normal file
9
src/pages/IntegrationsPage/mocks/amoCRMLogo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 72 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
@ -130,7 +130,7 @@ export default function OptionsAndPicture({
|
|||||||
if (selectedVariantId)
|
if (selectedVariantId)
|
||||||
clearQuestionImages(question.id, selectedVariantId);
|
clearQuestionImages(question.id, selectedVariantId);
|
||||||
}}
|
}}
|
||||||
cropAspectRatio={{ width: 452, height: 300 }}
|
cropAspectRatio={{ width: 300, height: 300 }}
|
||||||
/>
|
/>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { QuizStartpageType } from "@model/quizSettings";
|
|||||||
import InfoIcon from "@icons/InfoIcon";
|
import InfoIcon from "@icons/InfoIcon";
|
||||||
import UploadBox from "@ui_kit/UploadBox";
|
import UploadBox from "@ui_kit/UploadBox";
|
||||||
import UploadIcon from "../../assets/icons/UploadIcon";
|
import UploadIcon from "../../assets/icons/UploadIcon";
|
||||||
|
import CustomizedSwitch from "@ui_kit/CustomSwitch";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@ -46,6 +47,7 @@ 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";
|
||||||
|
|
||||||
const designTypes = [
|
const designTypes = [
|
||||||
[
|
[
|
||||||
@ -868,6 +870,22 @@ export default function StartPageSettings() {
|
|||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
/>
|
/>
|
||||||
<Extra />
|
<Extra />
|
||||||
|
<Box sx={{display: "flex", gap: "20px", alignItems: "center"}}>
|
||||||
|
<CustomizedSwitch
|
||||||
|
checked={quiz.config.antifraud}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateQuiz(quiz.id, (quiz) => {
|
||||||
|
quiz.config.antifraud = e.target.checked;
|
||||||
|
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography sx={{fontWeight: 500,
|
||||||
|
color: theme.palette.grey3.main,}}
|
||||||
|
>
|
||||||
|
Включить антифрод</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -62,6 +62,8 @@ export interface Quizes {
|
|||||||
law?: string;
|
law?: string;
|
||||||
};
|
};
|
||||||
meta: string;
|
meta: string;
|
||||||
|
antifraud: boolean;
|
||||||
|
showfc: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +152,8 @@ export const quizStore = create<QuizStore>()(
|
|||||||
law: "",
|
law: "",
|
||||||
},
|
},
|
||||||
meta: "что-то",
|
meta: "что-то",
|
||||||
|
antifraud: true,
|
||||||
|
showfc: true
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
set({ listQuizes: newListQuizes });
|
set({ listQuizes: newListQuizes });
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user