@decocms/apps 0.23.3 → 0.24.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.
Files changed (110) hide show
  1. package/LICENSE +21 -0
  2. package/commerce/components/Image.tsx +129 -143
  3. package/commerce/components/JsonLd.tsx +192 -201
  4. package/commerce/components/Picture.tsx +65 -75
  5. package/commerce/sdk/analytics.ts +15 -15
  6. package/commerce/sdk/formatPrice.ts +13 -16
  7. package/commerce/sdk/url.ts +7 -7
  8. package/commerce/sdk/useOffer.ts +46 -57
  9. package/commerce/sdk/useVariantPossibilities.ts +25 -25
  10. package/commerce/types/commerce.ts +868 -875
  11. package/commerce/utils/canonical.ts +5 -8
  12. package/commerce/utils/constants.ts +5 -6
  13. package/commerce/utils/filters.ts +4 -4
  14. package/commerce/utils/productToAnalyticsItem.ts +52 -56
  15. package/commerce/utils/stateByZip.ts +42 -42
  16. package/package.json +23 -4
  17. package/shopify/actions/cart/addItems.ts +24 -25
  18. package/shopify/actions/cart/updateCoupons.ts +19 -20
  19. package/shopify/actions/cart/updateItems.ts +19 -20
  20. package/shopify/actions/user/signIn.ts +25 -30
  21. package/shopify/actions/user/signUp.ts +19 -24
  22. package/shopify/client.ts +24 -24
  23. package/shopify/index.ts +20 -18
  24. package/shopify/init.ts +18 -21
  25. package/shopify/loaders/ProductDetailsPage.ts +16 -20
  26. package/shopify/loaders/ProductList.ts +66 -69
  27. package/shopify/loaders/ProductListingPage.ts +150 -158
  28. package/shopify/loaders/RelatedProducts.ts +24 -27
  29. package/shopify/loaders/cart.ts +53 -52
  30. package/shopify/loaders/shop.ts +22 -27
  31. package/shopify/loaders/user.ts +27 -32
  32. package/shopify/utils/admin/admin.ts +33 -34
  33. package/shopify/utils/admin/queries.ts +2 -2
  34. package/shopify/utils/cart.ts +18 -14
  35. package/shopify/utils/cookies.ts +62 -65
  36. package/shopify/utils/enums.ts +424 -424
  37. package/shopify/utils/graphql.ts +44 -55
  38. package/shopify/utils/storefront/queries.ts +24 -29
  39. package/shopify/utils/storefront/storefront.graphql.gen.ts +55 -55
  40. package/shopify/utils/transform.ts +370 -376
  41. package/shopify/utils/types.ts +118 -118
  42. package/shopify/utils/user.ts +11 -11
  43. package/shopify/utils/utils.ts +135 -140
  44. package/vtex/actions/address.ts +86 -86
  45. package/vtex/actions/auth.ts +14 -27
  46. package/vtex/actions/checkout.ts +36 -49
  47. package/vtex/actions/masterData.ts +10 -27
  48. package/vtex/actions/misc.ts +101 -111
  49. package/vtex/actions/newsletter.ts +48 -52
  50. package/vtex/actions/orders.ts +13 -16
  51. package/vtex/actions/profile.ts +55 -55
  52. package/vtex/actions/session.ts +36 -35
  53. package/vtex/actions/trigger.ts +25 -25
  54. package/vtex/actions/wishlist.ts +51 -53
  55. package/vtex/client.ts +14 -42
  56. package/vtex/hooks/index.ts +4 -4
  57. package/vtex/hooks/useAutocomplete.ts +42 -48
  58. package/vtex/hooks/useCart.ts +153 -165
  59. package/vtex/hooks/useUser.ts +40 -40
  60. package/vtex/hooks/useWishlist.ts +70 -70
  61. package/vtex/inline-loaders/productDetailsPage.ts +1 -3
  62. package/vtex/inline-loaders/productList.ts +121 -127
  63. package/vtex/inline-loaders/productListingPage.ts +10 -34
  64. package/vtex/inline-loaders/relatedProducts.ts +1 -3
  65. package/vtex/inline-loaders/suggestions.ts +36 -39
  66. package/vtex/inline-loaders/workflowProducts.ts +45 -49
  67. package/vtex/invoke.ts +159 -194
  68. package/vtex/loaders/address.ts +49 -54
  69. package/vtex/loaders/brands.ts +19 -26
  70. package/vtex/loaders/cart.ts +24 -21
  71. package/vtex/loaders/catalog.ts +51 -53
  72. package/vtex/loaders/collections.ts +25 -27
  73. package/vtex/loaders/legacy.ts +487 -534
  74. package/vtex/loaders/logistics.ts +33 -37
  75. package/vtex/loaders/navbar.ts +5 -8
  76. package/vtex/loaders/orders.ts +28 -39
  77. package/vtex/loaders/pageType.ts +41 -35
  78. package/vtex/loaders/payment.ts +27 -37
  79. package/vtex/loaders/profile.ts +38 -38
  80. package/vtex/loaders/promotion.ts +5 -8
  81. package/vtex/loaders/search.ts +56 -59
  82. package/vtex/loaders/session.ts +22 -30
  83. package/vtex/loaders/user.ts +39 -41
  84. package/vtex/loaders/wishlist.ts +35 -35
  85. package/vtex/loaders/wishlistProducts.ts +3 -15
  86. package/vtex/loaders/workflow.ts +220 -227
  87. package/vtex/middleware.ts +116 -119
  88. package/vtex/types.ts +201 -201
  89. package/vtex/utils/batch.ts +13 -16
  90. package/vtex/utils/cookies.ts +76 -80
  91. package/vtex/utils/enrichment.ts +62 -42
  92. package/vtex/utils/fetchCache.ts +1 -4
  93. package/vtex/utils/index.ts +6 -6
  94. package/vtex/utils/intelligentSearch.ts +48 -57
  95. package/vtex/utils/legacy.ts +108 -124
  96. package/vtex/utils/pickAndOmit.ts +15 -20
  97. package/vtex/utils/proxy.ts +136 -146
  98. package/vtex/utils/resourceRange.ts +3 -3
  99. package/vtex/utils/segment.ts +100 -111
  100. package/vtex/utils/similars.ts +1 -2
  101. package/vtex/utils/sitemap.ts +91 -91
  102. package/vtex/utils/slugCache.ts +2 -6
  103. package/vtex/utils/slugify.ts +9 -9
  104. package/vtex/utils/transform.ts +1012 -1105
  105. package/vtex/utils/types.ts +1381 -1381
  106. package/vtex/utils/vtexId.ts +44 -47
  107. package/.github/workflows/release.yml +0 -34
  108. package/.releaserc.json +0 -28
  109. package/knip.json +0 -19
  110. package/tsconfig.json +0 -11
