refactor update

This commit is contained in:
ArtChaos189 2023-08-15 14:52:09 +03:00
parent 143a8d1e57
commit 32bd57a113
70 changed files with 2458 additions and 2241 deletions

@ -49,5 +49,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"typescript": "^5.1.6"
}
}

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 />
</>
);
}

@ -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": "Пользователь уже существует",
};

@ -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: "/",
},
];

@ -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",
},
];

@ -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,
},
];

@ -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>
);
}

@ -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>
);
};

@ -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>
);
}

@ -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>
);
}

@ -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

@ -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>
);

@ -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>
);

@ -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>
);

@ -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>
);

@ -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);

@ -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>
);
}

@ -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>
);

@ -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>
);

@ -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>
</>
);
};

@ -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>
</>
);
};

@ -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>
);
}

@ -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>
);
}

@ -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;

@ -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>
);

@ -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>
);
}

@ -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);

@ -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;

@ -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;

@ -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>
);

@ -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>
);

@ -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>
);
};

@ -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

@ -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

@ -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

@ -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,8 +2,6 @@ import { create } from "zustand";
import { sendContactFormRequest } from "../api/contactForm";
import { getMessageFromFetchError } from "../utils/backendMessageHandler";
const initialState = {
isModalOpen: true,
isSubmitDisabled: false,
@ -13,9 +11,7 @@ const initialState = {
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 });
@ -33,8 +29,10 @@ export const sendContactForm = async () => {
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 (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}`;

@ -15,16 +15,16 @@ export const useUnauthTicketStore = create()(
isPreventAutoscroll: false,
}),
{
name: "Unauth tickets"
name: "Unauth tickets",
}
),
{
version: 0,
name: "unauth-ticket",
storage: createJSONStorage(() => localStorage),
partialize: state => ({
partialize: (state) => ({
sessionData: state.sessionData,
})
}),
}
)
);
@ -36,7 +36,7 @@ export const addOrUpdateUnauthMessages = (receivedMessages) => {
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);
@ -52,7 +52,8 @@ export const incrementUnauthMessageApiPage = () => {
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();

@ -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;
}

@ -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

@ -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"