@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/README.md +23 -0
- package/dist/index.cjs +329 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +115 -7
- package/dist/index.d.ts +115 -7
- package/dist/index.js +324 -121
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
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
|
|
25
|
+
var React27__namespace = /*#__PURE__*/_interopNamespace(React27);
|
|
26
26
|
|
|
27
27
|
// src/provider.tsx
|
|
28
|
-
var StorefrontContext =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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] =
|
|
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] =
|
|
796
|
+
const [selected, setSelected] = React27__namespace.useState(
|
|
777
797
|
() => product.variants[0]?.options ?? []
|
|
778
798
|
);
|
|
779
|
-
|
|
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] =
|
|
821
|
-
const [state, setState] =
|
|
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] =
|
|
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
|
-
|
|
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] =
|
|
1157
|
-
const [loading, setLoading] =
|
|
1158
|
-
const run =
|
|
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 =
|
|
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
|
-
|
|
1213
|
+
React27__namespace.useEffect(() => {
|
|
1180
1214
|
void refresh();
|
|
1181
1215
|
}, [refresh]);
|
|
1182
|
-
const value =
|
|
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 =
|
|
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] =
|
|
1212
|
-
const [error, setError] =
|
|
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 =
|
|
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] =
|
|
1494
|
-
const [loading, setLoading] =
|
|
1495
|
-
const [error, setError] =
|
|
1496
|
-
const run =
|
|
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 =
|
|
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
|
-
|
|
1563
|
+
React27__namespace.useEffect(() => {
|
|
1524
1564
|
void refresh();
|
|
1525
1565
|
}, [refresh]);
|
|
1526
|
-
const pay =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
1791
|
+
const [form, setForm] = React27__namespace.useState(
|
|
1734
1792
|
() => seed(checkout?.email ?? null, checkout?.shipping_address ?? null)
|
|
1735
1793
|
);
|
|
1736
|
-
const touched =
|
|
1737
|
-
|
|
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] =
|
|
1803
|
-
const [selected, setSelected] =
|
|
1804
|
-
const [fetching, setFetching] =
|
|
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
|
-
|
|
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] =
|
|
1892
|
-
const [fetching, setFetching] =
|
|
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
|
-
|
|
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
|
-
|
|
1912
|
-
|
|
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] =
|
|
2032
|
-
const [bootstrapped, setBootstrapped] =
|
|
2033
|
-
|
|
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
|
-
|
|
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] =
|
|
2162
|
-
const [loading, setLoading] =
|
|
2163
|
-
const [error, setError] =
|
|
2164
|
-
const load =
|
|
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
|
-
|
|
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] =
|
|
2185
|
-
const [methods, setMethods] =
|
|
2186
|
-
const [loading, setLoading] =
|
|
2187
|
-
const [busy, setBusy] =
|
|
2188
|
-
const [error, setError] =
|
|
2189
|
-
const load =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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] =
|
|
2671
|
-
|
|
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] =
|
|
2756
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
2962
|
+
var AccountContext = React27__namespace.createContext(null);
|
|
2857
2963
|
function AccountProvider({ children }) {
|
|
2858
2964
|
const client = useStorefront();
|
|
2859
|
-
const [customer, setCustomer] =
|
|
2860
|
-
const [loading, setLoading] =
|
|
2861
|
-
const [error, setError] =
|
|
2862
|
-
const refresh =
|
|
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
|
-
|
|
2979
|
+
React27__namespace.useEffect(() => {
|
|
2874
2980
|
void refresh();
|
|
2875
2981
|
}, [refresh]);
|
|
2876
|
-
const value =
|
|
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 =
|
|
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] =
|
|
2908
|
-
const done =
|
|
2909
|
-
|
|
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] =
|
|
2921
|
-
const [busy, setBusy] =
|
|
2922
|
-
const [sent, setSent] =
|
|
2923
|
-
const [error, setError] =
|
|
2924
|
-
const [devLink, setDevLink] =
|
|
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] =
|
|
3037
|
-
const [error, setError] =
|
|
3038
|
-
const [nextCursor, setNextCursor] =
|
|
3039
|
-
const [pageLoading, setPageLoading] =
|
|
3040
|
-
const [reload, setReload] =
|
|
3041
|
-
const [reordering, setReordering] =
|
|
3042
|
-
const [notice, setNotice] =
|
|
3043
|
-
|
|
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] =
|
|
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] =
|
|
3253
|
-
const [mode, setMode] =
|
|
3254
|
-
const [busy, setBusy] =
|
|
3255
|
-
const load =
|
|
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
|
-
|
|
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] =
|
|
3317
|
-
const [busy, setBusy] =
|
|
3318
|
-
const [error, setError] =
|
|
3319
|
-
const [form, setForm] =
|
|
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] =
|
|
3401
|
-
const [email, setEmail] =
|
|
3402
|
-
const [busy, setBusy] =
|
|
3403
|
-
const [notice, setNotice] =
|
|
3404
|
-
const [confirmClose, setConfirmClose] =
|
|
3405
|
-
const [retry, setRetry] =
|
|
3406
|
-
|
|
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;
|