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..4816c26 --- /dev/null +++ b/src/components/landing/HeroSection/HeroSection.tsx @@ -0,0 +1,60 @@ +import React, { FC } from 'react'; +import { Box, Heading, Text, Center, VStack, BoxProps } from '@chakra-ui/react'; +import { DemoVideo } from '../../../assets/videos'; +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