@asteby/storefront-react 0.24.0 → 0.26.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.
Files changed (48) hide show
  1. package/README.md +35 -2
  2. package/dist/account-server.cjs +128 -19
  3. package/dist/account-server.cjs.map +1 -1
  4. package/dist/account-server.d.cts +67 -4
  5. package/dist/account-server.d.ts +67 -4
  6. package/dist/account-server.js +125 -20
  7. package/dist/account-server.js.map +1 -1
  8. package/dist/account.cjs +261 -8
  9. package/dist/account.cjs.map +1 -1
  10. package/dist/account.css +99 -0
  11. package/dist/account.d.cts +132 -6
  12. package/dist/account.d.ts +132 -6
  13. package/dist/account.js +255 -9
  14. package/dist/account.js.map +1 -1
  15. package/dist/checkout.cjs +162 -8
  16. package/dist/checkout.cjs.map +1 -1
  17. package/dist/checkout.d.cts +104 -4
  18. package/dist/checkout.d.ts +104 -4
  19. package/dist/checkout.js +158 -10
  20. package/dist/checkout.js.map +1 -1
  21. package/dist/chunk-Z4C4LKXF.js +25 -0
  22. package/dist/chunk-Z4C4LKXF.js.map +1 -0
  23. package/dist/fit-key-B9sYQVwG.d.cts +49 -0
  24. package/dist/fit-key-B9sYQVwG.d.ts +49 -0
  25. package/dist/fitment.cjs +54 -9
  26. package/dist/fitment.cjs.map +1 -1
  27. package/dist/fitment.d.cts +20 -2
  28. package/dist/fitment.d.ts +20 -2
  29. package/dist/fitment.js +54 -10
  30. package/dist/fitment.js.map +1 -1
  31. package/dist/settings-types-jxFQFFqP.d.cts +77 -0
  32. package/dist/settings-types-jxFQFFqP.d.ts +77 -0
  33. package/dist/subjects-server.cjs +248 -0
  34. package/dist/subjects-server.cjs.map +1 -0
  35. package/dist/subjects-server.d.cts +130 -0
  36. package/dist/subjects-server.d.ts +130 -0
  37. package/dist/subjects-server.js +217 -0
  38. package/dist/subjects-server.js.map +1 -0
  39. package/dist/subjects.cjs +501 -0
  40. package/dist/subjects.cjs.map +1 -0
  41. package/dist/subjects.css +166 -0
  42. package/dist/subjects.d.cts +178 -0
  43. package/dist/subjects.d.ts +178 -0
  44. package/dist/subjects.js +465 -0
  45. package/dist/subjects.js.map +1 -0
  46. package/package.json +23 -2
  47. package/dist/types-D7pVK0GK.d.cts +0 -39
  48. package/dist/types-D7pVK0GK.d.ts +0 -39
package/dist/checkout.cjs CHANGED
@@ -1180,7 +1180,10 @@ var ES4 = {
1180
1180
  moreOptions: "Ver m\xE1s opciones ({n})",
1181
1181
  otherCarriers: "Otras opciones",
1182
1182
  error: "No pudimos consultar las opciones de env\xEDo en este momento.",
1183
- retry: "Intentar de nuevo"
1183
+ retry: "Intentar de nuevo",
1184
+ freeShipping: "Env\xEDo gratis",
1185
+ freeShippingNote: "Tu pedido ya incluye el env\xEDo: no tienes que elegir paqueter\xEDa.",
1186
+ ratesReady: "Opciones de env\xEDo listas ({n})."
1184
1187
  };
