@faststore/core 3.0.151 → 3.0.153

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 (63) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +26 -26
  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/webpack/client-production/0.pack +0 -0
  7. package/.next/cache/webpack/client-production/index.pack +0 -0
  8. package/.next/cache/webpack/server-production/0.pack +0 -0
  9. package/.next/cache/webpack/server-production/index.pack +0 -0
  10. package/.next/next-server.js.nft.json +1 -1
  11. package/.next/prerender-manifest.js +1 -1
  12. package/.next/prerender-manifest.json +1 -1
  13. package/.next/routes-manifest.json +1 -1
  14. package/.next/server/chunks/3202.js +1 -1
  15. package/.next/server/chunks/350.js +1 -1
  16. package/.next/server/chunks/5484.js +1 -1
  17. package/.next/server/chunks/5671.js +1 -1
  18. package/.next/server/chunks/9646.js +3 -2
  19. package/.next/server/middleware-build-manifest.js +1 -1
  20. package/.next/server/pages/[...slug].js +1 -1
  21. package/.next/server/pages/api/graphql.js +1 -1
  22. package/.next/server/pages/api/preview.js +1 -1
  23. package/.next/server/pages/en-US/404.html +1 -1
  24. package/.next/server/pages/en-US/404.json +1 -1
  25. package/.next/server/pages/en-US/500.html +1 -1
  26. package/.next/server/pages/en-US/500.json +1 -1
  27. package/.next/server/pages/en-US/account.html +1 -1
  28. package/.next/server/pages/en-US/account.json +1 -1
  29. package/.next/server/pages/en-US/checkout.html +1 -1
  30. package/.next/server/pages/en-US/checkout.json +1 -1
  31. package/.next/server/pages/en-US/login.html +1 -1
  32. package/.next/server/pages/en-US/login.json +1 -1
  33. package/.next/server/pages/en-US/s.html +1 -1
  34. package/.next/server/pages/en-US/s.json +1 -1
  35. package/.next/server/pages/en-US.html +2 -2
  36. package/.next/server/pages/en-US.json +1 -1
  37. package/.next/server/pages/s.js +1 -1
  38. package/.next/static/{dy3ygqBtz-vKIoH-4l0Gg → FXMFCIKLN-fFiSeONDqs3}/_buildManifest.js +1 -1
  39. package/.next/static/chunks/{202.adc31e548eafb936.js → 202.2a784500752e6a56.js} +1 -1
  40. package/.next/static/chunks/{484.3e4d0dcb4c646a45.js → 484.755bc4e4fb57ebb9.js} +1 -1
  41. package/.next/static/chunks/{707-83d159430b49c007.js → 707-6818d9951a83f35f.js} +1 -1
  42. package/.next/static/chunks/973-d39dc88330d50aca.js +1 -0
  43. package/.next/static/chunks/pages/{[...slug]-7251a2458aa75965.js → [...slug]-0db45ebf94081661.js} +1 -1
  44. package/.next/static/chunks/pages/{s-9002e83fb35c96b7.js → s-7ad120ac3e73cbea.js} +1 -1
  45. package/.next/static/chunks/{webpack-39f5c3558a96182c.js → webpack-ab89187244c136e9.js} +1 -1
  46. package/.next/trace +98 -98
  47. package/.turbo/turbo-build.log +3 -3
  48. package/.turbo/turbo-lint.log +1 -1
  49. package/.turbo/turbo-test.log +5 -5
  50. package/@generated/gql.ts +2 -2
  51. package/@generated/graphql.ts +16 -4
  52. package/@generated/persisted-documents.json +2 -2
  53. package/@generated/schema.graphql +2 -0
  54. package/package.json +9 -9
  55. package/src/components/templates/SearchPage/SearchWrapper.tsx +6 -5
  56. package/src/pages/api/preview.ts +33 -11
  57. package/src/sdk/analytics/platform/vtex/search.ts +29 -1
  58. package/src/sdk/analytics/types.ts +14 -0
  59. package/src/sdk/product/useProductGalleryQuery.ts +44 -2
  60. package/src/sdk/search/state.ts +6 -1
  61. package/src/sdk/search/useSuggestions.ts +3 -3
  62. package/.next/static/chunks/973-840ef2154b9d2940.js +0 -1
  63. /package/.next/static/{dy3ygqBtz-vKIoH-4l0Gg → FXMFCIKLN-fFiSeONDqs3}/_ssgManifest.js +0 -0
