@faststore/core 2.2.0-alpha.1 → 2.2.0-alpha.12

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.
Files changed (188) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +74 -75
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/config.json +3 -3
  5. package/.next/cache/eslint/.cache_1gneedd +1 -1
  6. package/.next/cache/next-server.js.nft.json +1 -1
  7. package/.next/cache/webpack/client-production/0.pack +0 -0
  8. package/.next/cache/webpack/client-production/index.pack +0 -0
  9. package/.next/cache/webpack/server-production/0.pack +0 -0
  10. package/.next/cache/webpack/server-production/index.pack +0 -0
  11. package/.next/next-server.js.nft.json +1 -1
  12. package/.next/prerender-manifest.json +1 -1
  13. package/.next/react-loadable-manifest.json +11 -8
  14. package/.next/required-server-files.json +1 -1
  15. package/.next/routes-manifest.json +1 -1
  16. package/.next/server/chunks/{647.js → 117.js} +32 -35
  17. package/.next/server/chunks/183.js +1 -0
  18. package/.next/server/chunks/289.js +25 -27
  19. package/.next/server/chunks/312.js +53 -72
  20. package/.next/server/chunks/350.js +8 -8
  21. package/.next/server/chunks/{483.js → 37.js} +121 -162
  22. package/.next/server/chunks/386.js +200 -0
  23. package/.next/server/chunks/{753.js → 387.js} +8 -12
  24. package/.next/server/chunks/574.js +42 -23
  25. package/.next/server/chunks/576.js +1 -0
  26. package/.next/server/chunks/{530.js → 585.js} +65 -51
  27. package/.next/server/chunks/{186.js → 692.js} +6 -8
  28. package/.next/server/chunks/{112.js → 732.js} +282 -457
  29. package/.next/server/chunks/74.js +247 -345
  30. package/.next/server/chunks/825.js +38 -3
  31. package/.next/server/chunks/{71.js → 897.js} +174 -181
  32. package/.next/server/chunks/98.js +42 -23
  33. package/.next/server/middleware-build-manifest.js +1 -1
  34. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  35. package/.next/server/pages/404.js +13 -13
  36. package/.next/server/pages/404.js.nft.json +1 -1
  37. package/.next/server/pages/500.js +14 -14
  38. package/.next/server/pages/500.js.nft.json +1 -1
  39. package/.next/server/pages/[...slug].js +141 -184
  40. package/.next/server/pages/[...slug].js.nft.json +1 -1
  41. package/.next/server/pages/[slug]/p.js +242 -219
  42. package/.next/server/pages/[slug]/p.js.nft.json +1 -1
  43. package/.next/server/pages/_app.js +3 -4
  44. package/.next/server/pages/_app.js.nft.json +1 -1
  45. package/.next/server/pages/_document.js +17 -33
  46. package/.next/server/pages/_document.js.nft.json +1 -1
  47. package/.next/server/pages/account.js +12 -12
  48. package/.next/server/pages/account.js.nft.json +1 -1
  49. package/.next/server/pages/api/graphql.js +15 -8
  50. package/.next/server/pages/api/graphql.js.nft.json +1 -1
  51. package/.next/server/pages/api/preview.js +2 -20
  52. package/.next/server/pages/checkout.js +12 -12
  53. package/.next/server/pages/checkout.js.nft.json +1 -1
  54. package/.next/server/pages/en-US/404.html +2 -2
  55. package/.next/server/pages/en-US/500.html +2 -2
  56. package/.next/server/pages/en-US/account.html +2 -2
  57. package/.next/server/pages/en-US/checkout.html +2 -2
  58. package/.next/server/pages/en-US/login.html +2 -2
  59. package/.next/server/pages/en-US/s.html +2 -2
  60. package/.next/server/pages/en-US.html +11 -2
  61. package/.next/server/pages/index.js +41 -48
  62. package/.next/server/pages/index.js.nft.json +1 -1
  63. package/.next/server/pages/login.js +15 -15
  64. package/.next/server/pages/login.js.nft.json +1 -1
  65. package/.next/server/pages/s.js +52 -52
  66. package/.next/server/pages/s.js.nft.json +1 -1
  67. package/.next/server/pages-manifest.json +2 -2
  68. package/.next/static/6R1hrzilfMwT81RW6cNqt/_buildManifest.js +1 -0
  69. package/.next/static/chunks/251.ffc4f3998ecb915a.js +1 -0
  70. package/.next/static/chunks/383-6217b37bd38ffd07.js +1 -0
  71. package/.next/static/chunks/386.d01e0db26c523f0f.js +1 -0
  72. package/.next/static/chunks/{574.d13dd0afe15cd635.js → 574.70612be06fd1365f.js} +1 -1
  73. package/.next/static/chunks/585.3350efefe61c9461.js +1 -0
  74. package/.next/static/chunks/635-666ee2cad2925bb7.js +1 -0
  75. package/.next/static/chunks/721-f1665b3f1d98b7a9.js +1 -0
  76. package/.next/static/chunks/722-d0cc87aacd616b5d.js +1 -0
  77. package/.next/static/chunks/783-ded9d8cda0d5c8d9.js +1 -0
  78. package/.next/static/chunks/{709.daf1eddebf1e7952.js → 800.851af48fe2ab4a4c.js} +1 -1
  79. package/.next/static/chunks/98.40c7e17d9de4eb8f.js +1 -0
  80. package/.next/static/chunks/988.afda042dd9ba11d1.js +1 -0
  81. package/.next/static/chunks/main-e4e873ee741162eb.js +1 -0
  82. package/.next/static/chunks/pages/{404-af78f7cd1d3c1f60.js → 404-6c674028b2f80cbb.js} +1 -1
  83. package/.next/static/chunks/pages/{500-f6346ca5f9dc4fef.js → 500-ff55de77265a7e43.js} +1 -1
  84. package/.next/static/chunks/pages/[...slug]-7f8dc13cc9542463.js +1 -0
  85. package/.next/static/chunks/pages/[slug]/p-2fb3fd9027f2923b.js +1 -0
  86. package/.next/static/chunks/pages/{_app-6d0e6ab9a4dd8106.js → _app-7db7de3d205714be.js} +1 -1
  87. package/.next/static/chunks/pages/{account-05bd79fb78365e88.js → account-b06035cba2c99604.js} +1 -1
  88. package/.next/static/chunks/pages/{checkout-c973786e68f25a39.js → checkout-f0c3e8d691cb8a54.js} +1 -1
  89. package/.next/static/chunks/pages/index-a141c747fcc197a1.js +1 -0
  90. package/.next/static/chunks/pages/{login-8deb9243376b6aa1.js → login-4e0e6cab7a07f1f3.js} +1 -1
  91. package/.next/static/chunks/pages/s-823f8e1cabbf63b3.js +1 -0
  92. package/.next/static/chunks/webpack-f3d0973d5a781e73.js +1 -0
  93. package/.next/static/css/6a7fdc5a21fbead5.css +1 -0
  94. package/.next/static/css/723835bce380750d.css +1 -0
  95. package/.next/static/css/8f93a4630936c20b.css +1 -0
  96. package/.next/static/css/9f79fa103f49bca1.css +1 -0
  97. package/.next/static/css/fd27ecc37832aa54.css +1 -0
  98. package/.next/trace +77 -80
  99. package/.turbo/turbo-build.log +24 -21
  100. package/.turbo/turbo-lint.log +3 -0
  101. package/.turbo/turbo-test.log +10 -10
  102. package/@generated/graphql/index.ts +38 -14
  103. package/@generated/graphql/persisted.json +6 -6
  104. package/README.md +3 -3
  105. package/cms/faststore/sections.json +24 -0
  106. package/cypress/global.js +8 -0
  107. package/cypress/integration/a11y.test.js +7 -24
  108. package/cypress/integration/analytics.test.js +78 -87
  109. package/cypress/integration/cart.test.js +4 -4
  110. package/cypress/integration/plp.test.js +6 -6
  111. package/cypress/integration/search.test.js +1 -1
  112. package/cypress/integration/seo.test.js +14 -14
  113. package/cypress.config.ts +19 -0
  114. package/faststore.config.js +1 -0
  115. package/index.ts +4 -0
  116. package/next.config.js +4 -0
  117. package/package.json +22 -18
  118. package/src/components/cart/CartSidebar/CartSidebar.tsx +41 -27
  119. package/src/components/product/NotAvailableButton/NotAvailableButton.tsx +13 -0
  120. package/src/components/product/NotAvailableButton/index.ts +1 -0
  121. package/src/components/product/ProductGrid/ProductGrid.tsx +2 -2
  122. package/src/components/search/Filter/Filter.tsx +1 -1
  123. package/src/components/search/Filter/FilterDesktop.tsx +1 -1
  124. package/src/components/search/Filter/FilterSlider.tsx +1 -1
  125. package/src/components/search/SearchInput/SearchInput.tsx +12 -1
  126. package/src/components/search/SearchTop/SearchTop.tsx +7 -1
  127. package/src/components/sections/CrossSellingShelf/CrossSellingShelf.tsx +0 -1
  128. package/src/components/sections/Newsletter/Overrides.tsx +4 -0
  129. package/src/components/sections/ProductDetails/Overrides.tsx +8 -4
  130. package/src/components/sections/ProductDetails/ProductDetails.tsx +26 -15
  131. package/src/components/sections/ProductTiles/ProductTiles.tsx +3 -2
  132. package/src/components/ui/ImageGallery/ImageGallery.tsx +13 -6
  133. package/src/components/ui/Newsletter/Newsletter.tsx +6 -44
  134. package/src/components/ui/Newsletter/NewsletterAddendum.tsx +86 -0
  135. package/src/components/ui/ProductDescription/ProductDescription.tsx +6 -1
  136. package/src/components/ui/ProductDetails/ProductDetailsSettings.tsx +97 -58
  137. package/src/components/ui/ProductGallery/ProductGallery.tsx +2 -2
  138. package/src/components/ui/ShippingSimulation/ShippingSimulation.tsx +12 -34
  139. package/src/customizations/fragments/{ClientProducts.ts → ClientManyProducts.ts} +1 -1
  140. package/src/customizations/fragments/ClientSearchSuggestions.ts +13 -0
  141. package/src/customizations/fragments/ClientShippingSimulation.ts +11 -0
  142. package/src/customizations/fragments/ClientTopSearchSuggestions.ts +13 -0
  143. package/src/customizations/fragments/{ServerProductPage.ts → ServerProduct.ts} +1 -1
  144. package/src/experimental/index.ts +36 -0
  145. package/src/pages/[...slug].tsx +5 -2
  146. package/src/pages/[slug]/p.tsx +7 -7
  147. package/src/sdk/analytics/index.tsx +1 -1
  148. package/src/sdk/analytics/platform/vtex/search.ts +53 -21
  149. package/src/sdk/analytics/types.ts +14 -0
  150. package/src/sdk/overrides/PageProvider.tsx +9 -5
  151. package/src/sdk/product/useLocalizedVariables.ts +2 -2
  152. package/src/sdk/product/usePageProductsQuery.ts +8 -8
  153. package/src/sdk/product/useProductGalleryQuery.ts +32 -3
  154. package/src/sdk/product/useProductsPrefetch.ts +4 -4
  155. package/src/sdk/product/useProductsQuery.ts +7 -7
  156. package/src/sdk/search/useSuggestions.ts +48 -16
  157. package/src/sdk/search/useTopSearch.ts +7 -12
  158. package/src/sdk/shipping/index.ts +6 -4
  159. package/src/{components/ui/ShippingSimulation → sdk/shipping}/useShippingSimulation.ts +33 -56
  160. package/src/server/generator/schema.ts +2 -1
  161. package/src/typings/overrides.ts +8 -2
  162. package/tsconfig.json +2 -1
  163. package/.next/static/chunks/148.3bb7e05cc5d1c1c4.js +0 -1
  164. package/.next/static/chunks/238-86838f629f3d0aa4.js +0 -1
  165. package/.next/static/chunks/243-ef9e49ef3df579c0.js +0 -1
  166. package/.next/static/chunks/530.848b014622932b93.js +0 -1
  167. package/.next/static/chunks/548-ab84e9e8b49413ab.js +0 -1
  168. package/.next/static/chunks/738-67a288ca3569cdbb.js +0 -1
  169. package/.next/static/chunks/932-33f45603c7d12a4b.js +0 -1
  170. package/.next/static/chunks/98.1632997dfd4d3a52.js +0 -1
  171. package/.next/static/chunks/988.d10040040cdfebbb.js +0 -1
  172. package/.next/static/chunks/main-fd466221927468fd.js +0 -1
  173. package/.next/static/chunks/pages/[...slug]-362204c7e0b533cf.js +0 -1
  174. package/.next/static/chunks/pages/[slug]/p-885042c4b2b4f8ed.js +0 -1
  175. package/.next/static/chunks/pages/index-79b05b0071c02fff.js +0 -1
  176. package/.next/static/chunks/pages/s-e1bb00f8de6a386e.js +0 -1
  177. package/.next/static/chunks/webpack-88d120cefa1c4c09.js +0 -1
  178. package/.next/static/css/20e4a3a45cdd65f4.css +0 -1
  179. package/.next/static/css/4b7138899cd07c63.css +0 -1
  180. package/.next/static/css/e3b039e8f5daf95f.css +0 -1
  181. package/.next/static/css/f0e2d1b8832e935d.css +0 -1
  182. package/.next/static/wPqgZ8bYrPY473PZzuIX_/_buildManifest.js +0 -1
  183. package/cypress.json +0 -9
  184. package/generate.sh +0 -71
  185. /package/.next/static/{wPqgZ8bYrPY473PZzuIX_ → 6R1hrzilfMwT81RW6cNqt}/_ssgManifest.js +0 -0
  186. /package/src/{components/ui/ProductGallery → sdk/search}/useDelayedFacets.ts +0 -0
  187. /package/src/{components/ui/ProductGallery → sdk/search}/useDelayedPagination.ts +0 -0
  188. /package/src/{components/search/Filter → sdk/search}/useFilter.ts +0 -0