1185
1188
  var EN4 = {
1186
1189
  etaOne: "Arrives in 1 business day",
@@ -1203,7 +1206,10 @@ var EN4 = {
1203
1206
  moreOptions: "Show more options ({n})",
1204
1207
  otherCarriers: "Other options",
1205
1208
  error: "We couldn't get delivery options right now.",
1206
- retry: "Try again"
1209
+ retry: "Try again",
1210
+ freeShipping: "Free shipping",
1211
+ freeShippingNote: "Shipping is included in your order: there is no carrier to choose.",
1212
+ ratesReady: "Delivery options ready ({n})."
1207
1213
  };
1208
1214
  function deliveryMessages(locale, overrides) {
1209
1215
  const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN4 : ES4;
@@ -1296,13 +1302,13 @@ function isQuoteChange(err) {
1296
1302
  }
1297
1303
  function matchRate(previous, rates) {
1298
1304
  if (!previous) return null;
1299
- const norm2 = (s) => (s ?? "").trim().toLowerCase();
1300
- const sameCarrier = (r) => norm2(r.carrier) === norm2(previous.carrier);
1305
+ const norm3 = (s) => (s ?? "").trim().toLowerCase();
1306
+ const sameCarrier = (r) => norm3(r.carrier) === norm3(previous.carrier);
1301
1307
  if (previous.service_code) {
1302
- const hit = rates.find((r) => sameCarrier(r) && norm2(r.service_code) === norm2(previous.service_code));
1308
+ const hit = rates.find((r) => sameCarrier(r) && norm3(r.service_code) === norm3(previous.service_code));
1303
1309
  if (hit) return hit;
1304
1310
  }
1305
- return rates.find((r) => sameCarrier(r) && norm2(r.title) === norm2(previous.title)) ?? null;
1311
+ return rates.find((r) => sameCarrier(r) && norm3(r.title) === norm3(previous.title)) ?? null;
1306
1312
  }
1307
1313
  var SR_ONLY = { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" };
1308
1314
  var useHydrated = () => react.useSyncExternalStore(
@@ -1339,7 +1345,8 @@ function DeliveryOptions(props) {
1339
1345
  children,
1340
1346
  layout = "list",
1341
1347
  error,
1342
- onRetry
1348
+ onRetry,
1349
+ free = false
1343
1350
  } = props;
1344
1351
  const locale = useResolvedLocale(props.locale);
1345
1352
  const m = react.useMemo(() => deliveryMessages(locale, props.messages), [locale, props.messages]);
@@ -1407,17 +1414,24 @@ function DeliveryOptions(props) {
1407
1414
  ] }, rate.id);
1408
1415
  };
1409
1416
  const errorText = error ? typeof error === "string" ? error : m.error : null;
1417
+ const freeRate = free && !loading && pickup.length === 0 ? rates.find((r) => !r.pickup) ?? null : null;
1410
1418
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx.root, "data-sf-delivery": true, "data-layout": layout, "aria-busy": loading || void 0, children: [
1411
1419
  partial && !errorText ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", className: cx.partial, "data-sf-delivery-partial": true, children: m.partial }) : null,
1412
1420
  requoted ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", className: cx.partial, "data-sf-delivery-requoted": true, children: m.requoted }) : null,
1413
1421
  loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: SR_ONLY, role: "status", "data-sf-delivery-loading": true, children: m.loading }) : null,
1422
+ props.announceReady && !loading && rates.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: SR_ONLY, role: "status", "data-sf-delivery-ready": true, children: freeRate ? m.freeShipping : m.ratesReady.replace("{n}", String(rates.length)) }) : null,
1414
1423
  errorText && rates.length === 0 && !loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: cx.error, "data-sf-delivery-error": true, children: [
1415
1424
  /* @__PURE__ */ jsxRuntime.jsx("p", { children: errorText }),
1416
1425
  onRetry ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: cx.retry, onClick: onRetry, "data-sf-delivery-retry": true, children: m.retry }) : null
1417
1426
  ] }) : null,
1427
+ freeRate ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx.freeBanner, role: props.announceReady ? void 0 : "status", "data-sf-delivery-free-banner": true, children: [
1428
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: rateValue(freeRate), ...testId ? { "data-testid": testId } : {} }),
1429
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: cx.free, "data-sf-delivery-free-title": true, children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: m.freeShipping }) }),
1430
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: cx.note, children: m.freeShippingNote })
1431
+ ] }) : null,
1418
1432
  children,
1419
1433
  loading && rates.length === 0 ? [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx.skeleton, "data-sf-delivery-skeleton": true, "aria-hidden": "true" }, i)) : null,
1420
- flat ? flat.map(({ rate, tags }) => rateRow(rate, { tags: showBadges ? tags : [] })) : plan ? plan.recommended.map(({ rate, tags }) => rateRow(rate, { tags: showBadges ? tags : [] })) : rates.map((rate) => rateRow(rate, { tags: badges.get(rate.id) ?? [] })),
1434
+ freeRate ? null : flat ? flat.map(({ rate, tags }) => rateRow(rate, { tags: showBadges ? tags : [] })) : plan ? plan.recommended.map(({ rate, tags }) => rateRow(rate, { tags: showBadges ? tags : [] })) : rates.map((rate) => rateRow(rate, { tags: badges.get(rate.id) ?? [] })),
1421
1435
  plan && plan.moreCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: cx.more, "data-sf-delivery-more": true, ...inMore ? { open: true } : {}, children: [
1422
1436
  /* @__PURE__ */ jsxRuntime.jsx("summary", { className: cx.moreSummary, "data-sf-delivery-more-summary": true, children: m.moreOptions.replace("{n}", String(plan.moreCount)) }),
1423
1437
  /* @__PURE__ */ jsxRuntime.jsx("div", { "data-sf-delivery-groups": true, children: plan.more.map((g) => {
@@ -1444,21 +1458,158 @@ function DeliveryOptions(props) {
1444
1458
  deadline ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: cx.note, "data-sf-delivery-note": true, "data-soon": shownNote?.soon || void 0, "aria-live": "polite", children: shownNote?.text ?? "" }) : null
1445
1459
  ] });
1446
1460
  }