@@ -5,159 +5,153 @@
5
5
  * Supports: query search, collection IDs, SKU IDs, facets, sort, and
6
6
  * hideUnavailableItems — matching the original productList.ts behavior.
7
7
  */
8
- import { intelligentSearch, getVtexConfig, toFacetPath } from "../client";
9
- import { toProduct, pickSku, sortProducts } from "../utils/transform";
10
- import type { Product as ProductVTEX } from "../utils/types";
8
+
11
9
  import type { Product } from "../../commerce/types/commerce";
10
+ import { getVtexConfig, intelligentSearch, toFacetPath } from "../client";
11
+ import { pickSku, sortProducts, toProduct } from "../utils/transform";
12
+ import type { Product as ProductVTEX } from "../utils/types";
12
13
 
13
14
  export interface ProductListProps {
14
- props?: CollectionProps | QueryProps | ProductIDProps | FacetsProps;
15
- query?: string;
16
- count?: number;
17
- sort?: string;
18
- collection?: string;
19
- hideUnavailableItems?: boolean;
15
+ props?: CollectionProps | QueryProps | ProductIDProps | FacetsProps;
16
+ query?: string;
17
+ count?: number;
18
+ sort?: string;
19
+ collection?: string;
20
+ hideUnavailableItems?: boolean;
20
21
  }
21
22
 
22
23
  interface CollectionProps {
23
- collection: string;
24
- count?: number;
25
- sort?: string;
26
- hideUnavailableItems?: boolean;
24
+ collection: string;
25
+ count?: number;
26
+ sort?: string;
27
+ hideUnavailableItems?: boolean;
27
28
  }
28
29
 
