feat: apply success stubs to landing content (#33)
This commit is contained in:
parent
409473413a
commit
407da721af
@ -1,52 +1,29 @@
|
|||||||
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'>
|
||||||
|
11
src/components/landing/BenefitsSection/helper.ts
Normal file
11
src/components/landing/BenefitsSection/helper.ts
Normal 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,
|
||||||
|
};
|
@ -1 +1,2 @@
|
|||||||
|
export type { BenefitsSectionProps } from './types';
|
||||||
export { BenefitsSection } from './BenefitsSection';
|
export { BenefitsSection } from './BenefitsSection';
|
14
src/components/landing/BenefitsSection/types.ts
Normal file
14
src/components/landing/BenefitsSection/types.ts
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
@ -6,7 +6,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
|
||||||
@ -15,7 +15,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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,22 +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
|
||||||
@ -47,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' />
|
||||||
|
9
src/components/landing/HeroSection/types.ts
Normal file
9
src/components/landing/HeroSection/types.ts
Normal 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'>;
|
@ -5,15 +5,16 @@ 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 />
|
||||||
|
@ -1 +1,2 @@
|
|||||||
|
export type { SocialProofSectionProps } from './types';
|
||||||
export { SocialProofSection } from './SocialProofSection';
|
export { SocialProofSection } from './SocialProofSection';
|
5
src/components/landing/SocialProofSection/types.ts
Normal file
5
src/components/landing/SocialProofSection/types.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type SocialProofSectionProps = {
|
||||||
|
data: {
|
||||||
|
heading: 'social-proof-section.heading';
|
||||||
|
};
|
||||||
|
};
|
@ -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"]
|
||||||
|
@ -1,13 +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 LandingSuccess from '../../../stubs/json/landing/landing-success.json';
|
||||||
import {
|
import { BenefitsSection, Footer, HeroSection, SocialProofSection } from '../../components/landing';
|
||||||
BenefitsSection,
|
|
||||||
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 (
|
||||||
@ -21,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>
|
||||||
|
15
src/pages/landing/types.ts
Normal file
15
src/pages/landing/types.ts
Normal 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';
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user