@sazito/checkout 0.1.0 → 0.1.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/README.md +34 -0
- package/dist/chunks/{labels-ChywPk2i.js → labels-ByA_yGKl.js} +131 -53
- package/dist/chunks/labels-ByA_yGKl.js.map +1 -0
- package/dist/chunks/{labels-BlMZkOsV.cjs → labels-DJ_RnkCQ.cjs} +132 -52
- package/dist/chunks/labels-DJ_RnkCQ.cjs.map +1 -0
- package/dist/chunks/{use-checkout-Cvp_K3UW.cjs → use-checkout-CQBSONj1.cjs} +2 -2
- package/dist/chunks/{use-checkout-Cvp_K3UW.cjs.map → use-checkout-CQBSONj1.cjs.map} +1 -1
- package/dist/chunks/{use-checkout-E0O8HsSs.js → use-checkout-gAjnCzae.js} +2 -2
- package/dist/chunks/{use-checkout-E0O8HsSs.js.map → use-checkout-gAjnCzae.js.map} +1 -1
- package/dist/core/index.cjs +3 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +9 -4
- package/dist/core/index.d.ts +9 -4
- package/dist/core/index.js +1 -1
- package/dist/next/index.cjs +19 -9
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +1 -0
- package/dist/next/index.d.ts +1 -0
- package/dist/next/index.js +21 -11
- package/dist/next/index.js.map +1 -1
- package/dist/react/index.cjs +2 -2
- package/dist/react/index.d.cts +1 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +2 -2
- package/dist/styles.css +123 -8
- package/package.json +12 -6
- package/dist/chunks/labels-BlMZkOsV.cjs.map +0 -1
- package/dist/chunks/labels-ChywPk2i.js.map +0 -1
|
@@ -202,6 +202,30 @@ function createBrowserEffectExecutor(config) {
|
|
|
202
202
|
const noopEffectExecutor = () => { };
|
|
203
203
|
|
|
204
204
|
const idStr = (value) => String(value);
|
|
205
|
+
function cartItemCreatedAt(item) {
|
|
206
|
+
if (!item.createdAt)
|
|
207
|
+
return null;
|
|
208
|
+
const timestamp = Date.parse(item.createdAt);
|
|
209
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
210
|
+
}
|
|
211
|
+
/** Return cart lines newest-first without mutating SDK state. */
|
|
212
|
+
function sortCartItemsNewestFirst(items) {
|
|
213
|
+
return [...items].sort((a, b) => {
|
|
214
|
+
const aCreatedAt = cartItemCreatedAt(a);
|
|
215
|
+
const bCreatedAt = cartItemCreatedAt(b);
|
|
216
|
+
if (aCreatedAt != null || bCreatedAt != null) {
|
|
217
|
+
if (aCreatedAt == null)
|
|
218
|
+
return 1;
|
|
219
|
+
if (bCreatedAt == null)
|
|
220
|
+
return -1;
|
|
221
|
+
if (aCreatedAt !== bCreatedAt)
|
|
222
|
+
return bCreatedAt - aCreatedAt;
|
|
223
|
+
}
|
|
224
|
+
const aId = Number(a.id);
|
|
225
|
+
const bId = Number(b.id);
|
|
226
|
+
return Number.isFinite(aId) && Number.isFinite(bId) ? bId - aId : 0;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
205
229
|
function emptyAddressForm() {
|
|
206
230
|
return {
|
|
207
231
|
firstName: '',
|
|
@@ -300,6 +324,28 @@ function uniqueRates(rates) {
|
|
|
300
324
|
}
|
|
301
325
|
return out;
|
|
302
326
|
}
|
|
327
|
+
function isDigitalServiceLabel(value) {
|
|
328
|
+
if (!value)
|
|
329
|
+
return false;
|
|
330
|
+
const normalized = value.trim().toLocaleLowerCase('en-US');
|
|
331
|
+
return (normalized.includes('digital') ||
|
|
332
|
+
normalized.includes('service') ||
|
|
333
|
+
normalized.includes('دیجیتال') ||
|
|
334
|
+
normalized.includes('خدمات') ||
|
|
335
|
+
normalized.includes('خدمت'));
|
|
336
|
+
}
|
|
337
|
+
function isDigitalServiceItem(item) {
|
|
338
|
+
return isDigitalServiceLabel(item.productType);
|
|
339
|
+
}
|
|
340
|
+
/** True for the API's automatic digital/service delivery group. */
|
|
341
|
+
function isDigitalServiceGroup(group) {
|
|
342
|
+
if (isDigitalServiceLabel(group.key) || isDigitalServiceLabel(group.title)) {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
const ratesAreDigitalService = group.rates.length > 0 &&
|
|
346
|
+
group.rates.every((rate) => isDigitalServiceLabel(rate.type) || isDigitalServiceLabel(rate.name));
|
|
347
|
+
return ratesAreDigitalService || (group.items.length > 0 && group.items.every(isDigitalServiceItem));
|
|
348
|
+
}
|
|
303
349
|
/**
|
|
304
350
|
* Group invoice items into shippable bundles with their switchable rates and
|
|
305
351
|
* the currently selected rate. Digital-only invoices yield an empty list.
|
|
@@ -319,10 +365,12 @@ function deriveShippingGroups(invoice, applicable) {
|
|
|
319
365
|
return [];
|
|
320
366
|
}
|
|
321
367
|
const itemById = new Map(invoice.items.map((item) => [idStr(item.id), item]));
|
|
322
|
-
//
|
|
368
|
+
// The API's plain `digital` products never receive a shipping assignment.
|
|
369
|
+
// Other digital/service variants may arrive through an automatic fulfillment
|
|
370
|
+
// rate; retain those groups for the API assignment and hide them in the UI.
|
|
323
371
|
const physicalItemsRate = (applicable.itemsShippingRate ?? []).filter((isr) => {
|
|
324
372
|
const item = itemById.get(idStr(isr.invoiceItemId));
|
|
325
|
-
return !item || item.productType !== 'digital';
|
|
373
|
+
return !item || item.productType?.trim().toLocaleLowerCase('en-US') !== 'digital';
|
|
326
374
|
});
|
|
327
375
|
if (physicalItemsRate.length === 0)
|
|
328
376
|
return [];
|
|
@@ -421,24 +469,27 @@ function deriveShippingGroups(invoice, applicable) {
|
|
|
421
469
|
}
|
|
422
470
|
];
|
|
423
471
|
}
|
|
424
|
-
/** Build the API payload from
|
|
472
|
+
/** Build the API payload from physical shipping selections. */
|
|
425
473
|
function buildShippingAssignments(groups) {
|
|
426
474
|
return groups
|
|
427
|
-
.filter((group) => group.selectedRateId != null)
|
|
475
|
+
.filter((group) => !isDigitalServiceGroup(group) && group.selectedRateId != null)
|
|
428
476
|
.map((group) => ({
|
|
429
477
|
rateId: group.selectedRateId,
|
|
430
478
|
invoiceItemIds: group.itemIds
|
|
431
479
|
}));
|
|
432
480
|
}
|
|
433
|
-
/** True once every
|
|
481
|
+
/** True once every physical shipping group has a selected rate. */
|
|
434
482
|
function isShippingComplete(invoice, groups) {
|
|
435
483
|
if (!invoice?.needsShipping) {
|
|
436
484
|
return true;
|
|
437
485
|
}
|
|
438
|
-
|
|
439
|
-
|
|
486
|
+
const physicalGroups = groups.filter((group) => !isDigitalServiceGroup(group));
|
|
487
|
+
if (physicalGroups.length === 0) {
|
|
488
|
+
// The API may set needsShipping for an automatic Digital/Service group.
|
|
489
|
+
// It is fulfillment metadata, not a customer-selectable shipping method.
|
|
490
|
+
return groups.length > 0 && groups.every(isDigitalServiceGroup);
|
|
440
491
|
}
|
|
441
|
-
return
|
|
492
|
+
return physicalGroups.every((group) => group.selectedRateId != null);
|
|
442
493
|
}
|
|
443
494
|
const codeDiscountTotal = (invoice) => (invoice.couponTotal || 0) + (invoice.discountTotal || 0);
|
|
444
495
|
/**
|
|
@@ -542,14 +593,22 @@ function selectDigitalItems(invoice, groups, applicable) {
|
|
|
542
593
|
// Before shipping methods are loaded, only explicitly-digital items are known.
|
|
543
594
|
// Showing everything as "digital" when applicable is null would be wrong.
|
|
544
595
|
if (applicable == null) {
|
|
545
|
-
return invoice.items.filter(
|
|
596
|
+
return invoice.items.filter(isDigitalServiceItem);
|
|
546
597
|
}
|
|
547
|
-
//
|
|
548
|
-
|
|
549
|
-
|
|
598
|
+
// The backend may model digital/service fulfillment as an automatic zero-cost
|
|
599
|
+
// shipping group. Treat its products as non-shippable in the UI while keeping
|
|
600
|
+
// the group in state so its assignment can still be submitted.
|
|
601
|
+
const digitalGroupItems = new Set(groups
|
|
602
|
+
.filter(isDigitalServiceGroup)
|
|
603
|
+
.flatMap((group) => group.itemIds.map(idStr)));
|
|
604
|
+
const groupedItems = new Set(groups.flatMap((group) => group.itemIds.map(idStr)));
|
|
605
|
+
return invoice.items.filter((item) => isDigitalServiceItem(item) ||
|
|
606
|
+
digitalGroupItems.has(idStr(item.id)) ||
|
|
607
|
+
!groupedItems.has(idStr(item.id)));
|
|
550
608
|
}
|
|
551
609
|
|
|
552
610
|
const STEP_ORDER = ['cart', 'shipping', 'payment', 'result'];
|
|
611
|
+
const ADDRESS_AUTOSUBMIT_DEBOUNCE_MS = 350;
|
|
553
612
|
function initialFlags() {
|
|
554
613
|
return {
|
|
555
614
|
bootstrapping: false,
|
|
@@ -596,6 +655,7 @@ function createCheckoutEngine(options) {
|
|
|
596
655
|
let effectExecutor = noopEffectExecutor;
|
|
597
656
|
let lock = Promise.resolve();
|
|
598
657
|
let addressRevision = 0;
|
|
658
|
+
let addressAutoSubmitTimer = null;
|
|
599
659
|
// ---- internal helpers -------------------------------------------------
|
|
600
660
|
const get = () => store.getState();
|
|
601
661
|
const set = store.setState;
|
|
@@ -616,6 +676,19 @@ function createCheckoutEngine(options) {
|
|
|
616
676
|
lock = next.then(() => undefined, () => undefined);
|
|
617
677
|
return next;
|
|
618
678
|
}
|
|
679
|
+
function clearAddressAutoSubmit() {
|
|
680
|
+
if (addressAutoSubmitTimer) {
|
|
681
|
+
clearTimeout(addressAutoSubmitTimer);
|
|
682
|
+
addressAutoSubmitTimer = null;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function scheduleAddressAutoSubmit(delay = ADDRESS_AUTOSUBMIT_DEBOUNCE_MS) {
|
|
686
|
+
clearAddressAutoSubmit();
|
|
687
|
+
addressAutoSubmitTimer = setTimeout(() => {
|
|
688
|
+
addressAutoSubmitTimer = null;
|
|
689
|
+
void withLock(submitAddressInternal);
|
|
690
|
+
}, delay);
|
|
691
|
+
}
|
|
619
692
|
function recomputeGroups() {
|
|
620
693
|
const { invoice, applicable } = get();
|
|
621
694
|
const groups = deriveShippingGroups(invoice, applicable);
|
|
@@ -811,13 +884,17 @@ function createCheckoutEngine(options) {
|
|
|
811
884
|
}
|
|
812
885
|
// ---- public actions ---------------------------------------------------
|
|
813
886
|
function goToStep(step) {
|
|
887
|
+
if (step !== 'shipping') {
|
|
888
|
+
clearAddressAutoSubmit();
|
|
889
|
+
}
|
|
814
890
|
set({ step, error: null });
|
|
815
891
|
emit('step_viewed', { step });
|
|
816
892
|
// When landing on shipping with a complete address but no methods fetched yet,
|
|
817
893
|
// auto-submit so methods appear without requiring a manual Continue press.
|
|
818
894
|
if (step === 'shipping') {
|
|
819
|
-
const { addressForm, invoice, applicable } = get();
|
|
820
|
-
|
|
895
|
+
const { addressForm, addressDirty, invoice, applicable, shippingGroups } = get();
|
|
896
|
+
const methodsUnresolved = Boolean(invoice?.needsShipping) && shippingGroups.length === 0;
|
|
897
|
+
if ((addressDirty || !applicable || methodsUnresolved) && isAddressComplete(addressForm, invoice?.needsShipping ?? true, get().postalCodeMandatory, get().emailMandatory)) {
|
|
821
898
|
void withLock(submitAddressInternal);
|
|
822
899
|
}
|
|
823
900
|
}
|
|
@@ -920,13 +997,18 @@ function createCheckoutEngine(options) {
|
|
|
920
997
|
// the Shipping step becomes visible.
|
|
921
998
|
const invoice = get().invoice;
|
|
922
999
|
if (isUsableSavedAddress(invoice?.shippingAddress)) {
|
|
923
|
-
|
|
1000
|
+
const addressForm = reconcileAddressWithRegions(addressFormFromInvoice(invoice), get().regions, savedAddressCityName(invoice?.shippingAddress));
|
|
1001
|
+
set({
|
|
1002
|
+
addressForm,
|
|
1003
|
+
addressDirty: isAddressDirty(addressForm, invoice)
|
|
1004
|
+
});
|
|
924
1005
|
}
|
|
925
1006
|
goToStep('shipping');
|
|
926
1007
|
}
|
|
927
1008
|
return;
|
|
928
1009
|
}
|
|
929
1010
|
if (state.step === 'shipping') {
|
|
1011
|
+
clearAddressAutoSubmit();
|
|
930
1012
|
// Phase 1: address not saved yet (or changed) → save it and reveal the
|
|
931
1013
|
// shipping methods, staying on the shipping step.
|
|
932
1014
|
const needsSave = state.addressDirty || !state.applicable;
|
|
@@ -971,10 +1053,6 @@ function createCheckoutEngine(options) {
|
|
|
971
1053
|
const res = await binding.client.cart.updateItem(cartProductId, variantId, quantity);
|
|
972
1054
|
if (res.data) {
|
|
973
1055
|
set({ cart: res.data });
|
|
974
|
-
// Optimistically update summary from cart data, then confirm with server.
|
|
975
|
-
const inv = get().invoice;
|
|
976
|
-
if (inv)
|
|
977
|
-
set({ invoice: applyCartToInvoice(inv, res.data) });
|
|
978
1056
|
await refreshInvoice();
|
|
979
1057
|
}
|
|
980
1058
|
else if (res.error) {
|
|
@@ -989,9 +1067,6 @@ function createCheckoutEngine(options) {
|
|
|
989
1067
|
const res = await binding.client.cart.removeItem(cartProductId, variantId);
|
|
990
1068
|
if (res.data) {
|
|
991
1069
|
set({ cart: res.data });
|
|
992
|
-
const inv = get().invoice;
|
|
993
|
-
if (inv)
|
|
994
|
-
set({ invoice: applyCartToInvoice(inv, res.data) });
|
|
995
1070
|
await refreshInvoice();
|
|
996
1071
|
}
|
|
997
1072
|
else if (res.error) {
|
|
@@ -1003,6 +1078,7 @@ function createCheckoutEngine(options) {
|
|
|
1003
1078
|
setAddressField(key, value) {
|
|
1004
1079
|
const before = get().addressForm;
|
|
1005
1080
|
const changed = before[key] !== value || (key === 'regionId' && before.cityId !== null);
|
|
1081
|
+
const destinationChanged = changed && (key === 'regionId' || key === 'cityId');
|
|
1006
1082
|
if (changed) {
|
|
1007
1083
|
addressRevision += 1;
|
|
1008
1084
|
}
|
|
@@ -1016,22 +1092,28 @@ function createCheckoutEngine(options) {
|
|
|
1016
1092
|
return {
|
|
1017
1093
|
addressForm,
|
|
1018
1094
|
addressDirty,
|
|
1019
|
-
//
|
|
1020
|
-
//
|
|
1021
|
-
...(
|
|
1095
|
+
// Shipping rates depend on region/city. Contact and street edits must
|
|
1096
|
+
// not discard valid methods for the same destination.
|
|
1097
|
+
...(destinationChanged ? { applicable: null, shippingGroups: [] } : {})
|
|
1022
1098
|
};
|
|
1023
1099
|
});
|
|
1024
|
-
//
|
|
1025
|
-
//
|
|
1026
|
-
//
|
|
1027
|
-
if (changed &&
|
|
1100
|
+
// Browser autofill often populates the selects before the final required
|
|
1101
|
+
// text field. Once the form becomes complete, debounce text changes and
|
|
1102
|
+
// submit automatically; a discrete city selection can submit immediately.
|
|
1103
|
+
if (changed && get().step === 'shipping') {
|
|
1028
1104
|
const state = get();
|
|
1029
|
-
|
|
1030
|
-
|
|
1105
|
+
const methodsUnresolved = !state.applicable
|
|
1106
|
+
|| (Boolean(state.invoice?.needsShipping) && state.shippingGroups.length === 0);
|
|
1107
|
+
if (isAddressComplete(state.addressForm, state.invoice?.needsShipping ?? true, state.postalCodeMandatory, state.emailMandatory) && (destinationChanged || methodsUnresolved)) {
|
|
1108
|
+
scheduleAddressAutoSubmit(key === 'cityId' ? 0 : undefined);
|
|
1109
|
+
}
|
|
1110
|
+
else if (!isAddressComplete(state.addressForm, state.invoice?.needsShipping ?? true, state.postalCodeMandatory, state.emailMandatory)) {
|
|
1111
|
+
clearAddressAutoSubmit();
|
|
1031
1112
|
}
|
|
1032
1113
|
}
|
|
1033
1114
|
},
|
|
1034
1115
|
async submitAddress() {
|
|
1116
|
+
clearAddressAutoSubmit();
|
|
1035
1117
|
return withLock(submitAddressInternal);
|
|
1036
1118
|
},
|
|
1037
1119
|
async selectShippingRate(groupKey, rateId) {
|
|
@@ -1153,6 +1235,8 @@ function createCheckoutEngine(options) {
|
|
|
1153
1235
|
const state = get();
|
|
1154
1236
|
const submittedAddressRevision = addressRevision;
|
|
1155
1237
|
const needsShipping = state.invoice?.needsShipping ?? true;
|
|
1238
|
+
const shouldReloadShipping = needsShipping
|
|
1239
|
+
&& (!state.applicable || state.shippingGroups.length === 0);
|
|
1156
1240
|
const abandonStaleSubmission = (invoice) => {
|
|
1157
1241
|
set({
|
|
1158
1242
|
...(invoice ? { invoice } : {}),
|
|
@@ -1197,7 +1281,15 @@ function createCheckoutEngine(options) {
|
|
|
1197
1281
|
}
|
|
1198
1282
|
set({ invoice: linkRes.data, addressDirty: false });
|
|
1199
1283
|
if (needsShipping) {
|
|
1200
|
-
|
|
1284
|
+
let ok = true;
|
|
1285
|
+
if (shouldReloadShipping) {
|
|
1286
|
+
ok = await loadApplicableShipping(submittedAddressRevision);
|
|
1287
|
+
}
|
|
1288
|
+
else if (recomputeGroups().length === 0) {
|
|
1289
|
+
// Invoice item IDs can change when the new address snapshot is linked.
|
|
1290
|
+
// Reload only if the existing applicable data can no longer form groups.
|
|
1291
|
+
ok = await loadApplicableShipping(submittedAddressRevision);
|
|
1292
|
+
}
|
|
1201
1293
|
if (ok) {
|
|
1202
1294
|
await assignCurrentShipping();
|
|
1203
1295
|
if (addressRevision !== submittedAddressRevision) {
|
|
@@ -1224,6 +1316,7 @@ function createCheckoutEngine(options) {
|
|
|
1224
1316
|
effectExecutor = executor;
|
|
1225
1317
|
},
|
|
1226
1318
|
destroy() {
|
|
1319
|
+
clearAddressAutoSubmit();
|
|
1227
1320
|
effectExecutor = noopEffectExecutor;
|
|
1228
1321
|
}
|
|
1229
1322
|
};
|
|
@@ -1276,23 +1369,6 @@ function toAddressInput(form) {
|
|
|
1276
1369
|
description: form.description.trim() || undefined
|
|
1277
1370
|
};
|
|
1278
1371
|
}
|
|
1279
|
-
/**
|
|
1280
|
-
* Immediately derive updated invoice item quantities and totals from a fresh
|
|
1281
|
-
* cart response so the summary panel doesn't wait for the invoice refresh.
|
|
1282
|
-
*/
|
|
1283
|
-
function applyCartToInvoice(invoice, cart) {
|
|
1284
|
-
const byVariant = new Map(cart.items.map((i) => [i.productVariantId, i]));
|
|
1285
|
-
const newItems = invoice.items
|
|
1286
|
-
.filter((item) => byVariant.has(item.productVariantId))
|
|
1287
|
-
.map((item) => {
|
|
1288
|
-
const ci = byVariant.get(item.productVariantId);
|
|
1289
|
-
return { ...item, quantity: ci.quantity, lineTotal: ci.lineTotal };
|
|
1290
|
-
});
|
|
1291
|
-
const subtotal = newItems.reduce((s, i) => s + i.lineTotal, 0);
|
|
1292
|
-
const discount = (invoice.itemsDiscount || 0) + (invoice.discountTotal || 0) + (invoice.couponTotal || 0);
|
|
1293
|
-
const finalTotal = Math.max(0, subtotal - discount + (invoice.shippingTotal || 0) + (invoice.vat || 0) - (invoice.creditTotal || 0));
|
|
1294
|
-
return { ...invoice, items: newItems, itemsTotalRawPrice: subtotal, netTotal: subtotal, finalTotal };
|
|
1295
|
-
}
|
|
1296
1372
|
// Map raw gateway-return query params into the typed payment-step input.
|
|
1297
1373
|
// Only the string-valued callback fields are forwarded; `trackingData` /
|
|
1298
1374
|
// `payload` are parsed from JSON when the gateway sends them encoded.
|
|
@@ -1453,7 +1529,8 @@ const fa = {
|
|
|
1453
1529
|
shippingMethod: 'روش ارسال',
|
|
1454
1530
|
shippingMethods: 'روشهای ارسال',
|
|
1455
1531
|
shippingMethodsHint: 'پس از ثبت نشانی، روشهای قابل استفاده نمایش داده میشوند.',
|
|
1456
|
-
digitalNoShipping: '
|
|
1532
|
+
digitalNoShipping: 'محصولات دیجیتال و خدمات',
|
|
1533
|
+
digitalNoShippingHint: 'این موارد به ارسال فیزیکی نیاز ندارند.',
|
|
1457
1534
|
errorRequired: 'این فیلد اجباری است',
|
|
1458
1535
|
errorMobilePhone: 'شماره موبایل معتبر وارد کنید (مثلاً ۰۹۱۲۳۴۵۶۷۸۹)',
|
|
1459
1536
|
errorEmail: 'آدرس ایمیل معتبر نیست',
|
|
@@ -1533,7 +1610,8 @@ const en = {
|
|
|
1533
1610
|
shippingMethod: 'Shipping method',
|
|
1534
1611
|
shippingMethods: 'Shipping methods',
|
|
1535
1612
|
shippingMethodsHint: 'Available delivery options will appear after you save this address.',
|
|
1536
|
-
digitalNoShipping: 'Digital
|
|
1613
|
+
digitalNoShipping: 'Digital products and services',
|
|
1614
|
+
digitalNoShippingHint: 'These items do not require physical delivery.',
|
|
1537
1615
|
errorRequired: 'This field is required',
|
|
1538
1616
|
errorMobilePhone: 'Enter a valid mobile number (e.g. 09123456789)',
|
|
1539
1617
|
errorEmail: 'Enter a valid email address',
|
|
@@ -1628,6 +1706,7 @@ exports.formatPrice = formatPrice;
|
|
|
1628
1706
|
exports.fromSdkError = fromSdkError;
|
|
1629
1707
|
exports.isAddressComplete = isAddressComplete;
|
|
1630
1708
|
exports.isAddressDirty = isAddressDirty;
|
|
1709
|
+
exports.isDigitalServiceGroup = isDigitalServiceGroup;
|
|
1631
1710
|
exports.isShippingComplete = isShippingComplete;
|
|
1632
1711
|
exports.isValidIranianMobile = isValidIranianMobile;
|
|
1633
1712
|
exports.isValidIranianPhone = isValidIranianPhone;
|
|
@@ -1640,7 +1719,8 @@ exports.paymentMethodInfo = paymentMethodInfo;
|
|
|
1640
1719
|
exports.paymentMethodLabel = paymentMethodLabel;
|
|
1641
1720
|
exports.selectDigitalItems = selectDigitalItems;
|
|
1642
1721
|
exports.selectSummary = selectSummary;
|
|
1722
|
+
exports.sortCartItemsNewestFirst = sortCartItemsNewestFirst;
|
|
1643
1723
|
exports.strings = strings;
|
|
1644
1724
|
exports.toEnglishDigits = toEnglishDigits;
|
|
1645
1725
|
exports.toPersianDigits = toPersianDigits;
|
|
1646
|
-
//# sourceMappingURL=labels-
|
|
1726
|
+
//# sourceMappingURL=labels-DJ_RnkCQ.cjs.map
|