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 { FC } from "react";
|
||||
import { FC, useEffect, useMemo, useRef, useState } from "react";
|
||||
import Radio from "@mui/material/Radio";
|
||||
import RadioGroup from "@mui/material/RadioGroup";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Box from "@mui/material/Box";
|
||||
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 = {
|
||||
items: string[];
|
||||
type?: string;
|
||||
selectedValue: string | null;
|
||||
setSelectedValue: (value: string | null) => void;
|
||||
pipelineId?: number | null;
|
||||
};
|
||||
|
||||
const SIZE = 25;
|
||||
|
||||
export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
||||
items,
|
||||
type,
|
||||
selectedValue,
|
||||
setSelectedValue,
|
||||
pipelineId,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [currentValue, setCurrentValue] = React.useState<string | null>(
|
||||
const [currentValue, setCurrentValue] = useState<string | null>(
|
||||
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>) => {
|
||||
setSelectedValue((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 (
|
||||
<Box
|
||||
ref={boxRef}
|
||||
onScroll={handleScroll}
|
||||
sx={{
|
||||
border: `1px solid ${theme.palette.grey2.main}`,
|
||||
borderRadius: "12px",
|
||||
@ -42,39 +257,7 @@ export const CustomRadioGroup: FC<CustomRadioGroupProps> = ({
|
||||
value={currentValue}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<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"}
|
||||
/>
|
||||
))}
|
||||
{formControlLabels}
|
||||
</RadioGroup>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@ -8,40 +8,151 @@ import {
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
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 arrow_down from "../../assets/icons/arrow_down.svg";
|
||||
import { getUsers, PaginationRequest, User } from "@api/integration";
|
||||
|
||||
type CustomSelectProps = {
|
||||
items: string[];
|
||||
selectedItem: string | null;
|
||||
setSelectedItem: (value: string | null) => void;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
items,
|
||||
selectedItem,
|
||||
setSelectedItem,
|
||||
type,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const [opened, setOpened] = useState<boolean>(false);
|
||||
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 selectWidth = ref.current ? ref.current.offsetWidth : undefined;
|
||||
|
||||
const [savedValue, setSavedValue] = useState<number | null>(null);
|
||||
|
||||
const onSelectItem = useCallback(
|
||||
(event: SelectChangeEvent<HTMLDivElement>) => {
|
||||
const newValue = event.target.value.toString();
|
||||
const selectedUser = users.find((user) => user.Name === newValue);
|
||||
if (selectedUser) {
|
||||
//для сохранения ID выбранного пользователя в стейт или конфиг...
|
||||
setSavedValue(selectedUser.ID);
|
||||
}
|
||||
setCurrentValue(newValue);
|
||||
setSelectedItem(newValue);
|
||||
},
|
||||
[setSelectedItem, setCurrentValue],
|
||||
[setSelectedItem, setCurrentValue, setSavedValue, users],
|
||||
);
|
||||
|
||||
const toggleOpened = useCallback(() => {
|
||||
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 (
|
||||
<Box>
|
||||
<Box
|
||||
@ -100,29 +211,21 @@ export const CustomSelect: FC<CustomSelectProps> = ({
|
||||
MenuProps={{
|
||||
disablePortal: true,
|
||||
PaperProps: {
|
||||
onScroll: handleScroll,
|
||||
style: {
|
||||
zIndex: 2,
|
||||
maxHeight: "300px",
|
||||
overflow: "auto",
|
||||
overflowX: "auto",
|
||||
maxWidth: selectWidth,
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{ width: "100%" }}
|
||||
sx={{}}
|
||||
onChange={onSelectItem}
|
||||
onClick={toggleOpened}
|
||||
>
|
||||
{items.map((item) => {
|
||||
const uniqueKey = `${item}-${Date.now()}`;
|
||||
return (
|
||||
<MenuItem
|
||||
key={uniqueKey}
|
||||
value={item}
|
||||
sx={{ padding: "12px", zIndex: 2 }}
|
||||
>
|
||||
{item}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
{menuItems}
|
||||
</Select>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@ -114,6 +114,8 @@ export interface QuizConfig {
|
||||
law?: string;
|
||||
};
|
||||
meta: string;
|
||||
antifraud: boolean;
|
||||
showfc: boolean;
|
||||
yandexMetricsNumber: number | undefined;
|
||||
vkMetricsNumber: number | undefined;
|
||||
}
|
||||
@ -228,6 +230,8 @@ export const defaultQuizConfig: QuizConfig = {
|
||||
button: "",
|
||||
},
|
||||
meta: "",
|
||||
antifraud: true,
|
||||
showfc: true,
|
||||
yandexMetricsNumber: undefined,
|
||||
vkMetricsNumber: undefined,
|
||||
};
|
||||
|
||||
@ -35,6 +35,7 @@ import {
|
||||
FieldSettingsDrawerState,
|
||||
FormContactFieldName,
|
||||
} from "@model/quizSettings";
|
||||
import CustomizedSwitch from "@ui_kit/CustomSwitch";
|
||||
|
||||
const buttons: { key: FormContactFieldName; name: string; desc: string }[] = [
|
||||
{ name: "Имя", desc: "Дмитрий", key: "name" },
|
||||
@ -63,6 +64,21 @@ export default function ContactFormPage() {
|
||||
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.email.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,
|
||||
useTheme,
|
||||
} 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 CloseIcon from "@mui/icons-material/Close";
|
||||
import { IntegrationStep1 } from "./IntegrationStep1/IntegrationStep1";
|
||||
import { IntegrationStep2 } from "./IntegrationStep2/IntegrationStep2";
|
||||
import { IntegrationStep3 } from "./IntegrationStep3/IntegrationStep3";
|
||||
import { IntegrationStep4 } from "./IntegrationStep4/IntegrationStep4";
|
||||
import { IntegrationStep5 } from "./IntegrationStep5/IntegrationStep5";
|
||||
import { IntegrationStep6 } from "./IntegrationStep6/IntegrationStep6";
|
||||
import { funnelsMock, performersMock, stagesMock } from "../mocks/MockData";
|
||||
import File from "@ui_kit/QuizPreview/QuizPreviewQuestionTypes/File";
|
||||
import { IntegrationsModalTitle } from "./IntegrationsModalTitle/IntegrationsModalTitle";
|
||||
import { SettingsBlock } from "./SettingsBlock/SettingsBlock";
|
||||
import { IntegrationStep7 } from "./IntegrationStep7/IntegrationStep7";
|
||||
import { AmoLogin } from "./AmoLogin/AmoLogin";
|
||||
import { AmoStep2 } from "./AmoStep2/AmoStep2";
|
||||
import { AmoStep3 } from "./AmoStep3/AmoStep3";
|
||||
import { AmoStep4 } from "./AmoStep4/AmoStep4";
|
||||
import { AmoStep6 } from "./IntegrationStep6/AmoStep6";
|
||||
import { AmoModalTitle } from "./AmoModalTitle/AmoModalTitle";
|
||||
import { AmoSettingsBlock } from "./SettingsBlock/AmoSettingsBlock";
|
||||
import { AmoStep7 } from "./IntegrationStep7/AmoStep7";
|
||||
import { AmoAccountInfo } from "./AmoAccountInfo/AmoAccountInfo";
|
||||
import { AccountResponse, getAccount } from "@api/integration";
|
||||
|
||||
export type TitleKeys = "contacts" | "company" | "deal" | "users" | "buyers";
|
||||
|
||||
@ -32,7 +31,7 @@ type IntegrationsModalProps = {
|
||||
export type TagKeys = "contact" | "company" | "deal" | "buyer";
|
||||
export type TTags = Record<TagKeys, string[] | []>;
|
||||
|
||||
export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
isModalOpen,
|
||||
handleCloseModal,
|
||||
companyName,
|
||||
@ -43,18 +42,19 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
|
||||
const [step, setStep] = useState<number>(0);
|
||||
const [isSettingsBlock, setIsSettingsBlock] = useState<boolean>(false);
|
||||
const [selectedFunnelPerformer, setSelectedFunnelPerformer] = useState<
|
||||
|
||||
const [accountInfo, setAccountInfo] = useState<AccountResponse | null>(null);
|
||||
const [selectedPipelinePerformer, setSelectedPipelinePerformer] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [selectedFunnel, setSelectedFunnel] = useState<string | null>(null);
|
||||
const [selectedStagePerformer, setSelectedStagePerformer] = useState<
|
||||
const [selectedPipeline, setSelectedPipeline] = useState<string | null>(null);
|
||||
const [selectedStepsPerformer, setSelectedStepsPerformer] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [selectedStage, setSelectedStage] = useState<string | null>(null);
|
||||
const [selectedStep, setSelectedStep] = useState<string | null>(null);
|
||||
const [selectedDealPerformer, setSelectedDealPerformer] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [utmFile, setUtmFile] = useState<File | null>(null);
|
||||
const [questionEntity, setQuestionEntity] = useState<TQuestionEntity>({
|
||||
contacts: [],
|
||||
company: [],
|
||||
@ -69,6 +69,20 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
buyer: [],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen) {
|
||||
const fetchAccount = async () => {
|
||||
const [account, error] = await getAccount();
|
||||
if (account && !error) {
|
||||
setAccountInfo(account);
|
||||
} else {
|
||||
setAccountInfo(null);
|
||||
}
|
||||
};
|
||||
fetchAccount();
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
|
||||
const handleNextStep = () => {
|
||||
setStep((prevState) => prevState + 1);
|
||||
};
|
||||
@ -79,27 +93,33 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
handleCloseModal();
|
||||
setStep(1);
|
||||
};
|
||||
|
||||
const steps = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: "Авторизация в аккаунте",
|
||||
title: accountInfo
|
||||
? "Информация об аккаунте"
|
||||
: "Авторизация в аккаунте",
|
||||
isSettingsAvailable: false,
|
||||
component: <IntegrationStep1 handleNextStep={handleNextStep} />,
|
||||
component: accountInfo ? (
|
||||
<AmoAccountInfo
|
||||
handleNextStep={handleNextStep}
|
||||
accountInfo={accountInfo}
|
||||
/>
|
||||
) : (
|
||||
<AmoLogin handleNextStep={handleNextStep} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Выбор воронки",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep2
|
||||
<AmoStep2
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleNextStep}
|
||||
selectedFunnelPerformer={selectedFunnelPerformer}
|
||||
setSelectedFunnelPerformer={setSelectedFunnelPerformer}
|
||||
selectedFunnel={selectedFunnel}
|
||||
setSelectedFunnel={setSelectedFunnel}
|
||||
performers={performersMock}
|
||||
funnels={funnelsMock}
|
||||
selectedPipelinePerformer={selectedPipelinePerformer}
|
||||
setSelectedPipelinePerformer={setSelectedPipelinePerformer}
|
||||
selectedPipeline={selectedPipeline}
|
||||
setSelectedPipeline={setSelectedPipeline}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@ -107,15 +127,14 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
title: "Выбор этапа воронки",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep3
|
||||
<AmoStep3
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleNextStep}
|
||||
selectedStagePerformer={selectedStagePerformer}
|
||||
setSelectedStagePerformer={setSelectedStagePerformer}
|
||||
selectedStage={selectedStage}
|
||||
setSelectedStage={setSelectedStage}
|
||||
performers={performersMock}
|
||||
stages={stagesMock}
|
||||
selectedStepsPerformer={selectedStepsPerformer}
|
||||
setSelectedStepsPerformer={setSelectedStepsPerformer}
|
||||
selectedStep={selectedStep}
|
||||
setSelectedStep={setSelectedStep}
|
||||
pipelineId={selectedPipeline}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@ -123,24 +142,11 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
title: "Сделка",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep4
|
||||
<AmoStep4
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleNextStep}
|
||||
selectedDealPerformer={selectedDealPerformer}
|
||||
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: "Соотнесение вопросов и сущностей",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep6
|
||||
<AmoStep6
|
||||
questionEntity={questionEntity}
|
||||
setQuestionEntity={setQuestionEntity}
|
||||
handlePrevStep={handlePrevStep}
|
||||
@ -160,7 +166,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
title: "Добавление тегов",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep7
|
||||
<AmoStep7
|
||||
handleSmallBtn={handlePrevStep}
|
||||
handleLargeBtn={handleSave}
|
||||
tags={tags}
|
||||
@ -171,11 +177,10 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
],
|
||||
[
|
||||
questionEntity,
|
||||
utmFile,
|
||||
selectedFunnelPerformer,
|
||||
selectedFunnel,
|
||||
selectedStagePerformer,
|
||||
selectedStage,
|
||||
selectedPipelinePerformer,
|
||||
selectedPipeline,
|
||||
selectedStepsPerformer,
|
||||
selectedStep,
|
||||
selectedDealPerformer,
|
||||
tags,
|
||||
],
|
||||
@ -188,7 +193,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
open={isModalOpen}
|
||||
onClose={handleCloseModal}
|
||||
fullWidth
|
||||
fullScreen={isMobile}
|
||||
// fullScreen={isMobile}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
maxWidth: isTablet ? "100%" : "920px",
|
||||
@ -209,6 +214,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
fontSize: isMobile ? "20px" : "24px",
|
||||
fontWeight: "500",
|
||||
padding: "20px",
|
||||
color: theme.palette.grey2.main,
|
||||
}}
|
||||
>
|
||||
Интеграция с {companyName ? companyName : "партнером"}
|
||||
@ -238,7 +244,7 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<IntegrationsModalTitle
|
||||
<AmoModalTitle
|
||||
step={step}
|
||||
steps={steps}
|
||||
isSettingsBlock={isSettingsBlock}
|
||||
@ -247,16 +253,15 @@ export const IntegrationsModal: FC<IntegrationsModalProps> = ({
|
||||
/>
|
||||
{isSettingsBlock ? (
|
||||
<Box sx={{ flexGrow: 1, width: "100%" }}>
|
||||
<SettingsBlock
|
||||
<AmoSettingsBlock
|
||||
stepTitles={stepTitles}
|
||||
setIsSettingsBlock={setIsSettingsBlock}
|
||||
setStep={setStep}
|
||||
selectedDealPerformer={selectedDealPerformer}
|
||||
selectedFunnelPerformer={selectedFunnelPerformer}
|
||||
selectedFunnel={selectedFunnel}
|
||||
selectedStagePerformer={selectedStagePerformer}
|
||||
selectedStage={selectedStage}
|
||||
utmFile={utmFile}
|
||||
selectedFunnelPerformer={selectedPipelinePerformer}
|
||||
selectedFunnel={selectedPipeline}
|
||||
selectedStagePerformer={selectedStepsPerformer}
|
||||
selectedStage={selectedStep}
|
||||
questionEntity={questionEntity}
|
||||
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 AccountSetting from "@icons/AccountSetting";
|
||||
|
||||
type IntegrationsModalTitleProps = {
|
||||
type AmoModalTitleProps = {
|
||||
step: number;
|
||||
steps: { title: string; isSettingsAvailable: boolean }[];
|
||||
isSettingsBlock?: boolean;
|
||||
@ -12,7 +12,7 @@ type IntegrationsModalTitleProps = {
|
||||
setStep: (value: number) => void;
|
||||
};
|
||||
|
||||
export const IntegrationsModalTitle: FC<IntegrationsModalTitleProps> = ({
|
||||
export const AmoModalTitle: FC<AmoModalTitleProps> = ({
|
||||
step,
|
||||
steps,
|
||||
setIsSettingsBlock,
|
||||
@ -4,31 +4,26 @@ import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
||||
import { CustomRadioGroup } from "../../../../components/CustomRadioGroup/CustomRadioGroup";
|
||||
|
||||
type IntegrationStep3Props = {
|
||||
type AmoStep2Props = {
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
selectedStagePerformer: string | null;
|
||||
setSelectedStagePerformer: (value: string | null) => void;
|
||||
selectedStage: string | null;
|
||||
setSelectedStage: (value: string | null) => void;
|
||||
performers: string[];
|
||||
stages: string[];
|
||||
selectedPipelinePerformer: string | null;
|
||||
setSelectedPipelinePerformer: (value: string | null) => void;
|
||||
selectedPipeline: string | null;
|
||||
setSelectedPipeline: (value: string | null) => void;
|
||||
};
|
||||
|
||||
export const IntegrationStep3: FC<IntegrationStep3Props> = ({
|
||||
export const AmoStep2: FC<AmoStep2Props> = ({
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
selectedStagePerformer,
|
||||
setSelectedStagePerformer,
|
||||
selectedStage,
|
||||
setSelectedStage,
|
||||
performers,
|
||||
stages,
|
||||
selectedPipelinePerformer,
|
||||
setSelectedPipelinePerformer,
|
||||
selectedPipeline,
|
||||
setSelectedPipeline,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -41,9 +36,9 @@ export const IntegrationStep3: FC<IntegrationStep3Props> = ({
|
||||
>
|
||||
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
||||
<CustomSelect
|
||||
selectedItem={selectedStagePerformer}
|
||||
items={performers}
|
||||
setSelectedItem={setSelectedStagePerformer}
|
||||
selectedItem={selectedPipelinePerformer}
|
||||
setSelectedItem={setSelectedPipelinePerformer}
|
||||
type={"typeUsers"}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
@ -55,9 +50,9 @@ export const IntegrationStep3: FC<IntegrationStep3Props> = ({
|
||||
}}
|
||||
>
|
||||
<CustomRadioGroup
|
||||
items={stages}
|
||||
selectedValue={selectedStage}
|
||||
setSelectedValue={setSelectedStage}
|
||||
selectedValue={selectedPipeline}
|
||||
setSelectedValue={setSelectedPipeline}
|
||||
type={"typePipelines"}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
@ -4,30 +4,29 @@ import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
||||
import { CustomRadioGroup } from "../../../../components/CustomRadioGroup/CustomRadioGroup";
|
||||
|
||||
type IntegrationStep2Props = {
|
||||
type AmoStep3Props = {
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
selectedFunnelPerformer: string | null;
|
||||
setSelectedFunnelPerformer: (value: string | null) => void;
|
||||
selectedFunnel: string | null;
|
||||
setSelectedFunnel: (value: string | null) => void;
|
||||
performers: string[];
|
||||
funnels: string[];
|
||||
selectedStepsPerformer: string | null;
|
||||
setSelectedStepsPerformer: (value: string | null) => void;
|
||||
selectedStep: string | null;
|
||||
setSelectedStep: (value: string | null) => void;
|
||||
pipelineId: string | null;
|
||||
};
|
||||
|
||||
export const IntegrationStep2: FC<IntegrationStep2Props> = ({
|
||||
export const AmoStep3: FC<AmoStep3Props> = ({
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
selectedFunnelPerformer,
|
||||
setSelectedFunnelPerformer,
|
||||
selectedFunnel,
|
||||
setSelectedFunnel,
|
||||
performers,
|
||||
funnels,
|
||||
selectedStepsPerformer,
|
||||
setSelectedStepsPerformer,
|
||||
selectedStep,
|
||||
setSelectedStep,
|
||||
pipelineId,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -40,9 +39,9 @@ export const IntegrationStep2: FC<IntegrationStep2Props> = ({
|
||||
>
|
||||
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
||||
<CustomSelect
|
||||
selectedItem={selectedFunnelPerformer}
|
||||
items={performers}
|
||||
setSelectedItem={setSelectedFunnelPerformer}
|
||||
selectedItem={selectedStepsPerformer}
|
||||
type={"typeUsers"}
|
||||
setSelectedItem={setSelectedStepsPerformer}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
@ -54,9 +53,11 @@ export const IntegrationStep2: FC<IntegrationStep2Props> = ({
|
||||
}}
|
||||
>
|
||||
<CustomRadioGroup
|
||||
items={funnels}
|
||||
selectedValue={selectedFunnel}
|
||||
setSelectedValue={setSelectedFunnel}
|
||||
// @ts-ignore
|
||||
pipelineId={pipelineId}
|
||||
type={"typeSteps"}
|
||||
selectedValue={selectedStep}
|
||||
setSelectedValue={setSelectedStep}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
@ -3,20 +3,18 @@ import { FC } from "react";
|
||||
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
||||
|
||||
type IntegrationStep4Props = {
|
||||
type AmoStep4Props = {
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
selectedDealPerformer: string | null;
|
||||
setSelectedDealPerformer: (value: string | null) => void;
|
||||
performers: string[];
|
||||
};
|
||||
|
||||
export const IntegrationStep4: FC<IntegrationStep4Props> = ({
|
||||
export const AmoStep4: FC<AmoStep4Props> = ({
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
selectedDealPerformer,
|
||||
setSelectedDealPerformer,
|
||||
performers,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
@ -35,8 +33,8 @@ export const IntegrationStep4: FC<IntegrationStep4Props> = ({
|
||||
<Box sx={{ width: "100%", marginTop: "20px", zIndex: 3 }}>
|
||||
<CustomSelect
|
||||
selectedItem={selectedDealPerformer}
|
||||
items={performers}
|
||||
setSelectedItem={setSelectedDealPerformer}
|
||||
type={"typeUsers"}
|
||||
/>
|
||||
</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";
|
||||
import { ItemsSelectionView } from "./ItemsSelectionView/ItemsSelectionView";
|
||||
import { ItemDetailsView } from "./ItemDetailsView/ItemDetailsView";
|
||||
import { TitleKeys, TQuestionEntity } from "../IntegrationsModal";
|
||||
import { TitleKeys, TQuestionEntity } from "../AmoCRMModal";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
type IntegrationStep6Props = {
|
||||
type AmoStep6Props = {
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
questionEntity: TQuestionEntity;
|
||||
setQuestionEntity: Dispatch<SetStateAction<TQuestionEntity>>;
|
||||
};
|
||||
|
||||
export const IntegrationStep6: FC<IntegrationStep6Props> = ({
|
||||
export const AmoStep6: FC<AmoStep6Props> = ({
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
questionEntity,
|
||||
@ -2,12 +2,7 @@ import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { FC } from "react";
|
||||
import { IconBtnAdd } from "./IconBtnAdd/IconBtnAdd";
|
||||
import { AnswerItem } from "./AnswerItem/AnswerItem";
|
||||
import {
|
||||
TagKeys,
|
||||
TitleKeys,
|
||||
TQuestionEntity,
|
||||
TTags,
|
||||
} from "../../IntegrationsModal";
|
||||
import { TagKeys, TitleKeys, TQuestionEntity, TTags } from "../../AmoCRMModal";
|
||||
|
||||
type ItemProps = {
|
||||
title: TitleKeys | TagKeys;
|
||||
|
||||
@ -2,7 +2,7 @@ import { Box, useTheme } from "@mui/material";
|
||||
import { Item } from "../Item/Item";
|
||||
import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
||||
import { FC } from "react";
|
||||
import { TQuestionEntity } from "../../IntegrationsModal";
|
||||
import { TQuestionEntity } from "../../AmoCRMModal";
|
||||
|
||||
type TitleKeys = "contacts" | "company" | "deal" | "users" | "buyers";
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
||||
import { FC } from "react";
|
||||
|
||||
type ItemsSelectionViewProps = {
|
||||
type?: string;
|
||||
items: string[];
|
||||
selectedValue: string | null;
|
||||
setSelectedValue: (value: string | null) => void;
|
||||
@ -17,6 +18,7 @@ export const ItemsSelectionView: FC<ItemsSelectionViewProps> = ({
|
||||
setSelectedValue,
|
||||
onLargeBtnClick,
|
||||
onSmallBtnClick,
|
||||
type,
|
||||
}) => {
|
||||
return (
|
||||
<Box
|
||||
@ -37,6 +39,7 @@ export const ItemsSelectionView: FC<ItemsSelectionViewProps> = ({
|
||||
}}
|
||||
>
|
||||
<CustomRadioGroup
|
||||
type={type}
|
||||
items={items}
|
||||
selectedValue={selectedValue}
|
||||
setSelectedValue={setSelectedValue}
|
||||
|
||||
@ -8,19 +8,19 @@ import {
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
import { TagKeys, TTags } from "../IntegrationsModal";
|
||||
import { TagKeys, TTags } from "../AmoCRMModal";
|
||||
import Box from "@mui/material/Box";
|
||||
import { ItemsSelectionView } from "../IntegrationStep6/ItemsSelectionView/ItemsSelectionView";
|
||||
import { TagsDetailsView } from "./TagsDetailsView/TagsDetailsView";
|
||||
|
||||
type IntegrationStep7Props = {
|
||||
type AmoStep7Props = {
|
||||
handleSmallBtn: () => void;
|
||||
handleLargeBtn: () => void;
|
||||
tags: TTags;
|
||||
setTags: Dispatch<SetStateAction<TTags>>;
|
||||
};
|
||||
|
||||
export const IntegrationStep7: FC<IntegrationStep7Props> = ({
|
||||
export const AmoStep7: FC<AmoStep7Props> = ({
|
||||
handleSmallBtn,
|
||||
handleLargeBtn,
|
||||
tags,
|
||||
@ -59,6 +59,7 @@ export const IntegrationStep7: FC<IntegrationStep7Props> = ({
|
||||
items={items}
|
||||
selectedValue={selectedValue}
|
||||
setSelectedValue={setSelectedValue}
|
||||
type={"typeTags"}
|
||||
onSmallBtnClick={() => {
|
||||
setActiveItem(null);
|
||||
setIsSelection(false);
|
||||
@ -1,7 +1,7 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { StepButtonsBlock } from "../../StepButtonsBlock/StepButtonsBlock";
|
||||
import { FC } from "react";
|
||||
import { TagKeys, TTags } from "../../IntegrationsModal";
|
||||
import { TagKeys, TTags } from "../../AmoCRMModal";
|
||||
import { Item } from "../../IntegrationStep6/Item/Item";
|
||||
|
||||
type TagsDetailsViewProps = {
|
||||
|
||||
@ -2,9 +2,9 @@ import { FC } from "react";
|
||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||
import { SettingItem } from "./SettingItem/SettingItem";
|
||||
import { TQuestionEntity, TTags } from "../IntegrationsModal";
|
||||
import { TQuestionEntity, TTags } from "../AmoCRMModal";
|
||||
|
||||
type SettingsBlockProps = {
|
||||
type AmoSettingsBlockProps = {
|
||||
stepTitles: string[];
|
||||
setStep: (value: number) => void;
|
||||
setIsSettingsBlock: (value: boolean) => void;
|
||||
@ -13,12 +13,11 @@ type SettingsBlockProps = {
|
||||
selectedStagePerformer: string | null;
|
||||
selectedStage: string | null;
|
||||
selectedDealPerformer: string | null;
|
||||
utmFile: File | null;
|
||||
questionEntity: TQuestionEntity;
|
||||
tags: TTags;
|
||||
};
|
||||
|
||||
export const SettingsBlock: FC<SettingsBlockProps> = ({
|
||||
export const AmoSettingsBlock: FC<AmoSettingsBlockProps> = ({
|
||||
stepTitles,
|
||||
setStep,
|
||||
setIsSettingsBlock,
|
||||
@ -27,7 +26,6 @@ export const SettingsBlock: FC<SettingsBlockProps> = ({
|
||||
selectedStagePerformer,
|
||||
selectedDealPerformer,
|
||||
selectedStage,
|
||||
utmFile,
|
||||
questionEntity,
|
||||
tags,
|
||||
}) => {
|
||||
@ -68,7 +66,6 @@ export const SettingsBlock: FC<SettingsBlockProps> = ({
|
||||
selectedStagePerformer={selectedStagePerformer}
|
||||
selectedDealPerformer={selectedDealPerformer}
|
||||
selectedStage={selectedStage}
|
||||
utmFile={utmFile}
|
||||
questionEntity={questionEntity}
|
||||
tags={tags}
|
||||
/>
|
||||
@ -4,8 +4,7 @@ import { Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { SettingItemHeader } from "./SettingItemHeader/SettingItemHeader";
|
||||
import { ResponsiblePerson } from "./ResponsiblePerson/ResponsiblePerson";
|
||||
import { SelectedParameter } from "./SelectedParameter/SelectedParameter";
|
||||
import { FileBlock } from "../../IntegrationStep5/FileBlock/FileBlock";
|
||||
import { TQuestionEntity, TTags } from "../../IntegrationsModal";
|
||||
import { TQuestionEntity, TTags } from "../../AmoCRMModal";
|
||||
|
||||
type SettingItemProps = {
|
||||
step: number;
|
||||
@ -17,7 +16,6 @@ type SettingItemProps = {
|
||||
selectedStagePerformer: string | null;
|
||||
selectedDealPerformer: string | null;
|
||||
selectedStage: string | null;
|
||||
utmFile: File | null;
|
||||
questionEntity: TQuestionEntity;
|
||||
tags: TTags;
|
||||
};
|
||||
@ -32,7 +30,6 @@ export const SettingItem: FC<SettingItemProps> = ({
|
||||
selectedStagePerformer,
|
||||
selectedDealPerformer,
|
||||
selectedStage,
|
||||
utmFile,
|
||||
questionEntity,
|
||||
tags,
|
||||
}) => {
|
||||
@ -68,17 +65,6 @@ export const SettingItem: FC<SettingItemProps> = ({
|
||||
);
|
||||
}
|
||||
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(
|
||||
(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 status = isFilled ? "Заполнено" : "Не заполнено";
|
||||
|
||||
@ -148,7 +134,6 @@ export const SettingItem: FC<SettingItemProps> = ({
|
||||
selectedStagePerformer,
|
||||
selectedDealPerformer,
|
||||
selectedStage,
|
||||
utmFile,
|
||||
questionEntity,
|
||||
tags,
|
||||
]);
|
||||
|
||||
@ -5,7 +5,6 @@ import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { useQuizStore } from "@root/quizes/store";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { PartnersBoard } from "./PartnersBoard/PartnersBoard";
|
||||
import { partnersMock } from "./mocks/MockData";
|
||||
import { QuizMetricType } from "@model/quizSettings";
|
||||
|
||||
interface IntegrationsPageProps {
|
||||
@ -26,6 +25,9 @@ export const IntegrationsPage = ({
|
||||
const [companyName, setCompanyName] = useState<
|
||||
keyof typeof QuizMetricType | null
|
||||
>(null);
|
||||
|
||||
const [isAmoCrmModalOpen, setIsAmoCrmModalOpen] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (editQuizId === null) navigate("/list");
|
||||
}, [navigate, editQuizId]);
|
||||
@ -38,9 +40,9 @@ export const IntegrationsPage = ({
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsModalOpen(false);
|
||||
// setTimeout(() => {
|
||||
// setCompanyName(null);
|
||||
// }, 300);
|
||||
};
|
||||
const handleCloseAmoSRMModal = () => {
|
||||
setIsAmoCrmModalOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -63,18 +65,15 @@ export const IntegrationsPage = ({
|
||||
Интеграции
|
||||
</Typography>
|
||||
<PartnersBoard
|
||||
partners={partnersMock}
|
||||
setIsModalOpen={setIsModalOpen}
|
||||
companyName={companyName}
|
||||
setCompanyName={setCompanyName}
|
||||
isModalOpen={isModalOpen}
|
||||
handleCloseModal={handleCloseModal}
|
||||
setIsAmoCrmModalOpen={setIsAmoCrmModalOpen}
|
||||
isAmoCrmModalOpen={isAmoCrmModalOpen}
|
||||
handleCloseAmoSRMModal={handleCloseAmoSRMModal}
|
||||
/>
|
||||
{/*<IntegrationsModal*/}
|
||||
{/* isModalOpen={isModalOpen}*/}
|
||||
{/* handleCloseModal={handleCloseModal}*/}
|
||||
{/* companyName={companyName}*/}
|
||||
{/*/>*/}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -25,11 +25,11 @@ interface Props {
|
||||
companyName: keyof typeof QuizMetricType;
|
||||
}
|
||||
|
||||
export default function AnalyticsModal({
|
||||
export const AnalyticsModal = ({
|
||||
isModalOpen,
|
||||
handleCloseModal,
|
||||
companyName,
|
||||
}: Props) {
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const quiz = useCurrentQuiz();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
@ -260,4 +260,4 @@ export default function AnalyticsModal({
|
||||
</Box>
|
||||
</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 { FC } from "react";
|
||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import React, { FC, lazy, Suspense } from "react";
|
||||
import { ServiceButton } from "./ServiceButton/ServiceButton";
|
||||
import { YandexMetricaLogo } from "../mocks/YandexMetricaLogo";
|
||||
import AnalyticsModal from "./AnalyticsModal/AnalyticsModal";
|
||||
// import AnalyticsModal from "./AnalyticsModal/AnalyticsModal";
|
||||
import { VKPixelLogo } from "../mocks/VKPixelLogo";
|
||||
import { QuizMetricType } from "@model/quizSettings";
|
||||
import { AmoCRMLogo } from "../mocks/AmoCRMLogo";
|
||||
|
||||
export type Partner = {
|
||||
name: string;
|
||||
logo?: string;
|
||||
category: string;
|
||||
};
|
||||
const AnalyticsModal = lazy(() =>
|
||||
import("./AnalyticsModal/AnalyticsModal").then((module) => ({
|
||||
default: module.AnalyticsModal,
|
||||
})),
|
||||
);
|
||||
|
||||
const AmoCRMModal = lazy(() =>
|
||||
import("../IntegrationsModal/AmoCRMModal").then((module) => ({
|
||||
default: module.AmoCRMModal,
|
||||
})),
|
||||
);
|
||||
|
||||
type PartnersBoardProps = {
|
||||
partners: Partner[];
|
||||
setIsModalOpen: (value: boolean) => void;
|
||||
companyName: keyof typeof QuizMetricType | null;
|
||||
setCompanyName: (value: keyof typeof QuizMetricType) => void;
|
||||
isModalOpen: boolean;
|
||||
handleCloseModal: () => void;
|
||||
setIsAmoCrmModalOpen: (value: boolean) => void;
|
||||
isAmoCrmModalOpen: boolean;
|
||||
handleCloseAmoSRMModal: () => void;
|
||||
};
|
||||
|
||||
export const PartnersBoard: FC<PartnersBoardProps> = ({
|
||||
partners,
|
||||
setIsModalOpen,
|
||||
isModalOpen,
|
||||
handleCloseModal,
|
||||
companyName,
|
||||
setCompanyName,
|
||||
setIsAmoCrmModalOpen,
|
||||
isAmoCrmModalOpen,
|
||||
handleCloseAmoSRMModal,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
// const partnersByCategory = partners.reduce(
|
||||
// (acc, partner) => {
|
||||
// (acc[partner.category] = acc[partner.category] || []).push(partner);
|
||||
// return acc;
|
||||
// },
|
||||
// {} as Record<string, Partner[]>,
|
||||
// );
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -48,39 +52,32 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
|
||||
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>
|
||||
<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
|
||||
variant="h6"
|
||||
sx={{
|
||||
@ -114,11 +111,22 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
|
||||
</Box>
|
||||
</Box>
|
||||
{companyName && (
|
||||
<AnalyticsModal
|
||||
isModalOpen={isModalOpen}
|
||||
handleCloseModal={handleCloseModal}
|
||||
companyName={companyName}
|
||||
/>
|
||||
<Suspense>
|
||||
<AnalyticsModal
|
||||
isModalOpen={isModalOpen}
|
||||
handleCloseModal={handleCloseModal}
|
||||
companyName={companyName}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
{companyName && (
|
||||
<Suspense>
|
||||
<AmoCRMModal
|
||||
isModalOpen={isAmoCrmModalOpen}
|
||||
handleCloseModal={handleCloseAmoSRMModal}
|
||||
companyName={companyName}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
</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 = [
|
||||
"Ангелина Полякова",
|
||||
"Петр Иванов",
|
||||
|
||||
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)
|
||||
clearQuestionImages(question.id, selectedVariantId);
|
||||
}}
|
||||
cropAspectRatio={{ width: 452, height: 300 }}
|
||||
cropAspectRatio={{ width: 300, height: 300 }}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
|
||||
@ -10,6 +10,7 @@ import { QuizStartpageType } from "@model/quizSettings";
|
||||
import InfoIcon from "@icons/InfoIcon";
|
||||
import UploadBox from "@ui_kit/UploadBox";
|
||||
import UploadIcon from "../../assets/icons/UploadIcon";
|
||||
import CustomizedSwitch from "@ui_kit/CustomSwitch";
|
||||
|
||||
import {
|
||||
Box,
|
||||
@ -46,6 +47,7 @@ import { DropZone } from "./dropZone";
|
||||
import Extra from "./extra";
|
||||
import TooltipClickInfo from "@ui_kit/Toolbars/TooltipClickInfo";
|
||||
import { VideoElement } from "./VideoElement";
|
||||
import * as React from "react";
|
||||
|
||||
const designTypes = [
|
||||
[
|
||||
@ -868,6 +870,22 @@ export default function StartPageSettings() {
|
||||
maxLength={1000}
|
||||
/>
|
||||
<Extra />
|
||||
<Box sx={{display: "flex", gap: "20px", alignItems: "center"}}>
|
||||
<CustomizedSwitch
|
||||
checked={quiz.config.antifraud}
|
||||
onChange={(e) => {
|
||||
updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.antifraud = e.target.checked;
|
||||
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<Typography sx={{fontWeight: 500,
|
||||
color: theme.palette.grey3.main,}}
|
||||
>
|
||||
Включить антифрод</Typography>
|
||||
</Box>
|
||||
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@ -62,6 +62,8 @@ export interface Quizes {
|
||||
law?: string;
|
||||
};
|
||||
meta: string;
|
||||
antifraud: boolean;
|
||||
showfc: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@ -150,6 +152,8 @@ export const quizStore = create<QuizStore>()(
|
||||
law: "",
|
||||
},
|
||||
meta: "что-то",
|
||||
antifraud: true,
|
||||
showfc: true
|
||||
},
|
||||
};
|
||||
set({ listQuizes: newListQuizes });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user