diff --git a/src/api/integration.ts b/src/api/integration.ts new file mode 100644 index 00000000..efa22fa8 --- /dev/null +++ b/src/api/integration.ts @@ -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({ + 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({ + 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({ + 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({ + 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({ + 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({ + 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({ + 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}`]; + } +}; diff --git a/src/assets/icons/Amologo.png b/src/assets/icons/Amologo.png new file mode 100644 index 00000000..058ea796 Binary files /dev/null and b/src/assets/icons/Amologo.png differ diff --git a/src/components/AmoButton/AmoButton.tsx b/src/components/AmoButton/AmoButton.tsx new file mode 100644 index 00000000..0c04b643 --- /dev/null +++ b/src/components/AmoButton/AmoButton.tsx @@ -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 = ({ onClick }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); + return ( + + ); +}; diff --git a/src/components/CustomRadioGroup/CustomRadioGroup.tsx b/src/components/CustomRadioGroup/CustomRadioGroup.tsx index 3710f25d..e83d8071 100644 --- a/src/components/CustomRadioGroup/CustomRadioGroup.tsx +++ b/src/components/CustomRadioGroup/CustomRadioGroup.tsx @@ -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 = ({ - items, + type, selectedValue, setSelectedValue, + pipelineId, }) => { const theme = useTheme(); - const [currentValue, setCurrentValue] = React.useState( + const [currentValue, setCurrentValue] = useState( selectedValue, ); + const [page, setPage] = useState(1); + const [isLoading, setIsLoading] = useState(false); + const [tags, setTags] = useState([]); + const [steps, setSteps] = useState([]); + const [pipelines, setPipelines] = useState([]); + const [hasMoreItems, setHasMoreItems] = useState(true); + const boxRef = useRef(null); + const handleChange = (event: React.ChangeEvent) => { setSelectedValue((event.target as HTMLInputElement).value); setCurrentValue((event.target as HTMLInputElement).value); }; + + const handleScroll = (e: React.UIEvent) => { + 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) => ( + + } + icon={} + /> + } + label={ + + + {item.Name} + + {item.Entity} + + } + labelPlacement={"start"} + /> + )); + } + if (type === "typeSteps" && steps && steps.length !== 0) { + return steps.map((step) => ( + + } + icon={} + /> + } + label={step.Name} + labelPlacement={"start"} + /> + )); + } + if (type === "typePipelines" && pipelines && pipelines.length !== 0) { + return pipelines.map((pipeline) => ( + + } + icon={} + /> + } + label={pipeline.Name} + labelPlacement={"start"} + /> + )); + } + return ( + + Нет элементов + + ); + }, [tags, steps, currentValue, type, pipelines]); + return ( = ({ value={currentValue} onChange={handleChange} > - {items.map((item) => ( - - } - icon={} - /> - } - label={item} - labelPlacement={"start"} - /> - ))} + {formControlLabels} ); diff --git a/src/components/CustomSelect/CustomSelect.tsx b/src/components/CustomSelect/CustomSelect.tsx index c98c5d55..a61cab41 100644 --- a/src/components/CustomSelect/CustomSelect.tsx +++ b/src/components/CustomSelect/CustomSelect.tsx @@ -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 = ({ - items, selectedItem, setSelectedItem, + type, }) => { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(600)); const [opened, setOpened] = useState(false); const [currentValue, setCurrentValue] = useState(selectedItem); + const [users, setUsers] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [page, setPage] = useState(1); + const [hasMoreItems, setHasMoreItems] = useState(true); + const SIZE = 25; const ref = useRef(null); + const selectWidth = ref.current ? ref.current.offsetWidth : undefined; + + const [savedValue, setSavedValue] = useState(null); const onSelectItem = useCallback( (event: SelectChangeEvent) => { 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) => { + 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) => ( + + + + {user.Name} + + + {user.Email} + + + {user.Role} + + + + )); + } + return ( + + нет данных + + ); + }, [users, type]); + return ( = ({ 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 ( - - {item} - - ); - })} + {menuItems} ); diff --git a/src/model/quizSettings.ts b/src/model/quizSettings.ts index b0118cd3..41fe92c1 100644 --- a/src/model/quizSettings.ts +++ b/src/model/quizSettings.ts @@ -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, }; diff --git a/src/pages/ContactFormPage/ContactFormPage.tsx b/src/pages/ContactFormPage/ContactFormPage.tsx index 0ad52a83..7117f422 100644 --- a/src/pages/ContactFormPage/ContactFormPage.tsx +++ b/src/pages/ContactFormPage/ContactFormPage.tsx @@ -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", }} > + + { + updateQuiz(quiz.id, (quiz) => { + quiz.config.showfc = e.target.checked; + + }) + }} + /> + + Показывать форму контактов + {!quiz?.config.formContact.fields.name.used && !quiz?.config.formContact.fields.email.used && !quiz?.config.formContact.fields.phone.used && diff --git a/src/pages/IntegrationsPage/IntegrationsModal/AmoAccountInfo/AmoAccountInfo.tsx b/src/pages/IntegrationsPage/IntegrationsModal/AmoAccountInfo/AmoAccountInfo.tsx new file mode 100644 index 00000000..6c9530f4 --- /dev/null +++ b/src/pages/IntegrationsPage/IntegrationsModal/AmoAccountInfo/AmoAccountInfo.tsx @@ -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 = ({ + handleNextStep, + accountInfo, +}) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); + + const infoItem = (title: string, value: string | number) => ( + + + + {title}: + + + + {value} + + + ); + + return ( + + + {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)} + + + + ); +}; diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationsModal.tsx b/src/pages/IntegrationsPage/IntegrationsModal/AmoCRMModal.tsx similarity index 64% rename from src/pages/IntegrationsPage/IntegrationsModal/IntegrationsModal.tsx rename to src/pages/IntegrationsPage/IntegrationsModal/AmoCRMModal.tsx index e78f1d71..27269f91 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationsModal.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/AmoCRMModal.tsx @@ -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; -export const IntegrationsModal: FC = ({ +export const AmoCRMModal: FC = ({ isModalOpen, handleCloseModal, companyName, @@ -43,18 +42,19 @@ export const IntegrationsModal: FC = ({ const [step, setStep] = useState(0); const [isSettingsBlock, setIsSettingsBlock] = useState(false); - const [selectedFunnelPerformer, setSelectedFunnelPerformer] = useState< + + const [accountInfo, setAccountInfo] = useState(null); + const [selectedPipelinePerformer, setSelectedPipelinePerformer] = useState< string | null >(null); - const [selectedFunnel, setSelectedFunnel] = useState(null); - const [selectedStagePerformer, setSelectedStagePerformer] = useState< + const [selectedPipeline, setSelectedPipeline] = useState(null); + const [selectedStepsPerformer, setSelectedStepsPerformer] = useState< string | null >(null); - const [selectedStage, setSelectedStage] = useState(null); + const [selectedStep, setSelectedStep] = useState(null); const [selectedDealPerformer, setSelectedDealPerformer] = useState< string | null >(null); - const [utmFile, setUtmFile] = useState(null); const [questionEntity, setQuestionEntity] = useState({ contacts: [], company: [], @@ -69,6 +69,20 @@ export const IntegrationsModal: FC = ({ 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 = ({ handleCloseModal(); setStep(1); }; - const steps = useMemo( () => [ { - title: "Авторизация в аккаунте", + title: accountInfo + ? "Информация об аккаунте" + : "Авторизация в аккаунте", isSettingsAvailable: false, - component: , + component: accountInfo ? ( + + ) : ( + + ), }, { title: "Выбор воронки", isSettingsAvailable: true, component: ( - ), }, @@ -107,15 +127,14 @@ export const IntegrationsModal: FC = ({ title: "Выбор этапа воронки", isSettingsAvailable: true, component: ( - ), }, @@ -123,24 +142,11 @@ export const IntegrationsModal: FC = ({ title: "Сделка", isSettingsAvailable: true, component: ( - - ), - }, - { - title: "Добавление utm-меток", - isSettingsAvailable: false, - component: ( - ), }, @@ -148,7 +154,7 @@ export const IntegrationsModal: FC = ({ title: "Соотнесение вопросов и сущностей", isSettingsAvailable: true, component: ( - = ({ title: "Добавление тегов", isSettingsAvailable: true, component: ( - = ({ ], [ questionEntity, - utmFile, - selectedFunnelPerformer, - selectedFunnel, - selectedStagePerformer, - selectedStage, + selectedPipelinePerformer, + selectedPipeline, + selectedStepsPerformer, + selectedStep, selectedDealPerformer, tags, ], @@ -188,7 +193,7 @@ export const IntegrationsModal: FC = ({ open={isModalOpen} onClose={handleCloseModal} fullWidth - fullScreen={isMobile} + // fullScreen={isMobile} PaperProps={{ sx: { maxWidth: isTablet ? "100%" : "920px", @@ -209,6 +214,7 @@ export const IntegrationsModal: FC = ({ fontSize: isMobile ? "20px" : "24px", fontWeight: "500", padding: "20px", + color: theme.palette.grey2.main, }} > Интеграция с {companyName ? companyName : "партнером"} @@ -238,7 +244,7 @@ export const IntegrationsModal: FC = ({ flexGrow: 1, }} > - = ({ /> {isSettingsBlock ? ( - diff --git a/src/pages/IntegrationsPage/IntegrationsModal/AmoLogin/AmoLogin.tsx b/src/pages/IntegrationsPage/IntegrationsModal/AmoLogin/AmoLogin.tsx new file mode 100644 index 00000000..9682bbef --- /dev/null +++ b/src/pages/IntegrationsPage/IntegrationsModal/AmoLogin/AmoLogin.tsx @@ -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 = ({ 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({ + // 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 ( + + {/**/} + {/* */} + {/* */} + {/**/} + + + Инструкция + + + После нажатия на кнопку - "Подключить", вас переадресует на страницу + подключения интеграции в ваш аккаунт AmoCRM. Пожалуйста, согласитесь + на всё, что мы предлагаем, иначе чуда не случится. + + + + + + {/**/} + + ); +}; diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationsModalTitle/IntegrationsModalTitle.tsx b/src/pages/IntegrationsPage/IntegrationsModal/AmoModalTitle/AmoModalTitle.tsx similarity index 96% rename from src/pages/IntegrationsPage/IntegrationsModal/IntegrationsModalTitle/IntegrationsModalTitle.tsx rename to src/pages/IntegrationsPage/IntegrationsModal/AmoModalTitle/AmoModalTitle.tsx index 93462345..39e297fb 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationsModalTitle/IntegrationsModalTitle.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/AmoModalTitle/AmoModalTitle.tsx @@ -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 = ({ +export const AmoModalTitle: FC = ({ step, steps, setIsSettingsBlock, diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep3/IntegrationStep3.tsx b/src/pages/IntegrationsPage/IntegrationsModal/AmoStep2/AmoStep2.tsx similarity index 66% rename from src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep3/IntegrationStep3.tsx rename to src/pages/IntegrationsPage/IntegrationsModal/AmoStep2/AmoStep2.tsx index 75d77f10..a32c375b 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep3/IntegrationStep3.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/AmoStep2/AmoStep2.tsx @@ -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 = ({ +export const AmoStep2: FC = ({ 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 ( = ({ > = ({ }} > 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 = ({ +export const AmoStep3: FC = ({ 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 ( = ({ > = ({ }} > void; handleNextStep: () => void; selectedDealPerformer: string | null; setSelectedDealPerformer: (value: string | null) => void; - performers: string[]; }; -export const IntegrationStep4: FC = ({ +export const AmoStep4: FC = ({ handlePrevStep, handleNextStep, selectedDealPerformer, setSelectedDealPerformer, - performers, }) => { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(600)); @@ -35,8 +33,8 @@ export const IntegrationStep4: FC = ({ 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 = ({ - handleNextStep, -}) => { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); - - const formik = useFormik({ - 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 ( - - - - - - - - Инструкция - - - Повседневная практика показывает, что постоянный количественный рост и - сфера нашей активности способствует подготовки и реализации систем - массового участия - - - - - ); -}; diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/CustomFileUploader/CustomFileUploader.tsx b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/CustomFileUploader/CustomFileUploader.tsx deleted file mode 100644 index 8d055829..00000000 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/CustomFileUploader/CustomFileUploader.tsx +++ /dev/null @@ -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 = ({ - accept, - description, - handleImageChange, -}) => { - const theme = useTheme(); - const dropZone = useRef(null); - const [ready, setReady] = useState(false); - const isMobile = useMediaQuery(theme.breakpoints.down(700)); - - const handleDragEnter = (event: DragEvent) => { - event.preventDefault(); - setReady(true); - }; - - const handleDrop = (event: DragEvent) => { - event.preventDefault(); - event.stopPropagation(); - - const file = event.dataTransfer.files[0]; - if (!file) return; - - handleImageChange(file); - }; - - const acceptedFormats = accept - ? accept.map((format) => "." + format).join(", ") - : ""; - - return ( - - { - 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" - /> - ) => - 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} - > - - - - Добавить файл - - - {description || "Принимает JPG, PNG, и GIF формат — максимум 5mb"} - - - - - ); -}; diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/FileBlock/FileBlock.tsx b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/FileBlock/FileBlock.tsx deleted file mode 100644 index 89d63fcf..00000000 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/FileBlock/FileBlock.tsx +++ /dev/null @@ -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 = ({ setFile, file }) => { - const theme = useTheme(); - return ( - - - Вы загрузили: - - - - {file?.name} - - {setFile && ( - setFile(null)} - sx={{ - backgroundColor: "#864BD9", - borderRadius: "50%", - width: "24px", - height: "24px", - color: "white", - }} - > - - - )} - - - ); -}; diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/IntegrationStep5.tsx b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/IntegrationStep5.tsx deleted file mode 100644 index dd5a60c1..00000000 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep5/IntegrationStep5.tsx +++ /dev/null @@ -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 = ({ - handlePrevStep, - handleNextStep, - utmFile, - setUtmFile, -}) => { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - - return ( - - - {utmFile ? ( - - ) : ( - - )} - - - - ); -}; diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/IntegrationStep6.tsx b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/AmoStep6.tsx similarity index 92% rename from src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/IntegrationStep6.tsx rename to src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/AmoStep6.tsx index fb7f7a1b..4c7d6645 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/IntegrationStep6.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/AmoStep6.tsx @@ -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>; }; -export const IntegrationStep6: FC = ({ +export const AmoStep6: FC = ({ handlePrevStep, handleNextStep, questionEntity, diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/Item/Item.tsx b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/Item/Item.tsx index 55c746f9..d902eb12 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/Item/Item.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/Item/Item.tsx @@ -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; diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/ItemDetailsView/ItemDetailsView.tsx b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/ItemDetailsView/ItemDetailsView.tsx index a22eec6e..e468013d 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/ItemDetailsView/ItemDetailsView.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/ItemDetailsView/ItemDetailsView.tsx @@ -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"; diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/ItemsSelectionView/ItemsSelectionView.tsx b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/ItemsSelectionView/ItemsSelectionView.tsx index 3523352f..ee2a12f6 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/ItemsSelectionView/ItemsSelectionView.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep6/ItemsSelectionView/ItemsSelectionView.tsx @@ -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 = ({ setSelectedValue, onLargeBtnClick, onSmallBtnClick, + type, }) => { return ( = ({ }} > void; handleLargeBtn: () => void; tags: TTags; setTags: Dispatch>; }; -export const IntegrationStep7: FC = ({ +export const AmoStep7: FC = ({ handleSmallBtn, handleLargeBtn, tags, @@ -59,6 +59,7 @@ export const IntegrationStep7: FC = ({ items={items} selectedValue={selectedValue} setSelectedValue={setSelectedValue} + type={"typeTags"} onSmallBtnClick={() => { setActiveItem(null); setIsSelection(false); diff --git a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep7/TagsDetailsView/TagsDetailsView.tsx b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep7/TagsDetailsView/TagsDetailsView.tsx index 43b050f7..f2091b71 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep7/TagsDetailsView/TagsDetailsView.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/IntegrationStep7/TagsDetailsView/TagsDetailsView.tsx @@ -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 = { diff --git a/src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/SettingsBlock.tsx b/src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/AmoSettingsBlock.tsx similarity index 91% rename from src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/SettingsBlock.tsx rename to src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/AmoSettingsBlock.tsx index 523666fd..7cc2211f 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/SettingsBlock.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/AmoSettingsBlock.tsx @@ -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 = ({ +export const AmoSettingsBlock: FC = ({ stepTitles, setStep, setIsSettingsBlock, @@ -27,7 +26,6 @@ export const SettingsBlock: FC = ({ selectedStagePerformer, selectedDealPerformer, selectedStage, - utmFile, questionEntity, tags, }) => { @@ -68,7 +66,6 @@ export const SettingsBlock: FC = ({ selectedStagePerformer={selectedStagePerformer} selectedDealPerformer={selectedDealPerformer} selectedStage={selectedStage} - utmFile={utmFile} questionEntity={questionEntity} tags={tags} /> diff --git a/src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/SettingItem/SettingItem.tsx b/src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/SettingItem/SettingItem.tsx index 80ace601..82f0ac12 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/SettingItem/SettingItem.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/SettingsBlock/SettingItem/SettingItem.tsx @@ -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 = ({ selectedStagePerformer, selectedDealPerformer, selectedStage, - utmFile, questionEntity, tags, }) => { @@ -68,17 +65,6 @@ export const SettingItem: FC = ({ ); } if (step === 4) { - return ( - - {utmFile ? ( - - ) : ( - Файл не загружен - )} - - ); - } - if (step === 5) { const isFilled = Object.values(questionEntity).some( (array) => array.length > 0, ); @@ -110,7 +96,7 @@ export const SettingItem: FC = ({ ); } - 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 = ({ selectedStagePerformer, selectedDealPerformer, selectedStage, - utmFile, questionEntity, tags, ]); diff --git a/src/pages/IntegrationsPage/IntegrationsPage.tsx b/src/pages/IntegrationsPage/IntegrationsPage.tsx index 7357dca1..b74e3650 100644 --- a/src/pages/IntegrationsPage/IntegrationsPage.tsx +++ b/src/pages/IntegrationsPage/IntegrationsPage.tsx @@ -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(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 = ({ Интеграции - {/**/} ); diff --git a/src/pages/IntegrationsPage/PartnersBoard/AnalyticsModal/AnalyticsModal.tsx b/src/pages/IntegrationsPage/PartnersBoard/AnalyticsModal/AnalyticsModal.tsx index 677c117b..4ad0f815 100644 --- a/src/pages/IntegrationsPage/PartnersBoard/AnalyticsModal/AnalyticsModal.tsx +++ b/src/pages/IntegrationsPage/PartnersBoard/AnalyticsModal/AnalyticsModal.tsx @@ -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({ ); -} +}; diff --git a/src/pages/IntegrationsPage/PartnersBoard/PartnerItem/PartnerItem.tsx b/src/pages/IntegrationsPage/PartnersBoard/PartnerItem/PartnerItem.tsx deleted file mode 100644 index b780ed4e..00000000 --- a/src/pages/IntegrationsPage/PartnersBoard/PartnerItem/PartnerItem.tsx +++ /dev/null @@ -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 = ({ - partner, - setIsModalOpen, - setCompanyName, -}) => { - const theme = useTheme(); - - const handleClick = () => { - setCompanyName(partner.name); - setIsModalOpen(true); - }; - - return ( - <> - {partner && ( - - {partner.logo ? ( - {partner.name} - ) : ( - {partner.name} - )} - - )} - - ); -}; diff --git a/src/pages/IntegrationsPage/PartnersBoard/PartnersBoard.tsx b/src/pages/IntegrationsPage/PartnersBoard/PartnersBoard.tsx index e39d98e8..5510eb08 100644 --- a/src/pages/IntegrationsPage/PartnersBoard/PartnersBoard.tsx +++ b/src/pages/IntegrationsPage/PartnersBoard/PartnersBoard.tsx @@ -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 = ({ - 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, - // ); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); return ( = ({ justifyContent: { xs: "center", sm: "center", md: "start" }, }} > - {/*{Object.entries(partnersByCategory).map(([category, partners]) => (*/} - {/* */} - {/* */} - {/* {category}*/} - {/* */} - {/* */} - {/* {partners.map((partner) => (*/} - {/* */} - {/* ))}*/} - - {/* */} - {/* */} - {/*))}*/} - + + CRM + + + } + setIsModalOpen={setIsAmoCrmModalOpen} + setCompanyName={setCompanyName} + name={"amoCRM"} + /> + + = ({ {companyName && ( - + + + + )} + {companyName && ( + + + )} ); diff --git a/src/pages/IntegrationsPage/mocks/AmoCRMLogo.tsx b/src/pages/IntegrationsPage/mocks/AmoCRMLogo.tsx new file mode 100644 index 00000000..be6b1460 --- /dev/null +++ b/src/pages/IntegrationsPage/mocks/AmoCRMLogo.tsx @@ -0,0 +1,6 @@ +import React from "react"; +import { ReactComponent as AmoLogo } from "./amoCRMLogo.svg"; + +export const AmoCRMLogo = () => { + return ; +}; diff --git a/src/pages/IntegrationsPage/mocks/MockData.ts b/src/pages/IntegrationsPage/mocks/MockData.ts index 22094d4f..e38462fc 100644 --- a/src/pages/IntegrationsPage/mocks/MockData.ts +++ b/src/pages/IntegrationsPage/mocks/MockData.ts @@ -1,9 +1,3 @@ -import amoCrmLogo from "./amoCrmLogo.png"; - -export const partnersMock = [ - { category: "CRM", name: "amoCRM", logo: amoCrmLogo }, -]; - export const performersMock = [ "Ангелина Полякова", "Петр Иванов", diff --git a/src/pages/IntegrationsPage/mocks/amoCRMLogo.svg b/src/pages/IntegrationsPage/mocks/amoCRMLogo.svg new file mode 100644 index 00000000..c531cc0b --- /dev/null +++ b/src/pages/IntegrationsPage/mocks/amoCRMLogo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/pages/IntegrationsPage/mocks/amoCrmLogo.png b/src/pages/IntegrationsPage/mocks/amoCrmLogo.png deleted file mode 100644 index 07d03d78..00000000 Binary files a/src/pages/IntegrationsPage/mocks/amoCrmLogo.png and /dev/null differ diff --git a/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx b/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx index 314947fa..651b4859 100644 --- a/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx +++ b/src/pages/Questions/OptionsAndPicture/OptionsAndPicture.tsx @@ -130,7 +130,7 @@ export default function OptionsAndPicture({ if (selectedVariantId) clearQuestionImages(question.id, selectedVariantId); }} - cropAspectRatio={{ width: 452, height: 300 }} + cropAspectRatio={{ width: 300, height: 300 }} /> + + { + updateQuiz(quiz.id, (quiz) => { + quiz.config.antifraud = e.target.checked; + + }) + }} + /> + + Включить антифрод + + )} diff --git a/src/stores/quizes.ts b/src/stores/quizes.ts index cdee3acc..327f9eb3 100644 --- a/src/stores/quizes.ts +++ b/src/stores/quizes.ts @@ -62,6 +62,8 @@ export interface Quizes { law?: string; }; meta: string; + antifraud: boolean; + showfc: boolean; }; } @@ -150,6 +152,8 @@ export const quizStore = create()( law: "", }, meta: "что-то", + antifraud: true, + showfc: true }, }; set({ listQuizes: newListQuizes });