29
30
  interface QueryProps {
30
- query: string;
31
- count?: number;
32
- sort?: string;
33
- fuzzy?: string;
34
- hideUnavailableItems?: boolean;
31
+ query: string;
32
+ count?: number;
33
+ sort?: string;
34
+ fuzzy?: string;
35
+ hideUnavailableItems?: boolean;
35
36
  }
36
37
 
37
38
  interface ProductIDProps {
38
- ids: string[];
39
- hideUnavailableItems?: boolean;
39
+ ids: string[];
40
+ hideUnavailableItems?: boolean;
40
41
  }
41
42
 
42
43
  interface FacetsProps {
43
- query?: string;
44
- facets: string;
45
- count?: number;
46
- sort?: string;
47
- hideUnavailableItems?: boolean;
44
+ query?: string;
45
+ facets: string;
46
+ count?: number;
47
+ sort?: string;
48
+ hideUnavailableItems?: boolean;
48
49
  }
49
50
 
50
51
  function isCollectionProps(p: any): p is CollectionProps {
51
- return typeof p?.collection === "string";
52
+ return typeof p?.collection === "string";
52
53
  }
53
54
  function isProductIDProps(p: any): p is ProductIDProps {
54
- return Array.isArray(p?.ids) && p.ids.length > 0;
55
+ return Array.isArray(p?.ids) && p.ids.length > 0;
55
56
  }
56
57
  function isFacetsProps(p: any): p is FacetsProps {
57
- return typeof p?.facets === "string";
58
+ return typeof p?.facets === "string";
58
59
  }
59
60
 
60
61
  function resolveParams(props: ProductListProps): {
61
- query: string;
62
- count: number;
63
- sort: string;
64
- facetPath: string;
65
- fuzzy?: string;
66
- hideUnavailableItems: boolean;
67
- ids?: string[];
62
+ query: string;
63
+ count: number;
64
+ sort: string;
65
+ facetPath: string;
66
+ fuzzy?: string;
67
+ hideUnavailableItems: boolean;
68
+ ids?: string[];
68
69
  } {
69
- const inner = props.props ?? props;
70
-
71
- if (isProductIDProps(inner)) {
72
- return {
73
- query: `sku:${inner.ids.join(";")}`,
74
- count: inner.ids.length,
75
- sort: "",
76
- facetPath: "",
77
- hideUnavailableItems: inner.hideUnavailableItems ?? false,
78
- ids: inner.ids,
79
- };
80
- }
81
-
82
- if (isFacetsProps(inner)) {
83
- return {
84
- query: inner.query ?? "",
85
- count: inner.count ?? 12,
86
- sort: inner.sort ?? "",
87
- facetPath: inner.facets,
88
- hideUnavailableItems: inner.hideUnavailableItems ?? false,
89
- };
90
- }
91
-
92
- if (isCollectionProps(inner)) {
93
- return {
94
- query: "",
95
- count: inner.count ?? 12,
96
- sort: inner.sort ?? "",
97
- facetPath: toFacetPath([{ key: "productClusterIds", value: inner.collection }]),
98
- hideUnavailableItems: inner.hideUnavailableItems ?? false,
99
- };
100
- }
101
-
102
- return {
103
- query: (inner as any).query ?? "",
104
- count: (inner as any).count ?? 12,
105
- sort: (inner as any).sort ?? "",
106
- facetPath: "",
107
- fuzzy: (inner as any).fuzzy,
108
- hideUnavailableItems: (inner as any).hideUnavailableItems ?? false,
109
- };
70
+ const inner = props.props ?? props;
71
+
72
+ if (isProductIDProps(inner)) {
73
+ return {
74
+ query: `sku:${inner.ids.join(";")}`,
75
+ count: inner.ids.length,
76
+ sort: "",
77
+ facetPath: "",
78
+ hideUnavailableItems: inner.hideUnavailableItems ?? false,
79
+ ids: inner.ids,
80
+ };
81
+ }
82
+
83
+ if (isFacetsProps(inner)) {
84
+ return {
85
+ query: inner.query ?? "",
86
+ count: inner.count ?? 12,
87
+ sort: inner.sort ?? "",
88
+ facetPath: inner.facets,
89
+ hideUnavailableItems: inner.hideUnavailableItems ?? false,
90
+ };
91
+ }
92
+
93
+ if (isCollectionProps(inner)) {
94
+ return {
95
+ query: "",
96
+ count: inner.count ?? 12,
97
+ sort: inner.sort ?? "",
98
+ facetPath: toFacetPath([{ key: "productClusterIds", value: inner.collection }]),
99
+ hideUnavailableItems: inner.hideUnavailableItems ?? false,
100
+ };
101
+ }
102
+
103
+ return {
104
+ query: (inner as any).query ?? "",
105
+ count: (inner as any).count ?? 12,
106
+ sort: (inner as any).sort ?? "",
107
+ facetPath: "",
108
+ fuzzy: (inner as any).fuzzy,
109
+ hideUnavailableItems: (inner as any).hideUnavailableItems ?? false,
110
+ };
110
111
  }