@@ -1,4 +1,5 @@
1
1
  import type { Dispatch, SetStateAction } from 'react'
2
+ import { useMemo } from 'react'
2
3
 
3
4
  import type { ProductDetailsFragment_ProductFragment } from '@generated/graphql'
4
5
 
@@ -13,6 +14,7 @@ import {
13
14
  Icon,
14
15
  Price,
15
16
  QuantitySelector,
17
+ __experimentalNotAvailableButton as NotAvailableButton,
16
18
  } from 'src/components/sections/ProductDetails/Overrides'
17
19
 
18
20
  interface ProductDetailsSettingsProps {
@@ -25,6 +27,7 @@ interface ProductDetailsSettingsProps {
25
27
  isValidating: boolean
26
28
  quantity: number
27
29
  setQuantity: Dispatch<SetStateAction<number>>
30
+ notAvailableButtonTitle: string
28
31
  }
29
32
 
30
33
  function ProductDetailsSettings({
@@ -37,6 +40,7 @@ function ProductDetailsSettings({
37
40
  icon: buyButtonIconName = Icon.props.name,
38
41
  alt: buyButtonIconAlt = Icon.props['aria-label'],
39
42
  },
43
+ notAvailableButtonTitle,
40
44
  }: ProductDetailsSettingsProps) {
41
45
  const {
42
46
  id,
@@ -54,8 +58,6 @@ function ProductDetailsSettings({
54
58
  },
55
59
  } = product
56
60
 
57
- const buyDisabled = availability !== 'https://schema.org/InStock'
58
-
59
61
  const buyProps = useBuyButton({
60
62
  id,
61
63
  price,
@@ -73,43 +75,94 @@ function ProductDetailsSettings({
73
75
  },
74
76
  })
75
77
 
78
+ const outOfStock = useMemo(
79
+ () => availability === 'https://schema.org/OutOfStock',
80
+ [availability]
81
+ )
82
+ const shouldShowDiscountedPrice = useMemo(
83
+ () => lowPrice !== listPrice,
84
+ [lowPrice, listPrice]
85
+ )
86
+
87
+ const AddToCartButton = () => {
88
+ return outOfStock ? (
89
+ // TODO: Adds <OutOfStock /> when component is ready to use
90
+ <NotAvailableButton.Component>
91
+ {notAvailableButtonTitle}
92
+ </NotAvailableButton.Component>
93
+ ) : (
94
+ <BuyButton.Component
95
+ {...BuyButton.props}
96
+ icon={
97
+ <Icon.Component
98
+ {...Icon.props}
99
+ name={buyButtonIconName}
100
+ aria-label={buyButtonIconAlt}
101
+ />
102
+ }
103
+ {...buyProps}
104
+ >
105
+ {buyButtonTitle || 'Add to Cart'}
106
+ </BuyButton.Component>
107
+ )
108
+ }
109
+
76
110
  return (
77
111
  <>
78
- <section data-fs-product-details-values>
79
- <div data-fs-product-details-prices>
80
- <Price.Component
81
- formatter={useFormattedPrice}
82
- testId="list-price"
83
- variant="listing"
84
- SRText="Original price:"
85
- {...Price.props}
86
- // Dynamic props shouldn't be overridable
87
- // This decision can be reviewed later if needed
88
- value={listPrice}
89
- data-value={listPrice}
90
- />
91
- <Price.Component
92
- formatter={useFormattedPrice}
93
- testId="price"
94
- variant="spot"
95
- className="text__lead"
96
- SRText="Sale Price:"
97
- {...Price.props}
112
+ {!outOfStock && (
113
+ <section data-fs-product-details-values>
114
+ <div data-fs-product-details-prices>
115
+ {shouldShowDiscountedPrice ? (
116
+ <>
117
+ <Price.Component
118
+ formatter={useFormattedPrice}
119
+ testId="list-price"
120
+ variant="listing"
121
+ SRText="Original price:"
122
+ {...Price.props}
123
+ // Dynamic props shouldn't be overridable
124
+ // This decision can be reviewed later if needed
125
+ value={listPrice}
126
+ data-value={listPrice}
127
+ />
128
+ <Price.Component
129
+ formatter={useFormattedPrice}
130
+ testId="price"
131
+ variant="spot"
132
+ className="text__lead"
133
+ SRText="Sale Price:"
134
+ {...Price.props}
135
+ // Dynamic props shouldn't be overridable
136
+ // This decision can be reviewed later if needed
137
+ value={lowPrice}
138
+ data-value={lowPrice}
139
+ />
140
+ </>
141
+ ) : (
142
+ <Price.Component
143
+ formatter={useFormattedPrice}
144
+ testId="list-price"
145
+ variant="spot"
146
+ className="text__lead"
147
+ SRText="Original price:"
148
+ {...Price.props}
149
+ // Dynamic props shouldn't be overridable
150
+ // This decision can be reviewed later if needed
151
+ value={lowPrice}
152
+ data-value={lowPrice}
153
+ />
154
+ )}
155
+ </div>
156
+ <QuantitySelector.Component
157
+ min={1}
158
+ max={10}
159
+ {...QuantitySelector.props}
98
160
  // Dynamic props shouldn't be overridable
99
161
  // This decision can be reviewed later if needed
100
- value={lowPrice}
101
- data-value={lowPrice}
162
+ onChange={setQuantity}
102
163
  />
103
- </div>
104
- <QuantitySelector.Component
105
- min={1}
106
- max={10}
107
- {...QuantitySelector.props}
108
- // Dynamic props shouldn't be overridable
109
- // This decision can be reviewed later if needed
110
- onChange={setQuantity}
111
- />
112
- </section>
164
+ </section>
165
+ )}
113
166
  {skuVariants && (
114
167
  <Selectors
115
168
  slugsMap={skuVariants.slugsMap}
@@ -118,30 +171,16 @@ function ProductDetailsSettings({
118
171
  data-fs-product-details-selectors
119
172
  />
120
173
  )}
121
- {
122
- /* NOTE: A loading skeleton had to be used to avoid a Lighthouse's
123
- non-composited animation violation due to the button transitioning its
124
- background color when changing from its initial disabled to active state.
125
- See full explanation on commit https://git.io/JyXV5. */
126
- isValidating ? (
127
- <AddToCartLoadingSkeleton />
128
- ) : (
129
- <BuyButton.Component
130
- {...BuyButton.props}
131
- icon={
132
- <Icon.Component
133
- {...Icon.props}
134
- aria-label={buyButtonIconAlt}
135
- name={buyButtonIconName}
136
- />
137
- }
138
- disabled={buyDisabled}
139
- {...buyProps}
140
- >
141
- {buyButtonTitle || 'Add to Cart'}
142
- </BuyButton.Component>
143
- )
144
- }
174
+ {isValidating ? (
175
+ /* NOTE:
176
+ A loading skeleton had to be used to avoid a Lighthouse's
177
+ non-composited animation violation due to the button transitioning its
178
+ background color when changing from its initial disabled to active state.
179
+ See full explanation on commit https://git.io/JyXV5. */
180
+ <AddToCartLoadingSkeleton />
181
+ ) : (
182
+ <AddToCartButton />
183
+ )}
145
184
  </>
146
185
  )
147
186
  }
@@ -22,8 +22,8 @@ import {
22
22
  ResultsCountSkeleton,
23
23
  SortSkeleton,
24
24
  } from 'src/components/sections/ProductGallery/Overrides'
25
- import { useDelayedFacets } from './useDelayedFacets'
26
- import { useDelayedPagination } from './useDelayedPagination'
25
+ import { useDelayedFacets } from 'src/sdk/search/useDelayedFacets'
26
+ import { useDelayedPagination } from 'src/sdk/search/useDelayedPagination'
27
27
  import {
28
28
  PLPContext,
29
29
  SearchPageContext,
@@ -1,10 +1,7 @@
1
1
  import { ShippingSimulationProps as UIShippingSimulationProps } from '@faststore/ui'
2
2
 
3
- import { getShippingSimulation } from 'src/sdk/shipping'
4
- import { ShippingSla } from '@generated/graphql'
5
3
  import { useSession } from 'src/sdk/session'
6
- import { IShippingItem } from '@faststore/api'
7
- import { useShippingSimulation } from './useShippingSimulation'
4
+ import { useShippingSimulation } from 'src/sdk/shipping/useShippingSimulation'
8
5
 
9
6
  import { ShippingSimulation as ShippingSimulationWrapper } from 'src/components/sections/ProductDetails/Overrides'
10
7
 
@@ -26,27 +23,6 @@ interface ShippingSimulationProps
26
23
  }
27
24
  }
28
25
 
29
- const fetchShippingSimulation = async (
30
- shippingItem: IShippingItem,
31
- country: string,
32
- postalCode: string
33
- ): Promise<[string, ShippingSla[]]> => {
34
- const shipping = await getShippingSimulation({
35
- country,
36
- postalCode: postalCode,
37
- items: [shippingItem],
38
- })
39
-
40
- const location =
41
- [shipping?.address?.neighborhood, shipping?.address?.city]
42
- .filter(Boolean)
43
- .join(' / ') ?? ''
44
-
45
- const options = shipping?.logisticsInfo?.[0]?.slas ?? []
46
-
47
- return [location, options as ShippingSla[]]
48
- }
49
-
50
26
  export default function ShippingSimulation({
51
27
  productShippingInfo,
52
28
  formatter,
@@ -55,24 +31,25 @@ export default function ShippingSimulation({
55
31
  idkPostalCodeLinkProps,
56
32
  ...otherProps
57
33
  }: ShippingSimulationProps) {
58
- const { country, postalCode: sessionPostalCode } = useSession()
59
-
60
34
  const {
61
35
  input,
62
36
  shippingSimulation,
63
37
  handleSubmit,
64
38
  handleOnInput,
65
39
  handleOnClear,
66
- } = useShippingSimulation(
67
- productShippingInfo,
68
- fetchShippingSimulation,
69
- sessionPostalCode,
70
- country
71
- )
40
+ } = useShippingSimulation(productShippingInfo)
72
41
 
73
42
  const { postalCode, displayClearButton, errorMessage } = input
74
43
 
75
- const { location, options } = shippingSimulation
44
+ const location =
45
+ [
46
+ shippingSimulation?.address?.neighborhood,
47
+ shippingSimulation?.address?.city,
48
+ ]
49
+ .filter(Boolean)
50
+ .join(' / ') ?? ''
51
+
52
+ const options = shippingSimulation?.logisticsInfo?.[0]?.slas ?? []
76
53
 
77
54
  return (
78
55
  <ShippingSimulationWrapper.Component
@@ -82,6 +59,7 @@ export default function ShippingSimulation({
82
59
  onClear={handleOnClear}
83
60
  location={location}
84
61
  options={options}
62
+ address={shippingSimulation?.address}
85
63
  displayClearButton={displayClearButton}
86
64
  errorMessage={errorMessage}
87
65
  postalCode={postalCode}
@@ -1,7 +1,7 @@
1
1
  import { gql } from '@faststore/graphql-utils'
2
2
 
3
3
  export const fragment = gql`
4
- fragment ClientProducts on Query {
4
+ fragment ClientManyProducts on Query {
5
5
  search(
6
6
  first: $first
7
7
  after: $after
@@ -0,0 +1,13 @@
1
+ import { gql } from '@faststore/graphql-utils'
2
+
3
+ export const fragment = gql`
4
+ fragment ClientSearchSuggestions on Query {
5
+ search(first: 5, term: $term, selectedFacets: $selectedFacets) {
6
+ suggestions {
7
+ terms {
8
+ value
9
+ }
10
+ }
11
+ }
12
+ }
13
+ `
@@ -0,0 +1,11 @@
1
+ import { gql } from '@faststore/graphql-utils'
2
+
3
+ export const fragment = gql`
4
+ fragment ClientShippingSimulation on Query {
5
+ shipping(items: $items, postalCode: $postalCode, country: $country) {
6
+ address {
7
+ city
8
+ }
9
+ }
10
+ }
11
+ `
@@ -0,0 +1,13 @@
1
+ import { gql } from '@faststore/graphql-utils'
2
+
3
+ export const fragment = gql`
4
+ fragment ClientTopSearchSuggestions on Query {
5
+ search(first: 5, term: $term, selectedFacets: $selectedFacets) {
6
+ suggestions {
7
+ terms {
8
+ value
9
+ }
10
+ }
11
+ }
12
+ }
13
+ `
@@ -1,7 +1,7 @@
1
1
  import { gql } from '@faststore/graphql-utils'
2
2
 
3
3
  export const fragment = gql`
4
- fragment ServerProductPage on Query {
4
+ fragment ServerProduct on Query {
5
5
  product(locator: $locator) {
6
6
  id: productID
7
7
  }
@@ -0,0 +1,36 @@
1
+ // SDK
2
+ export {
3
+ useSession as useSession_unstable,
4
+ sessionStore as sessionStore_unstable,
5
+ validateSession as validateSession_unstable,
6
+ } from '../../src/sdk/session'
7
+ export {
8
+ useCart as useCart_unstable,
9
+ cartStore as cartStore_unstable,
10
+ } from '../../src/sdk/cart'
11
+ export type { CartItem, Cart } from '../../src/sdk/cart'
12
+ export { useBuyButton as useBuyButton_unstable } from '../../src/sdk/cart/useBuyButton'
13
+ export { useCartToggleButton as useCartToggleButton_unstable } from '../../src/sdk/cart/useCartToggleButton'
14
+ export { useCheckoutButton as useCheckoutButton_unstable } from '../../src/sdk/cart/useCheckoutButton'
15
+ export { useRemoveButton as useRemoveButton_unstable } from '../../src/sdk/cart/useRemoveButton'
16
+ export { useQuery as useQuery_unstable } from '../../src/sdk/graphql/useQuery'
17
+ export { useLazyQuery as useLazyQuery_unstable } from '../../src/sdk/graphql/useLazyQuery'
18
+ export { useNewsletter as useNewsletter_unstable } from '../../src/sdk/newsletter/useNewsletter'
19
+ export { useDiscountPercent as useDiscountPercent_unstable } from '../../src/sdk/product/useDiscountPercent'
20
+ export { useFormattedPrice as useFormattedPrice_unstable } from '../../src/sdk/product/useFormattedPrice'
21
+ export { useLocalizedVariables as useLocalizedVariables_unstable } from '../../src/sdk/product/useLocalizedVariables'
22
+ export { useProductGalleryQuery as useProductGalleryQuery_unstable } from '../../src/sdk/product/useProductGalleryQuery'
23
+ export { useProductLink as useProductLink_unstable } from '../../src/sdk/product/useProductLink'
24
+ export { useProductQuery as useProductQuery_unstable } from '../../src/sdk/product/useProductQuery'
25
+ export { useProductsPrefetch as useProductsPrefetch_unstable } from '../../src/sdk/product/useProductsPrefetch'
26
+ export { default as useSearchHistory_unstable } from '../../src/sdk/search/useSearchHistory'
27
+ export { default as useSuggestions_unstable } from '../../src/sdk/search/useSuggestions'
28
+ export { default as useTopSearch_unstable } from '../../src/sdk/search/useTopSearch'
29
+ export { useFilter as useFilter_unstable } from '../../src/sdk/search/useFilter'
30
+ export { useDelayedFacets as useDelayedFacets_unstable } from '../../src/sdk/search/useDelayedFacets'
31
+ export { useDelayedPagination as useDelayedPagination_unstable } from '../../src/sdk/search/useDelayedPagination'
32
+ export { getShippingSimulation as getShippingSimulation_unstable } from '../../src/sdk/shipping'
33
+ export { useShippingSimulation as useShippingSimulation_unstable } from '../../src/sdk/shipping/useShippingSimulation'
34
+
35
+ // Components
36
+ export { Image as Image_unstable } from '../../src/components/ui/Image'
@@ -81,8 +81,10 @@ export const getStaticProps: GetStaticProps<
81
81
  { slug: string[] },
82
82
  Locator
83
83
  > = async ({ params, previewData }) => {
84
+ const slug = params?.slug.join('/') ?? ''
85
+
84
86
  const [landingPagePromise, globalSectionsPromise] = [
85
- getLandingPageBySlug(params?.slug[0], previewData),
87
+ getLandingPageBySlug(slug, previewData),
86
88
  getGlobalSectionsData(previewData),
87
89
  ]
88
90
 
@@ -101,7 +103,7 @@ export const getStaticProps: GetStaticProps<
101
103
  ServerCollectionPageQueryQueryVariables,
102
104
  ServerCollectionPageQueryQuery
103
105
  >({
104
- variables: { slug: params?.slug.join('/') ?? '' },
106
+ variables: { slug },
105
107
  operationName: query,
106
108
  }),
107
109
  getPage<PLPContentType>({
@@ -129,6 +131,7 @@ export const getStaticProps: GetStaticProps<
129
131
  page,
130
132
  globalSections: await globalSectionsPromise,
131
133
  type: 'plp',
134
+ key: slug,
132
135
  },
133
136
  }
134
137
  }
@@ -7,9 +7,8 @@ import type { ComponentType } from 'react'
7
7
  import deepmerge from 'deepmerge'
8
8
 
9
9
  import type {
10
- ClientProductQueryQuery,
11
- ServerProductPageQueryQuery,
12
- ServerProductPageQueryQueryVariables,
10
+ ServerProductQueryQuery,
11
+ ServerProductQueryQueryVariables,
13
12
  } from '@generated/graphql'
14
13
  import RenderSections from 'src/components/cms/RenderSections'
15
14
  import BannerNewsletter from 'src/components/sections/BannerNewsletter/BannerNewsletter'
@@ -44,7 +43,7 @@ const COMPONENTS: Record<string, ComponentType<any>> = {
44
43
  }
45
44
 
46
45
  type Props = PDPContentType & {
47
- data: ServerProductPageQueryQuery
46
+ data: ServerProductQueryQuery
48
47
  globalSections: GlobalSectionsData
49
48
  meta: {
50
49
  title: string
@@ -134,8 +133,8 @@ function Page({ data: server, sections, globalSections, offers, meta }: Props) {
134
133
  }
135
134
 
136
135
  const query = gql`
137
- query ServerProductPageQuery($locator: [IStoreSelectedFacet!]!) {
138
- ...ServerProductPage
136
+ query ServerProductQuery($locator: [IStoreSelectedFacet!]!) {
137
+ ...ServerProduct
139
138
  product(locator: $locator) {
140
139
  id: productID
141
140
 
@@ -200,7 +199,7 @@ export const getStaticProps: GetStaticProps<
200
199
  > = async ({ params, previewData }) => {
201
200
  const slug = params?.slug ?? ''
202
201
  const [searchResult, cmsPage, globalSections] = await Promise.all([
203
- execute<ServerProductPageQueryQueryVariables, ServerProductPageQueryQuery>({
202
+ execute<ServerProductQueryQueryVariables, ServerProductQueryQuery>({
204
203
  variables: { locator: [{ key: 'slug', value: slug }] },
205
204
  operationName: query,
206
205
  }),
@@ -253,6 +252,7 @@ export const getStaticProps: GetStaticProps<
253
252
  meta,
254
253
  offers,
255
254
  globalSections,
255
+ key: slug,
256
256
  },
257
257
  }
258
258
  }
@@ -11,7 +11,7 @@ if (typeof window !== 'undefined') {
11
11
  }
12
12
  }
13
13
 
14
- export const AnalyticsHandler = () => {
14
+ const AnalyticsHandler = () => {
15
15
  useAnalyticsEvent((event: AnalyticsEvent) => {
16
16
  // Cleans the ecommerce object before pushing a new one
17
17
  // This prevents the new data from getting merged with the previous one
@@ -2,7 +2,10 @@
2
2
  * More info at: https://www.notion.so/vtexhandbook/Event-API-Documentation-48eee26730cf4d7f80f8fd7262231f84
3
3
  */
4
4
  import type { AnalyticsEvent } from '@faststore/sdk'
5
- import type { SearchSelectItemEvent } from '../../types'
5
+ import type {
6
+ IntelligentSearchQueryEvent,
7
+ SearchSelectItemEvent,
8
+ } from '../../types'
6
9
 
7
10
  import config from '../../../../../faststore.config'
8
11
  import { getCookie } from '../../../../utils/getCookie'
@@ -52,6 +55,15 @@ type SearchEvent =
52
55
  url: string
53
56
  type: 'search.click'
54
57
  }
58
+ | {
59
+ text: string
60
+ misspelled: boolean
61
+ match: number
62
+ operator: string
63
+ locale: string
64
+ url: string
65
+ type: 'search.query'
66
+ }
55
67
 
56
68
  const sendEvent = (options: SearchEvent & { url?: string }) =>
57
69
  fetch(`https://sp.vtex.com/event-api/v1/${config.api.storeId}/event`, {
@@ -71,30 +83,50 @@ const isFullTextSearch = (url: URL) =>
71
83
  typeof url.searchParams.get('q') === 'string' &&
72
84
  /^\/s(\/)?$/g.test(url.pathname)
73
85
 
74
- const handleEvent = (event: AnalyticsEvent | SearchSelectItemEvent) => {
75
- if (event.name !== 'search_select_item') {
76
- return
77
- }
78
-
79
- const url = new URL(event.params.url)
80
-
81
- if (!isFullTextSearch(url)) {
82
- return
83
- }
84
-
85
- for (const item of event.params.items ?? []) {
86
- const productId = item.item_id ?? item.item_variant
87
- const position = item.index
86
+ const handleEvent = (
87
+ event: AnalyticsEvent | SearchSelectItemEvent | IntelligentSearchQueryEvent
88
+ ) => {
89
+ switch (event.name) {
90
+ case 'search_select_item': {
91
+ const url = new URL(event.params.url)
92
+
93
+ if (!isFullTextSearch(url)) {
94
+ return
95
+ }
96
+
97
+ for (const item of event.params.items ?? []) {
98
+ const productId = item.item_id ?? item.item_variant
99
+ const position = item.index
100
+
101
+ if (productId && position) {
102
+ sendEvent({
103
+ type: 'search.click',
104
+ productId,
105
+ position,
106
+ url: url.href,
107
+ text: url.searchParams.get('q') ?? '<empty>',
108
+ })
109
+ }
110
+ }
111
+
112
+ break
113
+ }
88
114
 
89
- if (productId && position) {
115
+ case 'intelligent_search_query': {
90
116
  sendEvent({
91
- type: 'search.click',
92
- productId,
93
- position,
94
- url: url.href,
95
- text: url.searchParams.get('q') ?? '<empty>',
117
+ type: 'search.query',
118
+ url: event.params.url,
119
+ text: event.params.term,
120
+ misspelled: event.params.isTermMisspelled,
121
+ match: event.params.totalCount,
122
+ operator: event.params.logicalOperator,
123
+ locale: event.params.locale,
96
124
  })
125
+
126
+ break
97
127
  }
128
+
129
+ default:
98
130
  }
99
131
  }
100
132
 
@@ -20,3 +20,17 @@ export interface SearchSelectItemEvent {
20
20
  name: 'search_select_item'
21
21
  params: SearchSelectItemParams
22
22
  }
23
+
24
+ export interface IntelligentSearchQueryParams {
25
+ url: string
26
+ locale: string
27
+ term: string
28
+ logicalOperator: string
29
+ isTermMisspelled: boolean
30
+ totalCount: number
31
+ }
32
+
33
+ export interface IntelligentSearchQueryEvent {
34
+ name: 'intelligent_search_query'
35
+ params: IntelligentSearchQueryParams
36
+ }
@@ -1,27 +1,31 @@
1
1
  import {
2
2
  ClientProductGalleryQueryQuery,
3
3
  ClientProductQueryQuery,
4
- ClientProductsQueryQuery,
4
+ ClientManyProductsQueryQuery,
5
5
  ServerCollectionPageQueryQuery,
6
- ServerProductPageQueryQuery,
6
+ ServerProductQueryQuery,
7
7
  } from '@generated/graphql'
8
8
  import type { PropsWithChildren } from 'react'
9
9
  import { createContext, useContext, useMemo } from 'react'
10
10
  import { SearchPageContextType } from 'src/pages/s'
11
11
 
12
12
  export interface PDPContext {
13
- data?: ServerProductPageQueryQuery &
13
+ data?: ServerProductQueryQuery &
14
14
  ClientProductQueryQuery['product'] & { isValidating?: boolean }
15
15
  }
16
16
 
17
17
  export interface PLPContext {
18
18
  data?: ServerCollectionPageQueryQuery &
19
- ClientProductGalleryQueryQuery & { pages: ClientProductsQueryQuery[] }
19
+ ClientProductGalleryQueryQuery & {
20
+ pages: ClientManyProductsQueryQuery[]
21
+ }
20
22
  }
21
23
 
22
24
  export interface SearchPageContext {
23
25
  data?: SearchPageContextType &
24
- ClientProductGalleryQueryQuery & { pages: ClientProductsQueryQuery[] }
26
+ ClientProductGalleryQueryQuery & {
27
+ pages: ClientManyProductsQueryQuery[]
28
+ }
25
29
  }
26
30
 
27
31
  export const isPDP = (x: any): x is PDPContext =>
@@ -1,5 +1,5 @@
1
1
  import { useMemo } from 'react'
2
- import { ClientProductsQueryQueryVariables } from '@generated/graphql'
2
+ import { ClientManyProductsQueryQueryVariables } from '@generated/graphql'
3
3
  import { useSession } from '../session'
4
4
  import { ITEMS_PER_SECTION } from 'src/constants'
5
5
 
@@ -12,7 +12,7 @@ export const useLocalizedVariables = ({
12
12
  sort,
13
13
  term,
14
14
  selectedFacets,
15
- }: Partial<ClientProductsQueryQueryVariables>) => {
15
+ }: Partial<ClientManyProductsQueryQueryVariables>) => {
16
16
  const { channel, locale } = useSession()
17
17
 
18
18
  return useMemo(() => {