1461
+ var DEFAULT_QUOTE_NAMES = {
1462
+ country_code: "country_code",
1463
+ zip: "zip",
1464
+ province: "province",
1465
+ city: "city",
1466
+ neighborhood: "neighborhood",
1467
+ address1: "address1"
1468
+ };
1469
+ var DEFAULT_REQUIRED = ["zip", "province", "city", "neighborhood", "address1"];
1470
+ var QUOTE_KEYS = ["country_code", "zip", "province", "city"];
1471
+ var norm2 = (v) => v.normalize("NFD").replace(/[̀-ͯ]/g, "").trim().toLowerCase();
1472
+ function quoteFingerprint(a) {
1473
+ return QUOTE_KEYS.map((k) => norm2(a[k] ?? "")).join("|");
1474
+ }
1475
+ function defaultRate(delivery) {
1476
+ let best = null;
1477
+ for (const r of delivery.rates) {
1478
+ if (r.pickup) continue;
1479
+ if (!best || Number(r.cost.amount) < Number(best.cost.amount)) best = r;
1480
+ }
1481
+ return best;
1482
+ }
1483
+ function readQuoteAddress(form, opts = {}) {
1484
+ if (!form) return null;
1485
+ const names = { ...DEFAULT_QUOTE_NAMES, ...opts.names };
1486
+ const data = new FormData(form);
1487
+ const get = (name) => String(data.get(name) ?? "").trim();
1488
+ const address = {
1489
+ country_code: get(names.country_code) || opts.defaultCountry || "MX",
1490
+ zip: get(names.zip),
1491
+ province: get(names.province),
1492
+ city: get(names.city),
1493
+ neighborhood: get(names.neighborhood),
1494
+ address1: get(names.address1)
1495
+ };
1496
+ const valid = opts.validPostalCode ?? ((z) => /^\d{5}$/.test(z));
1497
+ if (!valid(address.zip)) return null;
1498
+ for (const k of opts.required ?? DEFAULT_REQUIRED) if (!address[k]) return null;
1499
+ return address;
1500
+ }
1501
+ function useAutoQuote(options) {
1502
+ const { formRef, debounceMs = 600, enabled = true } = options;
1503
+ const [status, setStatus] = react.useState("idle");
1504
+ const [delivery, setDelivery] = react.useState(null);
1505
+ const [quotedFor, setQuotedFor] = react.useState(null);
1506
+ const opts = react.useRef(options);
1507
+ opts.current = options;
1508
+ const timer = react.useRef(null);
1509
+ const inflight = react.useRef(null);
1510
+ const quotedFp = react.useRef(null);
1511
+ const pendingFp = react.useRef(null);
1512
+ const run = react.useCallback((address, fp) => {
1513
+ inflight.current?.abort();
1514
+ const ctl = new AbortController();
1515
+ inflight.current = ctl;
1516
+ pendingFp.current = fp;
1517
+ setStatus("loading");
1518
+ opts.current.quote(address, ctl.signal).then((d) => {
1519
+ if (ctl.signal.aborted) return;
1520
+ quotedFp.current = fp;
1521
+ pendingFp.current = null;
1522
+ setDelivery(d);
1523
+ setQuotedFor(address);
1524
+ setStatus("ready");
1525
+ opts.current.onQuoted?.(d);
1526
+ }).catch(() => {
1527
+ if (ctl.signal.aborted) return;
1528
+ pendingFp.current = null;
1529
+ quotedFp.current = null;
1530
+ setStatus("error");
1531
+ });
1532
+ }, []);
1533
+ const evaluate = react.useCallback(
1534
+ (force) => {
1535
+ const address = readQuoteAddress(formRef.current, opts.current);
1536
+ if (!address) {
1537
+ if (timer.current) clearTimeout(timer.current);
1538
+ timer.current = null;
1539
+ inflight.current?.abort();
1540
+ pendingFp.current = null;
1541
+ setStatus("idle");
1542
+ return;
1543
+ }
1544
+ const fp = quoteFingerprint(address);
1545
+ if (!force && (fp === quotedFp.current || fp === pendingFp.current)) {
1546
+ if (timer.current) clearTimeout(timer.current);
1547
+ timer.current = null;
1548
+ if (fp === quotedFp.current) setStatus("ready");
1549
+ return;
1550
+ }
1551
+ if (timer.current) clearTimeout(timer.current);
1552
+ if (force) {
1553
+ timer.current = null;
1554
+ run(address, fp);
1555
+ return;
1556
+ }
1557
+ setStatus("waiting");
1558
+ timer.current = setTimeout(() => {
1559
+ timer.current = null;
1560
+ const latest = readQuoteAddress(formRef.current, opts.current);
1561
+ if (latest) run(latest, quoteFingerprint(latest));
1562
+ }, debounceMs);
1563
+ },
1564
+ [debounceMs, formRef, run]
1565
+ );
1566
+ react.useEffect(() => {
1567
+ const form = formRef.current;
1568
+ if (!enabled || !form) return;
1569
+ const onChange = () => evaluate(false);
1570
+ form.addEventListener("input", onChange);
1571
+ form.addEventListener("change", onChange);
1572
+ const first = setTimeout(onChange, 0);
1573
+ return () => {
1574
+ clearTimeout(first);
1575
+ form.removeEventListener("input", onChange);
1576
+ form.removeEventListener("change", onChange);
1577
+ if (timer.current) clearTimeout(timer.current);
1578
+ inflight.current?.abort();
1579
+ };
1580
+ }, [enabled, evaluate, formRef]);
1581
+ const retry = react.useCallback(() => evaluate(true), [evaluate]);
1582
+ const defaultValue = react.useMemo(() => {
1583
+ if (!delivery) return null;
1584
+ const saved = opts.current.savedValue;
1585
+ if (saved?.startsWith("rate:")) {
1586
+ const hit = delivery.rates.find((r) => `rate:${r.id}` === saved);
1587
+ if (hit) return `rate:${hit.id}`;
1588
+ }
1589
+ const pick = (opts.current.pickDefault ?? defaultRate)(delivery);
1590
+ return pick ? `rate:${pick.id}` : null;
1591
+ }, [delivery, options.savedValue]);
1592
+ const announce = status === "loading" || status === "waiting" ? "searching" : status === "error" ? "error" : status === "ready" ? delivery?.free ? "free" : "ready" : null;
1593
+ return { status, delivery, defaultValue, quotedFor, retry, announce };
1594
+ }
1447
1595
 
