@cimplify/sdk 0.100.0 → 0.100.2
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/advanced.d.mts +3 -3
- package/dist/advanced.d.ts +3 -3
- package/dist/advanced.js +22 -22
- package/dist/advanced.mjs +3 -3
- package/dist/{chunk-X6G6QERR.js → chunk-6JEKKL3M.js} +53 -38
- package/dist/{chunk-7TEMLVDT.mjs → chunk-B2YOCOLH.mjs} +2 -2
- package/dist/{chunk-V5TU5JKA.mjs → chunk-DLW3RWRK.mjs} +30 -6
- package/dist/{chunk-FECGCIVV.js → chunk-IFAQLFAJ.js} +4 -4
- package/dist/{chunk-J3UYIBKH.js → chunk-IJI7KMTG.js} +89 -82
- package/dist/{chunk-AETVRBKI.js → chunk-NIOUSKQQ.js} +1 -0
- package/dist/{chunk-K5EN2W2Q.mjs → chunk-O3YUHLZL.mjs} +1 -0
- package/dist/{chunk-GWKIDFN6.mjs → chunk-PJUXAUM2.mjs} +20 -14
- package/dist/{chunk-ZA5VKVUB.js → chunk-QRBBSIAY.js} +4 -4
- package/dist/{chunk-OX5L6XE7.mjs → chunk-TIQLOH2B.mjs} +30 -15
- package/dist/{chunk-64EDCDBR.js → chunk-WMEC5JWN.js} +32 -7
- package/dist/{chunk-I7L4VRHI.mjs → chunk-YWNJJO2U.mjs} +2 -2
- package/dist/{client-DQpvwf-p.d.mts → client-C6wb92AD.d.mts} +2 -2
- package/dist/{client-DmR3Z6pQ.d.ts → client-CgXkRu2o.d.ts} +2 -2
- package/dist/{client-C1vS5pf0.d.ts → client-D8XcrONA.d.mts} +38 -26
- package/dist/{client-B5MeD8da.d.mts → client-LPg_AUUr.d.ts} +38 -26
- package/dist/{index-Br-vFygC.d.ts → index-B_RYmvlF.d.ts} +3 -3
- package/dist/{index-DUOCn1Cs.d.mts → index-D-LUYk1Y.d.mts} +3 -3
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +117 -117
- package/dist/index.mjs +5 -5
- package/dist/mock/cli.mjs +1 -1
- package/dist/mock/library.js +1 -1
- package/dist/mock/library.mjs +1 -1
- package/dist/mock/msw.js +1 -1
- package/dist/mock/msw.mjs +1 -1
- package/dist/{payment-BIpS9oPG.d.mts → payment-BSzy7sly.d.mts} +1 -1
- package/dist/{payment-CsuBKHqg.d.ts → payment-jK8ztICx.d.ts} +1 -1
- package/dist/{price-Ml_uPoiY.d.ts → price-CJLfcYKd.d.ts} +1 -1
- package/dist/{price-IOOoVYS-.d.mts → price-D96h6c6L.d.mts} +1 -1
- package/dist/{product-BKV6TBj_.d.ts → product-CeV8euw4.d.mts} +23 -11
- package/dist/{product-BKV6TBj_.d.mts → product-CeV8euw4.d.ts} +23 -11
- package/dist/react.d.mts +10 -5
- package/dist/react.d.ts +10 -5
- package/dist/react.js +856 -450
- package/dist/react.mjs +737 -331
- package/dist/{server-C0iEk55_.d.mts → server--RyVAfPN.d.mts} +1 -1
- package/dist/{server-CfpYhSV_.d.ts → server-CNyy47HL.d.ts} +1 -1
- package/dist/server.d.mts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +10 -10
- package/dist/server.mjs +4 -4
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +3 -3
- package/dist/testing/msw.mjs +2 -2
- package/dist/testing/suite.d.mts +6 -6
- package/dist/testing/suite.d.ts +6 -6
- package/dist/testing/suite.js +25 -25
- package/dist/testing/suite.mjs +6 -6
- package/dist/testing.d.mts +5 -5
- package/dist/testing.d.ts +5 -5
- package/dist/testing.js +81 -81
- package/dist/testing.mjs +7 -7
- package/dist/utils.d.mts +4 -4
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +29 -29
- package/dist/utils.mjs +2 -2
- package/package.json +1 -1
- package/registry/cart-summary.json +1 -1
- package/registry/product-customizer.json +1 -1
- package/registry/sale-badge.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -3,10 +3,10 @@ import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, isMultiDayServi
|
|
|
3
3
|
export { getVariantDisplayName } from './chunk-4G5UWFC3.mjs';
|
|
4
4
|
import { splitGenUIContent, messagePreview } from './chunk-U4MW6PYU.mjs';
|
|
5
5
|
export { messagePreview, splitGenUIContent } from './chunk-U4MW6PYU.mjs';
|
|
6
|
-
import { SESSION_CLAIMED, SESSION_CLEARED, createCimplifyClient, MUTATION_ERROR } from './chunk-
|
|
7
|
-
import { generateIdempotencyKey, ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-
|
|
8
|
-
import { parsePrice, formatMoney, formatPrice, getProductCurrency, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-
|
|
9
|
-
import { isSupportedCurrency, CimplifyError } from './chunk-
|
|
6
|
+
import { SESSION_CLAIMED, SESSION_CLEARED, createCimplifyClient, AUTH_TOKEN_CHANGED, MUTATION_ERROR } from './chunk-PJUXAUM2.mjs';
|
|
7
|
+
import { generateIdempotencyKey, ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-TIQLOH2B.mjs';
|
|
8
|
+
import { parsePrice, formatMoney, formatPrice, getVariantUnitPrice, getProductCurrency, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-DLW3RWRK.mjs';
|
|
9
|
+
import { isSupportedCurrency, currencyCode, CimplifyError } from './chunk-O3YUHLZL.mjs';
|
|
10
10
|
import './chunk-3G6RQLXK.mjs';
|
|
11
11
|
import React14, { createContext, lazy, memo, Suspense, useMemo, useContext, useRef, useCallback, useState, useEffect, useId, useSyncExternalStore } from 'react';
|
|
12
12
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -355,6 +355,26 @@ function Ad({
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
// src/utils/cart-transform.ts
|
|
358
|
+
var DECIMAL_AMOUNT_PATTERN = /^([+-]?)(\d+)(?:\.(\d+))?$/;
|
|
359
|
+
function multiplyDecimalByInteger(value, multiplier) {
|
|
360
|
+
if (!Number.isSafeInteger(multiplier) || multiplier < 0) {
|
|
361
|
+
throw new RangeError("Cart item quantity must be a non-negative safe integer");
|
|
362
|
+
}
|
|
363
|
+
const source = String(value).trim();
|
|
364
|
+
const match = DECIMAL_AMOUNT_PATTERN.exec(source);
|
|
365
|
+
if (!match) {
|
|
366
|
+
throw new TypeError("Cart item price must be a decimal amount");
|
|
367
|
+
}
|
|
368
|
+
const [, sign, whole, fraction = ""] = match;
|
|
369
|
+
const scaledAmount = BigInt(`${sign}${whole}${fraction}`);
|
|
370
|
+
const scaledTotal = scaledAmount * BigInt(multiplier);
|
|
371
|
+
if (fraction.length === 0) return scaledTotal.toString();
|
|
372
|
+
const negative = scaledTotal < 0n;
|
|
373
|
+
const digits = (negative ? -scaledTotal : scaledTotal).toString().padStart(fraction.length + 1, "0");
|
|
374
|
+
const splitAt = digits.length - fraction.length;
|
|
375
|
+
const formatted = `${digits.slice(0, splitAt)}.${digits.slice(splitAt)}`;
|
|
376
|
+
return negative ? `-${formatted}` : formatted;
|
|
377
|
+
}
|
|
358
378
|
function getSelections(item) {
|
|
359
379
|
if (item.bundle_resolved?.selections?.length) {
|
|
360
380
|
return item.bundle_resolved.selections.map((s) => ({
|
|
@@ -373,11 +393,12 @@ function getSelections(item) {
|
|
|
373
393
|
}
|
|
374
394
|
function mapItem(item, fx) {
|
|
375
395
|
const amt = (value) => fx ? String(fx.convertPrice(value)) : String(value);
|
|
396
|
+
const currentUnitPrice = item.total_price;
|
|
376
397
|
const result = {
|
|
377
398
|
name: item.name,
|
|
378
399
|
quantity: item.quantity,
|
|
379
|
-
unit_price: amt(
|
|
380
|
-
total_price: amt(item.
|
|
400
|
+
unit_price: amt(currentUnitPrice),
|
|
401
|
+
total_price: amt(multiplyDecimalByInteger(currentUnitPrice, item.quantity)),
|
|
381
402
|
line_type: item.line_type
|
|
382
403
|
};
|
|
383
404
|
if (item.image_url) result.image_url = item.image_url;
|
|
@@ -398,9 +419,9 @@ function mapItem(item, fx) {
|
|
|
398
419
|
}
|
|
399
420
|
return result;
|
|
400
421
|
}
|
|
401
|
-
function transformToCheckoutCart(cart, fx) {
|
|
422
|
+
function transformToCheckoutCart(cart, fx, checkout) {
|
|
402
423
|
const amt = (value) => fx ? String(fx.convertPrice(value)) : String(value);
|
|
403
|
-
|
|
424
|
+
const checkoutCart = {
|
|
404
425
|
items: cart.items.map((item) => mapItem(item, fx)),
|
|
405
426
|
subtotal: amt(cart.pricing.subtotal),
|
|
406
427
|
tax_amount: amt(cart.pricing.tax_amount),
|
|
@@ -413,6 +434,7 @@ function transformToCheckoutCart(cart, fx) {
|
|
|
413
434
|
deposit_amount: cart.pricing.deposit_amount != null ? amt(cart.pricing.deposit_amount) : void 0,
|
|
414
435
|
requires_delivery: cart.requires_delivery
|
|
415
436
|
};
|
|
437
|
+
return checkoutCart;
|
|
416
438
|
}
|
|
417
439
|
|
|
418
440
|
// src/internal/collection.ts
|
|
@@ -574,10 +596,20 @@ function lineKeyFromItem(item) {
|
|
|
574
596
|
staffId: item.staffId
|
|
575
597
|
});
|
|
576
598
|
}
|
|
577
|
-
function attachInternalItemMetadata(item) {
|
|
578
|
-
|
|
599
|
+
function attachInternalItemMetadata(item, metadata = {}) {
|
|
600
|
+
const internalItem = {
|
|
601
|
+
...item,
|
|
602
|
+
_lineKey: lineKeyFromItem(item)
|
|
603
|
+
};
|
|
604
|
+
if (metadata.quotedUnitPrice !== void 0) {
|
|
605
|
+
internalItem._quotedUnitPrice = metadata.quotedUnitPrice;
|
|
606
|
+
}
|
|
607
|
+
return internalItem;
|
|
579
608
|
}
|
|
580
609
|
function calculateLineSubtotal(item) {
|
|
610
|
+
if (item._quotedUnitPrice !== void 0) {
|
|
611
|
+
return roundMoney(parsePrice(item._quotedUnitPrice) * item.quantity);
|
|
612
|
+
}
|
|
581
613
|
let unitPrice = parsePrice(item.product.default_price);
|
|
582
614
|
if (item.variant?.price_adjustment) {
|
|
583
615
|
unitPrice += parsePrice(item.variant.price_adjustment);
|
|
@@ -653,10 +685,7 @@ function emptyBase(currency, isLoading = true) {
|
|
|
653
685
|
return { items: [], subtotal: 0, tax: 0, total: 0, currency, isLoading };
|
|
654
686
|
}
|
|
655
687
|
function buildStoreKey(client, locationId) {
|
|
656
|
-
return [
|
|
657
|
-
client.getPublicKey() || "__no_public_key__",
|
|
658
|
-
locationId || "__no_location__"
|
|
659
|
-
].join(":");
|
|
688
|
+
return [client.getPublicKey() || "__no_public_key__", locationId || "__no_location__"].join(":");
|
|
660
689
|
}
|
|
661
690
|
function purgeLegacyCartCachesOnce() {
|
|
662
691
|
if (legacyCachesPurged) return;
|
|
@@ -685,7 +714,10 @@ function toProductFromServerItem(item, businessId) {
|
|
|
685
714
|
slug: item.item_id,
|
|
686
715
|
description: item.description,
|
|
687
716
|
image_url: item.image_url,
|
|
688
|
-
|
|
717
|
+
// Cart price_info is the accepted item price path. Do not reconstruct it
|
|
718
|
+
// from the catalogue base after a sale or shopper-specific quote.
|
|
719
|
+
default_price: item.price_info.final_price,
|
|
720
|
+
currency: item.price_info.currency,
|
|
689
721
|
type: "product",
|
|
690
722
|
inventory_type: "none",
|
|
691
723
|
variant_strategy: "fetch_all",
|
|
@@ -700,37 +732,44 @@ function mapServerCart(serverCart, fallbackCurrency) {
|
|
|
700
732
|
const variant = item.variant_info || item.variant_name ? {
|
|
701
733
|
id: item.variant_info?.id || item.variant_id || "",
|
|
702
734
|
name: item.variant_info?.name || item.variant_name || "Variant",
|
|
703
|
-
price_adjustment: item.variant_info?.price_adjustment
|
|
735
|
+
price_adjustment: item.variant_info?.price_adjustment,
|
|
736
|
+
price_info: item.price_info
|
|
704
737
|
} : void 0;
|
|
705
738
|
const quoteId = typeof item.metadata?.quote_id === "string" ? item.metadata.quote_id : void 0;
|
|
706
|
-
return attachInternalItemMetadata(
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
739
|
+
return attachInternalItemMetadata(
|
|
740
|
+
{
|
|
741
|
+
id: item.id,
|
|
742
|
+
product: toProductFromServerItem(item, serverCart.business_id),
|
|
743
|
+
quantity: clampQuantity(item.quantity),
|
|
744
|
+
locationId: serverCart.location_id,
|
|
745
|
+
variantId: item.variant_id,
|
|
746
|
+
variant,
|
|
747
|
+
quoteId,
|
|
748
|
+
addOnOptionIds: normalizeOptionIds(item.add_on_option_ids || void 0),
|
|
749
|
+
addOnOptions: (item.add_on_options || []).map((option) => ({
|
|
750
|
+
id: option.id,
|
|
751
|
+
name: option.name,
|
|
752
|
+
default_price: option.price
|
|
753
|
+
})),
|
|
754
|
+
bundleSelections: item.bundle_selections || void 0,
|
|
755
|
+
compositeSelections: item.composite_selections || void 0,
|
|
756
|
+
specialInstructions: item.special_instructions,
|
|
757
|
+
scheduledStart: item.scheduled_start || void 0,
|
|
758
|
+
scheduledEnd: item.scheduled_end || void 0,
|
|
759
|
+
staffId: item.staff_id || void 0,
|
|
760
|
+
units: item.units || void 0,
|
|
761
|
+
lineType: item.line_type,
|
|
762
|
+
confirmationCode: item.confirmation_code || void 0,
|
|
763
|
+
serviceStatus: item.service_status || void 0,
|
|
764
|
+
bundleResolved: item.bundle_resolved || void 0,
|
|
765
|
+
compositeResolved: item.composite_resolved || void 0,
|
|
766
|
+
customerInputs: item.customer_inputs || void 0
|
|
767
|
+
},
|
|
768
|
+
// CartItemDetails.total_price is the accepted configured unit amount,
|
|
769
|
+
// including add-ons. It is the only safe basis for optimistic quantity
|
|
770
|
+
// changes and line rendering after the server replaces the temp item.
|
|
771
|
+
{ quotedUnitPrice: item.total_price }
|
|
772
|
+
);
|
|
734
773
|
});
|
|
735
774
|
return {
|
|
736
775
|
items,
|
|
@@ -871,24 +910,27 @@ async function performAddItem(entry, product, quantity, options) {
|
|
|
871
910
|
addOnOptionIds: normalizeOptionIds(options.addOnOptionIds)
|
|
872
911
|
};
|
|
873
912
|
const lineKey = buildLineKey(product.id, normalizedOptions);
|
|
874
|
-
const tempItem = attachInternalItemMetadata(
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
913
|
+
const tempItem = attachInternalItemMetadata(
|
|
914
|
+
{
|
|
915
|
+
id: `tmp_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
|
|
916
|
+
product,
|
|
917
|
+
quantity: resolvedQuantity,
|
|
918
|
+
locationId: normalizedOptions.locationId,
|
|
919
|
+
variantId: normalizedOptions.variantId,
|
|
920
|
+
variant: normalizedOptions.variant,
|
|
921
|
+
quoteId: normalizedOptions.quoteId,
|
|
922
|
+
addOnOptionIds: normalizedOptions.addOnOptionIds,
|
|
923
|
+
addOnOptions: normalizedOptions.addOnOptions,
|
|
924
|
+
bundleSelections: normalizedOptions.bundleSelections,
|
|
925
|
+
compositeSelections: normalizedOptions.compositeSelections,
|
|
926
|
+
specialInstructions: normalizedOptions.specialInstructions,
|
|
927
|
+
scheduledStart: normalizedOptions.scheduledStart,
|
|
928
|
+
scheduledEnd: normalizedOptions.scheduledEnd,
|
|
929
|
+
staffId: normalizedOptions.staffId,
|
|
930
|
+
units: normalizedOptions.units
|
|
931
|
+
},
|
|
932
|
+
{ quotedUnitPrice: normalizedOptions.quotedUnitPrice }
|
|
933
|
+
);
|
|
892
934
|
const idempotencyKey = `idem_${typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`}`;
|
|
893
935
|
const patch = { kind: "add_item", tempItem, lineKey };
|
|
894
936
|
const useOptimistic = entry.optimisticEnabled;
|
|
@@ -998,12 +1040,12 @@ async function performReorder(entry, orderId) {
|
|
|
998
1040
|
return result.value;
|
|
999
1041
|
}
|
|
1000
1042
|
function useCart(options = {}) {
|
|
1001
|
-
const context =
|
|
1043
|
+
const context = useOptionalCimplify();
|
|
1002
1044
|
const client = options.client ?? context?.client;
|
|
1003
1045
|
if (!client) {
|
|
1004
1046
|
throw new Error("useCart must be used within CimplifyProvider or passed { client }.");
|
|
1005
1047
|
}
|
|
1006
|
-
const locationId = options.locationId ?? client.getLocationId();
|
|
1048
|
+
const locationId = options.locationId ?? (context?.client === client ? context.currentLocation?.id ?? (context.isReady ? null : client.getLocationId()) : client.getLocationId());
|
|
1007
1049
|
const currency = options.currency ?? context?.currency ?? "USD";
|
|
1008
1050
|
const optimisticEnabled = context?.optimistic ?? true;
|
|
1009
1051
|
const entry = useMemo(
|
|
@@ -1016,6 +1058,11 @@ function useCart(options = {}) {
|
|
|
1016
1058
|
[client, currency, locationId, optimisticEnabled]
|
|
1017
1059
|
);
|
|
1018
1060
|
const view = useCollectionStore(entry.collection);
|
|
1061
|
+
useEffect(() => {
|
|
1062
|
+
if (client.getLocationId() !== locationId) {
|
|
1063
|
+
client.setLocationId(locationId);
|
|
1064
|
+
}
|
|
1065
|
+
}, [client, locationId]);
|
|
1019
1066
|
useEffect(() => {
|
|
1020
1067
|
void ensureInitialized(entry);
|
|
1021
1068
|
}, [entry]);
|
|
@@ -4875,19 +4922,53 @@ function useSearch(options = {}) {
|
|
|
4875
4922
|
}
|
|
4876
4923
|
var quoteCache = /* @__PURE__ */ new Map();
|
|
4877
4924
|
var quoteInflight = /* @__PURE__ */ new Map();
|
|
4878
|
-
|
|
4879
|
-
|
|
4925
|
+
var quoteClientNamespaces = /* @__PURE__ */ new WeakMap();
|
|
4926
|
+
var nextQuoteClientNamespace = 0;
|
|
4927
|
+
var QUOTE_LOAD_DEBOUNCE_MS = 150;
|
|
4928
|
+
var MIN_AUTO_REFRESH_DELAY_MS = 1e3;
|
|
4929
|
+
function getQuoteClientNamespace(client) {
|
|
4930
|
+
const existing = quoteClientNamespaces.get(client);
|
|
4931
|
+
if (existing !== void 0) {
|
|
4932
|
+
return existing;
|
|
4933
|
+
}
|
|
4934
|
+
const namespace = nextQuoteClientNamespace++;
|
|
4935
|
+
quoteClientNamespaces.set(client, namespace);
|
|
4936
|
+
return namespace;
|
|
4937
|
+
}
|
|
4938
|
+
function buildQuoteCacheKey(client, sessionEpoch, locationId, inputSignature) {
|
|
4939
|
+
return JSON.stringify([
|
|
4940
|
+
getQuoteClientNamespace(client),
|
|
4941
|
+
client.getPublicKey(),
|
|
4942
|
+
sessionEpoch,
|
|
4943
|
+
locationId ?? "",
|
|
4944
|
+
inputSignature
|
|
4945
|
+
]);
|
|
4946
|
+
}
|
|
4947
|
+
function useClientSessionEpoch(client) {
|
|
4948
|
+
const subscribe = useCallback(
|
|
4949
|
+
(notify) => {
|
|
4950
|
+
const unsubscribe = [
|
|
4951
|
+
client.events.on(AUTH_TOKEN_CHANGED, notify),
|
|
4952
|
+
client.events.on(SESSION_CLAIMED, notify),
|
|
4953
|
+
client.events.on(SESSION_CLEARED, notify)
|
|
4954
|
+
];
|
|
4955
|
+
return () => {
|
|
4956
|
+
for (const stopListening of unsubscribe) {
|
|
4957
|
+
stopListening();
|
|
4958
|
+
}
|
|
4959
|
+
};
|
|
4960
|
+
},
|
|
4961
|
+
[client]
|
|
4962
|
+
);
|
|
4963
|
+
const getSnapshot = useCallback(() => client.getSessionEpoch(), [client]);
|
|
4964
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
4880
4965
|
}
|
|
4881
4966
|
function serializeQuoteBundleSelections(selections) {
|
|
4882
4967
|
if (!selections || selections.length === 0) {
|
|
4883
4968
|
return "";
|
|
4884
4969
|
}
|
|
4885
4970
|
return selections.map((selection) => {
|
|
4886
|
-
return [
|
|
4887
|
-
selection.component_id,
|
|
4888
|
-
selection.quantity,
|
|
4889
|
-
selection.variant_id ?? ""
|
|
4890
|
-
].join(":");
|
|
4971
|
+
return [selection.component_id, selection.quantity, selection.variant_id ?? ""].join(":");
|
|
4891
4972
|
}).join(";");
|
|
4892
4973
|
}
|
|
4893
4974
|
function serializeQuoteCompositeSelections(selections) {
|
|
@@ -4917,15 +4998,37 @@ function buildQuoteInputSignature(input) {
|
|
|
4917
4998
|
serializeQuoteCompositeSelections(input.compositeSelections)
|
|
4918
4999
|
].join("|");
|
|
4919
5000
|
}
|
|
4920
|
-
function
|
|
5001
|
+
function quoteExpiryTimestamp(quote) {
|
|
4921
5002
|
if (!quote?.expires_at) {
|
|
4922
|
-
return
|
|
5003
|
+
return Number.NaN;
|
|
4923
5004
|
}
|
|
4924
5005
|
const expiresAt = Date.parse(quote.expires_at);
|
|
4925
|
-
|
|
4926
|
-
|
|
5006
|
+
return Number.isFinite(expiresAt) ? expiresAt : Number.NaN;
|
|
5007
|
+
}
|
|
5008
|
+
function isQuoteExpired(quote, now = Date.now()) {
|
|
5009
|
+
const expiresAt = quoteExpiryTimestamp(quote);
|
|
5010
|
+
return Number.isFinite(expiresAt) && expiresAt <= now;
|
|
5011
|
+
}
|
|
5012
|
+
function readUsableCachedQuote(cacheKey2) {
|
|
5013
|
+
const entry = quoteCache.get(cacheKey2);
|
|
5014
|
+
if (!entry) {
|
|
5015
|
+
return null;
|
|
4927
5016
|
}
|
|
4928
|
-
|
|
5017
|
+
if (isQuoteExpired(entry.quote)) {
|
|
5018
|
+
quoteCache.delete(cacheKey2);
|
|
5019
|
+
return null;
|
|
5020
|
+
}
|
|
5021
|
+
return entry.quote;
|
|
5022
|
+
}
|
|
5023
|
+
function requestState(cacheKey2, quote, isLoading) {
|
|
5024
|
+
return {
|
|
5025
|
+
cacheKey: cacheKey2,
|
|
5026
|
+
quote,
|
|
5027
|
+
isLoading,
|
|
5028
|
+
error: null,
|
|
5029
|
+
isExpired: false,
|
|
5030
|
+
messages: quote?.ui_messages ?? []
|
|
5031
|
+
};
|
|
4929
5032
|
}
|
|
4930
5033
|
function normalizeInput(input, fallbackLocationId) {
|
|
4931
5034
|
const productId = input.productId.trim();
|
|
@@ -4942,7 +5045,7 @@ function normalizeInput(input, fallbackLocationId) {
|
|
|
4942
5045
|
};
|
|
4943
5046
|
}
|
|
4944
5047
|
function useQuote(input, options = {}) {
|
|
4945
|
-
const context =
|
|
5048
|
+
const context = useOptionalCimplify();
|
|
4946
5049
|
const client = options.client ?? context?.client;
|
|
4947
5050
|
if (!client) {
|
|
4948
5051
|
throw new Error("useQuote must be used within CimplifyProvider or passed { client }.");
|
|
@@ -4950,59 +5053,58 @@ function useQuote(input, options = {}) {
|
|
|
4950
5053
|
const enabled = options.enabled ?? true;
|
|
4951
5054
|
const autoRefresh = options.autoRefresh ?? true;
|
|
4952
5055
|
const refreshBeforeExpiryMs = Math.max(0, options.refreshBeforeExpiryMs ?? 3e4);
|
|
4953
|
-
const
|
|
5056
|
+
const sessionEpoch = useClientSessionEpoch(client);
|
|
5057
|
+
const fallbackLocationId = context?.client === client ? context.currentLocation?.id ?? (context.isReady ? null : client.getLocationId()) : client.getLocationId();
|
|
4954
5058
|
const requestIdRef = useRef(0);
|
|
4955
|
-
const debounceTimerRef = useRef(null);
|
|
4956
5059
|
const inputSignature = buildQuoteInputSignature(input);
|
|
4957
5060
|
const normalizedInput = useMemo(() => {
|
|
4958
5061
|
if (!input) {
|
|
4959
5062
|
return null;
|
|
4960
5063
|
}
|
|
4961
|
-
const normalized = normalizeInput(input,
|
|
5064
|
+
const normalized = normalizeInput(input, fallbackLocationId);
|
|
4962
5065
|
return normalized.product_id.length > 0 ? normalized : null;
|
|
4963
|
-
}, [inputSignature,
|
|
5066
|
+
}, [inputSignature, fallbackLocationId]);
|
|
4964
5067
|
const cacheKey2 = useMemo(
|
|
4965
|
-
() => buildQuoteCacheKey(
|
|
4966
|
-
|
|
5068
|
+
() => buildQuoteCacheKey(
|
|
5069
|
+
client,
|
|
5070
|
+
sessionEpoch,
|
|
5071
|
+
normalizedInput?.location_id ?? null,
|
|
5072
|
+
inputSignature
|
|
5073
|
+
),
|
|
5074
|
+
[client, inputSignature, normalizedInput?.location_id, sessionEpoch]
|
|
4967
5075
|
);
|
|
4968
|
-
const
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
const
|
|
4973
|
-
const
|
|
4974
|
-
const
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
5076
|
+
const [state, setState] = useState(() => {
|
|
5077
|
+
const cached = enabled && normalizedInput ? readUsableCachedQuote(cacheKey2) : null;
|
|
5078
|
+
return requestState(cacheKey2, cached, enabled && normalizedInput !== null && !cached);
|
|
5079
|
+
});
|
|
5080
|
+
const stateIsCurrent = state.cacheKey === cacheKey2 && enabled && normalizedInput !== null;
|
|
5081
|
+
const activeQuote = stateIsCurrent ? state.quote : null;
|
|
5082
|
+
const activeQuoteExpired = isQuoteExpired(activeQuote);
|
|
5083
|
+
const quote = activeQuoteExpired ? null : activeQuote;
|
|
5084
|
+
const isLoading = enabled && normalizedInput !== null && (!stateIsCurrent || state.isLoading);
|
|
5085
|
+
const error = stateIsCurrent ? state.error : null;
|
|
5086
|
+
const isExpired = stateIsCurrent ? state.isExpired || activeQuoteExpired : false;
|
|
5087
|
+
const messages = stateIsCurrent ? state.messages : [];
|
|
5088
|
+
const expiresAt = quoteExpiryTimestamp(activeQuote);
|
|
4981
5089
|
const load = useCallback(
|
|
4982
5090
|
async (force = false) => {
|
|
4983
5091
|
if (!enabled || !normalizedInput) {
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
setIsExpired(false);
|
|
4987
|
-
setError(null);
|
|
4988
|
-
setIsLoading(false);
|
|
5092
|
+
requestIdRef.current += 1;
|
|
5093
|
+
setState(requestState(cacheKey2, null, false));
|
|
4989
5094
|
return;
|
|
4990
5095
|
}
|
|
4991
5096
|
const nextRequestId = ++requestIdRef.current;
|
|
4992
|
-
setError(null);
|
|
4993
5097
|
if (!force) {
|
|
4994
|
-
const
|
|
4995
|
-
if (
|
|
4996
|
-
|
|
4997
|
-
setMessages(cacheEntry.quote?.ui_messages ?? []);
|
|
4998
|
-
setIsExpired(isQuoteExpired(cacheEntry.quote));
|
|
4999
|
-
setIsLoading(false);
|
|
5098
|
+
const cachedQuote = readUsableCachedQuote(cacheKey2);
|
|
5099
|
+
if (cachedQuote) {
|
|
5100
|
+
setState(requestState(cacheKey2, cachedQuote, false));
|
|
5000
5101
|
return;
|
|
5001
5102
|
}
|
|
5002
5103
|
}
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5104
|
+
setState((current) => ({
|
|
5105
|
+
...requestState(cacheKey2, null, true),
|
|
5106
|
+
isExpired: current.cacheKey === cacheKey2 && current.isExpired
|
|
5107
|
+
}));
|
|
5006
5108
|
try {
|
|
5007
5109
|
const existing = quoteInflight.get(cacheKey2);
|
|
5008
5110
|
const promise = existing ?? (async () => {
|
|
@@ -5019,55 +5121,58 @@ function useQuote(input, options = {}) {
|
|
|
5019
5121
|
}).catch(() => void 0);
|
|
5020
5122
|
}
|
|
5021
5123
|
const value = await promise;
|
|
5022
|
-
|
|
5023
|
-
if (
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
setError(null);
|
|
5124
|
+
const expired = isQuoteExpired(value);
|
|
5125
|
+
if (expired) {
|
|
5126
|
+
quoteCache.delete(cacheKey2);
|
|
5127
|
+
} else {
|
|
5128
|
+
quoteCache.set(cacheKey2, { quote: value });
|
|
5028
5129
|
}
|
|
5029
|
-
} catch (loadError) {
|
|
5030
5130
|
if (nextRequestId === requestIdRef.current) {
|
|
5031
|
-
|
|
5131
|
+
setState({
|
|
5132
|
+
...requestState(cacheKey2, expired ? null : value, false),
|
|
5133
|
+
isExpired: expired
|
|
5134
|
+
});
|
|
5032
5135
|
}
|
|
5033
|
-
}
|
|
5136
|
+
} catch (loadError) {
|
|
5034
5137
|
if (nextRequestId === requestIdRef.current) {
|
|
5035
|
-
|
|
5138
|
+
setState((current) => ({
|
|
5139
|
+
...requestState(cacheKey2, null, false),
|
|
5140
|
+
error: loadError,
|
|
5141
|
+
isExpired: current.cacheKey === cacheKey2 && current.isExpired
|
|
5142
|
+
}));
|
|
5036
5143
|
}
|
|
5037
5144
|
}
|
|
5038
5145
|
},
|
|
5039
5146
|
[cacheKey2, client, enabled, normalizedInput]
|
|
5040
5147
|
);
|
|
5041
5148
|
useEffect(() => {
|
|
5042
|
-
if (
|
|
5043
|
-
clearTimeout(debounceTimerRef.current);
|
|
5044
|
-
}
|
|
5045
|
-
const hasCached = quoteCache.has(cacheKey2);
|
|
5046
|
-
if (hasCached || !enabled || !normalizedInput) {
|
|
5149
|
+
if (!enabled || !normalizedInput || readUsableCachedQuote(cacheKey2)) {
|
|
5047
5150
|
void load(false);
|
|
5048
|
-
|
|
5049
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
5050
|
-
void load(false);
|
|
5051
|
-
}, 150);
|
|
5151
|
+
return;
|
|
5052
5152
|
}
|
|
5153
|
+
const timer = setTimeout(() => {
|
|
5154
|
+
void load(false);
|
|
5155
|
+
}, QUOTE_LOAD_DEBOUNCE_MS);
|
|
5053
5156
|
return () => {
|
|
5054
|
-
|
|
5055
|
-
clearTimeout(debounceTimerRef.current);
|
|
5056
|
-
debounceTimerRef.current = null;
|
|
5057
|
-
}
|
|
5157
|
+
clearTimeout(timer);
|
|
5058
5158
|
};
|
|
5059
5159
|
}, [load, cacheKey2, enabled, normalizedInput]);
|
|
5060
5160
|
const refresh = useCallback(async () => {
|
|
5061
5161
|
if (!enabled || !normalizedInput) {
|
|
5062
5162
|
return;
|
|
5063
5163
|
}
|
|
5064
|
-
if (!quote?.quote_id) {
|
|
5164
|
+
if (!quote?.quote_id || isQuoteExpired(quote)) {
|
|
5165
|
+
quoteCache.delete(cacheKey2);
|
|
5065
5166
|
await load(true);
|
|
5066
5167
|
return;
|
|
5067
5168
|
}
|
|
5068
5169
|
const nextRequestId = ++requestIdRef.current;
|
|
5069
|
-
|
|
5070
|
-
|
|
5170
|
+
setState((current) => ({
|
|
5171
|
+
...current,
|
|
5172
|
+
cacheKey: cacheKey2,
|
|
5173
|
+
isLoading: true,
|
|
5174
|
+
error: null
|
|
5175
|
+
}));
|
|
5071
5176
|
try {
|
|
5072
5177
|
const result = await client.catalogue.refreshQuote({
|
|
5073
5178
|
quote_id: quote.quote_id,
|
|
@@ -5077,52 +5182,67 @@ function useQuote(input, options = {}) {
|
|
|
5077
5182
|
throw result.error;
|
|
5078
5183
|
}
|
|
5079
5184
|
const refreshed = result.value.quote;
|
|
5080
|
-
|
|
5081
|
-
if (
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
setError(null);
|
|
5185
|
+
const expired = isQuoteExpired(refreshed);
|
|
5186
|
+
if (expired) {
|
|
5187
|
+
quoteCache.delete(cacheKey2);
|
|
5188
|
+
} else {
|
|
5189
|
+
quoteCache.set(cacheKey2, { quote: refreshed });
|
|
5086
5190
|
}
|
|
5087
|
-
} catch (refreshError) {
|
|
5088
5191
|
if (nextRequestId === requestIdRef.current) {
|
|
5089
|
-
|
|
5192
|
+
setState({
|
|
5193
|
+
...requestState(cacheKey2, expired ? null : refreshed, false),
|
|
5194
|
+
isExpired: expired
|
|
5195
|
+
});
|
|
5090
5196
|
}
|
|
5091
|
-
}
|
|
5197
|
+
} catch (refreshError) {
|
|
5092
5198
|
if (nextRequestId === requestIdRef.current) {
|
|
5093
|
-
|
|
5199
|
+
setState((current) => ({
|
|
5200
|
+
...current,
|
|
5201
|
+
cacheKey: cacheKey2,
|
|
5202
|
+
isLoading: false,
|
|
5203
|
+
error: refreshError
|
|
5204
|
+
}));
|
|
5094
5205
|
}
|
|
5095
5206
|
}
|
|
5096
5207
|
}, [cacheKey2, client, enabled, load, normalizedInput, quote]);
|
|
5097
5208
|
useEffect(() => {
|
|
5098
|
-
if (!Number.isFinite(
|
|
5099
|
-
setIsExpired(false);
|
|
5209
|
+
if (!activeQuote || !Number.isFinite(expiresAt)) {
|
|
5100
5210
|
return;
|
|
5101
5211
|
}
|
|
5102
|
-
const
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
timers.push(
|
|
5108
|
-
setTimeout(() => {
|
|
5109
|
-
setIsExpired(true);
|
|
5110
|
-
}, Math.max(0, quoteExpiryTimestamp - now))
|
|
5111
|
-
);
|
|
5112
|
-
}
|
|
5113
|
-
if (autoRefresh && enabled && !isCurrentlyExpired) {
|
|
5114
|
-
timers.push(
|
|
5115
|
-
setTimeout(() => {
|
|
5116
|
-
void refresh();
|
|
5117
|
-
}, Math.max(0, quoteExpiryTimestamp - now - refreshBeforeExpiryMs))
|
|
5118
|
-
);
|
|
5212
|
+
const remainingMs = expiresAt - Date.now();
|
|
5213
|
+
if (remainingMs <= 0) {
|
|
5214
|
+
quoteCache.delete(cacheKey2);
|
|
5215
|
+
void load(true);
|
|
5216
|
+
return;
|
|
5119
5217
|
}
|
|
5218
|
+
const expiryTimer = setTimeout(() => {
|
|
5219
|
+
quoteCache.delete(cacheKey2);
|
|
5220
|
+
setState({
|
|
5221
|
+
...requestState(cacheKey2, null, true),
|
|
5222
|
+
isExpired: true
|
|
5223
|
+
});
|
|
5224
|
+
void load(true);
|
|
5225
|
+
}, remainingMs);
|
|
5226
|
+
const refreshDelayMs = remainingMs - refreshBeforeExpiryMs;
|
|
5227
|
+
const refreshTimer = autoRefresh && enabled && refreshDelayMs >= MIN_AUTO_REFRESH_DELAY_MS ? setTimeout(() => {
|
|
5228
|
+
void refresh();
|
|
5229
|
+
}, refreshDelayMs) : null;
|
|
5120
5230
|
return () => {
|
|
5121
|
-
|
|
5122
|
-
|
|
5231
|
+
clearTimeout(expiryTimer);
|
|
5232
|
+
if (refreshTimer) {
|
|
5233
|
+
clearTimeout(refreshTimer);
|
|
5123
5234
|
}
|
|
5124
5235
|
};
|
|
5125
|
-
}, [
|
|
5236
|
+
}, [
|
|
5237
|
+
activeQuote,
|
|
5238
|
+
autoRefresh,
|
|
5239
|
+
cacheKey2,
|
|
5240
|
+
enabled,
|
|
5241
|
+
expiresAt,
|
|
5242
|
+
load,
|
|
5243
|
+
refresh,
|
|
5244
|
+
refreshBeforeExpiryMs
|
|
5245
|
+
]);
|
|
5126
5246
|
return { quote, isLoading, error, refresh, isExpired, messages };
|
|
5127
5247
|
}
|
|
5128
5248
|
|
|
@@ -5311,14 +5431,7 @@ function useBundles(options = {}) {
|
|
|
5311
5431
|
return { bundles: data ?? [], isLoading, error, refetch };
|
|
5312
5432
|
}
|
|
5313
5433
|
function useProductPrice(input) {
|
|
5314
|
-
const {
|
|
5315
|
-
product,
|
|
5316
|
-
quantity = 1,
|
|
5317
|
-
variant,
|
|
5318
|
-
addOnOptions,
|
|
5319
|
-
compositePrice,
|
|
5320
|
-
bundleTotalPrice
|
|
5321
|
-
} = input;
|
|
5434
|
+
const { product, quantity = 1, variant, addOnOptions, compositePrice, bundleTotalPrice } = input;
|
|
5322
5435
|
return useMemo(() => {
|
|
5323
5436
|
let unitPrice;
|
|
5324
5437
|
if (bundleTotalPrice != null) {
|
|
@@ -5326,10 +5439,7 @@ function useProductPrice(input) {
|
|
|
5326
5439
|
} else if (compositePrice) {
|
|
5327
5440
|
unitPrice = parsePrice(compositePrice.final_price);
|
|
5328
5441
|
} else {
|
|
5329
|
-
unitPrice =
|
|
5330
|
-
if (variant?.price_adjustment) {
|
|
5331
|
-
unitPrice += parsePrice(variant.price_adjustment);
|
|
5332
|
-
}
|
|
5442
|
+
unitPrice = getVariantUnitPrice(product, variant);
|
|
5333
5443
|
if (addOnOptions) {
|
|
5334
5444
|
for (const option of addOnOptions) {
|
|
5335
5445
|
if (option.default_price) {
|
|
@@ -5345,7 +5455,15 @@ function useProductPrice(input) {
|
|
|
5345
5455
|
totalPrice,
|
|
5346
5456
|
displayTotalPrice: totalPrice
|
|
5347
5457
|
};
|
|
5348
|
-
}, [
|
|
5458
|
+
}, [
|
|
5459
|
+
product,
|
|
5460
|
+
quantity,
|
|
5461
|
+
variant?.price_adjustment,
|
|
5462
|
+
variant?.price_info,
|
|
5463
|
+
addOnOptions,
|
|
5464
|
+
compositePrice,
|
|
5465
|
+
bundleTotalPrice
|
|
5466
|
+
]);
|
|
5349
5467
|
}
|
|
5350
5468
|
|
|
5351
5469
|
// src/react/hooks/use-orders.ts
|
|
@@ -9597,7 +9715,18 @@ function DateSlotPicker({
|
|
|
9597
9715
|
}
|
|
9598
9716
|
);
|
|
9599
9717
|
}
|
|
9600
|
-
|
|
9718
|
+
var REQUIRED_OPTIONS_MESSAGE = "Please select all required options";
|
|
9719
|
+
var QUOTE_LOADING_MESSAGE = "Updating price\u2026";
|
|
9720
|
+
var QUOTE_UNAVAILABLE_MESSAGE = "Price is unavailable. Please try again.";
|
|
9721
|
+
var CUSTOMIZER_VALIDATION_ID = "cimplify-customizer-validation";
|
|
9722
|
+
function initialVariantSelection(product) {
|
|
9723
|
+
const variant = product.variants?.find((candidate) => candidate.is_default) ?? product.variants?.[0];
|
|
9724
|
+
return { id: variant?.id, variant };
|
|
9725
|
+
}
|
|
9726
|
+
function ProductCustomizer(props) {
|
|
9727
|
+
return /* @__PURE__ */ jsx(ProductCustomizerContent, { ...props }, props.product.id);
|
|
9728
|
+
}
|
|
9729
|
+
function ProductCustomizerContent({
|
|
9601
9730
|
product,
|
|
9602
9731
|
onAddToCart,
|
|
9603
9732
|
onVariantChange,
|
|
@@ -9609,8 +9738,11 @@ function ProductCustomizer({
|
|
|
9609
9738
|
const [quantity, setQuantity] = useState(product.min_order_quantity ?? 1);
|
|
9610
9739
|
const [isAdded, setIsAdded] = useState(false);
|
|
9611
9740
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
9612
|
-
const [
|
|
9613
|
-
|
|
9741
|
+
const [variantSelection, setVariantSelection] = useState(
|
|
9742
|
+
() => initialVariantSelection(product)
|
|
9743
|
+
);
|
|
9744
|
+
const selectedVariantId = variantSelection.id;
|
|
9745
|
+
const selectedVariant = variantSelection.variant;
|
|
9614
9746
|
const [selectedAddOnOptionIds, setSelectedAddOnOptionIds] = useState([]);
|
|
9615
9747
|
const [compositeSelections, setCompositeSelections] = useState([]);
|
|
9616
9748
|
const [compositePrice, setCompositePrice] = useState(null);
|
|
@@ -9625,7 +9757,6 @@ function ProductCustomizer({
|
|
|
9625
9757
|
const [selectedStaffId, setSelectedStaffId] = useState(null);
|
|
9626
9758
|
const [unitsCount, setUnitsCount] = useState(1);
|
|
9627
9759
|
const cart = useCart();
|
|
9628
|
-
const currency = getProductCurrency(product);
|
|
9629
9760
|
const productType = product.type || "product";
|
|
9630
9761
|
const isComposite = productType === "composite";
|
|
9631
9762
|
const isBundle = productType === "bundle";
|
|
@@ -9635,26 +9766,6 @@ function ProductCustomizer({
|
|
|
9635
9766
|
const isStandard = !isComposite && !isBundle;
|
|
9636
9767
|
const hasVariants = isStandard && product.variants && product.variants.length > 0;
|
|
9637
9768
|
const hasAddOns = isStandard && product.add_ons && product.add_ons.length > 0;
|
|
9638
|
-
useEffect(() => {
|
|
9639
|
-
setQuantity(product.min_order_quantity ?? 1);
|
|
9640
|
-
setIsAdded(false);
|
|
9641
|
-
setIsSubmitting(false);
|
|
9642
|
-
setSelectedVariantId(void 0);
|
|
9643
|
-
setSelectedVariant(void 0);
|
|
9644
|
-
setSelectedAddOnOptionIds([]);
|
|
9645
|
-
setCompositeSelections([]);
|
|
9646
|
-
setCompositePrice(null);
|
|
9647
|
-
setCompositeReady(false);
|
|
9648
|
-
setBundleSelections([]);
|
|
9649
|
-
setBundleTotalPrice(null);
|
|
9650
|
-
setBundleReady(false);
|
|
9651
|
-
setSelectedBillingPlan(null);
|
|
9652
|
-
setCustomerInputValues({});
|
|
9653
|
-
setSpecialInstructions("");
|
|
9654
|
-
setSelectedSlot(null);
|
|
9655
|
-
setSelectedStaffId(null);
|
|
9656
|
-
setUnitsCount(1);
|
|
9657
|
-
}, [product.id, product.min_order_quantity]);
|
|
9658
9769
|
const slotStaff = useMemo(
|
|
9659
9770
|
() => (selectedSlot?.available_staff ?? []).map((member) => ({
|
|
9660
9771
|
id: member.staff_id,
|
|
@@ -9677,7 +9788,9 @@ function ProductCustomizer({
|
|
|
9677
9788
|
}, [product.add_ons, selectedAddOnOptionIds]);
|
|
9678
9789
|
const normalizedAddOnOptionIds = useMemo(() => {
|
|
9679
9790
|
if (selectedAddOnOptionIds.length === 0) return [];
|
|
9680
|
-
return Array.from(
|
|
9791
|
+
return Array.from(
|
|
9792
|
+
new Set(selectedAddOnOptionIds.map((id) => id.trim()).filter(Boolean))
|
|
9793
|
+
).sort();
|
|
9681
9794
|
}, [selectedAddOnOptionIds]);
|
|
9682
9795
|
const localTotalPrice = useMemo(() => {
|
|
9683
9796
|
if (isComposite && compositePrice) {
|
|
@@ -9686,17 +9799,23 @@ function ProductCustomizer({
|
|
|
9686
9799
|
if (isBundle && bundleTotalPrice != null) {
|
|
9687
9800
|
return bundleTotalPrice * quantity;
|
|
9688
9801
|
}
|
|
9689
|
-
let price =
|
|
9690
|
-
if (selectedVariant?.price_adjustment) {
|
|
9691
|
-
price += parsePrice(selectedVariant.price_adjustment);
|
|
9692
|
-
}
|
|
9802
|
+
let price = getVariantUnitPrice(product, selectedVariant);
|
|
9693
9803
|
for (const option of selectedAddOnOptions) {
|
|
9694
9804
|
if (option.default_price) {
|
|
9695
9805
|
price += parsePrice(option.default_price);
|
|
9696
9806
|
}
|
|
9697
9807
|
}
|
|
9698
9808
|
return price * quantity;
|
|
9699
|
-
}, [
|
|
9809
|
+
}, [
|
|
9810
|
+
product.default_price,
|
|
9811
|
+
selectedVariant,
|
|
9812
|
+
selectedAddOnOptions,
|
|
9813
|
+
quantity,
|
|
9814
|
+
isComposite,
|
|
9815
|
+
compositePrice,
|
|
9816
|
+
isBundle,
|
|
9817
|
+
bundleTotalPrice
|
|
9818
|
+
]);
|
|
9700
9819
|
const requiredAddOnsSatisfied = useMemo(() => {
|
|
9701
9820
|
if (!product.add_ons) return true;
|
|
9702
9821
|
for (const addOn of product.add_ons) {
|
|
@@ -9721,7 +9840,16 @@ function ProductCustomizer({
|
|
|
9721
9840
|
bundleSelections: isBundle && bundleSelections.length > 0 ? bundleSelections : void 0,
|
|
9722
9841
|
compositeSelections: isComposite && compositeSelections.length > 0 ? compositeSelections : void 0
|
|
9723
9842
|
}),
|
|
9724
|
-
[
|
|
9843
|
+
[
|
|
9844
|
+
product.id,
|
|
9845
|
+
quantity,
|
|
9846
|
+
selectedVariantId,
|
|
9847
|
+
normalizedAddOnOptionIds,
|
|
9848
|
+
isBundle,
|
|
9849
|
+
bundleSelections,
|
|
9850
|
+
isComposite,
|
|
9851
|
+
compositeSelections
|
|
9852
|
+
]
|
|
9725
9853
|
);
|
|
9726
9854
|
const requiredInputsSatisfied = useMemo(() => {
|
|
9727
9855
|
if (!product.input_fields || product.input_fields.length === 0) return true;
|
|
@@ -9732,17 +9860,23 @@ function ProductCustomizer({
|
|
|
9732
9860
|
});
|
|
9733
9861
|
}, [product.input_fields, customerInputValues]);
|
|
9734
9862
|
const quoteEnabled = isComposite ? compositeReady && requiredInputsSatisfied : isBundle ? bundleReady && requiredInputsSatisfied : requiredAddOnsSatisfied && requiredInputsSatisfied;
|
|
9735
|
-
const {
|
|
9863
|
+
const {
|
|
9864
|
+
quote,
|
|
9865
|
+
isLoading: isQuoteLoading,
|
|
9866
|
+
error: quoteError,
|
|
9867
|
+
isExpired: isQuoteExpired2
|
|
9868
|
+
} = useQuote(quoteInput, {
|
|
9736
9869
|
enabled: quoteEnabled
|
|
9737
9870
|
});
|
|
9871
|
+
const currency = quote?.currency ?? getProductCurrency(product);
|
|
9738
9872
|
const quoteId = quote?.quote_id;
|
|
9873
|
+
const quotedUnitPrice = (quote?.quoted_total_price_info ?? quote?.final_price_info)?.final_price;
|
|
9739
9874
|
const quotedTotalPrice = useMemo(() => {
|
|
9740
|
-
if (
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
}, [quote, quantity]);
|
|
9875
|
+
if (quotedUnitPrice === void 0 || quotedUnitPrice === null) return void 0;
|
|
9876
|
+
return parsePrice(quotedUnitPrice) * quantity;
|
|
9877
|
+
}, [quotedUnitPrice, quantity]);
|
|
9878
|
+
const quoteIsReady = quoteEnabled && quote !== null && !isQuoteLoading && !quoteError && !isQuoteExpired2;
|
|
9879
|
+
const validationMessage = !quoteEnabled ? REQUIRED_OPTIONS_MESSAGE : quoteError ? QUOTE_UNAVAILABLE_MESSAGE : !quoteIsReady ? QUOTE_LOADING_MESSAGE : null;
|
|
9746
9880
|
const displayTotalPrice = useMemo(() => {
|
|
9747
9881
|
const total = quotedTotalPrice ?? localTotalPrice;
|
|
9748
9882
|
const multiplier = serviceBillingMultiplier(product, {
|
|
@@ -9756,8 +9890,7 @@ function ProductCustomizer({
|
|
|
9756
9890
|
}, [quotedTotalPrice, localTotalPrice, product, quantity, unitsCount, selectedSlot]);
|
|
9757
9891
|
const handleVariantChange = useCallback(
|
|
9758
9892
|
(variantId, variant) => {
|
|
9759
|
-
|
|
9760
|
-
setSelectedVariant(variant);
|
|
9893
|
+
setVariantSelection({ id: variantId, variant });
|
|
9761
9894
|
onVariantChange?.(variantId, variant);
|
|
9762
9895
|
},
|
|
9763
9896
|
[onVariantChange]
|
|
@@ -9765,12 +9898,21 @@ function ProductCustomizer({
|
|
|
9765
9898
|
const addedTimerRef = useRef(void 0);
|
|
9766
9899
|
useEffect(() => () => clearTimeout(addedTimerRef.current), []);
|
|
9767
9900
|
const handleAddToCart = useCallback(async () => {
|
|
9768
|
-
if (isSubmitting)
|
|
9901
|
+
if (isSubmitting || !quoteIsReady || !quote || quotedUnitPrice === void 0 || quotedUnitPrice === null) {
|
|
9902
|
+
return;
|
|
9903
|
+
}
|
|
9769
9904
|
setIsSubmitting(true);
|
|
9770
9905
|
const options = {
|
|
9906
|
+
locationId: quote.location_id ?? void 0,
|
|
9771
9907
|
variantId: selectedVariantId,
|
|
9772
|
-
variant: selectedVariant ? {
|
|
9908
|
+
variant: selectedVariant ? {
|
|
9909
|
+
id: selectedVariant.id,
|
|
9910
|
+
name: selectedVariant.name || "",
|
|
9911
|
+
price_adjustment: selectedVariant.price_adjustment,
|
|
9912
|
+
price_info: selectedVariant.price_info
|
|
9913
|
+
} : void 0,
|
|
9773
9914
|
quoteId,
|
|
9915
|
+
quotedUnitPrice,
|
|
9774
9916
|
addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : void 0,
|
|
9775
9917
|
addOnOptions: selectedAddOnOptions.length > 0 ? selectedAddOnOptions.map((opt) => ({
|
|
9776
9918
|
id: opt.id,
|
|
@@ -9807,7 +9949,32 @@ function ProductCustomizer({
|
|
|
9807
9949
|
} finally {
|
|
9808
9950
|
setIsSubmitting(false);
|
|
9809
9951
|
}
|
|
9810
|
-
}, [
|
|
9952
|
+
}, [
|
|
9953
|
+
product,
|
|
9954
|
+
quantity,
|
|
9955
|
+
selectedVariantId,
|
|
9956
|
+
selectedVariant,
|
|
9957
|
+
quoteId,
|
|
9958
|
+
quotedUnitPrice,
|
|
9959
|
+
normalizedAddOnOptionIds,
|
|
9960
|
+
selectedAddOnOptions,
|
|
9961
|
+
isComposite,
|
|
9962
|
+
compositeSelections,
|
|
9963
|
+
isBundle,
|
|
9964
|
+
bundleSelections,
|
|
9965
|
+
selectedBillingPlan,
|
|
9966
|
+
customerInputValues,
|
|
9967
|
+
specialInstructions,
|
|
9968
|
+
isService,
|
|
9969
|
+
selectedSlot,
|
|
9970
|
+
selectedStaffId,
|
|
9971
|
+
unitsCount,
|
|
9972
|
+
isSubmitting,
|
|
9973
|
+
onAddToCart,
|
|
9974
|
+
cart,
|
|
9975
|
+
quoteIsReady,
|
|
9976
|
+
quote
|
|
9977
|
+
]);
|
|
9811
9978
|
return /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer": true, className: cn("space-y-6", className, classNames?.root), children: [
|
|
9812
9979
|
isComposite && product.groups && product.composite_id && /* @__PURE__ */ jsx(
|
|
9813
9980
|
CompositeSelector,
|
|
@@ -9920,27 +10087,48 @@ function ProductCustomizer({
|
|
|
9920
10087
|
currency
|
|
9921
10088
|
}
|
|
9922
10089
|
),
|
|
9923
|
-
product.deposit_type && product.deposit_type !== "none" && product.deposit_amount && /* @__PURE__ */ jsx(
|
|
9924
|
-
"
|
|
9925
|
-
/* @__PURE__ */ jsx(Price, { amount: product.deposit_amount, currency })
|
|
9926
|
-
] }) : /* @__PURE__ */ jsxs("span", { children: [
|
|
9927
|
-
"Deposit required: ",
|
|
9928
|
-
parsePrice(product.deposit_amount),
|
|
9929
|
-
"%"
|
|
9930
|
-
] }) }),
|
|
9931
|
-
product.allergies && product.allergies.length > 0 && /* @__PURE__ */ jsx("div", { "data-cimplify-customizer-allergens": true, className: cn("flex flex-wrap gap-1.5", classNames?.allergens), children: product.allergies.map((allergen) => /* @__PURE__ */ jsx(
|
|
9932
|
-
"span",
|
|
10090
|
+
product.deposit_type && product.deposit_type !== "none" && product.deposit_amount && /* @__PURE__ */ jsx(
|
|
10091
|
+
"div",
|
|
9933
10092
|
{
|
|
9934
|
-
"data-cimplify-
|
|
9935
|
-
className: "
|
|
9936
|
-
children:
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
10093
|
+
"data-cimplify-customizer-deposit": true,
|
|
10094
|
+
className: cn("text-sm text-muted-foreground", classNames?.depositInfo),
|
|
10095
|
+
children: product.deposit_type === "fixed" ? /* @__PURE__ */ jsxs("span", { children: [
|
|
10096
|
+
"Deposit required: ",
|
|
10097
|
+
/* @__PURE__ */ jsx(Price, { amount: product.deposit_amount, currency })
|
|
10098
|
+
] }) : /* @__PURE__ */ jsxs("span", { children: [
|
|
10099
|
+
"Deposit required: ",
|
|
10100
|
+
parsePrice(product.deposit_amount),
|
|
10101
|
+
"%"
|
|
10102
|
+
] })
|
|
10103
|
+
}
|
|
10104
|
+
),
|
|
10105
|
+
product.allergies && product.allergies.length > 0 && /* @__PURE__ */ jsx(
|
|
10106
|
+
"div",
|
|
10107
|
+
{
|
|
10108
|
+
"data-cimplify-customizer-allergens": true,
|
|
10109
|
+
className: cn("flex flex-wrap gap-1.5", classNames?.allergens),
|
|
10110
|
+
children: product.allergies.map((allergen) => /* @__PURE__ */ jsx(
|
|
10111
|
+
"span",
|
|
10112
|
+
{
|
|
10113
|
+
"data-cimplify-allergen-tag": true,
|
|
10114
|
+
className: "inline-block text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground",
|
|
10115
|
+
children: allergen
|
|
10116
|
+
},
|
|
10117
|
+
allergen
|
|
10118
|
+
))
|
|
10119
|
+
}
|
|
10120
|
+
),
|
|
10121
|
+
isService && product.duration_minutes != null && /* @__PURE__ */ jsxs(
|
|
10122
|
+
"div",
|
|
10123
|
+
{
|
|
10124
|
+
"data-cimplify-customizer-duration": true,
|
|
10125
|
+
className: cn("text-sm text-muted-foreground", classNames?.duration),
|
|
10126
|
+
children: [
|
|
10127
|
+
"Duration: ",
|
|
10128
|
+
formatDuration2(product.duration_minutes, product.duration_unit)
|
|
10129
|
+
]
|
|
10130
|
+
}
|
|
10131
|
+
),
|
|
9944
10132
|
showSpecialInstructions && !isDigital && /* @__PURE__ */ jsx("div", { "data-cimplify-customizer-special-instructions": true, children: /* @__PURE__ */ jsx(
|
|
9945
10133
|
"textarea",
|
|
9946
10134
|
{
|
|
@@ -9973,13 +10161,17 @@ function ProductCustomizer({
|
|
|
9973
10161
|
"data-cimplify-customizer-actions": true,
|
|
9974
10162
|
className: cn("pt-4 border-t border-border", classNames?.actions),
|
|
9975
10163
|
children: [
|
|
9976
|
-
|
|
10164
|
+
validationMessage && /* @__PURE__ */ jsx(
|
|
9977
10165
|
"p",
|
|
9978
10166
|
{
|
|
9979
|
-
id:
|
|
10167
|
+
id: CUSTOMIZER_VALIDATION_ID,
|
|
9980
10168
|
"data-cimplify-customizer-validation": true,
|
|
9981
|
-
className: cn(
|
|
9982
|
-
|
|
10169
|
+
className: cn(
|
|
10170
|
+
"text-sm mb-3",
|
|
10171
|
+
quoteError ? "text-destructive" : "text-muted-foreground",
|
|
10172
|
+
classNames?.validation
|
|
10173
|
+
),
|
|
10174
|
+
children: validationMessage
|
|
9983
10175
|
}
|
|
9984
10176
|
),
|
|
9985
10177
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
@@ -9992,28 +10184,54 @@ function ProductCustomizer({
|
|
|
9992
10184
|
min: product.min_order_quantity ?? 1
|
|
9993
10185
|
}
|
|
9994
10186
|
),
|
|
9995
|
-
product.min_order_quantity && product.min_order_quantity > 1 && /* @__PURE__ */ jsxs(
|
|
9996
|
-
"
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10187
|
+
product.min_order_quantity && product.min_order_quantity > 1 && /* @__PURE__ */ jsxs(
|
|
10188
|
+
"span",
|
|
10189
|
+
{
|
|
10190
|
+
"data-cimplify-customizer-min-hint": true,
|
|
10191
|
+
className: "text-xs text-muted-foreground text-center",
|
|
10192
|
+
children: [
|
|
10193
|
+
"Min. ",
|
|
10194
|
+
product.min_order_quantity
|
|
10195
|
+
]
|
|
10196
|
+
}
|
|
10197
|
+
)
|
|
10003
10198
|
] }),
|
|
10199
|
+
product.quantity_pricing && product.quantity_pricing.length > 0 && /* @__PURE__ */ jsxs(
|
|
10200
|
+
"div",
|
|
10201
|
+
{
|
|
10202
|
+
"data-cimplify-customizer-unit-price": true,
|
|
10203
|
+
className: "text-sm text-muted-foreground text-right shrink-0",
|
|
10204
|
+
children: [
|
|
10205
|
+
/* @__PURE__ */ jsx(
|
|
10206
|
+
Price,
|
|
10207
|
+
{
|
|
10208
|
+
amount: getUnitPriceAtQuantity(
|
|
10209
|
+
product.quantity_pricing,
|
|
10210
|
+
quantity,
|
|
10211
|
+
parsePrice(product.default_price)
|
|
10212
|
+
),
|
|
10213
|
+
currency,
|
|
10214
|
+
className: "font-medium text-foreground"
|
|
10215
|
+
}
|
|
10216
|
+
),
|
|
10217
|
+
" ",
|
|
10218
|
+
"ea."
|
|
10219
|
+
]
|
|
10220
|
+
}
|
|
10221
|
+
),
|
|
10004
10222
|
/* @__PURE__ */ jsx(
|
|
10005
10223
|
Button$1,
|
|
10006
10224
|
{
|
|
10007
10225
|
onClick: handleAddToCart,
|
|
10008
|
-
disabled: isAdded || isSubmitting || !
|
|
10009
|
-
"aria-describedby":
|
|
10226
|
+
disabled: isAdded || isSubmitting || !quoteIsReady,
|
|
10227
|
+
"aria-describedby": validationMessage ? CUSTOMIZER_VALIDATION_ID : void 0,
|
|
10010
10228
|
"data-cimplify-customizer-submit": true,
|
|
10011
10229
|
className: cn(
|
|
10012
10230
|
"flex-1 h-14 text-base bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed rounded-full",
|
|
10013
10231
|
isAdded && classNames?.submitButtonAdded,
|
|
10014
10232
|
classNames?.submitButton
|
|
10015
10233
|
),
|
|
10016
|
-
children: isAdded ? "Added" : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10234
|
+
children: isAdded ? "Added" : !quoteEnabled ? submitLabel ?? "Add to Cart" : !quoteIsReady ? quoteError ? QUOTE_UNAVAILABLE_MESSAGE : QUOTE_LOADING_MESSAGE : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10017
10235
|
submitLabel ?? "Add to Cart",
|
|
10018
10236
|
" \xB7",
|
|
10019
10237
|
" ",
|
|
@@ -10447,13 +10665,16 @@ function CartLineItemRow({
|
|
|
10447
10665
|
item,
|
|
10448
10666
|
onRemove,
|
|
10449
10667
|
onQuantityChange,
|
|
10668
|
+
currency,
|
|
10450
10669
|
classNames
|
|
10451
10670
|
}) {
|
|
10452
|
-
const { unitPrice } = useProductPrice({
|
|
10671
|
+
const { unitPrice: locallyResolvedUnitPrice } = useProductPrice({
|
|
10453
10672
|
product: item.product,
|
|
10454
10673
|
variant: item.variant,
|
|
10455
10674
|
addOnOptions: item.addOnOptions
|
|
10456
10675
|
});
|
|
10676
|
+
const pinnedUnitPrice = item._quotedUnitPrice;
|
|
10677
|
+
const unitPrice = pinnedUnitPrice === void 0 ? locallyResolvedUnitPrice : parsePrice(pinnedUnitPrice);
|
|
10457
10678
|
const hasComposite = item.compositeSelections && item.compositeSelections.length > 0;
|
|
10458
10679
|
const hasBundle = item.bundleSelections && item.bundleSelections.length > 0;
|
|
10459
10680
|
const isOptimistic2 = item.isOptimistic === true;
|
|
@@ -10476,15 +10697,25 @@ function CartLineItemRow({
|
|
|
10476
10697
|
{
|
|
10477
10698
|
"data-cimplify-cart-item-image": true,
|
|
10478
10699
|
className: "relative w-20 h-20 shrink-0 rounded-md overflow-hidden bg-muted",
|
|
10479
|
-
children: imageUrl ? /* @__PURE__ */ jsx(
|
|
10480
|
-
"
|
|
10700
|
+
children: imageUrl ? /* @__PURE__ */ jsx("img", { src: imageUrl, alt: "", className: "w-full h-full object-cover", loading: "lazy" }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full grid place-items-center text-muted-foreground/40", children: /* @__PURE__ */ jsx(
|
|
10701
|
+
"svg",
|
|
10481
10702
|
{
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10703
|
+
className: "w-6 h-6",
|
|
10704
|
+
fill: "none",
|
|
10705
|
+
stroke: "currentColor",
|
|
10706
|
+
strokeWidth: "1.5",
|
|
10707
|
+
viewBox: "0 0 24 24",
|
|
10708
|
+
"aria-hidden": true,
|
|
10709
|
+
children: /* @__PURE__ */ jsx(
|
|
10710
|
+
"path",
|
|
10711
|
+
{
|
|
10712
|
+
strokeLinecap: "round",
|
|
10713
|
+
strokeLinejoin: "round",
|
|
10714
|
+
d: "M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
|
|
10715
|
+
}
|
|
10716
|
+
)
|
|
10486
10717
|
}
|
|
10487
|
-
)
|
|
10718
|
+
) })
|
|
10488
10719
|
}
|
|
10489
10720
|
),
|
|
10490
10721
|
/* @__PURE__ */ jsxs("div", { "data-cimplify-cart-item-info": true, className: "flex-1 min-w-0 flex flex-col", children: [
|
|
@@ -10508,7 +10739,18 @@ function CartLineItemRow({
|
|
|
10508
10739
|
classNames?.removeButton
|
|
10509
10740
|
),
|
|
10510
10741
|
"aria-label": `Remove ${item.product.name}`,
|
|
10511
|
-
children: /* @__PURE__ */ jsx(
|
|
10742
|
+
children: /* @__PURE__ */ jsx(
|
|
10743
|
+
"svg",
|
|
10744
|
+
{
|
|
10745
|
+
className: "w-3.5 h-3.5",
|
|
10746
|
+
fill: "none",
|
|
10747
|
+
stroke: "currentColor",
|
|
10748
|
+
strokeWidth: "2",
|
|
10749
|
+
viewBox: "0 0 24 24",
|
|
10750
|
+
"aria-hidden": true,
|
|
10751
|
+
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" })
|
|
10752
|
+
}
|
|
10753
|
+
)
|
|
10512
10754
|
}
|
|
10513
10755
|
)
|
|
10514
10756
|
] }),
|
|
@@ -10554,43 +10796,124 @@ function CartLineItemRow({
|
|
|
10554
10796
|
]
|
|
10555
10797
|
}
|
|
10556
10798
|
),
|
|
10557
|
-
item.customerInputs && item.customerInputs.length > 0 && /* @__PURE__ */ jsx(
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
":
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
|
|
10799
|
+
item.customerInputs && item.customerInputs.length > 0 && /* @__PURE__ */ jsx(
|
|
10800
|
+
"dl",
|
|
10801
|
+
{
|
|
10802
|
+
"data-cimplify-cart-customer-inputs": true,
|
|
10803
|
+
className: cn("mt-1.5 text-xs space-y-0.5", classNames?.customerInputs),
|
|
10804
|
+
children: item.customerInputs.map((input) => /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-input": true, className: "flex gap-1.5", children: [
|
|
10805
|
+
/* @__PURE__ */ jsxs("dt", { "data-cimplify-cart-input-label": true, className: "text-muted-foreground", children: [
|
|
10806
|
+
input.field_name,
|
|
10807
|
+
":"
|
|
10808
|
+
] }),
|
|
10809
|
+
/* @__PURE__ */ jsx("dd", { "data-cimplify-cart-input-value": true, className: "text-foreground m-0 truncate", children: input.field_type === INPUT_FIELD_TYPE.File || input.field_type === INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
|
|
10810
|
+
] }, input.field_id))
|
|
10811
|
+
}
|
|
10812
|
+
),
|
|
10813
|
+
(item.lineType === "service" || item.product.type === "service") && item.scheduledStart && /* @__PURE__ */ jsxs(
|
|
10814
|
+
"div",
|
|
10815
|
+
{
|
|
10816
|
+
"data-cimplify-cart-service-info": true,
|
|
10817
|
+
className: cn(
|
|
10818
|
+
"mt-1.5 flex items-center gap-1.5 text-xs text-muted-foreground",
|
|
10819
|
+
classNames?.serviceInfo
|
|
10820
|
+
),
|
|
10821
|
+
children: [
|
|
10822
|
+
/* @__PURE__ */ jsx(
|
|
10823
|
+
"svg",
|
|
10824
|
+
{
|
|
10825
|
+
className: "w-3.5 h-3.5 shrink-0",
|
|
10826
|
+
fill: "none",
|
|
10827
|
+
stroke: "currentColor",
|
|
10828
|
+
strokeWidth: "2",
|
|
10829
|
+
viewBox: "0 0 24 24",
|
|
10830
|
+
"aria-hidden": true,
|
|
10831
|
+
children: /* @__PURE__ */ jsx(
|
|
10832
|
+
"path",
|
|
10833
|
+
{
|
|
10834
|
+
strokeLinecap: "round",
|
|
10835
|
+
strokeLinejoin: "round",
|
|
10836
|
+
d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
10837
|
+
}
|
|
10838
|
+
)
|
|
10839
|
+
}
|
|
10840
|
+
),
|
|
10841
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
10842
|
+
new Date(item.scheduledStart).toLocaleDateString(void 0, {
|
|
10843
|
+
month: "short",
|
|
10844
|
+
day: "numeric"
|
|
10845
|
+
}),
|
|
10846
|
+
" ",
|
|
10847
|
+
"at",
|
|
10848
|
+
" ",
|
|
10849
|
+
new Date(item.scheduledStart).toLocaleTimeString([], {
|
|
10850
|
+
hour: "2-digit",
|
|
10851
|
+
minute: "2-digit"
|
|
10852
|
+
})
|
|
10853
|
+
] }),
|
|
10854
|
+
item.serviceStatus && /* @__PURE__ */ jsx(
|
|
10855
|
+
"span",
|
|
10856
|
+
{
|
|
10857
|
+
"data-status": item.serviceStatus,
|
|
10858
|
+
className: "ml-auto px-1.5 py-0.5 rounded-full bg-muted text-[10px] uppercase tracking-wider",
|
|
10859
|
+
children: item.serviceStatus
|
|
10860
|
+
}
|
|
10861
|
+
)
|
|
10862
|
+
]
|
|
10863
|
+
}
|
|
10864
|
+
),
|
|
10865
|
+
(item.lineType === "digital" || item.product.type === "digital") && /* @__PURE__ */ jsxs(
|
|
10866
|
+
"div",
|
|
10867
|
+
{
|
|
10868
|
+
"data-cimplify-cart-digital-info": true,
|
|
10869
|
+
className: cn(
|
|
10870
|
+
"mt-1.5 flex items-center gap-1 text-xs text-muted-foreground",
|
|
10871
|
+
classNames?.digitalInfo
|
|
10872
|
+
),
|
|
10873
|
+
children: [
|
|
10874
|
+
/* @__PURE__ */ jsx(
|
|
10875
|
+
"svg",
|
|
10876
|
+
{
|
|
10877
|
+
className: "w-3.5 h-3.5 shrink-0",
|
|
10878
|
+
fill: "none",
|
|
10879
|
+
stroke: "currentColor",
|
|
10880
|
+
strokeWidth: "2",
|
|
10881
|
+
viewBox: "0 0 24 24",
|
|
10882
|
+
"aria-hidden": true,
|
|
10883
|
+
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 10V3L4 14h7v7l9-11h-7z" })
|
|
10884
|
+
}
|
|
10885
|
+
),
|
|
10886
|
+
/* @__PURE__ */ jsx("span", { children: "Instant digital delivery" })
|
|
10887
|
+
]
|
|
10888
|
+
}
|
|
10889
|
+
),
|
|
10578
10890
|
(item.bundleResolved || item.compositeResolved) && /* @__PURE__ */ jsx(
|
|
10579
10891
|
"ul",
|
|
10580
10892
|
{
|
|
10581
10893
|
"data-cimplify-cart-composite-breakdown": item.compositeResolved ? "" : void 0,
|
|
10582
10894
|
"data-cimplify-cart-bundle-breakdown": item.bundleResolved ? "" : void 0,
|
|
10583
|
-
className: cn(
|
|
10895
|
+
className: cn(
|
|
10896
|
+
"mt-1.5 pl-3 border-l border-border space-y-0.5 text-xs text-muted-foreground",
|
|
10897
|
+
item.compositeResolved ? classNames?.compositeBreakdown : classNames?.bundleBreakdown
|
|
10898
|
+
),
|
|
10584
10899
|
children: (item.bundleResolved?.selections ?? item.compositeResolved?.selections ?? []).map((sel) => {
|
|
10585
10900
|
const name = sel.product_name ?? sel.component_name ?? "";
|
|
10586
10901
|
const compId = sel.component_id ?? `${name}-${sel.quantity}`;
|
|
10587
|
-
return /* @__PURE__ */ jsxs(
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
"
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10902
|
+
return /* @__PURE__ */ jsxs(
|
|
10903
|
+
"li",
|
|
10904
|
+
{
|
|
10905
|
+
"data-product-type": sel.product_type,
|
|
10906
|
+
className: "flex items-baseline gap-1.5 m-0",
|
|
10907
|
+
children: [
|
|
10908
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium text-foreground/80", children: [
|
|
10909
|
+
sel.quantity,
|
|
10910
|
+
"\\u00D7"
|
|
10911
|
+
] }),
|
|
10912
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: name })
|
|
10913
|
+
]
|
|
10914
|
+
},
|
|
10915
|
+
compId
|
|
10916
|
+
);
|
|
10594
10917
|
})
|
|
10595
10918
|
}
|
|
10596
10919
|
),
|
|
@@ -10599,6 +10922,7 @@ function CartLineItemRow({
|
|
|
10599
10922
|
Price,
|
|
10600
10923
|
{
|
|
10601
10924
|
amount: lineTotal,
|
|
10925
|
+
currency,
|
|
10602
10926
|
className: cn("text-sm font-semibold tabular-nums", classNames?.itemPrice)
|
|
10603
10927
|
}
|
|
10604
10928
|
),
|
|
@@ -10636,9 +10960,11 @@ function CartSummary({
|
|
|
10636
10960
|
isEmpty,
|
|
10637
10961
|
isOptimistic: isOptimistic2,
|
|
10638
10962
|
pendingOpCount,
|
|
10963
|
+
currency,
|
|
10639
10964
|
removeItem,
|
|
10640
10965
|
updateQuantity
|
|
10641
10966
|
} = useCart();
|
|
10967
|
+
const displayCurrency = serverCart?.currency ?? currencyCode(currency);
|
|
10642
10968
|
const deliveryFee = serverCart?.delivery_fee;
|
|
10643
10969
|
const serviceCharge = serverCart?.service_charge;
|
|
10644
10970
|
const totalDiscounts = serverCart?.total_discounts;
|
|
@@ -10670,6 +10996,7 @@ function CartSummary({
|
|
|
10670
10996
|
item,
|
|
10671
10997
|
onRemove: handleRemove,
|
|
10672
10998
|
onQuantityChange: handleQuantityChange,
|
|
10999
|
+
currency: displayCurrency,
|
|
10673
11000
|
classNames
|
|
10674
11001
|
},
|
|
10675
11002
|
item.id
|
|
@@ -10678,38 +11005,115 @@ function CartSummary({
|
|
|
10678
11005
|
"div",
|
|
10679
11006
|
{
|
|
10680
11007
|
"data-cimplify-cart-totals": true,
|
|
10681
|
-
className: cn(
|
|
11008
|
+
className: cn(
|
|
11009
|
+
"mt-6 pt-4 border-t border-border space-y-2 text-sm",
|
|
11010
|
+
classNames?.totals
|
|
11011
|
+
),
|
|
10682
11012
|
children: [
|
|
10683
|
-
/* @__PURE__ */ jsxs(
|
|
10684
|
-
|
|
10685
|
-
|
|
10686
|
-
|
|
10687
|
-
" ",
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
|
|
10691
|
-
|
|
10692
|
-
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10703
|
-
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
11013
|
+
/* @__PURE__ */ jsxs(
|
|
11014
|
+
"div",
|
|
11015
|
+
{
|
|
11016
|
+
"data-cimplify-cart-subtotal": true,
|
|
11017
|
+
className: cn("flex items-baseline justify-between", classNames?.subtotal),
|
|
11018
|
+
children: [
|
|
11019
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
11020
|
+
"Subtotal (",
|
|
11021
|
+
itemCount,
|
|
11022
|
+
" ",
|
|
11023
|
+
itemCount === 1 ? "item" : "items",
|
|
11024
|
+
")"
|
|
11025
|
+
] }),
|
|
11026
|
+
/* @__PURE__ */ jsx(Price, { amount: subtotal, currency: displayCurrency, className: "tabular-nums" })
|
|
11027
|
+
]
|
|
11028
|
+
}
|
|
11029
|
+
),
|
|
11030
|
+
totalDiscounts != null && parsePrice(totalDiscounts) > 0 && /* @__PURE__ */ jsxs(
|
|
11031
|
+
"div",
|
|
11032
|
+
{
|
|
11033
|
+
"data-cimplify-cart-discount": true,
|
|
11034
|
+
className: cn("flex items-baseline justify-between", classNames?.discount),
|
|
11035
|
+
children: [
|
|
11036
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Discount" }),
|
|
11037
|
+
/* @__PURE__ */ jsx(
|
|
11038
|
+
Price,
|
|
11039
|
+
{
|
|
11040
|
+
amount: totalDiscounts,
|
|
11041
|
+
currency: displayCurrency,
|
|
11042
|
+
prefix: "-",
|
|
11043
|
+
className: "tabular-nums text-primary"
|
|
11044
|
+
}
|
|
11045
|
+
)
|
|
11046
|
+
]
|
|
11047
|
+
}
|
|
11048
|
+
),
|
|
11049
|
+
deliveryFee != null && /* @__PURE__ */ jsxs(
|
|
11050
|
+
"div",
|
|
11051
|
+
{
|
|
11052
|
+
"data-cimplify-cart-delivery-fee": true,
|
|
11053
|
+
className: cn("flex items-baseline justify-between", classNames?.deliveryFee),
|
|
11054
|
+
children: [
|
|
11055
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Delivery" }),
|
|
11056
|
+
parsePrice(deliveryFee) > 0 ? /* @__PURE__ */ jsx(
|
|
11057
|
+
Price,
|
|
11058
|
+
{
|
|
11059
|
+
amount: deliveryFee,
|
|
11060
|
+
currency: displayCurrency,
|
|
11061
|
+
className: "tabular-nums"
|
|
11062
|
+
}
|
|
11063
|
+
) : /* @__PURE__ */ jsx("span", { className: "font-medium text-primary", children: "Free" })
|
|
11064
|
+
]
|
|
11065
|
+
}
|
|
11066
|
+
),
|
|
11067
|
+
serviceCharge != null && parsePrice(serviceCharge) > 0 && /* @__PURE__ */ jsxs(
|
|
11068
|
+
"div",
|
|
11069
|
+
{
|
|
11070
|
+
"data-cimplify-cart-service-charge": true,
|
|
11071
|
+
className: cn("flex items-baseline justify-between", classNames?.serviceCharge),
|
|
11072
|
+
children: [
|
|
11073
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Service charge" }),
|
|
11074
|
+
/* @__PURE__ */ jsx(
|
|
11075
|
+
Price,
|
|
11076
|
+
{
|
|
11077
|
+
amount: serviceCharge,
|
|
11078
|
+
currency: displayCurrency,
|
|
11079
|
+
className: "tabular-nums"
|
|
11080
|
+
}
|
|
11081
|
+
)
|
|
11082
|
+
]
|
|
11083
|
+
}
|
|
11084
|
+
),
|
|
11085
|
+
/* @__PURE__ */ jsxs(
|
|
11086
|
+
"div",
|
|
11087
|
+
{
|
|
11088
|
+
"data-cimplify-cart-tax": true,
|
|
11089
|
+
className: cn("flex items-baseline justify-between", classNames?.tax),
|
|
11090
|
+
children: [
|
|
11091
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Tax" }),
|
|
11092
|
+
/* @__PURE__ */ jsx(Price, { amount: tax, currency: displayCurrency, className: "tabular-nums" })
|
|
11093
|
+
]
|
|
11094
|
+
}
|
|
11095
|
+
),
|
|
11096
|
+
/* @__PURE__ */ jsxs(
|
|
11097
|
+
"div",
|
|
11098
|
+
{
|
|
11099
|
+
"data-cimplify-cart-total": true,
|
|
11100
|
+
className: cn(
|
|
11101
|
+
"flex items-baseline justify-between pt-3 mt-1 border-t border-border",
|
|
11102
|
+
classNames?.total
|
|
11103
|
+
),
|
|
11104
|
+
children: [
|
|
11105
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: "Total" }),
|
|
11106
|
+
/* @__PURE__ */ jsx(
|
|
11107
|
+
Price,
|
|
11108
|
+
{
|
|
11109
|
+
amount: total,
|
|
11110
|
+
currency: displayCurrency,
|
|
11111
|
+
className: "text-base font-bold tabular-nums"
|
|
11112
|
+
}
|
|
11113
|
+
)
|
|
11114
|
+
]
|
|
11115
|
+
}
|
|
11116
|
+
)
|
|
10713
11117
|
]
|
|
10714
11118
|
}
|
|
10715
11119
|
),
|
|
@@ -10794,12 +11198,14 @@ function SaleBadge({
|
|
|
10794
11198
|
return null;
|
|
10795
11199
|
}
|
|
10796
11200
|
let percentage = null;
|
|
10797
|
-
if (
|
|
11201
|
+
if (product.sale) {
|
|
11202
|
+
percentage = parsePrice(product.sale.percent_off);
|
|
11203
|
+
} else if (hasDeal && dealInfo.benefit_type === "percentage") {
|
|
10798
11204
|
percentage = parsePrice(dealInfo.value);
|
|
10799
11205
|
} else if (onSale) {
|
|
10800
11206
|
percentage = getDiscountPercentage(product);
|
|
10801
11207
|
}
|
|
10802
|
-
const badgeText = label ?? dealInfo?.label ?? (percentage != null && percentage > 0 ? `${percentage}% off` : null);
|
|
11208
|
+
const badgeText = label ?? product.sale?.badge_text ?? dealInfo?.label ?? (percentage != null && percentage > 0 ? `${percentage}% off` : null);
|
|
10803
11209
|
if (!badgeText) {
|
|
10804
11210
|
return null;
|
|
10805
11211
|
}
|