@decocms/apps 0.23.2 → 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.
- 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 +23 -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/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 +1012 -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
package/vtex/actions/session.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* VTEX Sessions API actions.
|
|
3
3
|
* All session-mutating actions return Set-Cookie headers for propagation.
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
import type { VtexFetchResult } from "../client";
|
|
7
|
+
import { getVtexConfig, vtexFetchWithCookies, vtexIOGraphQL } from "../client";
|
|
7
8
|
import { buildAuthCookieHeader } from "../utils/vtexId";
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
@@ -11,21 +12,21 @@ import { buildAuthCookieHeader } from "../utils/vtexId";
|
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
13
|
|
|
13
14
|
export interface SessionData {
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
id: string;
|
|
16
|
+
namespaces: Record<string, Record<string, { value: string }>>;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export async function createSession(
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
data: Record<string, any>,
|
|
21
|
+
cookieHeader?: string,
|
|
21
22
|
): Promise<VtexFetchResult<SessionData>> {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
const headers: Record<string, string> = {};
|
|
24
|
+
if (cookieHeader) headers.cookie = cookieHeader;
|
|
25
|
+
return vtexFetchWithCookies<SessionData>("/api/sessions", {
|
|
26
|
+
method: "POST",
|
|
27
|
+
body: JSON.stringify(data),
|
|
28
|
+
headers,
|
|
29
|
+
});
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// ---------------------------------------------------------------------------
|
|
@@ -33,8 +34,8 @@ export async function createSession(
|
|
|
33
34
|
// ---------------------------------------------------------------------------
|
|
34
35
|
|
|
35
36
|
export interface EditSessionResponse {
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
id: string;
|
|
38
|
+
namespaces: Record<string, Record<string, { value: string }>>;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/**
|
|
@@ -42,17 +43,17 @@ export interface EditSessionResponse {
|
|
|
42
43
|
* Returns data + Set-Cookie headers.
|
|
43
44
|
*/
|
|
44
45
|
export async function editSession(
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
publicProperties: Record<string, { value: string }>,
|
|
47
|
+
cookieHeader?: string,
|
|
47
48
|
): Promise<VtexFetchResult<EditSessionResponse>> {
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
const headers: Record<string, string> = {};
|
|
50
|
+
if (cookieHeader) headers.cookie = cookieHeader;
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
return vtexFetchWithCookies<EditSessionResponse>("/api/sessions", {
|
|
53
|
+
method: "PATCH",
|
|
54
|
+
body: JSON.stringify({ public: { ...publicProperties } }),
|
|
55
|
+
headers,
|
|
56
|
+
});
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
// ---------------------------------------------------------------------------
|
|
@@ -60,7 +61,7 @@ export async function editSession(
|
|
|
60
61
|
// ---------------------------------------------------------------------------
|
|
61
62
|
|
|
62
63
|
export interface DeleteSessionResponse {
|
|
63
|
-
|
|
64
|
+
logOutFromSession: string;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
const DELETE_SESSION_MUTATION = `mutation LogOutFromSession($sessionId: ID) {
|
|
@@ -72,16 +73,16 @@ const DELETE_SESSION_MUTATION = `mutation LogOutFromSession($sessionId: ID) {
|
|
|
72
73
|
* Requires a valid auth cookie.
|
|
73
74
|
*/
|
|
74
75
|
export async function deleteSession(
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
sessionId: string,
|
|
77
|
+
authCookie: string,
|
|
77
78
|
): Promise<DeleteSessionResponse> {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
if (!authCookie) throw new Error("Auth cookie is required to delete session");
|
|
80
|
+
const { account } = getVtexConfig();
|
|
81
|
+
return vtexIOGraphQL<DeleteSessionResponse>(
|
|
82
|
+
{
|
|
83
|
+
query: DELETE_SESSION_MUTATION,
|
|
84
|
+
variables: { sessionId },
|
|
85
|
+
},
|
|
86
|
+
{ cookie: buildAuthCookieHeader(authCookie, account) },
|
|
87
|
+
);
|
|
87
88
|
}
|
package/vtex/actions/trigger.ts
CHANGED
|
@@ -9,29 +9,29 @@
|
|
|
9
9
|
|
|
10
10
|
/** Payload sent by VTEX when a SKU changes in the catalog. */
|
|
11
11
|
export interface VTEXNotificationPayload {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
12
|
+
/** SKU ID in VTEX. */
|
|
13
|
+
IdSku: string;
|
|
14
|
+
/** Seller's account name in VTEX (visible in the store's Admin URL). */
|
|
15
|
+
An: string;
|
|
16
|
+
/** Affiliate ID generated automatically in the configuration. */
|
|
17
|
+
IdAffiliate: string;
|
|
18
|
+
/** Product ID in VTEX. */
|
|
19
|
+
ProductId: number;
|
|
20
|
+
/** Date when the item was updated. */
|
|
21
|
+
DateModified: string;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the product is active. `false` means the product was
|
|
24
|
+
* deactivated and should be blocked / zeroed in the marketplace.
|
|
25
|
+
*/
|
|
26
|
+
IsActive: boolean;
|
|
27
|
+
/** Inventory level has changed -- run a Fulfillment Simulation to refresh. */
|
|
28
|
+
StockModified: boolean;
|
|
29
|
+
/** Price has changed -- run a Fulfillment Simulation to refresh. */
|
|
30
|
+
PriceModified: boolean;
|
|
31
|
+
/** Product/SKU registration data changed (name, description, weight, etc.). */
|
|
32
|
+
HasStockKeepingUnitModified: boolean;
|
|
33
|
+
/** Product is no longer associated with the trade policy. */
|
|
34
|
+
HasStockKeepingUnitRemovedFromAffiliate: boolean;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -39,5 +39,5 @@ export interface VTEXNotificationPayload {
|
|
|
39
39
|
* Implement this in your framework layer (e.g. TanStack Start API route).
|
|
40
40
|
*/
|
|
41
41
|
export type VTEXTriggerHandler = (
|
|
42
|
-
|
|
43
|
-
) => Promise<{ id: string } |
|
|
42
|
+
payload: VTEXNotificationPayload,
|
|
43
|
+
) => Promise<{ id: string } | undefined>;
|
package/vtex/actions/wishlist.ts
CHANGED
|
@@ -5,31 +5,29 @@
|
|
|
5
5
|
* - vtex/actions/wishlist/removeItem.ts
|
|
6
6
|
* @see https://developers.vtex.com/docs/apps/vtex.wish-list
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { getVtexConfig, vtexIOGraphQL } from "../client";
|
|
9
9
|
import { buildAuthCookieHeader } from "../utils/vtexId";
|
|
10
10
|
|
|
11
|
+
/** Maximum wishlist items to fetch in a single query. */
|
|
12
|
+
const WISHLIST_MAX_ITEMS = 500;
|
|
13
|
+
|
|
11
14
|
// ---------------------------------------------------------------------------
|
|
12
15
|
// Types
|
|
13
16
|
// ---------------------------------------------------------------------------
|
|
14
17
|
|
|
15
18
|
export interface WishlistItem {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
id?: string;
|
|
20
|
+
productId: string;
|
|
21
|
+
sku: string;
|
|
22
|
+
title?: string;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
// ---------------------------------------------------------------------------
|
|
23
26
|
// GraphQL helper (myvtex.com private graphql)
|
|
24
27
|
// ---------------------------------------------------------------------------
|
|
25
28
|
|
|
26
|
-
interface GqlResponse<T> {
|
|
27
|
-
data: T;
|
|
28
|
-
errors?: Array<{ message: string }>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
29
|
function buildCookieHeader(authCookie: string): string {
|
|
32
|
-
|
|
30
|
+
return buildAuthCookieHeader(authCookie, getVtexConfig().account);
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
// ---------------------------------------------------------------------------
|
|
@@ -55,16 +53,16 @@ const VIEW_WISHLIST = `query ViewList($shopperId: String!, $name: String!, $from
|
|
|
55
53
|
// ---------------------------------------------------------------------------
|
|
56
54
|
|
|
57
55
|
async function fetchWishlist(shopperId: string, authCookie: string): Promise<WishlistItem[]> {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
const data = await vtexIOGraphQL<{
|
|
57
|
+
viewList: { data: WishlistItem[] | null };
|
|
58
|
+
}>(
|
|
59
|
+
{
|
|
60
|
+
query: VIEW_WISHLIST,
|
|
61
|
+
variables: { shopperId, name: "Wishlist", from: 0, to: WISHLIST_MAX_ITEMS },
|
|
62
|
+
},
|
|
63
|
+
{ Cookie: buildCookieHeader(authCookie) },
|
|
64
|
+
);
|
|
65
|
+
return data.viewList?.data ?? [];
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
/**
|
|
@@ -72,23 +70,23 @@ async function fetchWishlist(shopperId: string, authCookie: string): Promise<Wis
|
|
|
72
70
|
* Returns the updated full wishlist.
|
|
73
71
|
*/
|
|
74
72
|
export async function addItem(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
item: { productId: string; sku: string; title?: string },
|
|
74
|
+
shopperId: string,
|
|
75
|
+
authCookie: string,
|
|
78
76
|
): Promise<WishlistItem[]> {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
77
|
+
if (!authCookie) throw new Error("User must be logged in to add to wishlist");
|
|
78
|
+
await vtexIOGraphQL<unknown>(
|
|
79
|
+
{
|
|
80
|
+
query: ADD_TO_WISHLIST,
|
|
81
|
+
variables: {
|
|
82
|
+
name: "Wishlist",
|
|
83
|
+
shopperId,
|
|
84
|
+
listItem: item,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{ Cookie: buildCookieHeader(authCookie) },
|
|
88
|
+
);
|
|
89
|
+
return fetchWishlist(shopperId, authCookie);
|
|
92
90
|
}
|
|
93
91
|
|
|
94
92
|
/**
|
|
@@ -96,21 +94,21 @@ export async function addItem(
|
|
|
96
94
|
* Returns the updated full wishlist.
|
|
97
95
|
*/
|
|
98
96
|
export async function removeItem(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
id: string,
|
|
98
|
+
shopperId: string,
|
|
99
|
+
authCookie: string,
|
|
102
100
|
): Promise<WishlistItem[]> {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
if (!authCookie) throw new Error("User must be logged in to remove from wishlist");
|
|
102
|
+
await vtexIOGraphQL<unknown>(
|
|
103
|
+
{
|
|
104
|
+
query: REMOVE_FROM_WISHLIST,
|
|
105
|
+
variables: {
|
|
106
|
+
id,
|
|
107
|
+
name: "Wishlist",
|
|
108
|
+
shopperId,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{ Cookie: buildCookieHeader(authCookie) },
|
|
112
|
+
);
|
|
113
|
+
return fetchWishlist(shopperId, authCookie);
|
|
116
114
|
}
|
package/vtex/client.ts
CHANGED
|
@@ -4,14 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { type FetchCacheOptions, fetchWithCache } from "./utils/fetchCache";
|
|
7
|
-
import {
|
|
7
|
+
import { parseSegment, SEGMENT_COOKIE_NAME } from "./utils/segment";
|
|
8
8
|
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// URL sanitization (ported from deco-cx/apps vtex/utils/fetchVTEX.ts)
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
|
|
13
|
-
const removeNonLatin1Chars = (str: string): string =>
|
|
14
|
-
str.replace(/[^\x00-\x7F]|["']/g, "");
|
|
13
|
+
const removeNonLatin1Chars = (str: string): string => str.replace(/[^\x00-\x7F]|["']/g, "");
|
|
15
14
|
|
|
16
15
|
const removeScriptChars = (str: string): string => {
|
|
17
16
|
return str
|
|
@@ -138,8 +137,7 @@ export function setResponseCookieForwarder(fn: (cookies: string[]) => void) {
|
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
export function getVtexConfig(): VtexConfig {
|
|
141
|
-
if (!_config)
|
|
142
|
-
throw new Error("VTEX not configured. Call configureVtex() first.");
|
|
140
|
+
if (!_config) throw new Error("VTEX not configured. Call configureVtex() first.");
|
|
143
141
|
return _config;
|
|
144
142
|
}
|
|
145
143
|
|
|
@@ -147,10 +145,7 @@ export function getVtexConfig(): VtexConfig {
|
|
|
147
145
|
* Build the VTEX hostname for a given environment.
|
|
148
146
|
* Centralizes `{account}.{env}.{domain}` so nothing is hardcoded.
|
|
149
147
|
*/
|
|
150
|
-
export function vtexHost(
|
|
151
|
-
environment: string = "vtexcommercestable",
|
|
152
|
-
config?: VtexConfig,
|
|
153
|
-
): string {
|
|
148
|
+
export function vtexHost(environment: string = "vtexcommercestable", config?: VtexConfig): string {
|
|
154
149
|
const c = config ?? getVtexConfig();
|
|
155
150
|
const domain = c.domain ?? "com.br";
|
|
156
151
|
return `${c.account}.${environment}.${domain}`;
|
|
@@ -185,18 +180,13 @@ function extractRegionIdFromCookies(): string | null {
|
|
|
185
180
|
if (!_getCookieHeader) return null;
|
|
186
181
|
const cookies = _getCookieHeader();
|
|
187
182
|
if (!cookies) return null;
|
|
188
|
-
const match = cookies.match(
|
|
189
|
-
new RegExp(`(?:^|;\\s*)${SEGMENT_COOKIE_NAME}=([^;]+)`),
|
|
190
|
-
);
|
|
183
|
+
const match = cookies.match(new RegExp(`(?:^|;\\s*)${SEGMENT_COOKIE_NAME}=([^;]+)`));
|
|
191
184
|
if (!match?.[1]) return null;
|
|
192
185
|
const segment = parseSegment(match[1]);
|
|
193
186
|
return segment?.regionId ?? null;
|
|
194
187
|
}
|
|
195
188
|
|
|
196
|
-
export async function vtexFetchResponse(
|
|
197
|
-
path: string,
|
|
198
|
-
init?: RequestInit,
|
|
199
|
-
): Promise<Response> {
|
|
189
|
+
export async function vtexFetchResponse(path: string, init?: RequestInit): Promise<Response> {
|
|
200
190
|
const raw = path.startsWith("http") ? path : `${baseUrl()}${path}`;
|
|
201
191
|
const url = sanitizeUrl(raw);
|
|
202
192
|
const response = await _fetch(url, {
|
|
@@ -204,17 +194,12 @@ export async function vtexFetchResponse(
|
|
|
204
194
|
headers: { ...authHeaders(), ...init?.headers },
|
|
205
195
|
});
|
|
206
196
|
if (!response.ok) {
|
|
207
|
-
throw new Error(
|
|
208
|
-
`VTEX API error: ${response.status} ${response.statusText} - ${url}`,
|
|
209
|
-
);
|
|
197
|
+
throw new Error(`VTEX API error: ${response.status} ${response.statusText} - ${url}`);
|
|
210
198
|
}
|
|
211
199
|
return response;
|
|
212
200
|
}
|
|
213
201
|
|
|
214
|
-
export async function vtexFetch<T>(
|
|
215
|
-
path: string,
|
|
216
|
-
init?: RequestInit,
|
|
217
|
-
): Promise<T> {
|
|
202
|
+
export async function vtexFetch<T>(path: string, init?: RequestInit): Promise<T> {
|
|
218
203
|
const response = await vtexFetchResponse(path, init);
|
|
219
204
|
return response.json();
|
|
220
205
|
}
|
|
@@ -370,9 +355,7 @@ export async function vtexIOGraphQL<T>(
|
|
|
370
355
|
},
|
|
371
356
|
);
|
|
372
357
|
if (res.errors?.length) {
|
|
373
|
-
throw new Error(
|
|
374
|
-
`VTEX IO GraphQL error: ${res.errors.map((e) => e.message).join(", ")}`,
|
|
375
|
-
);
|
|
358
|
+
throw new Error(`VTEX IO GraphQL error: ${res.errors.map((e) => e.message).join(", ")}`);
|
|
376
359
|
}
|
|
377
360
|
return res.data;
|
|
378
361
|
}
|
|
@@ -408,10 +391,7 @@ const PAGE_TYPE_TO_MAP_PARAM: Record<string, string | null> = {
|
|
|
408
391
|
FullText: null,
|
|
409
392
|
};
|
|
410
393
|
|
|
411
|
-
function pageTypeToMapParam(
|
|
412
|
-
type: PageType["pageType"],
|
|
413
|
-
index: number,
|
|
414
|
-
): string | null {
|
|
394
|
+
function pageTypeToMapParam(type: PageType["pageType"], index: number): string | null {
|
|
415
395
|
if (type === "Category" || type === "Department" || type === "SubCategory") {
|
|
416
396
|
return `category-${index + 1}`;
|
|
417
397
|
}
|
|
@@ -419,9 +399,7 @@ function pageTypeToMapParam(
|
|
|
419
399
|
}
|
|
420
400
|
|
|
421
401
|
function cachedPageType(term: string): Promise<PageType | null> {
|
|
422
|
-
return vtexCachedFetch<PageType>(
|
|
423
|
-
`/api/catalog_system/pub/portal/pagetype/${term}`,
|
|
424
|
-
);
|
|
402
|
+
return vtexCachedFetch<PageType>(`/api/catalog_system/pub/portal/pagetype/${term}`);
|
|
425
403
|
}
|
|
426
404
|
|
|
427
405
|
/**
|
|
@@ -453,9 +431,7 @@ const slugify = (str: string) =>
|
|
|
453
431
|
* Convert page types to selectedFacets with correct IS facet keys.
|
|
454
432
|
* Mirrors deco-cx/apps `filtersFromPathname`.
|
|
455
433
|
*/
|
|
456
|
-
export function filtersFromPageTypes(
|
|
457
|
-
pageTypes: PageType[],
|
|
458
|
-
): Array<{ key: string; value: string }> {
|
|
434
|
+
export function filtersFromPageTypes(pageTypes: PageType[]): Array<{ key: string; value: string }> {
|
|
459
435
|
return pageTypes
|
|
460
436
|
.map((page, index) => {
|
|
461
437
|
const key = pageTypeToMapParam(page.pageType, index);
|
|
@@ -469,12 +445,8 @@ export function filtersFromPageTypes(
|
|
|
469
445
|
* Build the IS facet path string from selectedFacets.
|
|
470
446
|
* Mirrors deco-cx/apps `toPath`.
|
|
471
447
|
*/
|
|
472
|
-
export function toFacetPath(
|
|
473
|
-
facets
|
|
474
|
-
): string {
|
|
475
|
-
return facets
|
|
476
|
-
.map(({ key, value }) => (key ? `${key}/${value}` : value))
|
|
477
|
-
.join("/");
|
|
448
|
+
export function toFacetPath(facets: Array<{ key: string; value: string }>): string {
|
|
449
|
+
return facets.map(({ key, value }) => (key ? `${key}/${value}` : value)).join("/");
|
|
478
450
|
}
|
|
479
451
|
|
|
480
452
|
export function initVtexFromBlocks(blocks: Record<string, any>) {
|
package/vtex/hooks/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
1
|
+
export { type UseAutocompleteOptions, useAutocomplete } from "./useAutocomplete";
|
|
2
|
+
export { type CartItem, type OrderForm, type UseCartOptions, useCart } from "./useCart";
|
|
3
|
+
export { type UseUserOptions, useUser, type VtexUser } from "./useUser";
|
|
4
|
+
export { type UseWishlistOptions, useWishlist, type WishlistItem } from "./useWishlist";
|
|
@@ -20,70 +20,64 @@
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import { useState, useCallback, useRef, useEffect } from "react";
|
|
24
23
|
import { useQuery } from "@tanstack/react-query";
|
|
24
|
+
import { useCallback, useEffect, useState } from "react";
|
|
25
25
|
import type { Suggestion } from "../../commerce/types/commerce";
|
|
26
26
|
|
|
27
27
|
export interface UseAutocompleteOptions {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
/** Debounce delay in ms @default 250 */
|
|
29
|
+
debounceMs?: number;
|
|
30
|
+
/** Max products to return @default 4 */
|
|
31
|
+
count?: number;
|
|
32
|
+
/** Custom fetch function — defaults to calling the inline-loader on the server */
|
|
33
|
+
fetchSuggestions?: (query: string, count: number) => Promise<Suggestion | null>;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const AUTOCOMPLETE_QUERY_KEY = "vtex-autocomplete";
|
|
37
37
|
|
|
38
38
|
function useDebounce<T>(value: T, delay: number): T {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
const [debounced, setDebounced] = useState(value);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const timer = setTimeout(() => setDebounced(value), delay);
|
|
42
|
+
return () => clearTimeout(timer);
|
|
43
|
+
}, [value, delay]);
|
|
44
|
+
return debounced;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
async function defaultFetchSuggestions(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
const res = await fetch(`/api/vtex/suggestions?${params}`);
|
|
53
|
-
if (!res.ok) return null;
|
|
54
|
-
return res.json();
|
|
47
|
+
async function defaultFetchSuggestions(query: string, _count: number): Promise<Suggestion | null> {
|
|
48
|
+
const params = new URLSearchParams({ query });
|
|
49
|
+
const res = await fetch(`/api/vtex/suggestions?${params}`);
|
|
50
|
+
if (!res.ok) return null;
|
|
51
|
+
return res.json();
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
export function useAutocomplete(opts?: UseAutocompleteOptions) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
const debounceMs = opts?.debounceMs ?? 250;
|
|
56
|
+
const count = opts?.count ?? 4;
|
|
57
|
+
const fetchFn = opts?.fetchSuggestions ?? defaultFetchSuggestions;
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
const [rawQuery, setRawQuery] = useState("");
|
|
60
|
+
const debouncedQuery = useDebounce(rawQuery.trim(), debounceMs);
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
const { data, isLoading, isFetching } = useQuery({
|
|
63
|
+
queryKey: [AUTOCOMPLETE_QUERY_KEY, debouncedQuery, count],
|
|
64
|
+
queryFn: () => fetchFn(debouncedQuery, count),
|
|
65
|
+
enabled: debouncedQuery.length > 0,
|
|
66
|
+
staleTime: 60_000,
|
|
67
|
+
});
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
},
|
|
76
|
-
[],
|
|
77
|
-
);
|
|
69
|
+
const setSearch = useCallback((query: string) => {
|
|
70
|
+
setRawQuery(query);
|
|
71
|
+
}, []);
|
|
78
72
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
73
|
+
return {
|
|
74
|
+
/** Set the search query (will be debounced automatically) */
|
|
75
|
+
setSearch,
|
|
76
|
+
/** Current raw (un-debounced) query */
|
|
77
|
+
query: rawQuery,
|
|
78
|
+
/** Suggestion result (searches + products) */
|
|
79
|
+
suggestions: data ?? null,
|
|
80
|
+
/** True while initial fetch is in progress */
|
|
81
|
+
loading: isLoading || isFetching,
|
|
82
|
+
};
|
|
89
83
|
}
|