1448
1596
  exports.ADDRESS_FILL_EVENT_FLAG = ADDRESS_FILL_EVENT_FLAG;
1449
1597
  exports.AddressFields = AddressFields;
1450
1598
  exports.BrandMark = BrandMark;
1451
1599
  exports.CheckoutField = CheckoutField;
1452
1600
  exports.ContactFields = ContactFields;
1601
+ exports.DEFAULT_QUOTE_NAMES = DEFAULT_QUOTE_NAMES;
1453
1602
  exports.DeliveryOptions = DeliveryOptions;
1454
1603
  exports.ErrorSummary = ErrorSummary;
1455
1604
  exports.OrderSummary = OrderSummary;
1456
1605
  exports.OrderSummaryDisclosure = OrderSummaryDisclosure;
1606
+ exports.QUOTE_KEYS = QUOTE_KEYS;
1457
1607
  exports.addressFieldsMessages = addressFieldsMessages;
1458
1608
  exports.brandKey = brandKey;
1459
1609
  exports.brandLogo = brandLogo;
1460
1610
  exports.brandName = brandName;
1461
1611
  exports.dedupeRates = dedupeRates;
1612
+ exports.defaultRate = defaultRate;
1462
1613
  exports.deliveryMessages = deliveryMessages;
1463
1614
  exports.fieldMessages = fieldMessages;
1464
1615
  exports.findBrand = findBrand;
@@ -1469,12 +1620,15 @@ exports.listBrands = listBrands;
1469
1620
  exports.matchRate = matchRate;
1470
1621
  exports.orderSummaryMessages = orderSummaryMessages;
1471
1622
  exports.quoteDeadline = quoteDeadline;
1623
+ exports.quoteFingerprint = quoteFingerprint;
1472
1624
  exports.quoteNote = quoteNote;
1473
1625
  exports.rateBadges = rateBadges;
1474
1626
  exports.rateCarrier = rateCarrier;
1475
1627
  exports.rateEta = rateEta;
1476
1628
  exports.rateService = rateService;
1629
+ exports.readQuoteAddress = readQuoteAddress;
1477
1630
  exports.recommendRates = recommendRates;
1631
+ exports.useAutoQuote = useAutoQuote;
1478
1632
  exports.useFieldErrors = useFieldErrors;
1479
1633
  exports.validationMessage = validationMessage;
1480
1634
  //# sourceMappingURL=checkout.cjs.map