@faststore/api 1.7.30 → 1.7.33

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 (48) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/api.cjs.development.js +313 -181
  3. package/dist/api.cjs.development.js.map +1 -1
  4. package/dist/api.cjs.production.min.js +1 -1
  5. package/dist/api.cjs.production.min.js.map +1 -1
  6. package/dist/api.esm.js +313 -181
  7. package/dist/api.esm.js.map +1 -1
  8. package/dist/index.d.ts +27 -13
  9. package/dist/platforms/vtex/clients/index.d.ts +4 -1
  10. package/dist/platforms/vtex/clients/search/index.d.ts +3 -3
  11. package/dist/platforms/vtex/clients/search/types/AttributeSearchResult.d.ts +25 -51
  12. package/dist/platforms/vtex/clients/search/types/FacetSearchResult.d.ts +31 -0
  13. package/dist/platforms/vtex/clients/search/types/ProductSearchResult.d.ts +146 -154
  14. package/dist/platforms/vtex/clients/sp/index.d.ts +13 -0
  15. package/dist/platforms/vtex/index.d.ts +28 -14
  16. package/dist/platforms/vtex/resolvers/aggregateOffer.d.ts +10 -6
  17. package/dist/platforms/vtex/resolvers/facet.d.ts +2 -2
  18. package/dist/platforms/vtex/resolvers/facetValue.d.ts +2 -2
  19. package/dist/platforms/vtex/resolvers/offer.d.ts +7 -6
  20. package/dist/platforms/vtex/resolvers/product.d.ts +11 -2
  21. package/dist/platforms/vtex/resolvers/productGroup.d.ts +5 -2
  22. package/dist/platforms/vtex/utils/enhanceSku.d.ts +3 -3
  23. package/dist/platforms/vtex/utils/price.d.ts +2 -0
  24. package/dist/platforms/vtex/utils/productStock.d.ts +5 -0
  25. package/dist/typings/index.d.ts +2 -0
  26. package/package.json +2 -2
  27. package/src/platforms/vtex/clients/fetch.ts +1 -1
  28. package/src/platforms/vtex/clients/index.ts +3 -0
  29. package/src/platforms/vtex/clients/search/index.ts +6 -6
  30. package/src/platforms/vtex/clients/search/types/AttributeSearchResult.ts +24 -53
  31. package/src/platforms/vtex/clients/search/types/FacetSearchResult.ts +33 -0
  32. package/src/platforms/vtex/clients/search/types/ProductSearchResult.ts +135 -164
  33. package/src/platforms/vtex/clients/sp/index.ts +67 -0
  34. package/src/platforms/vtex/index.ts +2 -2
  35. package/src/platforms/vtex/loaders/sku.ts +2 -2
  36. package/src/platforms/vtex/resolvers/aggregateOffer.ts +17 -35
  37. package/src/platforms/vtex/resolvers/facet.ts +5 -5
  38. package/src/platforms/vtex/resolvers/facetValue.ts +7 -6
  39. package/src/platforms/vtex/resolvers/offer.ts +107 -17
  40. package/src/platforms/vtex/resolvers/product.ts +55 -73
  41. package/src/platforms/vtex/resolvers/productGroup.ts +22 -13
  42. package/src/platforms/vtex/resolvers/query.ts +3 -3
  43. package/src/platforms/vtex/resolvers/searchResult.ts +24 -12
  44. package/src/platforms/vtex/utils/enhanceSku.ts +4 -4
  45. package/src/platforms/vtex/utils/facets.ts +8 -2
  46. package/src/platforms/vtex/utils/price.ts +10 -0
  47. package/src/platforms/vtex/utils/productStock.ts +25 -0
  48. package/src/typings/index.ts +4 -0
@@ -1,41 +1,23 @@
1
+ import type { Item } from '../clients/search/types/ProductSearchResult'
2
+ import type { StoreProduct } from './product'
3
+ import type { PromiseType } from '../../../typings'
4
+ import type { Resolver } from '..'
1
5
  import type { EnhancedSku } from '../utils/enhanceSku'
2
- import type { Simulation } from '../clients/commerce/types/Simulation'
3
6
 
4
- type Resolvers = (root: Simulation & { product: EnhancedSku }) => unknown
7
+ type Root = PromiseType<ReturnType<typeof StoreProduct.offers>>
5
8
 
