tasks 20,21,22,23,24,25,26
This commit is contained in:
parent
a66a4764cf
commit
f8f22e1397
@ -6,6 +6,81 @@ export type PaginationRequest = {
|
||||
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;
|
||||
@ -21,7 +96,6 @@ export type TagsResponse = {
|
||||
count: number;
|
||||
items: Tag[];
|
||||
};
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz/amocrm`;
|
||||
|
||||
export const getTags = async ({
|
||||
page,
|
||||
@ -39,6 +113,8 @@ export const getTags = async ({
|
||||
}
|
||||
};
|
||||
|
||||
//получение списка пользователей
|
||||
|
||||
export type User = {
|
||||
ID: number;
|
||||
AccountID: string;
|
||||
@ -75,6 +151,8 @@ export const getUsers = async ({
|
||||
}
|
||||
};
|
||||
|
||||
//получение списка шагов
|
||||
|
||||
export type Step = {
|
||||
ID: number;
|
||||
AmoID: number;
|
||||
@ -113,6 +191,8 @@ export const getSteps = async ({
|
||||
}
|
||||
};
|
||||
|
||||
//получение списка воронок
|
||||
|
||||
export type Pipeline = {
|
||||
ID: number;
|
||||
AmoID: number;
|
||||
@ -146,3 +226,107 @@ export const getPipelines = async ({
|
||||
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>
|
||||
);
|
||||
};
|
||||
@ -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,18 +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 { IntegrationStep6 } from "./IntegrationStep6/IntegrationStep6";
|
||||
import { performersMock } from "../mocks/MockData";
|
||||
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";
|
||||
|
||||
@ -41,6 +42,8 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
|
||||
const [step, setStep] = useState<number>(0);
|
||||
const [isSettingsBlock, setIsSettingsBlock] = useState<boolean>(false);
|
||||
|
||||
const [accountInfo, setAccountInfo] = useState<AccountResponse | null>(null);
|
||||
const [selectedPipelinePerformer, setSelectedPipelinePerformer] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
@ -66,6 +69,20 @@ export const AmoCRMModal: 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);
|
||||
};
|
||||
@ -76,19 +93,27 @@ export const AmoCRMModal: 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}
|
||||
selectedPipelinePerformer={selectedPipelinePerformer}
|
||||
@ -102,7 +127,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
title: "Выбор этапа воронки",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep3
|
||||
<AmoStep3
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleNextStep}
|
||||
selectedStepsPerformer={selectedStepsPerformer}
|
||||
@ -117,12 +142,11 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
title: "Сделка",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep4
|
||||
<AmoStep4
|
||||
handlePrevStep={handlePrevStep}
|
||||
handleNextStep={handleNextStep}
|
||||
selectedDealPerformer={selectedDealPerformer}
|
||||
setSelectedDealPerformer={setSelectedDealPerformer}
|
||||
performers={performersMock}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@ -130,7 +154,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
title: "Соотнесение вопросов и сущностей",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep6
|
||||
<AmoStep6
|
||||
questionEntity={questionEntity}
|
||||
setQuestionEntity={setQuestionEntity}
|
||||
handlePrevStep={handlePrevStep}
|
||||
@ -142,7 +166,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
title: "Добавление тегов",
|
||||
isSettingsAvailable: true,
|
||||
component: (
|
||||
<IntegrationStep7
|
||||
<AmoStep7
|
||||
handleSmallBtn={handlePrevStep}
|
||||
handleLargeBtn={handleSave}
|
||||
tags={tags}
|
||||
@ -153,7 +177,6 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
],
|
||||
[
|
||||
questionEntity,
|
||||
// utmFile,
|
||||
selectedPipelinePerformer,
|
||||
selectedPipeline,
|
||||
selectedStepsPerformer,
|
||||
@ -221,7 +244,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<IntegrationsModalTitle
|
||||
<AmoModalTitle
|
||||
step={step}
|
||||
steps={steps}
|
||||
isSettingsBlock={isSettingsBlock}
|
||||
@ -230,7 +253,7 @@ export const AmoCRMModal: FC<IntegrationsModalProps> = ({
|
||||
/>
|
||||
{isSettingsBlock ? (
|
||||
<Box sx={{ flexGrow: 1, width: "100%" }}>
|
||||
<SettingsBlock
|
||||
<AmoSettingsBlock
|
||||
stepTitles={stepTitles}
|
||||
setIsSettingsBlock={setIsSettingsBlock}
|
||||
setStep={setStep}
|
||||
|
||||
@ -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,7 +4,7 @@ import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
||||
import { CustomRadioGroup } from "../../../../components/CustomRadioGroup/CustomRadioGroup";
|
||||
|
||||
type IntegrationStep2Props = {
|
||||
type AmoStep2Props = {
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
selectedPipelinePerformer: string | null;
|
||||
@ -13,7 +13,7 @@ type IntegrationStep2Props = {
|
||||
setSelectedPipeline: (value: string | null) => void;
|
||||
};
|
||||
|
||||
export const IntegrationStep2: FC<IntegrationStep2Props> = ({
|
||||
export const AmoStep2: FC<AmoStep2Props> = ({
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
selectedPipelinePerformer,
|
||||
@ -4,7 +4,7 @@ import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||
import { CustomSelect } from "../../../../components/CustomSelect/CustomSelect";
|
||||
import { CustomRadioGroup } from "../../../../components/CustomRadioGroup/CustomRadioGroup";
|
||||
|
||||
type IntegrationStep3Props = {
|
||||
type AmoStep3Props = {
|
||||
handlePrevStep: () => void;
|
||||
handleNextStep: () => void;
|
||||
selectedStepsPerformer: string | null;
|
||||
@ -14,7 +14,7 @@ type IntegrationStep3Props = {
|
||||
pipelineId: string | null;
|
||||
};
|
||||
|
||||
export const IntegrationStep3: FC<IntegrationStep3Props> = ({
|
||||
export const AmoStep3: FC<AmoStep3Props> = ({
|
||||
handlePrevStep,
|
||||
handleNextStep,
|
||||
selectedStepsPerformer,
|
||||
@ -53,6 +53,7 @@ export const IntegrationStep3: FC<IntegrationStep3Props> = ({
|
||||
}}
|
||||
>
|
||||
<CustomRadioGroup
|
||||
// @ts-ignore
|
||||
pipelineId={pipelineId}
|
||||
type={"typeSteps"}
|
||||
selectedValue={selectedStep}
|
||||
@ -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));
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
@ -12,14 +12,14 @@ import { ItemDetailsView } from "./ItemDetailsView/ItemDetailsView";
|
||||
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,
|
||||
@ -13,14 +13,14 @@ 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,
|
||||
@ -4,7 +4,7 @@ import { StepButtonsBlock } from "../StepButtonsBlock/StepButtonsBlock";
|
||||
import { SettingItem } from "./SettingItem/SettingItem";
|
||||
import { TQuestionEntity, TTags } from "../AmoCRMModal";
|
||||
|
||||
type SettingsBlockProps = {
|
||||
type AmoSettingsBlockProps = {
|
||||
stepTitles: string[];
|
||||
setStep: (value: number) => void;
|
||||
setIsSettingsBlock: (value: boolean) => void;
|
||||
@ -17,7 +17,7 @@ type SettingsBlockProps = {
|
||||
tags: TTags;
|
||||
};
|
||||
|
||||
export const SettingsBlock: FC<SettingsBlockProps> = ({
|
||||
export const AmoSettingsBlock: FC<AmoSettingsBlockProps> = ({
|
||||
stepTitles,
|
||||
setStep,
|
||||
setIsSettingsBlock,
|
||||
Loading…
Reference in New Issue
Block a user