diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..b416381
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,57 @@
+pipeline {
+ agent {
+ docker {
+ image 'node:20'
+ }
+ }
+
+ stages {
+ stage('install (ci)') {
+ steps {
+ sh 'node -v'
+ sh 'npm -v'
+ script {
+ String tag = sh(returnStdout: true, script: 'git tag --contains').trim()
+ String branchName = sh(returnStdout: true, script: 'git rev-parse --abbrev-ref HEAD').trim()
+ String commit = sh(returnStdout: true, script: 'git log -1 --oneline').trim()
+ String commitMsg = commit.substring(commit.indexOf(' ')).trim()
+
+ if (tag) {
+ currentBuild.displayName = "#${BUILD_NUMBER}, tag ${tag}"
+ } else {
+ currentBuild.displayName = "#${BUILD_NUMBER}, branch ${branchName}"
+ }
+
+ String author = sh(returnStdout: true, script: "git log -1 --pretty=format:'%an'").trim()
+ currentBuild.description = "${author}
${commitMsg}"
+ echo 'starting installing'
+ sh 'npm ci'
+ }
+ }
+ }
+
+ stage('checks') {
+ parallel {
+ stage('eslint') {
+ steps {
+ sh 'npm run eslint'
+ }
+ }
+
+ stage('build') {
+ steps {
+ sh 'npm run build'
+ }
+ }
+ }
+ }
+
+ stage('clean-all') {
+ steps {
+ sh 'rm -rf .[!.]*'
+ sh 'rm -rf ./*'
+ sh 'ls -a'
+ }
+ }
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index d828fec..8015655 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,12 +15,14 @@
"@chakra-ui/react": "^2.4.2",
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
+ "@fontsource/open-sans": "^5.1.0",
"@types/react": "^18.3.12",
"express": "^4.21.1",
"framer-motion": "^6.2.8",
"i18next": "^23.16.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-icons": "^5.3.0",
"react-router-dom": "^6.27.0"
},
"devDependencies": {
@@ -3313,6 +3315,12 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@fontsource/open-sans": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@fontsource/open-sans/-/open-sans-5.1.0.tgz",
+ "integrity": "sha512-g+mjF8gWUDwck9DrRCkhmFeEj7fskjtKZJKAQguVzSg93lc6ThakTHMRgs0dZfe5qBbktrV839tDrb4bIDyZSA==",
+ "license": "OFL-1.1"
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -9091,6 +9099,15 @@
}
}
},
+ "node_modules/react-icons": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz",
+ "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -9971,6 +9988,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz",
"integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==",
+ "license": "MIT",
"engines": {
"node": ">= 18.12.0"
},
diff --git a/package.json b/package.json
index 0ce05c1..52ada7a 100644
--- a/package.json
+++ b/package.json
@@ -23,12 +23,14 @@
"@chakra-ui/react": "^2.4.2",
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
+ "@fontsource/open-sans": "^5.1.0",
"@types/react": "^18.3.12",
"express": "^4.21.1",
"framer-motion": "^6.2.8",
"i18next": "^23.16.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-icons": "^5.3.0",
"react-router-dom": "^6.27.0"
},
"devDependencies": {
diff --git a/remote-assets/demo.mp4 b/remote-assets/demo.mp4
new file mode 100644
index 0000000..4af423d
Binary files /dev/null and b/remote-assets/demo.mp4 differ
diff --git a/src/assets/icons/dry-master-logo.svg b/src/assets/icons/dry-master-logo.svg
new file mode 100644
index 0000000..a98dd4f
--- /dev/null
+++ b/src/assets/icons/dry-master-logo.svg
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts
new file mode 100644
index 0000000..405ca17
--- /dev/null
+++ b/src/assets/icons/index.ts
@@ -0,0 +1 @@
+export { default as LogoSvg } from './dry-master-logo.svg';
\ No newline at end of file
diff --git a/src/assets/images/demo-video-poster.webp b/src/assets/images/demo-video-poster.webp
new file mode 100644
index 0000000..0d2016d
Binary files /dev/null and b/src/assets/images/demo-video-poster.webp differ
diff --git a/src/assets/images/index.ts b/src/assets/images/index.ts
new file mode 100644
index 0000000..9e009f9
--- /dev/null
+++ b/src/assets/images/index.ts
@@ -0,0 +1 @@
+export { default as DemoVideoPosterImg } from './demo-video-poster.webp';
\ No newline at end of file
diff --git a/src/components/PageSpinner/PageSpinner.tsx b/src/components/PageSpinner/PageSpinner.tsx
new file mode 100644
index 0000000..0f0daff
--- /dev/null
+++ b/src/components/PageSpinner/PageSpinner.tsx
@@ -0,0 +1,16 @@
+import React, { FC } from 'react';
+import { Flex, Spinner } from '@chakra-ui/react';
+
+export const PageSpinner: FC = () => {
+ return (
+
+
+
+ );
+};
diff --git a/src/components/PageSpinner/index.ts b/src/components/PageSpinner/index.ts
new file mode 100644
index 0000000..31d2b5c
--- /dev/null
+++ b/src/components/PageSpinner/index.ts
@@ -0,0 +1 @@
+export { PageSpinner } from './PageSpinner';
\ No newline at end of file
diff --git a/src/components/index.ts b/src/components/index.ts
new file mode 100644
index 0000000..3a75cc1
--- /dev/null
+++ b/src/components/index.ts
@@ -0,0 +1 @@
+export * from './PageSpinner';
\ No newline at end of file
diff --git a/src/components/landing/BenefitsSection/BenefitsSection.tsx b/src/components/landing/BenefitsSection/BenefitsSection.tsx
new file mode 100644
index 0000000..6132df2
--- /dev/null
+++ b/src/components/landing/BenefitsSection/BenefitsSection.tsx
@@ -0,0 +1,48 @@
+import React, { FC } from 'react';
+import {
+ MdEco,
+ MdMiscellaneousServices,
+ MdPlace,
+ MdHandshake,
+} from 'react-icons/md';
+import { Heading, HStack, List, Text, VStack } from '@chakra-ui/react';
+import { CtaButton, PageSection } from '../';
+import { ListItem } from './ListItem';
+
+export const BenefitsSection: FC = () => {
+ return (
+
+
+ Преимущества экологичной автомойки
+
+ Откройте для себя преимущества наших услуг по химчистке автомобилей
+
+
+
+ {[
+ {
+ Icon: MdEco,
+ children: 'Экологически безопасные продукты',
+ },
+ {
+ Icon: MdMiscellaneousServices,
+ children: 'Быстрое и эффективное обслуживание',
+ },
+ {
+ Icon: MdPlace,
+ children: 'Удобный мобильный доступ',
+ },
+ {
+ Icon: MdHandshake,
+ children: 'Надежный и заслуживающий доверия',
+ },
+ ].map((props, i) => (
+
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/src/components/landing/BenefitsSection/ListItem/ListItem.tsx b/src/components/landing/BenefitsSection/ListItem/ListItem.tsx
new file mode 100644
index 0000000..4c9343d
--- /dev/null
+++ b/src/components/landing/BenefitsSection/ListItem/ListItem.tsx
@@ -0,0 +1,16 @@
+import React, { FC, PropsWithChildren } from 'react';
+import { ListIcon, ListItem as ChakraListItem } from '@chakra-ui/react';
+import { IconType } from 'react-icons';
+
+type ListItemProps = PropsWithChildren & {
+ Icon: IconType;
+};
+
+export const ListItem: FC = ({ Icon, children }) => {
+ return (
+
+
+ {children}
+
+ );
+};
diff --git a/src/components/landing/BenefitsSection/ListItem/index.ts b/src/components/landing/BenefitsSection/ListItem/index.ts
new file mode 100644
index 0000000..de932bf
--- /dev/null
+++ b/src/components/landing/BenefitsSection/ListItem/index.ts
@@ -0,0 +1 @@
+export { ListItem } from './ListItem';
\ No newline at end of file
diff --git a/src/components/landing/BenefitsSection/index.ts b/src/components/landing/BenefitsSection/index.ts
new file mode 100644
index 0000000..d10431b
--- /dev/null
+++ b/src/components/landing/BenefitsSection/index.ts
@@ -0,0 +1 @@
+export { BenefitsSection } from './BenefitsSection';
\ No newline at end of file
diff --git a/src/components/landing/CtaButton/CtaButton.tsx b/src/components/landing/CtaButton/CtaButton.tsx
new file mode 100644
index 0000000..a1168f3
--- /dev/null
+++ b/src/components/landing/CtaButton/CtaButton.tsx
@@ -0,0 +1,11 @@
+import React, { FC } from 'react';
+import { Link as RouterLink } from 'react-router-dom';
+import { ButtonProps, Button } from '@chakra-ui/react';
+
+export const CtaButton: FC = (props) => {
+ return (
+
+ );
+};
diff --git a/src/components/landing/CtaButton/index.ts b/src/components/landing/CtaButton/index.ts
new file mode 100644
index 0000000..08818cf
--- /dev/null
+++ b/src/components/landing/CtaButton/index.ts
@@ -0,0 +1 @@
+export { CtaButton } from './CtaButton';
\ No newline at end of file
diff --git a/src/components/landing/Footer/Copyright/Copyright.tsx b/src/components/landing/Footer/Copyright/Copyright.tsx
new file mode 100644
index 0000000..bc26f23
--- /dev/null
+++ b/src/components/landing/Footer/Copyright/Copyright.tsx
@@ -0,0 +1,8 @@
+import React, { FC } from 'react';
+import { Text } from '@chakra-ui/react';
+
+const currentYear = new Date().getFullYear();
+
+export const Copyright: FC = () => {
+ return © {currentYear} DryMaster. Все права защищены;
+};
diff --git a/src/components/landing/Footer/Copyright/index.ts b/src/components/landing/Footer/Copyright/index.ts
new file mode 100644
index 0000000..f80e480
--- /dev/null
+++ b/src/components/landing/Footer/Copyright/index.ts
@@ -0,0 +1 @@
+export { Copyright } from './Copyright';
\ No newline at end of file
diff --git a/src/components/landing/Footer/Footer.tsx b/src/components/landing/Footer/Footer.tsx
new file mode 100644
index 0000000..26b8462
--- /dev/null
+++ b/src/components/landing/Footer/Footer.tsx
@@ -0,0 +1,27 @@
+import React, { FC } from 'react';
+import { Link, List, ListItem } from '@chakra-ui/react';
+import { Link as RouterLink } from 'react-router-dom';
+import { SiteLogo, PageSection } from '../';
+import { Copyright } from './Copyright';
+
+export const Footer: FC = () => {
+ return (
+
+
+
+
+ {[
+ { to: '#', label: 'Политика конфиденциальности' },
+ { to: '#', label: 'Условия обслуживания' },
+ { to: '#', label: 'FAQ' },
+ ].map(({ to, label }, i) => (
+
+
+ {label}
+
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/landing/Footer/index.ts b/src/components/landing/Footer/index.ts
new file mode 100644
index 0000000..f6e9523
--- /dev/null
+++ b/src/components/landing/Footer/index.ts
@@ -0,0 +1 @@
+export { Footer } from './Footer';
\ No newline at end of file
diff --git a/src/components/landing/HeroSection/HeroSection.tsx b/src/components/landing/HeroSection/HeroSection.tsx
new file mode 100644
index 0000000..412eab0
--- /dev/null
+++ b/src/components/landing/HeroSection/HeroSection.tsx
@@ -0,0 +1,59 @@
+import React, { FC } from 'react';
+import { Box, Heading, Text, Center, VStack, BoxProps } from '@chakra-ui/react';
+import { DemoVideoPosterImg } from '../../../assets/images';
+import { CtaButton, SiteLogo, PageSection } from '../';
+
+type HeroSectionProps = Pick;
+
+export const HeroSection: FC = ({ flexShrink }) => {
+ return (
+
+
+
+
+
+
+
+
+ Оживите свою поездку с помощью экологически чистого ухода!
+
+
+ Ощутите максимальное удобство сухой мойки автомобилей, созданной для
+ того, чтобы планета стала чище
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/components/landing/HeroSection/index.ts b/src/components/landing/HeroSection/index.ts
new file mode 100644
index 0000000..ac31a65
--- /dev/null
+++ b/src/components/landing/HeroSection/index.ts
@@ -0,0 +1 @@
+export { HeroSection } from './HeroSection';
\ No newline at end of file
diff --git a/src/components/landing/PageSection/PageSection.tsx b/src/components/landing/PageSection/PageSection.tsx
new file mode 100644
index 0000000..23c04fe
--- /dev/null
+++ b/src/components/landing/PageSection/PageSection.tsx
@@ -0,0 +1,12 @@
+import React, { FC, PropsWithChildren } from 'react';
+import { StackProps, VStack } from '@chakra-ui/react';
+
+type PageSectionProps = StackProps & PropsWithChildren;
+
+export const PageSection: FC = ({ children, ...restProps }) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/components/landing/PageSection/index.ts b/src/components/landing/PageSection/index.ts
new file mode 100644
index 0000000..5289ae0
--- /dev/null
+++ b/src/components/landing/PageSection/index.ts
@@ -0,0 +1 @@
+export { PageSection } from './PageSection';
\ No newline at end of file
diff --git a/src/components/landing/SiteLogo/SiteLogo.tsx b/src/components/landing/SiteLogo/SiteLogo.tsx
new file mode 100644
index 0000000..449a117
--- /dev/null
+++ b/src/components/landing/SiteLogo/SiteLogo.tsx
@@ -0,0 +1,10 @@
+import React, { FC } from 'react';
+import { Image } from '@chakra-ui/react';
+import { LogoSvg } from '../../../assets/icons';
+
+export const SiteLogo: FC = () => {
+ return ;
+};
+
+// todo: add i18n for alt
+// todo: replace Image by SVG React component
\ No newline at end of file
diff --git a/src/components/landing/SiteLogo/index.ts b/src/components/landing/SiteLogo/index.ts
new file mode 100644
index 0000000..48bcc7d
--- /dev/null
+++ b/src/components/landing/SiteLogo/index.ts
@@ -0,0 +1 @@
+export { SiteLogo } from './SiteLogo';
diff --git a/src/components/landing/SocialProofSection/ReviewsSlider/ReviewCard/ReviewCard.tsx b/src/components/landing/SocialProofSection/ReviewsSlider/ReviewCard/ReviewCard.tsx
new file mode 100644
index 0000000..fcf3697
--- /dev/null
+++ b/src/components/landing/SocialProofSection/ReviewsSlider/ReviewCard/ReviewCard.tsx
@@ -0,0 +1,37 @@
+import React, { FC } from 'react';
+import { Card, Avatar, Text } from '@chakra-ui/react';
+
+type ReviewCardProps = {
+ firstname: string;
+ lastname: string;
+ picture: string;
+ text: string;
+};
+
+export const ReviewCard: FC = ({
+ firstname,
+ lastname,
+ picture,
+ text,
+}) => {
+ const name = [firstname, lastname].join(' ');
+
+ return (
+
+
+
+ {text}
+
+
+ );
+};
diff --git a/src/components/landing/SocialProofSection/ReviewsSlider/ReviewCard/index.ts b/src/components/landing/SocialProofSection/ReviewsSlider/ReviewCard/index.ts
new file mode 100644
index 0000000..1582f4d
--- /dev/null
+++ b/src/components/landing/SocialProofSection/ReviewsSlider/ReviewCard/index.ts
@@ -0,0 +1 @@
+export { ReviewCard } from './ReviewCard';
\ No newline at end of file
diff --git a/src/components/landing/SocialProofSection/ReviewsSlider/ReviewsSlider.tsx b/src/components/landing/SocialProofSection/ReviewsSlider/ReviewsSlider.tsx
new file mode 100644
index 0000000..be89118
--- /dev/null
+++ b/src/components/landing/SocialProofSection/ReviewsSlider/ReviewsSlider.tsx
@@ -0,0 +1,73 @@
+import React, { FC, useEffect, useState } from 'react';
+import {
+ Tab,
+ TabList,
+ TabPanel,
+ TabPanels,
+ Tabs,
+ Text,
+} from '@chakra-ui/react';
+import { mockReviews } from '../../../../mocks/landing';
+import { ReviewCard } from './ReviewCard';
+
+const reviewsCount = mockReviews.length;
+const SLIDE_CHANGE_INTERVAL = 5000;
+
+export const ReviewsSlider: FC = () => {
+ const [activeTab, setActiveTab] = useState(0);
+ const [isSlideShowStopped, setIsSlideShowStopped] = useState(false);
+
+ useEffect(() => {
+ const timer = setInterval(() => {
+ const newActiveTab = (activeTab + 1) % reviewsCount;
+ setActiveTab(newActiveTab);
+ }, SLIDE_CHANGE_INTERVAL);
+
+ if (isSlideShowStopped) {
+ clearInterval(timer);
+ }
+
+ return () => {
+ clearInterval(timer);
+ };
+ }, [activeTab]);
+
+ return (
+ {
+ setIsSlideShowStopped(true);
+ setActiveTab(selectedTab);
+ }}
+ display='flex'
+ flexDir='column'
+ alignItems='center'
+ variant='soft-rounded'
+ colorScheme='gray'
+ >
+
+ {mockReviews.map(({ id }, i) => (
+
+ {i}
+
+ ))}
+
+
+ {mockReviews.map(({ id, ...reviewProps }) => (
+
+
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/landing/SocialProofSection/ReviewsSlider/index.ts b/src/components/landing/SocialProofSection/ReviewsSlider/index.ts
new file mode 100644
index 0000000..773341e
--- /dev/null
+++ b/src/components/landing/SocialProofSection/ReviewsSlider/index.ts
@@ -0,0 +1 @@
+export { ReviewsSlider } from './ReviewsSlider';
\ No newline at end of file
diff --git a/src/components/landing/SocialProofSection/SocialProofSection.tsx b/src/components/landing/SocialProofSection/SocialProofSection.tsx
new file mode 100644
index 0000000..d2ec90a
--- /dev/null
+++ b/src/components/landing/SocialProofSection/SocialProofSection.tsx
@@ -0,0 +1,19 @@
+import React, { FC } from 'react';
+import {
+ Heading,
+ HStack,
+} from '@chakra-ui/react';
+import { CtaButton, PageSection } from '../';
+import { ReviewsSlider } from './ReviewsSlider';
+
+export const SocialProofSection: FC = () => {
+ return (
+
+ Нас выбирают
+
+
+
+
+
+ );
+};
diff --git a/src/components/landing/SocialProofSection/index.ts b/src/components/landing/SocialProofSection/index.ts
new file mode 100644
index 0000000..e20037f
--- /dev/null
+++ b/src/components/landing/SocialProofSection/index.ts
@@ -0,0 +1 @@
+export { SocialProofSection } from './SocialProofSection';
\ No newline at end of file
diff --git a/src/components/landing/index.ts b/src/components/landing/index.ts
new file mode 100644
index 0000000..54cda58
--- /dev/null
+++ b/src/components/landing/index.ts
@@ -0,0 +1,7 @@
+export * from './CtaButton';
+export * from './PageSection';
+export * from './BenefitsSection'; // CtaButton, PageSection
+export * from './SocialProofSection'; // CtaButton, PageSection
+export * from './SiteLogo';
+export * from './Footer'; // PageSection, SiteLogo
+export * from './HeroSection'; // CtaButton, PageSection, SiteLogo
\ No newline at end of file
diff --git a/src/containers/LandingThemeProvider/Fonts.tsx b/src/containers/LandingThemeProvider/Fonts.tsx
new file mode 100644
index 0000000..5e72244
--- /dev/null
+++ b/src/containers/LandingThemeProvider/Fonts.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { Global } from '@emotion/react';
+import '@fontsource/open-sans/400.css';
+import '@fontsource/open-sans/700.css';
+
+const Fonts = () => (
+
+);
+
+export default Fonts;
diff --git a/src/containers/LandingThemeProvider/LandingThemeProvider.tsx b/src/containers/LandingThemeProvider/LandingThemeProvider.tsx
new file mode 100644
index 0000000..c52bf1d
--- /dev/null
+++ b/src/containers/LandingThemeProvider/LandingThemeProvider.tsx
@@ -0,0 +1,13 @@
+import React, { FC, PropsWithChildren } from 'react';
+import { ChakraProvider } from '@chakra-ui/react';
+import { default as landingTheme } from './theme-config';
+import Fonts from './Fonts';
+
+export const LandingThemeProvider: FC = ({ children }) => {
+ return (
+
+
+ {children}
+
+ );
+};
diff --git a/src/containers/LandingThemeProvider/index.ts b/src/containers/LandingThemeProvider/index.ts
new file mode 100644
index 0000000..b68aca1
--- /dev/null
+++ b/src/containers/LandingThemeProvider/index.ts
@@ -0,0 +1 @@
+export { LandingThemeProvider } from './LandingThemeProvider';
\ No newline at end of file
diff --git a/src/containers/LandingThemeProvider/theme-config.ts b/src/containers/LandingThemeProvider/theme-config.ts
new file mode 100644
index 0000000..43a0676
--- /dev/null
+++ b/src/containers/LandingThemeProvider/theme-config.ts
@@ -0,0 +1,42 @@
+import { extendTheme } from '@chakra-ui/react';
+
+const overrides = {
+ colors: {
+ primary: {
+ 50: "#F1F8ED",
+ 100: "#D9EACC",
+ 200: "#C0DDAC",
+ 300: "#A8D08B",
+ 400: "#8FC26B",
+ 500: "#77B54A",
+ 600: "#5F913B",
+ 700: "#476D2C",
+ 800: "#2F481E",
+ 900: "#18240F"
+ },
+ secondary: {
+ 50: "#E7F7FD",
+ 100: "#BCEAFB",
+ 200: "#91DCF8",
+ 300: "#66CEF5",
+ 400: "#3BC1F2",
+ 500: "#0FB3F0",
+ 600: "#0C8FC0",
+ 700: "#096B90",
+ 800: "#064860",
+ 900: "#032430"
+ }
+ },
+ fonts: {
+ heading: `'Open Sans', sans-serif`,
+ },
+ styles: {
+ global: {
+ body: {
+ bg: 'gray.100'
+ }
+ },
+ },
+};
+
+export default extendTheme(overrides);
\ No newline at end of file
diff --git a/src/containers/index.ts b/src/containers/index.ts
new file mode 100644
index 0000000..f2db842
--- /dev/null
+++ b/src/containers/index.ts
@@ -0,0 +1 @@
+export * from './LandingThemeProvider';
\ No newline at end of file
diff --git a/src/mocks/landing/index.ts b/src/mocks/landing/index.ts
new file mode 100644
index 0000000..e6862bc
--- /dev/null
+++ b/src/mocks/landing/index.ts
@@ -0,0 +1,34 @@
+type ReviewItem = {
+ id: string;
+ firstname: string;
+ lastname: string;
+ picture: string;
+ text: string;
+};
+
+export const mockReviews: ReviewItem[] = [
+ {
+ firstname: 'Анна',
+ lastname: 'Смирнова',
+ picture: 'https://img.freepik.com/free-photo/indoor-portrait-beautiful-freckled-woman-with-dark-curly-hair-wears-fashionable-striped-shirt-rejoices-day-off-isolated-white-wall-curly-satisfied-woman-stands-indoor-alone_273609-15765.jpg',
+ text: "Недавно воспользовалась услугами сухой мойки автомобилей и осталась крайне удовлетворена. Процесс был проведён профессионально: сотрудники использовали качественные средства, которые не повредили лакокрасочное покрытие. Особенно впечатлила возможность мыть машину без воды, что не только экономит ресурсы, но и бережет окружающую среду. Рекомендую всем, кто заботится о своём автомобиле и экологии!"
+ },
+ {
+ firstname: 'Дмитрий',
+ lastname: 'Петров',
+ picture: 'https://img.freepik.com/free-photo/calm-handsome-curly-haired-boy-posing-isolated-light-grey-standing-still-looks-peaceful-wearing-casual-manner-youth-style-concept_176532-8831.jpg',
+ text: "Как же я рад, что нашел эту сухую мойку! Моя машина сияет, как новенькая! 🌟 Сначала был скептически настроен, думал, как же без воды можно отмыть всё это? Но результат превзошёл все ожидания! Ветеринар мойки профессионально подошёл к делу, и она теперь выглядит потрясающе. Если вы хотите, чтобы ваш автомобиль всегда выглядел на 100%, обязательно попробуйте!"
+ },
+ {
+ firstname: 'Алексей',
+ lastname: 'Сидоров',
+ picture: 'https://img.freepik.com/free-photo/waist-up-portrait-handsome-serious-unshaven-male-keeps-hands-together-dressed-dark-blue-shirt-has-talk-with-interlocutor-stands-against-white-wall-self-confident-man-freelancer_273609-16320.jpg',
+ text: "Сухая мойка автомобилей - интересное решение, которое я опробовал недавно. В целом остался доволен качеством работы. Однако, не все загрязнения удалось удалить с первого раза, но сотрудник предложил дополнительные услуги, что меня устроило. Плюс, большое внимание уделили защите поверхности, что тоже немаловажно. Думаю, в следующий раз снова воспользуюсь этой услугой."
+ },
+ {
+ firstname: 'Екатерина',
+ lastname: 'Иванова',
+ picture: 'https://img.freepik.com/free-photo/portrait-young-blonde-woman-with-plait-polka-dot-blouse_273609-10490.jpg',
+ text: "К сожалению, мой опыт с сухой мойкой автомобилей оказался неудачным. Ожидала увидеть чистую машину после процедуры, но многие участки остались незаделанными. Кроме того, процедура заняла больше времени, чем мне обещали. Возможно, в этом конкретном центре что-то пошло не так, но я бы не стала повторно обращаться за этой услугой."
+ },
+].map((data, i) => ({ id: `review${i}`, ...data }));
\ No newline at end of file
diff --git a/src/pages/landing/index.tsx b/src/pages/landing/index.tsx
index 96ee58e..1097a50 100644
--- a/src/pages/landing/index.tsx
+++ b/src/pages/landing/index.tsx
@@ -1,7 +1,37 @@
-import React from "react";
+import React, { FC } from 'react';
+import { Box, Container, VStack } from '@chakra-ui/react';
+import {
+ BenefitsSection,
+ Footer,
+ HeroSection,
+ SocialProofSection,
+} from '../../components/landing';
+import { LandingThemeProvider } from '../../containers';
-const Page = () => {
- return Landing
;
+const Page: FC = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
};
export default Page;
diff --git a/src/routes.tsx b/src/routes.tsx
index 6728eb6..ab2dbfa 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -1,16 +1,24 @@
-import React from 'react';
+import React, { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
+import { PageSpinner } from './components';
import Arm from './pages/arm';
-import Order from './pages/order-view';
-import Landing from './pages/landing';
+
+const Landing = lazy(() => import('./pages/landing'));
+const OrderForm = lazy(() => import('./pages/order-form'));
+const OrderView = lazy(() => import('./pages/order-view'));
const Routers = () => {
return (
-
- }>
- }>
- }>
-
+ }>
+
+
+ } />
+ } />
+ } />
+
+ }>
+
+
);
};
diff --git a/types.d.ts b/types.d.ts
index 14f21ec..62e52ee 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -3,3 +3,17 @@ declare interface NodeModule {
accept: (path: string, callback: () => void) => void;
};
}
+
+declare module "*.svg" {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const value: any;
+ export = value;
+}
+
+declare module "*.webp" {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const value: any;
+ export = value;
+}
+
+declare const __webpack_public_path__: string;
\ No newline at end of file