@@ -49,7 +49,7 @@ Route (pages) Size First Load JS
49
49
  ┌ ● / 1.23 kB 139 kB
50
50
  ├ └ css/197e314c5a03eabd.css 740 B
51
51
  ├ /_app 0 B 91 kB
52
- ├ ● /[...slug] 2.79 kB 152 kB
52
+ ├ ● /[...slug] 2.79 kB 153 kB
53
53
  ├ └ css/e47f1a002bdcf76f.css 2.38 kB
54
54
  ├ ● /[slug]/p 12.3 kB 150 kB
55
55
  ├ └ css/9b6bba2472d272ec.css 10.5 kB
@@ -67,11 +67,11 @@ Route (pages) Size First Load JS
67
67
  ├ chunks/framework-8e279965036b6169.js 45.4 kB
68
68
  ├ chunks/main-029f1328cfee9686.js 33.1 kB
69
69
  ├ chunks/pages/_app-cb1c3a94f987c5c8.js 9.87 kB
70
- ├ chunks/webpack-39f5c3558a96182c.js 2.73 kB
70
+ ├ chunks/webpack-ab89187244c136e9.js 2.74 kB
71
71
  └ css/ee0556daedda6306.css 3.07 kB
72
72
 
73
73
  λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
74
74
  ○ (Static) automatically rendered as static HTML (uses no initial props)
75
75
  ● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
76
76
 
77
- Done in 73.34s.
77
+ Done in 70.84s.
@@ -1,4 +1,4 @@
1
1
  yarn run v1.22.22
2
2
  $ next lint
3
3
  ✔ No ESLint warnings or errors
4
- Done in 6.20s.
4
+ Done in 6.02s.
@@ -1,12 +1,12 @@
1
1
  yarn run v1.22.22
2
2
  $ jest
3
- PASS test/utils/multipleTemplates.test.ts (35.103 s)
4
- PASS test/server/cms/index.test.ts (35.192 s)
5
- PASS test/server/index.test.ts (39.111 s)
3
+ PASS test/utils/multipleTemplates.test.ts (33.959 s)
4
+ PASS test/server/cms/index.test.ts (34.228 s)
5
+ PASS test/server/index.test.ts (38.562 s)
6
6
 
7
7
  Test Suites: 3 passed, 3 total
8
8
  Tests: 19 passed, 19 total
9
9
  Snapshots: 0 total
10
- Time: 40.302 s
10
+ Time: 39.653 s
11
11
  Ran all test suites.
12
- Done in 41.73s.
12
+ Done in 41.17s.
package/@generated/gql.ts CHANGED
@@ -44,7 +44,7 @@ const documents = {
44
44
  types.SubscribeToNewsletterDocument,
45
45
  '\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n':
46
46
  types.ClientManyProductsQueryDocument,
47
- '\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n }\n':
47
+ '\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n fuzzy\n }\n':
48
48
  types.ClientProductGalleryQueryDocument,
49
49
  '\n query ClientProductQuery($locator: [IStoreSelectedFacet!]!) {\n ...ClientProduct\n product(locator: $locator) {\n ...ProductDetailsFragment_product\n }\n }\n':
50
50
  types.ClientProductQueryDocument,
@@ -158,7 +158,7 @@ export function gql(
158
158
  * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
159
159
  */
160
160
  export function gql(
161
- source: '\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n }\n'
161
+ source: '\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n fuzzy\n }\n'
162
162
  ): typeof import('./graphql').ClientProductGalleryQueryDocument
163
163
  /**
164
164
  * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
@@ -523,6 +523,8 @@ export type QueryShippingArgs = {
523
523
 
524
524
  /** Search result. */
525
525
  export type SearchMetadata = {
526
+ /** Indicates how the search engine corrected the misspelled word by using fuzzy logic. */
527
+ fuzzy: Maybe<Scalars['String']['output']>
526
528
  /** Indicates if the search term was misspelled. */
527
529
  isTermMisspelled: Scalars['Boolean']['output']
528
530
  /** Logical operator used to run the search. */
@@ -1504,13 +1506,18 @@ export type ClientProductGalleryQueryQuery = {
1504
1506
  max: { selected: number; absolute: number }
1505
1507
  }
1506
1508
  >
1507
- metadata: { isTermMisspelled: boolean; logicalOperator: string } | null
1509
+ metadata: {
1510
+ isTermMisspelled: boolean
1511
+ logicalOperator: string
1512
+ fuzzy: string | null
1513
+ } | null
1508
1514
  }
1509
1515
  }
1510
1516
 
1511
1517
  export type SearchEvent_MetadataFragment = {
1512
1518
  isTermMisspelled: boolean
1513
1519
  logicalOperator: string
1520
+ fuzzy: string | null
1514
1521
  }
1515
1522
 
1516
1523
  export type ClientProductQueryQueryVariables = Exact<{
@@ -1597,7 +1604,11 @@ export type ClientSearchSuggestionsQueryQuery = {
1597
1604
  }>
1598
1605
  }
1599
1606
  products: { pageInfo: { totalCount: number } }
1600
- metadata: { isTermMisspelled: boolean; logicalOperator: string } | null
1607
+ metadata: {
1608
+ isTermMisspelled: boolean
1609
+ logicalOperator: string
1610
+ fuzzy: string | null
1611
+ } | null
1601
1612
  }
