@nevios/storefront-kit 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var React19 = require('react');
3
+ var React27 = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var storefrontJs = require('@nevios/storefront-js');
6
6
 
@@ -22,15 +22,15 @@ function _interopNamespace(e) {
22
22
  return Object.freeze(n);
23
23
  }
24
24
 
25
- var React19__namespace = /*#__PURE__*/_interopNamespace(React19);
25
+ var React27__namespace = /*#__PURE__*/_interopNamespace(React27);
26
26
 
27
27
  // src/provider.tsx
28
- var StorefrontContext = React19__namespace.createContext(null);
28
+ var StorefrontContext = React27__namespace.createContext(null);
29
29
  function NeviosProvider({ client, children }) {
30
30
  return /* @__PURE__ */ jsxRuntime.jsx(StorefrontContext.Provider, { value: client, children });
31
31
  }
32
32
  function useStorefront() {
33
- const client = React19__namespace.useContext(StorefrontContext);
33
+ const client = React27__namespace.useContext(StorefrontContext);
34
34
  if (!client) {
35
35
  throw new Error(
36
36
  "useStorefront must be used inside <NeviosProvider>. Wrap your app root with it."
@@ -39,7 +39,7 @@ function useStorefront() {
39
39
  return client;
40
40
  }
41
41
  function useStorefrontOptional() {
42
- return React19__namespace.useContext(StorefrontContext);
42
+ return React27__namespace.useContext(StorefrontContext);
43
43
  }
44
44
  var DEFAULTS = {
45
45
  brand: "#111111",
@@ -75,7 +75,7 @@ function ThemeProvider({
75
75
  as: Tag = "div"
76
76
  }) {
77
77
  const style = themeToCssVars(theme);
78
- return React19__namespace.createElement(
78
+ return React27__namespace.createElement(
79
79
  Tag,
80
80
  { "data-nevios-theme": "", style },
81
81
  children
@@ -84,6 +84,16 @@ function ThemeProvider({
84
84
 
85
85
  // src/i18n/en.ts
86
86
  var en = {
87
+ "subscriptions.purchaseOption": "Purchase option",
88
+ "subscriptions.empty": "You have no subscriptions yet.",
89
+ "subscriptions.status.active": "Active",
90
+ "subscriptions.status.paused": "Paused",
91
+ "subscriptions.status.pending": "Awaiting payment",
92
+ "subscriptions.status.cancelled": "Cancelled",
93
+ "subscriptions.title": "Subscription",
94
+ "subscriptions.consent": "I agree to the recurring price, delivery schedule and terms shown above.",
95
+ "subscriptions.eachDelivery": "For each delivery",
96
+ "subscriptions.once": "One-time purchase",
87
97
  // common
88
98
  "common.free": "Free",
89
99
  "common.close": "Close",
@@ -300,6 +310,16 @@ var en = {
300
310
 
301
311
  // src/i18n/cs.ts
302
312
  var cs = {
313
+ "subscriptions.purchaseOption": "Zp\u016Fsob n\xE1kupu",
314
+ "subscriptions.empty": "Zat\xEDm nem\xE1te \u017E\xE1dn\xE9 p\u0159edplatn\xE9.",
315
+ "subscriptions.status.active": "Aktivn\xED",
316
+ "subscriptions.status.paused": "Pozastaveno",
317
+ "subscriptions.status.pending": "\u010Cek\xE1 na platbu",
318
+ "subscriptions.status.cancelled": "Zru\u0161eno",
319
+ "subscriptions.title": "P\u0159edplatn\xE9",
320
+ "subscriptions.consent": "Souhlas\xEDm s uvedenou opakovanou cenou, pl\xE1nem doru\u010Den\xED a podm\xEDnkami.",
321
+ "subscriptions.eachDelivery": "Za ka\u017Ed\xE9 doru\u010Den\xED",
322
+ "subscriptions.once": "Jednor\xE1zov\xFD n\xE1kup",
303
323
  "common.free": "Zdarma",
304
324
  "common.close": "Zav\u0159\xEDt",
305
325
  "common.loadingShort": "\u2026",
@@ -541,13 +561,13 @@ function makeI18n(locale, messages) {
541
561
  return { locale, messages, t };
542
562
  }
543
563
  var DEFAULT_I18N = makeI18n("en", en);
544
- var I18nContext = React19__namespace.createContext(null);
564
+ var I18nContext = React27__namespace.createContext(null);
545
565
  function I18nProvider({
546
566
  locale,
547
567
  messages,
548
568
  children
549
569
  }) {
550
- const value = React19__namespace.useMemo(() => {
570
+ const value = React27__namespace.useMemo(() => {
551
571
  const lang = langOf(locale);
552
572
  if (locale && !(lang in BUNDLED) && !messages) {
553
573
  devWarn(
@@ -561,7 +581,7 @@ function I18nProvider({
561
581
  return /* @__PURE__ */ jsxRuntime.jsx(I18nContext.Provider, { value, children });
562
582
  }
563
583
  function useI18n() {
564
- return React19__namespace.useContext(I18nContext) ?? DEFAULT_I18N;
584
+ return React27__namespace.useContext(I18nContext) ?? DEFAULT_I18N;
565
585
  }
566
586
  function useT() {
567
587
  return useI18n().t;
@@ -685,7 +705,7 @@ function ProductCard({ product, href, locale }) {
685
705
  var MAIN_WIDTHS = [600, 800, 1e3];
686
706
  var THUMB_WIDTHS = [56, 112];
687
707
  function ProductGallery({ media, alt }) {
688
- const [active, setActive] = React19__namespace.useState(0);
708
+ const [active, setActive] = React27__namespace.useState(0);
689
709
  const imageBase = useStorefrontOptional()?.imageBase;
690
710
  if (media.length === 0) {
691
711
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -773,10 +793,10 @@ function matchVariant(product, selected) {
773
793
  ) ?? null;
774
794
  }
775
795
  function VariantPicker({ product, onChange }) {
776
- const [selected, setSelected] = React19__namespace.useState(
796
+ const [selected, setSelected] = React27__namespace.useState(
777
797
  () => product.variants[0]?.options ?? []
778
798
  );
779
- React19__namespace.useEffect(() => {
799
+ React27__namespace.useEffect(() => {
780
800
  onChange?.(matchVariant(product, selected));
781
801
  }, [product, selected]);
782
802
  if (product.options.length === 0) return null;
@@ -817,8 +837,8 @@ function BackInStockForm({
817
837
  }) {
818
838
  const t = useT();
819
839
  const client = useStorefront();
820
- const [email, setEmail] = React19__namespace.useState(defaultEmail);
821
- const [state, setState] = React19__namespace.useState("idle");
840
+ const [email, setEmail] = React27__namespace.useState(defaultEmail);
841
+ const [state, setState] = React27__namespace.useState("idle");
822
842
  if (!variant || variant.availability?.status !== "out_of_stock") return null;
823
843
  async function submit(e) {
824
844
  e.preventDefault();
@@ -918,7 +938,7 @@ function ProductDetailPage({
918
938
  locale,
919
939
  addToCart
920
940
  }) {
921
- const [variant, setVariant] = React19__namespace.useState(
941
+ const [variant, setVariant] = React27__namespace.useState(
922
942
  product.variants[0] ?? null
923
943
  );
924
944
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1150,12 +1170,26 @@ function TagFilter({ tags, selected = [], onChange }) {
1150
1170
  }) })
1151
1171
  ] });
1152
1172
  }
1153
- var CartContext = React19__namespace.createContext(null);
1173
+ function PurchaseOptions({ plans, value, onChange, disabled, locale }) {
1174
+ const t = useT();
1175
+ const loc = useFormatLocale(locale);
1176
+ const name = React27__namespace.useId();
1177
+ const options = [{ id: null, name: t("subscriptions.once"), price: void 0 }, ...plans];
1178
+ return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { disabled, style: { border: 0, padding: 0, margin: 0, display: "grid", gap: "0.5rem" }, children: [
1179
+ /* @__PURE__ */ jsxRuntime.jsx("legend", { style: { marginBottom: "0.5rem", fontWeight: 500 }, children: t("subscriptions.purchaseOption") }),
1180
+ options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", gap: "0.6rem", alignItems: "center", padding: "0.75rem", border: `1px solid ${value === option.id ? "var(--nv-fg)" : "var(--nv-border)"}`, borderRadius: "var(--nv-radius)", cursor: "pointer" }, children: [
1181
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "radio", name, checked: value === option.id, onChange: () => onChange(option.id) }),
1182
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1 }, children: option.name }),
1183
+ option.price ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatCents(option.price.amount_cents, option.price.currency, loc) }) : null
1184
+ ] }, option.id ?? "once"))
1185
+ ] });
1186
+ }
1187
+ var CartContext = React27__namespace.createContext(null);
1154
1188
  function CartProvider({ children }) {
1155
1189
  const client = useStorefront();
1156
- const [cart, setCart] = React19__namespace.useState(null);
1157
- const [loading, setLoading] = React19__namespace.useState(false);
1158
- const run = React19__namespace.useCallback(
1190
+ const [cart, setCart] = React27__namespace.useState(null);
1191
+ const [loading, setLoading] = React27__namespace.useState(false);
1192
+ const run = React27__namespace.useCallback(
1159
1193
  async (op) => {
1160
1194
  setLoading(true);
1161
1195
  try {
@@ -1168,7 +1202,7 @@ function CartProvider({ children }) {
1168
1202
  },
1169
1203
  []
1170
1204
  );
1171
- const refresh = React19__namespace.useCallback(async () => {
1205
+ const refresh = React27__namespace.useCallback(async () => {
1172
1206
  setLoading(true);
1173
1207
  try {
1174
1208
  setCart(await client.cart.get());
@@ -1176,16 +1210,17 @@ function CartProvider({ children }) {
1176
1210
  setLoading(false);
1177
1211
  }
1178
1212
  }, [client]);
1179
- React19__namespace.useEffect(() => {
1213
+ React27__namespace.useEffect(() => {
1180
1214
  void refresh();
1181
1215
  }, [refresh]);
1182
- const value = React19__namespace.useMemo(
1216
+ const value = React27__namespace.useMemo(
1183
1217
  () => ({
1184
1218
  cart,
1185
1219
  loading,
1186
1220
  itemCount: cart?.totals.item_count ?? 0,
1187
1221
  addLine: (input) => run(() => client.cart.addLine(input)),
1188
1222
  updateLine: (lineId, quantity) => run(() => client.cart.updateLine(lineId, quantity)),
1223
+ clearSubscription: () => run(() => client.cart.clearSubscription()),
1189
1224
  removeLine: (lineId) => run(() => client.cart.removeLine(lineId)),
1190
1225
  refresh
1191
1226
  }),
@@ -1194,7 +1229,7 @@ function CartProvider({ children }) {
1194
1229
  return /* @__PURE__ */ jsxRuntime.jsx(CartContext.Provider, { value, children });
1195
1230
  }
1196
1231
  function useCart() {
1197
- const ctx = React19__namespace.useContext(CartContext);
1232
+ const ctx = React27__namespace.useContext(CartContext);
1198
1233
  if (!ctx) {
1199
1234
  throw new Error("useCart must be used inside <CartProvider>.");
1200
1235
  }
@@ -1203,13 +1238,14 @@ function useCart() {
1203
1238
  function AddToCart({
1204
1239
  variant,
1205
1240
  quantity = 1,
1241
+ subscriptionPlanId,
1206
1242
  children,
1207
1243
  onAdded
1208
1244
  }) {
1209
1245
  const t = useT();
1210
1246
  const { addLine } = useCart();
1211
- const [busy, setBusy] = React19__namespace.useState(false);
1212
- const [error, setError] = React19__namespace.useState(null);
1247
+ const [busy, setBusy] = React27__namespace.useState(false);
1248
+ const [error, setError] = React27__namespace.useState(null);
1213
1249
  const disabled = variant == null || busy;
1214
1250
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
1215
1251
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1222,7 +1258,7 @@ function AddToCart({
1222
1258
  setBusy(true);
1223
1259
  setError(null);
1224
1260
  try {
1225
- await addLine({ variant_id: variant.id, quantity });
1261
+ await addLine({ variant_id: variant.id, quantity, ...subscriptionPlanId ? { subscription_plan_id: subscriptionPlanId } : {} });
1226
1262
  onAdded?.();
1227
1263
  } catch (err) {
1228
1264
  const code = err?.code;
@@ -1251,7 +1287,7 @@ function AddToCart({
1251
1287
  function CartLineItem({ line, locale }) {
1252
1288
  const t = useT();
1253
1289
  const loc = useFormatLocale(locale);
1254
- const { updateLine, removeLine } = useCart();
1290
+ const { cart, updateLine, removeLine } = useCart();
1255
1291
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.85rem", alignItems: "flex-start", padding: "0.75rem 0" }, children: [
1256
1292
  /* @__PURE__ */ jsxRuntime.jsx(
1257
1293
  "div",
@@ -1280,6 +1316,7 @@ function CartLineItem({ line, locale }) {
1280
1316
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
1281
1317
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500, lineHeight: 1.3 }, children: line.title }),
1282
1318
  line.variant_title ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--nv-muted)", fontSize: "0.875em" }, children: line.variant_title }) : null,
1319
+ line.subscription_plan_id ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--nv-muted)", fontSize: "0.875em" }, children: cart?.subscription?.plan?.name ?? t("subscriptions.title") }) : null,
1283
1320
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem", marginTop: "0.5rem" }, children: [
1284
1321
  /* @__PURE__ */ jsxRuntime.jsxs(
1285
1322
  "div",
@@ -1358,7 +1395,7 @@ function CartSummary({
1358
1395
  const { cart } = useCart();
1359
1396
  if (cart == null) return null;
1360
1397
  const t = cart.totals;
1361
- const empty = t.item_count === 0;
1398
+ const empty = t.item_count === 0 || cart.subscription?.available === false;
1362
1399
  const cta = checkoutLabel ?? tr("cart.checkout");
1363
1400
  const ctaStyle = {
1364
1401
  display: "block",
@@ -1379,6 +1416,7 @@ function CartSummary({
1379
1416
  t.tax_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.tax"), value: formatCents(t.tax_cents, t.currency, loc), muted: true }) : null,
1380
1417
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
1381
1418
  /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.total"), value: formatCents(t.total_cents, t.currency, loc), strong: true }),
1419
+ cart.subscription?.error ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", style: { margin: 0 }, children: cart.subscription.error.message }) : null,
1382
1420
  checkoutHref && !empty ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: checkoutHref, style: ctaStyle, children: cta }) : /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", disabled: empty, onClick: onCheckout, style: ctaStyle, children: cta })
1383
1421
  ] });
1384
1422
  }
@@ -1485,15 +1523,15 @@ function CartDrawer({
1485
1523
  }
1486
1524
  );
1487
1525
  }
1488
- var CheckoutContext = React19__namespace.createContext(null);
1526
+ var CheckoutContext = React27__namespace.createContext(null);
1489
1527
  function CheckoutProvider({
1490
1528
  children
1491
1529
  }) {
1492
1530
  const client = useStorefront();
1493
- const [checkout, setCheckout] = React19__namespace.useState(null);
1494
- const [loading, setLoading] = React19__namespace.useState(false);
1495
- const [error, setError] = React19__namespace.useState(null);
1496
- const run = React19__namespace.useCallback(
1531
+ const [checkout, setCheckout] = React27__namespace.useState(null);
1532
+ const [loading, setLoading] = React27__namespace.useState(false);
1533
+ const [error, setError] = React27__namespace.useState(null);
1534
+ const run = React27__namespace.useCallback(
1497
1535
  async (op, store) => {
1498
1536
  setLoading(true);
1499
1537
  try {
@@ -1511,23 +1549,36 @@ function CheckoutProvider({
1511
1549
  },
1512
1550
  []
1513
1551
  );
1514
- const refresh = React19__namespace.useCallback(async () => {
1552
+ const refresh = React27__namespace.useCallback(async () => {
1515
1553
  setLoading(true);
1516
1554
  try {
1517
1555
  setCheckout(await client.checkout.get());
1518
1556
  setError(null);
1557
+ } catch (err) {
1558
+ setError(err instanceof Error ? err : new Error(String(err)));
1519
1559
  } finally {
1520
1560
  setLoading(false);
1521
1561
  }
1522
1562
  }, [client]);
1523
- React19__namespace.useEffect(() => {
1563
+ React27__namespace.useEffect(() => {
1524
1564
  void refresh();
1525
1565
  }, [refresh]);
1526
- const pay = React19__namespace.useCallback(
1566
+ const pay = React27__namespace.useCallback(
1527
1567
  async (opts = {}) => {
1528
1568
  const co = checkout;
1529
1569
  if (!co) throw new Error("pay() called before a checkout exists");
1530
1570
  const idempotencyKey = opts.idempotencyKey ?? co.token;
1571
+ if (co.subscription) {
1572
+ if (co.subscription.subscription_id) {
1573
+ const payment = await run(() => client.checkout.subscriptionPayment(), () => null);
1574
+ setCheckout(await client.checkout.get());
1575
+ return { kind: "subscription_payment", payment };
1576
+ }
1577
+ if (!opts.subscription) throw new Error("Accept the current subscription terms before paying");
1578
+ const subscription = await run(() => client.checkout.enrollSubscription(opts.subscription), () => null);
1579
+ setCheckout(await client.checkout.get());
1580
+ return { kind: "subscription", subscription };
1581
+ }
1531
1582
  if (co.payment_category === "gateway") {
1532
1583
  const start = await run(
1533
1584
  () => client.checkout.startPayment({ return_url: opts.returnUrl, cancel_url: opts.cancelUrl }),
@@ -1546,11 +1597,18 @@ function CheckoutProvider({
1546
1597
  },
1547
1598
  [checkout, client, run]
1548
1599
  );
1549
- const value = React19__namespace.useMemo(
1600
+ const value = React27__namespace.useMemo(
1550
1601
  () => ({
1551
1602
  checkout,
1552
1603
  loading,
1553
1604
  error,
1605
+ selectSubscription: (selection) => run(async () => {
1606
+ const quote = await client.checkout.selectSubscription(selection);
1607
+ setCheckout(await client.checkout.get());
1608
+ return quote;
1609
+ }, () => null),
1610
+ clearSubscription: () => run(() => client.checkout.clearSubscription(), (r) => r),
1611
+ reviewSubscription: () => run(() => client.checkout.reviewSubscription(), () => null),
1554
1612
  create: (cartToken, input) => run(() => client.checkout.create(cartToken, input), (r) => r),
1555
1613
  setAddress: (input) => run(() => client.checkout.setAddress(input), (r) => r),
1556
1614
  shippingQuotes: () => run(() => client.checkout.shippingQuotes(), () => null),
@@ -1571,7 +1629,7 @@ function CheckoutProvider({
1571
1629
  return /* @__PURE__ */ jsxRuntime.jsx(CheckoutContext.Provider, { value, children });
1572
1630
  }
1573
1631
  function useCheckout() {
1574
- const ctx = React19__namespace.useContext(CheckoutContext);
1632
+ const ctx = React27__namespace.useContext(CheckoutContext);
1575
1633
  if (!ctx) {
1576
1634
  throw new Error("useCheckout must be used inside <CheckoutProvider>.");
1577
1635
  }
@@ -1730,11 +1788,11 @@ function AddressForm({
1730
1788
  }) {
1731
1789
  const t = useT();
1732
1790
  const { checkout, setAddress, loading } = useCheckout();
1733
- const [form, setForm] = React19__namespace.useState(
1791
+ const [form, setForm] = React27__namespace.useState(
1734
1792
  () => seed(checkout?.email ?? null, checkout?.shipping_address ?? null)
1735
1793
  );
1736
- const touched = React19__namespace.useRef(/* @__PURE__ */ new Set());
1737
- React19__namespace.useEffect(() => {
1794
+ const touched = React27__namespace.useRef(/* @__PURE__ */ new Set());
1795
+ React27__namespace.useEffect(() => {
1738
1796
  const prefill = checkout?.customer_prefill;
1739
1797
  const addresses = prefill?.addresses.filter((a) => !a.is_pickup);
1740
1798
  const saved = addresses?.find((a) => a.is_default) ?? addresses?.[0];
@@ -1799,11 +1857,11 @@ function ShippingPicker({
1799
1857
  const loc = useFormatLocale(locale);
1800
1858
  const { checkout, shippingQuotes, setShippingMethod, loading } = useCheckout();
1801
1859
  const heading = title ?? t("checkout.shippingMethod");
1802
- const [quotes, setQuotes] = React19__namespace.useState(null);
1803
- const [selected, setSelected] = React19__namespace.useState(null);
1804
- const [fetching, setFetching] = React19__namespace.useState(true);
1860
+ const [quotes, setQuotes] = React27__namespace.useState(null);
1861
+ const [selected, setSelected] = React27__namespace.useState(null);
1862
+ const [fetching, setFetching] = React27__namespace.useState(true);
1805
1863
  const addressKey = checkout?.shipping_address ? `${checkout.shipping_address.country_code}:${checkout.shipping_address.postal_code}` : null;
1806
- React19__namespace.useEffect(() => {
1864
+ React27__namespace.useEffect(() => {
1807
1865
  let live = true;
1808
1866
  setFetching(true);
1809
1867
  shippingQuotes().then((qs) => {
@@ -1878,20 +1936,45 @@ function PaymentMethodPicker({
1878
1936
  }
1879
1937
  );
1880
1938
  }
1939
+ function SubscriptionTerms({ quote, checked, onChange, locale }) {
1940
+ const t = useT();
1941
+ const loc = useFormatLocale(locale);
1942
+ return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { style: { margin: 0, padding: "1rem", border: "1px solid var(--nv-border)", borderRadius: "var(--nv-radius)" }, children: [
1943
+ /* @__PURE__ */ jsxRuntime.jsx("legend", { children: t("subscriptions.title") }),
1944
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { margin: "0 0 0.5rem", fontWeight: 500 }, children: [
1945
+ t("subscriptions.eachDelivery"),
1946
+ ": ",
1947
+ formatCents(quote.recurring_total_cents, quote.currency, loc)
1948
+ ] }),
1949
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "0 0 0.75rem", whiteSpace: "pre-wrap" }, children: quote.terms_text }),
1950
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "flex-start", gap: "0.6rem", cursor: "pointer" }, children: [
1951
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked, onChange: (event) => onChange(event.target.checked) }),
1952
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("subscriptions.consent") })
1953
+ ] })
1954
+ ] });
1955
+ }
1881
1956
  function PaymentSection({
1882
1957
  title,
1883
1958
  returnUrl,
1884
1959
  cancelUrl,
1885
1960
  onOrderPlaced,
1886
- locale
1961
+ locale,
1962
+ onSubscriptionPayment
1887
1963
  }) {
1888
1964
  const t = useT();
1889
1965
  const heading = title ?? t("checkout.payment");
1890
- const { checkout, paymentMethods, setPaymentMethod, pay, loading } = useCheckout();
1891
- const [methods, setMethods] = React19__namespace.useState(null);
1892
- const [fetching, setFetching] = React19__namespace.useState(true);
1966
+ const { checkout, paymentMethods, setPaymentMethod, pay, loading, refresh } = useCheckout();
1967
+ const [methods, setMethods] = React27__namespace.useState(null);
1968
+ const [fetching, setFetching] = React27__namespace.useState(true);
1893
1969
  const selected = checkout?.payment_method ?? null;
1894
- React19__namespace.useEffect(() => {
1970
+ const [acceptedDigest, setAcceptedDigest] = React27__namespace.useState(null);
1971
+ const [actionBusy, setActionBusy] = React27__namespace.useState(false);
1972
+ const quote = checkout?.subscription?.quote;
1973
+ const needsConsent = Boolean(checkout?.subscription?.requires_consent);
1974
+ const clickInFlight = React27__namespace.useRef(false);
1975
+ const [actionError, setActionError] = React27__namespace.useState(null);
1976
+ const accepted = Boolean(quote && acceptedDigest === quote.quote_digest);
1977
+ React27__namespace.useEffect(() => {
1895
1978
  let live = true;
1896
1979
  setFetching(true);
1897
1980
  paymentMethods().then((ms) => {
@@ -1904,12 +1987,30 @@ function PaymentSection({
1904
1987
  return () => {
1905
1988
  live = false;
1906
1989
  };
1907
- }, []);
1990
+ }, [checkout?.token, checkout?.shipping_method_id, checkout?.subscription?.selection.plan_id]);
1908
1991
  const selectedMethod = methods?.find((m) => m.method === selected) ?? null;
1909
1992
  const isGateway = selectedMethod?.category === "gateway";
1910
1993
  async function placeOrder() {
1911
- const result = await pay({ returnUrl, cancelUrl });
1912
- if (result.kind === "order") onOrderPlaced?.(result.checkout);
1994
+ if (clickInFlight.current) return;
1995
+ clickInFlight.current = true;
1996
+ setActionBusy(true);
1997
+ setActionError(null);
1998
+ try {
1999
+ const result = await pay({
2000
+ returnUrl,
2001
+ cancelUrl,
2002
+ ...needsConsent && quote && accepted ? { subscription: { quote, consent: true } } : {}
2003
+ });
2004
+ if (result.kind === "subscription") await onSubscriptionPayment?.(result.subscription.payment);
2005
+ if (result.kind === "subscription_payment") await onSubscriptionPayment?.(result.payment);
2006
+ if (result.kind === "subscription" || result.kind === "subscription_payment") await refresh();
2007
+ if (result.kind === "order") onOrderPlaced?.(result.checkout);
2008
+ } catch (err) {
2009
+ setActionError(err instanceof Error ? err.message : String(err));
2010
+ } finally {
2011
+ clickInFlight.current = false;
2012
+ setActionBusy(false);
2013
+ }
1913
2014
  }
1914
2015
  if (fetching && methods == null) {
1915
2016
  return /* @__PURE__ */ jsxRuntime.jsx(Section, { title: heading, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.loadingPayment") }) });
@@ -1929,9 +2030,12 @@ function PaymentSection({
1929
2030
  label: heading
1930
2031
  }
1931
2032
  ),
2033
+ quote && needsConsent ? /* @__PURE__ */ jsxRuntime.jsx(SubscriptionTerms, { locale, quote, checked: accepted, onChange: (value) => setAcceptedDigest(value && quote ? quote.quote_digest : null) }) : null,
2034
+ checkout?.subscription?.error ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", children: checkout.subscription.error.message }) : null,
2035
+ actionError ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", children: actionError }) : null,
1932
2036
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1933
- /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: placeOrder, busy: loading, disabled: selected == null, children: isGateway ? t("checkout.payNow") : t("checkout.placeOrder") }),
1934
- isGateway ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
2037
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: placeOrder, busy: loading || actionBusy, disabled: selected == null || Boolean(checkout?.subscription) && (needsConsent && (!accepted || !quote) || !onSubscriptionPayment || !checkout?.subscription?.available), children: isGateway ? t("checkout.payNow") : t("checkout.placeOrder") }),
2038
+ isGateway && !checkout?.subscription ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
1935
2039
  ] })
1936
2040
  ] });
1937
2041
  }
@@ -2019,6 +2123,7 @@ function reachedStep(checkout) {
2019
2123
  }
2020
2124
  function CheckoutFlow({
2021
2125
  cartToken,
2126
+ onSubscriptionPayment,
2022
2127
  onOrderPlaced,
2023
2128
  emptyState,
2024
2129
  renderConfirmation,
@@ -2028,19 +2133,19 @@ function CheckoutFlow({
2028
2133
  }) {
2029
2134
  const t = useT();
2030
2135
  const { checkout, loading, create } = useCheckout();
2031
- const [step, setStep] = React19__namespace.useState("address");
2032
- const [bootstrapped, setBootstrapped] = React19__namespace.useState(false);
2033
- React19__namespace.useEffect(() => {
2136
+ const [step, setStep] = React27__namespace.useState("address");
2137
+ const [bootstrapped, setBootstrapped] = React27__namespace.useState(false);
2138
+ React27__namespace.useEffect(() => {
2034
2139
  if (checkout || !cartToken || bootstrapped) return;
2035
2140
  setBootstrapped(true);
2036
2141
  void create(cartToken).catch(() => {
2037
2142
  });
2038
2143
  }, [checkout, cartToken, bootstrapped, create]);
2039
2144
  const serverStep = reachedStep(checkout);
2040
- React19__namespace.useEffect(() => {
2145
+ React27__namespace.useEffect(() => {
2041
2146
  setStep((cur) => rank(serverStep) > rank(cur) ? serverStep : cur);
2042
2147
  }, [serverStep]);
2043
- if (checkout?.order_id) {
2148
+ if (checkout?.order_id && (!checkout.subscription || checkout.phase === "success")) {
2044
2149
  return /* @__PURE__ */ jsxRuntime.jsx(Shell, { summary: /* @__PURE__ */ jsxRuntime.jsx(OrderSummary, { locale }), children: renderConfirmation ? renderConfirmation(checkout) : /* @__PURE__ */ jsxRuntime.jsx(DefaultConfirmation, { checkout }) });
2045
2150
  }
2046
2151
  if (!checkout) {
@@ -2054,6 +2159,7 @@ function CheckoutFlow({
2054
2159
  step === "address" ? /* @__PURE__ */ jsxRuntime.jsx(AddressForm, { onComplete: () => setStep("shipping") }) : step === "shipping" ? /* @__PURE__ */ jsxRuntime.jsx(ShippingPicker, { onComplete: () => setStep("payment"), locale }) : /* @__PURE__ */ jsxRuntime.jsx(
2055
2160
  PaymentSection,
2056
2161
  {
2162
+ onSubscriptionPayment,
2057
2163
  returnUrl,
2058
2164
  cancelUrl,
2059
2165
  onOrderPlaced,
@@ -2158,10 +2264,10 @@ function DefaultConfirmation({ checkout }) {
2158
2264
  }
2159
2265
  function useOrderStatus(statusToken, initial) {
2160
2266
  const client = useStorefront();
2161
- const [order, setOrder] = React19__namespace.useState(initial ?? null);
2162
- const [loading, setLoading] = React19__namespace.useState(false);
2163
- const [error, setError] = React19__namespace.useState(null);
2164
- const load = React19__namespace.useCallback(async () => {
2267
+ const [order, setOrder] = React27__namespace.useState(initial ?? null);
2268
+ const [loading, setLoading] = React27__namespace.useState(false);
2269
+ const [error, setError] = React27__namespace.useState(null);
2270
+ const load = React27__namespace.useCallback(async () => {
2165
2271
  if (!statusToken) return;
2166
2272
  setLoading(true);
2167
2273
  try {
@@ -2173,7 +2279,7 @@ function useOrderStatus(statusToken, initial) {
2173
2279
  setLoading(false);
2174
2280
  }
2175
2281
  }, [client, statusToken]);
2176
- React19__namespace.useEffect(() => {
2282
+ React27__namespace.useEffect(() => {
2177
2283
  if (initial) return;
2178
2284
  void load();
2179
2285
  }, [load, initial]);
@@ -2181,12 +2287,12 @@ function useOrderStatus(statusToken, initial) {
2181
2287
  }
2182
2288
  function usePayment(token, initial) {
2183
2289
  const client = useStorefront();
2184
- const [payment, setPayment] = React19__namespace.useState(initial ?? null);
2185
- const [methods, setMethods] = React19__namespace.useState([]);
2186
- const [loading, setLoading] = React19__namespace.useState(false);
2187
- const [busy, setBusy] = React19__namespace.useState(false);
2188
- const [error, setError] = React19__namespace.useState(null);
2189
- const load = React19__namespace.useCallback(async () => {
2290
+ const [payment, setPayment] = React27__namespace.useState(initial ?? null);
2291
+ const [methods, setMethods] = React27__namespace.useState([]);
2292
+ const [loading, setLoading] = React27__namespace.useState(false);
2293
+ const [busy, setBusy] = React27__namespace.useState(false);
2294
+ const [error, setError] = React27__namespace.useState(null);
2295
+ const load = React27__namespace.useCallback(async () => {
2190
2296
  if (!token) return;
2191
2297
  setLoading(true);
2192
2298
  try {
@@ -2203,7 +2309,7 @@ function usePayment(token, initial) {
2203
2309
  setLoading(false);
2204
2310
  }
2205
2311
  }, [client, token]);
2206
- React19__namespace.useEffect(() => {
2312
+ React27__namespace.useEffect(() => {
2207
2313
  if (!token) return;
2208
2314
  if (initial && initial.token === token) {
2209
2315
  setPayment(initial);
@@ -2212,7 +2318,7 @@ function usePayment(token, initial) {
2212
2318
  }
2213
2319
  void load();
2214
2320
  }, [token, load]);
2215
- const selectMethod = React19__namespace.useCallback(
2321
+ const selectMethod = React27__namespace.useCallback(
2216
2322
  async (method) => {
2217
2323
  if (!token) return null;
2218
2324
  setBusy(true);
@@ -2230,7 +2336,7 @@ function usePayment(token, initial) {
2230
2336
  },
2231
2337
  [client, token]
2232
2338
  );
2233
- const startPayment = React19__namespace.useCallback(
2339
+ const startPayment = React27__namespace.useCallback(
2234
2340
  async (input) => {
2235
2341
  if (!token) return null;
2236
2342
  setBusy(true);
@@ -2667,8 +2773,8 @@ function Row4({
2667
2773
  }
2668
2774
  function CopyRow({ label, value }) {
2669
2775
  const t = useT();
2670
- const [copied, setCopied] = React19__namespace.useState(false);
2671
- React19__namespace.useEffect(() => {
2776
+ const [copied, setCopied] = React27__namespace.useState(false);
2777
+ React27__namespace.useEffect(() => {
2672
2778
  if (!copied) return;
2673
2779
  const id = setTimeout(() => setCopied(false), 1800);
2674
2780
  return () => clearTimeout(id);
@@ -2752,12 +2858,12 @@ function PaymentPage({
2752
2858
  const t = useT();
2753
2859
  const loc = useFormatLocale(locale);
2754
2860
  const { payment, methods, loading, busy, error, reload, selectMethod, startPayment } = usePayment(token, initial);
2755
- const [verifying, setVerifying] = React19__namespace.useState(false);
2756
- React19__namespace.useEffect(() => {
2861
+ const [verifying, setVerifying] = React27__namespace.useState(false);
2862
+ React27__namespace.useEffect(() => {
2757
2863
  if (token) setVerifying(consumeHandoff(token));
2758
2864
  }, [token]);
2759
2865
  const settled = payment != null && !payment.can_pay && payment.blocked_reason === "already_paid";
2760
- React19__namespace.useEffect(() => {
2866
+ React27__namespace.useEffect(() => {
2761
2867
  if (!verifying || !token || settled) return;
2762
2868
  let attempts = 0;
2763
2869
  const id = setInterval(() => {
@@ -2772,7 +2878,7 @@ function PaymentPage({
2772
2878
  }, POLL_MS);
2773
2879
  return () => clearInterval(id);
2774
2880
  }, [verifying, token, settled, reload]);
2775
- React19__namespace.useEffect(() => {
2881
+ React27__namespace.useEffect(() => {
2776
2882
  if (!settled || !token || !payment) return;
2777
2883
  setVerifying(false);
2778
2884
  forgetHandoff(token);
@@ -2853,13 +2959,13 @@ function Amount({ label, value }) {
2853
2959
  function Link({ href, children }) {
2854
2960
  return /* @__PURE__ */ jsxRuntime.jsx("a", { href, style: { color: "var(--nv-brand)", fontSize: "0.875rem" }, children });
2855
2961
  }
2856
- var AccountContext = React19__namespace.createContext(null);
2962
+ var AccountContext = React27__namespace.createContext(null);
2857
2963
  function AccountProvider({ children }) {
2858
2964
  const client = useStorefront();
2859
- const [customer, setCustomer] = React19__namespace.useState(null);
2860
- const [loading, setLoading] = React19__namespace.useState(true);
2861
- const [error, setError] = React19__namespace.useState(false);
2862
- const refresh = React19__namespace.useCallback(async () => {
2965
+ const [customer, setCustomer] = React27__namespace.useState(null);
2966
+ const [loading, setLoading] = React27__namespace.useState(true);
2967
+ const [error, setError] = React27__namespace.useState(false);
2968
+ const refresh = React27__namespace.useCallback(async () => {
2863
2969
  setLoading(true);
2864
2970
  setError(false);
2865
2971
  try {
@@ -2870,10 +2976,10 @@ function AccountProvider({ children }) {
2870
2976
  setLoading(false);
2871
2977
  }
2872
2978
  }, [client]);
2873
- React19__namespace.useEffect(() => {
2979
+ React27__namespace.useEffect(() => {
2874
2980
  void refresh();
2875
2981
  }, [refresh]);
2876
- const value = React19__namespace.useMemo(
2982
+ const value = React27__namespace.useMemo(
2877
2983
  () => ({
2878
2984
  customer,
2879
2985
  signedIn: customer != null,
@@ -2896,7 +3002,7 @@ function AccountProvider({ children }) {
2896
3002
  return /* @__PURE__ */ jsxRuntime.jsx(AccountContext.Provider, { value, children });
2897
3003
  }
2898
3004
  function useAccount() {
2899
- const ctx = React19__namespace.useContext(AccountContext);
3005
+ const ctx = React27__namespace.useContext(AccountContext);
2900
3006
  if (!ctx) {
2901
3007
  throw new Error("useAccount must be used inside <AccountProvider>.");
2902
3008
  }
@@ -2904,9 +3010,9 @@ function useAccount() {
2904
3010
  }
2905
3011
  function useMagicLinkVerify(token) {
2906
3012
  const { verify } = useAccount();
2907
- const [status, setStatus] = React19__namespace.useState("idle");
2908
- const done = React19__namespace.useRef(false);
2909
- React19__namespace.useEffect(() => {
3013
+ const [status, setStatus] = React27__namespace.useState("idle");
3014
+ const done = React27__namespace.useRef(false);
3015
+ React27__namespace.useEffect(() => {
2910
3016
  if (!token || done.current) return;
2911
3017
  done.current = true;
2912
3018
  setStatus("verifying");
@@ -2917,11 +3023,11 @@ function useMagicLinkVerify(token) {
2917
3023
  function LoginForm({ verifyPath = "/account/verify", onSent }) {
2918
3024
  const t = useT();
2919
3025
  const { requestMagicLink } = useAccount();
2920
- const [email, setEmail] = React19__namespace.useState("");
2921
- const [busy, setBusy] = React19__namespace.useState(false);
2922
- const [sent, setSent] = React19__namespace.useState(false);
2923
- const [error, setError] = React19__namespace.useState(false);
2924
- const [devLink, setDevLink] = React19__namespace.useState(null);
3026
+ const [email, setEmail] = React27__namespace.useState("");
3027
+ const [busy, setBusy] = React27__namespace.useState(false);
3028
+ const [sent, setSent] = React27__namespace.useState(false);
3029
+ const [error, setError] = React27__namespace.useState(false);
3030
+ const [devLink, setDevLink] = React27__namespace.useState(null);
2925
3031
  async function submit(e) {
2926
3032
  e.preventDefault();
2927
3033
  setBusy(true);
@@ -3033,14 +3139,14 @@ function OrderHistory({
3033
3139
  const loc = useFormatLocale(locale);
3034
3140
  const { signedIn } = useAccount();
3035
3141
  const client = useStorefront();
3036
- const [orders, setOrders] = React19__namespace.useState(null);
3037
- const [error, setError] = React19__namespace.useState(false);
3038
- const [nextCursor, setNextCursor] = React19__namespace.useState(null);
3039
- const [pageLoading, setPageLoading] = React19__namespace.useState(false);
3040
- const [reload, setReload] = React19__namespace.useState(0);
3041
- const [reordering, setReordering] = React19__namespace.useState(null);
3042
- const [notice, setNotice] = React19__namespace.useState(null);
3043
- React19__namespace.useEffect(() => {
3142
+ const [orders, setOrders] = React27__namespace.useState(null);
3143
+ const [error, setError] = React27__namespace.useState(false);
3144
+ const [nextCursor, setNextCursor] = React27__namespace.useState(null);
3145
+ const [pageLoading, setPageLoading] = React27__namespace.useState(false);
3146
+ const [reload, setReload] = React27__namespace.useState(0);
3147
+ const [reordering, setReordering] = React27__namespace.useState(null);
3148
+ const [notice, setNotice] = React27__namespace.useState(null);
3149
+ React27__namespace.useEffect(() => {
3044
3150
  let live = true;
3045
3151
  if (!signedIn) {
3046
3152
  setOrders([]);
@@ -3151,7 +3257,7 @@ function OrderHistory({
3151
3257
  }
3152
3258
  function AddressForm2({ initial, onSubmit, onCancel, busy }) {
3153
3259
  const t = useT();
3154
- const [form, setForm] = React19__namespace.useState(() => ({
3260
+ const [form, setForm] = React27__namespace.useState(() => ({
3155
3261
  first_name: initial?.first_name ?? "",
3156
3262
  last_name: initial?.last_name ?? "",
3157
3263
  company: initial?.company ?? "",
@@ -3249,17 +3355,17 @@ function AddressBook() {
3249
3355
  const t = useT();
3250
3356
  const { signedIn } = useAccount();
3251
3357
  const client = useStorefront();
3252
- const [addresses, setAddresses] = React19__namespace.useState(null);
3253
- const [mode, setMode] = React19__namespace.useState({ kind: "list" });
3254
- const [busy, setBusy] = React19__namespace.useState(false);
3255
- const load = React19__namespace.useCallback(() => {
3358
+ const [addresses, setAddresses] = React27__namespace.useState(null);
3359
+ const [mode, setMode] = React27__namespace.useState({ kind: "list" });
3360
+ const [busy, setBusy] = React27__namespace.useState(false);
3361
+ const load = React27__namespace.useCallback(() => {
3256
3362
  if (!signedIn) {
3257
3363
  setAddresses([]);
3258
3364
  return;
3259
3365
  }
3260
3366
  client.account.addresses().then(setAddresses).catch(() => setAddresses([]));
3261
3367
  }, [signedIn, client]);
3262
- React19__namespace.useEffect(() => {
3368
+ React27__namespace.useEffect(() => {
3263
3369
  load();
3264
3370
  }, [load]);
3265
3371
  async function run(fn) {
@@ -3313,10 +3419,10 @@ function ProfileEditor() {
3313
3419
  const t = useT();
3314
3420
  const client = useStorefront();
3315
3421
  const { customer, refresh } = useAccount();
3316
- const [editing, setEditing] = React19__namespace.useState(false);
3317
- const [busy, setBusy] = React19__namespace.useState(false);
3318
- const [error, setError] = React19__namespace.useState(false);
3319
- const [form, setForm] = React19__namespace.useState({});
3422
+ const [editing, setEditing] = React27__namespace.useState(false);
3423
+ const [busy, setBusy] = React27__namespace.useState(false);
3424
+ const [error, setError] = React27__namespace.useState(false);
3425
+ const [form, setForm] = React27__namespace.useState({});
3320
3426
  if (!customer) return null;
3321
3427
  function startEdit() {
3322
3428
  setForm({
@@ -3397,13 +3503,13 @@ function AccountSecurity() {
3397
3503
  const client = useStorefront();
3398
3504
  const account = useAccount();
3399
3505
  const t = useT();
3400
- const [sessions, setSessions] = React19__namespace.useState(null);
3401
- const [email, setEmail] = React19__namespace.useState("");
3402
- const [busy, setBusy] = React19__namespace.useState(false);
3403
- const [notice, setNotice] = React19__namespace.useState(null);
3404
- const [confirmClose, setConfirmClose] = React19__namespace.useState(false);
3405
- const [retry, setRetry] = React19__namespace.useState(0);
3406
- React19__namespace.useEffect(() => {
3506
+ const [sessions, setSessions] = React27__namespace.useState(null);
3507
+ const [email, setEmail] = React27__namespace.useState("");
3508
+ const [busy, setBusy] = React27__namespace.useState(false);
3509
+ const [notice, setNotice] = React27__namespace.useState(null);
3510
+ const [confirmClose, setConfirmClose] = React27__namespace.useState(false);
3511
+ const [retry, setRetry] = React27__namespace.useState(0);
3512
+ React27__namespace.useEffect(() => {
3407
3513
  let live = true;
3408
3514
  if (account.signedIn) client.account.sessions().then((s) => {
3409
3515
  if (live) setSessions(s);
@@ -3466,6 +3572,103 @@ function AccountSecurity() {
3466
3572
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(Button, { disabled: busy, onClick: () => setConfirmClose(true), children: t("account.closeAccount") })
3467
3573
  ] });
3468
3574
  }
3575
+ function useCustomerSubscriptions() {
3576
+ const client = useStorefront();
3577
+ const { customer } = useAccount();
3578
+ const [dataOwner, setDataOwner] = React27__namespace.useState(void 0);
3579
+ const [subscriptions, setSubscriptions] = React27__namespace.useState([]);
3580
+ const [loading, setLoading] = React27__namespace.useState(false);
3581
+ const [error, setError] = React27__namespace.useState(null);
3582
+ const generation = React27__namespace.useRef(0);
3583
+ const owner = customer?.id;
3584
+ const api = client.account.subscriptions;
3585
+ const refresh = React27__namespace.useCallback(async () => {
3586
+ const current = ++generation.current;
3587
+ setError(null);
3588
+ if (!owner) {
3589
+ setSubscriptions([]);
3590
+ setLoading(false);
3591
+ return;
3592
+ }
3593
+ setLoading(true);
3594
+ try {
3595
+ const rows = await api.list();
3596
+ if (current === generation.current) {
3597
+ setSubscriptions(rows);
3598
+ setDataOwner(owner);
3599
+ setError(null);
3600
+ }
3601
+ } catch (err) {
3602
+ if (current === generation.current) setError(err instanceof Error ? err : new Error(String(err)));
3603
+ } finally {
3604
+ if (current === generation.current) setLoading(false);
3605
+ }
3606
+ }, [owner, api]);
3607
+ React27__namespace.useEffect(() => {
3608
+ setSubscriptions([]);
3609
+ void refresh();
3610
+ return () => {
3611
+ generation.current++;
3612
+ };
3613
+ }, [refresh]);
3614
+ return { subscriptions: dataOwner === owner ? subscriptions : [], loading, error, refresh, api };
3615
+ }
3616
+ function useCustomerSubscription(id) {
3617
+ const client = useStorefront();
3618
+ const { customer } = useAccount();
3619
+ const [dataKey, setDataKey] = React27__namespace.useState("");
3620
+ const [subscription, setSubscription] = React27__namespace.useState(null);
3621
+ const [loading, setLoading] = React27__namespace.useState(false);
3622
+ const [error, setError] = React27__namespace.useState(null);
3623
+ const generation = React27__namespace.useRef(0);
3624
+ const owner = customer?.id;
3625
+ const api = client.account.subscriptions;
3626
+ const refresh = React27__namespace.useCallback(async () => {
3627
+ const current = ++generation.current;
3628
+ setError(null);
3629
+ if (!owner || !id) {
3630
+ setSubscription(null);
3631
+ setLoading(false);
3632
+ return;
3633
+ }
3634
+ setLoading(true);
3635
+ try {
3636
+ const row = await api.management(id);
3637
+ if (current === generation.current) {
3638
+ setSubscription(row);
3639
+ setDataKey(`${owner}:${id}`);
3640
+ setError(null);
3641
+ }
3642
+ } catch (err) {
3643
+ if (current === generation.current) setError(err instanceof Error ? err : new Error(String(err)));
3644
+ } finally {
3645
+ if (current === generation.current) setLoading(false);
3646
+ }
3647
+ }, [owner, id, api]);
3648
+ React27__namespace.useEffect(() => {
3649
+ setSubscription(null);
3650
+ void refresh();
3651
+ return () => {
3652
+ generation.current++;
3653
+ };
3654
+ }, [refresh]);
3655
+ return { subscription: dataKey === `${owner}:${id}` ? subscription : null, loading, error, refresh, api };
3656
+ }
3657
+ function SubscriptionList({ href, locale }) {
3658
+ const t = useT();
3659
+ const { subscriptions, loading, error } = useCustomerSubscriptions();
3660
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { fontFamily: "var(--nv-font)", color: "var(--nv-fg)" }, children: [
3661
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.125rem", fontWeight: 500 }, children: t("subscriptions.title") }),
3662
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: t("checkout.loading") }) : error ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", children: error.message }) : subscriptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: t("subscriptions.empty") }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: subscriptions.map((sub) => /* @__PURE__ */ jsxRuntime.jsx("li", { style: { borderBottom: "1px solid var(--nv-border)", padding: "0.8rem 0" }, children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: href(sub.id), style: { color: "inherit", display: "flex", justifyContent: "space-between", gap: "1rem", flexWrap: "wrap" }, children: [
3663
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3664
+ "#",
3665
+ sub.id
3666
+ ] }),
3667
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t(`subscriptions.status.${sub.status}`) }),
3668
+ sub.status === "active" && sub.next_at ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: sub.next_at, children: new Date(sub.next_at).toLocaleDateString(locale) }) : null
3669
+ ] }) }, sub.id)) })
3670
+ ] });
3671
+ }
3469
3672
  function serializeJsonLd(data) {
3470
3673
  return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
3471
3674
  }
@@ -3609,10 +3812,13 @@ exports.ProductDetailPage = ProductDetailPage;
3609
3812
  exports.ProductGallery = ProductGallery;
3610
3813
  exports.ProductJsonLd = ProductJsonLd;
3611
3814
  exports.ProfileEditor = ProfileEditor;
3815
+ exports.PurchaseOptions = PurchaseOptions;
3612
3816
  exports.ShippingPicker = ShippingPicker;
3613
3817
  exports.SortSelect = SortSelect;
3614
3818
  exports.StatusBanner = StatusBanner;
3615
3819
  exports.StockBadge = StockBadge;
3820
+ exports.SubscriptionList = SubscriptionList;
3821
+ exports.SubscriptionTerms = SubscriptionTerms;
3616
3822
  exports.TagFilter = TagFilter;
3617
3823
  exports.ThemeProvider = ThemeProvider;
3618
3824
  exports.TransferInstructions = TransferInstructions;
@@ -3630,6 +3836,8 @@ exports.themeToCssVars = themeToCssVars;
3630
3836
  exports.useAccount = useAccount;
3631
3837
  exports.useCart = useCart;
3632
3838
  exports.useCheckout = useCheckout;
3839
+ exports.useCustomerSubscription = useCustomerSubscription;
3840
+ exports.useCustomerSubscriptions = useCustomerSubscriptions;
3633
3841
  exports.useI18n = useI18n;
3634
3842
  exports.useMagicLinkVerify = useMagicLinkVerify;
3635
3843
  exports.useOrderStatus = useOrderStatus;