111
112
 
112
- export default async function vtexProductList(
113
- props: ProductListProps,
114
- ): Promise<Product[] | null> {
115
- try {
116
- const { query, count, sort, facetPath, fuzzy, hideUnavailableItems, ids } =
117
- resolveParams(props);
118
-
119
- const config = getVtexConfig();
120
- const locale = config.locale ?? "pt-BR";
121
-
122
- const params: Record<string, string> = {
123
- page: "1",
124
- count: String(count),
125
- locale,
126
- hideUnavailableItems: String(hideUnavailableItems),
127
- };
128
- if (query) params.query = query;
129
- if (sort) params.sort = sort;
130
- if (fuzzy) params.fuzzy = fuzzy;
131
-
132
- const endpoint = facetPath
133
- ? `/product_search/${facetPath}`
134
- : "/product_search/";
135
-
136
- const data = await intelligentSearch<{ products: ProductVTEX[] }>(
137
- endpoint,
138
- params,
139
- );
140
-
141
- const vtexProducts = data.products ?? [];
142
- const baseUrl = config.publicUrl
143
- ? `https://${config.publicUrl}`
144
- : `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}`;
145
-
146
- let products = vtexProducts.map((p) => {
147
- const fetchedSkus = ids ? new Set(ids) : null;
148
- const preferredSku = fetchedSkus
149
- ? p.items.find((item) => fetchedSkus.has(item.itemId)) ?? pickSku(p)
150
- : pickSku(p);
151
- return toProduct(p, preferredSku, 0, { baseUrl, priceCurrency: "BRL" });
152
- });
153
-
154
- if (ids) {
155
- products = sortProducts(products, ids, "sku");
156
- }
157
-
158
- return products;
159
- } catch (error) {
160
- console.error("[VTEX] ProductList error:", error);
161
- return null;
162
- }
113
+ export default async function vtexProductList(props: ProductListProps): Promise<Product[] | null> {
114
+ try {
115
+ const { query, count, sort, facetPath, fuzzy, hideUnavailableItems, ids } =
116
+ resolveParams(props);
117
+
118
+ const config = getVtexConfig();
119
+ const locale = config.locale ?? "pt-BR";
120
+
121
+ const params: Record<string, string> = {
122
+ page: "1",
123
+ count: String(count),
124
+ locale,
125
+ hideUnavailableItems: String(hideUnavailableItems),
126
+ };
127
+ if (query) params.query = query;
128
+ if (sort) params.sort = sort;
129
+ if (fuzzy) params.fuzzy = fuzzy;
130
+
131
+ const endpoint = facetPath ? `/product_search/${facetPath}` : "/product_search/";
132
+
133
+ const data = await intelligentSearch<{ products: ProductVTEX[] }>(endpoint, params);
134
+
135
+ const vtexProducts = data.products ?? [];
136
+ const baseUrl = config.publicUrl
137
+ ? `https://${config.publicUrl}`
138
+ : `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}`;
139
+
140
+ let products = vtexProducts.map((p) => {
141
+ const fetchedSkus = ids ? new Set(ids) : null;
142
+ const preferredSku = fetchedSkus
143
+ ? (p.items.find((item) => fetchedSkus.has(item.itemId)) ?? pickSku(p))
144
+ : pickSku(p);
145
+ return toProduct(p, preferredSku, 0, { baseUrl, priceCurrency: "BRL" });
146
+ });
147
+
148
+ if (ids) {
149
+ products = sortProducts(products, ids, "sku");
150
+ }
151
+
152
+ return products;
153
+ } catch (error) {
154
+ console.error("[VTEX] ProductList error:", error);
155
+ return null;
156
+ }
163
157
  }
@@ -149,18 +149,13 @@ function facetToToggle(
149
149
  };
150
150
  }
