@faststore/core 2.1.83 → 2.2.0-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 +9 -7
- package/.turbo/turbo-test.log +25 -0
- package/@generated/graphql/index.ts +183 -79
- package/@generated/graphql/persisted.json +5 -5
- package/@generated/graphql/schema.graphql +1053 -0
- package/api/index.ts +15 -0
- package/codegen.yml +2 -1
- package/generate.sh +71 -0
- package/index.ts +15 -0
- package/jest.config.js +6 -0
- package/package.json +33 -18
- package/src/components/ThirdPartyScripts/ThirdPartyScripts.tsx +1 -2
- package/src/components/cms/RenderSections.tsx +4 -5
- package/src/components/product/ProductGrid/ProductGrid.tsx +4 -3
- package/src/components/sections/Breadcrumb/Breadcrumb.tsx +17 -22
- package/src/components/sections/CrossSellingShelf/CrossSellingShelf.tsx +8 -6
- package/src/components/sections/ProductDetails/ProductDetails.tsx +23 -33
- package/src/components/sections/ProductGallery/ProductGallery.tsx +15 -26
- package/src/components/sections/ProductShelf/ProductShelf.tsx +1 -1
- package/src/components/sections/ProductTiles/ProductTiles.tsx +4 -3
- package/src/components/templates/ProductListingPage/ProductListing.tsx +95 -0
- package/src/components/templates/ProductListingPage/ProductListingPage.tsx +34 -68
- package/src/components/templates/SearchPage/SearchPage.tsx +81 -0
- package/src/components/templates/SearchPage/index.ts +2 -0
- package/src/components/ui/ProductGallery/ProductGallery.tsx +24 -16
- package/src/components/ui/ProductGallery/ProductGalleryPage.tsx +8 -7
- package/src/components/ui/ProductGallery/useDelayedFacets.ts +3 -3
- package/src/components/ui/ProductShelf/ProductShelf.tsx +3 -2
- package/src/customizations/GlobalOverrides.tsx +0 -2
- package/src/customizations/fragments/ClientProduct.ts +9 -0
- package/src/customizations/fragments/ClientProductGallery.ts +19 -0
- package/src/customizations/fragments/ClientProducts.ts +19 -0
- package/src/customizations/fragments/ServerCollectionPage.ts +9 -0
- package/src/customizations/fragments/ServerProductPage.ts +9 -0
- package/src/customizations/graphql/thirdParty/resolvers/index.ts +3 -0
- package/src/customizations/graphql/vtex/resolvers/index.ts +3 -0
- package/src/pages/[...slug].tsx +5 -3
- package/src/pages/[slug]/p.tsx +38 -18
- package/src/pages/s.tsx +22 -34
- package/src/sdk/graphql/useQuery.ts +17 -13
- package/src/sdk/overrides/PageProvider.tsx +78 -0
- package/src/sdk/product/useLocalizedVariables.ts +33 -0
- package/src/sdk/product/usePageProductsQuery.ts +139 -0
- package/src/{components/sections/ProductGallery/useGalleryQuery.ts → sdk/product/useProductGalleryQuery.ts} +12 -13
- package/src/sdk/product/{useProduct.ts → useProductQuery.ts} +10 -7
- package/src/sdk/product/useProductsPrefetch.ts +72 -0
- package/src/sdk/product/useProductsQuery.ts +17 -63
- package/src/server/generator/generateGraphQLSchemaFile.ts +3 -0
- package/src/server/generator/schema.ts +82 -0
- package/src/server/index.ts +34 -21
- package/src/server/options.ts +16 -0
- package/test/server/index.test.ts +146 -0
- package/.next/BUILD_ID +0 -1
- package/.next/build-manifest.json +0 -129
- package/.next/cache/.tsbuildinfo +0 -1
- package/.next/cache/config.json +0 -7
- package/.next/cache/eslint/.cache_1gneedd +0 -1
- package/.next/cache/next-server.js.nft.json +0 -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/export-marker.json +0 -1
- package/.next/images-manifest.json +0 -1
- package/.next/next-server.js.nft.json +0 -1
- package/.next/package.json +0 -1
- package/.next/prerender-manifest.json +0 -1
- package/.next/react-loadable-manifest.json +0 -44
- package/.next/required-server-files.json +0 -1
- package/.next/routes-manifest.json +0 -1
- package/.next/server/chunks/143.js +0 -106
- package/.next/server/chunks/177.js +0 -120
- package/.next/server/chunks/183.js +0 -94
- package/.next/server/chunks/184.js +0 -61
- package/.next/server/chunks/186.js +0 -113
- package/.next/server/chunks/289.js +0 -239
- package/.next/server/chunks/312.js +0 -697
- package/.next/server/chunks/350.js +0 -143
- package/.next/server/chunks/483.js +0 -650
- package/.next/server/chunks/487.js +0 -9142
- package/.next/server/chunks/53.js +0 -61
- package/.next/server/chunks/530.js +0 -626
- package/.next/server/chunks/576.js +0 -94
- package/.next/server/chunks/650.js +0 -9142
- package/.next/server/chunks/676.js +0 -32
- package/.next/server/chunks/693.js +0 -58
- package/.next/server/chunks/71.js +0 -1254
- package/.next/server/chunks/74.js +0 -4054
- package/.next/server/chunks/753.js +0 -509
- package/.next/server/chunks/779.js +0 -58
- package/.next/server/chunks/825.js +0 -4039
- package/.next/server/chunks/854.js +0 -72
- package/.next/server/chunks/859.js +0 -959
- package/.next/server/chunks/907.js +0 -1911
- package/.next/server/chunks/933.js +0 -517
- package/.next/server/chunks/98.js +0 -124
- package/.next/server/chunks/988.js +0 -211
- package/.next/server/chunks/font-manifest.json +0 -1
- package/.next/server/font-manifest.json +0 -1
- package/.next/server/middleware-build-manifest.js +0 -1
- package/.next/server/middleware-manifest.json +0 -6
- package/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/.next/server/pages/404.js +0 -386
- package/.next/server/pages/404.js.nft.json +0 -1
- package/.next/server/pages/500.js +0 -388
- package/.next/server/pages/500.js.nft.json +0 -1
- package/.next/server/pages/[...slug].js +0 -1005
- package/.next/server/pages/[...slug].js.nft.json +0 -1
- package/.next/server/pages/[slug]/p.js +0 -2269
- package/.next/server/pages/[slug]/p.js.nft.json +0 -1
- package/.next/server/pages/_app.js +0 -280
- package/.next/server/pages/_app.js.nft.json +0 -1
- package/.next/server/pages/_document.js +0 -374
- package/.next/server/pages/_document.js.nft.json +0 -1
- package/.next/server/pages/_error.js +0 -164
- package/.next/server/pages/_error.js.nft.json +0 -1
- package/.next/server/pages/account.js +0 -363
- package/.next/server/pages/account.js.nft.json +0 -1
- package/.next/server/pages/api/graphql.js +0 -365
- package/.next/server/pages/api/graphql.js.nft.json +0 -1
- package/.next/server/pages/api/health/live.js +0 -31
- package/.next/server/pages/api/health/live.js.nft.json +0 -1
- package/.next/server/pages/api/health/ready.js +0 -31
- package/.next/server/pages/api/health/ready.js.nft.json +0 -1
- package/.next/server/pages/api/preview.js +0 -148
- package/.next/server/pages/api/preview.js.nft.json +0 -1
- package/.next/server/pages/checkout.js +0 -363
- package/.next/server/pages/checkout.js.nft.json +0 -1
- package/.next/server/pages/en-US/404.html +0 -81
- package/.next/server/pages/en-US/404.json +0 -1
- package/.next/server/pages/en-US/500.html +0 -81
- package/.next/server/pages/en-US/500.json +0 -1
- package/.next/server/pages/en-US/account.html +0 -81
- package/.next/server/pages/en-US/account.json +0 -1
- package/.next/server/pages/en-US/checkout.html +0 -81
- package/.next/server/pages/en-US/checkout.json +0 -1
- package/.next/server/pages/en-US/login.html +0 -81
- package/.next/server/pages/en-US/login.json +0 -1
- package/.next/server/pages/en-US/s.html +0 -81
- package/.next/server/pages/en-US/s.json +0 -1
- package/.next/server/pages/en-US.html +0 -81
- package/.next/server/pages/en-US.json +0 -1
- package/.next/server/pages/index.js +0 -439
- package/.next/server/pages/index.js.nft.json +0 -1
- package/.next/server/pages/login.js +0 -368
- package/.next/server/pages/login.js.nft.json +0 -1
- package/.next/server/pages/s.js +0 -466
- package/.next/server/pages/s.js.nft.json +0 -1
- package/.next/server/pages-manifest.json +0 -18
- package/.next/server/webpack-api-runtime.js +0 -229
- package/.next/server/webpack-runtime.js +0 -229
- package/.next/static/chunks/143.dd8a556e6957baa1.js +0 -1
- package/.next/static/chunks/148.3bb7e05cc5d1c1c4.js +0 -1
- package/.next/static/chunks/238-e5e4b2094f0e1df8.js +0 -1
- package/.next/static/chunks/243-8f5650ed908aa75c.js +0 -1
- package/.next/static/chunks/530.848b014622932b93.js +0 -1
- package/.next/static/chunks/548-ab84e9e8b49413ab.js +0 -1
- package/.next/static/chunks/603-d1c069aa8a349c86.js +0 -1
- package/.next/static/chunks/651.7142f31ce1e052b3.js +0 -1
- package/.next/static/chunks/709.7bc5a25ce30abda6.js +0 -1
- package/.next/static/chunks/738-67a288ca3569cdbb.js +0 -1
- package/.next/static/chunks/741.52f7fb873418346f.js +0 -1
- package/.next/static/chunks/98.97381d2021f86cd9.js +0 -1
- package/.next/static/chunks/988.d10040040cdfebbb.js +0 -1
- package/.next/static/chunks/framework-dfd14d7ce6600b03.js +0 -1
- package/.next/static/chunks/main-fd466221927468fd.js +0 -1
- package/.next/static/chunks/pages/404-af78f7cd1d3c1f60.js +0 -1
- package/.next/static/chunks/pages/500-f6346ca5f9dc4fef.js +0 -1
- package/.next/static/chunks/pages/[...slug]-ca533c74c22cb787.js +0 -1
- package/.next/static/chunks/pages/[slug]/p-7bb760bb3fcfc0bb.js +0 -1
- package/.next/static/chunks/pages/_app-895781b1c7b5bf56.js +0 -1
- package/.next/static/chunks/pages/_error-a7a0c1d9bfbb4f38.js +0 -1
- package/.next/static/chunks/pages/account-05bd79fb78365e88.js +0 -1
- package/.next/static/chunks/pages/checkout-c973786e68f25a39.js +0 -1
- package/.next/static/chunks/pages/index-d521ce4f4e2b89a6.js +0 -1
- package/.next/static/chunks/pages/login-8deb9243376b6aa1.js +0 -1
- package/.next/static/chunks/pages/s-0e516ab36bb49c99.js +0 -1
- package/.next/static/chunks/polyfills-c67a75d1b6f99dc8.js +0 -1
- package/.next/static/chunks/webpack-062512aedeb4b39e.js +0 -1
- package/.next/static/css/0d056c673d2869bb.css +0 -1
- package/.next/static/css/4b7138899cd07c63.css +0 -1
- package/.next/static/css/527e334fa69cf40a.css +0 -1
- package/.next/static/css/6e1a7434f061d0ef.css +0 -1
- package/.next/static/css/9e76fef1c9ca89af.css +0 -1
- package/.next/static/css/a2eefb25a4608343.css +0 -1
- package/.next/static/css/cb7d1fcea42fab9c.css +0 -1
- package/.next/static/css/df588bb98c0b0ca6.css +0 -1
- package/.next/static/css/e3b039e8f5daf95f.css +0 -1
- package/.next/static/css/f0e2d1b8832e935d.css +0 -1
- package/.next/static/jxv75E0IlXYBYk71TulQP/_buildManifest.js +0 -1
- package/.next/static/jxv75E0IlXYBYk71TulQP/_ssgManifest.js +0 -1
- package/.next/trace +0 -80
- package/public/~partytown/debug/partytown-atomics.js +0 -556
- package/public/~partytown/debug/partytown-media.js +0 -374
- package/public/~partytown/debug/partytown-sandbox-sw.js +0 -543
- package/public/~partytown/debug/partytown-sw.js +0 -59
- package/public/~partytown/debug/partytown-ww-atomics.js +0 -1789
- package/public/~partytown/debug/partytown-ww-sw.js +0 -1781
- package/public/~partytown/debug/partytown.js +0 -72
- package/public/~partytown/partytown-atomics.js +0 -2
- package/public/~partytown/partytown-media.js +0 -2
- package/public/~partytown/partytown-sw.js +0 -2
- package/public/~partytown/partytown.js +0 -2
- package/src/components/ui/ProductGallery/usePageProducts.ts +0 -48
- package/src/customizations/components/overrides/ThirdPartyScripts.tsx +0 -9
- package/src/customizations/scripts/ThirdPartyScripts.tsx +0 -8
package/src/pages/[slug]/p.tsx
CHANGED
|
@@ -4,8 +4,10 @@ import type { Locator } from '@vtex/client-cms'
|
|
|
4
4
|
import type { GetStaticPaths, GetStaticProps } from 'next'
|
|
5
5
|
import { BreadcrumbJsonLd, NextSeo, ProductJsonLd } from 'next-seo'
|
|
6
6
|
import type { ComponentType } from 'react'
|
|
7
|
+
import deepmerge from 'deepmerge'
|
|
7
8
|
|
|
8
9
|
import type {
|
|
10
|
+
ClientProductQueryQuery,
|
|
9
11
|
ServerProductPageQueryQuery,
|
|
10
12
|
ServerProductPageQueryQueryVariables,
|
|
11
13
|
} from '@generated/graphql'
|
|
@@ -26,6 +28,8 @@ import GlobalSections, {
|
|
|
26
28
|
getGlobalSectionsData,
|
|
27
29
|
} from 'src/components/cms/GlobalSections'
|
|
28
30
|
import storeConfig from '../../../faststore.config'
|
|
31
|
+
import { useProductQuery } from 'src/sdk/product/useProductQuery'
|
|
32
|
+
import PageProvider, { PDPContext } from 'src/sdk/overrides/PageProvider'
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
35
|
* Sections: Components imported from each store's custom components and '../components/sections' only.
|
|
@@ -39,19 +43,36 @@ const COMPONENTS: Record<string, ComponentType<any>> = {
|
|
|
39
43
|
...CUSTOM_COMPONENTS,
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
type Props =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
46
|
+
type Props = PDPContentType & {
|
|
47
|
+
data: ServerProductPageQueryQuery
|
|
48
|
+
globalSections: GlobalSectionsData
|
|
49
|
+
meta: {
|
|
50
|
+
title: string
|
|
51
|
+
description: string
|
|
52
|
+
canonical: string
|
|
50
53
|
}
|
|
54
|
+
}
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
// Array merging strategy from deepmerge that makes client arrays overwrite server array
|
|
57
|
+
// https://www.npmjs.com/package/deepmerge
|
|
58
|
+
const overwriteMerge = (_, sourceArray) => sourceArray
|
|
59
|
+
|
|
60
|
+
function Page({ data: server, sections, globalSections, offers, meta }: Props) {
|
|
61
|
+
const { product } = server
|
|
53
62
|
const { currency } = useSession()
|
|
54
63
|
|
|
64
|
+
// Stale while revalidate the product for fetching the new price etc
|
|
65
|
+
const { data: client, isValidating } = useProductQuery(product.id, {
|
|
66
|
+
product: product,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const context = {
|
|
70
|
+
data: {
|
|
71
|
+
...deepmerge(server, client, { arrayMerge: overwriteMerge }),
|
|
72
|
+
isValidating,
|
|
73
|
+
},
|
|
74
|
+
} as PDPContext
|
|
75
|
+
|
|
55
76
|
return (
|
|
56
77
|
<GlobalSections {...globalSections}>
|
|
57
78
|
{/* SEO */}
|
|
@@ -105,18 +126,17 @@ function Page({ product, sections, globalSections, offers, meta }: Props) {
|
|
|
105
126
|
If needed, wrap your component in a <Section /> component
|
|
106
127
|
(not the HTML tag) before rendering it here.
|
|
107
128
|
*/}
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
components={COMPONENTS}
|
|
112
|
-
/>
|
|
129
|
+
<PageProvider context={context}>
|
|
130
|
+
<RenderSections sections={sections} components={COMPONENTS} />
|
|
131
|
+
</PageProvider>
|
|
113
132
|
</GlobalSections>
|
|
114
133
|
)
|
|
115
134
|
}
|
|
116
135
|
|
|
117
136
|
const query = gql`
|
|
118
|
-
query ServerProductPageQuery($
|
|
119
|
-
|
|
137
|
+
query ServerProductPageQuery($locator: [IStoreSelectedFacet!]!) {
|
|
138
|
+
...ServerProductPage
|
|
139
|
+
product(locator: $locator) {
|
|
120
140
|
id: productID
|
|
121
141
|
|
|
122
142
|
seo {
|
|
@@ -181,7 +201,7 @@ export const getStaticProps: GetStaticProps<
|
|
|
181
201
|
const slug = params?.slug ?? ''
|
|
182
202
|
const [searchResult, cmsPage, globalSections] = await Promise.all([
|
|
183
203
|
execute<ServerProductPageQueryQueryVariables, ServerProductPageQueryQuery>({
|
|
184
|
-
variables: { slug },
|
|
204
|
+
variables: { locator: [{ key: 'slug', value: slug }] },
|
|
185
205
|
operationName: query,
|
|
186
206
|
}),
|
|
187
207
|
getPage<PDPContentType>({
|
|
@@ -228,7 +248,7 @@ export const getStaticProps: GetStaticProps<
|
|
|
228
248
|
|
|
229
249
|
return {
|
|
230
250
|
props: {
|
|
231
|
-
|
|
251
|
+
data,
|
|
232
252
|
...cmsPage,
|
|
233
253
|
meta,
|
|
234
254
|
offers,
|
package/src/pages/s.tsx
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { SearchState } from '@faststore/sdk'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
formatSearchState,
|
|
4
|
+
parseSearchState,
|
|
5
|
+
SearchProvider,
|
|
6
|
+
} from '@faststore/sdk'
|
|
3
7
|
import { SROnly as UISROnly } from '@faststore/ui'
|
|
4
8
|
import { NextSeo } from 'next-seo'
|
|
5
9
|
import { useRouter } from 'next/router'
|
|
6
|
-
import
|
|
7
|
-
import { useEffect, useState } from 'react'
|
|
10
|
+
import { useMemo } from 'react'
|
|
8
11
|
|
|
9
|
-
import Breadcrumb from 'src/components/sections/Breadcrumb'
|
|
10
|
-
import ProductGallery from 'src/components/sections/ProductGallery'
|
|
11
12
|
import { ITEMS_PER_PAGE } from 'src/constants'
|
|
12
|
-
import CUSTOM_COMPONENTS from 'src/customizations/components'
|
|
13
13
|
import { useApplySearchState } from 'src/sdk/search/state'
|
|
14
14
|
import { mark } from 'src/sdk/tests/mark'
|
|
15
15
|
|
|
@@ -19,19 +19,9 @@ import GlobalSections, {
|
|
|
19
19
|
getGlobalSectionsData,
|
|
20
20
|
GlobalSectionsData,
|
|
21
21
|
} from 'src/components/cms/GlobalSections'
|
|
22
|
-
import RenderSections from 'src/components/cms/RenderSections'
|
|
23
22
|
import { getPage, SearchContentType } from 'src/server/cms'
|
|
24
23
|
import storeConfig from '../../faststore.config'
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Sections: Components imported from each store's custom components and '../components/sections' only.
|
|
28
|
-
* Do not import or render components from any other folder in here.
|
|
29
|
-
*/
|
|
30
|
-
const COMPONENTS: Record<string, ComponentType<any>> = {
|
|
31
|
-
Breadcrumb,
|
|
32
|
-
ProductGallery,
|
|
33
|
-
...CUSTOM_COMPONENTS,
|
|
34
|
-
}
|
|
24
|
+
import SearchPage from 'src/components/templates/SearchPage/SearchPage'
|
|
35
25
|
|
|
36
26
|
type Props = {
|
|
37
27
|
page: SearchContentType
|
|
@@ -48,10 +38,9 @@ type UseSearchParams = {
|
|
|
48
38
|
}
|
|
49
39
|
|
|
50
40
|
const useSearchParams = ({ sort: defaultSort }: UseSearchParams) => {
|
|
51
|
-
const [params, setParams] = useState<SearchState | null>(null)
|
|
52
41
|
const { asPath } = useRouter()
|
|
53
42
|
|
|
54
|
-
|
|
43
|
+
return useMemo(() => {
|
|
55
44
|
const url = new URL(asPath, 'http://localhost')
|
|
56
45
|
|
|
57
46
|
const shouldUpdateDefaultSort = defaultSort && !url.searchParams.has('sort')
|
|
@@ -59,13 +48,14 @@ const useSearchParams = ({ sort: defaultSort }: UseSearchParams) => {
|
|
|
59
48
|
url.searchParams.set('sort', defaultSort)
|
|
60
49
|
}
|
|
61
50
|
|
|
62
|
-
|
|
51
|
+
const newState = parseSearchState(url)
|
|
52
|
+
const hrefState = formatSearchState(newState).href
|
|
53
|
+
return parseSearchState(new URL(hrefState))
|
|
63
54
|
}, [asPath, defaultSort])
|
|
64
|
-
|
|
65
|
-
return params
|
|
66
55
|
}
|
|
67
56
|
|
|
68
|
-
function Page({ page:
|
|
57
|
+
function Page({ page: searchContentType, globalSections }: Props) {
|
|
58
|
+
const { settings } = searchContentType
|
|
69
59
|
const searchParams = useSearchParams({
|
|
70
60
|
sort: settings?.productGallery?.sortBySelection as SearchState['sort'],
|
|
71
61
|
})
|
|
@@ -73,15 +63,22 @@ function Page({ page: { sections, settings }, globalSections }: Props) {
|
|
|
73
63
|
const title = 'Search Results'
|
|
74
64
|
const { description, titleTemplate } = storeConfig.seo
|
|
75
65
|
|
|
66
|
+
const itemsPerPage = settings?.productGallery?.itemsPerPage ?? ITEMS_PER_PAGE
|
|
67
|
+
|
|
76
68
|
if (!searchParams) {
|
|
77
69
|
return null
|
|
78
70
|
}
|
|
79
71
|
|
|
72
|
+
const server = {
|
|
73
|
+
title,
|
|
74
|
+
searchTerm: searchParams.term ?? undefined,
|
|
75
|
+
} as SearchPageContextType
|
|
76
|
+
|
|
80
77
|
return (
|
|
81
78
|
<GlobalSections {...globalSections}>
|
|
82
79
|
<SearchProvider
|
|
83
80
|
onChange={applySearchState}
|
|
84
|
-
itemsPerPage={
|
|
81
|
+
itemsPerPage={itemsPerPage}
|
|
85
82
|
{...searchParams}
|
|
86
83
|
>
|
|
87
84
|
{/* SEO */}
|
|
@@ -110,16 +107,7 @@ function Page({ page: { sections, settings }, globalSections }: Props) {
|
|
|
110
107
|
If needed, wrap your component in a <Section /> component
|
|
111
108
|
(not the HTML tag) before rendering it here.
|
|
112
109
|
*/}
|
|
113
|
-
<
|
|
114
|
-
sections={sections}
|
|
115
|
-
components={COMPONENTS}
|
|
116
|
-
context={
|
|
117
|
-
{
|
|
118
|
-
title,
|
|
119
|
-
searchTerm: searchParams.term ?? undefined,
|
|
120
|
-
} as SearchPageContextType
|
|
121
|
-
}
|
|
122
|
-
/>
|
|
110
|
+
<SearchPage page={searchContentType} data={server}></SearchPage>
|
|
123
111
|
</SearchProvider>
|
|
124
112
|
</GlobalSections>
|
|
125
113
|
)
|
|
@@ -4,7 +4,8 @@ import type { SWRConfiguration } from 'swr'
|
|
|
4
4
|
import { request } from './request'
|
|
5
5
|
import type { RequestOptions } from './request'
|
|
6
6
|
|
|
7
|
-
export type QueryOptions = SWRConfiguration &
|
|
7
|
+
export type QueryOptions = SWRConfiguration &
|
|
8
|
+
RequestOptions & { doNotRun?: boolean }
|
|
8
9
|
|
|
9
10
|
export const getKey = <Variables>(
|
|
10
11
|
operationName: string,
|
|
@@ -25,16 +26,19 @@ export const useQuery = <Data, Variables = Record<string, unknown>>(
|
|
|
25
26
|
variables: Variables,
|
|
26
27
|
options?: QueryOptions
|
|
27
28
|
) =>
|
|
28
|
-
useSWR<Data>(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
useSWR<Data>(
|
|
30
|
+
() => (options?.doNotRun ? null : getKey(operationName, variables)),
|
|
31
|
+
{
|
|
32
|
+
fetcher: () => {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
setTimeout(async () => {
|
|
35
|
+
resolve(
|
|
36
|
+
await request<Data, Variables>(operationName, variables, options)
|
|
37
|
+
)
|
|
38
|
+
})
|
|
35
39
|
})
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
},
|
|
41
|
+
...DEFAULT_OPTIONS,
|
|
42
|
+
...options,
|
|
43
|
+
}
|
|
44
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientProductGalleryQueryQuery,
|
|
3
|
+
ClientProductQueryQuery,
|
|
4
|
+
ClientProductsQueryQuery,
|
|
5
|
+
ServerCollectionPageQueryQuery,
|
|
6
|
+
ServerProductPageQueryQuery,
|
|
7
|
+
} from '@generated/graphql'
|
|
8
|
+
import type { PropsWithChildren } from 'react'
|
|
9
|
+
import { createContext, useContext, useMemo } from 'react'
|
|
10
|
+
import { SearchPageContextType } from 'src/pages/s'
|
|
11
|
+
|
|
12
|
+
export interface PDPContext {
|
|
13
|
+
data?: ServerProductPageQueryQuery &
|
|
14
|
+
ClientProductQueryQuery['product'] & { isValidating?: boolean }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PLPContext {
|
|
18
|
+
data?: ServerCollectionPageQueryQuery &
|
|
19
|
+
ClientProductGalleryQueryQuery & { pages: ClientProductsQueryQuery[] }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface SearchPageContext {
|
|
23
|
+
data?: SearchPageContextType &
|
|
24
|
+
ClientProductGalleryQueryQuery & { pages: ClientProductsQueryQuery[] }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const isPDP = (x: any): x is PDPContext =>
|
|
28
|
+
x?.data?.product?.sku != undefined && x?.data?.product?.sku != null
|
|
29
|
+
|
|
30
|
+
export const isPLP = (x: any): x is PLPContext =>
|
|
31
|
+
x?.data?.collection?.seo != undefined &&
|
|
32
|
+
x?.data?.collection?.seo != null &&
|
|
33
|
+
x?.data?.collection?.sku == undefined
|
|
34
|
+
|
|
35
|
+
export const isSearchPage = (x: any): x is SearchPageContext =>
|
|
36
|
+
x === undefined ||
|
|
37
|
+
x?.data?.title != undefined ||
|
|
38
|
+
x?.data?.searchTerm != undefined
|
|
39
|
+
|
|
40
|
+
export interface PageProviderContextValue {
|
|
41
|
+
context?: PDPContext | PLPContext | SearchPageContext
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const PageContext = createContext<PageProviderContextValue | null>(null)
|
|
45
|
+
|
|
46
|
+
function PageProvider({
|
|
47
|
+
context,
|
|
48
|
+
children,
|
|
49
|
+
}: PropsWithChildren<PageProviderContextValue>) {
|
|
50
|
+
const value = useMemo(
|
|
51
|
+
() => ({
|
|
52
|
+
context,
|
|
53
|
+
}),
|
|
54
|
+
[context]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return <PageContext.Provider value={value}>{children}</PageContext.Provider>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function usePage<
|
|
61
|
+
T extends PLPContext | SearchPageContext | PDPContext
|
|
62
|
+
>(): T {
|
|
63
|
+
const { context } = useContext(PageContext)
|
|
64
|
+
|
|
65
|
+
if (context == null) {
|
|
66
|
+
throw new Error('Missing Overrides context on React tree')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return context as T
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const usePDP = () => usePage<PDPContext>()
|
|
73
|
+
|
|
74
|
+
export const usePLP = () => usePage<PLPContext>()
|
|
75
|
+
|
|
76
|
+
export const useSearchPage = () => usePage<SearchPageContext>()
|
|
77
|
+
|
|
78
|
+
export default PageProvider
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { ClientProductsQueryQueryVariables } from '@generated/graphql'
|
|
3
|
+
import { useSession } from '../session'
|
|
4
|
+
import { ITEMS_PER_SECTION } from 'src/constants'
|
|
5
|
+
|
|
6
|
+
const toArray = <T>(x: T[] | T | undefined) =>
|
|
7
|
+
Array.isArray(x) ? x : x ? [x] : []
|
|
8
|
+
|
|
9
|
+
export const useLocalizedVariables = ({
|
|
10
|
+
first,
|
|
11
|
+
after,
|
|
12
|
+
sort,
|
|
13
|
+
term,
|
|
14
|
+
selectedFacets,
|
|
15
|
+
}: Partial<ClientProductsQueryQueryVariables>) => {
|
|
16
|
+
const { channel, locale } = useSession()
|
|
17
|
+
|
|
18
|
+
return useMemo(() => {
|
|
19
|
+
const facets = toArray(selectedFacets)
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
first: first ?? ITEMS_PER_SECTION,
|
|
23
|
+
after: after ?? '0',
|
|
24
|
+
sort: sort ?? ('score_desc' as const),
|
|
25
|
+
term: term ?? '',
|
|
26
|
+
selectedFacets: [
|
|
27
|
+
...facets,
|
|
28
|
+
{ key: 'channel', value: channel ?? '' },
|
|
29
|
+
{ key: 'locale', value: locale },
|
|
30
|
+
],
|
|
31
|
+
}
|
|
32
|
+
}, [selectedFacets, first, after, sort, term, channel, locale])
|
|
33
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { gql } from '@faststore/graphql-utils'
|
|
2
|
+
import { useSearch } from '@faststore/sdk'
|
|
3
|
+
import {
|
|
4
|
+
ClientProductsQueryQuery,
|
|
5
|
+
ClientProductsQueryQueryVariables,
|
|
6
|
+
} from '@generated/graphql'
|
|
7
|
+
import {
|
|
8
|
+
useEffect,
|
|
9
|
+
useCallback,
|
|
10
|
+
createContext,
|
|
11
|
+
useContext,
|
|
12
|
+
useRef,
|
|
13
|
+
useMemo,
|
|
14
|
+
useState,
|
|
15
|
+
} from 'react'
|
|
16
|
+
import { useQuery } from 'src/sdk/graphql/useQuery'
|
|
17
|
+
import { useLocalizedVariables } from './useLocalizedVariables'
|
|
18
|
+
|
|
19
|
+
export const UseGalleryPageContext = createContext<
|
|
20
|
+
ReturnType<typeof useCreateUseGalleryPage>['useGalleryPage']
|
|
21
|
+
>((_: number) => {
|
|
22
|
+
return { data: null }
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export const useGalleryPage = (page: number) => {
|
|
26
|
+
const useGalleryPageCallback = useContext(UseGalleryPageContext)
|
|
27
|
+
if (!useGalleryPageCallback) {
|
|
28
|
+
throw new Error('Missing UseGalleryPageContext on React tree')
|
|
29
|
+
}
|
|
30
|
+
return useGalleryPageCallback(page)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const query = gql`
|
|
34
|
+
query ClientProductsQuery(
|
|
35
|
+
$first: Int!
|
|
36
|
+
$after: String
|
|
37
|
+
$sort: StoreSort!
|
|
38
|
+
$term: String!
|
|
39
|
+
$selectedFacets: [IStoreSelectedFacet!]!
|
|
40
|
+
) {
|
|
41
|
+
...ClientProducts
|
|
42
|
+
search(
|
|
43
|
+
first: $first
|
|
44
|
+
after: $after
|
|
45
|
+
sort: $sort
|
|
46
|
+
term: $term
|
|
47
|
+
selectedFacets: $selectedFacets
|
|
48
|
+
) {
|
|
49
|
+
products {
|
|
50
|
+
pageInfo {
|
|
51
|
+
totalCount
|
|
52
|
+
}
|
|
53
|
+
edges {
|
|
54
|
+
node {
|
|
55
|
+
...ProductSummary_product
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`
|
|
62
|
+
|
|
63
|
+
const getKey = (object: any) => JSON.stringify(object)
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Use this hook for managed pages state and creating useGalleryPage hook that will be used for fetching a list of products per pages in PLP or Search
|
|
67
|
+
*/
|
|
68
|
+
export const useCreateUseGalleryPage = () => {
|
|
69
|
+
const [pages, setPages] = useState<ClientProductsQueryQuery[]>([])
|
|
70
|
+
// We create pagesRef as a mirror of the pages state so we don't have to add pages as a dependency of the useGalleryPage hook
|
|
71
|
+
const pagesRef = useRef<ClientProductsQueryQuery[]>([])
|
|
72
|
+
const pagesCache = useRef<string[]>([])
|
|
73
|
+
|
|
74
|
+
const useGalleryPage = useCallback(function useGalleryPage(page: number) {
|
|
75
|
+
const {
|
|
76
|
+
state: { sort, term, selectedFacets },
|
|
77
|
+
itemsPerPage,
|
|
78
|
+
} = useSearch()
|
|
79
|
+
|
|
80
|
+
const localizedVariables = useLocalizedVariables({
|
|
81
|
+
first: itemsPerPage,
|
|
82
|
+
after: (itemsPerPage * page).toString(),
|
|
83
|
+
sort,
|
|
84
|
+
term: term ?? '',
|
|
85
|
+
selectedFacets,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const hasSameVariables =
|
|
89
|
+
pagesCache.current[page] === getKey(localizedVariables)
|
|
90
|
+
|
|
91
|
+
const { data } = useQuery<
|
|
92
|
+
ClientProductsQueryQuery,
|
|
93
|
+
ClientProductsQueryQueryVariables
|
|
94
|
+
>(query, localizedVariables, {
|
|
95
|
+
fallbackData: null,
|
|
96
|
+
suspense: true,
|
|
97
|
+
doNotRun: hasSameVariables,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const shouldUpdatePages = !hasSameVariables && data !== null
|
|
101
|
+
|
|
102
|
+
if (shouldUpdatePages) {
|
|
103
|
+
pagesCache.current[page] = getKey(localizedVariables)
|
|
104
|
+
|
|
105
|
+
// Update refs
|
|
106
|
+
const newPages = [...pagesRef.current]
|
|
107
|
+
newPages[page] = data
|
|
108
|
+
pagesRef.current = newPages
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Prevents error: Cannot update a component (`ProductListing`) while rendering a different component (`ProductGalleryPage`).
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (shouldUpdatePages) {
|
|
114
|
+
// Update state
|
|
115
|
+
setPages((oldPages) => {
|
|
116
|
+
const newPages = [...oldPages]
|
|
117
|
+
newPages[page] = data
|
|
118
|
+
return newPages
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}, [data, page, shouldUpdatePages])
|
|
122
|
+
|
|
123
|
+
return useMemo(() => {
|
|
124
|
+
if (hasSameVariables) {
|
|
125
|
+
return { data: pagesRef.current[page] }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { data }
|
|
129
|
+
}, [hasSameVariables, data, page])
|
|
130
|
+
}, [])
|
|
131
|
+
|
|
132
|
+
return useMemo(
|
|
133
|
+
() => ({
|
|
134
|
+
pages,
|
|
135
|
+
useGalleryPage,
|
|
136
|
+
}),
|
|
137
|
+
[pages, useGalleryPage]
|
|
138
|
+
)
|
|
139
|
+
}
|
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
import { useSearch } from '@faststore/sdk'
|
|
2
1
|
import { gql } from '@faststore/graphql-utils'
|
|
3
2
|
|
|
4
|
-
import { useQuery } from 'src/sdk/graphql/useQuery'
|
|
5
3
|
import type {
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
ClientProductGalleryQueryQuery as Query,
|
|
5
|
+
ClientProductGalleryQueryQueryVariables as Variables,
|
|
8
6
|
} from '@generated/graphql'
|
|
9
|
-
|
|
10
|
-
import { useLocalizedVariables } from '
|
|
7
|
+
import { useQuery } from 'src/sdk/graphql/useQuery'
|
|
8
|
+
import { useLocalizedVariables } from './useLocalizedVariables'
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* This query is run on the browser and contains
|
|
14
12
|
* the current search state of the user
|
|
15
13
|
*/
|
|
16
14
|
export const query = gql`
|
|
17
|
-
query
|
|
15
|
+
query ClientProductGalleryQuery(
|
|
18
16
|
$first: Int!
|
|
19
17
|
$after: String!
|
|
20
18
|
$sort: StoreSort!
|
|
21
19
|
$term: String!
|
|
22
20
|
$selectedFacets: [IStoreSelectedFacet!]!
|
|
23
21
|
) {
|
|
22
|
+
...ClientProductGallery
|
|
24
23
|
search(
|
|
25
24
|
first: $first
|
|
26
25
|
after: $after
|
|
@@ -40,12 +39,12 @@ export const query = gql`
|
|
|
40
39
|
}
|
|
41
40
|
`
|
|
42
41
|
|
|
43
|
-
export const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
export const useProductGalleryQuery = ({
|
|
43
|
+
term,
|
|
44
|
+
sort,
|
|
45
|
+
selectedFacets,
|
|
46
|
+
itemsPerPage,
|
|
47
|
+
}) => {
|
|
49
48
|
const localizedVariables = useLocalizedVariables({
|
|
50
49
|
first: itemsPerPage,
|
|
51
50
|
after: '0',
|
|
@@ -2,29 +2,32 @@ import { gql } from '@faststore/graphql-utils'
|
|
|
2
2
|
import { useMemo } from 'react'
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
ClientProductQueryQuery,
|
|
6
|
+
ClientProductQueryQueryVariables,
|
|
7
7
|
} from '@generated/graphql'
|
|
8
8
|
|
|
9
9
|
import { useQuery } from '../graphql/useQuery'
|
|
10
10
|
import { useSession } from '../session'
|
|
11
11
|
|
|
12
12
|
const query = gql`
|
|
13
|
-
query
|
|
13
|
+
query ClientProductQuery($locator: [IStoreSelectedFacet!]!) {
|
|
14
|
+
...ClientProduct
|
|
14
15
|
product(locator: $locator) {
|
|
15
16
|
...ProductDetailsFragment_product
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
`
|
|
19
20
|
|
|
20
|
-
export const
|
|
21
|
+
export const useProductQuery = <T extends ClientProductQueryQuery>(
|
|
21
22
|
productID: string,
|
|
22
23
|
fallbackData?: T
|
|
23
24
|
) => {
|
|
24
25
|
const { channel, locale } = useSession()
|
|
25
26
|
const variables = useMemo(() => {
|
|
26
27
|
if (!channel) {
|
|
27
|
-
throw new Error(
|
|
28
|
+
throw new Error(
|
|
29
|
+
`useProductQuery: 'channel' from session is an empty string.`
|
|
30
|
+
)
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
return {
|
|
@@ -37,8 +40,8 @@ export const useProduct = <T extends BrowserProductQueryQuery>(
|
|
|
37
40
|
}, [channel, locale, productID])
|
|
38
41
|
|
|
39
42
|
return useQuery<
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
ClientProductQueryQuery & T,
|
|
44
|
+
ClientProductQueryQueryVariables
|
|
42
45
|
>(query, variables, {
|
|
43
46
|
fallbackData,
|
|
44
47
|
revalidateOnMount: true,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { gql } from '@faststore/graphql-utils'
|
|
2
|
+
import { useSearch } from '@faststore/sdk'
|
|
3
|
+
import { ClientProductsQueryQueryVariables } from '@generated/graphql'
|
|
4
|
+
import { useEffect, useCallback } from 'react'
|
|
5
|
+
import type { QueryOptions } from '../graphql/useQuery'
|
|
6
|
+
import { useSWRConfig } from 'swr'
|
|
7
|
+
import { prefetchQuery } from '../graphql/prefetchQuery'
|
|
8
|
+
import { useLocalizedVariables } from './useLocalizedVariables'
|
|
9
|
+
|
|
10
|
+
export const query = gql`
|
|
11
|
+
query ClientProductsQuery(
|
|
12
|
+
$first: Int!
|
|
13
|
+
$after: String
|
|
14
|
+
$sort: StoreSort!
|
|
15
|
+
$term: String!
|
|
16
|
+
$selectedFacets: [IStoreSelectedFacet!]!
|
|
17
|
+
) {
|
|
18
|
+
...ClientProducts
|
|
19
|
+
search(
|
|
20
|
+
first: $first
|
|
21
|
+
after: $after
|
|
22
|
+
sort: $sort
|
|
23
|
+
term: $term
|
|
24
|
+
selectedFacets: $selectedFacets
|
|
25
|
+
) {
|
|
26
|
+
products {
|
|
27
|
+
pageInfo {
|
|
28
|
+
totalCount
|
|
29
|
+
}
|
|
30
|
+
edges {
|
|
31
|
+
node {
|
|
32
|
+
...ProductSummary_product
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`
|
|
39
|
+
|
|
40
|
+
export const useProductsQueryPrefetch = (
|
|
41
|
+
variables: ClientProductsQueryQueryVariables,
|
|
42
|
+
options?: QueryOptions
|
|
43
|
+
) => {
|
|
44
|
+
const localizedVariables = useLocalizedVariables(variables)
|
|
45
|
+
const { cache } = useSWRConfig()
|
|
46
|
+
|
|
47
|
+
return useCallback(
|
|
48
|
+
() => prefetchQuery(query, localizedVariables, { cache, ...options }),
|
|
49
|
+
[localizedVariables, cache, options]
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const useProductsPrefetch = (page: number | null) => {
|
|
54
|
+
const {
|
|
55
|
+
itemsPerPage,
|
|
56
|
+
state: { sort, term, selectedFacets },
|
|
57
|
+
} = useSearch()
|
|
58
|
+
|
|
59
|
+
const prefetch = useProductsQueryPrefetch({
|
|
60
|
+
first: itemsPerPage,
|
|
61
|
+
after: (itemsPerPage * (page ?? 0)).toString(),
|
|
62
|
+
sort,
|
|
63
|
+
term: term ?? '',
|
|
64
|
+
selectedFacets,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (page !== null) {
|
|
69
|
+
prefetch()
|
|
70
|
+
}
|
|
71
|
+
}, [page, prefetch])
|
|
72
|
+
}
|