@decocms/apps-vtex 8.1.0-next.0 → 8.1.0-next.1
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/package.json +7 -52
- package/src/index.ts +10 -28
- package/src/utils/constants.ts +2 -56
- package/src/utils/cookieSanitizer.ts +59 -59
- package/src/utils/operationRouter.ts +75 -65
- package/src/utils/types.ts +709 -1557
- package/src/vtexClient.ts +354 -343
- package/src/README.md +0 -6
- package/src/__conformance__/upstream.test.ts +0 -158
- package/src/__tests__/client-segment-cookie.test.ts +0 -255
- package/src/__tests__/client-set-cookie-forward.test.ts +0 -257
- package/src/__tests__/schemas.test.ts +0 -71
- package/src/actions/address.ts +0 -250
- package/src/actions/analytics/sendEvent.ts +0 -86
- package/src/actions/auth.ts +0 -333
- package/src/actions/checkout.ts +0 -659
- package/src/actions/index.ts +0 -11
- package/src/actions/masterData.ts +0 -168
- package/src/actions/misc.ts +0 -188
- package/src/actions/newsletter.ts +0 -105
- package/src/actions/orders.ts +0 -34
- package/src/actions/profile.ts +0 -201
- package/src/actions/session.ts +0 -85
- package/src/actions/trigger.ts +0 -43
- package/src/actions/wishlist.ts +0 -114
- package/src/client.ts +0 -724
- package/src/commerceLoaders.ts +0 -267
- package/src/hooks/__tests__/createCart.render.test.ts +0 -114
- package/src/hooks/__tests__/createCart.test.ts +0 -92
- package/src/hooks/__tests__/createUseCart.test.ts +0 -184
- package/src/hooks/__tests__/createUseUser.test.ts +0 -48
- package/src/hooks/__tests__/createUseWishlist.test.ts +0 -87
- package/src/hooks/cartQuery.ts +0 -204
- package/src/hooks/createCart.ts +0 -427
- package/src/hooks/createUseCart.ts +0 -360
- package/src/hooks/createUseUser.ts +0 -153
- package/src/hooks/createUseWishlist.ts +0 -242
- package/src/hooks/index.ts +0 -25
- package/src/hooks/useAutocomplete.ts +0 -83
- package/src/hooks/useCart.ts +0 -243
- package/src/hooks/useUser.ts +0 -78
- package/src/hooks/useWishlist.ts +0 -119
- package/src/invoke.ts +0 -235
- package/src/loaders/ProductDetailsPage.ts +0 -1
- package/src/loaders/ProductList.ts +0 -1
- package/src/loaders/ProductListingPage.ts +0 -1
- package/src/loaders/__tests__/legacyProductList.test.ts +0 -157
- package/src/loaders/address.ts +0 -115
- package/src/loaders/autocomplete.ts +0 -56
- package/src/loaders/brands.ts +0 -44
- package/src/loaders/cart/attachments.ts +0 -53
- package/src/loaders/cart/full.ts +0 -67
- package/src/loaders/cart/gifts.ts +0 -43
- package/src/loaders/cart/orderFormId.ts +0 -31
- package/src/loaders/cart/shipping.test.ts +0 -125
- package/src/loaders/cart/shipping.ts +0 -136
- package/src/loaders/cart/summary.ts +0 -36
- package/src/loaders/cart.ts +0 -52
- package/src/loaders/catalog.ts +0 -163
- package/src/loaders/collections.ts +0 -55
- package/src/loaders/index.ts +0 -20
- package/src/loaders/intelligentSearch/__tests__/productListingPage.test.ts +0 -51
- package/src/loaders/intelligentSearch/productDetailsPage.ts +0 -111
- package/src/loaders/intelligentSearch/productList.ts +0 -170
- package/src/loaders/intelligentSearch/productListingPage.ts +0 -525
- package/src/loaders/intelligentSearch/suggestions.ts +0 -44
- package/src/loaders/legacy/productDetailsPage.ts +0 -1
- package/src/loaders/legacy/productList.ts +0 -1
- package/src/loaders/legacy/relatedProductsLoader.ts +0 -79
- package/src/loaders/legacy.ts +0 -710
- package/src/loaders/logistics.ts +0 -111
- package/src/loaders/minicart.ts +0 -78
- package/src/loaders/navbar.ts +0 -26
- package/src/loaders/orders.ts +0 -92
- package/src/loaders/pageType.ts +0 -68
- package/src/loaders/paths/PDPDefaultPath.ts +0 -51
- package/src/loaders/payment.ts +0 -97
- package/src/loaders/productListFull.ts +0 -157
- package/src/loaders/profile.ts +0 -138
- package/src/loaders/promotion.ts +0 -30
- package/src/loaders/search.ts +0 -124
- package/src/loaders/session.ts +0 -83
- package/src/loaders/user.ts +0 -87
- package/src/loaders/wishlist.ts +0 -89
- package/src/loaders/wishlistProducts.ts +0 -69
- package/src/loaders/workflow/products.ts +0 -62
- package/src/loaders/workflow.ts +0 -316
- package/src/logo.png +0 -0
- package/src/manifest.gen.ts +0 -89
- package/src/middleware.cacheHeaders.test.ts +0 -44
- package/src/middleware.ts +0 -240
- package/src/mod.ts +0 -173
- package/src/registry.ts +0 -9
- package/src/schemas.gen.ts +0 -3191
- package/src/schemas.ts +0 -30
- package/src/types.ts +0 -248
- package/src/utils/__tests__/cartProjection.test.ts +0 -118
- package/src/utils/__tests__/cookieSanitizer.test.ts +0 -162
- package/src/utils/__tests__/fetch.test.ts +0 -80
- package/src/utils/__tests__/fetchCache.test.ts +0 -155
- package/src/utils/__tests__/instrumentedFetch.test.ts +0 -158
- package/src/utils/__tests__/intelligentSearch.test.ts +0 -88
- package/src/utils/__tests__/minicart.test.ts +0 -184
- package/src/utils/__tests__/operationRouter.test.ts +0 -227
- package/src/utils/__tests__/resilience.test.ts +0 -205
- package/src/utils/__tests__/resourceRange.test.ts +0 -32
- package/src/utils/__tests__/simulationCache.test.ts +0 -40
- package/src/utils/__tests__/sitemap.test.ts +0 -185
- package/src/utils/__tests__/slugify.test.ts +0 -31
- package/src/utils/__tests__/storefrontBaseUrl.test.ts +0 -61
- package/src/utils/__tests__/transform.test.ts +0 -1111
- package/src/utils/accountLoaders.ts +0 -203
- package/src/utils/authHelpers.ts +0 -85
- package/src/utils/batch.ts +0 -18
- package/src/utils/buildOfferShelf.test.ts +0 -59
- package/src/utils/cartProjection.ts +0 -96
- package/src/utils/cookies.ts +0 -167
- package/src/utils/enrichment.ts +0 -556
- package/src/utils/fetch.ts +0 -111
- package/src/utils/fetchCache.ts +0 -55
- package/src/utils/index.ts +0 -19
- package/src/utils/instrumentedFetch.ts +0 -98
- package/src/utils/intelligentSearch.ts +0 -96
- package/src/utils/legacy.ts +0 -139
- package/src/utils/minicart.ts +0 -139
- package/src/utils/pickAndOmit.ts +0 -25
- package/src/utils/proxy.ts +0 -495
- package/src/utils/resilience.ts +0 -352
- package/src/utils/resourceRange.ts +0 -10
- package/src/utils/segment.ts +0 -152
- package/src/utils/similars.ts +0 -35
- package/src/utils/simulationCache.ts +0 -73
- package/src/utils/sitemap.ts +0 -283
- package/src/utils/slugCache.ts +0 -28
- package/src/utils/slugify.ts +0 -13
- package/src/utils/transform.ts +0 -1691
- package/src/utils/vtexId.ts +0 -151
- package/src/vtexClient.test.ts +0 -204
- package/tsconfig.json +0 -7
package/src/hooks/createCart.ts
DELETED
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cart v2 factory — modular, granular, framework-agnostic client hooks.
|
|
3
|
-
*
|
|
4
|
-
* Built on the same module-singleton + listener pattern as `createUseCart`
|
|
5
|
-
* (no `@tanstack/react-query` required, works identically under Next.js and
|
|
6
|
-
* TanStack Start), but redesigned around the Cart v2 goals:
|
|
7
|
-
*
|
|
8
|
-
* - **Lazy**: nothing hits VTEX on mount. No OrderForm is created until the
|
|
9
|
-
* first add-to-cart. The badge shows 0 (or the reconciled count) until then.
|
|
10
|
-
* - **Granular**: separate hooks per concern (`useCartSummary` for the badge,
|
|
11
|
-
* `useCart` for the drawer, `useShipping`, `useGifts`) so a component pulls
|
|
12
|
-
* only what it renders.
|
|
13
|
-
* - **Minimal by default + optimistic**: `useAddToCart` bumps the local count
|
|
14
|
-
* immediately and reconciles from the server's projected response. The
|
|
15
|
-
* optimistic + reconciliation logic lives here so the storefront never
|
|
16
|
-
* reimplements it.
|
|
17
|
-
*
|
|
18
|
-
* Injection: pass your site's `invoke` (the generated server-function proxy).
|
|
19
|
-
* The `CreateCartInvoke` interface declares only the v2 methods this factory
|
|
20
|
-
* calls — a structural subset, same as `CreateUseCartInvoke`.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* // src/hooks/cart.ts
|
|
25
|
-
* import { createCart } from "@decocms/apps/vtex/hooks/createCart";
|
|
26
|
-
* import { invoke } from "~/server/invoke";
|
|
27
|
-
* export const { useCart, useCartSummary, useAddToCart } = createCart({ invoke });
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
import type {
|
|
32
|
-
CartProjection,
|
|
33
|
-
CartSection,
|
|
34
|
-
CartSummary,
|
|
35
|
-
CartSummaryWithItems,
|
|
36
|
-
Minicart,
|
|
37
|
-
} from "@decocms/apps-commerce/types";
|
|
38
|
-
import { useEffect, useState } from "react";
|
|
39
|
-
import type { CartItemAttachments } from "../loaders/cart/attachments";
|
|
40
|
-
import type { CartGifts } from "../loaders/cart/gifts";
|
|
41
|
-
import type { CartShipping } from "../loaders/cart/shipping";
|
|
42
|
-
import type { OrderForm } from "../types";
|
|
43
|
-
import type { VtexCartProjectionResult } from "../utils/cartProjection";
|
|
44
|
-
|
|
45
|
-
/** Structural subset of the invoke proxy this factory needs. */
|
|
46
|
-
export interface CreateCartInvoke {
|
|
47
|
-
vtex: {
|
|
48
|
-
actions: {
|
|
49
|
-
getOrCreateCartV2: (args: {
|
|
50
|
-
data: { orderFormId?: string; projection?: CartProjection; sections?: CartSection[] };
|
|
51
|
-
}) => Promise<unknown>;
|
|
52
|
-
addItemsToCartV2: (args: {
|
|
53
|
-
data: {
|
|
54
|
-
orderFormId: string;
|
|
55
|
-
orderItems: Array<{ id: string; seller: string; quantity: number }>;
|
|
56
|
-
projection?: CartProjection;
|
|
57
|
-
sections?: CartSection[];
|
|
58
|
-
};
|
|
59
|
-
}) => Promise<unknown>;
|
|
60
|
-
updateCartItemsV2: (args: {
|
|
61
|
-
data: {
|
|
62
|
-
orderFormId: string;
|
|
63
|
-
orderItems: Array<{ index: number; quantity: number }>;
|
|
64
|
-
projection?: CartProjection;
|
|
65
|
-
sections?: CartSection[];
|
|
66
|
-
};
|
|
67
|
-
}) => Promise<unknown>;
|
|
68
|
-
addCouponToCartV2: (args: {
|
|
69
|
-
data: {
|
|
70
|
-
orderFormId: string;
|
|
71
|
-
text: string;
|
|
72
|
-
projection?: CartProjection;
|
|
73
|
-
sections?: CartSection[];
|
|
74
|
-
};
|
|
75
|
-
}) => Promise<unknown>;
|
|
76
|
-
};
|
|
77
|
-
loaders: {
|
|
78
|
-
cart: {
|
|
79
|
-
summary: (args: { data?: { orderFormId?: string } }) => Promise<CartSummary>;
|
|
80
|
-
full: (args: {
|
|
81
|
-
data?: {
|
|
82
|
-
orderFormId?: string;
|
|
83
|
-
freeShippingTarget?: number;
|
|
84
|
-
locale?: string;
|
|
85
|
-
checkoutHref?: string;
|
|
86
|
-
enableCoupon?: boolean;
|
|
87
|
-
};
|
|
88
|
-
}) => Promise<Minicart<OrderForm | null>>;
|
|
89
|
-
shipping: (args: {
|
|
90
|
-
data: {
|
|
91
|
-
items: Array<{ id: string | number; quantity: number; seller: string }>;
|
|
92
|
-
postalCode: string;
|
|
93
|
-
country?: string;
|
|
94
|
-
};
|
|
95
|
-
}) => Promise<CartShipping>;
|
|
96
|
-
gifts: (args: { data?: { orderFormId?: string } }) => Promise<CartGifts>;
|
|
97
|
-
attachments: (args: {
|
|
98
|
-
data: { orderFormId?: string; itemIndex: number };
|
|
99
|
-
}) => Promise<CartItemAttachments>;
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export interface CreateCartOptions {
|
|
106
|
-
invoke: CreateCartInvoke;
|
|
107
|
-
/** Override the orderFormId cookie name. Default: VTEX standard. */
|
|
108
|
-
orderFormCookieName?: string;
|
|
109
|
-
/** Override the cookie max-age in seconds. Default: 7 days. */
|
|
110
|
-
orderFormCookieMaxAge?: number;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const EMPTY_SUMMARY: CartSummary = { orderFormId: null, totalItems: 0, total: 0 };
|
|
114
|
-
|
|
115
|
-
export interface AddToCartOptions {
|
|
116
|
-
/** What the server should return. Default: `"summary+items"`. */
|
|
117
|
-
projection?: CartProjection;
|
|
118
|
-
sections?: CartSection[];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface UseCartInclude {
|
|
122
|
-
/** Fetch the full minicart (drawer). Default: false. */
|
|
123
|
-
full?: boolean;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export interface UseCartV2Options {
|
|
127
|
-
include?: UseCartInclude;
|
|
128
|
-
freeShippingTarget?: number;
|
|
129
|
-
locale?: string;
|
|
130
|
-
checkoutHref?: string;
|
|
131
|
-
enableCoupon?: boolean;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Build a per-site set of Cart v2 hooks. */
|
|
135
|
-
export function createCart(opts: CreateCartOptions) {
|
|
136
|
-
const { invoke } = opts;
|
|
137
|
-
const COOKIE_NAME = opts.orderFormCookieName ?? "checkout.vtex.com__orderFormId";
|
|
138
|
-
const COOKIE_MAX_AGE = opts.orderFormCookieMaxAge ?? 7 * 24 * 3600;
|
|
139
|
-
|
|
140
|
-
// --- module-singleton state --------------------------------------------
|
|
141
|
-
let _summary: CartSummary = EMPTY_SUMMARY;
|
|
142
|
-
let _minicart: Minicart<OrderForm | null> | null = null;
|
|
143
|
-
let _loading = false;
|
|
144
|
-
const _listeners = new Set<() => void>();
|
|
145
|
-
|
|
146
|
-
function notify() {
|
|
147
|
-
for (const fn of _listeners) fn();
|
|
148
|
-
}
|
|
149
|
-
function setSummary(s: CartSummary) {
|
|
150
|
-
_summary = s;
|
|
151
|
-
notify();
|
|
152
|
-
}
|
|
153
|
-
function setMinicart(m: Minicart<OrderForm | null> | null) {
|
|
154
|
-
_minicart = m;
|
|
155
|
-
if (m) {
|
|
156
|
-
// Keep the badge in sync whenever the full cart is loaded.
|
|
157
|
-
_summary = {
|
|
158
|
-
orderFormId: m.original?.orderFormId ?? _summary.orderFormId,
|
|
159
|
-
totalItems: m.storefront.items.reduce((n, i) => n + (i.quantity ?? 0), 0),
|
|
160
|
-
total: m.storefront.total,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
notify();
|
|
164
|
-
}
|
|
165
|
-
function setLoading(v: boolean) {
|
|
166
|
-
_loading = v;
|
|
167
|
-
notify();
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// --- cookie helpers ----------------------------------------------------
|
|
171
|
-
function escapeRegex(s: string): string {
|
|
172
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
173
|
-
}
|
|
174
|
-
function getOrderFormIdFromCookie(): string | null {
|
|
175
|
-
if (typeof document === "undefined") return null;
|
|
176
|
-
const re = new RegExp(`${escapeRegex(COOKIE_NAME)}=([^;]*)`);
|
|
177
|
-
const match = document.cookie.match(re);
|
|
178
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
179
|
-
}
|
|
180
|
-
function setOrderFormIdCookie(id: string) {
|
|
181
|
-
if (typeof document === "undefined") return;
|
|
182
|
-
// biome-ignore lint/suspicious/noDocumentCookie: the orderFormId cookie is intentionally client-readable (VTEX standard, mirrored by createUseCart).
|
|
183
|
-
document.cookie = `${COOKIE_NAME}=${encodeURIComponent(id)}; path=/; max-age=${COOKIE_MAX_AGE}; SameSite=Lax`;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Ensure an OrderForm exists. Only called from mutations — never on mount —
|
|
188
|
-
* so a browsing-only visitor provisions no cart. If the cookie has an id we
|
|
189
|
-
* trust it; otherwise we create one (projection "summary" — we only need the
|
|
190
|
-
* id + count back).
|
|
191
|
-
*/
|
|
192
|
-
async function ensureOrderFormId(): Promise<string> {
|
|
193
|
-
const existing = _summary.orderFormId ?? getOrderFormIdFromCookie();
|
|
194
|
-
if (existing) {
|
|
195
|
-
_summary = { ..._summary, orderFormId: existing };
|
|
196
|
-
return existing;
|
|
197
|
-
}
|
|
198
|
-
const created = (await invoke.vtex.actions.getOrCreateCartV2({
|
|
199
|
-
data: { projection: "summary" },
|
|
200
|
-
})) as CartSummary;
|
|
201
|
-
if (created.orderFormId) setOrderFormIdCookie(created.orderFormId);
|
|
202
|
-
setSummary(created);
|
|
203
|
-
return created.orderFormId ?? "";
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/** Reconcile local state from a projected mutation response. */
|
|
207
|
-
function reconcile(result: unknown, projection: CartProjection) {
|
|
208
|
-
if (projection === "none") return; // optimistic-only; nothing to reconcile
|
|
209
|
-
if (projection === "minicart") {
|
|
210
|
-
setMinicart(result as Minicart<OrderForm | null>);
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
// "summary" | "summary+items" | "raw" all expose the totals we need.
|
|
214
|
-
const r = result as Partial<CartSummaryWithItems> & { value?: number };
|
|
215
|
-
if (r && typeof r.totalItems === "number") {
|
|
216
|
-
setSummary({
|
|
217
|
-
orderFormId: r.orderFormId ?? _summary.orderFormId,
|
|
218
|
-
totalItems: r.totalItems,
|
|
219
|
-
total: r.total ?? _summary.total,
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// --- shared React subscription ----------------------------------------
|
|
225
|
-
function useCartState() {
|
|
226
|
-
const [, forceRender] = useState(0);
|
|
227
|
-
useEffect(() => {
|
|
228
|
-
const listener = () => forceRender((n) => n + 1);
|
|
229
|
-
_listeners.add(listener);
|
|
230
|
-
return () => {
|
|
231
|
-
_listeners.delete(listener);
|
|
232
|
-
};
|
|
233
|
-
}, []);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// --- hooks -------------------------------------------------------------
|
|
237
|
-
|
|
238
|
-
/** Badge hook. Reads the local summary; never triggers a VTEX call by itself. */
|
|
239
|
-
function useCartSummary() {
|
|
240
|
-
useCartState();
|
|
241
|
-
return { summary: _summary, totalItems: _summary.totalItems, loading: _loading };
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Add-to-cart with built-in optimistic count + reconciliation.
|
|
246
|
-
* `projection: "none"` → pure 200, optimistic-only (no server data used).
|
|
247
|
-
*/
|
|
248
|
-
function useAddToCart(hookOpts: AddToCartOptions = {}) {
|
|
249
|
-
useCartState();
|
|
250
|
-
const projection: CartProjection = hookOpts.projection ?? "summary+items";
|
|
251
|
-
|
|
252
|
-
async function add(params: {
|
|
253
|
-
id: string;
|
|
254
|
-
seller: string;
|
|
255
|
-
quantity?: number;
|
|
256
|
-
}): Promise<VtexCartProjectionResult> {
|
|
257
|
-
const qty = params.quantity ?? 1;
|
|
258
|
-
// Optimistic: bump the badge immediately.
|
|
259
|
-
setSummary({ ..._summary, totalItems: _summary.totalItems + qty });
|
|
260
|
-
setLoading(true);
|
|
261
|
-
try {
|
|
262
|
-
const orderFormId = await ensureOrderFormId();
|
|
263
|
-
const result = await invoke.vtex.actions.addItemsToCartV2({
|
|
264
|
-
data: {
|
|
265
|
-
orderFormId,
|
|
266
|
-
orderItems: [{ id: params.id, seller: params.seller, quantity: qty }],
|
|
267
|
-
projection,
|
|
268
|
-
sections: hookOpts.sections,
|
|
269
|
-
},
|
|
270
|
-
});
|
|
271
|
-
reconcile(result, projection);
|
|
272
|
-
// Return the projected payload so callers can drive a toast / analytics
|
|
273
|
-
// without a second fetch (e.g. `summary+items` → name/image/price/variant).
|
|
274
|
-
return result as VtexCartProjectionResult;
|
|
275
|
-
} catch (err) {
|
|
276
|
-
// Roll back the optimistic bump on failure.
|
|
277
|
-
setSummary({ ..._summary, totalItems: Math.max(0, _summary.totalItems - qty) });
|
|
278
|
-
console.error("[cart] addToCart failed:", err);
|
|
279
|
-
throw err;
|
|
280
|
-
} finally {
|
|
281
|
-
setLoading(false);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return { add, loading: _loading };
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Composed cart hook for the drawer. Fetches ONLY what `include` enables —
|
|
290
|
-
* default is nothing beyond the already-local summary. Call `openDrawer()`
|
|
291
|
-
* (or pass `include.full`) to load the full minicart on demand.
|
|
292
|
-
*/
|
|
293
|
-
function useCart(cartOpts: UseCartV2Options = {}) {
|
|
294
|
-
useCartState();
|
|
295
|
-
const wantFull = cartOpts.include?.full === true;
|
|
296
|
-
|
|
297
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: the drawer loads once when enabled; storefront options are stable per-mount and must not re-trigger the fetch.
|
|
298
|
-
useEffect(() => {
|
|
299
|
-
if (!wantFull) return;
|
|
300
|
-
let cancelled = false;
|
|
301
|
-
setLoading(true);
|
|
302
|
-
invoke.vtex.loaders.cart
|
|
303
|
-
.full({
|
|
304
|
-
data: {
|
|
305
|
-
freeShippingTarget: cartOpts.freeShippingTarget,
|
|
306
|
-
locale: cartOpts.locale,
|
|
307
|
-
checkoutHref: cartOpts.checkoutHref,
|
|
308
|
-
enableCoupon: cartOpts.enableCoupon,
|
|
309
|
-
},
|
|
310
|
-
})
|
|
311
|
-
.then((m) => {
|
|
312
|
-
if (!cancelled) setMinicart(m);
|
|
313
|
-
})
|
|
314
|
-
.catch((err) => console.error("[cart] load full failed:", err))
|
|
315
|
-
.finally(() => {
|
|
316
|
-
if (!cancelled) setLoading(false);
|
|
317
|
-
});
|
|
318
|
-
return () => {
|
|
319
|
-
cancelled = true;
|
|
320
|
-
};
|
|
321
|
-
}, [wantFull]);
|
|
322
|
-
|
|
323
|
-
async function refresh() {
|
|
324
|
-
const m = await invoke.vtex.loaders.cart.full({
|
|
325
|
-
data: {
|
|
326
|
-
freeShippingTarget: cartOpts.freeShippingTarget,
|
|
327
|
-
locale: cartOpts.locale,
|
|
328
|
-
checkoutHref: cartOpts.checkoutHref,
|
|
329
|
-
enableCoupon: cartOpts.enableCoupon,
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
setMinicart(m);
|
|
333
|
-
return m;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
async function updateQuantity(index: number, quantity: number) {
|
|
337
|
-
const orderFormId = await ensureOrderFormId();
|
|
338
|
-
setLoading(true);
|
|
339
|
-
try {
|
|
340
|
-
const result = await invoke.vtex.actions.updateCartItemsV2({
|
|
341
|
-
data: { orderFormId, orderItems: [{ index, quantity }], projection: "minicart" },
|
|
342
|
-
});
|
|
343
|
-
reconcile(result, "minicart");
|
|
344
|
-
return result as Minicart<OrderForm | null>;
|
|
345
|
-
} finally {
|
|
346
|
-
setLoading(false);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
async function removeItem(index: number) {
|
|
351
|
-
return updateQuantity(index, 0);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
async function addCoupon(text: string) {
|
|
355
|
-
const orderFormId = await ensureOrderFormId();
|
|
356
|
-
setLoading(true);
|
|
357
|
-
try {
|
|
358
|
-
const result = await invoke.vtex.actions.addCouponToCartV2({
|
|
359
|
-
data: { orderFormId, text, projection: "minicart" },
|
|
360
|
-
});
|
|
361
|
-
reconcile(result, "minicart");
|
|
362
|
-
return result as Minicart<OrderForm | null>;
|
|
363
|
-
} finally {
|
|
364
|
-
setLoading(false);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
return {
|
|
369
|
-
minicart: _minicart,
|
|
370
|
-
summary: _summary,
|
|
371
|
-
loading: _loading,
|
|
372
|
-
refresh,
|
|
373
|
-
updateQuantity,
|
|
374
|
-
removeItem,
|
|
375
|
-
addCoupon,
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/** On-demand shipping estimate for the drawer. Not cached (see loader note). */
|
|
380
|
-
function useShipping() {
|
|
381
|
-
useCartState();
|
|
382
|
-
async function estimate(params: {
|
|
383
|
-
items: Array<{ id: string | number; quantity: number; seller: string }>;
|
|
384
|
-
postalCode: string;
|
|
385
|
-
country?: string;
|
|
386
|
-
}) {
|
|
387
|
-
return invoke.vtex.loaders.cart.shipping({ data: params });
|
|
388
|
-
}
|
|
389
|
-
return { estimate };
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/** On-demand selectable-gifts / promotions read. */
|
|
393
|
-
function useGifts() {
|
|
394
|
-
useCartState();
|
|
395
|
-
async function load() {
|
|
396
|
-
return invoke.vtex.loaders.cart.gifts({ data: {} });
|
|
397
|
-
}
|
|
398
|
-
return { load };
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/** On-demand attachments/offerings read for a single line (engraving, gift-wrap…). */
|
|
402
|
-
function useAttachments() {
|
|
403
|
-
useCartState();
|
|
404
|
-
async function load(itemIndex: number) {
|
|
405
|
-
return invoke.vtex.loaders.cart.attachments({ data: { itemIndex } });
|
|
406
|
-
}
|
|
407
|
-
return { load };
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/** Reset all module-level state (e.g. after logout / order placed). */
|
|
411
|
-
function resetCart() {
|
|
412
|
-
_summary = EMPTY_SUMMARY;
|
|
413
|
-
_minicart = null;
|
|
414
|
-
_loading = false;
|
|
415
|
-
notify();
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
return {
|
|
419
|
-
useCart,
|
|
420
|
-
useCartSummary,
|
|
421
|
-
useAddToCart,
|
|
422
|
-
useShipping,
|
|
423
|
-
useGifts,
|
|
424
|
-
useAttachments,
|
|
425
|
-
resetCart,
|
|
426
|
-
};
|
|
427
|
-
}
|