refactor update
This commit is contained in:
parent
143a8d1e57
commit
32bd57a113
@ -49,5 +49,8 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
||||
|
29
src/App.tsx
Normal file
29
src/App.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
|
||||
import { Header } from "./components/ui/Header";
|
||||
import { HomeSection } from "./components/HomeSection";
|
||||
import { EarnMoreSection } from "./components/EarnMoreSection";
|
||||
import { InstallSection } from "./components/InstallSection";
|
||||
import { TariffWrapper } from "./components/lib/TariffWrapper";
|
||||
import { ManagersSection } from "./components/ManagersSection";
|
||||
import { EffectSection } from "./components/EffectSection";
|
||||
import Widget from "./components/widget/widget";
|
||||
import Footer from "./components/ui/Footer";
|
||||
import FloatingSupportChat from "./components/floatingSupportChat/FloatingSupportChat";
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<HomeSection />
|
||||
<EarnMoreSection />
|
||||
<InstallSection />
|
||||
<TariffWrapper />
|
||||
<ManagersSection />
|
||||
<EffectSection />
|
||||
<Widget />
|
||||
<Footer />
|
||||
<FloatingSupportChat />
|
||||
</>
|
||||
);
|
||||
}
|
19
src/__mocks__/backendErrorMessage.ts
Normal file
19
src/__mocks__/backendErrorMessage.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export interface BackendErrorMessage {
|
||||
"user not found": string;
|
||||
"invalid password": string;
|
||||
"field <password> is empty": string;
|
||||
"field <login> is empty": string;
|
||||
"field <email> is empty": string;
|
||||
"field <phoneNumber> is empty": string;
|
||||
"user with this email or login is exist": string;
|
||||
}
|
||||
|
||||
export const backendErrorMessage: BackendErrorMessage = {
|
||||
"user not found": "Пользователь не найден",
|
||||
"invalid password": "Неправильный пароль",
|
||||
"field <password> is empty": 'Поле "Пароль" не заполнено',
|
||||
"field <login> is empty": 'Поле "Логин" не заполнено',
|
||||
"field <email> is empty": 'Поле "E-mail" не заполнено',
|
||||
"field <phoneNumber> is empty": 'Поле "Номер телефона" не заполнено',
|
||||
"user with this email or login is exist": "Пользователь уже существует",
|
||||
};
|
30
src/__mocks__/buttonMenu.ts
Normal file
30
src/__mocks__/buttonMenu.ts
Normal file
@ -0,0 +1,30 @@
|
||||
export const buttonMenu = [
|
||||
{
|
||||
menuTitle: "Menu1",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu2",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu3",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu4",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu5",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu6",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu7",
|
||||
pageUrl: "/",
|
||||
},
|
||||
];
|
22
src/__mocks__/cardsTour.ts
Normal file
22
src/__mocks__/cardsTour.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export const cardsTour = [
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного. Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: "../assets/image/Rectangle.png",
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного. Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: "../assets/image/Rectangle.png",
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного. Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: "../assets/image/Rectangle.png",
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного. Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: "../assets/image/Rectangle.png",
|
||||
},
|
||||
];
|
31
src/__mocks__/оfferCards.ts
Normal file
31
src/__mocks__/оfferCards.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import iconFader from "../assets/image/offers/Icon-Faders.svg";
|
||||
import ArrowsOut from "../assets/image/offers/ArrowsOut.svg";
|
||||
import ClipboardText from "../assets/image/offers/ClipboardText.svg";
|
||||
import FolderSimpleStar from "../assets/image/offers/FolderSimpleStar.svg";
|
||||
|
||||
export const OfferCards = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Последовательная настройка amoCRM под текущие задачи",
|
||||
text: "Объясним, расскажем и поможем\n Предлагаем понятное решение с четкими сроками",
|
||||
icon: ArrowsOut,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Доработка amoCRM с настройкой хайповых инструментов",
|
||||
text: "Вы путешествуете, а мы выполняем задачи\n Непрерывная коммуникация с вашей командой",
|
||||
icon: FolderSimpleStar,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Комплексная настройка amoCRM с реализацией вашего списка задач",
|
||||
text: "Сэкономим ваше время предложим лучшее решение\n Точно в срок без лишних вопросов",
|
||||
icon: iconFader,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Интеграция amoCRM с сервисами по ТЗ заказчика",
|
||||
text: "Мы команда для реализации ваших идей\n Развернем инфраструктуру с разработкой и поддержкой",
|
||||
icon: ClipboardText,
|
||||
},
|
||||
];
|
28
src/app.js
28
src/app.js
@ -1,28 +0,0 @@
|
||||
import React from "react";
|
||||
import Header from "./components/ui/header";
|
||||
import Section1 from "./components/section-1";
|
||||
import EarnMore from "./components/earn-more.js";
|
||||
import Install from "./components/install.js";
|
||||
import Tariff from "./components/tariff.js";
|
||||
import Managers from "./components/managers.js";
|
||||
import Effect from "./components/effect.js";
|
||||
import Widget from "./components/widget/widget";
|
||||
import Footer from "./components/ui/footer";
|
||||
import FloatingSupportChat from "./components/floatingSupportChat/FloatingSupportChat";
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Section1 />
|
||||
<EarnMore />
|
||||
<Install />
|
||||
<Tariff />
|
||||
<Managers />
|
||||
<Effect />
|
||||
<Widget />
|
||||
<Footer />
|
||||
<FloatingSupportChat />
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
export default function CircleDoubleDown({ isUp = false }) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexShrink: 0,
|
||||
transform: isUp ? "scale(1, -1)" : undefined,
|
||||
}}
|
||||
>
|
||||
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16.9004 4C10.273 4 4.90039 9.37258 4.90039 16C4.90039 22.6274 10.273 28 16.9004 28C23.5278 28 28.9004 22.6274 28.9004 16C28.9004 9.37258 23.5278 4 16.9004 4Z"
|
||||
stroke="#252734"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.9004 21L16.9004 17L20.9004 21"
|
||||
stroke="#252734"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.9004 14L16.9004 10L20.9004 14"
|
||||
stroke="#252734"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
||||
}
|
44
src/assets/Icons/CircleDoubleDownIcon.tsx
Normal file
44
src/assets/Icons/CircleDoubleDownIcon.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { Box } from "@mui/material";
|
||||
import { FC } from "react";
|
||||
|
||||
interface Iprops {
|
||||
isUp: boolean;
|
||||
}
|
||||
|
||||
export const CircleDoubleDown: FC<Iprops> = ({ isUp = false }) => (
|
||||
<Box
|
||||
sx={{
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexShrink: 0,
|
||||
transform: isUp ? "scale(1, -1)" : undefined,
|
||||
}}
|
||||
>
|
||||
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16.9004 4C10.273 4 4.90039 9.37258 4.90039 16C4.90039 22.6274 10.273 28 16.9004 28C23.5278 28 28.9004 22.6274 28.9004 16C28.9004 9.37258 23.5278 4 16.9004 4Z"
|
||||
stroke="#252734"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.9004 21L16.9004 17L20.9004 21"
|
||||
stroke="#252734"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.9004 14L16.9004 10L20.9004 14"
|
||||
stroke="#252734"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
@ -1,17 +0,0 @@
|
||||
export const LockIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
style={{ position: "absolute", top: "4px" }}
|
||||
width="10"
|
||||
height="13"
|
||||
viewBox="0 0 10 13"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 6.5C10 5.78305 9.43937 5.2 8.75 5.2H8.125V3.25C8.125 1.45795 6.72312 0 5 0C3.27688 0 1.875 1.45795 1.875 3.25V5.2H1.25C0.560625 5.2 0 5.78305 0 6.5V11.7C0 12.4169 0.560625 13 1.25 13H8.75C9.43937 13 10 12.4169 10 11.7V6.5ZM3.125 3.25C3.125 2.1749 3.96625 1.3 5 1.3C6.03375 1.3 6.875 2.1749 6.875 3.25V5.2H3.125V3.25Z"
|
||||
fill="#9A9AAF"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
17
src/assets/Icons/LockIcon.tsx
Normal file
17
src/assets/Icons/LockIcon.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { FC } from "react";
|
||||
|
||||
export const LockIcon: FC = () => (
|
||||
<svg
|
||||
style={{ position: "absolute", top: "4px" }}
|
||||
width="10"
|
||||
height="13"
|
||||
viewBox="0 0 10 13"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 6.5C10 5.78305 9.43937 5.2 8.75 5.2H8.125V3.25C8.125 1.45795 6.72312 0 5 0C3.27688 0 1.875 1.45795 1.875 3.25V5.2H1.25C0.560625 5.2 0 5.78305 0 6.5V11.7C0 12.4169 0.560625 13 1.25 13H8.75C9.43937 13 10 12.4169 10 11.7V6.5ZM3.125 3.25C3.125 2.1749 3.96625 1.3 5 1.3C6.03375 1.3 6.875 2.1749 6.875 3.25V5.2H3.125V3.25Z"
|
||||
fill="#9A9AAF"
|
||||
/>
|
||||
</svg>
|
||||
);
|
@ -1,10 +0,0 @@
|
||||
export default function SendIcon({ style }) {
|
||||
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="45" height="45" viewBox="0 0 45 45" fill="none" style={style}>
|
||||
<circle cx="22.5" cy="22.5" r="22.5" fill="#944FEE" />
|
||||
<path d="M33.8489 22.1816L15.9232 12.1415C15.7722 12.0581 15.5994 12.0227 15.4277 12.0399C15.2561 12.0571 15.0938 12.1263 14.9624 12.2381C14.831 12.3498 14.7368 12.499 14.6923 12.6657C14.6478 12.8323 14.6551 13.0086 14.7133 13.171L18.0883 22.638C18.1627 22.8218 18.1627 23.0273 18.0883 23.2111L14.7133 32.6781C14.6551 32.8405 14.6478 33.0167 14.6923 33.1834C14.7368 33.3501 14.831 33.4992 14.9624 33.611C15.0938 33.7228 15.2561 33.7919 15.4277 33.8092C15.5994 33.8264 15.7722 33.791 15.9232 33.7076L33.8489 23.6675C33.9816 23.594 34.0922 23.4864 34.1693 23.3558C34.2463 23.2251 34.2869 23.0762 34.2869 22.9245C34.2869 22.7729 34.2463 22.624 34.1693 22.4933C34.0922 22.3627 33.9816 22.255 33.8489 22.1816V22.1816Z" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M18.1943 22.9248H24.9868" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
21
src/assets/Icons/SendIcon.tsx
Normal file
21
src/assets/Icons/SendIcon.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { CSSProperties, FC } from "react";
|
||||
|
||||
interface Iprops {
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
export const SendIcon: FC<Iprops> = ({ style }) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="45" height="45" viewBox="0 0 45 45" fill="none" style={style}>
|
||||
<circle cx="22.5" cy="22.5" r="22.5" fill="#944FEE" />
|
||||
<path
|
||||
d="M33.8489 22.1816L15.9232 12.1415C15.7722 12.0581 15.5994 12.0227 15.4277 12.0399C15.2561 12.0571 15.0938 12.1263 14.9624 12.2381C14.831 12.3498 14.7368 12.499 14.6923 12.6657C14.6478 12.8323 14.6551 13.0086 14.7133 13.171L18.0883 22.638C18.1627 22.8218 18.1627 23.0273 18.0883 23.2111L14.7133 32.6781C14.6551 32.8405 14.6478 33.0167 14.6923 33.1834C14.7368 33.3501 14.831 33.4992 14.9624 33.611C15.0938 33.7228 15.2561 33.7919 15.4277 33.8092C15.5994 33.8264 15.7722 33.791 15.9232 33.7076L33.8489 23.6675C33.9816 23.594 34.0922 23.4864 34.1693 23.3558C34.2463 23.2251 34.2869 23.0762 34.2869 22.9245C34.2869 22.7729 34.2463 22.624 34.1693 22.4933C34.0922 22.3627 33.9816 22.255 33.8489 22.1816V22.1816Z"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path d="M18.1943 22.9248H24.9868" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
|
||||
export default function UserCircleIcon() {
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<svg width="32" height="33" viewBox="0 0 32 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 28.5C22.6274 28.5 28 23.1274 28 16.5C28 9.87258 22.6274 4.5 16 4.5C9.37258 4.5 4 9.87258 4 16.5C4 23.1274 9.37258 28.5 16 28.5Z" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M16 20.5C18.7614 20.5 21 18.2614 21 15.5C21 12.7386 18.7614 10.5 16 10.5C13.2386 10.5 11 12.7386 11 15.5C11 18.2614 13.2386 20.5 16 20.5Z" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M7.97461 25.425C8.727 23.943 9.87506 22.6983 11.2915 21.8289C12.708 20.9595 14.3376 20.4992 15.9996 20.4992C17.6616 20.4992 19.2912 20.9595 20.7077 21.8289C22.1242 22.6983 23.2722 23.943 24.0246 25.425" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
||||
}
|
38
src/assets/Icons/UserCircleIcon.tsx
Normal file
38
src/assets/Icons/UserCircleIcon.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
export const UserCircleIcon = () => (
|
||||
<Box
|
||||
sx={{
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<svg width="32" height="33" viewBox="0 0 32 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16 28.5C22.6274 28.5 28 23.1274 28 16.5C28 9.87258 22.6274 4.5 16 4.5C9.37258 4.5 4 9.87258 4 16.5C4 23.1274 9.37258 28.5 16 28.5Z"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16 20.5C18.7614 20.5 21 18.2614 21 15.5C21 12.7386 18.7614 10.5 16 10.5C13.2386 10.5 11 12.7386 11 15.5C11 18.2614 13.2386 20.5 16 20.5Z"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.97461 25.425C8.727 23.943 9.87506 22.6983 11.2915 21.8289C12.708 20.9595 14.3376 20.4992 15.9996 20.4992C17.6616 20.4992 19.2912 20.9595 20.7077 21.8289C22.1242 22.6983 23.2722 23.943 24.0246 25.425"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</Box>
|
||||
);
|
@ -1,24 +0,0 @@
|
||||
|
||||
|
||||
function declension(number, declensions, cases = [2, 0, 1, 1, 1, 2]) {
|
||||
return declensions[
|
||||
number % 100 > 4 && number % 100 < 20
|
||||
? 2
|
||||
: cases[number % 10 < 5 ? number % 10 : 5]
|
||||
];
|
||||
}
|
||||
|
||||
export function getDeclension(number, word) {
|
||||
switch (word) {
|
||||
case "шаблон":
|
||||
return declension(number, ["шаблон", "шаблона", "шаблонов"]);
|
||||
case "день":
|
||||
return declension(number, ["день", "дня", "дней"]);
|
||||
case "месяц":
|
||||
return declension(number, ["месяц", "месяца", "месяцев"]);
|
||||
case "год":
|
||||
return declension(number, ["год", "года", "лет"]);
|
||||
case "МБ":
|
||||
return "МБ";
|
||||
}
|
||||
};
|
@ -1,7 +1,8 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Box, Button, SvgIcon, TextField, Typography, useMediaQuery, useTheme, Modal } from "@mui/material";
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
import { LockIcon } from "../../assets/Icons/LockIcon";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import {
|
||||
sendContactForm,
|
||||
setContactFormNameField,
|
||||
@ -11,7 +12,8 @@ import {
|
||||
setIsContactFormOpen,
|
||||
useContactFormStore,
|
||||
} from "../../stores/contactForm";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { LockIcon } from "../../assets/Icons/LockIcon";
|
||||
|
||||
export const ContactFormModal = () => {
|
||||
const theme = useTheme();
|
||||
@ -119,7 +121,7 @@ export const ContactFormModal = () => {
|
||||
/>
|
||||
<TextField
|
||||
value={phoneNumberField}
|
||||
onChange={(e) => setContactFormPhoneNumberField(e.target.value)}
|
||||
onChange={(event) => setContactFormPhoneNumberField(event.target.value)}
|
||||
placeholder="Номер телефона"
|
||||
name="phoneNumber"
|
||||
fullWidth
|
||||
@ -145,7 +147,7 @@ export const ContactFormModal = () => {
|
||||
</Typography>
|
||||
<TextField
|
||||
value={telegramField}
|
||||
onChange={(e) => setContactFormTelegramField(e.target.value)}
|
||||
onChange={(event) => setContactFormTelegramField(event.target.value)}
|
||||
placeholder="Telegram"
|
||||
name="telegram"
|
||||
fullWidth
|
||||
@ -171,7 +173,7 @@ export const ContactFormModal = () => {
|
||||
</Typography>
|
||||
<TextField
|
||||
value={whatsappField}
|
||||
onChange={(e) => setContactFormWhatsappField(e.target.value)}
|
||||
onChange={(event) => setContactFormWhatsappField(event.target.value)}
|
||||
placeholder="Whatsapp"
|
||||
name="whatsapp"
|
||||
fullWidth
|
93
src/components/EarnMoreSection.tsx
Normal file
93
src/components/EarnMoreSection.tsx
Normal file
@ -0,0 +1,93 @@
|
||||
import React, { FC } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography, Button } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/SectionStyled";
|
||||
|
||||
import getMore1 from "../assets/image/getMore/1.png";
|
||||
import getMore2 from "../assets/image/getMore/2.png";
|
||||
import getMore3 from "../assets/image/getMore/3.png";
|
||||
import getMore4 from "../assets/image/getMore/4.png";
|
||||
import amoLabel from "../assets/image/amoLabel.png";
|
||||
import { Benefit } from "./lib/Benefit";
|
||||
|
||||
const BoxAdvantages = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down(490)]: {
|
||||
justifyContent: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
export const EarnMoreSection: FC = () => (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#7e2aea"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{ minHeight: "607px" }}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
height: "100%",
|
||||
color: "#ffffff",
|
||||
padding: "50px 10px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
fontSize={"36px"}
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "24px",
|
||||
md: "36px",
|
||||
},
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Зарабатывайте больше на 30%
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
fontSize={"18px"}
|
||||
sx={{
|
||||
fontWeight: "400",
|
||||
margin: "15px 0 70px 0",
|
||||
}}
|
||||
>
|
||||
благодаря оптимизации работы с документами
|
||||
</Typography>
|
||||
</Box>
|
||||
<BoxAdvantages
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Benefit pic={getMore1} text={"Сократите время на заполнение договоров"} />
|
||||
<Benefit pic={getMore2} text={"Исключите ошибки в документах"} />
|
||||
<Benefit pic={getMore3} text={"Увеличьте эффективность отдела продаж"} />
|
||||
<Benefit pic={getMore4} text={"Автоматизируйте документооборот"} />
|
||||
</BoxAdvantages>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "350px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
marginTop: "40px",
|
||||
fontWeight: "normal",
|
||||
backgroundColor: "#ffffff",
|
||||
color: "#000000",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<img alt={"AMOCRM"} src={amoLabel} style={{ marginRight: "15px" }} />
|
||||
Установить самостоятельно
|
||||
</Button>
|
||||
</SectionStyled>
|
||||
);
|
87
src/components/EffectSection.tsx
Normal file
87
src/components/EffectSection.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import React, { FC } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography, Button } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/SectionStyled";
|
||||
import { EffectCard } from "./ui/EffectCard";
|
||||
|
||||
const BoxToDo = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
}));
|
||||
|
||||
export const EffectSection: FC = () => (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#f2f3f7"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{ minHeight: "661px" }}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
height: "100%",
|
||||
color: "##f2f3f7",
|
||||
padding: "70px 10px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
sx={{
|
||||
fontWeight: "500",
|
||||
fontSize: {
|
||||
xxs: "24px",
|
||||
md: "36px",
|
||||
},
|
||||
maxWidth: "750px",
|
||||
}}
|
||||
>
|
||||
Эффективность отдела продаж возрастет на 30%
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
fontSize={"18px"}
|
||||
sx={{
|
||||
fontWeight: "400",
|
||||
margin: "15px 0 0 0",
|
||||
maxWidth: "450px",
|
||||
}}
|
||||
>
|
||||
а менеджеры будут довольны уменьшением количества рутины
|
||||
</Typography>
|
||||
</Box>
|
||||
<BoxToDo
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<EffectCard num={1} tit={"Заходим в сделку, открываем виджет getdoc"} subtit={""} />
|
||||
<EffectCard num={2} tit={"Выбираем типовой шаблон"} subtit={"(договор, счет, акт, счет-фактура и другие)"} />
|
||||
<EffectCard num={3} tit={"Выбираем нужный формат"} subtit={"docx, pdf"} />
|
||||
<EffectCard num={4} tit={"Готовый шаблон скачан на компьютер"} subtit={""} />
|
||||
</BoxToDo>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "353px",
|
||||
minHeight: "43px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "normal",
|
||||
backgroundColor: "#7E2AEA",
|
||||
color: "#ffffff",
|
||||
marginTop: "60px",
|
||||
}}
|
||||
>
|
||||
Загрузите инструкцию по установке
|
||||
</Button>
|
||||
</SectionStyled>
|
||||
);
|
140
src/components/HomeSection.tsx
Normal file
140
src/components/HomeSection.tsx
Normal file
@ -0,0 +1,140 @@
|
||||
import React, { FC } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import { Typography } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/SectionStyled";
|
||||
|
||||
import logotip from "../assets/Icons/black_logo_PenaHab.svg";
|
||||
import abstraction from "../assets/image/abstraction.png";
|
||||
import logoAmoCrm from "../assets/image/logoAmoCrm.png";
|
||||
|
||||
const BoxHead = styled(Box)(({ theme }) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
const Logo = styled("img")(({ theme }) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
display: "none",
|
||||
},
|
||||
}));
|
||||
|
||||
const AmoLogo = styled("img")(({ theme }) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "161px",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxImage = styled(Box)(({ theme }) => ({
|
||||
width: "60%",
|
||||
height: "100%",
|
||||
textAlign: "right",
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "294px",
|
||||
height: "auto",
|
||||
},
|
||||
}));
|
||||
|
||||
export const HomeSection: FC = () => (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#f2f3f7"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{
|
||||
minHeight: "628px",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: {
|
||||
xxs: "column-reverse",
|
||||
md: "row",
|
||||
},
|
||||
justifyContent: "center",
|
||||
alignItems: {
|
||||
xxs: "center",
|
||||
md: "",
|
||||
},
|
||||
height: "100%",
|
||||
padding: "30px 18px",
|
||||
paddingBottom: "70px",
|
||||
}}
|
||||
>
|
||||
<BoxHead
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "50%",
|
||||
height: "100%",
|
||||
justifyContent: "space-around",
|
||||
padding: {
|
||||
xxs: "0",
|
||||
md: "45px 0 75px 0",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "180px",
|
||||
height: {
|
||||
xxs: "auto",
|
||||
md: "69px",
|
||||
},
|
||||
backgroundSize: "contain",
|
||||
}}
|
||||
>
|
||||
<Logo src={logotip} alt="logo" />
|
||||
</Box>
|
||||
<Typography
|
||||
variant="h6"
|
||||
fontSize="calc(29px + 23 * (100vw / 1160))"
|
||||
sx={{
|
||||
lineHeight: {
|
||||
xxs: "40px",
|
||||
md: "70px",
|
||||
},
|
||||
marginTop: {
|
||||
xxs: "0",
|
||||
md: "20px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Шаблонизатор для <AmoLogo src={logoAmoCrm} alt="AmoCRM" />
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
lineHeight: "21.33px",
|
||||
maxWidth: "580px",
|
||||
margin: {
|
||||
xxs: "30px 0",
|
||||
md: "10px 0",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Виджет для генерации документов в 2 клика уберет рутину с договорами и актами за 2 минуты на основе данных из
|
||||
amoCRM и шаблонов документов компании в формате docx и pdf
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "355px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "normal",
|
||||
}}
|
||||
>
|
||||
Загрузить инструкцию по установке
|
||||
</Button>
|
||||
</BoxHead>
|
||||
<BoxImage>
|
||||
<img width={"100%"} src={abstraction} alt="" />
|
||||
</BoxImage>
|
||||
</SectionStyled>
|
||||
);
|
96
src/components/InstallSection.tsx
Normal file
96
src/components/InstallSection.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
import React, { FC } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography, Button } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import { ProfitCard } from "./ui/ProfitCard";
|
||||
|
||||
import SectionStyled from "./lib/SectionStyled";
|
||||
|
||||
import install1 from "../assets/image/install/1.png";
|
||||
import install2 from "../assets/image/install/2.png";
|
||||
import install3 from "../assets/image/install/3.png";
|
||||
import install4 from "../assets/image/install/4.png";
|
||||
import install5 from "../assets/image/install/5.png";
|
||||
import install6 from "../assets/image/install/6.png";
|
||||
import amoLabel from "../assets/image/amoLabel.png";
|
||||
|
||||
const BoxCard = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
justifyContent: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
export const InstallSection: FC = () => (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#f2f3f7"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{ minHeight: "911px" }}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
height: "100%",
|
||||
color: "#000000",
|
||||
padding: "55px 14px 63px 10px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography variant={"h6"} fontSize={"calc(29px + 1 * (100vw / 1160))"} sx={{ fontWeight: "500" }}>
|
||||
Установить самостоятельно
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
fontSize={"18px"}
|
||||
sx={{
|
||||
fontWeight: "400",
|
||||
margin: "15px 0 30px 0",
|
||||
maxWidth: "450px",
|
||||
}}
|
||||
>
|
||||
Сократите количество рабочих часов менеджеров по подготовке документов в 2 клика
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<BoxCard
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<ProfitCard pic={install1} tit={"Сократит количество ошибок"} subtit={"и уберет лишнюю нагрузку с менеджеров"} />
|
||||
<ProfitCard
|
||||
pic={install2}
|
||||
tit={"Все документы хранятся в CRM"}
|
||||
subtit={"компания не зависит от менеджера и его компьютера"}
|
||||
/>
|
||||
<ProfitCard pic={install3} tit={"Удобно использовать"} subtit={"разобраться с интерфейсом можно за 5 минут"} />
|
||||
<ProfitCard
|
||||
pic={install4}
|
||||
tit={"Не нужен программист"}
|
||||
subtit={"начать работу с виджетом можно самостоятельно"}
|
||||
/>
|
||||
<ProfitCard pic={install5} tit={"Дисциплинирует менеджеров"} subtit={"заполнять данные по клиенту"} />
|
||||
<ProfitCard pic={install6} tit={"Все данные в одной базе"} subtit={"их можно использовать повторно"} />
|
||||
</BoxCard>
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "350px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
marginTop: "40px",
|
||||
fontWeight: "normal",
|
||||
backgroundColor: "#7E2AEA",
|
||||
color: "#ffffff",
|
||||
}}
|
||||
>
|
||||
<img alt={"AMOCRM"} src={amoLabel} style={{ marginRight: "15px", filter: "invert(100%)" }} />
|
||||
Установить самостоятельно
|
||||
</Button>
|
||||
</SectionStyled>
|
||||
);
|
219
src/components/ManagersSection.tsx
Normal file
219
src/components/ManagersSection.tsx
Normal file
@ -0,0 +1,219 @@
|
||||
import { FC } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography } from "@mui/material";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
import SectionStyled from "./lib/SectionStyled";
|
||||
|
||||
import { BoxFich } from "./ui/BoxFich";
|
||||
|
||||
import amoLabel from "../assets/image/amoLabel.png";
|
||||
|
||||
export const ManagersSection: FC = () => (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#333647"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{ minHeight: "1528px" }}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
height: "100%",
|
||||
padding: "70px 10px",
|
||||
color: "#ffffff",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<Box marginBottom={"76px"}>
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "24px",
|
||||
md: "36px",
|
||||
},
|
||||
fontWeight: "500",
|
||||
lineHeight: {
|
||||
xxs: "28px",
|
||||
md: "43px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Менеджеры будут продавать больше
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21px",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
за счет автоматизации документооборота
|
||||
</Typography>
|
||||
</Box>
|
||||
<BoxFich>
|
||||
<Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#7e2aea",
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
}}
|
||||
>
|
||||
01
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
>
|
||||
Создавайте любые документы
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography
|
||||
component="div"
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
<ul>
|
||||
<li>договоры, акты, счета, накладные и др.</li>
|
||||
<li>в форматах docx или pdf</li>
|
||||
<li>загружайте свои шаблоны</li>
|
||||
<li>в любом количестве</li>
|
||||
<li>и скачивайте их в любое время</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</Box>
|
||||
</BoxFich>
|
||||
<BoxFich>
|
||||
<Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#7e2aea",
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
}}
|
||||
>
|
||||
02
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
>
|
||||
Удобно использовать
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography
|
||||
component="div"
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
<ul>
|
||||
<li>предпросмотр документа</li>
|
||||
<li>
|
||||
выделение подставляемых в шаблон значений
|
||||
<br />в предпросмотре
|
||||
</li>
|
||||
<li>примечание о создании документа</li>
|
||||
<li>и многое другое</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</Box>
|
||||
</BoxFich>
|
||||
<BoxFich>
|
||||
<Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#7e2aea",
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
}}
|
||||
>
|
||||
03
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
>
|
||||
PenaDoc умеет
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography
|
||||
component="div"
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
<ul>
|
||||
<li>склонять по падежам</li>
|
||||
<li>писать числа прописью</li>
|
||||
<li>писать дату прописью</li>
|
||||
<li>писать сумму прописью</li>
|
||||
<li>сокращать ФИО до инициалов</li>
|
||||
<li>несколько одновременных преобразований данных</li>
|
||||
<li>например: вычислить НДС и перевести его в строковый формат</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</Box>
|
||||
</BoxFich>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "350px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "normal",
|
||||
backgroundColor: "#7E2AEA",
|
||||
color: "#ffffff",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<img src={amoLabel} style={{ marginRight: "15px", filter: "invert(100%)" }} />
|
||||
Установить самостоятельно
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</SectionStyled>
|
||||
);
|
@ -1,143 +0,0 @@
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography, Button } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/section";
|
||||
|
||||
import getMore1 from "../assets/image/getMore/1.png";
|
||||
import getMore2 from "../assets/image/getMore/2.png";
|
||||
import getMore3 from "../assets/image/getMore/3.png";
|
||||
import getMore4 from "../assets/image/getMore/4.png";
|
||||
import amoLabel from "../assets/image/amoLabel.png";
|
||||
|
||||
const BoxAdvantages = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down(490)]: {
|
||||
justifyContent: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
function Square({ pic }) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "59px",
|
||||
height: "59px",
|
||||
marginBottom: "20px",
|
||||
/* backgroundColor: '#CBDADE', */
|
||||
borderRadius: "6px",
|
||||
backgroundImage: `url( ${pic} )`,
|
||||
backgroundSize: "90%",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const Benefit = ({ pic, text }) => (
|
||||
<Box
|
||||
sx={{
|
||||
minWidth: "250px",
|
||||
minHeight: "180px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
width: {
|
||||
xxs: "100%",
|
||||
md: "auto",
|
||||
},
|
||||
marginBottom: {
|
||||
xxs: "50px",
|
||||
md: "0",
|
||||
},
|
||||
borderBottom: {
|
||||
xxs: "2px solid white",
|
||||
md: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Square pic={pic} />
|
||||
<Typography variant={"span"} sx={{ width: "220px", fontSize: "24px", fontWeight: "500", lineHeight: "26px" }}>
|
||||
{text}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#7e2aea"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{ minHeight: "607px" }}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
height: "100%",
|
||||
color: "#ffffff",
|
||||
padding: "50px 10px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
fontSize={"36px"}
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "24px",
|
||||
md: "36px",
|
||||
},
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Зарабатывайте больше на 30%
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
fontSize={"18px"}
|
||||
sx={{
|
||||
fontWeight: "400",
|
||||
margin: "15px 0 70px 0",
|
||||
}}
|
||||
>
|
||||
благодаря оптимизации работы с документами
|
||||
</Typography>
|
||||
</Box>
|
||||
<BoxAdvantages
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Benefit pic={getMore1} text={"Сократите время на заполнение договоров"} />
|
||||
<Benefit pic={getMore2} text={"Исключите ошибки в документах"} />
|
||||
<Benefit pic={getMore3} text={"Увеличьте эффективность отдела продаж"} />
|
||||
<Benefit pic={getMore4} text={"Автоматизируйте документооборот"} />
|
||||
</BoxAdvantages>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "350px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
marginTop: "40px",
|
||||
fontWeight: "normal",
|
||||
backgroundColor: "#ffffff",
|
||||
color: "#000000",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<img alt={"AMOCRM"} src={amoLabel} style={{ marginRight: "15px" }} />
|
||||
Установить самостоятельно
|
||||
</Button>
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography, Button } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/section";
|
||||
|
||||
const BoxToDo = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
}));
|
||||
|
||||
function Square({ num }) {
|
||||
console.log(num);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "59px",
|
||||
height: "59px",
|
||||
/* backgroundColor: '#CBDADE', */
|
||||
borderRadius: "6px",
|
||||
fontSize: "50px",
|
||||
fontWeight: "500",
|
||||
color: "#7E2AEA",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
{num}{" "}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const EffCard = ({ num, tit, subtit }) => (
|
||||
<Box
|
||||
sx={{
|
||||
width: {
|
||||
xxs: "140px",
|
||||
md: "250px",
|
||||
},
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
marginTop: "15px",
|
||||
}}
|
||||
>
|
||||
<Square num={num} />
|
||||
<Typography
|
||||
variant={"span"}
|
||||
sx={{
|
||||
marginTop: "25px",
|
||||
fontSize: {
|
||||
xss: "18px",
|
||||
md: "24px",
|
||||
},
|
||||
fontWeight: "500",
|
||||
lineHeight: {
|
||||
xss: "21px",
|
||||
md: "26px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{tit}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "16px",
|
||||
md: "18px",
|
||||
},
|
||||
fontWeight: "400",
|
||||
marginTop: "8px",
|
||||
}}
|
||||
>
|
||||
{subtit}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#f2f3f7"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{ minHeight: "661px" }}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
height: "100%",
|
||||
color: "##f2f3f7",
|
||||
padding: "70px 10px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
sx={{
|
||||
fontWeight: "500",
|
||||
fontSize: {
|
||||
xxs: "24px",
|
||||
md: "36px",
|
||||
},
|
||||
maxWidth: "750px",
|
||||
}}
|
||||
>
|
||||
Эффективность отдела продаж возрастет на 30%
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
fontSize={"18px"}
|
||||
sx={{
|
||||
fontWeight: "400",
|
||||
margin: "15px 0 0 0",
|
||||
maxWidth: "450px",
|
||||
}}
|
||||
>
|
||||
а менеджеры будут довольны уменьшением количества рутины
|
||||
</Typography>
|
||||
</Box>
|
||||
<BoxToDo
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<EffCard num={1} tit={"Заходим в сделку, открываем виджет getdoc"} subtit={""} />
|
||||
<EffCard num={2} tit={"Выбираем типовой шаблон"} subtit={"(договор, счет, акт, счет-фактура и другие)"} />
|
||||
<EffCard num={3} tit={"Выбираем нужный формат"} subtit={"docx, pdf"} />
|
||||
<EffCard num={4} tit={"Готовый шаблон скачан на компьютер"} subtit={""} />
|
||||
</BoxToDo>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "353px",
|
||||
minHeight: "43px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "normal",
|
||||
backgroundColor: "#7E2AEA",
|
||||
color: "#ffffff",
|
||||
marginTop: "60px",
|
||||
}}
|
||||
>
|
||||
Загрузите инструкцию по установке
|
||||
</Button>
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
@ -1,14 +1,31 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
FormControl,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
InputBase,
|
||||
SxProps,
|
||||
Theme,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import {
|
||||
useTicketMessages,
|
||||
getMessageFromFetchError,
|
||||
useSSESubscription,
|
||||
useEventListener,
|
||||
createTicket,
|
||||
throttle,
|
||||
makeRequest,
|
||||
} from "@frontend/kitui";
|
||||
|
||||
import ChatMessage from "./ChatMessage";
|
||||
|
||||
import {
|
||||
addOrUpdateUnauthMessages,
|
||||
useUnauthTicketStore,
|
||||
@ -17,22 +34,15 @@ import {
|
||||
setUnauthSessionData,
|
||||
setIsMessageSending,
|
||||
} from "../../stores/unauthTicket";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import ChatMessage from "./ChatMessage";
|
||||
import SendIcon from "../../assets/Icons/SendIcon";
|
||||
import UserCircleIcon from "../../assets/Icons/UserCircleIcon";
|
||||
import { throttle } from "@frontend/kitui";
|
||||
import { makeRequest } from "@frontend/kitui";
|
||||
import {
|
||||
useTicketMessages,
|
||||
getMessageFromFetchError,
|
||||
useSSESubscription,
|
||||
useEventListener,
|
||||
createTicket,
|
||||
} from "@frontend/kitui";
|
||||
|
||||
export default function Chat({ sx }) {
|
||||
import { SendIcon } from "../../assets/Icons/SendIcon";
|
||||
import { UserCircleIcon } from "../../assets/Icons/UserCircleIcon";
|
||||
|
||||
interface ChatProps {
|
||||
sx: SxProps<Theme>;
|
||||
}
|
||||
|
||||
export default function Chat({ sx }: ChatProps) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const [messageField, setMessageField] = useState("");
|
||||
@ -43,7 +53,8 @@ export default function Chat({ sx }) {
|
||||
const isMessageSending = useUnauthTicketStore((state) => state.isMessageSending);
|
||||
const isPreventAutoscroll = useUnauthTicketStore((state) => state.isPreventAutoscroll);
|
||||
const lastMessageId = useUnauthTicketStore((state) => state.lastMessageId);
|
||||
const chatBoxRef = useRef(null);
|
||||
|
||||
const chatBoxRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const fetchState = useTicketMessages({
|
||||
url: "https://admin.pena.digital/heruvym/getMessages",
|
||||
@ -98,7 +109,7 @@ export default function Chat({ sx }) {
|
||||
|
||||
if (!isPreventAutoscroll) {
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
scrollToBottom("auto");
|
||||
}, 50);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@ -158,7 +169,7 @@ export default function Chat({ sx }) {
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToBottom(behavior) {
|
||||
function scrollToBottom(behavior: "auto" | "smooth") {
|
||||
if (!chatBoxRef.current) return;
|
||||
|
||||
const chatBox = chatBoxRef.current;
|
||||
@ -169,7 +180,7 @@ export default function Chat({ sx }) {
|
||||
});
|
||||
}
|
||||
|
||||
const handleTextfieldKeyPress = (e) => {
|
||||
const handleTextfieldKeyPress = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSendMessage();
|
||||
@ -241,7 +252,7 @@ export default function Chat({ sx }) {
|
||||
}}
|
||||
>
|
||||
{sessionData &&
|
||||
messages.map((message) => (
|
||||
messages.map((message: { id: string; message: string; created_at: string; user_id: string }) => (
|
||||
<ChatMessage
|
||||
unAuthenticated
|
||||
key={message.id}
|
@ -1,7 +1,25 @@
|
||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Box, PaletteColor, PaletteColorOptions, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { isDateToday } from "./date";
|
||||
|
||||
export default function ChatMessage({ unAuthenticated = false, isSelf, text, createdAt }) {
|
||||
interface ChatMessageProps {
|
||||
unAuthenticated?: boolean;
|
||||
isSelf: boolean;
|
||||
text: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
declare module "@mui/material/styles" {
|
||||
interface Palette {
|
||||
grey2: PaletteColor;
|
||||
grey3: PaletteColor;
|
||||
}
|
||||
interface PaletteOptions {
|
||||
grey2?: PaletteColorOptions;
|
||||
grey3?: PaletteColorOptions;
|
||||
}
|
||||
}
|
||||
|
||||
export default function ChatMessage({ unAuthenticated = false, isSelf, text, createdAt }: ChatMessageProps) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { Box, Fab, Typography } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import CircleDoubleDown from "../../assets/Icons/CircleDoubleDownIcon";
|
||||
import Chat from "./Chat";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { Box, Fab, Typography } from "@mui/material";
|
||||
|
||||
import Chat from "./Chat";
|
||||
|
||||
import { CircleDoubleDown } from "../../assets/Icons/CircleDoubleDownIcon";
|
||||
|
||||
export default function FloatingSupportChat() {
|
||||
const location = useLocation();
|
@ -1,6 +1,6 @@
|
||||
import { getDeclension } from "../../assets/Icons/declension.js";
|
||||
import { getDeclension } from "./declension";
|
||||
|
||||
export function isDateToday(date) {
|
||||
export function isDateToday(date: Date): boolean {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
return date.getTime() > today.getTime();
|
||||
@ -9,7 +9,7 @@ export function isDateToday(date) {
|
||||
const avgDaysInMonth = 30.43692;
|
||||
const avgDaysInYear = 365.242199;
|
||||
|
||||
export function formatDateWithDeclention(numberOfDays) {
|
||||
export function formatDateWithDeclention(numberOfDays: number): string {
|
||||
if (numberOfDays === 0) return "0 дней";
|
||||
|
||||
const years = Math.floor(numberOfDays / avgDaysInYear);
|
18
src/components/floatingSupportChat/declension.ts
Normal file
18
src/components/floatingSupportChat/declension.ts
Normal file
@ -0,0 +1,18 @@
|
||||
function declension(number: number, declensions: string[], cases = [2, 0, 1, 1, 1, 2]) {
|
||||
return declensions[number % 100 > 4 && number % 100 < 20 ? 2 : cases[number % 10 < 5 ? number % 10 : 5]];
|
||||
}
|
||||
|
||||
export function getDeclension(number: number, word: string) {
|
||||
switch (word) {
|
||||
case "шаблон":
|
||||
return declension(number, ["шаблон", "шаблона", "шаблонов"]);
|
||||
case "день":
|
||||
return declension(number, ["день", "дня", "дней"]);
|
||||
case "месяц":
|
||||
return declension(number, ["месяц", "месяца", "месяцев"]);
|
||||
case "год":
|
||||
return declension(number, ["год", "года", "лет"]);
|
||||
case "МБ":
|
||||
return "МБ";
|
||||
}
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography, Button } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/section";
|
||||
|
||||
import install1 from "../assets/image/install/1.png";
|
||||
import install2 from "../assets/image/install/2.png";
|
||||
import install3 from "../assets/image/install/3.png";
|
||||
import install4 from "../assets/image/install/4.png";
|
||||
import install5 from "../assets/image/install/5.png";
|
||||
import install6 from "../assets/image/install/6.png";
|
||||
import amoLabel from "../assets/image/amoLabel.png";
|
||||
|
||||
const BoxCard = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
justifyContent: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
function Square({ pic }) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "59px",
|
||||
height: "59px",
|
||||
/* backgroundColor: '#CBDADE', */
|
||||
borderRadius: "6px",
|
||||
backgroundImage: `url( ${pic} )`,
|
||||
backgroundSize: "70%",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundColor: "#ffffff",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const ProfitCard = ({ pic, tit, subtit }) => (
|
||||
<Box
|
||||
sx={{
|
||||
width: {
|
||||
xxs: "100%",
|
||||
sm: "250px",
|
||||
lg: "300px",
|
||||
},
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: "40px",
|
||||
}}
|
||||
>
|
||||
<Square pic={pic} />
|
||||
<Typography
|
||||
variant={"span"}
|
||||
paddingBottom={"0px"}
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "20px",
|
||||
md: "24px",
|
||||
},
|
||||
fontWeight: "500",
|
||||
lineHeight: "26px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
{tit}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"span"}
|
||||
paddingBottom={"0px"}
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "26px",
|
||||
color: "#4D4D4D",
|
||||
}}
|
||||
>
|
||||
{subtit}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#f2f3f7"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{ minHeight: "911px" }}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
height: "100%",
|
||||
color: "#000000",
|
||||
padding: "55px 14px 63px 10px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography variant={"h6"} fontSize={"calc(29px + 1 * (100vw / 1160))"} sx={{ fontWeight: "500" }}>
|
||||
Установить самостоятельно
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
fontSize={"18px"}
|
||||
sx={{
|
||||
fontWeight: "400",
|
||||
margin: "15px 0 30px 0",
|
||||
maxWidth: "450px",
|
||||
}}
|
||||
>
|
||||
Сократите количество рабочих часов менеджеров по подготовке документов в 2 клика
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<BoxCard
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<ProfitCard
|
||||
pic={install1}
|
||||
tit={"Сократит количество ошибок"}
|
||||
subtit={"и уберет лишнюю нагрузку с менеджеров"}
|
||||
/>
|
||||
<ProfitCard
|
||||
pic={install2}
|
||||
tit={"Все документы хранятся в CRM"}
|
||||
subtit={"компания не зависит от менеджера и его компьютера"}
|
||||
/>
|
||||
<ProfitCard pic={install3} tit={"Удобно использовать"} subtit={"разобраться с интерфейсом можно за 5 минут"} />
|
||||
<ProfitCard
|
||||
pic={install4}
|
||||
tit={"Не нужен программист"}
|
||||
subtit={"начать работу с виджетом можно самостоятельно"}
|
||||
/>
|
||||
<ProfitCard pic={install5} tit={"Дисциплинирует менеджеров"} subtit={"заполнять данные по клиенту"} />
|
||||
<ProfitCard pic={install6} tit={"Все данные в одной базе"} subtit={"их можно использовать повторно"} />
|
||||
</BoxCard>
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "350px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
marginTop: "40px",
|
||||
fontWeight: "normal",
|
||||
backgroundColor: "#7E2AEA",
|
||||
color: "#ffffff",
|
||||
}}
|
||||
>
|
||||
<img alt={"AMOCRM"} src={amoLabel} style={{ marginRight: "15px", filter: "invert(100%)" }} />
|
||||
Установить самостоятельно
|
||||
</Button>
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
50
src/components/lib/Benefit.tsx
Normal file
50
src/components/lib/Benefit.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import { FC } from "react";
|
||||
import { Typography } from "@mui/material";
|
||||
import { Box } from "@mui/system";
|
||||
|
||||
import { Square } from "components/ui/Squre";
|
||||
|
||||
interface BenefitProps {
|
||||
pic: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const Benefit: FC<BenefitProps> = ({ pic, text }) => (
|
||||
<Box
|
||||
sx={{
|
||||
minWidth: "250px",
|
||||
minHeight: "180px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
width: {
|
||||
xxs: "100%",
|
||||
md: "auto",
|
||||
},
|
||||
marginBottom: {
|
||||
xxs: "50px",
|
||||
md: "0",
|
||||
},
|
||||
borderBottom: {
|
||||
xxs: "2px solid white",
|
||||
md: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Square
|
||||
sx={{
|
||||
width: "59px",
|
||||
height: "59px",
|
||||
marginBottom: "20px",
|
||||
/* backgroundColor: '#CBDADE', */
|
||||
borderRadius: "6px",
|
||||
backgroundImage: `url( ${pic} )`,
|
||||
backgroundSize: "90%",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
pic={pic}
|
||||
/>
|
||||
<Typography sx={{ width: "220px", fontSize: "24px", fontWeight: "500", lineHeight: "26px" }}>{text}</Typography>
|
||||
</Box>
|
||||
);
|
49
src/components/lib/CardItemTour.tsx
Normal file
49
src/components/lib/CardItemTour.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { FC } from "react";
|
||||
|
||||
interface CardItemTourProprs {
|
||||
title: string;
|
||||
text: string;
|
||||
image: string;
|
||||
}
|
||||
|
||||
export const CardItemTour: FC<CardItemTourProprs> = ({ title, text, image }) => (
|
||||
<Box
|
||||
sx={{
|
||||
padding: "80px 10px",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "space-between",
|
||||
gap: "20px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "20px",
|
||||
background: "none",
|
||||
maxWidth: "450px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" sx={{ fontSize: "20px", fontWeight: "500", lineHeight: "24px" }}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="h5" sx={{ fontSize: "18px", fontWeight: "400", lineHeight: "21px" }}>
|
||||
{text}
|
||||
</Typography>
|
||||
</Box>
|
||||
<img
|
||||
style={{
|
||||
width: "100%",
|
||||
borderRadius: "12px",
|
||||
maxWidth: "560px",
|
||||
boxShadow:
|
||||
"0px 100px 309px rgba(210, 208, 225, 0.24), 0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525), 0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066), 0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12), 0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343), 0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)",
|
||||
}}
|
||||
src={image}
|
||||
alt={image}
|
||||
/>
|
||||
</Box>
|
||||
);
|
43
src/components/lib/CardItemVideo.tsx
Normal file
43
src/components/lib/CardItemVideo.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { Typography } from "@mui/material";
|
||||
import { Box } from "@mui/system";
|
||||
import ButtonPlay from "../../assets/Icons/ButtonPlay.svg";
|
||||
import { FC } from "react";
|
||||
|
||||
interface CardItemVideoProps {
|
||||
text: string;
|
||||
title: string;
|
||||
image: string;
|
||||
}
|
||||
|
||||
export const CardItemVideo: FC<CardItemVideoProps> = ({ text, title, image }) => {
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ maxWidth: "560px", display: "flex", flexDirection: "column", gap: "20px" }}>
|
||||
<Box sx={{ position: "relative" }}>
|
||||
<img style={{ width: "100%" }} src={image} alt="" />
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
maxHeight: "340px",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<img src={ButtonPlay} alt="" />
|
||||
</Box>
|
||||
</Box>
|
||||
<Typography variant="h4" sx={{ fontSize: "20px", fontWeight: "500", lineHeight: "24px", marginTop: "10px" }}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="h5" sx={{ fontSize: "18px", fontWeight: "400", lineHeight: "21px" }}>
|
||||
{text}
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
21
src/components/lib/Hamburger.tsx
Normal file
21
src/components/lib/Hamburger.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { useState } from "react";
|
||||
import { Drawer, IconButton } from "@mui/material";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
|
||||
import LoginButton from "components/ui/LoginButton";
|
||||
import { Navigate } from "components/ui/Navigate";
|
||||
|
||||
export const Hamburger = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<IconButton component="button">
|
||||
<MenuIcon onClick={() => setOpen(true)} />
|
||||
</IconButton>
|
||||
<Drawer open={open} onClose={() => setOpen(false)}>
|
||||
<Navigate />
|
||||
<LoginButton />
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
40
src/components/lib/SectionStyled.tsx
Normal file
40
src/components/lib/SectionStyled.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { ReactNode } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { SxProps, Theme } from "@mui/system";
|
||||
|
||||
interface SectionProps {
|
||||
tag?: React.ElementType;
|
||||
bg?: string;
|
||||
sxsect?: SxProps<Theme>;
|
||||
mwidth?: string | number;
|
||||
sxcont?: SxProps<Theme>;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export default function SectionStyled({ tag = "section", bg, sxsect, mwidth, sxcont, children }: SectionProps) {
|
||||
return (
|
||||
<Box
|
||||
component={tag}
|
||||
backgroundColor={bg}
|
||||
sx={{
|
||||
width: "100%",
|
||||
fontFamily: "Rubik",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
...sxsect,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
maxWidth={mwidth}
|
||||
sx={{
|
||||
width: "100%",
|
||||
padding: "0 5px",
|
||||
...sxcont,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
99
src/components/lib/TariffWrapper.tsx
Normal file
99
src/components/lib/TariffWrapper.tsx
Normal file
@ -0,0 +1,99 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
import SectionStyled from "./SectionStyled";
|
||||
|
||||
import { Tariff } from "../ui/Tariff";
|
||||
|
||||
import tariff1 from "../../assets/image/tariffs/1.png";
|
||||
import tariff2 from "../../assets/image/tariffs/2.png";
|
||||
import tariff3 from "../../assets/image/tariffs/3.png";
|
||||
|
||||
export const TariffWrapper = () => (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#ffffff"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{
|
||||
minHeight: "544px",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
padding: "70px 10px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h4"
|
||||
fontWeight={"500"}
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "24px",
|
||||
md: "36px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Выберите удобный тариф
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
marginTop: "40px",
|
||||
}}
|
||||
>
|
||||
<Tariff icon={tariff1} caption={"Тариф на время"} text={"безлимит на 1 месяц, 3, 6, 12"} />
|
||||
<Tariff
|
||||
icon={tariff2}
|
||||
caption={"Тариф на объем"}
|
||||
text={"200 шаблонов, 1000 шаблонов, 5000 шаблонов, 10 000 шаблонов"}
|
||||
/>
|
||||
<Tariff icon={tariff3} caption={"Кастом"} text={"Текст-заполнитель — это текст, который имеет "} />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
paddingBottom: "10px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h4"
|
||||
fontWeight={"400"}
|
||||
fontSize={"18px"}
|
||||
sx={{
|
||||
marginTop: "30px",
|
||||
fontWeight: "400",
|
||||
}}
|
||||
>
|
||||
Или попробуйте наш
|
||||
<Box
|
||||
component="span"
|
||||
sx={{
|
||||
color: "#7E2AEA",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
бесплатный план
|
||||
</Box>
|
||||
</Typography>
|
||||
</Box>
|
||||
</SectionStyled>
|
||||
);
|
@ -1,30 +0,0 @@
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
export default function Section(props) {
|
||||
return (
|
||||
<Box
|
||||
component={props.tag}
|
||||
backgroundColor={props.bg}
|
||||
sx={{
|
||||
width: "100%",
|
||||
fontFamily: "Rubik",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
...props.sxsect,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
maxWidth={props.mwidth}
|
||||
sx={{
|
||||
width: "100%",
|
||||
padding: "0 5px",
|
||||
...props.sxcont,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
@ -1,270 +0,0 @@
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography } from "@mui/material";
|
||||
import Button from "@mui/material/Button";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/section";
|
||||
|
||||
import Desktop from "../assets/image/Desktop.png";
|
||||
import amoLabel from "../assets/image/amoLabel.png";
|
||||
|
||||
const BoxText = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down(1100)]: {
|
||||
width: "100%",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxImage = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down(1100)]: {
|
||||
width: "100%",
|
||||
padding: "0",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxButton = styled("div")(({ theme }) => ({}));
|
||||
|
||||
function BoxFich(props) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: "300px",
|
||||
backgroundColor: "#252734",
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
marginBottom: "75px",
|
||||
padding: "0 15px",
|
||||
}}
|
||||
>
|
||||
<BoxText
|
||||
sx={{
|
||||
width: "50%",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</BoxText>
|
||||
<BoxImage
|
||||
sx={{
|
||||
padding: "39px 39px 0 0",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<img src={Desktop} />
|
||||
</BoxImage>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#333647"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{ minHeight: "1528px" }}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
height: "100%",
|
||||
padding: "70px 10px",
|
||||
color: "#ffffff",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<Box marginBottom={"76px"}>
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "24px",
|
||||
md: "36px",
|
||||
},
|
||||
fontWeight: "500",
|
||||
lineHeight: {
|
||||
xxs: "28px",
|
||||
md: "43px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Менеджеры будут продавать больше
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21px",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
за счет автоматизации документооборота
|
||||
</Typography>
|
||||
</Box>
|
||||
<BoxFich>
|
||||
<Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#7e2aea",
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
}}
|
||||
>
|
||||
01
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
>
|
||||
Создавайте любые документы
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography
|
||||
component="div"
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
<ul>
|
||||
<li>договоры, акты, счета, накладные и др.</li>
|
||||
<li>в форматах docx или pdf</li>
|
||||
<li>загружайте свои шаблоны</li>
|
||||
<li>в любом количестве</li>
|
||||
<li>и скачивайте их в любое время</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</Box>
|
||||
</BoxFich>
|
||||
<BoxFich>
|
||||
<Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#7e2aea",
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
}}
|
||||
>
|
||||
02
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
>
|
||||
Удобно использовать
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography
|
||||
component="div"
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
<ul>
|
||||
<li>предпросмотр документа</li>
|
||||
<li>
|
||||
выделение подставляемых в шаблон значений
|
||||
<br />в предпросмотре
|
||||
</li>
|
||||
<li>примечание о создании документа</li>
|
||||
<li>и многое другое</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</Box>
|
||||
</BoxFich>
|
||||
<BoxFich>
|
||||
<Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#7e2aea",
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
}}
|
||||
>
|
||||
03
|
||||
</Typography>
|
||||
<Typography
|
||||
variant={"h6"}
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
lineHeight: "28.44px",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
>
|
||||
PenaDoc умеет
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography
|
||||
component="div"
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
<ul>
|
||||
<li>склонять по падежам</li>
|
||||
<li>писать числа прописью</li>
|
||||
<li>писать дату прописью</li>
|
||||
<li>писать сумму прописью</li>
|
||||
<li>сокращать ФИО до инициалов</li>
|
||||
<li>несколько одновременных преобразований данных</li>
|
||||
<li>например: вычислить НДС и перевести его в строковый формат</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</Box>
|
||||
</BoxFich>
|
||||
<BoxButton>
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "350px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "normal",
|
||||
backgroundColor: "#7E2AEA",
|
||||
color: "#ffffff",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
>
|
||||
<img src={amoLabel} style={{ marginRight: "15px", filter: "invert(100%)" }} />
|
||||
Установить самостоятельно
|
||||
</Button>
|
||||
</BoxButton>
|
||||
</Box>
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import { Typography } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/section";
|
||||
|
||||
import logotip from "../assets/Icons/black_logo_PenaHab.svg";
|
||||
import abstraction from "../assets/image/abstraction.png";
|
||||
import logoAmoCrm from "../assets/image/logoAmoCrm.png";
|
||||
|
||||
const BoxHead = styled(Box)(({ theme }) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
const Logo = styled("img")(({ theme }) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
display: "none",
|
||||
},
|
||||
}));
|
||||
|
||||
const AmoLogo = styled("img")(({ theme }) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "161px",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxImage = styled(Box)(({ theme }) => ({
|
||||
width: "60%",
|
||||
height: "100%",
|
||||
textAlign: "right",
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "294px",
|
||||
height: "auto",
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#f2f3f7"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{
|
||||
minHeight: "628px",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: {
|
||||
xxs: "column-reverse",
|
||||
md: "row",
|
||||
},
|
||||
justifyContent: "center",
|
||||
alignItems: {
|
||||
xxs: "center",
|
||||
md: "",
|
||||
},
|
||||
height: "100%",
|
||||
padding: "30px 18px",
|
||||
paddingBottom: "70px",
|
||||
}}
|
||||
>
|
||||
<BoxHead
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "50%",
|
||||
height: "100%",
|
||||
justifyContent: "space-around",
|
||||
padding: {
|
||||
xxs: "0",
|
||||
md: "45px 0 75px 0",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "180px",
|
||||
height: {
|
||||
xxs: "auto",
|
||||
md: "69px",
|
||||
},
|
||||
backgroundSize: "contain",
|
||||
}}
|
||||
>
|
||||
<Logo src={logotip} alt="logo" />
|
||||
</Box>
|
||||
<Typography
|
||||
variant="h6"
|
||||
fontSize="calc(29px + 23 * (100vw / 1160))"
|
||||
sx={{
|
||||
lineHeight: {
|
||||
xxs: "40px",
|
||||
md: "70px",
|
||||
},
|
||||
marginTop: {
|
||||
xxs: "0",
|
||||
md: "20px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Шаблонизатор для <AmoLogo src={logoAmoCrm} alt="AmoCRM" />
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
lineHeight: "21.33px",
|
||||
maxWidth: "580px",
|
||||
margin: {
|
||||
xxs: "30px 0",
|
||||
md: "10px 0",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Виджет для генерации документов в 2 клика уберет рутину с договорами и актами за 2 минуты на основе данных из
|
||||
amoCRM и шаблонов документов компании в формате docx и pdf
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
component="button"
|
||||
sx={{
|
||||
maxWidth: "355px",
|
||||
textTransform: "none",
|
||||
borderRadius: "8px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "normal",
|
||||
}}
|
||||
>
|
||||
Загрузить инструкцию по установке
|
||||
</Button>
|
||||
</BoxHead>
|
||||
<BoxImage>
|
||||
<img width={"100%"} src={abstraction} alt="" />
|
||||
</BoxImage>
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Typography } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "./lib/section";
|
||||
|
||||
import tariff1 from "../assets/image/tariffs/1.png";
|
||||
import tariff2 from "../assets/image/tariffs/2.png";
|
||||
import tariff3 from "../assets/image/tariffs/3.png";
|
||||
|
||||
const BoxCard = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down(730)]: {
|
||||
width: "100%",
|
||||
},
|
||||
}));
|
||||
|
||||
function Square({ pic }) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "36px",
|
||||
height: "36px",
|
||||
backgroundColor: "#7E2AEA",
|
||||
borderRadius: "6px",
|
||||
backgroundImage: `url( ${pic} )`,
|
||||
backgroundSize: "70%",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function Tariff({ icon, caption, text }) {
|
||||
return (
|
||||
<BoxCard
|
||||
sx={{
|
||||
width: "360px",
|
||||
minHeight: "250px",
|
||||
backgroundColor: "#ffffff",
|
||||
borderRadius: "12px",
|
||||
padding: "20px",
|
||||
marginBottom: "10px",
|
||||
boxShadow: `0px 100px 309px rgba(210, 208, 225, 0.24),
|
||||
0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525),
|
||||
0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066),
|
||||
0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12),
|
||||
0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343),
|
||||
0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)`,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ height: "50%" }}>
|
||||
<Square pic={icon} />
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
marginTop: "10px",
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
{caption}{" "}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
marginTop: "10px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21.3px",
|
||||
color: "#4D4D4D",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
{text}{" "}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ height: "50%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "180px",
|
||||
height: "44px",
|
||||
padding: "8px 40px 10px 40px",
|
||||
border: "1px solid #7E2AEA",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: "5px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
cursor: "pointer",
|
||||
marginTop: "60px",
|
||||
}}
|
||||
>
|
||||
Подробнее
|
||||
</Box>
|
||||
</Box>
|
||||
</BoxCard>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"section"}
|
||||
bg={"#ffffff"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{
|
||||
minHeight: "544px",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
padding: "70px 10px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h4"
|
||||
fontWeight={"500"}
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "24px",
|
||||
md: "36px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Выберите удобный тариф
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
marginTop: "40px",
|
||||
}}
|
||||
>
|
||||
<Tariff icon={tariff1} caption={"Тариф на время"} text={"безлимит на 1 месяц, 3, 6, 12"} />
|
||||
<Tariff
|
||||
icon={tariff2}
|
||||
caption={"Тариф на объем"}
|
||||
text={"200 шаблонов, 1000 шаблонов, 5000 шаблонов, 10 000 шаблонов"}
|
||||
/>
|
||||
<Tariff icon={tariff3} caption={"Кастом"} text={"Текст-заполнитель — это текст, который имеет "} />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
paddingBottom: "10px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h4"
|
||||
fontWeight={"400"}
|
||||
fontSize={"18px"}
|
||||
sx={{
|
||||
marginTop: "30px",
|
||||
fontWeight: "400",
|
||||
}}
|
||||
>
|
||||
Или попробуйте наш{" "}
|
||||
<Box
|
||||
component="span"
|
||||
sx={{
|
||||
color: "#7E2AEA",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
бесплатный план
|
||||
</Box>
|
||||
</Typography>
|
||||
</Box>
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
54
src/components/ui/BoxFich.tsx
Normal file
54
src/components/ui/BoxFich.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { FC, ReactNode } from "react";
|
||||
|
||||
import { Box, styled } from "@mui/system";
|
||||
|
||||
import Desktop from "../../assets/image/Desktop.png";
|
||||
|
||||
const BoxText = styled("div")(({ theme }: any) => ({
|
||||
[theme.breakpoints.down(1100)]: {
|
||||
width: "100%",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxImage = styled("div")(({ theme }: any) => ({
|
||||
[theme.breakpoints.down(1100)]: {
|
||||
width: "100%",
|
||||
padding: "0",
|
||||
},
|
||||
}));
|
||||
|
||||
interface BoxFichProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const BoxFich: FC<BoxFichProps> = ({ children }) => (
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: "300px",
|
||||
backgroundColor: "#252734",
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
marginBottom: "75px",
|
||||
padding: "0 15px",
|
||||
}}
|
||||
>
|
||||
<BoxText
|
||||
sx={{
|
||||
width: "50%",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</BoxText>
|
||||
<BoxImage
|
||||
sx={{
|
||||
padding: "39px 39px 0 0",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<img src={Desktop} />
|
||||
</BoxImage>
|
||||
</Box>
|
||||
);
|
@ -1,4 +1,5 @@
|
||||
import { Button, styled } from "@mui/material";
|
||||
import { FC } from "react";
|
||||
|
||||
const Item = styled(Button)({
|
||||
textTransform: "none",
|
||||
@ -10,6 +11,12 @@ const Item = styled(Button)({
|
||||
fontWeight: 400,
|
||||
});
|
||||
|
||||
export default function ButtonMore(params) {
|
||||
return <Item>{params.text}</Item>;
|
||||
interface ButtonMoreProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
const ButtonMore: FC<ButtonMoreProps> = ({ text }) => {
|
||||
return <Item>{text}</Item>;
|
||||
};
|
||||
|
||||
export default ButtonMore;
|
65
src/components/ui/EffectCard.tsx
Normal file
65
src/components/ui/EffectCard.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import { FC } from "react";
|
||||
import { Square } from "./Squre";
|
||||
import { Box } from "@mui/system";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
interface EffectCard {
|
||||
num: number;
|
||||
tit: string;
|
||||
subtit: string;
|
||||
}
|
||||
|
||||
export const EffectCard: FC<EffectCard> = ({ num, tit, subtit }) => (
|
||||
<Box
|
||||
sx={{
|
||||
width: {
|
||||
xxs: "140px",
|
||||
md: "250px",
|
||||
},
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
marginTop: "15px",
|
||||
}}
|
||||
>
|
||||
<Square
|
||||
sx={{
|
||||
width: "59px",
|
||||
height: "59px",
|
||||
borderRadius: "6px",
|
||||
fontSize: "50px",
|
||||
fontWeight: "500",
|
||||
color: "#7E2AEA",
|
||||
}}
|
||||
num={num}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
marginTop: "25px",
|
||||
fontSize: {
|
||||
xss: "18px",
|
||||
md: "24px",
|
||||
},
|
||||
fontWeight: "500",
|
||||
lineHeight: {
|
||||
xss: "21px",
|
||||
md: "26px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{tit}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "16px",
|
||||
md: "18px",
|
||||
},
|
||||
fontWeight: "400",
|
||||
marginTop: "8px",
|
||||
}}
|
||||
>
|
||||
{subtit}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
213
src/components/ui/Footer.tsx
Normal file
213
src/components/ui/Footer.tsx
Normal file
@ -0,0 +1,213 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import logotip from "../../assets/Icons/logoPenaHab.svg";
|
||||
import SectionStyled from "components/lib/SectionStyled";
|
||||
|
||||
const RootBox = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
justifyContent: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxServis = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("lg")]: {
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxMenu = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("xs")]: {
|
||||
width: "100%",
|
||||
},
|
||||
}));
|
||||
|
||||
export default function component() {
|
||||
return (
|
||||
<SectionStyled
|
||||
tag="footer"
|
||||
bg="#252734"
|
||||
mwidth="1160px"
|
||||
sxsect={{
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
minHeight: "300px",
|
||||
padding: {
|
||||
md: "100px 10px",
|
||||
xxs: "50px 10px 10px 10px",
|
||||
},
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<RootBox
|
||||
sx={{
|
||||
display: "flex",
|
||||
height: "50%",
|
||||
justifyContent: {
|
||||
xxs: "flex-start",
|
||||
md: "space-between",
|
||||
},
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "124px",
|
||||
height: "48px",
|
||||
backgroundSize: "contain",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<img src={logotip} alt="logotip" />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "457px",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<BoxMenu
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: {
|
||||
xxs: "flex-start",
|
||||
md: "center",
|
||||
},
|
||||
button: {
|
||||
paddingLeft: "0",
|
||||
color: "#F2F3F7",
|
||||
textTransform: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Link to="/docs">
|
||||
<Button variant="text" component="button">
|
||||
Оферта
|
||||
</Button>
|
||||
</Link>
|
||||
<Button component="button" variant="text">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
</BoxMenu>
|
||||
<BoxMenu
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: {
|
||||
xxs: "flex-start",
|
||||
md: "center",
|
||||
},
|
||||
button: {
|
||||
paddingLeft: "0",
|
||||
color: "#F2F3F7",
|
||||
textTransform: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
</BoxMenu>
|
||||
<BoxMenu
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: {
|
||||
xxs: "flex-start",
|
||||
md: "center",
|
||||
},
|
||||
button: {
|
||||
paddingLeft: "0",
|
||||
color: "#F2F3F7",
|
||||
textTransform: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
</BoxMenu>
|
||||
</Box>
|
||||
<BoxServis
|
||||
sx={{
|
||||
minHeight: "105px",
|
||||
width: "275px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: "left",
|
||||
fontWeight: "500",
|
||||
borderBottom: {
|
||||
xxs: "2px solid white",
|
||||
md: "none",
|
||||
},
|
||||
paddingBottom: "20px",
|
||||
}}
|
||||
width={"inherit"}
|
||||
color={"#F2F3F7"}
|
||||
>
|
||||
Сервисы помогают предпринимателям, маркетологам и агентствам сделать интернет-маркетинг прозрачным
|
||||
</Typography>
|
||||
</BoxServis>
|
||||
</RootBox>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "end",
|
||||
height: "50%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: {
|
||||
xxs: "left",
|
||||
md: "center",
|
||||
},
|
||||
fontWeight: "500",
|
||||
}}
|
||||
color={"#F2F3F7"}
|
||||
>
|
||||
Конструктор маркетинговых решений. © 2022
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
47
src/components/ui/Header.tsx
Normal file
47
src/components/ui/Header.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import { styled } from "@mui/system";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import React from "react";
|
||||
|
||||
import SectionStyled from "../lib/SectionStyled";
|
||||
import LogoWrapper from "./LogoWrapper";
|
||||
import { Navigate } from "./Navigate";
|
||||
import LoginButton from "./LoginButton";
|
||||
import { Hamburger } from "components/lib/Hamburger";
|
||||
|
||||
export const Header = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"header"}
|
||||
mwidth={"1160px"}
|
||||
bg={"#f2f3f7"}
|
||||
sxsect={{
|
||||
justifyContent: "space-around",
|
||||
borderBottom: "1px solid rgba(0,0,0,.1)",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: "18px 5px",
|
||||
}}
|
||||
>
|
||||
<LogoWrapper />
|
||||
{useMediaQuery(theme.breakpoints.up("md")) ? (
|
||||
<>
|
||||
<Navigate />
|
||||
<LoginButton />
|
||||
</>
|
||||
) : (
|
||||
<Hamburger />
|
||||
)}
|
||||
</SectionStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default styled(Header, {
|
||||
name: "Header",
|
||||
slot: "Root",
|
||||
})(({ theme }: any) => theme.components.Header.styleOverrides.root);
|
22
src/components/ui/LoginButton.tsx
Normal file
22
src/components/ui/LoginButton.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import { Button } from "@mui/material";
|
||||
|
||||
const LoginButton = () => (
|
||||
<Button
|
||||
sx={{
|
||||
borderRadius: "8px",
|
||||
padding: "10px 18px",
|
||||
fontSize: "18px",
|
||||
lineHeight: "24px",
|
||||
fontWeight: 400,
|
||||
textTransform: "none",
|
||||
border: "1px solid black",
|
||||
color: "black",
|
||||
}}
|
||||
variant={"outlined"}
|
||||
>
|
||||
Личный кабинет
|
||||
</Button>
|
||||
);
|
||||
|
||||
export default LoginButton;
|
16
src/components/ui/LogoWrapper.tsx
Normal file
16
src/components/ui/LogoWrapper.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { FC } from "react";
|
||||
|
||||
import logoPenaHub from "../../assets/Icons/black_logo_PenaHab.svg";
|
||||
import { useTheme } from "@mui/system";
|
||||
import { useMediaQuery } from "@mui/material";
|
||||
|
||||
const LogoWrapper: FC = () => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
return (
|
||||
<img style={{ display: isMobile ? "block" : "none", width: 100, height: 38 }} src={logoPenaHub} alt="PenaLtd" />
|
||||
);
|
||||
};
|
||||
|
||||
export default LogoWrapper;
|
29
src/components/ui/Navigate.tsx
Normal file
29
src/components/ui/Navigate.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { Box, Button } from "@mui/material";
|
||||
import { buttonMenu } from "__mocks__/buttonMenu";
|
||||
import { FC } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const styleLink = {
|
||||
marginRight: "30px",
|
||||
color: "black",
|
||||
textDecoration: "none",
|
||||
fontWeight: 500,
|
||||
"a:hover": {
|
||||
color: "#7E2AEA",
|
||||
},
|
||||
};
|
||||
|
||||
export const Navigate: FC = () => (
|
||||
<Box sx={{ marginRight: "5px" }} component="nav">
|
||||
{buttonMenu.map((e, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
component={() => (
|
||||
<Link style={styleLink} to={e.pageUrl}>
|
||||
{e.menuTitle}
|
||||
</Link>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
63
src/components/ui/ProfitCard.tsx
Normal file
63
src/components/ui/ProfitCard.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { FC } from "react";
|
||||
import { Square } from "./Squre";
|
||||
|
||||
interface ProfitCardProps {
|
||||
pic: string;
|
||||
tit: string;
|
||||
subtit: string;
|
||||
}
|
||||
|
||||
export const ProfitCard: FC<ProfitCardProps> = ({ pic, tit, subtit }) => (
|
||||
<Box
|
||||
sx={{
|
||||
width: {
|
||||
xxs: "100%",
|
||||
sm: "250px",
|
||||
lg: "300px",
|
||||
},
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: "40px",
|
||||
}}
|
||||
>
|
||||
<Square
|
||||
sx={{
|
||||
width: "59px",
|
||||
height: "59px",
|
||||
borderRadius: "6px",
|
||||
backgroundSize: "70%",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundColor: "#ffffff",
|
||||
}}
|
||||
pic={pic}
|
||||
/>
|
||||
<Typography
|
||||
paddingBottom={"0px"}
|
||||
sx={{
|
||||
fontSize: {
|
||||
xxs: "20px",
|
||||
md: "24px",
|
||||
},
|
||||
fontWeight: "500",
|
||||
lineHeight: "26px",
|
||||
marginTop: "30px",
|
||||
}}
|
||||
>
|
||||
{tit}
|
||||
</Typography>
|
||||
<Typography
|
||||
paddingBottom={"0px"}
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "26px",
|
||||
color: "#4D4D4D",
|
||||
}}
|
||||
>
|
||||
{subtit}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
21
src/components/ui/Squre.tsx
Normal file
21
src/components/ui/Squre.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { FC } from "react";
|
||||
import { Box, SxProps, Theme } from "@mui/system";
|
||||
|
||||
interface SquareProps {
|
||||
pic?: string;
|
||||
sx?: SxProps<Theme>;
|
||||
num?: number;
|
||||
}
|
||||
|
||||
export const Square: FC<SquareProps> = ({ pic, sx, num }) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
backgroundImage: `url( ${pic} )`,
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
{num}
|
||||
</Box>
|
||||
);
|
||||
};
|
90
src/components/ui/Tariff.tsx
Normal file
90
src/components/ui/Tariff.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import { Box, Typography, styled } from "@mui/material";
|
||||
import { FC } from "react";
|
||||
import { Square } from "./Squre";
|
||||
|
||||
interface TariffProps {
|
||||
icon: string;
|
||||
caption: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const Tariff: FC<TariffProps> = ({ icon, caption, text }) => {
|
||||
const BoxCard = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down(730)]: {
|
||||
width: "100%",
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<BoxCard
|
||||
sx={{
|
||||
width: "360px",
|
||||
height: "260px",
|
||||
backgroundColor: "#ffffff",
|
||||
borderRadius: "12px",
|
||||
padding: "20px",
|
||||
marginBottom: "10px",
|
||||
boxShadow: `0px 100px 309px rgba(210, 208, 225, 0.24),
|
||||
0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525),
|
||||
0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066),
|
||||
0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12),
|
||||
0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343),
|
||||
0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)`,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ height: "50%" }}>
|
||||
<Square
|
||||
sx={{
|
||||
width: "36px",
|
||||
height: "36px",
|
||||
backgroundColor: "#7E2AEA",
|
||||
borderRadius: "6px",
|
||||
backgroundSize: "70%",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
pic={icon}
|
||||
/>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
marginTop: "10px",
|
||||
fontSize: "24px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
{caption}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
marginTop: "10px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "21.3px",
|
||||
color: "#4D4D4D",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ height: "50%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "180px",
|
||||
height: "44px",
|
||||
padding: "8px 40px 10px 40px",
|
||||
border: "1px solid #7E2AEA",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: "5px",
|
||||
fontSize: "18px",
|
||||
fontWeight: "400",
|
||||
cursor: "pointer",
|
||||
marginTop: "60px",
|
||||
}}
|
||||
>
|
||||
Подробнее
|
||||
</Box>
|
||||
</Box>
|
||||
</BoxCard>
|
||||
);
|
||||
};
|
@ -1,214 +0,0 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "../lib/section";
|
||||
|
||||
import logotip from "../../assets/Icons/logoPenaHab.svg";
|
||||
|
||||
const RootBox = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
justifyContent: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxServis = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("lg")]: {
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
const BoxMenu = styled("div")(({ theme }) => ({
|
||||
[theme.breakpoints.down("xs")]: {
|
||||
width: "100%",
|
||||
},
|
||||
}));
|
||||
|
||||
export default function component() {
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"footer"}
|
||||
bg={"#252734"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
minHeight: "300px",
|
||||
padding: {
|
||||
md: "100px 10px",
|
||||
xxs: "50px 10px 10px 10px",
|
||||
},
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<RootBox
|
||||
sx={{
|
||||
display: "flex",
|
||||
height: "50%",
|
||||
justifyContent: {
|
||||
xxs: "flex-start",
|
||||
md: "space-between",
|
||||
},
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "124px",
|
||||
height: "48px",
|
||||
backgroundSize: "contain",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<img src={logotip} />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "457px",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<BoxMenu
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: {
|
||||
xxs: "flex-start",
|
||||
md: "center",
|
||||
},
|
||||
button: {
|
||||
paddingLeft: "0",
|
||||
color: "#F2F3F7",
|
||||
textTransform: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Link to="/docs">
|
||||
<Button variant="text" component="button">
|
||||
Оферта
|
||||
</Button>
|
||||
</Link>
|
||||
<Button component="button" variant="text">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
</BoxMenu>
|
||||
<BoxMenu
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: {
|
||||
xxs: "flex-start",
|
||||
md: "center",
|
||||
},
|
||||
button: {
|
||||
paddingLeft: "0",
|
||||
color: "#F2F3F7",
|
||||
textTransform: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
</BoxMenu>
|
||||
<BoxMenu
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: {
|
||||
xxs: "flex-start",
|
||||
md: "center",
|
||||
},
|
||||
button: {
|
||||
paddingLeft: "0",
|
||||
color: "#F2F3F7",
|
||||
textTransform: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
<Button variant="text" component="button">
|
||||
Меню 1
|
||||
</Button>
|
||||
</BoxMenu>
|
||||
</Box>
|
||||
<BoxServis
|
||||
sx={{
|
||||
minHeight: "105px",
|
||||
width: "275px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: "left",
|
||||
fontWeight: "500",
|
||||
borderBottom: {
|
||||
xxs: "2px solid white",
|
||||
md: "none",
|
||||
},
|
||||
paddingBottom: "20px",
|
||||
}}
|
||||
width={"inherit"}
|
||||
color={"#F2F3F7"}
|
||||
>
|
||||
Сервисы помогают предпринимателям, маркетологам и агентствам сделать интернет-маркетинг прозрачным
|
||||
</Typography>
|
||||
</BoxServis>
|
||||
</RootBox>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "end",
|
||||
height: "50%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="span"
|
||||
sx={{
|
||||
textAlign: {
|
||||
xxs: "left",
|
||||
md: "center",
|
||||
},
|
||||
fontWeight: "500",
|
||||
}}
|
||||
color={"#F2F3F7"}
|
||||
>
|
||||
Конструктор маркетинговых решений. © 2022
|
||||
</Typography>
|
||||
</Box>
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import Button from "@mui/material/Button";
|
||||
import { styled } from "@mui/system";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import logoPenaHub from "../../assets/Icons/black_logo_PenaHab.svg";
|
||||
import { Link } from "react-router-dom";
|
||||
import Box from "@mui/material/Box";
|
||||
import SectionStyled from "../lib/section.js";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Drawer from "@mui/material/Drawer";
|
||||
|
||||
const LogoWrapper = styled("img", {
|
||||
name: "Header",
|
||||
slot: "Logo",
|
||||
})(({ theme }) => theme.components.Header.styleOverrides.Logo);
|
||||
|
||||
const Logo = () => <LogoWrapper src={logoPenaHub} alt="PenaLtd" />;
|
||||
|
||||
const buttonMenu = [
|
||||
{
|
||||
menuTitle: "Menu1",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu2",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu3",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu4",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu5",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu6",
|
||||
pageUrl: "/",
|
||||
},
|
||||
{
|
||||
menuTitle: "Menu7",
|
||||
pageUrl: "/",
|
||||
},
|
||||
];
|
||||
|
||||
const N = styled(Box, {
|
||||
name: "Header",
|
||||
slot: "Menu",
|
||||
})(({ theme }) => theme.components.Header.styleOverrides.Menu);
|
||||
const Nav = () => (
|
||||
<N>
|
||||
{buttonMenu.map((e, index) => (
|
||||
<Button key={index} component={() => <Link to={e.pageUrl}>{e.menuTitle}</Link>} />
|
||||
))}
|
||||
</N>
|
||||
);
|
||||
|
||||
const LB = styled(Button, {
|
||||
name: "Header",
|
||||
slot: "LoginBtn",
|
||||
})(({ theme }) => theme.components.Header.styleOverrides.LoginBtn);
|
||||
const LoginButton = () => <LB variant={"outlined"}>Личный кабинет</LB>;
|
||||
|
||||
const Hamburger = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<IconButton component="button">
|
||||
<MenuIcon onClick={() => setOpen(true)} />
|
||||
</IconButton>
|
||||
<Drawer open={open} onClose={() => setOpen(false)}>
|
||||
<Nav />
|
||||
<LoginButton />
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function Header(props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"header"}
|
||||
mwidth={"1160px"}
|
||||
bg={"#f2f3f7"}
|
||||
sxsect={{
|
||||
justifyContent: "space-around",
|
||||
borderBottom: "1px solid rgba(0,0,0,.1)",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: "18px 5px",
|
||||
}}
|
||||
>
|
||||
<Logo />
|
||||
{useMediaQuery(theme.breakpoints.up("md")) ? (
|
||||
<>
|
||||
<Nav />
|
||||
<LoginButton />
|
||||
</>
|
||||
) : (
|
||||
<Hamburger />
|
||||
)}
|
||||
</SectionStyled>
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(Header, {
|
||||
name: "Header",
|
||||
slot: "Root",
|
||||
})(({ theme }) => theme.components.Header.styleOverrides.root);
|
@ -4,7 +4,7 @@ import Typography from "@mui/material/Typography";
|
||||
import { Button } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import SectionStyled from "../lib/section";
|
||||
import SectionStyled from "../lib/SectionStyled";
|
||||
|
||||
import arrow from "../../assets/image/arrow.png";
|
||||
import amoLabel from "../../assets/image/amoLabel.png";
|
||||
@ -62,7 +62,6 @@ export default function Component() {
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="span"
|
||||
color={"#FFFFFF"}
|
||||
sx={{
|
||||
fontSize: "calc(29px + 3 * (100vw / 1160))",
|
4
src/declaration.d.ts
vendored
Normal file
4
src/declaration.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module "*.png";
|
||||
declare module "*.svg";
|
||||
declare module "*.jpeg";
|
||||
declare module "*.jpg";
|
@ -3,17 +3,17 @@ import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import { CssBaseline } from "@mui/material";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import { SnackbarProvider, useSnackbar } from "notistack";
|
||||
import { SnackbarProvider } from "notistack";
|
||||
|
||||
import App from "./app.js";
|
||||
import App from "./App";
|
||||
import Docs from "./docs/docs.js";
|
||||
import Offer from "./pages/offer.js";
|
||||
import Tour from "./pages/tour.js";
|
||||
import Video from "./pages/video.js";
|
||||
import OfferPage from "./pages/OfferPage";
|
||||
import TourPage from "./pages/TourPage";
|
||||
import Video from "./pages/VideoPage";
|
||||
|
||||
import theme from "./Theme/theme.js";
|
||||
import { theme } from "./theme/theme";
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<CssBaseline />
|
||||
@ -22,8 +22,8 @@ root.render(
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/docs" element={<Docs />} />
|
||||
<Route path="/offers" element={<Offer />} />
|
||||
<Route path="/tour" element={<Tour />} />
|
||||
<Route path="/offers" element={<OfferPage />} />
|
||||
<Route path="/tour" element={<TourPage />} />
|
||||
<Route path="/video" element={<Video />} />
|
||||
<Route path="/*" element={<App />} />
|
||||
</Routes>
|
@ -10,43 +10,15 @@ import {
|
||||
useMediaQuery,
|
||||
} from "@mui/material";
|
||||
import React from "react";
|
||||
import Header from "../components/ui/header";
|
||||
import Footer from "../components/ui/footer";
|
||||
import theme from "../Theme/theme";
|
||||
import Header from "../components/ui/Header";
|
||||
import Footer from "../components/ui/Footer";
|
||||
import { theme } from "../theme/theme";
|
||||
import ButtonMore from "../components/ui/ButtonMore";
|
||||
|
||||
import { OfferCards } from "../__mocks__/оfferCards";
|
||||
|
||||
import logoPenaHub from "../assets/Icons/black_logo_PenaHab.svg";
|
||||
import logoAmoCrm from "../assets/image/logoAmoCrm.png";
|
||||
import iconFader from "../assets/image/offers/Icon-Faders.svg";
|
||||
import ArrowsOut from "../assets/image/offers/ArrowsOut.svg";
|
||||
import ClipboardText from "../assets/image/offers/ClipboardText.svg";
|
||||
import FolderSimpleStar from "../assets/image/offers/FolderSimpleStar.svg";
|
||||
import ButtonMore from "../components/ui/button";
|
||||
|
||||
const Cards = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Последовательная настройка amoCRM под текущие задачи",
|
||||
text: "Объясним, расскажем и поможем\n Предлагаем понятное решение с четкими сроками",
|
||||
icon: ArrowsOut,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Доработка amoCRM с настройкой хайповых инструментов",
|
||||
text: "Вы путешествуете, а мы выполняем задачи\n Непрерывная коммуникация с вашей командой",
|
||||
icon: FolderSimpleStar,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Комплексная настройка amoCRM с реализацией вашего списка задач",
|
||||
text: "Сэкономим ваше время предложим лучшее решение\n Точно в срок без лишних вопросов",
|
||||
icon: iconFader,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Интеграция amoCRM с сервисами по ТЗ заказчика",
|
||||
text: "Мы команда для реализации ваших идей\n Развернем инфраструктуру с разработкой и поддержкой",
|
||||
icon: ClipboardText,
|
||||
},
|
||||
];
|
||||
|
||||
const CardInfo = styled(Card)(() => ({
|
||||
background: "#FFFFFF",
|
||||
@ -64,7 +36,7 @@ const CardInfo = styled(Card)(() => ({
|
||||
gap: "10px",
|
||||
}));
|
||||
|
||||
export default function Offer(props) {
|
||||
export default function OfferPage() {
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("lg"));
|
||||
return (
|
||||
<React.Fragment>
|
||||
@ -106,8 +78,8 @@ export default function Offer(props) {
|
||||
Услуги по внедрению и настройке <img src={logoAmoCrm} alt="AmoCRM" />
|
||||
</Typography>
|
||||
<Grid container columns={{ xs: 2, lg: 6, xl: 12 }} columnGap="justify-content" rowGap="20px" spacing={2}>
|
||||
{Cards.map((card, i) => (
|
||||
<Grid item xs={6} key={i}>
|
||||
{OfferCards.map((card, index) => (
|
||||
<Grid item xs={6} key={index}>
|
||||
<CardInfo>
|
||||
<Box
|
||||
sx={{
|
121
src/pages/TourPage.tsx
Normal file
121
src/pages/TourPage.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import { Box, CssBaseline, Typography, useMediaQuery } from "@mui/material";
|
||||
import { ThemeProvider } from "@mui/system";
|
||||
|
||||
import { Header } from "../components/ui/Header";
|
||||
import Footer from "../components/ui/Footer";
|
||||
import SectionStyled from "../components/lib/SectionStyled";
|
||||
import { CardItemTour } from "components/lib/CardItemTour";
|
||||
|
||||
import { cardsTour } from "__mocks__/cardsTour";
|
||||
|
||||
import { theme } from "../theme/theme";
|
||||
|
||||
export default function TourPage() {
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("lg"));
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<ThemeProvider theme={theme}>
|
||||
<Header />
|
||||
<SectionStyled
|
||||
tag={"footer"}
|
||||
bg={"#F2F3F7"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
minHeight: "300px",
|
||||
padding: {
|
||||
md: "100px 10px",
|
||||
xxs: "10px",
|
||||
},
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
background: "#F2F3F7",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
paddingBottom: "96px",
|
||||
}}
|
||||
>
|
||||
<Link to="/">
|
||||
<ArrowBackIcon
|
||||
sx={{
|
||||
display: {
|
||||
xxs: "block",
|
||||
md: "none",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
fontSize="calc(29px + 23 * (100vw / 1160))"
|
||||
sx={{
|
||||
lineHeight: {
|
||||
xxs: "38px",
|
||||
md: "70px",
|
||||
},
|
||||
marginTop: "20px",
|
||||
marginBottom: "20px",
|
||||
width: "min-content",
|
||||
}}
|
||||
>
|
||||
Пошаговое руководство
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
maxWidth: "500px",
|
||||
lineHeight: "28px",
|
||||
alignSelf: "flex-end",
|
||||
}}
|
||||
variant="h6"
|
||||
>
|
||||
Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
"&>*:nth-child(2n+1)": {
|
||||
backgroundColor: "#FFF",
|
||||
flexDirection: "row-reverse",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{cardsTour.map(({ title, text, image }, index) => (
|
||||
<CardItemTour key={index} title={title} text={text} image={image} />
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</SectionStyled>
|
||||
<Footer />
|
||||
</ThemeProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
89
src/pages/VideoPage.tsx
Normal file
89
src/pages/VideoPage.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import { Box, Container, CssBaseline, Grid, ThemeProvider, Typography, useMediaQuery } from "@mui/material";
|
||||
|
||||
import Header from "../components/ui/Header";
|
||||
import Footer from "../components/ui/Footer";
|
||||
import { CardItemVideo } from "components/lib/CardItemVideo";
|
||||
|
||||
import logoPenaHub from "../assets/Icons/black_logo_PenaHab.svg";
|
||||
|
||||
import { theme } from "../theme/theme";
|
||||
import { cardsTour } from "__mocks__/cardsTour";
|
||||
|
||||
export default function Video() {
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("lg"));
|
||||
return (
|
||||
<>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Header />
|
||||
<Box
|
||||
sx={{
|
||||
background: "#F2F3F7",
|
||||
paddingTop: "73px",
|
||||
paddingBottom: "160px",
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="xl" fixed>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "80px",
|
||||
justifyContent: "flex-start",
|
||||
paddingBottom: "96px",
|
||||
}}
|
||||
>
|
||||
{isMobile ? (
|
||||
<></>
|
||||
) : (
|
||||
<>
|
||||
<img style={{ maxWidth: "180px", maxHeight: "70px" }} src={logoPenaHub} alt="PenaLtd" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
fontSize="calc(33px + 23 * (100vw / 1160))"
|
||||
sx={{
|
||||
lineHeight: "70px",
|
||||
marginTop: "20px",
|
||||
width: "min-content",
|
||||
}}
|
||||
>
|
||||
Видео руководство
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
maxWidth: "500px",
|
||||
lineHeight: "28px",
|
||||
alignSelf: "flex-end",
|
||||
}}
|
||||
variant="h6"
|
||||
>
|
||||
Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Grid container columns={{ xs: 2, lg: 6, xl: 12 }} columnGap="justify-content" rowGap="80px" spacing={2}>
|
||||
{cardsTour.map(({ title, text, image }, index) => (
|
||||
<Grid item xs={6}>
|
||||
<CardItemVideo key={index} title={title} text={text} image={image} />
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
<Footer />
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import { Box, CssBaseline, Typography, useMediaQuery } from "@mui/material";
|
||||
import { ThemeProvider } from "@mui/system";
|
||||
|
||||
import Header from "../components/ui/header";
|
||||
import Footer from "../components/ui/footer";
|
||||
import SectionStyled from "../components/lib/section";
|
||||
|
||||
import Rectangle from "../assets/image/Rectangle.png";
|
||||
|
||||
import theme from "../Theme/theme";
|
||||
|
||||
const CardArray = [
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного. Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного. Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного. Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного. Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного.Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
];
|
||||
|
||||
function CardItem(params, theme) {
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
padding: "80px 10px",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "space-between",
|
||||
gap: "20px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "20px",
|
||||
background: "none",
|
||||
maxWidth: "450px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" sx={{ fontSize: "20px", fontWeight: "500", lineHeight: "24px" }}>
|
||||
{params.title}
|
||||
</Typography>
|
||||
<Typography variant="h5" sx={{ fontSize: "18px", fontWeight: "400", lineHeight: "21px" }}>
|
||||
{params.text}
|
||||
</Typography>
|
||||
</Box>
|
||||
<img
|
||||
style={{
|
||||
width: "100%",
|
||||
borderRadius: "12px",
|
||||
maxWidth: "560px",
|
||||
boxShadow:
|
||||
"0px 100px 309px rgba(210, 208, 225, 0.24), 0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525), 0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066), 0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12), 0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343), 0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)",
|
||||
}}
|
||||
src={params.image}
|
||||
alt={params.image}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Tour(params) {
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("lg"));
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<ThemeProvider theme={theme}>
|
||||
<Header />
|
||||
<SectionStyled
|
||||
tag={"footer"}
|
||||
bg={"#F2F3F7"}
|
||||
mwidth={"1160px"}
|
||||
sxsect={{
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
sxcont={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
minHeight: "300px",
|
||||
padding: {
|
||||
md: "100px 10px",
|
||||
xxs: "10px",
|
||||
},
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
background: "#F2F3F7",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
paddingBottom: "96px",
|
||||
}}
|
||||
>
|
||||
<Link to={"/"}>
|
||||
<ArrowBackIcon
|
||||
sx={{
|
||||
display: {
|
||||
xxs: "block",
|
||||
md: "none",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
fontSize="calc(29px + 23 * (100vw / 1160))"
|
||||
sx={{
|
||||
lineHeight: {
|
||||
xxs: "38px",
|
||||
md: "70px",
|
||||
},
|
||||
marginTop: "20px",
|
||||
marginBottom: "20px",
|
||||
width: "min-content",
|
||||
}}
|
||||
>
|
||||
Пошаговое руководство
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
maxWidth: "500px",
|
||||
lineHeight: "28px",
|
||||
alignSelf: "flex-end",
|
||||
}}
|
||||
variant="h6"
|
||||
>
|
||||
Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
"&>*:nth-child(2n+1)": {
|
||||
backgroundColor: "#FFF",
|
||||
flexDirection: "row-reverse",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{CardArray.map((Card, i) => (
|
||||
<CardItem title={Card.title} text={Card.text} image={Card.image}></CardItem>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</SectionStyled>
|
||||
<Footer />
|
||||
</ThemeProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
import { Box, Container, CssBaseline, Grid, ThemeProvider, Typography, useMediaQuery } from "@mui/material";
|
||||
|
||||
import Header from "../components/ui/header";
|
||||
import Footer from "../components/ui/footer";
|
||||
|
||||
import logoPenaHub from "../assets/Icons/black_logo_PenaHab.svg";
|
||||
import Rectangle from "../assets/image/Rectangle.png";
|
||||
import ButtonPlay from "../assets/Icons/ButtonPlay.svg";
|
||||
|
||||
import theme from "../Theme/theme";
|
||||
|
||||
const CardArray = [
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
{
|
||||
title: "Текст-заполнитель — это текст, который имеет некоторые характеристики",
|
||||
text: "Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного",
|
||||
image: Rectangle,
|
||||
},
|
||||
];
|
||||
|
||||
function CardItem(params) {
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ maxWidth: "560px", display: "flex", flexDirection: "column", gap: "20px" }}>
|
||||
<Box sx={{ position: "relative" }}>
|
||||
<img style={{ width: "100%" }} src={params.image} alt="" />
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
maxHeight: "340px",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<img style={{}} src={ButtonPlay} alt="" />
|
||||
</Box>
|
||||
</Box>
|
||||
<Typography variant="h4" sx={{ fontSize: "20px", fontWeight: "500", lineHeight: "24px", marginTop: "10px" }}>
|
||||
{params.title}
|
||||
</Typography>
|
||||
<Typography variant="h5" sx={{ fontSize: "18px", fontWeight: "400", lineHeight: "21px" }}>
|
||||
{params.text}
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Video(params) {
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("lg"));
|
||||
return (
|
||||
<>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Header />
|
||||
<Box
|
||||
sx={{
|
||||
background: "#F2F3F7",
|
||||
paddingTop: "73px",
|
||||
paddingBottom: "160px",
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="xl" fixed>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "80px",
|
||||
justifyContent: "flex-start",
|
||||
paddingBottom: "96px",
|
||||
}}
|
||||
>
|
||||
{isMobile ? (
|
||||
<></>
|
||||
) : (
|
||||
<>
|
||||
<img style={{ maxWidth: "180px", maxHeight: "70px" }} src={logoPenaHub} alt="PenaLtd" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
fontSize="calc(33px + 23 * (100vw / 1160))"
|
||||
sx={{
|
||||
lineHeight: "70px",
|
||||
marginTop: "20px",
|
||||
width: "min-content",
|
||||
}}
|
||||
>
|
||||
Видео руководство
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "24px",
|
||||
maxWidth: "500px",
|
||||
lineHeight: "28px",
|
||||
alignSelf: "flex-end",
|
||||
}}
|
||||
variant="h6"
|
||||
>
|
||||
Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Grid container columns={{ xs: 2, lg: 6, xl: 12 }} columnGap="justify-content" rowGap="80px" spacing={2}>
|
||||
{CardArray.map((elem, i) => (
|
||||
<Grid item xs={6}>
|
||||
<CardItem key={i} title={elem.title} text={elem.text} image={elem.image} />
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
<Footer />
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
}
|
@ -2,58 +2,56 @@ import { create } from "zustand";
|
||||
import { sendContactFormRequest } from "../api/contactForm";
|
||||
import { getMessageFromFetchError } from "../utils/backendMessageHandler";
|
||||
|
||||
|
||||
|
||||
const initialState = {
|
||||
isModalOpen: true,
|
||||
isSubmitDisabled: false,
|
||||
nameField: "",
|
||||
phoneNumberField: "",
|
||||
telegramField: "",
|
||||
whatsappField: "",
|
||||
isModalOpen: true,
|
||||
isSubmitDisabled: false,
|
||||
nameField: "",
|
||||
phoneNumberField: "",
|
||||
telegramField: "",
|
||||
whatsappField: "",
|
||||
};
|
||||
|
||||
export const useContactFormStore = create()(
|
||||
(set, get) => initialState
|
||||
);
|
||||
export const useContactFormStore = create()((set, get) => initialState);
|
||||
|
||||
export const setIsContactFormOpen = (isModalOpen) => useContactFormStore.setState({ isModalOpen });
|
||||
export const setContactFormNameField = (nameField) => useContactFormStore.setState({ nameField });
|
||||
export const setContactFormTelegramField = (telegramField) => useContactFormStore.setState({ telegramField });
|
||||
export const setIsSubmitDisabled = (isSubmitDisabled) => useContactFormStore.setState({ isSubmitDisabled });
|
||||
export const setContactFormPhoneNumberField = (phoneNumberField) => {
|
||||
if (/^\+?\d*$/.test(phoneNumberField)) useContactFormStore.setState({ phoneNumberField });
|
||||
if (/^\+?\d*$/.test(phoneNumberField)) useContactFormStore.setState({ phoneNumberField });
|
||||
};
|
||||
export const setContactFormWhatsappField = (whatsappField) => {
|
||||
if (/^\+?\d*$/.test(whatsappField)) useContactFormStore.setState({ whatsappField });
|
||||
if (/^\+?\d*$/.test(whatsappField)) useContactFormStore.setState({ whatsappField });
|
||||
};
|
||||
|
||||
export const sendContactForm = async () => {
|
||||
const { nameField, phoneNumberField, telegramField, whatsappField } = useContactFormStore.getState();
|
||||
const { nameField, phoneNumberField, telegramField, whatsappField } = useContactFormStore.getState();
|
||||
|
||||
if (!nameField) return "Имя не указано";
|
||||
if (!phoneNumberField && !telegramField && !whatsappField) return "Ни один из контактов не указан";
|
||||
if (phoneNumberField && !/^\+\d{5,}|\d{6,}$/.test(phoneNumberField)) return "Номер телефона должен содержать минимум 6 символов";
|
||||
if (whatsappField && !/^\+\d{5,}|\d{6,}$/.test(whatsappField)) return "Номер Whatsapp должен содержать минимум 6 символов";
|
||||
if (!nameField) return "Имя не указано";
|
||||
if (!phoneNumberField && !telegramField && !whatsappField) return "Ни один из контактов не указан";
|
||||
if (phoneNumberField && !/^\+\d{5,}|\d{6,}$/.test(phoneNumberField))
|
||||
return "Номер телефона должен содержать минимум 6 символов";
|
||||
if (whatsappField && !/^\+\d{5,}|\d{6,}$/.test(whatsappField))
|
||||
return "Номер Whatsapp должен содержать минимум 6 символов";
|
||||
|
||||
let contact = `phonenumber:${phoneNumberField}`;
|
||||
if (telegramField) contact += `\ntelegram:${telegramField}`;
|
||||
if (whatsappField) contact += `\nwhatsapp:${whatsappField}`;
|
||||
let contact = `phonenumber:${phoneNumberField}`;
|
||||
if (telegramField) contact += `\ntelegram:${telegramField}`;
|
||||
if (whatsappField) contact += `\nwhatsapp:${whatsappField}`;
|
||||
|
||||
try {
|
||||
useContactFormStore.setState({ isSubmitDisabled: true });
|
||||
const response = await sendContactFormRequest({
|
||||
contact,
|
||||
whoami: nameField,
|
||||
});
|
||||
try {
|
||||
useContactFormStore.setState({ isSubmitDisabled: true });
|
||||
const response = await sendContactFormRequest({
|
||||
contact,
|
||||
whoami: nameField,
|
||||
});
|
||||
|
||||
if (response.status !== 200) throw new Error(response.statusText);
|
||||
if (response.status !== 200) throw new Error(response.statusText);
|
||||
|
||||
useContactFormStore.setState({ ...initialState });
|
||||
useContactFormStore.setState({ ...initialState });
|
||||
|
||||
return "Данные отправлены";
|
||||
} catch (error) {
|
||||
useContactFormStore.setState({ isSubmitDisabled: false });
|
||||
return getMessageFromFetchError(error);
|
||||
}
|
||||
return "Данные отправлены";
|
||||
} catch (error) {
|
||||
useContactFormStore.setState({ isSubmitDisabled: false });
|
||||
return getMessageFromFetchError(error);
|
||||
}
|
||||
};
|
@ -3,59 +3,60 @@ import { create } from "zustand";
|
||||
import { createJSONStorage, devtools, persist } from "zustand/middleware";
|
||||
|
||||
export const useUnauthTicketStore = create()(
|
||||
persist(
|
||||
devtools(
|
||||
(set, get) => ({
|
||||
sessionData: null,
|
||||
isMessageSending: false,
|
||||
messages: [],
|
||||
apiPage: 0,
|
||||
messagesPerPage: 10,
|
||||
lastMessageId: undefined,
|
||||
isPreventAutoscroll: false,
|
||||
}),
|
||||
{
|
||||
name: "Unauth tickets"
|
||||
}
|
||||
),
|
||||
{
|
||||
version: 0,
|
||||
name: "unauth-ticket",
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
partialize: state => ({
|
||||
sessionData: state.sessionData,
|
||||
})
|
||||
}
|
||||
)
|
||||
persist(
|
||||
devtools(
|
||||
(set, get) => ({
|
||||
sessionData: null,
|
||||
isMessageSending: false,
|
||||
messages: [],
|
||||
apiPage: 0,
|
||||
messagesPerPage: 10,
|
||||
lastMessageId: undefined,
|
||||
isPreventAutoscroll: false,
|
||||
}),
|
||||
{
|
||||
name: "Unauth tickets",
|
||||
}
|
||||
),
|
||||
{
|
||||
version: 0,
|
||||
name: "unauth-ticket",
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
partialize: (state) => ({
|
||||
sessionData: state.sessionData,
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const setUnauthSessionData = (sessionData) => useUnauthTicketStore.setState({ sessionData });
|
||||
export const setIsMessageSending = (isMessageSending) => useUnauthTicketStore.setState({ isMessageSending });
|
||||
|
||||
export const addOrUpdateUnauthMessages = (receivedMessages) => {
|
||||
const state = useUnauthTicketStore.getState();
|
||||
const messageIdToMessageMap = {};
|
||||
const state = useUnauthTicketStore.getState();
|
||||
const messageIdToMessageMap = {};
|
||||
|
||||
[...state.messages, ...receivedMessages].forEach(message => messageIdToMessageMap[message.id] = message);
|
||||
[...state.messages, ...receivedMessages].forEach((message) => (messageIdToMessageMap[message.id] = message));
|
||||
|
||||
const sortedMessages = Object.values(messageIdToMessageMap).sort(sortMessagesByTime);
|
||||
const sortedMessages = Object.values(messageIdToMessageMap).sort(sortMessagesByTime);
|
||||
|
||||
useUnauthTicketStore.setState({
|
||||
messages: sortedMessages,
|
||||
lastMessageId: sortedMessages.at(-1)?.id,
|
||||
});
|
||||
useUnauthTicketStore.setState({
|
||||
messages: sortedMessages,
|
||||
lastMessageId: sortedMessages.at(-1)?.id,
|
||||
});
|
||||
};
|
||||
|
||||
export const incrementUnauthMessageApiPage = () => {
|
||||
const state = useUnauthTicketStore.getState();
|
||||
const state = useUnauthTicketStore.getState();
|
||||
|
||||
useUnauthTicketStore.setState({ apiPage: state.apiPage + 1 });
|
||||
useUnauthTicketStore.setState({ apiPage: state.apiPage + 1 });
|
||||
};
|
||||
|
||||
export const setUnauthIsPreventAutoscroll = (isPreventAutoscroll) => useUnauthTicketStore.setState({ isPreventAutoscroll });
|
||||
export const setUnauthIsPreventAutoscroll = (isPreventAutoscroll) =>
|
||||
useUnauthTicketStore.setState({ isPreventAutoscroll });
|
||||
|
||||
function sortMessagesByTime(ticket1, ticket2) {
|
||||
const date1 = new Date(ticket1.created_at).getTime();
|
||||
const date2 = new Date(ticket2.created_at).getTime();
|
||||
return date1 - date2;
|
||||
const date1 = new Date(ticket1.created_at).getTime();
|
||||
const date2 = new Date(ticket2.created_at).getTime();
|
||||
return date1 - date2;
|
||||
}
|
@ -1,5 +1,46 @@
|
||||
import { createTheme } from "@mui/material";
|
||||
let theme = createTheme({
|
||||
import { PaletteColor, PaletteColorOptions, Theme, createTheme } from "@mui/material";
|
||||
|
||||
declare module "@mui/material/Button" {
|
||||
interface ButtonPropsVariantOverrides {
|
||||
template: true;
|
||||
gray: true;
|
||||
}
|
||||
}
|
||||
|
||||
declare module "@mui/material/styles" {
|
||||
interface BreakpointOverrides {
|
||||
xxs: true;
|
||||
xs: true;
|
||||
sm: true;
|
||||
md: true;
|
||||
}
|
||||
|
||||
interface Palette {
|
||||
lightPurple: PaletteColor;
|
||||
darkPurple: PaletteColor;
|
||||
brightPurple: PaletteColor;
|
||||
fadePurple: PaletteColor;
|
||||
grey1: PaletteColor;
|
||||
grey2: PaletteColor;
|
||||
grey3: PaletteColor;
|
||||
orange: PaletteColor;
|
||||
navbarbg: PaletteColor;
|
||||
}
|
||||
|
||||
interface PaletteOptions {
|
||||
lightPurple?: PaletteColorOptions;
|
||||
darkPurple?: PaletteColorOptions;
|
||||
brightPurple?: PaletteColorOptions;
|
||||
fadePurple?: PaletteColorOptions;
|
||||
grey1?: PaletteColorOptions;
|
||||
grey2?: PaletteColorOptions;
|
||||
grey3?: PaletteColorOptions;
|
||||
orange?: PaletteColorOptions;
|
||||
navbarbg?: PaletteColorOptions;
|
||||
}
|
||||
}
|
||||
|
||||
export const theme: Theme = createTheme({
|
||||
breakpoints: {
|
||||
values: {
|
||||
xxs: 300,
|
||||
@ -103,7 +144,7 @@ let theme = createTheme({
|
||||
},
|
||||
});
|
||||
|
||||
theme = createTheme(theme, {
|
||||
const themeHeader = createTheme(theme, {
|
||||
components: {
|
||||
Header: {
|
||||
styleOverrides: {
|
||||
@ -151,5 +192,3 @@ theme = createTheme(theme, {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
@ -1,32 +0,0 @@
|
||||
import { isAxiosError } from "axios";
|
||||
|
||||
|
||||
const backendErrorMessage = {
|
||||
"user not found": "Пользователь не найден",
|
||||
"invalid password": "Неправильный пароль",
|
||||
"field <password> is empty": "Поле \"Пароль\" не заполнено",
|
||||
"field <login> is empty": "Поле \"Логин\" не заполнено",
|
||||
"field <email> is empty": "Поле \"E-mail\" не заполнено",
|
||||
"field <phoneNumber> is empty": "Поле \"Номер телефона\" не заполнено",
|
||||
"user with this email or login is exist": "Пользователь уже существует",
|
||||
};
|
||||
|
||||
const unknownErrorMessage = "Что-то пошло не так. Повторите попытку позже";
|
||||
|
||||
export function getMessageFromFetchError(error) {
|
||||
if (process.env.NODE_ENV !== "production") console.log(error);
|
||||
|
||||
const message = backendErrorMessage[error.response?.data?.message];
|
||||
if (message) return message;
|
||||
|
||||
if (error.message === "Failed to fetch") return "Ошибка сети";
|
||||
|
||||
if (isAxiosError(error)) {
|
||||
switch (error.code) {
|
||||
case "ERR_NETWORK": return "Ошибка сети";
|
||||
case "ERR_CANCELED": return null;
|
||||
}
|
||||
}
|
||||
|
||||
return unknownErrorMessage;
|
||||
}
|
40
src/utils/backendMessageHandler.ts
Normal file
40
src/utils/backendMessageHandler.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { isAxiosError } from "axios";
|
||||
|
||||
import { backendErrorMessage, BackendErrorMessage } from "../__mocks__/backendErrorMessage";
|
||||
|
||||
const unknownErrorMessage = "Что-то пошло не так. Повторите попытку позже";
|
||||
|
||||
type FetchError = {
|
||||
response?: {
|
||||
data?: {
|
||||
message?: keyof BackendErrorMessage;
|
||||
};
|
||||
};
|
||||
message?: string;
|
||||
code?: string;
|
||||
};
|
||||
|
||||
export function getMessageFromFetchError(error: FetchError): string {
|
||||
if (process.env.NODE_ENV !== "production") console.log(error);
|
||||
|
||||
const messageKey = error.response?.data?.message;
|
||||
|
||||
if (messageKey && backendErrorMessage.hasOwnProperty(messageKey)) {
|
||||
return backendErrorMessage[messageKey];
|
||||
}
|
||||
|
||||
if (error.message === "Failed to fetch") return "Ошибка сети";
|
||||
|
||||
if (isAxiosError(error)) {
|
||||
switch (error.code) {
|
||||
case "ERR_NETWORK":
|
||||
return "Ошибка сети";
|
||||
case "ERR_CANCELED":
|
||||
return "";
|
||||
default:
|
||||
return "Ошибка сети";
|
||||
}
|
||||
}
|
||||
|
||||
return unknownErrorMessage;
|
||||
}
|
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom", "dom.iterable", "esnext"],
|
||||
"types": ["node"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": "src"
|
||||
},
|
||||
"include": ["src", "**/*.ts , src/declaration.d.ts"]
|
||||
}
|
@ -9022,6 +9022,11 @@ typedarray-to-buffer@^3.1.5:
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typescript@^5.1.6:
|
||||
version "5.1.6"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
|
||||
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
|
||||
|
||||
unbox-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user