@faststore/core 2.1.18 → 2.1.22
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +49 -45
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/eslint/.cache_1gneedd +1 -1
- package/.next/cache/next-server.js.nft.json +1 -1
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/routes-manifest.json +1 -1
- package/.next/server/chunks/{205.js → 144.js} +619 -2
- package/.next/server/chunks/721.js +679 -0
- package/.next/server/chunks/74.js +54 -20
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.js.nft.json +1 -1
- package/.next/server/pages/500.js.nft.json +1 -1
- package/.next/server/pages/[...slug].js +298 -71
- package/.next/server/pages/[...slug].js.nft.json +1 -1
- package/.next/server/pages/[slug]/p.js +2 -2
- package/.next/server/pages/[slug]/p.js.nft.json +1 -1
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js +5 -1
- package/.next/server/pages/_error.js.nft.json +1 -1
- package/.next/server/pages/account.js.nft.json +1 -1
- package/.next/server/pages/api/preview.js +35 -6
- package/.next/server/pages/checkout.js.nft.json +1 -1
- package/.next/server/pages/en-US/404.html +3 -3
- package/.next/server/pages/en-US/500.html +3 -3
- package/.next/server/pages/en-US/account.html +3 -3
- package/.next/server/pages/en-US/checkout.html +3 -3
- package/.next/server/pages/en-US/login.html +3 -3
- package/.next/server/pages/en-US/s.html +3 -3
- package/.next/server/pages/en-US.html +3 -3
- package/.next/server/pages/en-US.json +1 -1
- package/.next/server/pages/index.js +4 -474
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/login.js.nft.json +1 -1
- package/.next/server/pages/s.js +1 -1
- package/.next/server/pages/s.js.nft.json +1 -1
- package/.next/server/pages-manifest.json +4 -4
- package/.next/static/JOMe5dtqNwu-zmVu2HIcF/_buildManifest.js +1 -0
- package/.next/static/chunks/548-6b23e7ad82cd22b9.js +1 -0
- package/.next/static/chunks/706-8049971f9e5f6ad2.js +1 -0
- package/.next/static/chunks/{738-67a288ca3569cdbb.js → 738-a5ff304828f20cbf.js} +1 -1
- package/.next/static/chunks/791-c23aa4269c7955c8.js +1 -0
- package/.next/static/chunks/pages/{404-6da332b2c4ef0f41.js → 404-d5f20744ecd83121.js} +1 -1
- package/.next/static/chunks/pages/{500-88dd73506f17946c.js → 500-3911549ab88d0378.js} +1 -1
- package/.next/static/chunks/pages/[...slug]-3a37fd4d13cb2ba8.js +1 -0
- package/.next/static/chunks/pages/[slug]/{p-93d3f1c0f2d3aed2.js → p-bf47c90571846f86.js} +1 -1
- package/.next/static/chunks/pages/account-d248acc931146694.js +1 -0
- package/.next/static/chunks/pages/checkout-97f6d6f36f041a6f.js +1 -0
- package/.next/static/chunks/pages/index-e9727cb06d9ae961.js +1 -0
- package/.next/static/chunks/pages/{login-a688a70872ea61f5.js → login-4cc4b8e52608f076.js} +1 -1
- package/.next/static/chunks/pages/s-aabfa5c08338974a.js +1 -0
- package/.next/static/chunks/{webpack-8bf035049b590d82.js → webpack-764ce5753552a617.js} +1 -1
- package/.next/static/css/08e3fc80f9bad95d.css +1 -0
- package/.next/trace +80 -78
- package/.turbo/turbo-build.log +7 -8
- package/cms/faststore/content-types.json +2 -2
- package/package.json +2 -2
- package/src/components/ThirdPartyScripts/vtex.tsx +6 -0
- package/src/components/cms/GlobalSections.tsx +2 -3
- package/src/components/templates/LandingPage/LandingPage.tsx +102 -0
- package/src/components/templates/LandingPage/index.ts +2 -0
- package/src/components/templates/ProductListingPage/ProductListingPage.tsx +138 -0
- package/src/components/templates/ProductListingPage/index.ts +2 -0
- package/src/pages/[...slug].tsx +45 -130
- package/src/pages/api/preview.ts +18 -2
- package/src/pages/index.tsx +1 -3
- package/src/sdk/error/MissingContentError/MissingContentError.ts +15 -0
- package/src/sdk/error/MissingContentError/index.ts +1 -0
- package/src/sdk/error/MultipleContentError/MultipleContentError.ts +15 -0
- package/src/sdk/error/MultipleContentError/index.ts +1 -0
- package/src/server/cms.ts +7 -21
- package/.next/server/chunks/513.js +0 -257
- package/.next/server/chunks/90.js +0 -623
- package/.next/static/chunks/113-207e8eceab001eea.js +0 -1
- package/.next/static/chunks/548-ab84e9e8b49413ab.js +0 -1
- package/.next/static/chunks/791-b6747a7c7ca0967e.js +0 -1
- package/.next/static/chunks/pages/[...slug]-ab804df4ac6c945e.js +0 -1
- package/.next/static/chunks/pages/account-06126db1f6dc537e.js +0 -1
- package/.next/static/chunks/pages/checkout-5138b7956d64dde8.js +0 -1
- package/.next/static/chunks/pages/index-7ba4f6708af42d8e.js +0 -1
- package/.next/static/chunks/pages/s-99aad326e6aafeb7.js +0 -1
- package/.next/static/css/13ddbbc10e89ff0e.css +0 -1
- package/.next/static/hlJfefjb0gNJqepjDW_Eh/_buildManifest.js +0 -1
- /package/.next/static/{hlJfefjb0gNJqepjDW_Eh → JOMe5dtqNwu-zmVu2HIcF}/_ssgManifest.js +0 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -30,26 +30,25 @@ info - Generating static pages (7/7)
|
|
|
30
30
|
info - Finalizing page optimization...
|
|
31
31
|
|
|
32
32
|
Route (pages) Size First Load JS
|
|
33
|
-
┌ ● / 3.
|
|
34
|
-
├ └ css/584640ffee46aa49.css 6.51 kB
|
|
33
|
+
┌ ● / 3.23 kB 197 kB
|
|
35
34
|
├ /_app 0 B 77.9 kB
|
|
36
|
-
├ ● /[...slug]
|
|
37
|
-
├ └ css/
|
|
35
|
+
├ ● /[...slug] 4.14 kB 209 kB
|
|
36
|
+
├ └ css/08e3fc80f9bad95d.css 1.83 kB
|
|
38
37
|
├ ● /[slug]/p 11 kB 205 kB
|
|
39
38
|
├ └ css/0e00026896a2ee3e.css 11.2 kB
|
|
40
39
|
├ ○ /404 1.28 kB 114 kB
|
|
41
40
|
├ ● /500 1.3 kB 114 kB
|
|
42
|
-
├ ● /account
|
|
41
|
+
├ ● /account 670 B 113 kB
|
|
43
42
|
├ λ /api/graphql 0 B 77.9 kB
|
|
44
43
|
├ λ /api/preview 0 B 77.9 kB
|
|
45
|
-
├ ● /checkout
|
|
44
|
+
├ ● /checkout 657 B 113 kB
|
|
46
45
|
├ ● /login 1.2 kB 114 kB
|
|
47
|
-
└ ● /s
|
|
46
|
+
└ ● /s 4.55 kB 127 kB
|
|
48
47
|
+ First Load JS shared by all 80.6 kB
|
|
49
48
|
├ chunks/framework-dfd14d7ce6600b03.js 45.3 kB
|
|
50
49
|
├ chunks/main-fd466221927468fd.js 23.9 kB
|
|
51
50
|
├ chunks/pages/_app-79d333aa6001a806.js 6.38 kB
|
|
52
|
-
├ chunks/webpack-
|
|
51
|
+
├ chunks/webpack-764ce5753552a617.js 2.29 kB
|
|
53
52
|
└ css/f7ed956d370744ea.css 2.77 kB
|
|
54
53
|
|
|
55
54
|
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faststore/core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.22",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"browserslist": "supports es6-module and not dead",
|
|
6
6
|
"scripts": {
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
"msw": {
|
|
111
111
|
"workerDirectory": "public"
|
|
112
112
|
},
|
|
113
|
-
"gitHead": "
|
|
113
|
+
"gitHead": "72c6d601f027a15482fd8564b2ff32b6d348f5c6"
|
|
114
114
|
}
|
|
@@ -51,9 +51,8 @@ export const getGlobalSectionsData = async (
|
|
|
51
51
|
previewData: Locator
|
|
52
52
|
): Promise<GlobalSectionsData> => {
|
|
53
53
|
const { sections } = await getPage<PageContentType>({
|
|
54
|
-
...(previewData?.contentType === GLOBAL_SECTIONS_CONTENT_TYPE
|
|
55
|
-
|
|
56
|
-
: { filters: { 'settings.seo.slug': '/' } }),
|
|
54
|
+
...(previewData?.contentType === GLOBAL_SECTIONS_CONTENT_TYPE &&
|
|
55
|
+
previewData),
|
|
57
56
|
contentType: GLOBAL_SECTIONS_CONTENT_TYPE,
|
|
58
57
|
})
|
|
59
58
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { NextSeo, SiteLinksSearchBoxJsonLd } from 'next-seo'
|
|
2
|
+
import type { ComponentType } from 'react'
|
|
3
|
+
import type { Locator } from '@vtex/client-cms'
|
|
4
|
+
|
|
5
|
+
import MissingContentError from 'src/sdk/error/MissingContentError/MissingContentError'
|
|
6
|
+
import RenderSections from 'src/components/cms/RenderSections'
|
|
7
|
+
import Hero from 'src/components/sections/Hero'
|
|
8
|
+
import Incentives from 'src/components/sections/Incentives'
|
|
9
|
+
import ProductShelf from 'src/components/sections/ProductShelf'
|
|
10
|
+
import ProductTiles from 'src/components/sections/ProductTiles'
|
|
11
|
+
import BannerText from 'src/components/sections/BannerText'
|
|
12
|
+
import Newsletter from 'src/components/sections/Newsletter'
|
|
13
|
+
import { getPage } from 'src/server/cms'
|
|
14
|
+
import type { PageContentType } from 'src/server/cms'
|
|
15
|
+
import CUSTOM_COMPONENTS from 'src/customizations/components'
|
|
16
|
+
|
|
17
|
+
import storeConfig from '../../../../faststore.config'
|
|
18
|
+
|
|
19
|
+
/* A list of components that can be used in the CMS. */
|
|
20
|
+
const COMPONENTS: Record<string, ComponentType<any>> = {
|
|
21
|
+
Hero,
|
|
22
|
+
Incentives,
|
|
23
|
+
ProductShelf,
|
|
24
|
+
ProductTiles,
|
|
25
|
+
BannerText,
|
|
26
|
+
Newsletter,
|
|
27
|
+
...CUSTOM_COMPONENTS,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type LandingPageProps = {
|
|
31
|
+
page: PageContentType
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default function LandingPage({
|
|
35
|
+
page: { sections, settings },
|
|
36
|
+
}: LandingPageProps) {
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
{/* SEO */}
|
|
40
|
+
<NextSeo
|
|
41
|
+
title={settings?.seo?.title ?? storeConfig.seo.title}
|
|
42
|
+
description={settings?.seo?.description ?? storeConfig.seo?.description}
|
|
43
|
+
titleTemplate={storeConfig.seo?.titleTemplate ?? storeConfig.seo?.title}
|
|
44
|
+
canonical={settings?.seo?.canonical ?? storeConfig.storeUrl}
|
|
45
|
+
openGraph={{
|
|
46
|
+
type: 'website',
|
|
47
|
+
url: storeConfig.storeUrl,
|
|
48
|
+
title: settings?.seo?.title ?? storeConfig.seo.title,
|
|
49
|
+
description:
|
|
50
|
+
settings?.seo?.description ?? storeConfig.seo.description,
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
<SiteLinksSearchBoxJsonLd
|
|
54
|
+
url={storeConfig.storeUrl}
|
|
55
|
+
potentialActions={[
|
|
56
|
+
{
|
|
57
|
+
target: `${storeConfig.storeUrl}/s/?q`,
|
|
58
|
+
queryInput: 'search_term_string',
|
|
59
|
+
},
|
|
60
|
+
]}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
{/*
|
|
64
|
+
WARNING: Do not import or render components from any
|
|
65
|
+
other folder than '../components/sections' in here.
|
|
66
|
+
|
|
67
|
+
This is necessary to keep the integration with the CMS
|
|
68
|
+
easy and consistent, enabling the change and reorder
|
|
69
|
+
of elements on this page.
|
|
70
|
+
|
|
71
|
+
If needed, wrap your component in a <Section /> component
|
|
72
|
+
(not the HTML tag) before rendering it here.
|
|
73
|
+
*/}
|
|
74
|
+
<RenderSections sections={sections} components={COMPONENTS} />
|
|
75
|
+
</>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const getLandingPageBySlug = async (
|
|
80
|
+
slug: string,
|
|
81
|
+
previewData: Locator
|
|
82
|
+
) => {
|
|
83
|
+
try {
|
|
84
|
+
const landingPageData = await getPage<PageContentType>({
|
|
85
|
+
...(previewData?.contentType === 'landingPage'
|
|
86
|
+
? previewData
|
|
87
|
+
: {
|
|
88
|
+
filters: {
|
|
89
|
+
filters: { 'settings.seo.slug': `/${slug}` },
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
contentType: 'landingPage',
|
|
93
|
+
})
|
|
94
|
+
return landingPageData
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (error instanceof MissingContentError) {
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
throw error
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { SearchState } from '@faststore/sdk'
|
|
2
|
+
import {
|
|
3
|
+
formatSearchState,
|
|
4
|
+
parseSearchState,
|
|
5
|
+
SearchProvider,
|
|
6
|
+
} from '@faststore/sdk'
|
|
7
|
+
import { BreadcrumbJsonLd, NextSeo } from 'next-seo'
|
|
8
|
+
import { useRouter } from 'next/router'
|
|
9
|
+
import { useMemo } from 'react'
|
|
10
|
+
|
|
11
|
+
import type { ServerCollectionPageQueryQuery } from '@generated/graphql'
|
|
12
|
+
import Breadcrumb from 'src/components/sections/Breadcrumb'
|
|
13
|
+
import Hero from 'src/components/sections/Hero'
|
|
14
|
+
import ProductGallery from 'src/components/sections/ProductGallery'
|
|
15
|
+
import ProductShelf from 'src/components/sections/ProductShelf'
|
|
16
|
+
import ScrollToTopButton from 'src/components/sections/ScrollToTopButton'
|
|
17
|
+
import { ITEMS_PER_PAGE } from 'src/constants'
|
|
18
|
+
import { useApplySearchState } from 'src/sdk/search/state'
|
|
19
|
+
|
|
20
|
+
import type { ComponentType } from 'react'
|
|
21
|
+
import RenderSections from 'src/components/cms/RenderSections'
|
|
22
|
+
import CUSTOM_COMPONENTS from 'src/customizations/components'
|
|
23
|
+
import { PLPContentType } from 'src/server/cms'
|
|
24
|
+
|
|
25
|
+
import storeConfig from '../../../../faststore.config'
|
|
26
|
+
|
|
27
|
+
export type ProductListingPageProps = ServerCollectionPageQueryQuery & {
|
|
28
|
+
page: PLPContentType
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Sections: Components imported from each store's custom components and '../components/sections' only.
|
|
33
|
+
* Do not import or render components from any other folder in here.
|
|
34
|
+
*/
|
|
35
|
+
const COMPONENTS: Record<string, ComponentType<any>> = {
|
|
36
|
+
Breadcrumb,
|
|
37
|
+
Hero,
|
|
38
|
+
ProductGallery,
|
|
39
|
+
ProductShelf,
|
|
40
|
+
...CUSTOM_COMPONENTS,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type UseSearchParams = ServerCollectionPageQueryQuery & {
|
|
44
|
+
sort: SearchState['sort']
|
|
45
|
+
}
|
|
46
|
+
const useSearchParams = ({
|
|
47
|
+
collection,
|
|
48
|
+
sort,
|
|
49
|
+
}: UseSearchParams): SearchState => {
|
|
50
|
+
const selectedFacets = collection?.meta.selectedFacets
|
|
51
|
+
const { asPath } = useRouter()
|
|
52
|
+
|
|
53
|
+
const hrefState = useMemo(() => {
|
|
54
|
+
const url = new URL(asPath, 'http://localhost')
|
|
55
|
+
|
|
56
|
+
const shouldUpdateDefaultSort = sort && !url.searchParams.has('sort')
|
|
57
|
+
if (shouldUpdateDefaultSort) {
|
|
58
|
+
url.searchParams.set('sort', sort)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const newState = parseSearchState(url)
|
|
62
|
+
// In case we are in an incomplete url
|
|
63
|
+
if (newState.selectedFacets.length === 0) {
|
|
64
|
+
newState.selectedFacets = selectedFacets
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return formatSearchState(newState).href
|
|
68
|
+
}, [asPath, selectedFacets, sort])
|
|
69
|
+
|
|
70
|
+
return useMemo(() => parseSearchState(new URL(hrefState)), [hrefState])
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default function ProductListingPage({
|
|
74
|
+
page: { sections, settings },
|
|
75
|
+
...otherProps
|
|
76
|
+
}: ProductListingPageProps) {
|
|
77
|
+
const { collection } = otherProps
|
|
78
|
+
const router = useRouter()
|
|
79
|
+
const applySearchState = useApplySearchState()
|
|
80
|
+
const searchParams = useSearchParams({
|
|
81
|
+
...otherProps,
|
|
82
|
+
sort: settings?.productGallery?.sortBySelection as SearchState['sort'],
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const { page, sort } = searchParams
|
|
86
|
+
const title = collection?.seo.title ?? storeConfig.seo.title
|
|
87
|
+
const description = collection?.seo.description ?? storeConfig.seo.title
|
|
88
|
+
const pageQuery = page !== 0 ? `?page=${page}` : ''
|
|
89
|
+
const separator = pageQuery !== '' ? '&' : '?'
|
|
90
|
+
const sortQuery = !!sort ? `${separator}sort=${sort}` : ''
|
|
91
|
+
const [pathname] = router.asPath.split('?')
|
|
92
|
+
const canonical = `${storeConfig.storeUrl}${pathname}${pageQuery}${sortQuery}`
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<>
|
|
96
|
+
<SearchProvider
|
|
97
|
+
onChange={applySearchState}
|
|
98
|
+
itemsPerPage={settings?.productGallery?.itemsPerPage ?? ITEMS_PER_PAGE}
|
|
99
|
+
{...searchParams}
|
|
100
|
+
>
|
|
101
|
+
{/* SEO */}
|
|
102
|
+
<NextSeo
|
|
103
|
+
title={title}
|
|
104
|
+
description={description}
|
|
105
|
+
titleTemplate={storeConfig.seo.titleTemplate}
|
|
106
|
+
canonical={canonical}
|
|
107
|
+
openGraph={{
|
|
108
|
+
type: 'website',
|
|
109
|
+
title,
|
|
110
|
+
description,
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
<BreadcrumbJsonLd
|
|
114
|
+
itemListElements={collection?.breadcrumbList.itemListElement ?? []}
|
|
115
|
+
/>
|
|
116
|
+
|
|
117
|
+
{/*
|
|
118
|
+
WARNING: Do not import or render components from any
|
|
119
|
+
other folder than '../components/sections' in here.
|
|
120
|
+
|
|
121
|
+
This is necessary to keep the integration with the CMS
|
|
122
|
+
easy and consistent, enabling the change and reorder
|
|
123
|
+
of elements on this page.
|
|
124
|
+
|
|
125
|
+
If needed, wrap your component in a <Section /> component
|
|
126
|
+
(not the HTML tag) before rendering it here.
|
|
127
|
+
*/}
|
|
128
|
+
<RenderSections
|
|
129
|
+
context={collection}
|
|
130
|
+
sections={sections}
|
|
131
|
+
components={COMPONENTS}
|
|
132
|
+
/>
|
|
133
|
+
|
|
134
|
+
<ScrollToTopButton />
|
|
135
|
+
</SearchProvider>
|
|
136
|
+
</>
|
|
137
|
+
)
|
|
138
|
+
}
|
package/src/pages/[...slug].tsx
CHANGED
|
@@ -1,152 +1,51 @@
|
|
|
1
1
|
import { isNotFoundError } from '@faststore/api'
|
|
2
2
|
import { gql } from '@faststore/graphql-utils'
|
|
3
|
-
import type { SearchState } from '@faststore/sdk'
|
|
4
|
-
import {
|
|
5
|
-
formatSearchState,
|
|
6
|
-
parseSearchState,
|
|
7
|
-
SearchProvider,
|
|
8
|
-
} from '@faststore/sdk'
|
|
9
3
|
import type { GetStaticPaths, GetStaticProps } from 'next'
|
|
10
|
-
import { BreadcrumbJsonLd, NextSeo } from 'next-seo'
|
|
11
|
-
import { useRouter } from 'next/router'
|
|
12
|
-
import { useMemo } from 'react'
|
|
13
4
|
|
|
14
5
|
import type {
|
|
15
6
|
ServerCollectionPageQueryQuery,
|
|
16
7
|
ServerCollectionPageQueryQueryVariables,
|
|
17
8
|
} from '@generated/graphql'
|
|
18
|
-
import Breadcrumb from 'src/components/sections/Breadcrumb'
|
|
19
|
-
import Hero from 'src/components/sections/Hero'
|
|
20
|
-
import ProductGallery from 'src/components/sections/ProductGallery'
|
|
21
|
-
import ProductShelf from 'src/components/sections/ProductShelf'
|
|
22
|
-
import ScrollToTopButton from 'src/components/sections/ScrollToTopButton'
|
|
23
|
-
import { ITEMS_PER_PAGE, ITEMS_PER_SECTION } from 'src/constants'
|
|
24
|
-
import { useApplySearchState } from 'src/sdk/search/state'
|
|
25
9
|
import { mark } from 'src/sdk/tests/mark'
|
|
26
10
|
import { execute } from 'src/server'
|
|
27
11
|
|
|
28
12
|
import { Locator } from '@vtex/client-cms'
|
|
29
|
-
import type { ComponentType } from 'react'
|
|
30
13
|
import GlobalSections, {
|
|
31
14
|
getGlobalSectionsData,
|
|
32
15
|
GlobalSectionsData,
|
|
33
16
|
} from 'src/components/cms/GlobalSections'
|
|
34
|
-
import
|
|
35
|
-
import
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
17
|
+
import { getPage, PageContentType, PLPContentType } from 'src/server/cms'
|
|
18
|
+
import ProductListingPage, {
|
|
19
|
+
ProductListingPageProps,
|
|
20
|
+
} from 'src/components/templates/ProductListingPage'
|
|
21
|
+
import LandingPage, {
|
|
22
|
+
getLandingPageBySlug,
|
|
23
|
+
LandingPageProps,
|
|
24
|
+
} from 'src/components/templates/LandingPage'
|
|
25
|
+
|
|
26
|
+
type BaseProps = {
|
|
41
27
|
globalSections: GlobalSectionsData
|
|
42
28
|
}
|
|
43
29
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
type UseSearchParams = ServerCollectionPageQueryQuery & {
|
|
57
|
-
sort: SearchState['sort']
|
|
58
|
-
}
|
|
59
|
-
const useSearchParams = ({
|
|
60
|
-
collection,
|
|
61
|
-
sort,
|
|
62
|
-
}: UseSearchParams): SearchState => {
|
|
63
|
-
const selectedFacets = collection?.meta.selectedFacets
|
|
64
|
-
const { asPath } = useRouter()
|
|
65
|
-
|
|
66
|
-
const hrefState = useMemo(() => {
|
|
67
|
-
const url = new URL(asPath, 'http://localhost')
|
|
68
|
-
|
|
69
|
-
const shouldUpdateDefaultSort = sort && !url.searchParams.has('sort')
|
|
70
|
-
if (shouldUpdateDefaultSort) {
|
|
71
|
-
url.searchParams.set('sort', sort)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const newState = parseSearchState(url)
|
|
75
|
-
// In case we are in an incomplete url
|
|
76
|
-
if (newState.selectedFacets.length === 0) {
|
|
77
|
-
newState.selectedFacets = selectedFacets
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return formatSearchState(newState).href
|
|
81
|
-
}, [asPath, selectedFacets, sort])
|
|
82
|
-
|
|
83
|
-
return useMemo(() => parseSearchState(new URL(hrefState)), [hrefState])
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function Page({
|
|
87
|
-
page: { sections, settings },
|
|
88
|
-
globalSections,
|
|
89
|
-
...otherProps
|
|
90
|
-
}: Props) {
|
|
91
|
-
const { collection } = otherProps
|
|
92
|
-
const router = useRouter()
|
|
93
|
-
const applySearchState = useApplySearchState()
|
|
94
|
-
const searchParams = useSearchParams({
|
|
95
|
-
...otherProps,
|
|
96
|
-
sort: settings?.productGallery?.sortBySelection as SearchState['sort'],
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
const { page, sort } = searchParams
|
|
100
|
-
const title = collection?.seo.title ?? storeConfig.seo.title
|
|
101
|
-
const description = collection?.seo.description ?? storeConfig.seo.title
|
|
102
|
-
const pageQuery = page !== 0 ? `?page=${page}` : ''
|
|
103
|
-
const separator = pageQuery !== '' ? '&' : '?'
|
|
104
|
-
const sortQuery = !!sort ? `${separator}sort=${sort}` : ''
|
|
105
|
-
const [pathname] = router.asPath.split('?')
|
|
106
|
-
const canonical = `${storeConfig.storeUrl}${pathname}${pageQuery}${sortQuery}`
|
|
30
|
+
type Props = BaseProps &
|
|
31
|
+
(
|
|
32
|
+
| ({
|
|
33
|
+
type: 'plp'
|
|
34
|
+
page: PLPContentType
|
|
35
|
+
} & ServerCollectionPageQueryQuery)
|
|
36
|
+
| {
|
|
37
|
+
type: 'page'
|
|
38
|
+
page: PageContentType
|
|
39
|
+
}
|
|
40
|
+
)
|
|
107
41
|
|
|
42
|
+
function Page({ globalSections, type, ...otherProps }: Props) {
|
|
108
43
|
return (
|
|
109
44
|
<GlobalSections {...globalSections}>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
>
|
|
115
|
-
{/* SEO */}
|
|
116
|
-
<NextSeo
|
|
117
|
-
title={title}
|
|
118
|
-
description={description}
|
|
119
|
-
titleTemplate={storeConfig.seo.titleTemplate}
|
|
120
|
-
canonical={canonical}
|
|
121
|
-
openGraph={{
|
|
122
|
-
type: 'website',
|
|
123
|
-
title,
|
|
124
|
-
description,
|
|
125
|
-
}}
|
|
126
|
-
/>
|
|
127
|
-
<BreadcrumbJsonLd
|
|
128
|
-
itemListElements={collection?.breadcrumbList.itemListElement ?? []}
|
|
129
|
-
/>
|
|
130
|
-
|
|
131
|
-
{/*
|
|
132
|
-
WARNING: Do not import or render components from any
|
|
133
|
-
other folder than '../components/sections' in here.
|
|
134
|
-
|
|
135
|
-
This is necessary to keep the integration with the CMS
|
|
136
|
-
easy and consistent, enabling the change and reorder
|
|
137
|
-
of elements on this page.
|
|
138
|
-
|
|
139
|
-
If needed, wrap your component in a <Section /> component
|
|
140
|
-
(not the HTML tag) before rendering it here.
|
|
141
|
-
*/}
|
|
142
|
-
<RenderSections
|
|
143
|
-
context={collection}
|
|
144
|
-
sections={sections}
|
|
145
|
-
components={COMPONENTS}
|
|
146
|
-
/>
|
|
147
|
-
|
|
148
|
-
<ScrollToTopButton />
|
|
149
|
-
</SearchProvider>
|
|
45
|
+
{type === 'plp' && (
|
|
46
|
+
<ProductListingPage {...(otherProps as ProductListingPageProps)} />
|
|
47
|
+
)}
|
|
48
|
+
{type === 'page' && <LandingPage {...(otherProps as LandingPageProps)} />}
|
|
150
49
|
</GlobalSections>
|
|
151
50
|
)
|
|
152
51
|
}
|
|
@@ -180,7 +79,22 @@ export const getStaticProps: GetStaticProps<
|
|
|
180
79
|
{ slug: string[] },
|
|
181
80
|
Locator
|
|
182
81
|
> = async ({ params, previewData }) => {
|
|
183
|
-
const [
|
|
82
|
+
const [landingPagePromise, globalSectionsPromise] = [
|
|
83
|
+
getLandingPageBySlug(params?.slug[0], previewData),
|
|
84
|
+
getGlobalSectionsData(previewData),
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
if (await landingPagePromise) {
|
|
88
|
+
return {
|
|
89
|
+
props: {
|
|
90
|
+
page: await landingPagePromise,
|
|
91
|
+
globalSections: await globalSectionsPromise,
|
|
92
|
+
type: 'page',
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const [{ data, errors = [] }, page] = await Promise.all([
|
|
184
98
|
execute<
|
|
185
99
|
ServerCollectionPageQueryQueryVariables,
|
|
186
100
|
ServerCollectionPageQueryQuery
|
|
@@ -192,7 +106,6 @@ export const getStaticProps: GetStaticProps<
|
|
|
192
106
|
...(previewData?.contentType === 'plp' ? previewData : null),
|
|
193
107
|
contentType: 'plp',
|
|
194
108
|
}),
|
|
195
|
-
getGlobalSectionsData(previewData),
|
|
196
109
|
])
|
|
197
110
|
|
|
198
111
|
const notFound = errors.find(isNotFoundError)
|
|
@@ -204,6 +117,7 @@ export const getStaticProps: GetStaticProps<
|
|
|
204
117
|
}
|
|
205
118
|
|
|
206
119
|
if (errors.length > 0) {
|
|
120
|
+
console.error(...errors)
|
|
207
121
|
throw errors[0]
|
|
208
122
|
}
|
|
209
123
|
|
|
@@ -211,7 +125,8 @@ export const getStaticProps: GetStaticProps<
|
|
|
211
125
|
props: {
|
|
212
126
|
...data,
|
|
213
127
|
page,
|
|
214
|
-
globalSections,
|
|
128
|
+
globalSections: await globalSectionsPromise,
|
|
129
|
+
type: 'plp',
|
|
215
130
|
},
|
|
216
131
|
}
|
|
217
132
|
}
|
package/src/pages/api/preview.ts
CHANGED
|
@@ -3,6 +3,13 @@ import type { NextApiHandler, NextApiRequest } from 'next'
|
|
|
3
3
|
import { clientCMS } from 'src/server/cms'
|
|
4
4
|
import { previewRedirects } from '../../../faststore.config'
|
|
5
5
|
|
|
6
|
+
type Settings = {
|
|
7
|
+
seo: {
|
|
8
|
+
slug: string
|
|
9
|
+
title: string
|
|
10
|
+
description: string
|
|
11
|
+
}
|
|
12
|
+
}
|
|
6
13
|
class StatusError extends Error {
|
|
7
14
|
constructor(message: string, public status: number) {
|
|
8
15
|
super(message)
|
|
@@ -46,8 +53,17 @@ const handler: NextApiHandler = async (req, res) => {
|
|
|
46
53
|
res.setPreviewData(locator, { maxAge: 3600 })
|
|
47
54
|
|
|
48
55
|
// Redirect to the path from the fetched locator
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
if (previewRedirects[locator.contentType]) {
|
|
57
|
+
res.redirect(previewRedirects[locator.contentType])
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (locator.contentType === 'landingPage') {
|
|
62
|
+
res.redirect(`${(page.settings as Settings)?.seo.slug}`)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
res.redirect('/')
|
|
51
67
|
} catch (error) {
|
|
52
68
|
if (error instanceof StatusError) {
|
|
53
69
|
res.status(error.status).end(error.message)
|
package/src/pages/index.tsx
CHANGED
|
@@ -87,9 +87,7 @@ export const getStaticProps: GetStaticProps<
|
|
|
87
87
|
> = async ({ previewData }) => {
|
|
88
88
|
const [page, globalSections] = await Promise.all([
|
|
89
89
|
getPage<PageContentType>({
|
|
90
|
-
...(previewData?.contentType === 'home'
|
|
91
|
-
? previewData
|
|
92
|
-
: { filters: { 'settings.seo.slug': '/' } }),
|
|
90
|
+
...(previewData?.contentType === 'home' && previewData),
|
|
93
91
|
contentType: 'home',
|
|
94
92
|
}),
|
|
95
93
|
getGlobalSectionsData(previewData),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Options } from 'src/server/cms'
|
|
2
|
+
|
|
3
|
+
export default class MissingContentError extends Error {
|
|
4
|
+
constructor(options: Options) {
|
|
5
|
+
super(
|
|
6
|
+
`Missing content on the CMS for content type ${
|
|
7
|
+
options.contentType
|
|
8
|
+
}. Add content before proceeding. Context: ${JSON.stringify(
|
|
9
|
+
options,
|
|
10
|
+
null,
|
|
11
|
+
2
|
|
12
|
+
)}`
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './MissingContentError'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Options } from 'src/server/cms'
|
|
2
|
+
|
|
3
|
+
export default class MultipleContentError extends Error {
|
|
4
|
+
constructor(options: Options) {
|
|
5
|
+
super(
|
|
6
|
+
`Multiple content defined on the CMS for content type ${
|
|
7
|
+
options.contentType
|
|
8
|
+
}. Remove duplicated content before proceeding. Context: ${JSON.stringify(
|
|
9
|
+
options,
|
|
10
|
+
null,
|
|
11
|
+
2
|
|
12
|
+
)}`
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './MultipleContentError'
|