@nevios/storefront-kit 1.2.0 → 1.3.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.js CHANGED
@@ -1,14 +1,14 @@
1
- import * as React10 from 'react';
1
+ import * as React19 from 'react';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { imageSrcSet } from '@nevios/storefront-js';
3
+ import { imageSrcSet, StorefrontError } from '@nevios/storefront-js';
4
4
 
5
5
  // src/provider.tsx
6
- var StorefrontContext = React10.createContext(null);
6
+ var StorefrontContext = React19.createContext(null);
7
7
  function NeviosProvider({ client, children }) {
8
8
  return /* @__PURE__ */ jsx(StorefrontContext.Provider, { value: client, children });
9
9
  }
10
10
  function useStorefront() {
11
- const client = React10.useContext(StorefrontContext);
11
+ const client = React19.useContext(StorefrontContext);
12
12
  if (!client) {
13
13
  throw new Error(
14
14
  "useStorefront must be used inside <NeviosProvider>. Wrap your app root with it."
@@ -17,7 +17,7 @@ function useStorefront() {
17
17
  return client;
18
18
  }
19
19
  function useStorefrontOptional() {
20
- return React10.useContext(StorefrontContext);
20
+ return React19.useContext(StorefrontContext);
21
21
  }
22
22
  var DEFAULTS = {
23
23
  brand: "#111111",
@@ -53,7 +53,7 @@ function ThemeProvider({
53
53
  as: Tag = "div"
54
54
  }) {
55
55
  const style = themeToCssVars(theme);
56
- return React10.createElement(
56
+ return React19.createElement(
57
57
  Tag,
58
58
  { "data-nevios-theme": "", style },
59
59
  children
@@ -226,6 +226,20 @@ var en = {
226
226
  "account.signOut": "Sign out",
227
227
  "account.greeting": "Signed in as {email}",
228
228
  "account.orders": "Your orders",
229
+ "account.loadError": "Could not load your account. Please try again.",
230
+ "account.retry": "Try again",
231
+ "account.loadMore": "Load more",
232
+ "account.signInError": "Could not request a sign-in link. Please try again shortly.",
233
+ "account.security": "Account security",
234
+ "account.newEmail": "New email",
235
+ "account.changeEmail": "Verify new email",
236
+ "account.recentLogin": "Sign in again by email before making this change.",
237
+ "account.sessions": "Sessions",
238
+ "account.currentSession": "Current session",
239
+ "account.revokeSession": "Sign out",
240
+ "account.closeAccount": "Close account",
241
+ "account.closeWarning": "Closing your account signs out all devices and disables account access. Orders and invoices are retained. Contact the shop to restore access.",
242
+ "account.confirmClose": "Confirm account closure",
229
243
  "account.noOrders": "No orders yet.",
230
244
  "account.addresses": "Saved addresses",
231
245
  "account.noAddresses": "No saved addresses yet.",
@@ -407,6 +421,20 @@ var cs = {
407
421
  "account.signOut": "Odhl\xE1sit se",
408
422
  "account.greeting": "P\u0159ihl\xE1\u0161en jako {email}",
409
423
  "account.orders": "Va\u0161e objedn\xE1vky",
424
+ "account.loadError": "\xDAdaje se nepoda\u0159ilo na\u010D\xEDst. Zkuste to znovu.",
425
+ "account.retry": "Zkusit znovu",
426
+ "account.loadMore": "Na\u010D\xEDst dal\u0161\xED",
427
+ "account.signInError": "P\u0159ihl\xE1\u0161en\xED se nepoda\u0159ilo zah\xE1jit. Zkuste to za chv\xEDli znovu.",
428
+ "account.security": "Zabezpe\u010Den\xED \xFA\u010Dtu",
429
+ "account.newEmail": "Nov\xFD e-mail",
430
+ "account.changeEmail": "Ov\u011B\u0159it nov\xFD e-mail",
431
+ "account.recentLogin": "Pro tuto zm\u011Bnu se nejprve znovu p\u0159ihlaste p\u0159es e-mail.",
432
+ "account.sessions": "P\u0159ihl\xE1\u0161en\xED",
433
+ "account.currentSession": "Toto p\u0159ihl\xE1\u0161en\xED",
434
+ "account.revokeSession": "Odhl\xE1sit",
435
+ "account.closeAccount": "Uzav\u0159\xEDt \xFA\u010Det",
436
+ "account.closeWarning": "Uzav\u0159en\xEDm \xFA\u010Dtu ukon\u010D\xEDte p\u0159\xEDstup a odhl\xE1s\xEDte v\u0161echna za\u0159\xEDzen\xED. Objedn\xE1vky a doklady z\u016Fstanou zachov\xE1ny. Obnoven\xED p\u0159\xEDstupu \u0159e\u0161\xED podpora obchodu.",
437
+ "account.confirmClose": "Potvrdit uzav\u0159en\xED \xFA\u010Dtu",
410
438
  "account.noOrders": "Zat\xEDm \u017E\xE1dn\xE9 objedn\xE1vky.",
411
439
  "account.addresses": "Ulo\u017Een\xE9 adresy",
412
440
  "account.noAddresses": "Zat\xEDm \u017E\xE1dn\xE9 ulo\u017Een\xE9 adresy.",
@@ -491,13 +519,13 @@ function makeI18n(locale, messages) {
491
519
  return { locale, messages, t };
492
520
  }
493
521
  var DEFAULT_I18N = makeI18n("en", en);
494
- var I18nContext = React10.createContext(null);
522
+ var I18nContext = React19.createContext(null);
495
523
  function I18nProvider({
496
524
  locale,
497
525
  messages,
498
526
  children
499
527
  }) {
500
- const value = React10.useMemo(() => {
528
+ const value = React19.useMemo(() => {
501
529
  const lang = langOf(locale);
502
530
  if (locale && !(lang in BUNDLED) && !messages) {
503
531
  devWarn(
@@ -511,7 +539,7 @@ function I18nProvider({
511
539
  return /* @__PURE__ */ jsx(I18nContext.Provider, { value, children });
512
540
  }
513
541
  function useI18n() {
514
- return React10.useContext(I18nContext) ?? DEFAULT_I18N;
542
+ return React19.useContext(I18nContext) ?? DEFAULT_I18N;
515
543
  }
516
544
  function useT() {
517
545
  return useI18n().t;
@@ -635,7 +663,7 @@ function ProductCard({ product, href, locale }) {
635
663
  var MAIN_WIDTHS = [600, 800, 1e3];
636
664
  var THUMB_WIDTHS = [56, 112];
637
665
  function ProductGallery({ media, alt }) {
638
- const [active, setActive] = React10.useState(0);
666
+ const [active, setActive] = React19.useState(0);
639
667
  const imageBase = useStorefrontOptional()?.imageBase;
640
668
  if (media.length === 0) {
641
669
  return /* @__PURE__ */ jsx(
@@ -723,10 +751,10 @@ function matchVariant(product, selected) {
723
751
  ) ?? null;
724
752
  }
725
753
  function VariantPicker({ product, onChange }) {
726
- const [selected, setSelected] = React10.useState(
754
+ const [selected, setSelected] = React19.useState(
727
755
  () => product.variants[0]?.options ?? []
728
756
  );
729
- React10.useEffect(() => {
757
+ React19.useEffect(() => {
730
758
  onChange?.(matchVariant(product, selected));
731
759
  }, [product, selected]);
732
760
  if (product.options.length === 0) return null;
@@ -767,8 +795,8 @@ function BackInStockForm({
767
795
  }) {
768
796
  const t = useT();
769
797
  const client = useStorefront();
770
- const [email, setEmail] = React10.useState(defaultEmail);
771
- const [state, setState] = React10.useState("idle");
798
+ const [email, setEmail] = React19.useState(defaultEmail);
799
+ const [state, setState] = React19.useState("idle");
772
800
  if (!variant || variant.availability?.status !== "out_of_stock") return null;
773
801
  async function submit(e) {
774
802
  e.preventDefault();
@@ -868,7 +896,7 @@ function ProductDetailPage({
868
896
  locale,
869
897
  addToCart
870
898
  }) {
871
- const [variant, setVariant] = React10.useState(
899
+ const [variant, setVariant] = React19.useState(
872
900
  product.variants[0] ?? null
873
901
  );
874
902
  return /* @__PURE__ */ jsxs(
@@ -1100,12 +1128,12 @@ function TagFilter({ tags, selected = [], onChange }) {
1100
1128
  }) })
1101
1129
  ] });
1102
1130
  }
1103
- var CartContext = React10.createContext(null);
1131
+ var CartContext = React19.createContext(null);
1104
1132
  function CartProvider({ children }) {
1105
1133
  const client = useStorefront();
1106
- const [cart, setCart] = React10.useState(null);
1107
- const [loading, setLoading] = React10.useState(false);
1108
- const run = React10.useCallback(
1134
+ const [cart, setCart] = React19.useState(null);
1135
+ const [loading, setLoading] = React19.useState(false);
1136
+ const run = React19.useCallback(
1109
1137
  async (op) => {
1110
1138
  setLoading(true);
1111
1139
  try {
@@ -1118,7 +1146,7 @@ function CartProvider({ children }) {
1118
1146
  },
1119
1147
  []
1120
1148
  );
1121
- const refresh = React10.useCallback(async () => {
1149
+ const refresh = React19.useCallback(async () => {
1122
1150
  setLoading(true);
1123
1151
  try {
1124
1152
  setCart(await client.cart.get());
@@ -1126,10 +1154,10 @@ function CartProvider({ children }) {
1126
1154
  setLoading(false);
1127
1155
  }
1128
1156
  }, [client]);
1129
- React10.useEffect(() => {
1157
+ React19.useEffect(() => {
1130
1158
  void refresh();
1131
1159
  }, [refresh]);
1132
- const value = React10.useMemo(
1160
+ const value = React19.useMemo(
1133
1161
  () => ({
1134
1162
  cart,
1135
1163
  loading,
@@ -1144,7 +1172,7 @@ function CartProvider({ children }) {
1144
1172
  return /* @__PURE__ */ jsx(CartContext.Provider, { value, children });
1145
1173
  }
1146
1174
  function useCart() {
1147
- const ctx = React10.useContext(CartContext);
1175
+ const ctx = React19.useContext(CartContext);
1148
1176
  if (!ctx) {
1149
1177
  throw new Error("useCart must be used inside <CartProvider>.");
1150
1178
  }
@@ -1158,8 +1186,8 @@ function AddToCart({
1158
1186
  }) {
1159
1187
  const t = useT();
1160
1188
  const { addLine } = useCart();
1161
- const [busy, setBusy] = React10.useState(false);
1162
- const [error, setError] = React10.useState(null);
1189
+ const [busy, setBusy] = React19.useState(false);
1190
+ const [error, setError] = React19.useState(null);
1163
1191
  const disabled = variant == null || busy;
1164
1192
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
1165
1193
  /* @__PURE__ */ jsx(
@@ -1435,15 +1463,15 @@ function CartDrawer({
1435
1463
  }
1436
1464
  );
1437
1465
  }
1438
- var CheckoutContext = React10.createContext(null);
1466
+ var CheckoutContext = React19.createContext(null);
1439
1467
  function CheckoutProvider({
1440
1468
  children
1441
1469
  }) {
1442
1470
  const client = useStorefront();
1443
- const [checkout, setCheckout] = React10.useState(null);
1444
- const [loading, setLoading] = React10.useState(false);
1445
- const [error, setError] = React10.useState(null);
1446
- const run = React10.useCallback(
1471
+ const [checkout, setCheckout] = React19.useState(null);
1472
+ const [loading, setLoading] = React19.useState(false);
1473
+ const [error, setError] = React19.useState(null);
1474
+ const run = React19.useCallback(
1447
1475
  async (op, store) => {
1448
1476
  setLoading(true);
1449
1477
  try {
@@ -1461,7 +1489,7 @@ function CheckoutProvider({
1461
1489
  },
1462
1490
  []
1463
1491
  );
1464
- const refresh = React10.useCallback(async () => {
1492
+ const refresh = React19.useCallback(async () => {
1465
1493
  setLoading(true);
1466
1494
  try {
1467
1495
  setCheckout(await client.checkout.get());
@@ -1470,10 +1498,10 @@ function CheckoutProvider({
1470
1498
  setLoading(false);
1471
1499
  }
1472
1500
  }, [client]);
1473
- React10.useEffect(() => {
1501
+ React19.useEffect(() => {
1474
1502
  void refresh();
1475
1503
  }, [refresh]);
1476
- const pay = React10.useCallback(
1504
+ const pay = React19.useCallback(
1477
1505
  async (opts = {}) => {
1478
1506
  const co = checkout;
1479
1507
  if (!co) throw new Error("pay() called before a checkout exists");
@@ -1496,7 +1524,7 @@ function CheckoutProvider({
1496
1524
  },
1497
1525
  [checkout, client, run]
1498
1526
  );
1499
- const value = React10.useMemo(
1527
+ const value = React19.useMemo(
1500
1528
  () => ({
1501
1529
  checkout,
1502
1530
  loading,
@@ -1521,7 +1549,7 @@ function CheckoutProvider({
1521
1549
  return /* @__PURE__ */ jsx(CheckoutContext.Provider, { value, children });
1522
1550
  }
1523
1551
  function useCheckout() {
1524
- const ctx = React10.useContext(CheckoutContext);
1552
+ const ctx = React19.useContext(CheckoutContext);
1525
1553
  if (!ctx) {
1526
1554
  throw new Error("useCheckout must be used inside <CheckoutProvider>.");
1527
1555
  }
@@ -1680,10 +1708,25 @@ function AddressForm({
1680
1708
  }) {
1681
1709
  const t = useT();
1682
1710
  const { checkout, setAddress, loading } = useCheckout();
1683
- const [form, setForm] = React10.useState(
1711
+ const [form, setForm] = React19.useState(
1684
1712
  () => seed(checkout?.email ?? null, checkout?.shipping_address ?? null)
1685
1713
  );
1686
- const set = (key) => (e) => setForm((f) => ({ ...f, [key]: e.target.value }));
1714
+ const touched = React19.useRef(/* @__PURE__ */ new Set());
1715
+ React19.useEffect(() => {
1716
+ const prefill = checkout?.customer_prefill;
1717
+ const addresses = prefill?.addresses.filter((a) => !a.is_pickup);
1718
+ const saved = addresses?.find((a) => a.is_default) ?? addresses?.[0];
1719
+ const address = checkout?.shipping_address ?? (saved ? { ...saved, postal_code: saved.zip } : null);
1720
+ const defaults = seed(checkout?.email ?? prefill?.email ?? null, address);
1721
+ defaults.first_name || (defaults.first_name = prefill?.first_name ?? "");
1722
+ defaults.last_name || (defaults.last_name = prefill?.last_name ?? "");
1723
+ defaults.phone || (defaults.phone = prefill?.phone ?? "");
1724
+ setForm((current) => Object.fromEntries(Object.entries(current).map(([key, value]) => [key, touched.current.has(key) ? value : value || defaults[key]])));
1725
+ }, [checkout?.token]);
1726
+ const set = (key) => (e) => {
1727
+ touched.current.add(key);
1728
+ setForm((f) => ({ ...f, [key]: e.target.value }));
1729
+ };
1687
1730
  async function submit(e) {
1688
1731
  e.preventDefault();
1689
1732
  const { email, ...rest } = form;
@@ -1734,11 +1777,11 @@ function ShippingPicker({
1734
1777
  const loc = useFormatLocale(locale);
1735
1778
  const { checkout, shippingQuotes, setShippingMethod, loading } = useCheckout();
1736
1779
  const heading = title ?? t("checkout.shippingMethod");
1737
- const [quotes, setQuotes] = React10.useState(null);
1738
- const [selected, setSelected] = React10.useState(null);
1739
- const [fetching, setFetching] = React10.useState(true);
1780
+ const [quotes, setQuotes] = React19.useState(null);
1781
+ const [selected, setSelected] = React19.useState(null);
1782
+ const [fetching, setFetching] = React19.useState(true);
1740
1783
  const addressKey = checkout?.shipping_address ? `${checkout.shipping_address.country_code}:${checkout.shipping_address.postal_code}` : null;
1741
- React10.useEffect(() => {
1784
+ React19.useEffect(() => {
1742
1785
  let live = true;
1743
1786
  setFetching(true);
1744
1787
  shippingQuotes().then((qs) => {
@@ -1766,17 +1809,13 @@ function ShippingPicker({
1766
1809
  }
1767
1810
  return /* @__PURE__ */ jsxs(Section, { title: heading, children: [
1768
1811
  /* @__PURE__ */ jsx("div", { role: "radiogroup", style: { display: "flex", flexDirection: "column", gap: "0.6rem" }, children: quotes.map((q) => {
1769
- const days = estDays(q, t);
1770
1812
  return /* @__PURE__ */ jsx(
1771
1813
  SelectRow,
1772
1814
  {
1773
1815
  selected: selected === q.rate_id,
1774
1816
  onSelect: () => setSelected(q.rate_id),
1775
1817
  trailing: q.is_free ? t("common.free") : formatCents(q.price_cents, q.currency, loc),
1776
- children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
1777
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: q.label }),
1778
- days ? /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: days }) : null
1779
- ] })
1818
+ children: /* @__PURE__ */ jsx("span", { style: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: q.label }) })
1780
1819
  },
1781
1820
  q.rate_id
1782
1821
  );
@@ -1784,15 +1823,6 @@ function ShippingPicker({
1784
1823
  /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { onClick: submit, busy: loading, disabled: selected == null, children: submitLabel ?? t("checkout.continueToPayment") }) })
1785
1824
  ] });
1786
1825
  }
1787
- function estDays(q, t) {
1788
- const { estimated_days_min: lo, estimated_days_max: hi } = q;
1789
- if (lo == null && hi == null) return null;
1790
- if (lo != null && hi != null) {
1791
- return lo === hi ? t("shipping.days", { count: lo }) : t("shipping.daysRange", { min: lo, max: hi });
1792
- }
1793
- const n = lo ?? hi;
1794
- return t("shipping.days", { count: n });
1795
- }
1796
1826
  function PaymentMethodPicker({
1797
1827
  methods,
1798
1828
  value,
@@ -1836,10 +1866,10 @@ function PaymentSection({
1836
1866
  const t = useT();
1837
1867
  const heading = title ?? t("checkout.payment");
1838
1868
  const { checkout, paymentMethods, setPaymentMethod, pay, loading } = useCheckout();
1839
- const [methods, setMethods] = React10.useState(null);
1840
- const [fetching, setFetching] = React10.useState(true);
1869
+ const [methods, setMethods] = React19.useState(null);
1870
+ const [fetching, setFetching] = React19.useState(true);
1841
1871
  const selected = checkout?.payment_method ?? null;
1842
- React10.useEffect(() => {
1872
+ React19.useEffect(() => {
1843
1873
  let live = true;
1844
1874
  setFetching(true);
1845
1875
  paymentMethods().then((ms) => {
@@ -1976,16 +2006,16 @@ function CheckoutFlow({
1976
2006
  }) {
1977
2007
  const t = useT();
1978
2008
  const { checkout, loading, create } = useCheckout();
1979
- const [step, setStep] = React10.useState("address");
1980
- const [bootstrapped, setBootstrapped] = React10.useState(false);
1981
- React10.useEffect(() => {
2009
+ const [step, setStep] = React19.useState("address");
2010
+ const [bootstrapped, setBootstrapped] = React19.useState(false);
2011
+ React19.useEffect(() => {
1982
2012
  if (checkout || !cartToken || bootstrapped) return;
1983
2013
  setBootstrapped(true);
1984
2014
  void create(cartToken).catch(() => {
1985
2015
  });
1986
2016
  }, [checkout, cartToken, bootstrapped, create]);
1987
2017
  const serverStep = reachedStep(checkout);
1988
- React10.useEffect(() => {
2018
+ React19.useEffect(() => {
1989
2019
  setStep((cur) => rank(serverStep) > rank(cur) ? serverStep : cur);
1990
2020
  }, [serverStep]);
1991
2021
  if (checkout?.order_id) {
@@ -2106,10 +2136,10 @@ function DefaultConfirmation({ checkout }) {
2106
2136
  }
2107
2137
  function useOrderStatus(statusToken, initial) {
2108
2138
  const client = useStorefront();
2109
- const [order, setOrder] = React10.useState(initial ?? null);
2110
- const [loading, setLoading] = React10.useState(false);
2111
- const [error, setError] = React10.useState(null);
2112
- const load = React10.useCallback(async () => {
2139
+ const [order, setOrder] = React19.useState(initial ?? null);
2140
+ const [loading, setLoading] = React19.useState(false);
2141
+ const [error, setError] = React19.useState(null);
2142
+ const load = React19.useCallback(async () => {
2113
2143
  if (!statusToken) return;
2114
2144
  setLoading(true);
2115
2145
  try {
@@ -2121,7 +2151,7 @@ function useOrderStatus(statusToken, initial) {
2121
2151
  setLoading(false);
2122
2152
  }
2123
2153
  }, [client, statusToken]);
2124
- React10.useEffect(() => {
2154
+ React19.useEffect(() => {
2125
2155
  if (initial) return;
2126
2156
  void load();
2127
2157
  }, [load, initial]);
@@ -2129,12 +2159,12 @@ function useOrderStatus(statusToken, initial) {
2129
2159
  }
2130
2160
  function usePayment(token, initial) {
2131
2161
  const client = useStorefront();
2132
- const [payment, setPayment] = React10.useState(initial ?? null);
2133
- const [methods, setMethods] = React10.useState([]);
2134
- const [loading, setLoading] = React10.useState(false);
2135
- const [busy, setBusy] = React10.useState(false);
2136
- const [error, setError] = React10.useState(null);
2137
- const load = React10.useCallback(async () => {
2162
+ const [payment, setPayment] = React19.useState(initial ?? null);
2163
+ const [methods, setMethods] = React19.useState([]);
2164
+ const [loading, setLoading] = React19.useState(false);
2165
+ const [busy, setBusy] = React19.useState(false);
2166
+ const [error, setError] = React19.useState(null);
2167
+ const load = React19.useCallback(async () => {
2138
2168
  if (!token) return;
2139
2169
  setLoading(true);
2140
2170
  try {
@@ -2151,7 +2181,7 @@ function usePayment(token, initial) {
2151
2181
  setLoading(false);
2152
2182
  }
2153
2183
  }, [client, token]);
2154
- React10.useEffect(() => {
2184
+ React19.useEffect(() => {
2155
2185
  if (!token) return;
2156
2186
  if (initial && initial.token === token) {
2157
2187
  setPayment(initial);
@@ -2160,7 +2190,7 @@ function usePayment(token, initial) {
2160
2190
  }
2161
2191
  void load();
2162
2192
  }, [token, load]);
2163
- const selectMethod = React10.useCallback(
2193
+ const selectMethod = React19.useCallback(
2164
2194
  async (method) => {
2165
2195
  if (!token) return null;
2166
2196
  setBusy(true);
@@ -2178,7 +2208,7 @@ function usePayment(token, initial) {
2178
2208
  },
2179
2209
  [client, token]
2180
2210
  );
2181
- const startPayment = React10.useCallback(
2211
+ const startPayment = React19.useCallback(
2182
2212
  async (input) => {
2183
2213
  if (!token) return null;
2184
2214
  setBusy(true);
@@ -2615,8 +2645,8 @@ function Row4({
2615
2645
  }
2616
2646
  function CopyRow({ label, value }) {
2617
2647
  const t = useT();
2618
- const [copied, setCopied] = React10.useState(false);
2619
- React10.useEffect(() => {
2648
+ const [copied, setCopied] = React19.useState(false);
2649
+ React19.useEffect(() => {
2620
2650
  if (!copied) return;
2621
2651
  const id = setTimeout(() => setCopied(false), 1800);
2622
2652
  return () => clearTimeout(id);
@@ -2700,12 +2730,12 @@ function PaymentPage({
2700
2730
  const t = useT();
2701
2731
  const loc = useFormatLocale(locale);
2702
2732
  const { payment, methods, loading, busy, error, reload, selectMethod, startPayment } = usePayment(token, initial);
2703
- const [verifying, setVerifying] = React10.useState(false);
2704
- React10.useEffect(() => {
2733
+ const [verifying, setVerifying] = React19.useState(false);
2734
+ React19.useEffect(() => {
2705
2735
  if (token) setVerifying(consumeHandoff(token));
2706
2736
  }, [token]);
2707
2737
  const settled = payment != null && !payment.can_pay && payment.blocked_reason === "already_paid";
2708
- React10.useEffect(() => {
2738
+ React19.useEffect(() => {
2709
2739
  if (!verifying || !token || settled) return;
2710
2740
  let attempts = 0;
2711
2741
  const id = setInterval(() => {
@@ -2720,7 +2750,7 @@ function PaymentPage({
2720
2750
  }, POLL_MS);
2721
2751
  return () => clearInterval(id);
2722
2752
  }, [verifying, token, settled, reload]);
2723
- React10.useEffect(() => {
2753
+ React19.useEffect(() => {
2724
2754
  if (!settled || !token || !payment) return;
2725
2755
  setVerifying(false);
2726
2756
  forgetHandoff(token);
@@ -2801,27 +2831,32 @@ function Amount({ label, value }) {
2801
2831
  function Link({ href, children }) {
2802
2832
  return /* @__PURE__ */ jsx("a", { href, style: { color: "var(--nv-brand)", fontSize: "0.875rem" }, children });
2803
2833
  }
2804
- var AccountContext = React10.createContext(null);
2834
+ var AccountContext = React19.createContext(null);
2805
2835
  function AccountProvider({ children }) {
2806
2836
  const client = useStorefront();
2807
- const [customer, setCustomer] = React10.useState(null);
2808
- const [loading, setLoading] = React10.useState(true);
2809
- const refresh = React10.useCallback(async () => {
2837
+ const [customer, setCustomer] = React19.useState(null);
2838
+ const [loading, setLoading] = React19.useState(true);
2839
+ const [error, setError] = React19.useState(false);
2840
+ const refresh = React19.useCallback(async () => {
2810
2841
  setLoading(true);
2842
+ setError(false);
2811
2843
  try {
2812
2844
  setCustomer(await client.account.me());
2845
+ } catch {
2846
+ setError(true);
2813
2847
  } finally {
2814
2848
  setLoading(false);
2815
2849
  }
2816
2850
  }, [client]);
2817
- React10.useEffect(() => {
2851
+ React19.useEffect(() => {
2818
2852
  void refresh();
2819
2853
  }, [refresh]);
2820
- const value = React10.useMemo(
2854
+ const value = React19.useMemo(
2821
2855
  () => ({
2822
2856
  customer,
2823
2857
  signedIn: customer != null,
2824
2858
  loading,
2859
+ error,
2825
2860
  requestMagicLink: (email) => client.account.requestMagicLink(email),
2826
2861
  verify: async (token) => {
2827
2862
  const c = await client.account.verify(token);
@@ -2834,12 +2869,12 @@ function AccountProvider({ children }) {
2834
2869
  },
2835
2870
  refresh
2836
2871
  }),
2837
- [customer, loading, client, refresh]
2872
+ [customer, loading, error, client, refresh]
2838
2873
  );
2839
2874
  return /* @__PURE__ */ jsx(AccountContext.Provider, { value, children });
2840
2875
  }
2841
2876
  function useAccount() {
2842
- const ctx = React10.useContext(AccountContext);
2877
+ const ctx = React19.useContext(AccountContext);
2843
2878
  if (!ctx) {
2844
2879
  throw new Error("useAccount must be used inside <AccountProvider>.");
2845
2880
  }
@@ -2847,9 +2882,9 @@ function useAccount() {
2847
2882
  }
2848
2883
  function useMagicLinkVerify(token) {
2849
2884
  const { verify } = useAccount();
2850
- const [status, setStatus] = React10.useState("idle");
2851
- const done = React10.useRef(false);
2852
- React10.useEffect(() => {
2885
+ const [status, setStatus] = React19.useState("idle");
2886
+ const done = React19.useRef(false);
2887
+ React19.useEffect(() => {
2853
2888
  if (!token || done.current) return;
2854
2889
  done.current = true;
2855
2890
  setStatus("verifying");
@@ -2860,18 +2895,22 @@ function useMagicLinkVerify(token) {
2860
2895
  function LoginForm({ verifyPath = "/account/verify", onSent }) {
2861
2896
  const t = useT();
2862
2897
  const { requestMagicLink } = useAccount();
2863
- const [email, setEmail] = React10.useState("");
2864
- const [busy, setBusy] = React10.useState(false);
2865
- const [sent, setSent] = React10.useState(false);
2866
- const [devLink, setDevLink] = React10.useState(null);
2898
+ const [email, setEmail] = React19.useState("");
2899
+ const [busy, setBusy] = React19.useState(false);
2900
+ const [sent, setSent] = React19.useState(false);
2901
+ const [error, setError] = React19.useState(false);
2902
+ const [devLink, setDevLink] = React19.useState(null);
2867
2903
  async function submit(e) {
2868
2904
  e.preventDefault();
2869
2905
  setBusy(true);
2906
+ setError(false);
2870
2907
  try {
2871
2908
  const r = await requestMagicLink(email);
2872
2909
  setSent(true);
2873
2910
  if (r.devToken) setDevLink(`${verifyPath}?token=${encodeURIComponent(r.devToken)}`);
2874
2911
  onSent?.(email);
2912
+ } catch {
2913
+ setError(true);
2875
2914
  } finally {
2876
2915
  setBusy(false);
2877
2916
  }
@@ -2910,6 +2949,7 @@ function LoginForm({ verifyPath = "/account/verify", onSent }) {
2910
2949
  }
2911
2950
  )
2912
2951
  ] }),
2952
+ error ? /* @__PURE__ */ jsx("p", { role: "alert", children: t("account.signInError") }) : null,
2913
2953
  /* @__PURE__ */ jsx(
2914
2954
  "button",
2915
2955
  {
@@ -2971,20 +3011,47 @@ function OrderHistory({
2971
3011
  const loc = useFormatLocale(locale);
2972
3012
  const { signedIn } = useAccount();
2973
3013
  const client = useStorefront();
2974
- const [orders, setOrders] = React10.useState(null);
2975
- const [reordering, setReordering] = React10.useState(null);
2976
- const [notice, setNotice] = React10.useState(null);
2977
- React10.useEffect(() => {
3014
+ const [orders, setOrders] = React19.useState(null);
3015
+ const [error, setError] = React19.useState(false);
3016
+ const [nextCursor, setNextCursor] = React19.useState(null);
3017
+ const [pageLoading, setPageLoading] = React19.useState(false);
3018
+ const [reload, setReload] = React19.useState(0);
3019
+ const [reordering, setReordering] = React19.useState(null);
3020
+ const [notice, setNotice] = React19.useState(null);
3021
+ React19.useEffect(() => {
2978
3022
  let live = true;
2979
3023
  if (!signedIn) {
2980
3024
  setOrders([]);
2981
3025
  return;
2982
3026
  }
2983
- client.account.orders().then((o) => live && setOrders(o)).catch(() => live && setOrders([]));
3027
+ setError(false);
3028
+ setOrders(null);
3029
+ client.account.ordersPage().then((page) => {
3030
+ if (live) {
3031
+ setOrders(page.orders);
3032
+ setNextCursor(page.nextCursor);
3033
+ }
3034
+ }).catch(() => {
3035
+ if (live) setError(true);
3036
+ });
2984
3037
  return () => {
2985
3038
  live = false;
2986
3039
  };
2987
- }, [signedIn, client]);
3040
+ }, [signedIn, client, reload]);
3041
+ async function loadMore() {
3042
+ if (!nextCursor || pageLoading) return;
3043
+ setPageLoading(true);
3044
+ setError(false);
3045
+ try {
3046
+ const page = await client.account.ordersPage({ beforeId: nextCursor });
3047
+ setOrders((previous) => [...previous ?? [], ...page.orders]);
3048
+ setNextCursor(page.nextCursor);
3049
+ } catch {
3050
+ setError(true);
3051
+ } finally {
3052
+ setPageLoading(false);
3053
+ }
3054
+ }
2988
3055
  async function reorder(statusToken) {
2989
3056
  setReordering(statusToken);
2990
3057
  setNotice(null);
@@ -3005,7 +3072,11 @@ function OrderHistory({
3005
3072
  }
3006
3073
  return /* @__PURE__ */ jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
3007
3074
  /* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.orders") }),
3008
- orders == null ? /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("common.loadingShort") }) : orders.length === 0 ? /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("account.noOrders") }) : /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.5rem" }, children: orders.map((o) => /* @__PURE__ */ jsxs(
3075
+ error ? /* @__PURE__ */ jsxs("div", { role: "alert", children: [
3076
+ /* @__PURE__ */ jsx("p", { children: t("account.loadError") }),
3077
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => orders ? void loadMore() : setReload((x) => x + 1), children: t("account.retry") })
3078
+ ] }) : null,
3079
+ orders == null ? error ? null : /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("common.loadingShort") }) : orders.length === 0 ? /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("account.noOrders") }) : /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.5rem" }, children: orders.map((o) => /* @__PURE__ */ jsxs(
3009
3080
  "li",
3010
3081
  {
3011
3082
  style: {
@@ -3052,12 +3123,13 @@ function OrderHistory({
3052
3123
  ]
3053
3124
  },
3054
3125
  o.status_token
3055
- )) })
3126
+ )) }),
3127
+ nextCursor ? /* @__PURE__ */ jsx("button", { type: "button", disabled: pageLoading, onClick: () => void loadMore(), children: pageLoading ? t("common.loadingShort") : t("account.loadMore") }) : null
3056
3128
  ] });
3057
3129
  }
3058
3130
  function AddressForm2({ initial, onSubmit, onCancel, busy }) {
3059
3131
  const t = useT();
3060
- const [form, setForm] = React10.useState(() => ({
3132
+ const [form, setForm] = React19.useState(() => ({
3061
3133
  first_name: initial?.first_name ?? "",
3062
3134
  last_name: initial?.last_name ?? "",
3063
3135
  company: initial?.company ?? "",
@@ -3155,17 +3227,17 @@ function AddressBook() {
3155
3227
  const t = useT();
3156
3228
  const { signedIn } = useAccount();
3157
3229
  const client = useStorefront();
3158
- const [addresses, setAddresses] = React10.useState(null);
3159
- const [mode, setMode] = React10.useState({ kind: "list" });
3160
- const [busy, setBusy] = React10.useState(false);
3161
- const load = React10.useCallback(() => {
3230
+ const [addresses, setAddresses] = React19.useState(null);
3231
+ const [mode, setMode] = React19.useState({ kind: "list" });
3232
+ const [busy, setBusy] = React19.useState(false);
3233
+ const load = React19.useCallback(() => {
3162
3234
  if (!signedIn) {
3163
3235
  setAddresses([]);
3164
3236
  return;
3165
3237
  }
3166
3238
  client.account.addresses().then(setAddresses).catch(() => setAddresses([]));
3167
3239
  }, [signedIn, client]);
3168
- React10.useEffect(() => {
3240
+ React19.useEffect(() => {
3169
3241
  load();
3170
3242
  }, [load]);
3171
3243
  async function run(fn) {
@@ -3219,10 +3291,10 @@ function ProfileEditor() {
3219
3291
  const t = useT();
3220
3292
  const client = useStorefront();
3221
3293
  const { customer, refresh } = useAccount();
3222
- const [editing, setEditing] = React10.useState(false);
3223
- const [busy, setBusy] = React10.useState(false);
3224
- const [error, setError] = React10.useState(false);
3225
- const [form, setForm] = React10.useState({});
3294
+ const [editing, setEditing] = React19.useState(false);
3295
+ const [busy, setBusy] = React19.useState(false);
3296
+ const [error, setError] = React19.useState(false);
3297
+ const [form, setForm] = React19.useState({});
3226
3298
  if (!customer) return null;
3227
3299
  function startEdit() {
3228
3300
  setForm({
@@ -3299,6 +3371,79 @@ function ProfileEditor() {
3299
3371
  ] })
3300
3372
  ] });
3301
3373
  }
3374
+ function AccountSecurity() {
3375
+ const client = useStorefront();
3376
+ const account = useAccount();
3377
+ const t = useT();
3378
+ const [sessions, setSessions] = React19.useState(null);
3379
+ const [email, setEmail] = React19.useState("");
3380
+ const [busy, setBusy] = React19.useState(false);
3381
+ const [notice, setNotice] = React19.useState(null);
3382
+ const [confirmClose, setConfirmClose] = React19.useState(false);
3383
+ const [retry, setRetry] = React19.useState(0);
3384
+ React19.useEffect(() => {
3385
+ let live = true;
3386
+ if (account.signedIn) client.account.sessions().then((s) => {
3387
+ if (live) setSessions(s);
3388
+ }).catch(() => {
3389
+ if (live) setNotice(t("account.loadError"));
3390
+ });
3391
+ return () => {
3392
+ live = false;
3393
+ };
3394
+ }, [client, account.signedIn, retry, t]);
3395
+ async function act(operation) {
3396
+ setBusy(true);
3397
+ setNotice(null);
3398
+ try {
3399
+ await operation();
3400
+ } catch (e) {
3401
+ setNotice(e instanceof StorefrontError && e.status === 401 ? t("account.recentLogin") : t("account.loadError"));
3402
+ } finally {
3403
+ setBusy(false);
3404
+ }
3405
+ }
3406
+ if (!account.signedIn) return null;
3407
+ return /* @__PURE__ */ jsxs("section", { style: { display: "grid", gap: "0.75rem" }, children: [
3408
+ /* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.security") }),
3409
+ notice ? /* @__PURE__ */ jsx("p", { role: "status", children: notice }) : null,
3410
+ /* @__PURE__ */ jsxs(
3411
+ "form",
3412
+ {
3413
+ style: { display: "flex", gap: "0.75rem", flexWrap: "wrap", alignItems: "end" },
3414
+ onSubmit: (e) => {
3415
+ e.preventDefault();
3416
+ void act(async () => {
3417
+ await client.account.requestEmailChange(email);
3418
+ setNotice(t("account.checkEmail"));
3419
+ });
3420
+ },
3421
+ children: [
3422
+ /* @__PURE__ */ jsx(Field, { label: t("account.newEmail"), type: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value) }),
3423
+ /* @__PURE__ */ jsx(Button, { type: "submit", busy, children: t("account.changeEmail") })
3424
+ ]
3425
+ }
3426
+ ),
3427
+ /* @__PURE__ */ jsx("h3", { children: t("account.sessions") }),
3428
+ sessions ? sessions.map((s) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem", justifyContent: "space-between" }, children: [
3429
+ /* @__PURE__ */ jsx("span", { children: s.current ? t("account.currentSession") : /* @__PURE__ */ jsx("time", { dateTime: s.created_at, children: s.created_at.slice(0, 10) }) }),
3430
+ /* @__PURE__ */ jsx(Button, { busy, onClick: () => void act(async () => {
3431
+ await client.account.revokeSession(s.id);
3432
+ if (s.current) await account.logout();
3433
+ else setSessions((old) => old?.filter((row) => row.id !== s.id) ?? null);
3434
+ }), children: t("account.revokeSession") })
3435
+ ] }, s.id)) : /* @__PURE__ */ jsx(Button, { onClick: () => setRetry((x) => x + 1), children: t("account.retry") }),
3436
+ confirmClose ? /* @__PURE__ */ jsxs("div", { children: [
3437
+ /* @__PURE__ */ jsx("p", { children: t("account.closeWarning") }),
3438
+ /* @__PURE__ */ jsx(Button, { busy, onClick: () => void act(async () => {
3439
+ await client.account.close();
3440
+ await account.refresh();
3441
+ }), children: t("account.confirmClose") }),
3442
+ " ",
3443
+ /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => setConfirmClose(false), children: t("account.cancel") })
3444
+ ] }) : /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => setConfirmClose(true), children: t("account.closeAccount") })
3445
+ ] });
3446
+ }
3302
3447
  function serializeJsonLd(data) {
3303
3448
  return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
3304
3449
  }
@@ -3403,6 +3548,6 @@ function OrganizationJsonLd({
3403
3548
  return /* @__PURE__ */ jsx(JsonLd, { data });
3404
3549
  }
3405
3550
 
3406
- export { AccountDashboard, AccountProvider, AddToCart, AddressBook, AddressForm, AvailabilityFilter, BackInStockForm, BreadcrumbJsonLd, CartDrawer, CartLineItem, CartProvider, CartSummary, CheckoutFlow, CheckoutProvider, CollectionPage, I18nProvider, JsonLd, LoginForm, MarketSwitcher, NeviosProvider, OrderConfirmation, OrderHistory, OrderLines, OrderStatusPage, OrderSummary, OrderTotals, OrganizationJsonLd, PaymentMethodPicker, PaymentPage, PaymentSection, PhaseTimeline, PriceMoney, PriceRangeFilter, ProductCard, ProductDetailPage, ProductGallery, ProductJsonLd, ProfileEditor, ShippingPicker, SortSelect, StatusBanner, StockBadge, TagFilter, ThemeProvider, TransferInstructions, VariantPicker, absoluteUrl, bundledMessages, cs, en, formatCents, formatMoney, isTerminal, serializeJsonLd, stripHtml, themeToCssVars, useAccount, useCart, useCheckout, useI18n, useMagicLinkVerify, useOrderStatus, usePayment, useStorefront, useStorefrontOptional, useT };
3551
+ export { AccountDashboard, AccountProvider, AccountSecurity, AddToCart, AddressBook, AddressForm, AvailabilityFilter, BackInStockForm, BreadcrumbJsonLd, CartDrawer, CartLineItem, CartProvider, CartSummary, CheckoutFlow, CheckoutProvider, CollectionPage, I18nProvider, JsonLd, LoginForm, MarketSwitcher, NeviosProvider, OrderConfirmation, OrderHistory, OrderLines, OrderStatusPage, OrderSummary, OrderTotals, OrganizationJsonLd, PaymentMethodPicker, PaymentPage, PaymentSection, PhaseTimeline, PriceMoney, PriceRangeFilter, ProductCard, ProductDetailPage, ProductGallery, ProductJsonLd, ProfileEditor, ShippingPicker, SortSelect, StatusBanner, StockBadge, TagFilter, ThemeProvider, TransferInstructions, VariantPicker, absoluteUrl, bundledMessages, cs, en, formatCents, formatMoney, isTerminal, serializeJsonLd, stripHtml, themeToCssVars, useAccount, useCart, useCheckout, useI18n, useMagicLinkVerify, useOrderStatus, usePayment, useStorefront, useStorefrontOptional, useT };
3407
3552
  //# sourceMappingURL=index.js.map
3408
3553
  //# sourceMappingURL=index.js.map