@faststore/core 2.0.130-alpha.0 → 2.0.133-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +16 -15
- package/CHANGELOG.md +12 -0
- package/cms/faststore/sections.json +1 -1
- package/lighthouserc.js +1 -0
- package/next.config.js +2 -0
- package/package.json +4 -4
- package/src/components/cms/GlobalSections.tsx +4 -3
- package/src/components/common/Footer/Footer.tsx +23 -118
- package/src/components/common/Footer/FooterLinks.tsx +23 -97
- package/src/components/common/Footer/FooterSocial.tsx +42 -0
- package/src/components/common/Footer/index.ts +6 -1
- package/src/components/common/Toast/Toast.tsx +5 -3
- package/src/components/common/Toast/section.module.scss +7 -0
- package/src/components/product/ProductCard/ProductCard.tsx +13 -7
- package/src/components/product/ProductGrid/ProductGrid.tsx +5 -0
- package/src/components/region/RegionButton/RegionButton.tsx +2 -2
- package/src/components/sections/BannerNewsletter/BannerNewsletter.tsx +1 -0
- package/src/components/sections/BannerNewsletter/section.module.scss +1 -0
- package/src/components/sections/EmptyState/EmptyState.tsx +28 -0
- package/src/components/sections/EmptyState/index.ts +2 -0
- package/src/components/sections/EmptyState/section.module.scss +8 -0
- package/src/components/sections/Footer/Footer.tsx +87 -0
- package/src/components/sections/Footer/index.ts +1 -0
- package/src/components/{common → sections}/Footer/section.module.scss +1 -0
- package/src/components/sections/Hero/Hero.tsx +0 -1
- package/src/components/sections/Incentives/Incentives.tsx +19 -0
- package/src/components/sections/Incentives/index.ts +1 -0
- package/src/components/sections/ProductDetails/section.module.scss +1 -0
- package/src/components/sections/ProductGallery/section.module.scss +1 -0
- package/src/components/sections/ProductShelf/ProductShelf.tsx +12 -4
- package/src/components/sections/ProductTiles/ProductTiles.tsx +21 -0
- package/src/components/ui/Button/ButtonSignIn/ButtonSignIn.tsx +1 -1
- package/src/components/ui/Image/Image.tsx +7 -18
- package/src/components/ui/Image/loader.ts +16 -0
- package/src/components/ui/ImageGallery/ImageGallery.tsx +7 -14
- package/src/components/ui/Incentives/Incentives.tsx +11 -7
- package/src/components/ui/Incentives/index.ts +1 -1
- package/src/components/ui/SkuSelector/Selectors.tsx +4 -17
- package/src/pages/404.tsx +5 -4
- package/src/pages/500.tsx +19 -5
- package/src/pages/[...slug].tsx +0 -1
- package/src/pages/index.tsx +2 -2
- package/src/pages/login.tsx +5 -3
- package/src/utils/utilities.ts +13 -0
- package/src/components/common/Footer/FooterFlags.tsx +0 -26
- package/src/components/sections/IncentivesHeader/IncentivesHeader.tsx +0 -24
- package/src/components/sections/IncentivesHeader/index.ts +0 -1
- package/src/components/ui/Image/thumborUrlBuilder.ts +0 -103
- package/src/components/ui/Image/useImage.ts +0 -46
- package/src/components/ui/Incentives/incentivesMock.ts +0 -27
- /package/src/components/sections/{IncentivesHeader → Incentives}/section.module.scss +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
import type { PropsWithChildren } from 'react'
|
|
3
|
+
|
|
4
|
+
import Section from '../Section'
|
|
5
|
+
import styles from './section.module.scss'
|
|
6
|
+
|
|
7
|
+
import { EmptyState as UIEmptyState } from '@faststore/ui'
|
|
8
|
+
|
|
9
|
+
export interface EmptyStateProps {
|
|
10
|
+
title: string
|
|
11
|
+
titleIcon?: ReactNode
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function EmptyState({
|
|
15
|
+
title,
|
|
16
|
+
titleIcon,
|
|
17
|
+
children,
|
|
18
|
+
}: PropsWithChildren<EmptyStateProps>) {
|
|
19
|
+
return (
|
|
20
|
+
<Section className={`${styles.section} section-empty-state`}>
|
|
21
|
+
<UIEmptyState title={title} titleIcon={titleIcon} bkgColor="light">
|
|
22
|
+
{children}
|
|
23
|
+
</UIEmptyState>
|
|
24
|
+
</Section>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default EmptyState
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/* stylelint-disable no-invalid-position-at-import-rule */
|
|
2
|
+
@import "@faststore/ui/src/styles/base/utilities.scss";
|
|
3
|
+
|
|
4
|
+
.section {
|
|
5
|
+
@import "@faststore/ui/src/components/atoms/Icon/styles.scss";
|
|
6
|
+
@import "@faststore/ui/src/components/atoms/Loader/styles.scss";
|
|
7
|
+
@import "@faststore/ui/src/components/organisms/EmptyState/styles.scss";
|
|
8
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { PaymentMethods as UIPaymentMethods } from '@faststore/ui'
|
|
2
|
+
import type { PaymentMethodsProps as UIPaymentMethodProps } from '@faststore/ui'
|
|
3
|
+
|
|
4
|
+
import Section from '../Section'
|
|
5
|
+
|
|
6
|
+
import UIFooter, {
|
|
7
|
+
FooterLinks,
|
|
8
|
+
FooterSocial,
|
|
9
|
+
FooterInfo as UIFooterInfo,
|
|
10
|
+
FooterNavigation as UIFooterNavigation,
|
|
11
|
+
} from '../../common/Footer'
|
|
12
|
+
import type { FooterLinksProps, FooterSocialProps } from '../../common/Footer'
|
|
13
|
+
|
|
14
|
+
import { Image } from '../../ui/Image'
|
|
15
|
+
import UIIncentives from '../../ui/Incentives'
|
|
16
|
+
import type { Incentive } from '../../ui/Incentives'
|
|
17
|
+
|
|
18
|
+
import styles from './section.module.scss'
|
|
19
|
+
|
|
20
|
+
export type FooterProps = {
|
|
21
|
+
incentives: Incentive[]
|
|
22
|
+
footerLinks: FooterLinksProps['links']
|
|
23
|
+
footerSocial: {
|
|
24
|
+
title?: FooterSocialProps['title']
|
|
25
|
+
socialLinks: FooterSocialProps['links']
|
|
26
|
+
}
|
|
27
|
+
logo: {
|
|
28
|
+
src: string
|
|
29
|
+
alt: string
|
|
30
|
+
}
|
|
31
|
+
copyrightInfo: string
|
|
32
|
+
acceptedPaymentMethods: {
|
|
33
|
+
showPaymentMethods: boolean
|
|
34
|
+
title?: string
|
|
35
|
+
paymentMethods?: UIPaymentMethodProps['flagList']
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const Footer = ({
|
|
40
|
+
incentives,
|
|
41
|
+
footerLinks,
|
|
42
|
+
footerSocial,
|
|
43
|
+
footerSocial: { title: footerSocialTitle },
|
|
44
|
+
logo: { src: logoSrc, alt: logoAlt },
|
|
45
|
+
copyrightInfo,
|
|
46
|
+
acceptedPaymentMethods: {
|
|
47
|
+
showPaymentMethods,
|
|
48
|
+
title: acceptedPaymentMethodsTitle,
|
|
49
|
+
paymentMethods,
|
|
50
|
+
},
|
|
51
|
+
}: FooterProps) => {
|
|
52
|
+
return (
|
|
53
|
+
<Section className={`${styles.section} section-footer`}>
|
|
54
|
+
<UIFooter>
|
|
55
|
+
<UIIncentives incentives={incentives} />
|
|
56
|
+
<UIFooterNavigation>
|
|
57
|
+
<FooterLinks links={footerLinks} />
|
|
58
|
+
<FooterSocial
|
|
59
|
+
title={footerSocialTitle}
|
|
60
|
+
links={footerSocial.socialLinks}
|
|
61
|
+
/>
|
|
62
|
+
</UIFooterNavigation>
|
|
63
|
+
<UIFooterInfo>
|
|
64
|
+
<Image
|
|
65
|
+
data-fs-footer-logo
|
|
66
|
+
loading="lazy"
|
|
67
|
+
src={logoSrc}
|
|
68
|
+
alt={logoAlt}
|
|
69
|
+
width={112}
|
|
70
|
+
height={119}
|
|
71
|
+
/>
|
|
72
|
+
{showPaymentMethods && (
|
|
73
|
+
<UIPaymentMethods
|
|
74
|
+
flagList={paymentMethods}
|
|
75
|
+
title={<p>{acceptedPaymentMethodsTitle}</p>}
|
|
76
|
+
/>
|
|
77
|
+
)}
|
|
78
|
+
<div data-fs-footer-copyright className="text__legend">
|
|
79
|
+
<p>{copyrightInfo}</p>
|
|
80
|
+
</div>
|
|
81
|
+
</UIFooterInfo>
|
|
82
|
+
</UIFooter>
|
|
83
|
+
</Section>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default Footer
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Footer'
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
@import "@faststore/ui/src/styles/base/utilities.scss";
|
|
3
3
|
|
|
4
4
|
.section {
|
|
5
|
+
@import "@faststore/ui/src/components/atoms/Button/styles.scss";
|
|
5
6
|
@import "@faststore/ui/src/components/atoms/Link/styles.scss";
|
|
6
7
|
@import "@faststore/ui/src/components/atoms/List/styles.scss";
|
|
7
8
|
@import "@faststore/ui/src/components/atoms/Logo/styles.scss";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import UIIncentives from 'src/components/ui/Incentives/Incentives'
|
|
2
|
+
import type { Incentive } from 'src/components/ui/Incentives'
|
|
3
|
+
|
|
4
|
+
import Section from '../Section'
|
|
5
|
+
import styles from './section.module.scss'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
incentives: Incentive[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function Incentives({ incentives }: Props) {
|
|
12
|
+
return (
|
|
13
|
+
<Section className={`${styles.section} section-incentives`}>
|
|
14
|
+
<UIIncentives incentives={incentives} colored />
|
|
15
|
+
</Section>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default Incentives
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Incentives'
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
@import "@faststore/ui/src/components/atoms/Badge/styles.scss";
|
|
11
11
|
@import "@faststore/ui/src/components/atoms/Button/styles.scss";
|
|
12
12
|
@import "@faststore/ui/src/components/atoms/Input/styles.scss";
|
|
13
|
+
@import "@faststore/ui/src/components/atoms/Price/styles.scss";
|
|
13
14
|
@import "@faststore/ui/src/components/molecules/Accordion/styles.scss";
|
|
14
15
|
@import "@faststore/ui/src/components/molecules/Breadcrumb/styles.scss";
|
|
15
16
|
@import "@faststore/ui/src/components/molecules/BuyButton/styles.scss";
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
@import "@faststore/ui/src/components/molecules/Accordion/styles.scss";
|
|
17
17
|
@import "@faststore/ui/src/components/molecules/DiscountBadge/styles.scss";
|
|
18
18
|
@import "@faststore/ui/src/components/molecules/InputField/styles.scss";
|
|
19
|
+
@import "@faststore/ui/src/components/molecules/LinkButton/styles.scss";
|
|
19
20
|
@import "@faststore/ui/src/components/molecules/ProductCard/styles.scss";
|
|
20
21
|
@import "@faststore/ui/src/components/molecules/ProductCardSkeleton/styles";
|
|
21
22
|
@import "@faststore/ui/src/components/molecules/SelectField/styles.scss";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react'
|
|
1
|
+
import { useEffect, useId, useRef } from 'react'
|
|
2
2
|
import { useInView } from 'react-intersection-observer'
|
|
3
3
|
|
|
4
4
|
import { ProductShelf as UIProductShelf } from '@faststore/ui'
|
|
@@ -7,11 +7,12 @@ import type { ProductsQueryQueryVariables } from '@generated/graphql'
|
|
|
7
7
|
import ProductShelfSkeleton from 'src/components/skeletons/ProductShelfSkeleton'
|
|
8
8
|
import { useViewItemListEvent } from 'src/sdk/analytics/hooks/useViewItemListEvent'
|
|
9
9
|
import { useProductsQuery } from 'src/sdk/product/useProductsQuery'
|
|
10
|
+
import { textToKebabCase } from 'src/utils/utilities'
|
|
10
11
|
|
|
12
|
+
import Carousel from '../../ui/Carousel'
|
|
13
|
+
import Section from '../Section'
|
|
11
14
|
import { Components } from './Overrides'
|
|
12
15
|
const { ProductCard } = Components
|
|
13
|
-
import Section from '../Section'
|
|
14
|
-
import Carousel from '../../ui/Carousel'
|
|
15
16
|
|
|
16
17
|
import styles from './section.module.scss'
|
|
17
18
|
|
|
@@ -25,6 +26,8 @@ function ProductShelf({
|
|
|
25
26
|
withDivisor = false,
|
|
26
27
|
...variables
|
|
27
28
|
}: ProductShelfProps) {
|
|
29
|
+
const titleId = textToKebabCase(title)
|
|
30
|
+
const id = useId()
|
|
28
31
|
const viewedOnce = useRef(false)
|
|
29
32
|
const { ref, inView } = useInView()
|
|
30
33
|
const products = useProductsQuery(variables)
|
|
@@ -63,7 +66,7 @@ function ProductShelf({
|
|
|
63
66
|
loading={products === undefined}
|
|
64
67
|
>
|
|
65
68
|
<UIProductShelf>
|
|
66
|
-
<Carousel>
|
|
69
|
+
<Carousel id={titleId || id}>
|
|
67
70
|
{productEdges.map((product, idx) => (
|
|
68
71
|
<ProductCard
|
|
69
72
|
bordered
|
|
@@ -71,6 +74,11 @@ function ProductShelf({
|
|
|
71
74
|
product={product.node}
|
|
72
75
|
index={idx + 1}
|
|
73
76
|
aspectRatio={aspectRatio}
|
|
77
|
+
imgProps={{
|
|
78
|
+
width: 216,
|
|
79
|
+
height: 216,
|
|
80
|
+
sizes: '(max-width: 768px) 42vw, 30vw',
|
|
81
|
+
}}
|
|
74
82
|
/>
|
|
75
83
|
))}
|
|
76
84
|
</Carousel>
|
|
@@ -33,6 +33,26 @@ const getRatio = (products: number, idx: number) => {
|
|
|
33
33
|
return 3 / 4
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
const getSizes = (products: number, idx: number) => {
|
|
37
|
+
const expandsFirstTile =
|
|
38
|
+
products === NUMBER_ITEMS_TO_EXPAND_FIRST && idx === 0
|
|
39
|
+
|
|
40
|
+
const expandsFirstTwoTile =
|
|
41
|
+
products === NUMBER_ITEMS_TO_EXPAND_FIRST_TWO && (idx === 0 || idx === 1)
|
|
42
|
+
|
|
43
|
+
if (expandsFirstTile || expandsFirstTwoTile) {
|
|
44
|
+
return {
|
|
45
|
+
width: 594,
|
|
46
|
+
height: 364,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
width: 284,
|
|
52
|
+
height: 364,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
36
56
|
const ProductTiles = ({ title, ...variables }: ProductTilesProps) => {
|
|
37
57
|
const viewedOnce = useRef(false)
|
|
38
58
|
const { ref, inView } = useInView()
|
|
@@ -75,6 +95,7 @@ const ProductTiles = ({ title, ...variables }: ProductTilesProps) => {
|
|
|
75
95
|
index={idx + 1}
|
|
76
96
|
variant="wide"
|
|
77
97
|
aspectRatio={getRatio(productEdges.length, idx)}
|
|
98
|
+
imgProps={getSizes(productEdges.length, idx)}
|
|
78
99
|
/>
|
|
79
100
|
</Tile>
|
|
80
101
|
))}
|
|
@@ -1,29 +1,18 @@
|
|
|
1
1
|
import { memo } from 'react'
|
|
2
2
|
|
|
3
3
|
import NextImage, { ImageProps } from 'next/future/image'
|
|
4
|
-
import
|
|
5
|
-
import { useImage } from './useImage'
|
|
4
|
+
import loader from './loader'
|
|
6
5
|
|
|
7
|
-
// Next loader function does not handle all props as height and options
|
|
8
|
-
// so we use
|
|
6
|
+
// Next loader function does not handle all props as height and options
|
|
7
|
+
// so we use a custom loader to handle images using thumbor server with VTEX CDN
|
|
9
8
|
// https://nextjs.org/docs/api-reference/next/image#loader
|
|
10
|
-
function Image({
|
|
11
|
-
const { src: thumborSrc, alt } = useImage({
|
|
12
|
-
src: String(src),
|
|
13
|
-
width: Number(width),
|
|
14
|
-
height: Number(height),
|
|
15
|
-
options: quality ? ({ filters: { quality } } as ThumborOptions) : undefined,
|
|
16
|
-
...otherProps,
|
|
17
|
-
})
|
|
18
|
-
|
|
9
|
+
function Image({ loading = 'lazy', ...otherProps }: ImageProps) {
|
|
19
10
|
return (
|
|
20
11
|
<NextImage
|
|
21
12
|
data-fs-image
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
height={height}
|
|
26
|
-
alt={alt}
|
|
13
|
+
loader={loader}
|
|
14
|
+
loading={loading}
|
|
15
|
+
priority={loading === 'eager'}
|
|
27
16
|
{...otherProps}
|
|
28
17
|
/>
|
|
29
18
|
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import storeConfig from 'faststore.config'
|
|
2
|
+
const THUMBOR_SERVER = `https://${storeConfig.api.storeId}.vtexassets.com`
|
|
3
|
+
|
|
4
|
+
export default function customImageLoader({ src, width, quality }) {
|
|
5
|
+
const preSizeComponents = [THUMBOR_SERVER, 'unsafe']
|
|
6
|
+
|
|
7
|
+
// proportional to the width, enter a height of 0,
|
|
8
|
+
const height = 0
|
|
9
|
+
const finalSize = `${width}x${height}`
|
|
10
|
+
|
|
11
|
+
const postSizeComponents: string[] = ['center', 'middle']
|
|
12
|
+
quality && postSizeComponents.push(`filters:quality(${quality || 80})`)
|
|
13
|
+
postSizeComponents.push(encodeURIComponent(src))
|
|
14
|
+
|
|
15
|
+
return [...preSizeComponents, finalSize, ...postSizeComponents].join('/')
|
|
16
|
+
}
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
1
|
import {
|
|
3
|
-
ImageGallery as UIImageGallery,
|
|
4
2
|
ImageElementData,
|
|
5
3
|
ImageZoom,
|
|
4
|
+
ImageGallery as UIImageGallery,
|
|
6
5
|
} from '@faststore/ui'
|
|
6
|
+
import { useEffect, useState } from 'react'
|
|
7
7
|
|
|
8
|
-
import { Image } from 'src/components/ui/Image'
|
|
9
8
|
import { useRouter } from 'next/router'
|
|
9
|
+
import { Image } from 'src/components/ui/Image'
|
|
10
10
|
|
|
11
11
|
const ImageComponent = ({ url, alternateName }) => (
|
|
12
|
-
<Image
|
|
13
|
-
src={url}
|
|
14
|
-
alt={alternateName}
|
|
15
|
-
sizes="(max-width: 72px) 25vw, 30vw"
|
|
16
|
-
width={72}
|
|
17
|
-
height={72}
|
|
18
|
-
/>
|
|
12
|
+
<Image src={url} alt={alternateName} width={68} height={68} />
|
|
19
13
|
)
|
|
20
14
|
|
|
21
15
|
export interface ImageGalleryProps {
|
|
@@ -41,11 +35,10 @@ const ImageGallery = ({ images, ...otherProps }: ImageGalleryProps) => {
|
|
|
41
35
|
<Image
|
|
42
36
|
src={currentImage.url}
|
|
43
37
|
alt={currentImage.alternateName}
|
|
44
|
-
sizes="(max-width:
|
|
45
|
-
width={
|
|
46
|
-
height={
|
|
38
|
+
sizes="(max-width: 768px) 25vw, 30vw"
|
|
39
|
+
width={691}
|
|
40
|
+
height={691 * (3 / 4)}
|
|
47
41
|
loading="eager"
|
|
48
|
-
priority
|
|
49
42
|
/>
|
|
50
43
|
</ImageZoom>
|
|
51
44
|
</UIImageGallery>
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Icon as UIIcon,
|
|
3
|
+
List as UIList,
|
|
4
|
+
Incentive as UIIncentive,
|
|
5
|
+
} from '@faststore/ui'
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
export type Incentive = {
|
|
4
8
|
icon: string
|
|
5
|
-
title
|
|
9
|
+
title: string
|
|
6
10
|
firstLineText: string
|
|
7
11
|
secondLineText?: string
|
|
12
|
+
alt?: string
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
export interface IncentivesProps {
|
|
@@ -34,16 +39,15 @@ function Incentives({
|
|
|
34
39
|
{incentives.map((incentive, index) => (
|
|
35
40
|
<li key={String(index)}>
|
|
36
41
|
<UIIncentive>
|
|
37
|
-
<
|
|
42
|
+
<UIIcon
|
|
38
43
|
data-fs-incentive-icon
|
|
44
|
+
aria-label={incentive.alt}
|
|
39
45
|
name={incentive.icon}
|
|
40
46
|
width={32}
|
|
41
47
|
height={32}
|
|
42
48
|
/>
|
|
43
49
|
<div data-fs-incentive-content>
|
|
44
|
-
{incentive.title
|
|
45
|
-
<p data-fs-incentive-title>{incentive.title}</p>
|
|
46
|
-
)}
|
|
50
|
+
<p data-fs-incentive-title>{incentive.title}</p>
|
|
47
51
|
<span data-fs-incentive-description>
|
|
48
52
|
{incentive.firstLineText}
|
|
49
53
|
</span>
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from './Incentives'
|
|
2
|
-
export type { IncentivesProps } from './Incentives'
|
|
2
|
+
export type { IncentivesProps, Incentive } from './Incentives'
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { HTMLAttributes
|
|
1
|
+
import { HTMLAttributes } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
SkuSelector as UISkuSelector,
|
|
6
|
-
SkuSelectorProps,
|
|
7
|
-
SkuOption,
|
|
8
|
-
} from '@faststore/ui'
|
|
3
|
+
import { SkuSelectorProps, SkuSelector as UISkuSelector } from '@faststore/ui'
|
|
9
4
|
import NextLink from 'next/link'
|
|
5
|
+
import { Image } from '../Image'
|
|
10
6
|
|
|
11
7
|
export type SkuVariantsByName = Record<
|
|
12
8
|
string,
|
|
@@ -32,16 +28,7 @@ const ImageComponent: SkuSelectorProps['ImageComponent'] = ({
|
|
|
32
28
|
src,
|
|
33
29
|
alt,
|
|
34
30
|
...otherProps
|
|
35
|
-
}) =>
|
|
36
|
-
<Image
|
|
37
|
-
src={src}
|
|
38
|
-
alt={alt}
|
|
39
|
-
width={20}
|
|
40
|
-
height={20}
|
|
41
|
-
loading="lazy"
|
|
42
|
-
{...otherProps}
|
|
43
|
-
/>
|
|
44
|
-
)
|
|
31
|
+
}) => <Image src={src} alt={alt} width={34} height={34} {...otherProps} />
|
|
45
32
|
|
|
46
33
|
function Selectors({
|
|
47
34
|
slugsMap,
|
package/src/pages/404.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { NextSeo } from 'next-seo'
|
|
2
2
|
import { useRouter } from 'next/router'
|
|
3
|
-
import { EmptyState as UIEmptyState, Icon as UIIcon } from '@faststore/ui'
|
|
4
3
|
import GlobalSections, {
|
|
5
4
|
GlobalSectionsData,
|
|
6
5
|
getGlobalSectionsData,
|
|
@@ -8,6 +7,9 @@ import GlobalSections, {
|
|
|
8
7
|
import { GetStaticProps } from 'next'
|
|
9
8
|
import { Locator } from '@vtex/client-cms'
|
|
10
9
|
|
|
10
|
+
import { Icon as UIIcon } from '@faststore/ui'
|
|
11
|
+
import EmptyState from 'src/components/sections/EmptyState'
|
|
12
|
+
|
|
11
13
|
const useErrorState = () => {
|
|
12
14
|
const router = useRouter()
|
|
13
15
|
const { from } = router.query
|
|
@@ -29,7 +31,7 @@ function Page({ globalSections }: Props) {
|
|
|
29
31
|
<GlobalSections {...globalSections}>
|
|
30
32
|
<NextSeo noindex nofollow />
|
|
31
33
|
|
|
32
|
-
<
|
|
34
|
+
<EmptyState
|
|
33
35
|
title="Not Found: 404"
|
|
34
36
|
titleIcon={
|
|
35
37
|
<UIIcon
|
|
@@ -39,10 +41,9 @@ function Page({ globalSections }: Props) {
|
|
|
39
41
|
weight="thin"
|
|
40
42
|
/>
|
|
41
43
|
}
|
|
42
|
-
bkgColor="light"
|
|
43
44
|
>
|
|
44
45
|
<p>This app could not find url {fromUrl}</p>
|
|
45
|
-
</
|
|
46
|
+
</EmptyState>
|
|
46
47
|
</GlobalSections>
|
|
47
48
|
)
|
|
48
49
|
}
|
package/src/pages/500.tsx
CHANGED
|
@@ -7,6 +7,9 @@ import GlobalSections, {
|
|
|
7
7
|
getGlobalSectionsData,
|
|
8
8
|
} from 'src/components/cms/GlobalSections'
|
|
9
9
|
|
|
10
|
+
import { Icon as UIIcon } from '@faststore/ui'
|
|
11
|
+
import EmptyState from 'src/components/sections/EmptyState'
|
|
12
|
+
|
|
10
13
|
type Props = {
|
|
11
14
|
globalSections: GlobalSectionsData
|
|
12
15
|
}
|
|
@@ -28,12 +31,23 @@ function Page({ globalSections }: Props) {
|
|
|
28
31
|
<GlobalSections {...globalSections}>
|
|
29
32
|
<NextSeo noindex nofollow />
|
|
30
33
|
|
|
31
|
-
<
|
|
32
|
-
|
|
34
|
+
<EmptyState
|
|
35
|
+
title="500"
|
|
36
|
+
titleIcon={
|
|
37
|
+
<UIIcon
|
|
38
|
+
name="CircleWavyWarning"
|
|
39
|
+
width={56}
|
|
40
|
+
height={56}
|
|
41
|
+
weight="thin"
|
|
42
|
+
/>
|
|
43
|
+
}
|
|
44
|
+
>
|
|
45
|
+
<h2>Internal Server Error</h2>
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
<div>
|
|
48
|
+
The server errored with id {errorId} when visiting page {fromUrl}
|
|
49
|
+
</div>
|
|
50
|
+
</EmptyState>
|
|
37
51
|
</GlobalSections>
|
|
38
52
|
)
|
|
39
53
|
}
|
package/src/pages/[...slug].tsx
CHANGED
|
@@ -11,7 +11,6 @@ import { BreadcrumbJsonLd, NextSeo } from 'next-seo'
|
|
|
11
11
|
import { useRouter } from 'next/router'
|
|
12
12
|
import { useMemo } from 'react'
|
|
13
13
|
|
|
14
|
-
import { Icon } from '@faststore/ui'
|
|
15
14
|
import type {
|
|
16
15
|
ServerCollectionPageQueryQuery,
|
|
17
16
|
ServerCollectionPageQueryQueryVariables,
|
package/src/pages/index.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import type { ComponentType } from 'react'
|
|
|
6
6
|
import RenderSections from 'src/components/cms/RenderSections'
|
|
7
7
|
import BannerText from 'src/components/sections/BannerText'
|
|
8
8
|
import Hero from 'src/components/sections/Hero'
|
|
9
|
-
import
|
|
9
|
+
import Incentives from 'src/components/sections/Incentives'
|
|
10
10
|
import Newsletter from 'src/components/sections/Newsletter'
|
|
11
11
|
import ProductShelf from 'src/components/sections/ProductShelf'
|
|
12
12
|
import ProductTiles from 'src/components/sections/ProductTiles'
|
|
@@ -24,7 +24,7 @@ import GlobalSections, {
|
|
|
24
24
|
/* A list of components that can be used in the CMS. */
|
|
25
25
|
const COMPONENTS: Record<string, ComponentType<any>> = {
|
|
26
26
|
Hero,
|
|
27
|
-
|
|
27
|
+
Incentives,
|
|
28
28
|
ProductShelf,
|
|
29
29
|
ProductTiles,
|
|
30
30
|
BannerText,
|
package/src/pages/login.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useEffect } from 'react'
|
|
2
2
|
import { NextSeo } from 'next-seo'
|
|
3
|
-
import { EmptyState as UIEmptyState, Loader as UILoader } from '@faststore/ui'
|
|
4
3
|
|
|
5
4
|
import storeConfig from '../../faststore.config'
|
|
6
5
|
import GlobalSections, {
|
|
@@ -10,6 +9,9 @@ import GlobalSections, {
|
|
|
10
9
|
import { GetStaticProps } from 'next'
|
|
11
10
|
import { Locator } from '@vtex/client-cms'
|
|
12
11
|
|
|
12
|
+
import { Loader as UILoader } from '@faststore/ui'
|
|
13
|
+
import EmptyState from 'src/components/sections/EmptyState'
|
|
14
|
+
|
|
13
15
|
type Props = {
|
|
14
16
|
globalSections: GlobalSectionsData
|
|
15
17
|
}
|
|
@@ -23,9 +25,9 @@ function Page({ globalSections }: Props) {
|
|
|
23
25
|
<GlobalSections {...globalSections}>
|
|
24
26
|
<NextSeo noindex nofollow />
|
|
25
27
|
|
|
26
|
-
<
|
|
28
|
+
<EmptyState title="Loading">
|
|
27
29
|
<UILoader />
|
|
28
|
-
</
|
|
30
|
+
</EmptyState>
|
|
29
31
|
</GlobalSections>
|
|
30
32
|
)
|
|
31
33
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//Input "Example Text!". Output: example-text
|
|
2
|
+
export function textToKebabCase(text: string): string {
|
|
3
|
+
// Replace spaces and special characters with hyphens
|
|
4
|
+
let kebabCase = text.replace(/[^\w\s]/gi, '-')
|
|
5
|
+
|
|
6
|
+
// Remove whitespace
|
|
7
|
+
kebabCase = kebabCase.replace(/\s+/g, '-')
|
|
8
|
+
|
|
9
|
+
// Convert to lowercase
|
|
10
|
+
kebabCase = kebabCase.toLowerCase()
|
|
11
|
+
|
|
12
|
+
return kebabCase ?? ''
|
|
13
|
+
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Icon } from '@faststore/ui'
|
|
2
|
-
|
|
3
|
-
const FooterFlags = [
|
|
4
|
-
{ image: <Icon width={32} height={22.5} name="Visa" />, text: 'Visa' },
|
|
5
|
-
{
|
|
6
|
-
image: <Icon width={32} height={22.5} name="Diners" />,
|
|
7
|
-
text: 'Diners Club',
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
image: <Icon width={32} height={22.5} name="Mastercard" />,
|
|
11
|
-
text: 'Mastercard',
|
|
12
|
-
},
|
|
13
|
-
{ image: <Icon width={32} height={22.5} name="EloCard" />, text: 'Elo Card' },
|
|
14
|
-
{ image: <Icon width={32} height={22.5} name="PayPal" />, text: 'PayPal' },
|
|
15
|
-
{ image: <Icon width={32} height={22.5} name="Stripe" />, text: 'Stripe' },
|
|
16
|
-
{
|
|
17
|
-
image: <Icon width={32} height={22.5} name="GooglePay" />,
|
|
18
|
-
text: 'GooglePay',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
image: <Icon width={32} height={22.5} name="ApplePay" />,
|
|
22
|
-
text: 'ApplePay',
|
|
23
|
-
},
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
export default FooterFlags
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import Incentives from 'src/components/ui/Incentives/Incentives'
|
|
2
|
-
import Section from '../Section'
|
|
3
|
-
import styles from './section.module.scss'
|
|
4
|
-
|
|
5
|
-
interface Incentive {
|
|
6
|
-
icon: string
|
|
7
|
-
title?: string
|
|
8
|
-
firstLineText: string
|
|
9
|
-
secondLineText?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface Props {
|
|
13
|
-
incentives: Incentive[]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function IncentivesHeader({ incentives }: Props) {
|
|
17
|
-
return (
|
|
18
|
-
<Section className={`${styles.section} section-incentives-header`}>
|
|
19
|
-
<Incentives incentives={incentives} colored />
|
|
20
|
-
</Section>
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default IncentivesHeader
|