151
151
 
152
- function toFilter(
153
- selectedFacets: SelectedFacet[],
154
- paramsToPersist?: URLSearchParams,
155
- ) {
152
+ function toFilter(selectedFacets: SelectedFacet[], paramsToPersist?: URLSearchParams) {
156
153
  return (facet: ISFacet) => ({
157
154
  "@type": "FilterToggle" as const,
158
155
  key: facet.key,
159
156
  label: facet.name,
160
157
  quantity: facet.quantity,
161
- values: facet.values.map(
162
- facetToToggle(selectedFacets, facet.key, paramsToPersist),
163
- ),
158
+ values: facet.values.map(facetToToggle(selectedFacets, facet.key, paramsToPersist)),
164
159
  });
165
160
  }
166
161
 
@@ -169,9 +164,7 @@ function toFilter(
169
164
  function pageTypesToBreadcrumb(pageTypes: PageType[]) {
170
165
  const filtered = pageTypes.filter(
171
166
  (pt) =>
172
- pt.pageType === "Category" ||
173
- pt.pageType === "Department" ||
174
- pt.pageType === "SubCategory",
167
+ pt.pageType === "Category" || pt.pageType === "Department" || pt.pageType === "SubCategory",
175
168
  );
176
169
  return filtered.map((page, index) => {
177
170
  const position = index + 1;
@@ -268,12 +261,8 @@ function isValidPLPPath(path: string): boolean {
268
261
  * 4. Transform facets to FilterToggle format
269
262
  * 5. Build pagination from IS response
270
263
  */
271
- export default async function vtexProductListingPage(
272
- props: PLPProps,
273
- ): Promise<any | null> {
274
- const pageUrl = props.__pageUrl
275
- ? new URL(props.__pageUrl, "https://localhost")
276
- : null;
264
+ export default async function vtexProductListingPage(props: PLPProps): Promise<any | null> {
265
+ const pageUrl = props.__pageUrl ? new URL(props.__pageUrl, "https://localhost") : null;
277
266
 
278
267
  const query = props.query ?? pageUrl?.searchParams.get("q") ?? "";
279
268
  const countFromUrl = pageUrl?.searchParams.get("PS");
@@ -283,21 +272,14 @@ export default async function vtexProductListingPage(
283
272
  const fuzzy = props.fuzzy ?? pageUrl?.searchParams.get("fuzzy") ?? undefined;
284
273
  const pageFromUrl = pageUrl?.searchParams.get("page");
285
274
  const rawPage = props.page ?? (pageFromUrl ? Number(pageFromUrl) - 1 : 0);
286
- const page =
287
- Number.isFinite(rawPage) && rawPage >= 0 ? Math.floor(rawPage) : 0;
275
+ const page = Number.isFinite(rawPage) && rawPage >= 0 ? Math.floor(rawPage) : 0;
288
276
 
289
- const {
290
- selectedFacets: cmsSelectedFacets,
291
- hideUnavailableItems = false,
292
- __pagePath,
293
- } = props;
277
+ const { selectedFacets: cmsSelectedFacets, hideUnavailableItems = false, __pagePath } = props;
294
278
 
295
279
  try {
296
280
  // 1. Resolve selected facets (CMS + URL filter.* params, matching original)
297
281
  let facets: SelectedFacet[] =
298
- cmsSelectedFacets && cmsSelectedFacets.length > 0
299
- ? [...cmsSelectedFacets]
300
- : [];
282
+ cmsSelectedFacets && cmsSelectedFacets.length > 0 ? [...cmsSelectedFacets] : [];
301
283
 
302
284
  // Extract filter.* params from URL (e.g. filter.category-1=telhas)
303
285
  if (pageUrl) {
@@ -344,9 +326,7 @@ export default async function vtexProductListingPage(
344
326
  hideUnavailableItems,
345
327
  });
346
328
 
347
- const productEndpoint = facetPath
348
- ? `/product_search/${facetPath}`
349
- : "/product_search/";
329
+ const productEndpoint = facetPath ? `/product_search/${facetPath}` : "/product_search/";
350
330
 
351
331
  const facetsEndpoint = facetPath ? `/facets/${facetPath}` : "/facets/";
352
332
 
@@ -356,11 +336,7 @@ export default async function vtexProductListingPage(
356
336
  intelligentSearch<ISFacetsResult>(facetsEndpoint, params),
357
337
  ]);
358
338
 
359
- const {
360
- products: vtexProducts,
361
- pagination,
362
- recordsFiltered,
363
- } = productsResult;
339
+ const { products: vtexProducts, pagination, recordsFiltered } = productsResult;
364
340
 
365
341
  // 3. Transform products using shared transform pipeline (same as deco-cx/apps)
366
342
  const baseUrl = config.publicUrl
@@ -69,9 +69,7 @@ export default async function vtexRelatedProducts(
69
69
 
70
70
  if (props.hideUnavailableItems) {
71
71
  result = result.filter((p) =>
72
- p.offers?.offers?.some(
73
- (o) => o.availability === "https://schema.org/InStock",
74
- ),
72
+ p.offers?.offers?.some((o) => o.availability === "https://schema.org/InStock"),
75
73
  );
76
74
  }
77
75
 
@@ -2,48 +2,45 @@
2
2
  * IS autocomplete suggestions loader.
3
3
  * Maps VTEX IS response to commerce Suggestion type.
4
4
  */
5
- import { intelligentSearch, getVtexConfig, toFacetPath } from "../client";
6
- import { toProduct, pickSku } from "../utils/transform";
5
+
6
+ import type { Product, Suggestion } from "../../commerce/types/commerce";
7
+ import { getVtexConfig, intelligentSearch } from "../client";
8
+ import { pickSku, toProduct } from "../utils/transform";
7
9
  import type { Product as ProductVTEX } from "../utils/types";
8
- import type { Suggestion, Product } from "../../commerce/types/commerce";
9
10
 
10
11
  export interface SuggestionsProps {
11
- query?: string;
12
- count?: number;
12
+ query?: string;
13
+ count?: number;
13
14
  }
14
15
 
15
- export default async function vtexSuggestions(
16
- props: SuggestionsProps,
17
- ): Promise<Suggestion | null> {
18
- const query = props.query || "";
19
- if (!query.trim()) return { searches: [], products: [] };
20
-
21
- try {
22
- const data = await intelligentSearch<{
23
- searches: Array<{ term: string; count: number }>;
24
- products: ProductVTEX[];
25
- }>("/autocomplete_suggestions/", { query });
26
-
27
- const searches = (data.searches ?? []).map((s) => ({
28
- term: s.term,
29
- hits: s.count || 0,
30
- }));
31
-
32
- const config = getVtexConfig();
33
- const baseUrl = config.publicUrl
34
- ? `https://${config.publicUrl}`
35
- : `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}`;
36
-
37
- const products: Product[] = (data.products ?? [])
38
- .slice(0, props.count ?? 4)
39
- .map((p) => {
40
- const sku = pickSku(p);
41
- return toProduct(p, sku, 0, { baseUrl, priceCurrency: "BRL" });
42
- });
43
-
44
- return { searches, products };
45
- } catch (error) {
46
- console.error("[VTEX] Suggestions error:", error);
47
- return null;
48
- }
16
+ export default async function vtexSuggestions(props: SuggestionsProps): Promise<Suggestion | null> {
17
+ const query = props.query || "";
18
+ if (!query.trim()) return { searches: [], products: [] };
19
+
20
+ try {
21
+ const data = await intelligentSearch<{
22
+ searches: Array<{ term: string; count: number }>;
23
+ products: ProductVTEX[];
24
+ }>("/autocomplete_suggestions/", { query });
25
+
26
+ const searches = (data.searches ?? []).map((s) => ({
27
+ term: s.term,
28
+ hits: s.count || 0,
29
+ }));
30
+
31
+ const config = getVtexConfig();
32
+ const baseUrl = config.publicUrl
33
+ ? `https://${config.publicUrl}`
34
+ : `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}`;
35
+
36
+ const products: Product[] = (data.products ?? []).slice(0, props.count ?? 4).map((p) => {
37
+ const sku = pickSku(p);
38
+ return toProduct(p, sku, 0, { baseUrl, priceCurrency: "BRL" });
39
+ });
40
+
41
+ return { searches, products };
42
+ } catch (error) {
43
+ console.error("[VTEX] Suggestions error:", error);
44
+ return null;
45
+ }
49
46
  }
@@ -2,67 +2,63 @@
2
2
  * Workflow/collection products loader using Intelligent Search + shared transform.
3
3
  * Maps IS response to schema.org Product[] following deco-cx/apps pattern.
4
4
  */
5
- import { intelligentSearch, getVtexConfig, toFacetPath } from "../client";
6
- import { toProduct, pickSku } from "../utils/transform";
7
- import type { Product as ProductVTEX } from "../utils/types";
5
+
8
6
  import type { Product } from "../../commerce/types/commerce";
7
+ import { getVtexConfig, intelligentSearch, toFacetPath } from "../client";
8
+ import { pickSku, toProduct } from "../utils/transform";
9
+ import type { Product as ProductVTEX } from "../utils/types";
9
10
 
10
11
  export interface WorkflowProductsProps {
11
- props?: {
12
- query?: string;
13
- count?: number;
14
- sort?: string;
15
- collection?: string;
16
- };
17
- page?: number;
18
- pagesize?: number;
12
+ props?: {
13
+ query?: string;
14
+ count?: number;
15
+ sort?: string;
16
+ collection?: string;
17
+ };
18
+ page?: number;
19
+ pagesize?: number;
19
20
  }
20
21
 
21
22
  export default async function vtexWorkflowProducts(
22
- props: WorkflowProductsProps,
23
+ props: WorkflowProductsProps,
23
24
  ): Promise<Product[] | null> {
24
- const inner = props.props ?? props;
25
- const collection = (inner as any).collection;
26
- const query = (inner as any).query ?? "";
27
- const count = (inner as any).count ?? props.pagesize ?? 12;
28
- const sort = (inner as any).sort ?? "";
25
+ const inner = props.props ?? props;
26
+ const collection = (inner as any).collection;
27
+ const query = (inner as any).query ?? "";
28
+ const count = (inner as any).count ?? props.pagesize ?? 12;
29
+ const sort = (inner as any).sort ?? "";
29
30
 
30
- try {
31
- const config = getVtexConfig();
32
- const locale = config.locale ?? "pt-BR";
31
+ try {
32
+ const config = getVtexConfig();
33
+ const locale = config.locale ?? "pt-BR";
33
34
 
34
- const params: Record<string, string> = {
35
- count: String(count),
36
- locale,
37
- page: String((props.page ?? 0) + 1),
38
- };
39
- if (query) params.query = query;
40
- if (sort) params.sort = sort;
35
+ const params: Record<string, string> = {
36
+ count: String(count),
37
+ locale,
38
+ page: String((props.page ?? 0) + 1),
39
+ };
40
+ if (query) params.query = query;
41
+ if (sort) params.sort = sort;
41
42
 
42
- const facetPath = collection
43
- ? toFacetPath([{ key: "productClusterIds", value: collection }])
44
- : "";
43
+ const facetPath = collection
44
+ ? toFacetPath([{ key: "productClusterIds", value: collection }])
45
+ : "";
45
46
 
46
- const endpoint = facetPath
47
- ? `/product_search/${facetPath}`
48
- : "/product_search/";
47
+ const endpoint = facetPath ? `/product_search/${facetPath}` : "/product_search/";
49
48
 
50
- const data = await intelligentSearch<{ products: ProductVTEX[] }>(
51
- endpoint,
52
- params,
53
- );
49
+ const data = await intelligentSearch<{ products: ProductVTEX[] }>(endpoint, params);
54
50
 
55
- const products = data.products ?? [];
56
- const baseUrl = config.publicUrl
57
- ? `https://${config.publicUrl}`
58
- : `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}`;
51
+ const products = data.products ?? [];
52
+ const baseUrl = config.publicUrl
53
+ ? `https://${config.publicUrl}`
54
+ : `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}`;
59
55
 
60
- return products.map((p) => {
61
- const sku = pickSku(p);
62
- return toProduct(p, sku, 0, { baseUrl, priceCurrency: "BRL" });
63
- });
64
- } catch (error) {
65
- console.error("[VTEX] Workflow products error:", error);
66
- return null;
67
- }
56
+ return products.map((p) => {
57
+ const sku = pickSku(p);
58
+ return toProduct(p, sku, 0, { baseUrl, priceCurrency: "BRL" });
59
+ });
60
+ } catch (error) {
61
+ console.error("[VTEX] Workflow products error:", error);
62
+ return null;
63
+ }
68
64
  }