@jay-framework/wix-cart 0.15.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/dist/contracts/cart-indicator.jay-contract +43 -0
- package/dist/contracts/cart-indicator.jay-contract.d.ts +29 -0
- package/dist/contracts/cart-page.jay-contract +328 -0
- package/dist/contracts/cart-page.jay-contract.d.ts +146 -0
- package/dist/index.client-VoD54p4E.d.ts +436 -0
- package/dist/index.client.d.ts +8 -0
- package/dist/index.client.js +566 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +445 -0
- package/package.json +66 -0
- package/plugin.yaml +14 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import { createJayContext, useGlobalContext } from "@jay-framework/runtime";
|
|
2
|
+
import { registerReactiveGlobalContext, createSignal, useReactive, createEffect } from "@jay-framework/component";
|
|
3
|
+
import { WIX_CLIENT_CONTEXT } from "@jay-framework/wix-server-client/client";
|
|
4
|
+
import { currentCart } from "@wix/ecom";
|
|
5
|
+
import { makeJayStackComponent, makeJayInit } from "@jay-framework/fullstack-component";
|
|
6
|
+
import { patch, REPLACE } from "@jay-framework/json-patch";
|
|
7
|
+
let currentCartInstance;
|
|
8
|
+
function getCurrentCartClient(wixClient) {
|
|
9
|
+
if (!currentCartInstance) {
|
|
10
|
+
currentCartInstance = wixClient.use(currentCart);
|
|
11
|
+
}
|
|
12
|
+
return currentCartInstance;
|
|
13
|
+
}
|
|
14
|
+
function parseWixMediaUrl(url) {
|
|
15
|
+
if (!url) return null;
|
|
16
|
+
const match = url.match(
|
|
17
|
+
/^wix:(image|video|document|audio):\/\/v1\/([^/]+)\/([^#]+)(?:#(.*))?$/
|
|
18
|
+
);
|
|
19
|
+
if (!match) return null;
|
|
20
|
+
const [, type, mediaId, fileName, hashParams] = match;
|
|
21
|
+
const result = {
|
|
22
|
+
type,
|
|
23
|
+
mediaId,
|
|
24
|
+
fileName: decodeURIComponent(fileName)
|
|
25
|
+
};
|
|
26
|
+
if (hashParams) {
|
|
27
|
+
const params = new URLSearchParams(hashParams);
|
|
28
|
+
const originWidth = params.get("originWidth");
|
|
29
|
+
const originHeight = params.get("originHeight");
|
|
30
|
+
if (originWidth) result.originWidth = parseInt(originWidth, 10);
|
|
31
|
+
if (originHeight) result.originHeight = parseInt(originHeight, 10);
|
|
32
|
+
const posterUri = params.get("posterUri");
|
|
33
|
+
const posterWidth = params.get("posterWidth");
|
|
34
|
+
const posterHeight = params.get("posterHeight");
|
|
35
|
+
if (posterUri) result.posterUri = decodeURIComponent(posterUri);
|
|
36
|
+
if (posterWidth) result.posterWidth = parseInt(posterWidth, 10);
|
|
37
|
+
if (posterHeight) result.posterHeight = parseInt(posterHeight, 10);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
function formatWixMediaUrl(_id, url, resize) {
|
|
42
|
+
const resizeFragment = "";
|
|
43
|
+
if (url == null ? void 0 : url.startsWith("wix:")) {
|
|
44
|
+
const parsed = parseWixMediaUrl(url);
|
|
45
|
+
if (parsed) {
|
|
46
|
+
return `https://static.wixstatic.com/media/${parsed.mediaId}${resizeFragment}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if ((url == null ? void 0 : url.startsWith("http://")) || (url == null ? void 0 : url.startsWith("https://"))) {
|
|
50
|
+
return url;
|
|
51
|
+
}
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
function mapLineItem(item) {
|
|
55
|
+
const catalogRef = item.catalogReference;
|
|
56
|
+
const physicalProperties = item.physicalProperties;
|
|
57
|
+
const priceData = item.price;
|
|
58
|
+
const descriptionLines = item.descriptionLines || [];
|
|
59
|
+
const variantParts = descriptionLines.filter((line) => line.name?.translated).map(
|
|
60
|
+
(line) => `${line.name?.translated}: ${line.colorInfo?.translated || line.plainText?.translated || ""}`
|
|
61
|
+
);
|
|
62
|
+
const slug = item.url?.split("/").pop() || "";
|
|
63
|
+
return {
|
|
64
|
+
lineItemId: item._id || "",
|
|
65
|
+
productId: catalogRef?.catalogItemId || "",
|
|
66
|
+
productName: item.productName?.translated || item.productName?.original || "",
|
|
67
|
+
productUrl: slug ? `/products/${slug}` : "",
|
|
68
|
+
variantName: variantParts.join(" / "),
|
|
69
|
+
sku: physicalProperties?.sku || "",
|
|
70
|
+
image: {
|
|
71
|
+
url: formatWixMediaUrl("", item.image || ""),
|
|
72
|
+
altText: item.productName?.translated || ""
|
|
73
|
+
},
|
|
74
|
+
quantity: item.quantity || 1,
|
|
75
|
+
unitPrice: {
|
|
76
|
+
amount: priceData?.amount || "0",
|
|
77
|
+
formattedAmount: priceData?.formattedAmount || ""
|
|
78
|
+
},
|
|
79
|
+
lineTotal: {
|
|
80
|
+
amount: item.lineItemPrice?.amount || "0",
|
|
81
|
+
formattedAmount: item.lineItemPrice?.formattedAmount || ""
|
|
82
|
+
},
|
|
83
|
+
lineDiscount: {
|
|
84
|
+
amount: "0",
|
|
85
|
+
formattedAmount: ""
|
|
86
|
+
},
|
|
87
|
+
hasDiscount: false
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function calculateTotalDiscount(appliedDiscounts) {
|
|
91
|
+
if (!appliedDiscounts) return 0;
|
|
92
|
+
return appliedDiscounts.reduce(
|
|
93
|
+
(sum, d) => sum + parseFloat(d.merchantDiscount?.amount?.amount || "0"),
|
|
94
|
+
0
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
function calculateItemCount(lineItems) {
|
|
98
|
+
if (!lineItems) return 0;
|
|
99
|
+
return lineItems.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
|
100
|
+
}
|
|
101
|
+
function getEmptySummary() {
|
|
102
|
+
return {
|
|
103
|
+
itemCount: 0,
|
|
104
|
+
subtotal: { amount: "0", formattedAmount: "$0.00" },
|
|
105
|
+
discount: { amount: "0", formattedAmount: "" },
|
|
106
|
+
hasDiscount: false,
|
|
107
|
+
estimatedTax: { amount: "0", formattedAmount: "" },
|
|
108
|
+
showTax: false,
|
|
109
|
+
estimatedTotal: { amount: "0", formattedAmount: "$0.00" },
|
|
110
|
+
currency: "USD"
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function mapCartSummary(cart) {
|
|
114
|
+
if (!cart) {
|
|
115
|
+
return getEmptySummary();
|
|
116
|
+
}
|
|
117
|
+
const totalDiscount = calculateTotalDiscount(cart.appliedDiscounts);
|
|
118
|
+
const itemCount = calculateItemCount(cart.lineItems);
|
|
119
|
+
return {
|
|
120
|
+
itemCount,
|
|
121
|
+
subtotal: {
|
|
122
|
+
amount: "0",
|
|
123
|
+
formattedAmount: "$0.00"
|
|
124
|
+
},
|
|
125
|
+
discount: {
|
|
126
|
+
amount: totalDiscount.toString(),
|
|
127
|
+
formattedAmount: totalDiscount > 0 ? `-$${totalDiscount.toFixed(2)}` : ""
|
|
128
|
+
},
|
|
129
|
+
hasDiscount: totalDiscount > 0,
|
|
130
|
+
estimatedTax: {
|
|
131
|
+
amount: "0",
|
|
132
|
+
formattedAmount: ""
|
|
133
|
+
},
|
|
134
|
+
showTax: false,
|
|
135
|
+
estimatedTotal: {
|
|
136
|
+
amount: "0",
|
|
137
|
+
formattedAmount: "$0.00"
|
|
138
|
+
},
|
|
139
|
+
currency: cart.currency || "USD"
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function mapCartToState(cart) {
|
|
143
|
+
if (!cart) {
|
|
144
|
+
return getEmptyCartState();
|
|
145
|
+
}
|
|
146
|
+
const lineItems = (cart.lineItems || []).map(mapLineItem);
|
|
147
|
+
const appliedCoupon = cart.appliedDiscounts?.find((d) => d.coupon?.code)?.coupon?.code || "";
|
|
148
|
+
return {
|
|
149
|
+
cartId: cart._id || "",
|
|
150
|
+
isEmpty: lineItems.length === 0,
|
|
151
|
+
lineItems,
|
|
152
|
+
summary: mapCartSummary(cart),
|
|
153
|
+
appliedCoupon,
|
|
154
|
+
hasAppliedCoupon: !!appliedCoupon
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function getEmptyCartState() {
|
|
158
|
+
return {
|
|
159
|
+
cartId: "",
|
|
160
|
+
isEmpty: true,
|
|
161
|
+
lineItems: [],
|
|
162
|
+
summary: getEmptySummary(),
|
|
163
|
+
appliedCoupon: "",
|
|
164
|
+
hasAppliedCoupon: false
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function mapCartToIndicator(cart) {
|
|
168
|
+
if (!cart || !cart.lineItems?.length) {
|
|
169
|
+
return {
|
|
170
|
+
itemCount: 0,
|
|
171
|
+
hasItems: false
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const itemCount = calculateItemCount(cart.lineItems);
|
|
175
|
+
return {
|
|
176
|
+
itemCount,
|
|
177
|
+
hasItems: itemCount > 0
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function isCartNotFoundError(error) {
|
|
181
|
+
const err = error;
|
|
182
|
+
if (err?.response?.status === 404) return true;
|
|
183
|
+
if (err?.status === 404) return true;
|
|
184
|
+
if (err?.code === 404) return true;
|
|
185
|
+
const message = err?.message?.toLowerCase() || "";
|
|
186
|
+
if (message.includes("not found") && message.includes("cart")) return true;
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
async function getCurrentCartOrNull(cartClient) {
|
|
190
|
+
try {
|
|
191
|
+
const response = await cartClient.getCurrentCart();
|
|
192
|
+
return response ?? null;
|
|
193
|
+
} catch (error) {
|
|
194
|
+
if (isCartNotFoundError(error)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function estimateCurrentCartTotalsOrNull(cartClient) {
|
|
201
|
+
try {
|
|
202
|
+
const response = await cartClient.estimateCurrentCartTotals({});
|
|
203
|
+
return response ?? null;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
if (isCartNotFoundError(error)) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function mapEstimateTotalsToState(estimate) {
|
|
212
|
+
if (!estimate?.cart) {
|
|
213
|
+
return getEmptyCartState();
|
|
214
|
+
}
|
|
215
|
+
const cart = estimate.cart;
|
|
216
|
+
const lineItems = (cart.lineItems || []).map(mapLineItem);
|
|
217
|
+
const appliedCoupon = cart.appliedDiscounts?.find((d) => d.coupon?.code)?.coupon?.code || "";
|
|
218
|
+
const itemCount = lineItems.reduce((sum, item) => sum + item.quantity, 0);
|
|
219
|
+
const priceSummary = estimate.priceSummary;
|
|
220
|
+
const taxSummary = estimate.taxSummary;
|
|
221
|
+
const discountAmount = parseFloat(priceSummary?.discount?.amount || "0");
|
|
222
|
+
const hasTax = parseFloat(taxSummary?.totalTax?.amount || "0") > 0;
|
|
223
|
+
return {
|
|
224
|
+
cartId: cart._id || "",
|
|
225
|
+
isEmpty: lineItems.length === 0,
|
|
226
|
+
lineItems,
|
|
227
|
+
summary: {
|
|
228
|
+
itemCount,
|
|
229
|
+
subtotal: {
|
|
230
|
+
amount: priceSummary?.subtotal?.amount || "0",
|
|
231
|
+
formattedAmount: priceSummary?.subtotal?.formattedAmount || "$0.00"
|
|
232
|
+
},
|
|
233
|
+
discount: {
|
|
234
|
+
amount: discountAmount.toString(),
|
|
235
|
+
formattedAmount: priceSummary?.discount?.formattedAmount || ""
|
|
236
|
+
},
|
|
237
|
+
hasDiscount: discountAmount > 0,
|
|
238
|
+
estimatedTax: {
|
|
239
|
+
amount: taxSummary?.totalTax?.amount || "0",
|
|
240
|
+
formattedAmount: taxSummary?.totalTax?.formattedAmount || ""
|
|
241
|
+
},
|
|
242
|
+
showTax: hasTax,
|
|
243
|
+
estimatedTotal: {
|
|
244
|
+
amount: priceSummary?.total?.amount || priceSummary?.subtotal?.amount || "0",
|
|
245
|
+
formattedAmount: priceSummary?.total?.formattedAmount || priceSummary?.subtotal?.formattedAmount || "$0.00"
|
|
246
|
+
},
|
|
247
|
+
currency: cart.currency || "USD"
|
|
248
|
+
},
|
|
249
|
+
appliedCoupon,
|
|
250
|
+
hasAppliedCoupon: !!appliedCoupon
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const WIX_STORES_APP_ID = "215238eb-22a5-4c36-9e7b-e7c08025e04e";
|
|
254
|
+
const WIX_CART_CONTEXT = createJayContext();
|
|
255
|
+
function provideWixCartContext() {
|
|
256
|
+
const wixClientContext = useGlobalContext(WIX_CLIENT_CONTEXT);
|
|
257
|
+
const wixClient = wixClientContext.client;
|
|
258
|
+
const cartClient = getCurrentCartClient(wixClient);
|
|
259
|
+
const cartContext = registerReactiveGlobalContext(WIX_CART_CONTEXT, () => {
|
|
260
|
+
const [itemCount, setItemCount] = createSignal(0);
|
|
261
|
+
const [hasItems, setHasItems] = createSignal(false);
|
|
262
|
+
const reactive = useReactive();
|
|
263
|
+
function updateIndicatorFromCart(cart) {
|
|
264
|
+
const indicator = mapCartToIndicator(cart);
|
|
265
|
+
reactive.batchReactions(() => {
|
|
266
|
+
setItemCount(indicator.itemCount);
|
|
267
|
+
setHasItems(indicator.hasItems);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async function refreshCartIndicator() {
|
|
271
|
+
const cart = await getCurrentCartOrNull(cartClient);
|
|
272
|
+
updateIndicatorFromCart(cart);
|
|
273
|
+
}
|
|
274
|
+
async function getEstimatedCart() {
|
|
275
|
+
const estimate = await estimateCurrentCartTotalsOrNull(cartClient);
|
|
276
|
+
return mapEstimateTotalsToState(estimate);
|
|
277
|
+
}
|
|
278
|
+
async function addToCart(productId, quantity = 1, options) {
|
|
279
|
+
console.log(`[WixCart] Adding to cart: ${productId} x ${quantity}`, options);
|
|
280
|
+
const lineItem = {
|
|
281
|
+
catalogReference: {
|
|
282
|
+
catalogItemId: productId,
|
|
283
|
+
appId: WIX_STORES_APP_ID,
|
|
284
|
+
options: {
|
|
285
|
+
variantId: options?.variantId,
|
|
286
|
+
options: options?.modifiers,
|
|
287
|
+
customTextFields: options?.customTextFields
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
quantity
|
|
291
|
+
};
|
|
292
|
+
const result = await cartClient.addToCurrentCart({
|
|
293
|
+
lineItems: [lineItem]
|
|
294
|
+
});
|
|
295
|
+
updateIndicatorFromCart(result.cart ?? null);
|
|
296
|
+
return { cartState: mapCartToState(result.cart ?? null) };
|
|
297
|
+
}
|
|
298
|
+
async function removeLineItems(lineItemIds) {
|
|
299
|
+
const result = await cartClient.removeLineItemsFromCurrentCart(lineItemIds);
|
|
300
|
+
updateIndicatorFromCart(result.cart ?? null);
|
|
301
|
+
return { cartState: mapCartToState(result.cart ?? null) };
|
|
302
|
+
}
|
|
303
|
+
async function updateLineItemQuantity(lineItemId, quantity) {
|
|
304
|
+
let result;
|
|
305
|
+
if (quantity === 0) {
|
|
306
|
+
result = await cartClient.removeLineItemsFromCurrentCart([lineItemId]);
|
|
307
|
+
} else {
|
|
308
|
+
result = await cartClient.updateCurrentCartLineItemQuantity([
|
|
309
|
+
{ _id: lineItemId, quantity }
|
|
310
|
+
]);
|
|
311
|
+
}
|
|
312
|
+
updateIndicatorFromCart(result.cart ?? null);
|
|
313
|
+
return { cartState: mapCartToState(result.cart ?? null) };
|
|
314
|
+
}
|
|
315
|
+
async function clearCart() {
|
|
316
|
+
const cart = await getCurrentCartOrNull(cartClient);
|
|
317
|
+
if (cart?.lineItems?.length) {
|
|
318
|
+
const lineItemIds = cart.lineItems.map((item) => item._id || "").filter(Boolean);
|
|
319
|
+
if (lineItemIds.length > 0) {
|
|
320
|
+
await cartClient.removeLineItemsFromCurrentCart(lineItemIds);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
setItemCount(0);
|
|
324
|
+
setHasItems(false);
|
|
325
|
+
}
|
|
326
|
+
async function applyCoupon(couponCode) {
|
|
327
|
+
const result = await cartClient.updateCurrentCart({ couponCode });
|
|
328
|
+
updateIndicatorFromCart(result ?? null);
|
|
329
|
+
return { cartState: mapCartToState(result ?? null) };
|
|
330
|
+
}
|
|
331
|
+
async function removeCoupon() {
|
|
332
|
+
const result = await cartClient.removeCouponFromCurrentCart();
|
|
333
|
+
updateIndicatorFromCart(result.cart ?? null);
|
|
334
|
+
return { cartState: mapCartToState(result.cart ?? null) };
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
cartIndicator: {
|
|
338
|
+
itemCount,
|
|
339
|
+
hasItems
|
|
340
|
+
},
|
|
341
|
+
refreshCartIndicator,
|
|
342
|
+
getEstimatedCart,
|
|
343
|
+
addToCart,
|
|
344
|
+
removeLineItems,
|
|
345
|
+
updateLineItemQuantity,
|
|
346
|
+
clearCart,
|
|
347
|
+
applyCoupon,
|
|
348
|
+
removeCoupon
|
|
349
|
+
};
|
|
350
|
+
});
|
|
351
|
+
console.log("[wix-cart] Client cart context initialized (reactive)");
|
|
352
|
+
return cartContext;
|
|
353
|
+
}
|
|
354
|
+
function CartIndicatorInteractive(_props, refs, viewStateSignals, _carryForward, cartContext) {
|
|
355
|
+
const { isLoading: [isLoading, setIsLoading], justAdded: [justAdded, setJustAdded] } = viewStateSignals;
|
|
356
|
+
const [prevItemCount, setPrevItemCount] = createSignal(cartContext.cartIndicator.itemCount());
|
|
357
|
+
createEffect(() => {
|
|
358
|
+
const currentCount = cartContext.cartIndicator.itemCount();
|
|
359
|
+
if (currentCount > prevItemCount()) {
|
|
360
|
+
setJustAdded(true);
|
|
361
|
+
setTimeout(() => setJustAdded(false), 1500);
|
|
362
|
+
}
|
|
363
|
+
setPrevItemCount(currentCount);
|
|
364
|
+
});
|
|
365
|
+
refs.cartLink?.onclick(() => {
|
|
366
|
+
});
|
|
367
|
+
return {
|
|
368
|
+
render: () => ({
|
|
369
|
+
// Read directly from reactive global context signals
|
|
370
|
+
itemCount: cartContext.cartIndicator.itemCount(),
|
|
371
|
+
hasItems: cartContext.cartIndicator.hasItems(),
|
|
372
|
+
isLoading: isLoading(),
|
|
373
|
+
justAdded: justAdded()
|
|
374
|
+
})
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const cartIndicator = makeJayStackComponent().withProps().withContexts(WIX_CART_CONTEXT).withInteractive(CartIndicatorInteractive);
|
|
378
|
+
function mapLineItemToViewState(item) {
|
|
379
|
+
return {
|
|
380
|
+
lineItemId: item.lineItemId,
|
|
381
|
+
productId: item.productId,
|
|
382
|
+
productName: item.productName,
|
|
383
|
+
productUrl: item.productUrl,
|
|
384
|
+
variantName: item.variantName,
|
|
385
|
+
sku: item.sku,
|
|
386
|
+
image: item.image,
|
|
387
|
+
quantity: item.quantity,
|
|
388
|
+
isUpdatingQuantity: false,
|
|
389
|
+
unitPrice: item.unitPrice,
|
|
390
|
+
lineTotal: item.lineTotal,
|
|
391
|
+
lineDiscount: item.lineDiscount,
|
|
392
|
+
hasDiscount: item.hasDiscount,
|
|
393
|
+
isRemoving: false
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function mapCartStateToViewState(cart) {
|
|
397
|
+
return {
|
|
398
|
+
isEmpty: cart.isEmpty,
|
|
399
|
+
isLoading: false,
|
|
400
|
+
isCheckingOut: false,
|
|
401
|
+
lineItems: cart.lineItems.map(mapLineItemToViewState),
|
|
402
|
+
summary: {
|
|
403
|
+
itemCount: cart.summary.itemCount,
|
|
404
|
+
subtotal: cart.summary.subtotal,
|
|
405
|
+
discount: cart.summary.discount,
|
|
406
|
+
hasDiscount: cart.summary.hasDiscount,
|
|
407
|
+
estimatedTax: cart.summary.estimatedTax,
|
|
408
|
+
showTax: cart.summary.showTax,
|
|
409
|
+
estimatedTotal: cart.summary.estimatedTotal,
|
|
410
|
+
currency: cart.summary.currency
|
|
411
|
+
},
|
|
412
|
+
coupon: {
|
|
413
|
+
code: "",
|
|
414
|
+
isApplying: false,
|
|
415
|
+
appliedCode: cart.appliedCoupon,
|
|
416
|
+
hasAppliedCoupon: cart.hasAppliedCoupon,
|
|
417
|
+
errorMessage: "",
|
|
418
|
+
hasError: false
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function CartPageInteractive(_props, refs, viewStateSignals, _carryForward, cartContext) {
|
|
423
|
+
const { isEmpty: [isEmpty, setIsEmpty], isLoading: [isLoading, setIsLoading], isCheckingOut: [isCheckingOut, setIsCheckingOut], lineItems: [lineItems, setLineItems], summary: [summary, setSummary], coupon: [coupon, setCoupon] } = viewStateSignals;
|
|
424
|
+
async function loadCart() {
|
|
425
|
+
try {
|
|
426
|
+
setIsLoading(true);
|
|
427
|
+
const cartState = await cartContext.getEstimatedCart();
|
|
428
|
+
const viewState = mapCartStateToViewState(cartState);
|
|
429
|
+
setIsEmpty(viewState.isEmpty);
|
|
430
|
+
setLineItems(viewState.lineItems);
|
|
431
|
+
setSummary(viewState.summary);
|
|
432
|
+
setCoupon(viewState.coupon);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
console.error("[CartPage] Failed to load cart:", error);
|
|
435
|
+
} finally {
|
|
436
|
+
setIsLoading(false);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async function handleQuantityChange(lineItemId, newQuantity) {
|
|
440
|
+
const itemIndex = lineItems().findIndex((item) => item.lineItemId === lineItemId);
|
|
441
|
+
if (itemIndex === -1)
|
|
442
|
+
return;
|
|
443
|
+
setLineItems(patch(lineItems(), [
|
|
444
|
+
{ op: REPLACE, path: [itemIndex, "isUpdatingQuantity"], value: true }
|
|
445
|
+
]));
|
|
446
|
+
try {
|
|
447
|
+
await cartContext.updateLineItemQuantity(lineItemId, newQuantity);
|
|
448
|
+
await loadCart();
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.error("[CartPage] Failed to update quantity:", error);
|
|
451
|
+
setLineItems(patch(lineItems(), [
|
|
452
|
+
{ op: REPLACE, path: [itemIndex, "isUpdatingQuantity"], value: false }
|
|
453
|
+
]));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
async function handleRemoveItem(lineItemId) {
|
|
457
|
+
const itemIndex = lineItems().findIndex((item) => item.lineItemId === lineItemId);
|
|
458
|
+
if (itemIndex === -1)
|
|
459
|
+
return;
|
|
460
|
+
setLineItems(patch(lineItems(), [{ op: REPLACE, path: [itemIndex, "isRemoving"], value: true }]));
|
|
461
|
+
try {
|
|
462
|
+
await cartContext.removeLineItems([lineItemId]);
|
|
463
|
+
await loadCart();
|
|
464
|
+
} catch (error) {
|
|
465
|
+
console.error("[CartPage] Failed to remove item:", error);
|
|
466
|
+
setLineItems(patch(lineItems(), [
|
|
467
|
+
{ op: REPLACE, path: [itemIndex, "isRemoving"], value: false }
|
|
468
|
+
]));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function handleClearCart() {
|
|
472
|
+
try {
|
|
473
|
+
setIsLoading(true);
|
|
474
|
+
await cartContext.clearCart();
|
|
475
|
+
await loadCart();
|
|
476
|
+
} catch (error) {
|
|
477
|
+
console.error("[CartPage] Failed to clear cart:", error);
|
|
478
|
+
setIsLoading(false);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async function handleApplyCoupon() {
|
|
482
|
+
const code = coupon().code.trim();
|
|
483
|
+
if (!code)
|
|
484
|
+
return;
|
|
485
|
+
setCoupon(patch(coupon(), [
|
|
486
|
+
{ op: REPLACE, path: ["isApplying"], value: true },
|
|
487
|
+
{ op: REPLACE, path: ["hasError"], value: false },
|
|
488
|
+
{ op: REPLACE, path: ["errorMessage"], value: "" }
|
|
489
|
+
]));
|
|
490
|
+
try {
|
|
491
|
+
await cartContext.applyCoupon(code);
|
|
492
|
+
await loadCart();
|
|
493
|
+
} catch (error) {
|
|
494
|
+
console.error("[CartPage] Failed to apply coupon:", error);
|
|
495
|
+
setCoupon(patch(coupon(), [
|
|
496
|
+
{ op: REPLACE, path: ["isApplying"], value: false },
|
|
497
|
+
{ op: REPLACE, path: ["hasError"], value: true },
|
|
498
|
+
{ op: REPLACE, path: ["errorMessage"], value: "Invalid coupon code" }
|
|
499
|
+
]));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
async function handleRemoveCoupon() {
|
|
503
|
+
try {
|
|
504
|
+
await cartContext.removeCoupon();
|
|
505
|
+
await loadCart();
|
|
506
|
+
} catch (error) {
|
|
507
|
+
console.error("[CartPage] Failed to remove coupon:", error);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
async function handleCheckout() {
|
|
511
|
+
setIsCheckingOut(true);
|
|
512
|
+
window.location.href = "/checkout";
|
|
513
|
+
}
|
|
514
|
+
refs.clearCartButton?.onclick(handleClearCart);
|
|
515
|
+
refs.checkoutButton?.onclick(handleCheckout);
|
|
516
|
+
refs.coupon?.code?.oninput(({ event }) => {
|
|
517
|
+
const code = event.target.value;
|
|
518
|
+
setCoupon(patch(coupon(), [{ op: REPLACE, path: ["code"], value: code }]));
|
|
519
|
+
});
|
|
520
|
+
refs.coupon?.applyButton?.onclick(handleApplyCoupon);
|
|
521
|
+
refs.coupon?.removeButton?.onclick(handleRemoveCoupon);
|
|
522
|
+
refs.lineItems?.quantity.oninput(({ event, coordinate }) => {
|
|
523
|
+
const lineItemId = coordinate[0];
|
|
524
|
+
const newQty = parseInt(event.target.value) || 1;
|
|
525
|
+
handleQuantityChange(lineItemId, newQty);
|
|
526
|
+
});
|
|
527
|
+
refs.lineItems?.decrementButton.onclick(({ viewState }) => {
|
|
528
|
+
if (viewState.quantity > 1) {
|
|
529
|
+
handleQuantityChange(viewState.lineItemId, viewState.quantity - 1);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
refs.lineItems?.incrementButton.onclick(({ viewState }) => {
|
|
533
|
+
handleQuantityChange(viewState.lineItemId, viewState.quantity + 1);
|
|
534
|
+
});
|
|
535
|
+
refs.lineItems?.removeButton.onclick(({ viewState }) => {
|
|
536
|
+
handleRemoveItem(viewState.lineItemId);
|
|
537
|
+
});
|
|
538
|
+
loadCart();
|
|
539
|
+
return {
|
|
540
|
+
render: () => ({
|
|
541
|
+
isEmpty: isEmpty(),
|
|
542
|
+
isLoading: isLoading(),
|
|
543
|
+
isCheckingOut: isCheckingOut(),
|
|
544
|
+
lineItems: lineItems(),
|
|
545
|
+
summary: summary(),
|
|
546
|
+
coupon: coupon()
|
|
547
|
+
})
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
const cartPage = makeJayStackComponent().withProps().withContexts(WIX_CART_CONTEXT).withInteractive(CartPageInteractive);
|
|
551
|
+
const init = makeJayInit().withClient(async (data) => {
|
|
552
|
+
console.log("[wix-cart] Initializing client-side cart context...");
|
|
553
|
+
const { enableClientCart } = data;
|
|
554
|
+
const cartContext = provideWixCartContext();
|
|
555
|
+
if (enableClientCart) {
|
|
556
|
+
await cartContext.refreshCartIndicator();
|
|
557
|
+
}
|
|
558
|
+
console.log("[wix-cart] Client initialization complete");
|
|
559
|
+
console.log(`[wix-cart] Cart enabled: ${enableClientCart}`);
|
|
560
|
+
});
|
|
561
|
+
export {
|
|
562
|
+
WIX_CART_CONTEXT,
|
|
563
|
+
cartIndicator,
|
|
564
|
+
cartPage,
|
|
565
|
+
init
|
|
566
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { WixClient } from '@wix/sdk';
|
|
2
|
+
import { W as WixCartService } from './index.client-VoD54p4E.js';
|
|
3
|
+
export { A as AddToCartOptions, q as CartIndicatorState, n as CartLineItem, C as CartOperationResult, l as CartState, o as CartSummary, R as ReactiveCartIndicator, b as WIX_CART_CONTEXT, a as WIX_CART_SERVICE, c as WixCartContext, d as WixCartInitData, r as cartIndicator, s as cartPage, j as estimateCurrentCartTotalsOrNull, i as getCurrentCartOrNull, h as getEmptyCartState, t as init, e as mapCartSummary, g as mapCartToIndicator, f as mapCartToState, k as mapEstimateTotalsToState, m as mapLineItem, p as provideWixCartContext } from './index.client-VoD54p4E.js';
|
|
4
|
+
import { currentCart } from '@wix/ecom';
|
|
5
|
+
import { BuildDescriptors } from '@wix/sdk-types';
|
|
6
|
+
import '@jay-framework/runtime';
|
|
7
|
+
import '@jay-framework/reactive';
|
|
8
|
+
import '@wix/auto_sdk_ecom_current-cart';
|
|
9
|
+
import '@jay-framework/fullstack-component';
|
|
10
|
+
import '@jay-framework/component';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wix Cart Client Factory
|
|
14
|
+
*
|
|
15
|
+
* Creates a singleton instance of the Wix Cart client.
|
|
16
|
+
* Used by both server service and client context.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get a configured Wix eCommerce Current Cart client (singleton)
|
|
21
|
+
*
|
|
22
|
+
* The Current Cart API allows you to manage the visitor's shopping cart.
|
|
23
|
+
*
|
|
24
|
+
* @returns Current Cart client instance from @wix/ecom
|
|
25
|
+
* @see https://dev.wix.com/docs/sdk/backend-modules/ecom/current-cart/introduction
|
|
26
|
+
*/
|
|
27
|
+
declare function getCurrentCartClient(wixClient: WixClient): BuildDescriptors<typeof currentCart, {}>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Server-side Wix Cart Service
|
|
31
|
+
*
|
|
32
|
+
* Provides access to Wix Cart APIs on the server using API Key authentication.
|
|
33
|
+
* This file contains server-only code (registerService).
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates, registers, and returns a Wix Cart service instance.
|
|
38
|
+
* Called during server initialization.
|
|
39
|
+
*/
|
|
40
|
+
declare function provideWixCartService(wixClient: WixClient): WixCartService;
|
|
41
|
+
|
|
42
|
+
export { WixCartService, getCurrentCartClient, provideWixCartService };
|