1602
1613
  }
1603
1614
 
@@ -2047,6 +2058,7 @@ export const SearchEvent_MetadataFragmentDoc = new TypedDocumentString(
2047
2058
  fragment SearchEvent_metadata on SearchMetadata {
2048
2059
  isTermMisspelled
2049
2060
  logicalOperator
2061
+ fuzzy
2050
2062
  }
2051
2063
  `,
2052
2064
  { fragmentName: 'SearchEvent_metadata' }
@@ -2099,7 +2111,7 @@ export const ClientManyProductsQueryDocument = {
2099
2111
  export const ClientProductGalleryQueryDocument = {
2100
2112
  __meta__: {
2101
2113
  operationName: 'ClientProductGalleryQuery',
2102
- operationHash: '177fe68cb385737b0901fc9e105f0a4813e18a20',
2114
+ operationHash: 'bfc40da32b60f9404a4adb96b0856e3fbb04b076',
2103
2115
  },
2104
2116
  } as unknown as TypedDocumentString<
2105
2117
  ClientProductGalleryQueryQuery,
@@ -2117,7 +2129,7 @@ export const ClientProductQueryDocument = {
2117
2129
  export const ClientSearchSuggestionsQueryDocument = {
2118
2130
  __meta__: {
2119
2131
  operationName: 'ClientSearchSuggestionsQuery',
2120
- operationHash: '4d9f934764d8578aea08673b8ba57e8bf738f534',
2132
+ operationHash: '47e48eaee91d16a4237eb2c1241bc2ed3e2ad9bb',
2121
2133
  },
2122
2134
  } as unknown as TypedDocumentString<
2123
2135
  ClientSearchSuggestionsQueryQuery,
@@ -4,9 +4,9 @@
4
4
  "c2b3f8bff73ebf6ac79d758c66cabbc21ba9fcc0": "fragment CartItem on StoreOffer { itemOffered { ...CartProductItem } listPrice listPriceWithTaxes price priceWithTaxes quantity seller { identifier } } fragment CartMessage on StoreCartMessage { status text } fragment CartProductItem on StoreProduct { additionalProperty { name propertyID value valueReference } brand { name } gtin image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations availableVariations slugsMap } } name sku unitMultiplier } mutation ValidateCartMutation($cart: IStoreCart!, $session: IStoreSession!) { validateCart(cart: $cart, session: $session) { messages { ...CartMessage } order { acceptedOffer { ...CartItem } orderNumber shouldSplitItem } } }",
5
5
  "feb7005103a859e2bc8cf2360d568806fd88deba": "mutation SubscribeToNewsletter($data: IPersonNewsletter!) { subscribeToNewsletter(data: $data) { id } }",
6
6
  "ad2eb78cfccb9dbd5a9f2d1e150cc70fea5da99a": "fragment ClientManyProducts on Query { search( first: $first after: $after sort: $sort term: $term selectedFacets: $selectedFacets ) { products { pageInfo { totalCount } } } } fragment ProductSummary_product on StoreProduct { additionalProperty { name propertyID value valueReference } advertisement { adId adResponseId } brand { brandName: name } brand { name } gtin image { alternateName url } isVariantOf { name productGroupID } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price quantity seller { identifier } } } id: productID sku slug } query ClientManyProductsQuery($after: String, $first: Int!, $selectedFacets: [IStoreSelectedFacet!]!, $sort: StoreSort!, $term: String!) { search( first: $first after: $after sort: $sort term: $term selectedFacets: $selectedFacets ) { products { edges { node { ...ProductSummary_product } } pageInfo { totalCount } } } ...ClientManyProducts }",
7
- "177fe68cb385737b0901fc9e105f0a4813e18a20": "fragment ClientProductGallery on Query { search( first: $first after: $after sort: $sort term: $term selectedFacets: $selectedFacets ) { products { pageInfo { totalCount } } } } fragment Filter_facets on StoreFacet { ... on StoreFacetBoolean { __typename key label values { label quantity selected value } } ... on StoreFacetRange { __typename key label max { absolute selected } min { absolute selected } } } fragment SearchEvent_metadata on SearchMetadata { isTermMisspelled logicalOperator } query ClientProductGalleryQuery($after: String!, $first: Int!, $selectedFacets: [IStoreSelectedFacet!]!, $sort: StoreSort!, $term: String!) { redirect(term: $term, selectedFacets: $selectedFacets) { url } search( first: $first after: $after sort: $sort term: $term selectedFacets: $selectedFacets ) { facets { ...Filter_facets } metadata { ...SearchEvent_metadata } products { pageInfo { totalCount } } } ...ClientProductGallery }",
7
+ "bfc40da32b60f9404a4adb96b0856e3fbb04b076": "fragment ClientProductGallery on Query { search( first: $first after: $after sort: $sort term: $term selectedFacets: $selectedFacets ) { products { pageInfo { totalCount } } } } fragment Filter_facets on StoreFacet { ... on StoreFacetBoolean { __typename key label values { label quantity selected value } } ... on StoreFacetRange { __typename key label max { absolute selected } min { absolute selected } } } fragment SearchEvent_metadata on SearchMetadata { fuzzy isTermMisspelled logicalOperator } query ClientProductGalleryQuery($after: String!, $first: Int!, $selectedFacets: [IStoreSelectedFacet!]!, $sort: StoreSort!, $term: String!) { redirect(term: $term, selectedFacets: $selectedFacets) { url } search( first: $first after: $after sort: $sort term: $term selectedFacets: $selectedFacets ) { facets { ...Filter_facets } metadata { ...SearchEvent_metadata } products { pageInfo { totalCount } } } ...ClientProductGallery }",
8
8
  "7d121ef8d4dc99174e64e4429a9b977b8bbebed8": "fragment CartProductItem on StoreProduct { additionalProperty { name propertyID value valueReference } brand { name } gtin image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations availableVariations slugsMap } } name sku unitMultiplier } fragment ClientProduct on Query { product(locator: $locator) { id: productID } } fragment ProductDetailsFragment_product on StoreProduct { additionalProperty { name propertyID value valueReference } brand { name } description gtin image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations availableVariations slugsMap } } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price priceWithTaxes seller { identifier } } } id: productID sku unitMultiplier ...CartProductItem } query ClientProductQuery($locator: [IStoreSelectedFacet!]!) { product(locator: $locator) { ...ProductDetailsFragment_product } ...ClientProduct }",
9
- "4d9f934764d8578aea08673b8ba57e8bf738f534": "fragment ClientSearchSuggestions on Query { search(first: 5, term: $term, selectedFacets: $selectedFacets) { suggestions { terms { value } } } } fragment ProductSummary_product on StoreProduct { additionalProperty { name propertyID value valueReference } advertisement { adId adResponseId } brand { brandName: name } brand { name } gtin image { alternateName url } isVariantOf { name productGroupID } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price quantity seller { identifier } } } id: productID sku slug } fragment SearchEvent_metadata on SearchMetadata { isTermMisspelled logicalOperator } query ClientSearchSuggestionsQuery($selectedFacets: [IStoreSelectedFacet!], $term: String!) { search(first: 5, term: $term, selectedFacets: $selectedFacets) { metadata { ...SearchEvent_metadata } products { pageInfo { totalCount } } suggestions { products { ...ProductSummary_product } terms { value } } } ...ClientSearchSuggestions }",
9
+ "47e48eaee91d16a4237eb2c1241bc2ed3e2ad9bb": "fragment ClientSearchSuggestions on Query { search(first: 5, term: $term, selectedFacets: $selectedFacets) { suggestions { terms { value } } } } fragment ProductSummary_product on StoreProduct { additionalProperty { name propertyID value valueReference } advertisement { adId adResponseId } brand { brandName: name } brand { name } gtin image { alternateName url } isVariantOf { name productGroupID } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price quantity seller { identifier } } } id: productID sku slug } fragment SearchEvent_metadata on SearchMetadata { fuzzy isTermMisspelled logicalOperator } query ClientSearchSuggestionsQuery($selectedFacets: [IStoreSelectedFacet!], $term: String!) { search(first: 5, term: $term, selectedFacets: $selectedFacets) { metadata { ...SearchEvent_metadata } products { pageInfo { totalCount } } suggestions { products { ...ProductSummary_product } terms { value } } } ...ClientSearchSuggestions }",
10
10
  "e2385b0f11726d0068f96548f57a8dd441c064e3": "fragment ClientTopSearchSuggestions on Query { search(first: 5, term: $term, selectedFacets: $selectedFacets) { suggestions { terms { value } } } } query ClientTopSearchSuggestionsQuery($selectedFacets: [IStoreSelectedFacet!], $term: String!) { search(first: 5, term: $term, selectedFacets: $selectedFacets) { suggestions { terms { value } } } ...ClientTopSearchSuggestions }",
11
11
  "1e69c734ed31bd9e763a34fe9660f5bbad3fd143": "mutation ValidateSession($search: String!, $session: IStoreSession!) { validateSession(session: $session, search: $search) { addressType b2b { customerId } channel country currency { code symbol } deliveryMode { deliveryChannel deliveryMethod deliveryWindow { endDate startDate } } geoCoordinates { latitude longitude } locale person { email familyName givenName id } postalCode } }",
12
12
  "d6667f1de2a26b94b9b55f4b25d7d823f82635a0": "fragment ClientShippingSimulation on Query { shipping(items: $items, postalCode: $postalCode, country: $country) { address { city } } } query ClientShippingSimulationQuery($country: String!, $items: [IShippingItem!]!, $postalCode: String!) { shipping(items: $items, postalCode: $postalCode, country: $country) { address { city neighborhood state } logisticsInfo { slas { availableDeliveryWindows { endDateUtc listPrice price startDateUtc } carrier localizedEstimates price shippingEstimate } } } ...ClientShippingSimulation }"
@@ -545,6 +545,8 @@ type SearchMetadata {
545
545
  isTermMisspelled: Boolean!
546
546
  """Logical operator used to run the search."""
547
547
  logicalOperator: String!
548
+ """Indicates how the search engine corrected the misspelled word by using fuzzy logic."""
549
+ fuzzy: String
548
550
  }
549
551
 
550
552
  """Search result."""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "3.0.151",
3
+ "version": "3.0.153",
4
4
  "license": "MIT",
5
5
  "repository": "vtex/faststore",
6
6
  "browserslist": "supports es6-module and not dead",
@@ -43,12 +43,12 @@
43
43
  "@envelop/graphql-jit": "^8.0.3",
44
44
  "@envelop/parser-cache": "^6.0.2",
45
45
  "@envelop/validation-cache": "^6.0.2",
46
- "@faststore/api": "^3.0.150",
47
- "@faststore/components": "^3.0.150",
48
- "@faststore/graphql-utils": "^3.0.150",
49
- "@faststore/lighthouse": "^3.0.150",
50
- "@faststore/sdk": "^3.0.150",
51
- "@faststore/ui": "^3.0.150",
46
+ "@faststore/api": "^3.0.152",
47
+ "@faststore/components": "^3.0.152",
48
+ "@faststore/graphql-utils": "^3.0.152",
49
+ "@faststore/lighthouse": "^3.0.152",
50
+ "@faststore/sdk": "^3.0.152",
51
+ "@faststore/ui": "^3.0.152",
52
52
  "@graphql-codegen/cli": "5.0.2",
53
53
  "@graphql-codegen/client-preset": "4.2.6",
54
54
  "@graphql-codegen/typescript": "4.0.7",
@@ -87,7 +87,7 @@
87
87
  "devDependencies": {
88
88
  "@cypress/code-coverage": "^3.12.1",
89
89
  "@envelop/testing": "^6.0.0",
90
- "@faststore/eslint-config": "^3.0.150",
90
+ "@faststore/eslint-config": "^3.0.152",
91
91
  "@lhci/cli": "^0.9.0",
92
92
  "@testing-library/cypress": "^10.0.1",
93
93
  "@types/cypress": "^1.1.3",
@@ -128,5 +128,5 @@
128
128
  "node": "18.19.0",
129
129
  "yarn": "1.19.1"
130
130
  },
131
- "gitHead": "2285089dbd70649e33a1dde1fd90ceb2edca6dba"
131
+ "gitHead": "6f2e5b5970de162e5c425f70ce20552fe195a3de"
132
132
  }
@@ -1,12 +1,12 @@
1
- import { useRouter } from 'next/router'
2
1
  import { useSearch } from '@faststore/sdk'
2
+ import { useRouter } from 'next/router'
3
3
 
4
- import type { SearchContentType } from 'src/server/cms'
5
- import type { SearchPageContextType } from 'src/pages/s'
6
- import { useProductGalleryQuery } from 'src/sdk/product/useProductGalleryQuery'
7
- import Section from 'src/components/sections/Section'
8
4
  import EmptyState from 'src/components/sections/EmptyState'
9
5
  import ProductGalleryStyles from 'src/components/sections/ProductGallery/section.module.scss'
6
+ import Section from 'src/components/sections/Section'
7
+ import type { SearchPageContextType } from 'src/pages/s'
8
+ import { useProductGalleryQuery } from 'src/sdk/product/useProductGalleryQuery'
9
+ import type { SearchContentType } from 'src/server/cms'
10
10
 
11
11
  import SearchPage from './SearchPage'
12
12
 
@@ -37,6 +37,7 @@ export default function SearchWrapper({
37
37
  const {
38
38
  state: { term, sort, selectedFacets },
39
39
  } = useSearch()
40
+
40
41
  const { data: pageProductGalleryData, isValidating } = useProductGalleryQuery(
41
42
  {
42
43
  term,
@@ -1,4 +1,5 @@
1
1
  import type { NextApiHandler, NextApiRequest } from 'next'
2
+ import type { Locator } from '@vtex/client-cms'
2
3
 
3
4
  import { clientCMS } from 'src/server/cms'
4
5
  import { previewRedirects } from '../../../discovery.config'
@@ -20,10 +21,7 @@ const pickParam = (req: NextApiRequest, parameter: string) => {
20
21
  const maybeParam = req.query[parameter]
21
22
 
22
23
  if (typeof maybeParam !== 'string') {
23
- throw new StatusError(
24
- `Parameter ${parameter} missing from querystring`,
25
- 400
26
- )
24
+ return undefined
27
25
  }
28
26
 
29
27
  return maybeParam
@@ -32,14 +30,37 @@ const pickParam = (req: NextApiRequest, parameter: string) => {
32
30
  // TODO: Improve security by disabling CMS preview in production
33
31
  const handler: NextApiHandler = async (req, res) => {
34
32
  try {
35
- const locator = {
36
- contentType: pickParam(req, 'contentType'),
37
- documentId: pickParam(req, 'documentId'),
38
- versionId: pickParam(req, 'versionId'),
33
+ const locator = [
34
+ 'contentType',
35
+ 'documentId',
36
+ 'versionId',
37
+ 'releaseId',
38
+ ].reduce((acc, param) => {
39
+ const value = pickParam(req, param)
40
+
41
+ if (value !== undefined) acc[param] = value
42
+
43
+ return acc
44
+ }, {} as Record<string, string>)
45
+
46
+ // Check if required path params are present
47
+ if (!locator.contentType || !locator.documentId) {
48
+ throw new StatusError(
49
+ `The following path params are required: contentType, documentId`,
50
+ 400
51
+ )
52
+ }
53
+
54
+ // Check if at least one of the querystring params are present
55
+ if (!locator.versionId && !locator.releaseId) {
56
+ throw new StatusError(
57
+ `One of the following querystring params are required: versionId, releaseId`,
58
+ 400
59
+ )
39
60
  }
40
61
 
41
62
  // Fetch CMS to check if the provided `locator` exists
42
- const page = await clientCMS.getCMSPage(locator)
63
+ const page = await clientCMS.getCMSPage(locator as Locator)
43
64
 
44
65
  // If the content doesn't exist prevent preview mode from being enabled
45
66
  if (!page) {
@@ -56,8 +77,9 @@ const handler: NextApiHandler = async (req, res) => {
56
77
  })
57
78
 
58
79
  // Redirect to the path from the fetched locator
59
- if (previewRedirects[locator.contentType]) {
60
- res.redirect(previewRedirects[locator.contentType])
80
+ const redirects = previewRedirects as Record<string, string>
81
+ if (redirects[locator.contentType]) {
82
+ res.redirect(redirects[locator.contentType])
61
83
  return
62
84
  }
63
85
 
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { AnalyticsEvent } from '@faststore/sdk'
5
5
  import type {
6
+ IntelligentSearchAutocompleteQueryEvent,
6
7
  IntelligentSearchQueryEvent,
7
8
  SearchSelectItemEvent,
8
9
  } from '../../types'
@@ -66,6 +67,15 @@ type SearchEvent =
66
67
  url: string
67
68
  type: 'search.query'
68
69
  }
70
+ | {
71
+ text: string
72
+ misspelled: boolean
73
+ match: number
74
+ operator: string
75
+ locale: string
76
+ url: string
77
+ type: 'search.autocomplete.query'
78
+ }
69
79
 
70
80
  const sendEvent = (options: SearchEvent & { url?: string }) =>
71
81
  fetch(`https://sp.vtex.com/event-api/v1/${config.api.storeId}/event`, {
@@ -86,7 +96,11 @@ const isFullTextSearch = (url: URL) =>
86
96
  /^\/s(\/)?$/g.test(url.pathname)
87
97
 
88
98
  const handleEvent = (
89
- event: AnalyticsEvent | SearchSelectItemEvent | IntelligentSearchQueryEvent
99
+ event:
100
+ | AnalyticsEvent
101
+ | SearchSelectItemEvent
102
+ | IntelligentSearchQueryEvent
103
+ | IntelligentSearchAutocompleteQueryEvent
90
104
  ) => {
91
105
  switch (event.name) {
92
106
  case 'search_select_item': {
@@ -128,6 +142,20 @@ const handleEvent = (
128
142
  break
129
143
  }
130
144
 
145
+ case 'intelligent_search_autocomplete_query': {
146
+ sendEvent({
147
+ type: 'search.autocomplete.query',
148
+ url: event.params.url,
149
+ text: event.params.term,
150
+ misspelled: event.params.isTermMisspelled,
151
+ match: event.params.totalCount,
152
+ operator: event.params.logicalOperator,
153
+ locale: event.params.locale,
154
+ })
155
+
156
+ break
157
+ }
158
+
131
159
  default:
132
160
  }
133
161
  }
@@ -34,3 +34,17 @@ export interface IntelligentSearchQueryEvent {
34
34
  name: 'intelligent_search_query'
35
35
  params: IntelligentSearchQueryParams
36
36
  }
37
+
38
+ export interface IntelligentSearchAutocompleteQueryParams {
39
+ url: string
40
+ locale: string
41
+ term: string
42
+ logicalOperator: string
43
+ isTermMisspelled: boolean
44
+ totalCount: number
45
+ }
46
+
47
+ export interface IntelligentSearchAutocompleteQueryEvent {
48
+ name: 'intelligent_search_autocomplete_query'
49
+ params: IntelligentSearchAutocompleteQueryParams
50
+ }
@@ -3,6 +3,7 @@ import { useQuery } from 'src/sdk/graphql/useQuery'
3
3
  import { useSession } from 'src/sdk/session'
4
4
  import { useLocalizedVariables } from './useLocalizedVariables'
5
5
 
6
+ import { useSearch } from '@faststore/sdk'
6
7
  import { Facet } from '@faststore/sdk/dist/types'
7
8
  import type {
8
9
  ClientManyProductsQueryQueryVariables,
@@ -53,6 +54,7 @@ export const query = gql(`
53
54
  fragment SearchEvent_metadata on SearchMetadata {
54
55
  isTermMisspelled
55
56
  logicalOperator
57
+ fuzzy
56
58
  }
57
59
  `)
58
60
 
@@ -63,6 +65,14 @@ type ProductGalleryQueryOptions = {
63
65
  term: ClientManyProductsQueryQueryVariables['term']
64
66
  }
65
67
 
68
+ export const findFacetValue = (
69
+ facets: Facet[],
70
+ searchParam: string
71
+ ): string | null => {
72
+ const facet = facets.find(({ key }) => key === searchParam)
73
+ return facet?.value ?? null
74
+ }
75
+
66
76
  export const useProductGalleryQuery = ({
67
77
  term,
68
78
  sort,
@@ -70,6 +80,7 @@ export const useProductGalleryQuery = ({
70
80
  itemsPerPage,
71
81
  }: ProductGalleryQueryOptions) => {
72
82
  const { locale } = useSession()
83
+ const { state, setState } = useSearch()
73
84
  const localizedVariables = useLocalizedVariables({
74
85
  first: itemsPerPage,
75
86
  after: '0',
@@ -78,9 +89,12 @@ export const useProductGalleryQuery = ({
78
89
  selectedFacets,
79
90
  })
80
91
 
81
- return useQuery<Query, Variables>(query, localizedVariables, {
92
+ const fuzzyFacetValue = findFacetValue(selectedFacets, 'fuzzy')
93
+ const operatorFacetValue = findFacetValue(selectedFacets, 'operator')
94
+
95
+ const queryResult = useQuery<Query, Variables>(query, localizedVariables, {
82
96
  onSuccess: (data) => {
83
- if (data && term) {
97
+ if (data && term && fuzzyFacetValue && operatorFacetValue) {
84
98
  import('@faststore/sdk').then(({ sendAnalyticsEvent }) => {
85
99
  sendAnalyticsEvent<IntelligentSearchQueryEvent>({
86
100
  name: 'intelligent_search_query',
@@ -97,4 +111,32 @@ export const useProductGalleryQuery = ({
97
111
  }
98
112
  },
99
113
  })
114
+
115
+ // If there is no fuzzy or operator facet, we need to add them to the selectedFacets and re-fetch the query
116
+ const shouldRefetchQuery =
117
+ !queryResult.error && (!fuzzyFacetValue || !operatorFacetValue)
118
+
119
+ if (shouldRefetchQuery) {
120
+ if (queryResult.data) {
121
+ setState({
122
+ ...state,
123
+ selectedFacets: [
124
+ ...selectedFacets,
125
+ {
126
+ key: 'fuzzy',
127
+ value: queryResult.data.search.metadata?.fuzzy ?? 'auto',
128
+ },
129
+ {
130
+ key: 'operator',
131
+ value: queryResult.data.search.metadata?.logicalOperator ?? 'and',
132
+ },
133
+ ],
134
+ })
135
+ }
136
+
137
+ // The first result is not relevant, return null data to avoid rendering the page while the query is being re-fetched
138
+ return { ...queryResult, isValidating: true, data: null }
139
+ }
140
+
141
+ return queryResult
100
142
  }
@@ -5,7 +5,12 @@ export const useApplySearchState = () => {
5
5
  const router = useRouter()
6
6
 
7
7
  return useCallback(
8
- (url: URL) => router.push(`${url.pathname}${url.search}`),
8
+ (url: URL) => {
9
+ const newUrl = `${url.pathname}${url.search}`
10
+ return url.searchParams.has('fuzzy') && url.searchParams.has('operator')
11
+ ? router.replace(newUrl)
12
+ : router.push(newUrl)
13
+ },
9
14
  [router]
10
15
  )
11
16
  }
@@ -7,7 +7,7 @@ import type {
7
7
  ClientSearchSuggestionsQueryQuery as Query,
8
8
  ClientSearchSuggestionsQueryQueryVariables as Variables,
9
9
  } from '@generated/graphql'
10
- import type { IntelligentSearchQueryEvent } from '../analytics/types'
10
+ import type { IntelligentSearchAutocompleteQueryEvent } from '../analytics/types'
11
11
 
12
12
  import { useSession } from '../session'
13
13
 
@@ -55,8 +55,8 @@ function useSuggestions(term: string) {
55
55
  onSuccess: (callbackData) => {
56
56
  if (callbackData && term) {
57
57
  import('@faststore/sdk').then(({ sendAnalyticsEvent }) => {
58
- sendAnalyticsEvent<IntelligentSearchQueryEvent>({
59
- name: 'intelligent_search_query',
58
+ sendAnalyticsEvent<IntelligentSearchAutocompleteQueryEvent>({
59
+ name: 'intelligent_search_autocomplete_query',
60
60
  params: {
61
61
  locale,
62
62
  term,