6
- const inStock = (item: Simulation['items'][0]) =>
7
- item.availability === 'available'
8
-
9
- // Smallest Available Selling Price First
10
- export const sortOfferByPrice = (
11
- items: Simulation['items']
12
- ): Simulation['items'] =>
13
- items.sort((a, b) => {
14
- if (inStock(a) && !inStock(b)) {
15
- return -1
16
- }
17
-
18
- if (!inStock(a) && inStock(b)) {
19
- return 1
20
- }
21
-
22
- return a.sellingPrice - b.sellingPrice
23
- })
24
-
25
- export const StoreAggregateOffer: Record<string, Resolvers> = {
26
- highPrice: ({ items }) => {
27
- const availableItems = items.filter(inStock)
28
- const highPrice = availableItems.pop()?.sellingPrice
29
-
30
- return (highPrice ?? 0) / 1e2
31
- },
32
- lowPrice: ({ items }) => {
33
- const availableItems = items.filter(inStock)
34
- const lowPrice = availableItems[0]?.sellingPrice
35
-
36
- return (lowPrice ?? 0) / 1e2
37
- },
9
+ export const StoreAggregateOffer: Record<string, Resolver<Root>> & {
10
+ offers: Resolver<Root, any, Array<Item & { product: EnhancedSku }>>
11
+ } = {
12
+ highPrice: ({ product }) =>
13
+ product.isVariantOf.priceRange.sellingPrice.highPrice ?? 0,
14
+ lowPrice: (root) =>
15
+ root.product.isVariantOf.priceRange.sellingPrice.lowPrice ?? 0,
38
16
  offerCount: ({ items }) => items.length,
39
17
  priceCurrency: () => '',
40
- offers: ({ items, product }) => items.map((item) => ({ ...item, product })),
18
+ offers: ({ items, product }) =>
19
+ items.map((item) => ({
20
+ ...item,
21
+ product,
22
+ })),
41
23
  }
@@ -1,11 +1,11 @@
1
1
  import type { Resolver } from '..'
2
- import type { Attribute } from '../clients/search/types/AttributeSearchResult'
2
+ import type { Facet } from '../clients/search/types/FacetSearchResult'
3
3
 
4
- type Root = Attribute
4
+ type Root = Facet
5
5
 
6
6
  export const StoreFacet: Record<string, Resolver<Root>> = {
7
- key: ({ key }) => key,
8
- label: ({ label }) => label,
7
+ key: ({ key }) => key ?? '',
8
+ label: ({ name }) => name ?? 'unknown',
9
9
  values: ({ values }) => values,
10
- type: ({ type }) => (type === 'text' ? 'BOOLEAN' : 'RANGE'),
10
+ type: ({ type }) => (type === 'TEXT' ? 'BOOLEAN' : 'RANGE'),
11
11
  }
@@ -1,11 +1,12 @@
1
1
  import type { Resolver } from '..'
2
- import type { Value } from '../clients/search/types/AttributeSearchResult'
2
+ import type { FacetValue } from '../clients/search/types/FacetSearchResult'
3
3
 
4
- type Root = Value
4
+ type Root = FacetValue
5
5
 
6
6
  export const StoreFacetValue: Record<string, Resolver<Root>> = {
7
- value: ({ key, from, to }) => key ?? `${from}-to-${to}`,
8
- label: ({ label }) => label ?? 'unknown',
9
- selected: ({ active }) => active,
10
- quantity: ({ count }) => count,
7
+ value: ({ value, range }) =>
8
+ value ?? `${range?.from ?? ''}-to-${range?.to ?? ''}`,
9
+ label: ({ name }) => name || 'unknown',
10
+ selected: ({ selected }) => selected,
11
+ quantity: ({ quantity }) => quantity,
11
12
  }
@@ -1,26 +1,116 @@
1
- import type { EnhancedSku } from '../utils/enhanceSku'
2
1
  import type { Resolver } from '..'
3
- import type { Item } from '../clients/commerce/types/Simulation'
2
+ import type { StoreAggregateOffer } from './aggregateOffer'
3
+ import {
4
+ getFirstSeller,
5
+ inStock,
6
+ inStockOrderFormItem,
7
+ } from '../utils/productStock'
8
+ import { getItemPriceByKey } from '../utils/price'
9
+ import type { ArrayElementType } from '../../../typings'
10
+ import type { Item } from '../clients/search/types/ProductSearchResult'
11
+ import type { EnhancedSku } from '../utils/enhanceSku'
4
12
  import type { OrderFormItem } from '../clients/commerce/types/OrderForm'
5
13
 
6
- type Root =
7
- | (Item & { product: EnhancedSku }) // when querying search/product
8
- | (OrderFormItem & { product: Promise<EnhancedSku> }) // when querying order
14
+ type OrderFormProduct = OrderFormItem & { product: Promise<EnhancedSku> }
15
+ type SearchProduct = ArrayElementType<
16
+ ReturnType<typeof StoreAggregateOffer.offers>
17
+ >
18
+ type Root = SearchProduct | OrderFormProduct
19
+
20
+ const isSearchItem = (item: any): item is Item => 'sellers' in item
21
+ const isOrderFormItem = (item: any): item is OrderFormProduct =>
22
+ 'skuName' in item
23
+
24
+ const getAvailability = (available: boolean) =>
25
+ available ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock'
9
26
 
10
27
  export const StoreOffer: Record<string, Resolver<Root>> = {
11
28
  priceCurrency: () => '',
12
- priceValidUntil: ({ priceValidUntil }) => priceValidUntil ?? '',
29
+ priceValidUntil: (item) => {
30
+ if (isSearchItem(item)) {
31
+ return getFirstSeller(item.sellers)?.commertialOffer.PriceValidUntil ?? ''
32
+ }
33
+
34
+ if (isOrderFormItem(item)) {
35
+ return item.priceValidUntil ?? ''
36
+ }
37
+
38
+ return null
39
+ },
13
40
  itemCondition: () => 'https://schema.org/NewCondition',
14
- availability: ({ availability }) =>
15
- availability === 'available'
16
- ? 'https://schema.org/InStock'
17
- : 'https://schema.org/OutOfStock',
18
- seller: ({ seller }) => ({
19
- identifier: seller,
20
- }),
21
- price: ({ sellingPrice }) => sellingPrice / 1e2, // TODO add spot price calculation
22
- sellingPrice: ({ sellingPrice }) => sellingPrice / 1e2,
23
- listPrice: ({ listPrice }) => listPrice / 1e2,
41
+ availability: async (item) => {
42
+ if (isSearchItem(item)) {
43
+ return getAvailability(!!inStock(item))
44
+ }
45
+
46
+ if (isOrderFormItem(item)) {
47
+ return getAvailability(inStockOrderFormItem(item.availability))
48
+ }
49
+
50
+ return null
51
+ },
52
+ seller: (item) => {
53
+ if (isSearchItem(item)) {
54
+ return {
55
+ identifier: getFirstSeller(item.sellers)?.sellerId ?? '',
56
+ }
57
+ }
58
+
59
+ if (isOrderFormItem(item)) {
60
+ return {
61
+ identifier: item.seller,
62
+ }
63
+ }
64
+
65
+ return null
66
+ },
67
+ price: (item) => {
68
+ if (isSearchItem(item)) {
69
+ return getItemPriceByKey(item, 'spotPrice')
70
+ }
71
+
72
+ if (isOrderFormItem(item)) {
73
+ return item.price / 1e2
74
+ }
75
+
76
+ return null
77
+ },
78
+ sellingPrice: (item) => {
79
+ if (isSearchItem(item)) {
80
+ return getItemPriceByKey(item, 'Price')
81
+ }
82
+
83
+ if (isOrderFormItem(item)) {
84
+ return item.sellingPrice / 1e2
85
+ }
86
+
87
+ return null
88
+ },
89
+ listPrice: (item) => {
90
+ if (isSearchItem(item)) {
91
+ return getItemPriceByKey(item, 'ListPrice')
92
+ }
93
+
94
+ if (isOrderFormItem(item)) {
95
+ return item.listPrice / 1e2
96
+ }
97
+
98
+ return null
99
+ },
24
100
  itemOffered: ({ product }) => product,
25
- quantity: ({ quantity }) => quantity,
101
+ quantity: (item) => {
102
+ if (isSearchItem(item)) {
103
+ return item.sellers.reduce(
104
+ (quantity, seller) =>
105
+ quantity + seller.commertialOffer.AvailableQuantity,
106
+ 0
107
+ )
108
+ }
109
+
110
+ if (isOrderFormItem(item)) {
111
+ return item.quantity
112
+ }
113
+
114
+ return null
115
+ },
26
116
  }
@@ -1,13 +1,15 @@
1
- import { slugify } from '../utils/slugify'
2
1
  import type { Resolver } from '..'
2
+ import { sortOfferByPrice } from '../utils/productStock'
3
+ import type { PromiseType } from '../../../typings'
4
+ import type { Query } from './query'
5
+ import type { Item } from '../clients/search/types/ProductSearchResult'
3
6
  import type { EnhancedSku } from '../utils/enhanceSku'
4
- import { sortOfferByPrice } from './aggregateOffer'
5
7
 
6
- type Root = EnhancedSku
8
+ type Root = PromiseType<ReturnType<typeof Query.product>>
7
9
 
8
10
  const DEFAULT_IMAGE = {
9
- name: 'image',
10
- value:
11
+ imageText: 'image',
12
+ imageUrl:
11
13
  'https://storecomponents.vtexassets.com/assets/faststore/images/image___117a6d3e229a96ad0e0d0876352566e2.svg',
12
14
  }
13
15
 
@@ -16,84 +18,64 @@ const getPath = (link: string, id: string) => `/${getSlug(link, id)}/p`
16
18
  const nonEmptyArray = <T>(array: T[] | null | undefined) =>
17
19
  Array.isArray(array) && array.length > 0 ? array : null
18
20
 
19
- export const StoreProduct: Record<string, Resolver<Root>> = {
20
- productID: ({ id }) => id,
21
- name: ({ isVariantOf, name }) => name ?? isVariantOf.name,
22
- slug: ({ isVariantOf: { link }, id }) => getSlug(link, id),
21
+ export const StoreProduct: Record<string, Resolver<Root>> & {
22
+ offers: Resolver<Root, any, { items: Item[]; product: EnhancedSku }>
23
+ isVariantOf: Resolver<Root, any, Root>
24
+ } = {
25
+ productID: ({ itemId }) => itemId,
26
+ name: ({ isVariantOf, name }) => name ?? isVariantOf.productName,
27
+ slug: ({ isVariantOf: { linkText }, itemId }) => getSlug(linkText, itemId),
23
28
  description: ({ isVariantOf: { description } }) => description,
24
- seo: ({ isVariantOf: { name, description } }) => ({
25
- title: name,
29
+ seo: ({ isVariantOf: { description, productName } }) => ({
30
+ title: productName,
26
31
  description,
27
32
  }),
28
33
  brand: ({ isVariantOf: { brand } }) => ({ name: brand }),
29
- breadcrumbList: ({ isVariantOf: { categoryTrees, name, link }, id }) => ({
30
- itemListElement: [
31
- ...categoryTrees.reverse().map(({ categoryNames }, index) => ({
32
- name: categoryNames[categoryNames.length - 1],
33
- item: `/${categoryNames
34
- .map((categoryName) => slugify(categoryName))
35
- .join('/')}`,
36
- position: index + 1,
37
- })),
38
- {
39
- name,
40
- item: getPath(link, id),
41
- position: categoryTrees.length + 1,
42
- },
43
- ],
44
- numberOfItems: categoryTrees.length,
45
- }),
46
- image: ({ isVariantOf, images }) =>
47
- (
48
- nonEmptyArray(images) ??
49
- nonEmptyArray(isVariantOf.images) ?? [DEFAULT_IMAGE]
50
- ).map(({ name, value }) => ({
51
- alternateName: name ?? '',
52
- url: value.replace('vteximg.com.br', 'vtexassets.com'),
53
- })),
54
- sku: ({ id }) => id,
55
- gtin: ({ reference }) => reference ?? '',
56
- review: () => [],
57
- aggregateRating: () => ({}),
58
- offers: async (product, _, ctx) => {
59
- const {
60
- loaders: { simulationLoader },
61
- storage: { channel },
62
- } = ctx
63
-
64
- const { id, policies } = product
65
-
66
- const sellers = policies.find(
67
- (policy) => policy.id === channel.salesChannel
68
- )?.sellers
34
+ breadcrumbList: ({
35
+ isVariantOf: { categories, productName, linkText },
36
+ itemId,
37
+ }) => {
38
+ return {
39
+ itemListElement: [
40
+ ...categories.reverse().map((categoryPath, index) => {
41
+ const categoryNames = categoryPath.split('/')
69
42
 
70
- if (sellers === null || sellers === undefined) {
71
- // This error will likely happen when you forget to forward the channel somewhere in your code.
72
- // Make sure all queries that lead to a product are forwarding the channel in context corectly
73
- throw new Error(
74
- `Product with id ${id} has no sellers for sales channel ${channel.salesChannel}.`
75
- )
43
+ return {
44
+ name: categoryNames[categoryNames.length - 2],
45
+ item: categoryPath.toLowerCase(),
46
+ position: index + 1,
47
+ }
48
+ }),
49
+ {
50
+ name: productName,
51
+ item: getPath(linkText, itemId),
52
+ position: categories.length + 1,
53
+ },
54
+ ],
55
+ numberOfItems: categories.length,
76
56
  }
77
-
78
- // Unique seller ids
79
- const sellerIds = sellers.map((seller) => seller.id)
80
- const items = Array.from(new Set(sellerIds)).map((seller) => ({
81
- quantity: 1,
82
- seller,
83
- id,
84
- }))
85
-
86
- const simulation = await simulationLoader.load(items)
87
-
57
+ },
58
+ image: ({ images }) =>
59
+ (nonEmptyArray(images) ?? [DEFAULT_IMAGE]).map(
60
+ ({ imageUrl, imageText }) => ({
61
+ alternateName: imageText ?? '',
62
+ url: imageUrl.replace('vteximg.com.br', 'vtexassets.com'),
63
+ })
64
+ ),
65
+ sku: ({ itemId }) => itemId,
66
+ gtin: ({ referenceId }) => referenceId[0]?.Value ?? '',
67
+ review: () => [],
68
+ aggregateRating: () => ({}),
69
+ offers: (product): { items: Item[]; product: Root } => {
88
70
  return {
89
- ...simulation,
90
- items: sortOfferByPrice(simulation.items),
71
+ items: sortOfferByPrice(product.isVariantOf.items),
91
72
  product,
92
73
  }
93
74
  },
94
- isVariantOf: ({ isVariantOf }) => isVariantOf,
95
- additionalProperty: ({ attributes = [] }) =>
96
- attributes.map((attribute) => ({
75
+ isVariantOf: (root) => root,
76
+ // TODO: get this value. Fix any type
77
+ additionalProperty: ({ attributes = [] }: any) =>
78
+ attributes.map((attribute: any) => ({
97
79
  name: attribute.key,
98
80
  value: attribute.value,
99
81
  })),
@@ -1,19 +1,28 @@
1
1
  import { enhanceSku } from '../utils/enhanceSku'
2
- import type { Product } from '../clients/search/types/ProductSearchResult'
3
2
  import type { Resolver } from '..'
3
+ import type { PromiseType } from '../../../typings'
4
+ import type { StoreProduct } from './product'
4
5
 
5
- export const StoreProductGroup: Record<string, Resolver<Product>> = {
6
- hasVariant: (root) => root.skus.map((sku) => enhanceSku(sku, root)),
7
- productGroupID: ({ product }) => product,
8
- name: ({ name }) => name,
9
- additionalProperty: ({ textAttributes = [], productSpecifications = [] }) => {
10
- const specs = new Set(productSpecifications)
6
+ type Root = PromiseType<ReturnType<typeof StoreProduct.isVariantOf>>
11
7
 
12
- return textAttributes
13
- .filter((attribute) => specs.has(attribute.labelKey))
14
- .map((attribute) => ({
15
- name: attribute.labelKey,
16
- value: attribute.labelValue,
8
+ const BLOCKED_PROPERTIES: Record<string, boolean> = {
9
+ sellerId: true,
10
+ }
11
+
12
+ export const StoreProductGroup: Record<string, Resolver<Root>> = {
13
+ hasVariant: (root) =>
14
+ root.isVariantOf.items.map((item) => enhanceSku(item, root.isVariantOf)),
15
+ productGroupID: ({ isVariantOf }) => isVariantOf.productId,
16
+ name: ({ isVariantOf }) => isVariantOf.productName,
17
+ additionalProperty: ({ isVariantOf: { properties } }) =>
18
+ properties.flatMap((property) => {
19
+ if (BLOCKED_PROPERTIES[property.name]) {
20
+ return []
21
+ }
22
+
23
+ return property.values.map((propertyValue) => ({
24
+ name: property.name,
25
+ value: propertyValue,
17
26
  }))
18
- },
27
+ }),
19
28
  }
@@ -77,7 +77,7 @@ export const Query = {
77
77
  })
78
78
 
79
79
  const skus = products.products
80
- .map((product) => product.skus.map((sku) => enhanceSku(sku, product)))
80
+ .map((product) => product.items.map((sku) => enhanceSku(sku, product)))
81
81
  .flat()
82
82
  .filter((sku) => sku.sellers.length > 0)
83
83
 
@@ -86,8 +86,8 @@ export const Query = {
86
86
  hasNextPage: products.pagination.after.length > 0,
87
87
  hasPreviousPage: products.pagination.before.length > 0,
88
88
  startCursor: '0',
89
- endCursor: products.total.toString(),
90
- totalCount: products.total,
89
+ endCursor: products.recordsFiltered.toString(),
90
+ totalCount: products.recordsFiltered,
91
91
  },
92
92
  // after + index is bigger than after+first itself because of the array flat() above
93
93
  edges: skus.map((sku, index) => ({
@@ -1,23 +1,35 @@
1
1
  import { enhanceSku } from '../utils/enhanceSku'
2
2
  import type { Resolver } from '..'
3
3
  import type { SearchArgs } from '../clients/search'
4
- import type { Attribute } from '../clients/search/types/AttributeSearchResult'
4
+ import type { Facet } from '../clients/search/types/FacetSearchResult'
5
5
 
6
6
  type Root = Omit<SearchArgs, 'type'>
7
7
 
8
- const REMOVED_FACETS_FROM_COLLECTION_PAGE = ['departamento']
8
+ const REMOVED_FACETS_FROM_COLLECTION_PAGE = ['departamento', 'Departamento']
9
9
 
10
10
  export const StoreSearchResult: Record<string, Resolver<Root>> = {
11
11
  products: async (searchArgs, _, ctx) => {
12
12
  const {
13
- clients: { search },
13
+ clients: { search, sp },
14
14
  } = ctx
15
15
 
16
16
  const products = await search.products(searchArgs)
17
17
 
18
+ // Raise event on search's analytics API when performing
19
+ // a full text search.
20
+ if (searchArgs.query) {
21
+ sp.sendEvent({
22
+ type: 'search.query',
23
+ text: searchArgs.query,
24
+ misspelled: products.correction.misspelled,
25
+ match: products.recordsFiltered,
26
+ operator: products.operator,
27
+ }).catch(console.error)
28
+ }
29
+
18
30
  const skus = products.products
19
31
  .map((product) => {
20
- const [maybeSku] = product.skus
32
+ const [maybeSku] = product.items
21
33
 
22
34
  return maybeSku && enhanceSku(maybeSku, product)
23
35
  })
@@ -28,8 +40,8 @@ export const StoreSearchResult: Record<string, Resolver<Root>> = {
28
40
  hasNextPage: products.pagination.after.length > 0,
29
41
  hasPreviousPage: products.pagination.before.length > 0,
30
42
  startCursor: '0',
31
- endCursor: products.total.toString(),
32
- totalCount: products.total,
43
+ endCursor: products.recordsFiltered.toString(),
44
+ totalCount: products.recordsFiltered,
33
45
  },
34
46
  edges: skus.map((sku, index) => ({
35
47
  node: sku,
@@ -42,12 +54,12 @@ export const StoreSearchResult: Record<string, Resolver<Root>> = {
42
54
  clients: { search: is },
43
55
  } = ctx
44
56
 
45
- const facets = await is.facets(searchArgs)
57
+ const { facets } = await is.facets(searchArgs)
46
58
 
47
59
  const isCollectionPage = !searchArgs.query
48
- const filteredFacets = facets?.attributes?.reduce((acc, currentFacet) => {
60
+ const filteredFacets = facets?.reduce((acc, currentFacet) => {
49
61
  const shouldFilterFacet = REMOVED_FACETS_FROM_COLLECTION_PAGE.includes(
50
- currentFacet.key
62
+ currentFacet.name
51
63
  )
52
64
 
53
65
  const shouldRemoveFacetFromCollectionPage =
@@ -58,8 +70,8 @@ export const StoreSearchResult: Record<string, Resolver<Root>> = {
58
70
  }
59
71
 
60
72
  currentFacet.values.sort((a, b) => {
61
- const firstItemLabel = a.label ?? ''
62
- const secondItemLabel = b.label ?? ''
73
+ const firstItemLabel = a.name ?? ''
74
+ const secondItemLabel = b.name ?? ''
63
75
 
64
76
  return firstItemLabel.localeCompare(secondItemLabel)
65
77
  })
@@ -67,7 +79,7 @@ export const StoreSearchResult: Record<string, Resolver<Root>> = {
67
79
  acc.push(currentFacet)
68
80
 
69
81
  return acc
70
- }, [] as Attribute[])
82
+ }, [] as Facet[])
71
83
 
72
84
  return filteredFacets ?? []
73
85
  },
@@ -1,8 +1,8 @@
1
- import type { Product, Sku } from '../clients/search/types/ProductSearchResult'
1
+ import type { Product, Item } from '../clients/search/types/ProductSearchResult'
2
2
 
3
- export type EnhancedSku = Sku & { isVariantOf: Product }
3
+ export type EnhancedSku = Item & { isVariantOf: Product }
4
4
 
5
- export const enhanceSku = (sku: Sku, product: Product): EnhancedSku => ({
6
- ...sku,
5
+ export const enhanceSku = (item: Item, product: Product): EnhancedSku => ({
6
+ ...item,
7
7
  isVariantOf: product,
8
8
  })
@@ -13,9 +13,15 @@ export const transformSelectedFacet = ({ key, value }: SelectedFacet) => {
13
13
  switch (key) {
14
14
  case 'channel': {
15
15
  const channel = ChannelMarshal.parse(value)
16
+ const channelFacets = [
17
+ { key: 'trade-policy', value: channel.salesChannel },
18
+ ]
16
19
 
17
- // This array should have all values from channel string
18
- return [{ key: 'trade-policy', value: channel.salesChannel }]
20
+ if (channel.regionId) {
21
+ channelFacets.push({ key: 'region-id', value: channel.regionId })
22
+ }
23
+
24
+ return channelFacets
19
25
  }
20
26
 
21
27
  default:
@@ -0,0 +1,10 @@
1
+ import type {
2
+ CommertialOffer,
3
+ Item,
4
+ } from '../clients/search/types/ProductSearchResult'
5
+ import { getFirstSeller } from './productStock'
6
+
7
+ export const getItemPriceByKey = (
8
+ item: Item,
9
+ key: keyof CommertialOffer
10
+ ): number => getFirstSeller(item.sellers)?.commertialOffer[key] ?? 0
@@ -0,0 +1,25 @@
1
+ import type { Item, Seller } from '../clients/search/types/ProductSearchResult'
2
+ import { getItemPriceByKey } from './price'
3
+
4
+ export const inStock = (item: Item) =>
5
+ item.sellers.find((seller) => seller.commertialOffer.AvailableQuantity > 0)
6
+
7
+ export const getFirstSeller = (sellers: Seller[]): Seller | undefined =>
8
+ sellers[0]
9
+
10
+ // Smallest Available Selling Price First
11
+ export const sortOfferByPrice = (items: Item[]): Item[] =>
12
+ items.sort((a, b) => {
13
+ if (inStock(a) && !inStock(b)) {
14
+ return -1
15
+ }
16
+
17
+ if (!inStock(a) && inStock(b)) {
18
+ return 1
19
+ }
20
+
21
+ return getItemPriceByKey(a, 'Price') - getItemPriceByKey(b, 'Price')
22
+ })
23
+
24
+ export const inStockOrderFormItem = (availability: string) =>
25
+ availability === 'available'
@@ -1 +1,5 @@
1
1
  export type Platform = 'vtex'
2
+
3
+ export type PromiseType<T> = T extends Promise<infer U> ? U : T
4
+
5
+ export type ArrayElementType<T> = T extends Array<infer U> ? U : T