@decocms/apps 0.23.3 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/commerce/components/Image.tsx +129 -143
- package/commerce/components/JsonLd.tsx +192 -201
- package/commerce/components/Picture.tsx +65 -75
- package/commerce/sdk/analytics.ts +15 -15
- package/commerce/sdk/formatPrice.ts +13 -16
- package/commerce/sdk/url.ts +7 -7
- package/commerce/sdk/useOffer.ts +46 -57
- package/commerce/sdk/useVariantPossibilities.ts +25 -25
- package/commerce/types/commerce.ts +868 -875
- package/commerce/utils/canonical.ts +5 -8
- package/commerce/utils/constants.ts +5 -6
- package/commerce/utils/filters.ts +4 -4
- package/commerce/utils/productToAnalyticsItem.ts +52 -56
- package/commerce/utils/stateByZip.ts +42 -42
- package/package.json +24 -4
- package/shopify/actions/cart/addItems.ts +24 -25
- package/shopify/actions/cart/updateCoupons.ts +19 -20
- package/shopify/actions/cart/updateItems.ts +19 -20
- package/shopify/actions/user/signIn.ts +25 -30
- package/shopify/actions/user/signUp.ts +19 -24
- package/shopify/client.ts +24 -24
- package/shopify/index.ts +20 -18
- package/shopify/init.ts +18 -21
- package/shopify/loaders/ProductDetailsPage.ts +16 -20
- package/shopify/loaders/ProductList.ts +66 -69
- package/shopify/loaders/ProductListingPage.ts +150 -158
- package/shopify/loaders/RelatedProducts.ts +24 -27
- package/shopify/loaders/cart.ts +53 -52
- package/shopify/loaders/shop.ts +22 -27
- package/shopify/loaders/user.ts +27 -32
- package/shopify/utils/admin/admin.ts +33 -34
- package/shopify/utils/admin/queries.ts +2 -2
- package/shopify/utils/cart.ts +18 -14
- package/shopify/utils/cookies.ts +62 -65
- package/shopify/utils/enums.ts +424 -424
- package/shopify/utils/graphql.ts +44 -55
- package/shopify/utils/storefront/queries.ts +24 -29
- package/shopify/utils/storefront/storefront.graphql.gen.ts +55 -55
- package/shopify/utils/transform.ts +370 -376
- package/shopify/utils/types.ts +118 -118
- package/shopify/utils/user.ts +11 -11
- package/shopify/utils/utils.ts +135 -140
- package/vtex/actions/address.ts +86 -86
- package/vtex/actions/auth.ts +14 -27
- package/vtex/actions/checkout.ts +36 -49
- package/vtex/actions/masterData.ts +10 -27
- package/vtex/actions/misc.ts +101 -111
- package/vtex/actions/newsletter.ts +48 -52
- package/vtex/actions/orders.ts +13 -16
- package/vtex/actions/profile.ts +55 -55
- package/vtex/actions/session.ts +36 -35
- package/vtex/actions/trigger.ts +25 -25
- package/vtex/actions/wishlist.ts +51 -53
- package/vtex/client.ts +14 -42
- package/vtex/hooks/index.ts +4 -4
- package/vtex/hooks/useAutocomplete.ts +42 -48
- package/vtex/hooks/useCart.ts +153 -165
- package/vtex/hooks/useUser.ts +40 -40
- package/vtex/hooks/useWishlist.ts +70 -70
- package/vtex/inline-loaders/productDetailsPage.ts +1 -3
- package/vtex/inline-loaders/productList.ts +121 -127
- package/vtex/inline-loaders/productListShelf.ts +159 -0
- package/vtex/inline-loaders/productListingPage.ts +10 -34
- package/vtex/inline-loaders/relatedProducts.ts +1 -3
- package/vtex/inline-loaders/suggestions.ts +36 -39
- package/vtex/inline-loaders/workflowProducts.ts +45 -49
- package/vtex/invoke.ts +159 -194
- package/vtex/loaders/address.ts +49 -54
- package/vtex/loaders/brands.ts +19 -26
- package/vtex/loaders/cart.ts +24 -21
- package/vtex/loaders/catalog.ts +51 -53
- package/vtex/loaders/collections.ts +25 -27
- package/vtex/loaders/legacy.ts +487 -534
- package/vtex/loaders/logistics.ts +33 -37
- package/vtex/loaders/navbar.ts +5 -8
- package/vtex/loaders/orders.ts +28 -39
- package/vtex/loaders/pageType.ts +41 -35
- package/vtex/loaders/payment.ts +27 -37
- package/vtex/loaders/profile.ts +38 -38
- package/vtex/loaders/promotion.ts +5 -8
- package/vtex/loaders/search.ts +56 -59
- package/vtex/loaders/session.ts +22 -30
- package/vtex/loaders/user.ts +39 -41
- package/vtex/loaders/wishlist.ts +35 -35
- package/vtex/loaders/wishlistProducts.ts +3 -15
- package/vtex/loaders/workflow.ts +220 -227
- package/vtex/middleware.ts +116 -119
- package/vtex/types.ts +201 -201
- package/vtex/utils/batch.ts +13 -16
- package/vtex/utils/cookies.ts +76 -80
- package/vtex/utils/enrichment.ts +62 -42
- package/vtex/utils/fetchCache.ts +1 -4
- package/vtex/utils/index.ts +6 -6
- package/vtex/utils/intelligentSearch.ts +48 -57
- package/vtex/utils/legacy.ts +108 -124
- package/vtex/utils/pickAndOmit.ts +15 -20
- package/vtex/utils/proxy.ts +136 -146
- package/vtex/utils/resourceRange.ts +3 -3
- package/vtex/utils/segment.ts +100 -111
- package/vtex/utils/similars.ts +1 -2
- package/vtex/utils/sitemap.ts +91 -91
- package/vtex/utils/slugCache.ts +2 -6
- package/vtex/utils/slugify.ts +9 -9
- package/vtex/utils/transform.ts +1178 -1105
- package/vtex/utils/types.ts +1381 -1381
- package/vtex/utils/vtexId.ts +44 -47
- package/.github/workflows/release.yml +0 -34
- package/.releaserc.json +0 -28
- package/knip.json +0 -19
- 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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
collection: string;
|
|
25
|
+
count?: number;
|
|
26
|
+
sort?: string;
|
|
27
|
+
hideUnavailableItems?: boolean;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
interface QueryProps {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
query: string;
|
|
32
|
+
count?: number;
|
|
33
|
+
sort?: string;
|
|
34
|
+
fuzzy?: string;
|
|
35
|
+
hideUnavailableItems?: boolean;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
interface ProductIDProps {
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
ids: string[];
|
|
40
|
+
hideUnavailableItems?: boolean;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
interface FacetsProps {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
52
|
+
return typeof p?.collection === "string";
|
|
52
53
|
}
|
|
53
54
|
function isProductIDProps(p: any): p is ProductIDProps {
|
|
54
|
-
|
|
55
|
+
return Array.isArray(p?.ids) && p.ids.length > 0;
|
|
55
56
|
}
|
|
56
57
|
function isFacetsProps(p: any): p is FacetsProps {
|
|
57
|
-
|
|
58
|
+
return typeof p?.facets === "string";
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
function resolveParams(props: ProductListProps): {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
query: string;
|
|
63
|
+
count: number;
|
|
64
|
+
sort: string;
|
|
65
|
+
facetPath: string;
|
|
66
|
+
fuzzy?: string;
|
|
67
|
+
hideUnavailableItems: boolean;
|
|
68
|
+
ids?: string[];
|
|
68
69
|
} {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lean product list loader for shelf/card display.
|
|
3
|
+
* Same API call as productList.ts but uses toProductShelf() for ~90% smaller payloads.
|
|
4
|
+
*
|
|
5
|
+
* Use this loader for ProductShelf sections where only card-level data is needed
|
|
6
|
+
* (name, URL, images, price, installments, PIX, availability, brand).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Product } from "../../commerce/types/commerce";
|
|
10
|
+
import { getVtexConfig, intelligentSearch, toFacetPath } from "../client";
|
|
11
|
+
import { pickSku, sortProducts, toProductShelf } from "../utils/transform";
|
|
12
|
+
import type { Product as ProductVTEX } from "../utils/types";
|
|
13
|
+
|
|
14
|
+
export interface ProductListProps {
|
|
15
|
+
props?: CollectionProps | QueryProps | ProductIDProps | FacetsProps;
|
|
16
|
+
query?: string;
|
|
17
|
+
count?: number;
|
|
18
|
+
sort?: string;
|
|
19
|
+
collection?: string;
|
|
20
|
+
hideUnavailableItems?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface CollectionProps {
|
|
24
|
+
collection: string;
|
|
25
|
+
count?: number;
|
|
26
|
+
sort?: string;
|
|
27
|
+
hideUnavailableItems?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface QueryProps {
|
|
31
|
+
query: string;
|
|
32
|
+
count?: number;
|
|
33
|
+
sort?: string;
|
|
34
|
+
fuzzy?: string;
|
|
35
|
+
hideUnavailableItems?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface ProductIDProps {
|
|
39
|
+
ids: string[];
|
|
40
|
+
hideUnavailableItems?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface FacetsProps {
|
|
44
|
+
query?: string;
|
|
45
|
+
facets: string;
|
|
46
|
+
count?: number;
|
|
47
|
+
sort?: string;
|
|
48
|
+
hideUnavailableItems?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isCollectionProps(p: any): p is CollectionProps {
|
|
52
|
+
return typeof p?.collection === "string";
|
|
53
|
+
}
|
|
54
|
+
function isProductIDProps(p: any): p is ProductIDProps {
|
|
55
|
+
return Array.isArray(p?.ids) && p.ids.length > 0;
|
|
56
|
+
}
|
|
57
|
+
function isFacetsProps(p: any): p is FacetsProps {
|
|
58
|
+
return typeof p?.facets === "string";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function resolveParams(props: ProductListProps): {
|
|
62
|
+
query: string;
|
|
63
|
+
count: number;
|
|
64
|
+
sort: string;
|
|
65
|
+
facetPath: string;
|
|
66
|
+
fuzzy?: string;
|
|
67
|
+
hideUnavailableItems: boolean;
|
|
68
|
+
ids?: string[];
|
|
69
|
+
} {
|
|
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
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default async function vtexProductListShelf(
|
|
114
|
+
props: ProductListProps,
|
|
115
|
+
): Promise<Product[] | null> {
|
|
116
|
+
try {
|
|
117
|
+
const { query, count, sort, facetPath, fuzzy, hideUnavailableItems, ids } =
|
|
118
|
+
resolveParams(props);
|
|
119
|
+
|
|
120
|
+
const config = getVtexConfig();
|
|
121
|
+
const locale = config.locale ?? "pt-BR";
|
|
122
|
+
|
|
123
|
+
const params: Record<string, string> = {
|
|
124
|
+
page: "1",
|
|
125
|
+
count: String(count),
|
|
126
|
+
locale,
|
|
127
|
+
hideUnavailableItems: String(hideUnavailableItems),
|
|
128
|
+
};
|
|
129
|
+
if (query) params.query = query;
|
|
130
|
+
if (sort) params.sort = sort;
|
|
131
|
+
if (fuzzy) params.fuzzy = fuzzy;
|
|
132
|
+
|
|
133
|
+
const endpoint = facetPath ? `/product_search/${facetPath}` : "/product_search/";
|
|
134
|
+
|
|
135
|
+
const data = await intelligentSearch<{ products: ProductVTEX[] }>(endpoint, params);
|
|
136
|
+
|
|
137
|
+
const vtexProducts = data.products ?? [];
|
|
138
|
+
const baseUrl = config.publicUrl
|
|
139
|
+
? `https://${config.publicUrl}`
|
|
140
|
+
: `https://${config.account}.vtexcommercestable.${config.domain ?? "com.br"}`;
|
|
141
|
+
|
|
142
|
+
let products = vtexProducts.map((p) => {
|
|
143
|
+
const fetchedSkus = ids ? new Set(ids) : null;
|
|
144
|
+
const preferredSku = fetchedSkus
|
|
145
|
+
? (p.items.find((item) => fetchedSkus.has(item.itemId)) ?? pickSku(p))
|
|
146
|
+
: pickSku(p);
|
|
147
|
+
return toProductShelf(p, preferredSku, 0, { baseUrl, priceCurrency: "BRL" });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (ids) {
|
|
151
|
+
products = sortProducts(products, ids, "sku");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return products;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error("[VTEX] ProductListShelf error:", error);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -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:
|
|
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
|
-
|
|
6
|
-
import {
|
|
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
|
-
|
|
12
|
-
|
|
12
|
+
query?: string;
|
|
13
|
+
count?: number;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export default async function vtexSuggestions(
|
|
16
|
-
|
|
17
|
-
):
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
}
|