feat: apply success stubs to landing content (#33)
Some checks failed
it-academy/dry-wash-pl/pipeline/head This commit looks good
it-academy/dry-wash-pl/pipeline/pr-main There was a failure building this commit

This commit is contained in:
RustamRu 2024-11-17 18:07:35 +03:00
parent 2fb59303d2
commit 519be23ea6
13 changed files with 109 additions and 61 deletions

View File

@ -1,50 +1,27 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import {
MdEco,
MdMiscellaneousServices,
MdPlace,
MdHandshake,
} from 'react-icons/md';
import { Heading, HStack, List, Text, VStack } from '@chakra-ui/react'; import { Heading, HStack, List, Text, VStack } from '@chakra-ui/react';
import { CtaButton, PageSection } from '../'; import { CtaButton, PageSection } from '../';
import { ListItem } from './ListItem'; import { ListItem } from './ListItem';
import { BenefitsSectionProps } from './types';
import { iconsMap } from './helper';
export const BenefitsSection: FC = () => { export const BenefitsSection: FC<BenefitsSectionProps> = ({
const { t } = useTranslation('~', { data: { heading, description, list },
keyPrefix: 'dry-wash.landing.benefits-section', }) => {
}); const { t } = useTranslation('~', { keyPrefix: 'dry-wash.landing' });
const listData = [
{
Icon: MdEco,
children: t('list.0'),
},
{
Icon: MdMiscellaneousServices,
children: t('list.1'),
},
{
Icon: MdPlace,
children: t('list.2'),
},
{
Icon: MdHandshake,
children: t('list.3'),
},
];
return ( return (
<PageSection> <PageSection>
<VStack w='full' spacing={2}> <VStack w='full' spacing={2}>
<Heading as='h2'>{t('heading')}</Heading> <Heading as='h2'>{t(heading)}</Heading>
<Text> <Text>{t(description)}</Text>
{t('description')}
</Text>
</VStack> </VStack>
<List display='flex' flexDirection='column' spacing={3}> <List display='flex' flexDirection='column' spacing={3}>
{listData.map((props, i) => ( {list.map((itemKey, i) => (
<ListItem key={i} {...props} /> <ListItem key={i} Icon={iconsMap[itemKey]}>
{t(itemKey)}
</ListItem>
))} ))}
</List> </List>
<HStack w='full' justify='flex-end'> <HStack w='full' justify='flex-end'>

View File

@ -0,0 +1,11 @@
import { IconType } from "react-icons";
import { MdEco, MdMiscellaneousServices, MdPlace, MdHandshake } from "react-icons/md";
import { ArrElement } from "../../../lib";
import { BenefitsList } from "./types";
export const iconsMap: Record<ArrElement<BenefitsList>, IconType> = {
"benefits-section.list.0": MdEco,
"benefits-section.list.1": MdMiscellaneousServices,
"benefits-section.list.2": MdPlace,
"benefits-section.list.3": MdHandshake,
};

View File

@ -1 +1,2 @@
export type { BenefitsSectionProps } from './types';
export { BenefitsSection } from './BenefitsSection'; export { BenefitsSection } from './BenefitsSection';

View File

@ -0,0 +1,14 @@
export type BenefitsList = [
'benefits-section.list.0',
'benefits-section.list.1',
'benefits-section.list.2',
'benefits-section.list.3',
];
export type BenefitsSectionProps = {
data: {
heading: 'benefits-section.heading';
description: 'benefits-section.description';
list: BenefitsList;
};
};

View File

@ -5,7 +5,7 @@ import { ButtonProps, Button } from '@chakra-ui/react';
import { URLs } from '../../../__data__/urls'; import { URLs } from '../../../__data__/urls';
export const CtaButton: FC<ButtonProps> = (props) => { export const CtaButton: FC<ButtonProps> = (props) => {
const { t } = useTranslation(); const { t } = useTranslation('~', { keyPrefix: 'dry-wash.landing' });
return ( return (
<Button <Button
@ -14,7 +14,7 @@ export const CtaButton: FC<ButtonProps> = (props) => {
colorScheme='primary' colorScheme='primary'
{...props} {...props}
> >
{t('dry-wash.landing.make-order-button')} {t('make-order-button')}
</Button> </Button>
); );
}; };

View File

@ -1,21 +1,21 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Box, Heading, Text, Center, VStack, BoxProps } from '@chakra-ui/react'; import { Box, Heading, Text, Center, VStack } from '@chakra-ui/react';
import { DemoVideoPosterImg } from '../../../assets/images'; import { DemoVideoPosterImg } from '../../../assets/images';
import { CtaButton, SiteLogo, PageSection } from '../'; import { CtaButton, SiteLogo, PageSection } from '../';
import { HeroSectionProps } from './types';
type HeroSectionProps = Pick<BoxProps, 'flexShrink'>; export const HeroSection: FC<HeroSectionProps> = ({
data: { headline, description, video },
export const HeroSection: FC<HeroSectionProps> = ({ flexShrink }) => { flexShrink,
const { t } = useTranslation('~', { }) => {
keyPrefix: 'dry-wash.landing.hero-section', const { t } = useTranslation('~', { keyPrefix: 'dry-wash.landing' });
});
return ( return (
<Box flexShrink={flexShrink} as='header' pos='relative' zIndex={0}> <Box flexShrink={flexShrink} as='header' pos='relative' zIndex={0}>
<Box <Box
as='video' as='video'
src={`${__webpack_public_path__}/remote-assets/demo.mp4`} src={`${__webpack_public_path__}/remote-assets/${video}`}
poster={DemoVideoPosterImg} poster={DemoVideoPosterImg}
autoPlay autoPlay
loop loop
@ -46,14 +46,14 @@ export const HeroSection: FC<HeroSectionProps> = ({ flexShrink }) => {
color='white' color='white'
__css={{ textWrap: 'balance' }} __css={{ textWrap: 'balance' }}
> >
{t('headline')} {t(headline)}
</Heading> </Heading>
<Text <Text
textAlign='center' textAlign='center'
__css={{ textWrap: 'balance' }} __css={{ textWrap: 'balance' }}
color='white' color='white'
> >
{t('description')} {t(description)}
</Text> </Text>
</VStack> </VStack>
<CtaButton size='lg' /> <CtaButton size='lg' />

View File

@ -0,0 +1,9 @@
import { BoxProps } from "@chakra-ui/react";
export type HeroSectionProps = {
data: {
headline: 'hero-section.headline';
description: 'hero-section.description';
video: string;
};
} & Pick<BoxProps, 'flexShrink'>;

View File

@ -3,15 +3,16 @@ import { useTranslation } from 'react-i18next';
import { Heading, HStack } from '@chakra-ui/react'; import { Heading, HStack } from '@chakra-ui/react';
import { CtaButton, PageSection } from '../'; import { CtaButton, PageSection } from '../';
import { ReviewsSlider } from './ReviewsSlider'; import { ReviewsSlider } from './ReviewsSlider';
import { SocialProofSectionProps } from './types';
export const SocialProofSection: FC = () => { export const SocialProofSection: FC<SocialProofSectionProps> = ({
const { t } = useTranslation('~', { data: { heading },
keyPrefix: 'dry-wash.landing.social-proof-section', }) => {
}); const { t } = useTranslation('~', { keyPrefix: 'dry-wash.landing' });
return ( return (
<PageSection> <PageSection>
<Heading as='h2'>{t('heading')}</Heading> <Heading as='h2'>{t(heading)}</Heading>
<ReviewsSlider /> <ReviewsSlider />
<HStack w='full' justify='flex-end'> <HStack w='full' justify='flex-end'>
<CtaButton /> <CtaButton />

View File

@ -1 +1,2 @@
export type { SocialProofSectionProps } from './types';
export { SocialProofSection } from './SocialProofSection'; export { SocialProofSection } from './SocialProofSection';

View File

@ -0,0 +1,5 @@
export type SocialProofSectionProps = {
data: {
heading: 'social-proof-section.heading';
};
};

View File

@ -1,3 +1,11 @@
/**
* @example type Output = ArrElement<['a', 'b', 'c']>;
* // "a" | "b" | "c"
*/
export type ArrElement<ArrType> = ArrType extends readonly (infer ElementType)[]
? ElementType
: never;
/** /**
* @example type Output = Split<'a.b1' | 'a.b2', '.'>; * @example type Output = Split<'a.b1' | 'a.b2', '.'>;
* // ["a", "b1"] | ["a", "b2"] * // ["a", "b1"] | ["a", "b2"]

View File

@ -1,12 +1,9 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { Container, VStack } from '@chakra-ui/react'; import { Container, VStack } from '@chakra-ui/react';
import { import LandingSuccess from '../../../stubs/json/landing/landing-success.json';
BenefitsSection, import { BenefitsSection, Footer, HeroSection, SocialProofSection } from '../../components/landing';
Footer,
HeroSection,
SocialProofSection,
} from '../../components/landing';
import { LandingThemeProvider } from '../../containers'; import { LandingThemeProvider } from '../../containers';
import { isBenefitsSectionData, isSocialProofSectionData } from './types';
const Page: FC = () => { const Page: FC = () => {
return ( return (
@ -20,10 +17,19 @@ const Page: FC = () => {
centerContent centerContent
> >
<VStack w='full' h='full' alignItems='stretch' flexGrow={1}> <VStack w='full' h='full' alignItems='stretch' flexGrow={1}>
<HeroSection flexShrink={0} /> <HeroSection
data={LandingSuccess.body['hero-section']}
flexShrink={0}
/>
<VStack as='main' flexGrow={1}> <VStack as='main' flexGrow={1}>
<BenefitsSection /> {LandingSuccess.body.sections.map(({ type, ...data }, i) => {
<SocialProofSection /> if (isBenefitsSectionData(type, data)) {
return <BenefitsSection key={i} data={data} />;
}
if (isSocialProofSectionData(type, data)) {
return <SocialProofSection key={i} data={data} />;
}
})}
</VStack> </VStack>
<Footer /> <Footer />
</VStack> </VStack>

View File

@ -0,0 +1,15 @@
import LandingSuccess from "../../../stubs/json/landing/landing-success.json";
import { BenefitsSectionProps, SocialProofSectionProps } from "../../components/landing";
import { ArrElement } from "../../lib";
type SectionsItemData = ArrElement<LandingSuccess['body']['sections']>;
type SectionType = SectionsItemData['type'];
type SectionData = Omit<SectionsItemData, 'type'>;
export const isBenefitsSectionData = (type: SectionType, data: SectionData): data is BenefitsSectionProps['data'] => {
return type === 'benefits-section';
};
export const isSocialProofSectionData = (type: SectionType, data: SectionData): data is SocialProofSectionProps['data'] => {
return type === 'social-proof-section';
};