Compare commits
3 Commits
d15d761f1a
...
519be23ea6
Author | SHA1 | Date | |
---|---|---|---|
|
519be23ea6 | ||
|
2fb59303d2 | ||
|
f67abf5e46 |
88
package-lock.json
generated
88
package-lock.json
generated
@ -34,6 +34,7 @@
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"globals": "^15.11.0",
|
||||
"prettier": "3.3.3",
|
||||
"ts-json-as-const": "^1.0.7",
|
||||
"typescript-eslint": "^8.12.2"
|
||||
}
|
||||
},
|
||||
@ -2863,6 +2864,13 @@
|
||||
"react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@dfoverdx/tocamelcase": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@dfoverdx/tocamelcase/-/tocamelcase-1.0.7.tgz",
|
||||
"integrity": "sha512-QDlMJqwcE4eVCvxxQXp8nh7Nw9m5VQHPCAiyTD+W86Tl89VGhVJRb//RJRZKpn5A/Bq3EQNYDYlepurQ805MOQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz",
|
||||
@ -6777,6 +6785,13 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/get-own-enumerable-property-symbols": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
|
||||
"integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/get-symbol-description": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
|
||||
@ -7573,6 +7588,19 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-obj": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-3.0.0.tgz",
|
||||
"integrity": "sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-path-cwd": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
|
||||
@ -7635,6 +7663,19 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-regexp": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz",
|
||||
"integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-root": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz",
|
||||
@ -7783,6 +7824,19 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isbinaryfile": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
|
||||
"integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/gjtorikian/"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
@ -10123,6 +10177,21 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/stringify-object": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-4.0.1.tgz",
|
||||
"integrity": "sha512-qpV1FBpN0R1gDAhCHIU71SYGZb35Te+gOQbQ6lYRmVJT7pF1NB8mkHeEJvyYNiHXw+fB4KIbeIjQl1rgiIijiA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"get-own-enumerable-property-symbols": "^3.0.2",
|
||||
"is-obj": "^3.0.0",
|
||||
"is-regexp": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
@ -10413,6 +10482,25 @@
|
||||
"typescript": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-json-as-const": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/ts-json-as-const/-/ts-json-as-const-1.0.7.tgz",
|
||||
"integrity": "sha512-UMM24g4uBevqBuEqeMUNm2yBEd6VrpJt2hhGWSpmr3nGuhQYwYcLpmxUjsAh5qxJbafF+ICrHvOvHn16zh9Ojg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@dfoverdx/tocamelcase": "^1.0.7",
|
||||
"isbinaryfile": "^4.0.8",
|
||||
"json5": "^2.2.0",
|
||||
"stringify-object": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"ts-json-as-const": "index.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
|
||||
|
@ -42,6 +42,7 @@
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"globals": "^15.11.0",
|
||||
"prettier": "3.3.3",
|
||||
"ts-json-as-const": "^1.0.7",
|
||||
"typescript-eslint": "^8.12.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,27 @@
|
||||
import React, { FC } from 'react';
|
||||
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 { CtaButton, PageSection } from '../';
|
||||
import { ListItem } from './ListItem';
|
||||
import { BenefitsSectionProps } from './types';
|
||||
import { iconsMap } from './helper';
|
||||
|
||||
export const BenefitsSection: FC = () => {
|
||||
const { t } = useTranslation('~', {
|
||||
keyPrefix: 'dry-wash.landing.benefits-section',
|
||||
});
|
||||
|
||||
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'),
|
||||
},
|
||||
];
|
||||
export const BenefitsSection: FC<BenefitsSectionProps> = ({
|
||||
data: { heading, description, list },
|
||||
}) => {
|
||||
const { t } = useTranslation('~', { keyPrefix: 'dry-wash.landing' });
|
||||
|
||||
return (
|
||||
<PageSection>
|
||||
<VStack w='full' spacing={2}>
|
||||
<Heading as='h2'>{t('heading')}</Heading>
|
||||
<Text>
|
||||
{t('description')}
|
||||
</Text>
|
||||
<Heading as='h2'>{t(heading)}</Heading>
|
||||
<Text>{t(description)}</Text>
|
||||
</VStack>
|
||||
<List display='flex' flexDirection='column' spacing={3}>
|
||||
{listData.map((props, i) => (
|
||||
<ListItem key={i} {...props} />
|
||||
{list.map((itemKey, i) => (
|
||||
<ListItem key={i} Icon={iconsMap[itemKey]}>
|
||||
{t(itemKey)}
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
<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';
|
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;
|
||||
};
|
||||
};
|
@ -5,7 +5,7 @@ import { ButtonProps, Button } from '@chakra-ui/react';
|
||||
import { URLs } from '../../../__data__/urls';
|
||||
|
||||
export const CtaButton: FC<ButtonProps> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation('~', { keyPrefix: 'dry-wash.landing' });
|
||||
|
||||
return (
|
||||
<Button
|
||||
@ -14,7 +14,7 @@ export const CtaButton: FC<ButtonProps> = (props) => {
|
||||
colorScheme='primary'
|
||||
{...props}
|
||||
>
|
||||
{t('dry-wash.landing.make-order-button')}
|
||||
{t('make-order-button')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
@ -1,21 +1,21 @@
|
||||
import React, { FC } from 'react';
|
||||
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 { CtaButton, SiteLogo, PageSection } from '../';
|
||||
import { HeroSectionProps } from './types';
|
||||
|
||||
type HeroSectionProps = Pick<BoxProps, 'flexShrink'>;
|
||||
|
||||
export const HeroSection: FC<HeroSectionProps> = ({ flexShrink }) => {
|
||||
const { t } = useTranslation('~', {
|
||||
keyPrefix: 'dry-wash.landing.hero-section',
|
||||
});
|
||||
export const HeroSection: FC<HeroSectionProps> = ({
|
||||
data: { headline, description, video },
|
||||
flexShrink,
|
||||
}) => {
|
||||
const { t } = useTranslation('~', { keyPrefix: 'dry-wash.landing' });
|
||||
|
||||
return (
|
||||
<Box flexShrink={flexShrink} as='header' pos='relative' zIndex={0}>
|
||||
<Box
|
||||
as='video'
|
||||
src={`${__webpack_public_path__}/remote-assets/demo.mp4`}
|
||||
src={`${__webpack_public_path__}/remote-assets/${video}`}
|
||||
poster={DemoVideoPosterImg}
|
||||
autoPlay
|
||||
loop
|
||||
@ -46,14 +46,14 @@ export const HeroSection: FC<HeroSectionProps> = ({ flexShrink }) => {
|
||||
color='white'
|
||||
__css={{ textWrap: 'balance' }}
|
||||
>
|
||||
{t('headline')}
|
||||
{t(headline)}
|
||||
</Heading>
|
||||
<Text
|
||||
textAlign='center'
|
||||
__css={{ textWrap: 'balance' }}
|
||||
color='white'
|
||||
>
|
||||
{t('description')}
|
||||
{t(description)}
|
||||
</Text>
|
||||
</VStack>
|
||||
<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'>;
|
@ -3,15 +3,16 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Heading, HStack } from '@chakra-ui/react';
|
||||
import { CtaButton, PageSection } from '../';
|
||||
import { ReviewsSlider } from './ReviewsSlider';
|
||||
import { SocialProofSectionProps } from './types';
|
||||
|
||||
export const SocialProofSection: FC = () => {
|
||||
const { t } = useTranslation('~', {
|
||||
keyPrefix: 'dry-wash.landing.social-proof-section',
|
||||
});
|
||||
export const SocialProofSection: FC<SocialProofSectionProps> = ({
|
||||
data: { heading },
|
||||
}) => {
|
||||
const { t } = useTranslation('~', { keyPrefix: 'dry-wash.landing' });
|
||||
|
||||
return (
|
||||
<PageSection>
|
||||
<Heading as='h2'>{t('heading')}</Heading>
|
||||
<Heading as='h2'>{t(heading)}</Heading>
|
||||
<ReviewsSlider />
|
||||
<HStack w='full' justify='flex-end'>
|
||||
<CtaButton />
|
||||
|
@ -1 +1,2 @@
|
||||
export type { SocialProofSectionProps } from './types';
|
||||
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
src/lib/index.ts
Normal file
1
src/lib/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './types';
|
41
src/lib/types.ts
Normal file
41
src/lib/types.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @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', '.'>;
|
||||
* // ["a", "b1"] | ["a", "b2"]
|
||||
*/
|
||||
type Split<S extends string, D extends string> =
|
||||
S extends `${infer A}${D}${infer B}` ? [A, ...Split<B, D>] : [S];
|
||||
|
||||
/**
|
||||
* @example type Output = NestedObject<["a", "b1"] | ["a", "b2"]>;
|
||||
* // { a: { b1: string; }; } | { a: { b2: string; }; }
|
||||
*/
|
||||
type NestedObject<T extends string[]> =
|
||||
T extends [infer Head, ...infer Tail] ?
|
||||
Head extends string ?
|
||||
{ [key in Head]: NestedObject<Tail extends string[] ? Tail : []> } : never :
|
||||
string;
|
||||
|
||||
/**
|
||||
* @example type Output = UnionToIntersection<{ a: { b1: string; }; } | { a: { b2: string; }; }>;
|
||||
* // { a: { b1: string; }; } & { a: { b2: string; }; }
|
||||
*/
|
||||
type UnionToIntersection<U> =
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
||||
|
||||
/**
|
||||
* @example type Output = CreateTree<'a.b1' | 'a.b2', '.'>;
|
||||
* // { a: { b1: string; }; } & { a: { b2: string; }; }
|
||||
*/
|
||||
export type CreateTree<T> =
|
||||
UnionToIntersection<T extends infer U ?
|
||||
U extends string ?
|
||||
NestedObject<Split<U, '.'>> : never : never>;
|
23
src/models/i18next.d.ts
vendored
23
src/models/i18next.d.ts
vendored
@ -1,24 +1,5 @@
|
||||
import defaultLocale from '../../locales/ru.json';
|
||||
|
||||
type Split<S extends string, D extends string> =
|
||||
S extends `${infer A}${D}${infer B}` ? [A, ...Split<B, D>] : [S];
|
||||
|
||||
type NestedObject<T extends string[]> =
|
||||
T extends [infer Head, ...infer Tail] ?
|
||||
Head extends string ?
|
||||
{ [key in Head]: NestedObject<Tail extends string[] ? Tail : []> } : never :
|
||||
string;
|
||||
|
||||
// Основная утилита для обработки union type
|
||||
type CreateTree<T> =
|
||||
UnionToIntersection<T extends infer U ?
|
||||
U extends string ?
|
||||
NestedObject<Split<U, '.'>> : never : never>;
|
||||
|
||||
// Утилита для объединения типов
|
||||
type UnionToIntersection<U> =
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
||||
import { CreateTree } from '../lib';
|
||||
|
||||
type LanguageResource = CreateTree<keyof typeof defaultLocale>;
|
||||
|
||||
@ -26,6 +7,6 @@ declare module "i18next" {
|
||||
interface CustomTypeOptions {
|
||||
resources: {
|
||||
'~': LanguageResource
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,9 @@
|
||||
import React, { FC } from 'react';
|
||||
import { Container, VStack } from '@chakra-ui/react';
|
||||
import {
|
||||
BenefitsSection,
|
||||
Footer,
|
||||
HeroSection,
|
||||
SocialProofSection,
|
||||
} from '../../components/landing';
|
||||
import LandingSuccess from '../../../stubs/json/landing/landing-success.json';
|
||||
import { BenefitsSection, Footer, HeroSection, SocialProofSection } from '../../components/landing';
|
||||
import { LandingThemeProvider } from '../../containers';
|
||||
import { isBenefitsSectionData, isSocialProofSectionData } from './types';
|
||||
|
||||
const Page: FC = () => {
|
||||
return (
|
||||
@ -20,10 +17,19 @@ const Page: FC = () => {
|
||||
centerContent
|
||||
>
|
||||
<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}>
|
||||
<BenefitsSection />
|
||||
<SocialProofSection />
|
||||
{LandingSuccess.body.sections.map(({ type, ...data }, i) => {
|
||||
if (isBenefitsSectionData(type, data)) {
|
||||
return <BenefitsSection key={i} data={data} />;
|
||||
}
|
||||
if (isSocialProofSectionData(type, data)) {
|
||||
return <SocialProofSection key={i} data={data} />;
|
||||
}
|
||||
})}
|
||||
</VStack>
|
||||
<Footer />
|
||||
</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';
|
||||
};
|
27
stubs/json/landing/landing-success.json
Normal file
27
stubs/json/landing/landing-success.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"success": true,
|
||||
"body": {
|
||||
"hero-section": {
|
||||
"headline": "hero-section.headline",
|
||||
"description": "hero-section.description",
|
||||
"video": "demo.mp4"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"type": "benefits-section",
|
||||
"heading": "benefits-section.heading",
|
||||
"description": "benefits-section.description",
|
||||
"list": [
|
||||
"benefits-section.list.0",
|
||||
"benefits-section.list.1",
|
||||
"benefits-section.list.2",
|
||||
"benefits-section.list.3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "social-proof-section",
|
||||
"heading": "social-proof-section.heading"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
28
stubs/json/landing/landing-success.json.d.ts
vendored
Normal file
28
stubs/json/landing/landing-success.json.d.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
interface LandingSuccess {
|
||||
success: true,
|
||||
body: {
|
||||
'hero-section': {
|
||||
headline: 'hero-section.headline',
|
||||
description: 'hero-section.description',
|
||||
video: 'demo.mp4'
|
||||
},
|
||||
sections: [
|
||||
{
|
||||
type: 'benefits-section',
|
||||
heading: 'benefits-section.heading',
|
||||
description: 'benefits-section.description',
|
||||
list: [
|
||||
'benefits-section.list.0',
|
||||
'benefits-section.list.1',
|
||||
'benefits-section.list.2',
|
||||
'benefits-section.list.3'
|
||||
]
|
||||
},
|
||||
{type: 'social-proof-section', heading: 'social-proof-section.heading'}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
declare const LandingSuccess: LandingSuccess;
|
||||
|
||||
export = LandingSuccess;
|
Loading…
Reference in New Issue
Block a user