@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.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as React27 from 'react';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { imageSrcSet, StorefrontError } from '@nevios/storefront-js';
|
|
4
4
|
|
|
5
5
|
// src/provider.tsx
|
|
6
|
-
var StorefrontContext =
|
|
6
|
+
var StorefrontContext = React27.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 =
|
|
11
|
+
const client = React27.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
|
|
20
|
+
return React27.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
|
|
56
|
+
return React27.createElement(
|
|
57
57
|
Tag,
|
|
58
58
|
{ "data-nevios-theme": "", style },
|
|
59
59
|
children
|
|
@@ -62,6 +62,16 @@ function ThemeProvider({
|
|
|
62
62
|
|
|
63
63
|
// src/i18n/en.ts
|
|
64
64
|
var en = {
|
|
65
|
+
"subscriptions.purchaseOption": "Purchase option",
|
|
66
|
+
"subscriptions.empty": "You have no subscriptions yet.",
|
|
67
|
+
"subscriptions.status.active": "Active",
|
|
68
|
+
"subscriptions.status.paused": "Paused",
|
|
69
|
+
"subscriptions.status.pending": "Awaiting payment",
|
|
70
|
+
"subscriptions.status.cancelled": "Cancelled",
|
|
71
|
+
"subscriptions.title": "Subscription",
|
|
72
|
+
"subscriptions.consent": "I agree to the recurring price, delivery schedule and terms shown above.",
|
|
73
|
+
"subscriptions.eachDelivery": "For each delivery",
|
|
74
|
+
"subscriptions.once": "One-time purchase",
|
|
65
75
|
// common
|
|
66
76
|
"common.free": "Free",
|
|
67
77
|
"common.close": "Close",
|
|
@@ -278,6 +288,16 @@ var en = {
|
|
|
278
288
|
|
|
279
289
|
// src/i18n/cs.ts
|
|
280
290
|
var cs = {
|
|
291
|
+
"subscriptions.purchaseOption": "Zp\u016Fsob n\xE1kupu",
|
|
292
|
+
"subscriptions.empty": "Zat\xEDm nem\xE1te \u017E\xE1dn\xE9 p\u0159edplatn\xE9.",
|
|
293
|
+
"subscriptions.status.active": "Aktivn\xED",
|
|
294
|
+
"subscriptions.status.paused": "Pozastaveno",
|
|
295
|
+
"subscriptions.status.pending": "\u010Cek\xE1 na platbu",
|
|
296
|
+
"subscriptions.status.cancelled": "Zru\u0161eno",
|
|
297
|
+
"subscriptions.title": "P\u0159edplatn\xE9",
|
|
298
|
+
"subscriptions.consent": "Souhlas\xEDm s uvedenou opakovanou cenou, pl\xE1nem doru\u010Den\xED a podm\xEDnkami.",
|
|
299
|
+
"subscriptions.eachDelivery": "Za ka\u017Ed\xE9 doru\u010Den\xED",
|
|
300
|
+
"subscriptions.once": "Jednor\xE1zov\xFD n\xE1kup",
|
|
281
301
|
"common.free": "Zdarma",
|
|
282
302
|
"common.close": "Zav\u0159\xEDt",
|
|
283
303
|
"common.loadingShort": "\u2026",
|
|
@@ -519,13 +539,13 @@ function makeI18n(locale, messages) {
|
|
|
519
539
|
return { locale, messages, t };
|
|
520
540
|
}
|
|
521
541
|
var DEFAULT_I18N = makeI18n("en", en);
|
|
522
|
-
var I18nContext =
|
|
542
|
+
var I18nContext = React27.createContext(null);
|
|
523
543
|
function I18nProvider({
|
|
524
544
|
locale,
|
|
525
545
|
messages,
|
|
526
546
|
children
|
|
527
547
|
}) {
|
|
528
|
-
const value =
|
|
548
|
+
const value = React27.useMemo(() => {
|
|
529
549
|
const lang = langOf(locale);
|
|
530
550
|
if (locale && !(lang in BUNDLED) && !messages) {
|
|
531
551
|
devWarn(
|
|
@@ -539,7 +559,7 @@ function I18nProvider({
|
|
|
539
559
|
return /* @__PURE__ */ jsx(I18nContext.Provider, { value, children });
|
|
540
560
|
}
|
|
541
561
|
function useI18n() {
|
|
542
|
-
return
|
|
562
|
+
return React27.useContext(I18nContext) ?? DEFAULT_I18N;
|
|
543
563
|
}
|
|
544
564
|
function useT() {
|
|
545
565
|
return useI18n().t;
|
|
@@ -663,7 +683,7 @@ function ProductCard({ product, href, locale }) {
|
|
|
663
683
|
var MAIN_WIDTHS = [600, 800, 1e3];
|
|
664
684
|
var THUMB_WIDTHS = [56, 112];
|
|
665
685
|
function ProductGallery({ media, alt }) {
|
|
666
|
-
const [active, setActive] =
|
|
686
|
+
const [active, setActive] = React27.useState(0);
|
|
667
687
|
const imageBase = useStorefrontOptional()?.imageBase;
|
|
668
688
|
if (media.length === 0) {
|
|
669
689
|
return /* @__PURE__ */ jsx(
|
|
@@ -751,10 +771,10 @@ function matchVariant(product, selected) {
|
|
|
751
771
|
) ?? null;
|
|
752
772
|
}
|
|
753
773
|
function VariantPicker({ product, onChange }) {
|
|
754
|
-
const [selected, setSelected] =
|
|
774
|
+
const [selected, setSelected] = React27.useState(
|
|
755
775
|
() => product.variants[0]?.options ?? []
|
|
756
776
|
);
|
|
757
|
-
|
|
777
|
+
React27.useEffect(() => {
|
|
758
778
|
onChange?.(matchVariant(product, selected));
|
|
759
779
|
}, [product, selected]);
|
|
760
780
|
if (product.options.length === 0) return null;
|
|
@@ -795,8 +815,8 @@ function BackInStockForm({
|
|
|
795
815
|
}) {
|
|
796
816
|
const t = useT();
|
|
797
817
|
const client = useStorefront();
|
|
798
|
-
const [email, setEmail] =
|
|
799
|
-
const [state, setState] =
|
|
818
|
+
const [email, setEmail] = React27.useState(defaultEmail);
|
|
819
|
+
const [state, setState] = React27.useState("idle");
|
|
800
820
|
if (!variant || variant.availability?.status !== "out_of_stock") return null;
|
|
801
821
|
async function submit(e) {
|
|
802
822
|
e.preventDefault();
|
|
@@ -896,7 +916,7 @@ function ProductDetailPage({
|
|
|
896
916
|
locale,
|
|
897
917
|
addToCart
|
|
898
918
|
}) {
|
|
899
|
-
const [variant, setVariant] =
|
|
919
|
+
const [variant, setVariant] = React27.useState(
|
|
900
920
|
product.variants[0] ?? null
|
|
901
921
|
);
|
|
902
922
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1128,12 +1148,26 @@ function TagFilter({ tags, selected = [], onChange }) {
|
|
|
1128
1148
|
}) })
|
|
1129
1149
|
] });
|
|
1130
1150
|
}
|
|
1131
|
-
|
|
1151
|
+
function PurchaseOptions({ plans, value, onChange, disabled, locale }) {
|
|
1152
|
+
const t = useT();
|
|
1153
|
+
const loc = useFormatLocale(locale);
|
|
1154
|
+
const name = React27.useId();
|
|
1155
|
+
const options = [{ id: null, name: t("subscriptions.once"), price: void 0 }, ...plans];
|
|
1156
|
+
return /* @__PURE__ */ jsxs("fieldset", { disabled, style: { border: 0, padding: 0, margin: 0, display: "grid", gap: "0.5rem" }, children: [
|
|
1157
|
+
/* @__PURE__ */ jsx("legend", { style: { marginBottom: "0.5rem", fontWeight: 500 }, children: t("subscriptions.purchaseOption") }),
|
|
1158
|
+
options.map((option) => /* @__PURE__ */ 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: [
|
|
1159
|
+
/* @__PURE__ */ jsx("input", { type: "radio", name, checked: value === option.id, onChange: () => onChange(option.id) }),
|
|
1160
|
+
/* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: option.name }),
|
|
1161
|
+
option.price ? /* @__PURE__ */ jsx("span", { children: formatCents(option.price.amount_cents, option.price.currency, loc) }) : null
|
|
1162
|
+
] }, option.id ?? "once"))
|
|
1163
|
+
] });
|
|
1164
|
+
}
|
|
1165
|
+
var CartContext = React27.createContext(null);
|
|
1132
1166
|
function CartProvider({ children }) {
|
|
1133
1167
|
const client = useStorefront();
|
|
1134
|
-
const [cart, setCart] =
|
|
1135
|
-
const [loading, setLoading] =
|
|
1136
|
-
const run =
|
|
1168
|
+
const [cart, setCart] = React27.useState(null);
|
|
1169
|
+
const [loading, setLoading] = React27.useState(false);
|
|
1170
|
+
const run = React27.useCallback(
|
|
1137
1171
|
async (op) => {
|
|
1138
1172
|
setLoading(true);
|
|
1139
1173
|
try {
|
|
@@ -1146,7 +1180,7 @@ function CartProvider({ children }) {
|
|
|
1146
1180
|
},
|
|
1147
1181
|
[]
|
|
1148
1182
|
);
|
|
1149
|
-
const refresh =
|
|
1183
|
+
const refresh = React27.useCallback(async () => {
|
|
1150
1184
|
setLoading(true);
|
|
1151
1185
|
try {
|
|
1152
1186
|
setCart(await client.cart.get());
|
|
@@ -1154,16 +1188,17 @@ function CartProvider({ children }) {
|
|
|
1154
1188
|
setLoading(false);
|
|
1155
1189
|
}
|
|
1156
1190
|
}, [client]);
|
|
1157
|
-
|
|
1191
|
+
React27.useEffect(() => {
|
|
1158
1192
|
void refresh();
|
|
1159
1193
|
}, [refresh]);
|
|
1160
|
-
const value =
|
|
1194
|
+
const value = React27.useMemo(
|
|
1161
1195
|
() => ({
|
|
1162
1196
|
cart,
|
|
1163
1197
|
loading,
|
|
1164
1198
|
itemCount: cart?.totals.item_count ?? 0,
|
|
1165
1199
|
addLine: (input) => run(() => client.cart.addLine(input)),
|
|
1166
1200
|
updateLine: (lineId, quantity) => run(() => client.cart.updateLine(lineId, quantity)),
|
|
1201
|
+
clearSubscription: () => run(() => client.cart.clearSubscription()),
|
|
1167
1202
|
removeLine: (lineId) => run(() => client.cart.removeLine(lineId)),
|
|
1168
1203
|
refresh
|
|
1169
1204
|
}),
|
|
@@ -1172,7 +1207,7 @@ function CartProvider({ children }) {
|
|
|
1172
1207
|
return /* @__PURE__ */ jsx(CartContext.Provider, { value, children });
|
|
1173
1208
|
}
|
|
1174
1209
|
function useCart() {
|
|
1175
|
-
const ctx =
|
|
1210
|
+
const ctx = React27.useContext(CartContext);
|
|
1176
1211
|
if (!ctx) {
|
|
1177
1212
|
throw new Error("useCart must be used inside <CartProvider>.");
|
|
1178
1213
|
}
|
|
@@ -1181,13 +1216,14 @@ function useCart() {
|
|
|
1181
1216
|
function AddToCart({
|
|
1182
1217
|
variant,
|
|
1183
1218
|
quantity = 1,
|
|
1219
|
+
subscriptionPlanId,
|
|
1184
1220
|
children,
|
|
1185
1221
|
onAdded
|
|
1186
1222
|
}) {
|
|
1187
1223
|
const t = useT();
|
|
1188
1224
|
const { addLine } = useCart();
|
|
1189
|
-
const [busy, setBusy] =
|
|
1190
|
-
const [error, setError] =
|
|
1225
|
+
const [busy, setBusy] = React27.useState(false);
|
|
1226
|
+
const [error, setError] = React27.useState(null);
|
|
1191
1227
|
const disabled = variant == null || busy;
|
|
1192
1228
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
1193
1229
|
/* @__PURE__ */ jsx(
|
|
@@ -1200,7 +1236,7 @@ function AddToCart({
|
|
|
1200
1236
|
setBusy(true);
|
|
1201
1237
|
setError(null);
|
|
1202
1238
|
try {
|
|
1203
|
-
await addLine({ variant_id: variant.id, quantity });
|
|
1239
|
+
await addLine({ variant_id: variant.id, quantity, ...subscriptionPlanId ? { subscription_plan_id: subscriptionPlanId } : {} });
|
|
1204
1240
|
onAdded?.();
|
|
1205
1241
|
} catch (err) {
|
|
1206
1242
|
const code = err?.code;
|
|
@@ -1229,7 +1265,7 @@ function AddToCart({
|
|
|
1229
1265
|
function CartLineItem({ line, locale }) {
|
|
1230
1266
|
const t = useT();
|
|
1231
1267
|
const loc = useFormatLocale(locale);
|
|
1232
|
-
const { updateLine, removeLine } = useCart();
|
|
1268
|
+
const { cart, updateLine, removeLine } = useCart();
|
|
1233
1269
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.85rem", alignItems: "flex-start", padding: "0.75rem 0" }, children: [
|
|
1234
1270
|
/* @__PURE__ */ jsx(
|
|
1235
1271
|
"div",
|
|
@@ -1258,6 +1294,7 @@ function CartLineItem({ line, locale }) {
|
|
|
1258
1294
|
/* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1259
1295
|
/* @__PURE__ */ jsx("div", { style: { fontWeight: 500, lineHeight: 1.3 }, children: line.title }),
|
|
1260
1296
|
line.variant_title ? /* @__PURE__ */ jsx("div", { style: { color: "var(--nv-muted)", fontSize: "0.875em" }, children: line.variant_title }) : null,
|
|
1297
|
+
line.subscription_plan_id ? /* @__PURE__ */ jsx("div", { style: { color: "var(--nv-muted)", fontSize: "0.875em" }, children: cart?.subscription?.plan?.name ?? t("subscriptions.title") }) : null,
|
|
1261
1298
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem", marginTop: "0.5rem" }, children: [
|
|
1262
1299
|
/* @__PURE__ */ jsxs(
|
|
1263
1300
|
"div",
|
|
@@ -1336,7 +1373,7 @@ function CartSummary({
|
|
|
1336
1373
|
const { cart } = useCart();
|
|
1337
1374
|
if (cart == null) return null;
|
|
1338
1375
|
const t = cart.totals;
|
|
1339
|
-
const empty = t.item_count === 0;
|
|
1376
|
+
const empty = t.item_count === 0 || cart.subscription?.available === false;
|
|
1340
1377
|
const cta = checkoutLabel ?? tr("cart.checkout");
|
|
1341
1378
|
const ctaStyle = {
|
|
1342
1379
|
display: "block",
|
|
@@ -1357,6 +1394,7 @@ function CartSummary({
|
|
|
1357
1394
|
t.tax_cents > 0 ? /* @__PURE__ */ jsx(Row, { label: tr("cart.tax"), value: formatCents(t.tax_cents, t.currency, loc), muted: true }) : null,
|
|
1358
1395
|
/* @__PURE__ */ jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
|
|
1359
1396
|
/* @__PURE__ */ jsx(Row, { label: tr("cart.total"), value: formatCents(t.total_cents, t.currency, loc), strong: true }),
|
|
1397
|
+
cart.subscription?.error ? /* @__PURE__ */ jsx("p", { role: "alert", style: { margin: 0 }, children: cart.subscription.error.message }) : null,
|
|
1360
1398
|
checkoutHref && !empty ? /* @__PURE__ */ jsx("a", { href: checkoutHref, style: ctaStyle, children: cta }) : /* @__PURE__ */ jsx("button", { type: "button", disabled: empty, onClick: onCheckout, style: ctaStyle, children: cta })
|
|
1361
1399
|
] });
|
|
1362
1400
|
}
|
|
@@ -1463,15 +1501,15 @@ function CartDrawer({
|
|
|
1463
1501
|
}
|
|
1464
1502
|
);
|
|
1465
1503
|
}
|
|
1466
|
-
var CheckoutContext =
|
|
1504
|
+
var CheckoutContext = React27.createContext(null);
|
|
1467
1505
|
function CheckoutProvider({
|
|
1468
1506
|
children
|
|
1469
1507
|
}) {
|
|
1470
1508
|
const client = useStorefront();
|
|
1471
|
-
const [checkout, setCheckout] =
|
|
1472
|
-
const [loading, setLoading] =
|
|
1473
|
-
const [error, setError] =
|
|
1474
|
-
const run =
|
|
1509
|
+
const [checkout, setCheckout] = React27.useState(null);
|
|
1510
|
+
const [loading, setLoading] = React27.useState(false);
|
|
1511
|
+
const [error, setError] = React27.useState(null);
|
|
1512
|
+
const run = React27.useCallback(
|
|
1475
1513
|
async (op, store) => {
|
|
1476
1514
|
setLoading(true);
|
|
1477
1515
|
try {
|
|
@@ -1489,23 +1527,36 @@ function CheckoutProvider({
|
|
|
1489
1527
|
},
|
|
1490
1528
|
[]
|
|
1491
1529
|
);
|
|
1492
|
-
const refresh =
|
|
1530
|
+
const refresh = React27.useCallback(async () => {
|
|
1493
1531
|
setLoading(true);
|
|
1494
1532
|
try {
|
|
1495
1533
|
setCheckout(await client.checkout.get());
|
|
1496
1534
|
setError(null);
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1497
1537
|
} finally {
|
|
1498
1538
|
setLoading(false);
|
|
1499
1539
|
}
|
|
1500
1540
|
}, [client]);
|
|
1501
|
-
|
|
1541
|
+
React27.useEffect(() => {
|
|
1502
1542
|
void refresh();
|
|
1503
1543
|
}, [refresh]);
|
|
1504
|
-
const pay =
|
|
1544
|
+
const pay = React27.useCallback(
|
|
1505
1545
|
async (opts = {}) => {
|
|
1506
1546
|
const co = checkout;
|
|
1507
1547
|
if (!co) throw new Error("pay() called before a checkout exists");
|
|
1508
1548
|
const idempotencyKey = opts.idempotencyKey ?? co.token;
|
|
1549
|
+
if (co.subscription) {
|
|
1550
|
+
if (co.subscription.subscription_id) {
|
|
1551
|
+
const payment = await run(() => client.checkout.subscriptionPayment(), () => null);
|
|
1552
|
+
setCheckout(await client.checkout.get());
|
|
1553
|
+
return { kind: "subscription_payment", payment };
|
|
1554
|
+
}
|
|
1555
|
+
if (!opts.subscription) throw new Error("Accept the current subscription terms before paying");
|
|
1556
|
+
const subscription = await run(() => client.checkout.enrollSubscription(opts.subscription), () => null);
|
|
1557
|
+
setCheckout(await client.checkout.get());
|
|
1558
|
+
return { kind: "subscription", subscription };
|
|
1559
|
+
}
|
|
1509
1560
|
if (co.payment_category === "gateway") {
|
|
1510
1561
|
const start = await run(
|
|
1511
1562
|
() => client.checkout.startPayment({ return_url: opts.returnUrl, cancel_url: opts.cancelUrl }),
|
|
@@ -1524,11 +1575,18 @@ function CheckoutProvider({
|
|
|
1524
1575
|
},
|
|
1525
1576
|
[checkout, client, run]
|
|
1526
1577
|
);
|
|
1527
|
-
const value =
|
|
1578
|
+
const value = React27.useMemo(
|
|
1528
1579
|
() => ({
|
|
1529
1580
|
checkout,
|
|
1530
1581
|
loading,
|
|
1531
1582
|
error,
|
|
1583
|
+
selectSubscription: (selection) => run(async () => {
|
|
1584
|
+
const quote = await client.checkout.selectSubscription(selection);
|
|
1585
|
+
setCheckout(await client.checkout.get());
|
|
1586
|
+
return quote;
|
|
1587
|
+
}, () => null),
|
|
1588
|
+
clearSubscription: () => run(() => client.checkout.clearSubscription(), (r) => r),
|
|
1589
|
+
reviewSubscription: () => run(() => client.checkout.reviewSubscription(), () => null),
|
|
1532
1590
|
create: (cartToken, input) => run(() => client.checkout.create(cartToken, input), (r) => r),
|
|
1533
1591
|
setAddress: (input) => run(() => client.checkout.setAddress(input), (r) => r),
|
|
1534
1592
|
shippingQuotes: () => run(() => client.checkout.shippingQuotes(), () => null),
|
|
@@ -1549,7 +1607,7 @@ function CheckoutProvider({
|
|
|
1549
1607
|
return /* @__PURE__ */ jsx(CheckoutContext.Provider, { value, children });
|
|
1550
1608
|
}
|
|
1551
1609
|
function useCheckout() {
|
|
1552
|
-
const ctx =
|
|
1610
|
+
const ctx = React27.useContext(CheckoutContext);
|
|
1553
1611
|
if (!ctx) {
|
|
1554
1612
|
throw new Error("useCheckout must be used inside <CheckoutProvider>.");
|
|
1555
1613
|
}
|
|
@@ -1708,11 +1766,11 @@ function AddressForm({
|
|
|
1708
1766
|
}) {
|
|
1709
1767
|
const t = useT();
|
|
1710
1768
|
const { checkout, setAddress, loading } = useCheckout();
|
|
1711
|
-
const [form, setForm] =
|
|
1769
|
+
const [form, setForm] = React27.useState(
|
|
1712
1770
|
() => seed(checkout?.email ?? null, checkout?.shipping_address ?? null)
|
|
1713
1771
|
);
|
|
1714
|
-
const touched =
|
|
1715
|
-
|
|
1772
|
+
const touched = React27.useRef(/* @__PURE__ */ new Set());
|
|
1773
|
+
React27.useEffect(() => {
|
|
1716
1774
|
const prefill = checkout?.customer_prefill;
|
|
1717
1775
|
const addresses = prefill?.addresses.filter((a) => !a.is_pickup);
|
|
1718
1776
|
const saved = addresses?.find((a) => a.is_default) ?? addresses?.[0];
|
|
@@ -1777,11 +1835,11 @@ function ShippingPicker({
|
|
|
1777
1835
|
const loc = useFormatLocale(locale);
|
|
1778
1836
|
const { checkout, shippingQuotes, setShippingMethod, loading } = useCheckout();
|
|
1779
1837
|
const heading = title ?? t("checkout.shippingMethod");
|
|
1780
|
-
const [quotes, setQuotes] =
|
|
1781
|
-
const [selected, setSelected] =
|
|
1782
|
-
const [fetching, setFetching] =
|
|
1838
|
+
const [quotes, setQuotes] = React27.useState(null);
|
|
1839
|
+
const [selected, setSelected] = React27.useState(null);
|
|
1840
|
+
const [fetching, setFetching] = React27.useState(true);
|
|
1783
1841
|
const addressKey = checkout?.shipping_address ? `${checkout.shipping_address.country_code}:${checkout.shipping_address.postal_code}` : null;
|
|
1784
|
-
|
|
1842
|
+
React27.useEffect(() => {
|
|
1785
1843
|
let live = true;
|
|
1786
1844
|
setFetching(true);
|
|
1787
1845
|
shippingQuotes().then((qs) => {
|
|
@@ -1856,20 +1914,45 @@ function PaymentMethodPicker({
|
|
|
1856
1914
|
}
|
|
1857
1915
|
);
|
|
1858
1916
|
}
|
|
1917
|
+
function SubscriptionTerms({ quote, checked, onChange, locale }) {
|
|
1918
|
+
const t = useT();
|
|
1919
|
+
const loc = useFormatLocale(locale);
|
|
1920
|
+
return /* @__PURE__ */ jsxs("fieldset", { style: { margin: 0, padding: "1rem", border: "1px solid var(--nv-border)", borderRadius: "var(--nv-radius)" }, children: [
|
|
1921
|
+
/* @__PURE__ */ jsx("legend", { children: t("subscriptions.title") }),
|
|
1922
|
+
/* @__PURE__ */ jsxs("p", { style: { margin: "0 0 0.5rem", fontWeight: 500 }, children: [
|
|
1923
|
+
t("subscriptions.eachDelivery"),
|
|
1924
|
+
": ",
|
|
1925
|
+
formatCents(quote.recurring_total_cents, quote.currency, loc)
|
|
1926
|
+
] }),
|
|
1927
|
+
/* @__PURE__ */ jsx("p", { style: { margin: "0 0 0.75rem", whiteSpace: "pre-wrap" }, children: quote.terms_text }),
|
|
1928
|
+
/* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "flex-start", gap: "0.6rem", cursor: "pointer" }, children: [
|
|
1929
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", checked, onChange: (event) => onChange(event.target.checked) }),
|
|
1930
|
+
/* @__PURE__ */ jsx("span", { children: t("subscriptions.consent") })
|
|
1931
|
+
] })
|
|
1932
|
+
] });
|
|
1933
|
+
}
|
|
1859
1934
|
function PaymentSection({
|
|
1860
1935
|
title,
|
|
1861
1936
|
returnUrl,
|
|
1862
1937
|
cancelUrl,
|
|
1863
1938
|
onOrderPlaced,
|
|
1864
|
-
locale
|
|
1939
|
+
locale,
|
|
1940
|
+
onSubscriptionPayment
|
|
1865
1941
|
}) {
|
|
1866
1942
|
const t = useT();
|
|
1867
1943
|
const heading = title ?? t("checkout.payment");
|
|
1868
|
-
const { checkout, paymentMethods, setPaymentMethod, pay, loading } = useCheckout();
|
|
1869
|
-
const [methods, setMethods] =
|
|
1870
|
-
const [fetching, setFetching] =
|
|
1944
|
+
const { checkout, paymentMethods, setPaymentMethod, pay, loading, refresh } = useCheckout();
|
|
1945
|
+
const [methods, setMethods] = React27.useState(null);
|
|
1946
|
+
const [fetching, setFetching] = React27.useState(true);
|
|
1871
1947
|
const selected = checkout?.payment_method ?? null;
|
|
1872
|
-
|
|
1948
|
+
const [acceptedDigest, setAcceptedDigest] = React27.useState(null);
|
|
1949
|
+
const [actionBusy, setActionBusy] = React27.useState(false);
|
|
1950
|
+
const quote = checkout?.subscription?.quote;
|
|
1951
|
+
const needsConsent = Boolean(checkout?.subscription?.requires_consent);
|
|
1952
|
+
const clickInFlight = React27.useRef(false);
|
|
1953
|
+
const [actionError, setActionError] = React27.useState(null);
|
|
1954
|
+
const accepted = Boolean(quote && acceptedDigest === quote.quote_digest);
|
|
1955
|
+
React27.useEffect(() => {
|
|
1873
1956
|
let live = true;
|
|
1874
1957
|
setFetching(true);
|
|
1875
1958
|
paymentMethods().then((ms) => {
|
|
@@ -1882,12 +1965,30 @@ function PaymentSection({
|
|
|
1882
1965
|
return () => {
|
|
1883
1966
|
live = false;
|
|
1884
1967
|
};
|
|
1885
|
-
}, []);
|
|
1968
|
+
}, [checkout?.token, checkout?.shipping_method_id, checkout?.subscription?.selection.plan_id]);
|
|
1886
1969
|
const selectedMethod = methods?.find((m) => m.method === selected) ?? null;
|
|
1887
1970
|
const isGateway = selectedMethod?.category === "gateway";
|
|
1888
1971
|
async function placeOrder() {
|
|
1889
|
-
|
|
1890
|
-
|
|
1972
|
+
if (clickInFlight.current) return;
|
|
1973
|
+
clickInFlight.current = true;
|
|
1974
|
+
setActionBusy(true);
|
|
1975
|
+
setActionError(null);
|
|
1976
|
+
try {
|
|
1977
|
+
const result = await pay({
|
|
1978
|
+
returnUrl,
|
|
1979
|
+
cancelUrl,
|
|
1980
|
+
...needsConsent && quote && accepted ? { subscription: { quote, consent: true } } : {}
|
|
1981
|
+
});
|
|
1982
|
+
if (result.kind === "subscription") await onSubscriptionPayment?.(result.subscription.payment);
|
|
1983
|
+
if (result.kind === "subscription_payment") await onSubscriptionPayment?.(result.payment);
|
|
1984
|
+
if (result.kind === "subscription" || result.kind === "subscription_payment") await refresh();
|
|
1985
|
+
if (result.kind === "order") onOrderPlaced?.(result.checkout);
|
|
1986
|
+
} catch (err) {
|
|
1987
|
+
setActionError(err instanceof Error ? err.message : String(err));
|
|
1988
|
+
} finally {
|
|
1989
|
+
clickInFlight.current = false;
|
|
1990
|
+
setActionBusy(false);
|
|
1991
|
+
}
|
|
1891
1992
|
}
|
|
1892
1993
|
if (fetching && methods == null) {
|
|
1893
1994
|
return /* @__PURE__ */ jsx(Section, { title: heading, children: /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.loadingPayment") }) });
|
|
@@ -1907,9 +2008,12 @@ function PaymentSection({
|
|
|
1907
2008
|
label: heading
|
|
1908
2009
|
}
|
|
1909
2010
|
),
|
|
2011
|
+
quote && needsConsent ? /* @__PURE__ */ jsx(SubscriptionTerms, { locale, quote, checked: accepted, onChange: (value) => setAcceptedDigest(value && quote ? quote.quote_digest : null) }) : null,
|
|
2012
|
+
checkout?.subscription?.error ? /* @__PURE__ */ jsx("p", { role: "alert", children: checkout.subscription.error.message }) : null,
|
|
2013
|
+
actionError ? /* @__PURE__ */ jsx("p", { role: "alert", children: actionError }) : null,
|
|
1910
2014
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1911
|
-
/* @__PURE__ */ jsx(Button, { onClick: placeOrder, busy: loading, disabled: selected == null, children: isGateway ? t("checkout.payNow") : t("checkout.placeOrder") }),
|
|
1912
|
-
isGateway ? /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
|
|
2015
|
+
/* @__PURE__ */ 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") }),
|
|
2016
|
+
isGateway && !checkout?.subscription ? /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
|
|
1913
2017
|
] })
|
|
1914
2018
|
] });
|
|
1915
2019
|
}
|
|
@@ -1997,6 +2101,7 @@ function reachedStep(checkout) {
|
|
|
1997
2101
|
}
|
|
1998
2102
|
function CheckoutFlow({
|
|
1999
2103
|
cartToken,
|
|
2104
|
+
onSubscriptionPayment,
|
|
2000
2105
|
onOrderPlaced,
|
|
2001
2106
|
emptyState,
|
|
2002
2107
|
renderConfirmation,
|
|
@@ -2006,19 +2111,19 @@ function CheckoutFlow({
|
|
|
2006
2111
|
}) {
|
|
2007
2112
|
const t = useT();
|
|
2008
2113
|
const { checkout, loading, create } = useCheckout();
|
|
2009
|
-
const [step, setStep] =
|
|
2010
|
-
const [bootstrapped, setBootstrapped] =
|
|
2011
|
-
|
|
2114
|
+
const [step, setStep] = React27.useState("address");
|
|
2115
|
+
const [bootstrapped, setBootstrapped] = React27.useState(false);
|
|
2116
|
+
React27.useEffect(() => {
|
|
2012
2117
|
if (checkout || !cartToken || bootstrapped) return;
|
|
2013
2118
|
setBootstrapped(true);
|
|
2014
2119
|
void create(cartToken).catch(() => {
|
|
2015
2120
|
});
|
|
2016
2121
|
}, [checkout, cartToken, bootstrapped, create]);
|
|
2017
2122
|
const serverStep = reachedStep(checkout);
|
|
2018
|
-
|
|
2123
|
+
React27.useEffect(() => {
|
|
2019
2124
|
setStep((cur) => rank(serverStep) > rank(cur) ? serverStep : cur);
|
|
2020
2125
|
}, [serverStep]);
|
|
2021
|
-
if (checkout?.order_id) {
|
|
2126
|
+
if (checkout?.order_id && (!checkout.subscription || checkout.phase === "success")) {
|
|
2022
2127
|
return /* @__PURE__ */ jsx(Shell, { summary: /* @__PURE__ */ jsx(OrderSummary, { locale }), children: renderConfirmation ? renderConfirmation(checkout) : /* @__PURE__ */ jsx(DefaultConfirmation, { checkout }) });
|
|
2023
2128
|
}
|
|
2024
2129
|
if (!checkout) {
|
|
@@ -2032,6 +2137,7 @@ function CheckoutFlow({
|
|
|
2032
2137
|
step === "address" ? /* @__PURE__ */ jsx(AddressForm, { onComplete: () => setStep("shipping") }) : step === "shipping" ? /* @__PURE__ */ jsx(ShippingPicker, { onComplete: () => setStep("payment"), locale }) : /* @__PURE__ */ jsx(
|
|
2033
2138
|
PaymentSection,
|
|
2034
2139
|
{
|
|
2140
|
+
onSubscriptionPayment,
|
|
2035
2141
|
returnUrl,
|
|
2036
2142
|
cancelUrl,
|
|
2037
2143
|
onOrderPlaced,
|
|
@@ -2136,10 +2242,10 @@ function DefaultConfirmation({ checkout }) {
|
|
|
2136
2242
|
}
|
|
2137
2243
|
function useOrderStatus(statusToken, initial) {
|
|
2138
2244
|
const client = useStorefront();
|
|
2139
|
-
const [order, setOrder] =
|
|
2140
|
-
const [loading, setLoading] =
|
|
2141
|
-
const [error, setError] =
|
|
2142
|
-
const load =
|
|
2245
|
+
const [order, setOrder] = React27.useState(initial ?? null);
|
|
2246
|
+
const [loading, setLoading] = React27.useState(false);
|
|
2247
|
+
const [error, setError] = React27.useState(null);
|
|
2248
|
+
const load = React27.useCallback(async () => {
|
|
2143
2249
|
if (!statusToken) return;
|
|
2144
2250
|
setLoading(true);
|
|
2145
2251
|
try {
|
|
@@ -2151,7 +2257,7 @@ function useOrderStatus(statusToken, initial) {
|
|
|
2151
2257
|
setLoading(false);
|
|
2152
2258
|
}
|
|
2153
2259
|
}, [client, statusToken]);
|
|
2154
|
-
|
|
2260
|
+
React27.useEffect(() => {
|
|
2155
2261
|
if (initial) return;
|
|
2156
2262
|
void load();
|
|
2157
2263
|
}, [load, initial]);
|
|
@@ -2159,12 +2265,12 @@ function useOrderStatus(statusToken, initial) {
|
|
|
2159
2265
|
}
|
|
2160
2266
|
function usePayment(token, initial) {
|
|
2161
2267
|
const client = useStorefront();
|
|
2162
|
-
const [payment, setPayment] =
|
|
2163
|
-
const [methods, setMethods] =
|
|
2164
|
-
const [loading, setLoading] =
|
|
2165
|
-
const [busy, setBusy] =
|
|
2166
|
-
const [error, setError] =
|
|
2167
|
-
const load =
|
|
2268
|
+
const [payment, setPayment] = React27.useState(initial ?? null);
|
|
2269
|
+
const [methods, setMethods] = React27.useState([]);
|
|
2270
|
+
const [loading, setLoading] = React27.useState(false);
|
|
2271
|
+
const [busy, setBusy] = React27.useState(false);
|
|
2272
|
+
const [error, setError] = React27.useState(null);
|
|
2273
|
+
const load = React27.useCallback(async () => {
|
|
2168
2274
|
if (!token) return;
|
|
2169
2275
|
setLoading(true);
|
|
2170
2276
|
try {
|
|
@@ -2181,7 +2287,7 @@ function usePayment(token, initial) {
|
|
|
2181
2287
|
setLoading(false);
|
|
2182
2288
|
}
|
|
2183
2289
|
}, [client, token]);
|
|
2184
|
-
|
|
2290
|
+
React27.useEffect(() => {
|
|
2185
2291
|
if (!token) return;
|
|
2186
2292
|
if (initial && initial.token === token) {
|
|
2187
2293
|
setPayment(initial);
|
|
@@ -2190,7 +2296,7 @@ function usePayment(token, initial) {
|
|
|
2190
2296
|
}
|
|
2191
2297
|
void load();
|
|
2192
2298
|
}, [token, load]);
|
|
2193
|
-
const selectMethod =
|
|
2299
|
+
const selectMethod = React27.useCallback(
|
|
2194
2300
|
async (method) => {
|
|
2195
2301
|
if (!token) return null;
|
|
2196
2302
|
setBusy(true);
|
|
@@ -2208,7 +2314,7 @@ function usePayment(token, initial) {
|
|
|
2208
2314
|
},
|
|
2209
2315
|
[client, token]
|
|
2210
2316
|
);
|
|
2211
|
-
const startPayment =
|
|
2317
|
+
const startPayment = React27.useCallback(
|
|
2212
2318
|
async (input) => {
|
|
2213
2319
|
if (!token) return null;
|
|
2214
2320
|
setBusy(true);
|
|
@@ -2645,8 +2751,8 @@ function Row4({
|
|
|
2645
2751
|
}
|
|
2646
2752
|
function CopyRow({ label, value }) {
|
|
2647
2753
|
const t = useT();
|
|
2648
|
-
const [copied, setCopied] =
|
|
2649
|
-
|
|
2754
|
+
const [copied, setCopied] = React27.useState(false);
|
|
2755
|
+
React27.useEffect(() => {
|
|
2650
2756
|
if (!copied) return;
|
|
2651
2757
|
const id = setTimeout(() => setCopied(false), 1800);
|
|
2652
2758
|
return () => clearTimeout(id);
|
|
@@ -2730,12 +2836,12 @@ function PaymentPage({
|
|
|
2730
2836
|
const t = useT();
|
|
2731
2837
|
const loc = useFormatLocale(locale);
|
|
2732
2838
|
const { payment, methods, loading, busy, error, reload, selectMethod, startPayment } = usePayment(token, initial);
|
|
2733
|
-
const [verifying, setVerifying] =
|
|
2734
|
-
|
|
2839
|
+
const [verifying, setVerifying] = React27.useState(false);
|
|
2840
|
+
React27.useEffect(() => {
|
|
2735
2841
|
if (token) setVerifying(consumeHandoff(token));
|
|
2736
2842
|
}, [token]);
|
|
2737
2843
|
const settled = payment != null && !payment.can_pay && payment.blocked_reason === "already_paid";
|
|
2738
|
-
|
|
2844
|
+
React27.useEffect(() => {
|
|
2739
2845
|
if (!verifying || !token || settled) return;
|
|
2740
2846
|
let attempts = 0;
|
|
2741
2847
|
const id = setInterval(() => {
|
|
@@ -2750,7 +2856,7 @@ function PaymentPage({
|
|
|
2750
2856
|
}, POLL_MS);
|
|
2751
2857
|
return () => clearInterval(id);
|
|
2752
2858
|
}, [verifying, token, settled, reload]);
|
|
2753
|
-
|
|
2859
|
+
React27.useEffect(() => {
|
|
2754
2860
|
if (!settled || !token || !payment) return;
|
|
2755
2861
|
setVerifying(false);
|
|
2756
2862
|
forgetHandoff(token);
|
|
@@ -2831,13 +2937,13 @@ function Amount({ label, value }) {
|
|
|
2831
2937
|
function Link({ href, children }) {
|
|
2832
2938
|
return /* @__PURE__ */ jsx("a", { href, style: { color: "var(--nv-brand)", fontSize: "0.875rem" }, children });
|
|
2833
2939
|
}
|
|
2834
|
-
var AccountContext =
|
|
2940
|
+
var AccountContext = React27.createContext(null);
|
|
2835
2941
|
function AccountProvider({ children }) {
|
|
2836
2942
|
const client = useStorefront();
|
|
2837
|
-
const [customer, setCustomer] =
|
|
2838
|
-
const [loading, setLoading] =
|
|
2839
|
-
const [error, setError] =
|
|
2840
|
-
const refresh =
|
|
2943
|
+
const [customer, setCustomer] = React27.useState(null);
|
|
2944
|
+
const [loading, setLoading] = React27.useState(true);
|
|
2945
|
+
const [error, setError] = React27.useState(false);
|
|
2946
|
+
const refresh = React27.useCallback(async () => {
|
|
2841
2947
|
setLoading(true);
|
|
2842
2948
|
setError(false);
|
|
2843
2949
|
try {
|
|
@@ -2848,10 +2954,10 @@ function AccountProvider({ children }) {
|
|
|
2848
2954
|
setLoading(false);
|
|
2849
2955
|
}
|
|
2850
2956
|
}, [client]);
|
|
2851
|
-
|
|
2957
|
+
React27.useEffect(() => {
|
|
2852
2958
|
void refresh();
|
|
2853
2959
|
}, [refresh]);
|
|
2854
|
-
const value =
|
|
2960
|
+
const value = React27.useMemo(
|
|
2855
2961
|
() => ({
|
|
2856
2962
|
customer,
|
|
2857
2963
|
signedIn: customer != null,
|
|
@@ -2874,7 +2980,7 @@ function AccountProvider({ children }) {
|
|
|
2874
2980
|
return /* @__PURE__ */ jsx(AccountContext.Provider, { value, children });
|
|
2875
2981
|
}
|
|
2876
2982
|
function useAccount() {
|
|
2877
|
-
const ctx =
|
|
2983
|
+
const ctx = React27.useContext(AccountContext);
|
|
2878
2984
|
if (!ctx) {
|
|
2879
2985
|
throw new Error("useAccount must be used inside <AccountProvider>.");
|
|
2880
2986
|
}
|
|
@@ -2882,9 +2988,9 @@ function useAccount() {
|
|
|
2882
2988
|
}
|
|
2883
2989
|
function useMagicLinkVerify(token) {
|
|
2884
2990
|
const { verify } = useAccount();
|
|
2885
|
-
const [status, setStatus] =
|
|
2886
|
-
const done =
|
|
2887
|
-
|
|
2991
|
+
const [status, setStatus] = React27.useState("idle");
|
|
2992
|
+
const done = React27.useRef(false);
|
|
2993
|
+
React27.useEffect(() => {
|
|
2888
2994
|
if (!token || done.current) return;
|
|
2889
2995
|
done.current = true;
|
|
2890
2996
|
setStatus("verifying");
|
|
@@ -2895,11 +3001,11 @@ function useMagicLinkVerify(token) {
|
|
|
2895
3001
|
function LoginForm({ verifyPath = "/account/verify", onSent }) {
|
|
2896
3002
|
const t = useT();
|
|
2897
3003
|
const { requestMagicLink } = useAccount();
|
|
2898
|
-
const [email, setEmail] =
|
|
2899
|
-
const [busy, setBusy] =
|
|
2900
|
-
const [sent, setSent] =
|
|
2901
|
-
const [error, setError] =
|
|
2902
|
-
const [devLink, setDevLink] =
|
|
3004
|
+
const [email, setEmail] = React27.useState("");
|
|
3005
|
+
const [busy, setBusy] = React27.useState(false);
|
|
3006
|
+
const [sent, setSent] = React27.useState(false);
|
|
3007
|
+
const [error, setError] = React27.useState(false);
|
|
3008
|
+
const [devLink, setDevLink] = React27.useState(null);
|
|
2903
3009
|
async function submit(e) {
|
|
2904
3010
|
e.preventDefault();
|
|
2905
3011
|
setBusy(true);
|
|
@@ -3011,14 +3117,14 @@ function OrderHistory({
|
|
|
3011
3117
|
const loc = useFormatLocale(locale);
|
|
3012
3118
|
const { signedIn } = useAccount();
|
|
3013
3119
|
const client = useStorefront();
|
|
3014
|
-
const [orders, setOrders] =
|
|
3015
|
-
const [error, setError] =
|
|
3016
|
-
const [nextCursor, setNextCursor] =
|
|
3017
|
-
const [pageLoading, setPageLoading] =
|
|
3018
|
-
const [reload, setReload] =
|
|
3019
|
-
const [reordering, setReordering] =
|
|
3020
|
-
const [notice, setNotice] =
|
|
3021
|
-
|
|
3120
|
+
const [orders, setOrders] = React27.useState(null);
|
|
3121
|
+
const [error, setError] = React27.useState(false);
|
|
3122
|
+
const [nextCursor, setNextCursor] = React27.useState(null);
|
|
3123
|
+
const [pageLoading, setPageLoading] = React27.useState(false);
|
|
3124
|
+
const [reload, setReload] = React27.useState(0);
|
|
3125
|
+
const [reordering, setReordering] = React27.useState(null);
|
|
3126
|
+
const [notice, setNotice] = React27.useState(null);
|
|
3127
|
+
React27.useEffect(() => {
|
|
3022
3128
|
let live = true;
|
|
3023
3129
|
if (!signedIn) {
|
|
3024
3130
|
setOrders([]);
|
|
@@ -3129,7 +3235,7 @@ function OrderHistory({
|
|
|
3129
3235
|
}
|
|
3130
3236
|
function AddressForm2({ initial, onSubmit, onCancel, busy }) {
|
|
3131
3237
|
const t = useT();
|
|
3132
|
-
const [form, setForm] =
|
|
3238
|
+
const [form, setForm] = React27.useState(() => ({
|
|
3133
3239
|
first_name: initial?.first_name ?? "",
|
|
3134
3240
|
last_name: initial?.last_name ?? "",
|
|
3135
3241
|
company: initial?.company ?? "",
|
|
@@ -3227,17 +3333,17 @@ function AddressBook() {
|
|
|
3227
3333
|
const t = useT();
|
|
3228
3334
|
const { signedIn } = useAccount();
|
|
3229
3335
|
const client = useStorefront();
|
|
3230
|
-
const [addresses, setAddresses] =
|
|
3231
|
-
const [mode, setMode] =
|
|
3232
|
-
const [busy, setBusy] =
|
|
3233
|
-
const load =
|
|
3336
|
+
const [addresses, setAddresses] = React27.useState(null);
|
|
3337
|
+
const [mode, setMode] = React27.useState({ kind: "list" });
|
|
3338
|
+
const [busy, setBusy] = React27.useState(false);
|
|
3339
|
+
const load = React27.useCallback(() => {
|
|
3234
3340
|
if (!signedIn) {
|
|
3235
3341
|
setAddresses([]);
|
|
3236
3342
|
return;
|
|
3237
3343
|
}
|
|
3238
3344
|
client.account.addresses().then(setAddresses).catch(() => setAddresses([]));
|
|
3239
3345
|
}, [signedIn, client]);
|
|
3240
|
-
|
|
3346
|
+
React27.useEffect(() => {
|
|
3241
3347
|
load();
|
|
3242
3348
|
}, [load]);
|
|
3243
3349
|
async function run(fn) {
|
|
@@ -3291,10 +3397,10 @@ function ProfileEditor() {
|
|
|
3291
3397
|
const t = useT();
|
|
3292
3398
|
const client = useStorefront();
|
|
3293
3399
|
const { customer, refresh } = useAccount();
|
|
3294
|
-
const [editing, setEditing] =
|
|
3295
|
-
const [busy, setBusy] =
|
|
3296
|
-
const [error, setError] =
|
|
3297
|
-
const [form, setForm] =
|
|
3400
|
+
const [editing, setEditing] = React27.useState(false);
|
|
3401
|
+
const [busy, setBusy] = React27.useState(false);
|
|
3402
|
+
const [error, setError] = React27.useState(false);
|
|
3403
|
+
const [form, setForm] = React27.useState({});
|
|
3298
3404
|
if (!customer) return null;
|
|
3299
3405
|
function startEdit() {
|
|
3300
3406
|
setForm({
|
|
@@ -3375,13 +3481,13 @@ function AccountSecurity() {
|
|
|
3375
3481
|
const client = useStorefront();
|
|
3376
3482
|
const account = useAccount();
|
|
3377
3483
|
const t = useT();
|
|
3378
|
-
const [sessions, setSessions] =
|
|
3379
|
-
const [email, setEmail] =
|
|
3380
|
-
const [busy, setBusy] =
|
|
3381
|
-
const [notice, setNotice] =
|
|
3382
|
-
const [confirmClose, setConfirmClose] =
|
|
3383
|
-
const [retry, setRetry] =
|
|
3384
|
-
|
|
3484
|
+
const [sessions, setSessions] = React27.useState(null);
|
|
3485
|
+
const [email, setEmail] = React27.useState("");
|
|
3486
|
+
const [busy, setBusy] = React27.useState(false);
|
|
3487
|
+
const [notice, setNotice] = React27.useState(null);
|
|
3488
|
+
const [confirmClose, setConfirmClose] = React27.useState(false);
|
|
3489
|
+
const [retry, setRetry] = React27.useState(0);
|
|
3490
|
+
React27.useEffect(() => {
|
|
3385
3491
|
let live = true;
|
|
3386
3492
|
if (account.signedIn) client.account.sessions().then((s) => {
|
|
3387
3493
|
if (live) setSessions(s);
|
|
@@ -3444,6 +3550,103 @@ function AccountSecurity() {
|
|
|
3444
3550
|
] }) : /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => setConfirmClose(true), children: t("account.closeAccount") })
|
|
3445
3551
|
] });
|
|
3446
3552
|
}
|
|
3553
|
+
function useCustomerSubscriptions() {
|
|
3554
|
+
const client = useStorefront();
|
|
3555
|
+
const { customer } = useAccount();
|
|
3556
|
+
const [dataOwner, setDataOwner] = React27.useState(void 0);
|
|
3557
|
+
const [subscriptions, setSubscriptions] = React27.useState([]);
|
|
3558
|
+
const [loading, setLoading] = React27.useState(false);
|
|
3559
|
+
const [error, setError] = React27.useState(null);
|
|
3560
|
+
const generation = React27.useRef(0);
|
|
3561
|
+
const owner = customer?.id;
|
|
3562
|
+
const api = client.account.subscriptions;
|
|
3563
|
+
const refresh = React27.useCallback(async () => {
|
|
3564
|
+
const current = ++generation.current;
|
|
3565
|
+
setError(null);
|
|
3566
|
+
if (!owner) {
|
|
3567
|
+
setSubscriptions([]);
|
|
3568
|
+
setLoading(false);
|
|
3569
|
+
return;
|
|
3570
|
+
}
|
|
3571
|
+
setLoading(true);
|
|
3572
|
+
try {
|
|
3573
|
+
const rows = await api.list();
|
|
3574
|
+
if (current === generation.current) {
|
|
3575
|
+
setSubscriptions(rows);
|
|
3576
|
+
setDataOwner(owner);
|
|
3577
|
+
setError(null);
|
|
3578
|
+
}
|
|
3579
|
+
} catch (err) {
|
|
3580
|
+
if (current === generation.current) setError(err instanceof Error ? err : new Error(String(err)));
|
|
3581
|
+
} finally {
|
|
3582
|
+
if (current === generation.current) setLoading(false);
|
|
3583
|
+
}
|
|
3584
|
+
}, [owner, api]);
|
|
3585
|
+
React27.useEffect(() => {
|
|
3586
|
+
setSubscriptions([]);
|
|
3587
|
+
void refresh();
|
|
3588
|
+
return () => {
|
|
3589
|
+
generation.current++;
|
|
3590
|
+
};
|
|
3591
|
+
}, [refresh]);
|
|
3592
|
+
return { subscriptions: dataOwner === owner ? subscriptions : [], loading, error, refresh, api };
|
|
3593
|
+
}
|
|
3594
|
+
function useCustomerSubscription(id) {
|
|
3595
|
+
const client = useStorefront();
|
|
3596
|
+
const { customer } = useAccount();
|
|
3597
|
+
const [dataKey, setDataKey] = React27.useState("");
|
|
3598
|
+
const [subscription, setSubscription] = React27.useState(null);
|
|
3599
|
+
const [loading, setLoading] = React27.useState(false);
|
|
3600
|
+
const [error, setError] = React27.useState(null);
|
|
3601
|
+
const generation = React27.useRef(0);
|
|
3602
|
+
const owner = customer?.id;
|
|
3603
|
+
const api = client.account.subscriptions;
|
|
3604
|
+
const refresh = React27.useCallback(async () => {
|
|
3605
|
+
const current = ++generation.current;
|
|
3606
|
+
setError(null);
|
|
3607
|
+
if (!owner || !id) {
|
|
3608
|
+
setSubscription(null);
|
|
3609
|
+
setLoading(false);
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
3612
|
+
setLoading(true);
|
|
3613
|
+
try {
|
|
3614
|
+
const row = await api.management(id);
|
|
3615
|
+
if (current === generation.current) {
|
|
3616
|
+
setSubscription(row);
|
|
3617
|
+
setDataKey(`${owner}:${id}`);
|
|
3618
|
+
setError(null);
|
|
3619
|
+
}
|
|
3620
|
+
} catch (err) {
|
|
3621
|
+
if (current === generation.current) setError(err instanceof Error ? err : new Error(String(err)));
|
|
3622
|
+
} finally {
|
|
3623
|
+
if (current === generation.current) setLoading(false);
|
|
3624
|
+
}
|
|
3625
|
+
}, [owner, id, api]);
|
|
3626
|
+
React27.useEffect(() => {
|
|
3627
|
+
setSubscription(null);
|
|
3628
|
+
void refresh();
|
|
3629
|
+
return () => {
|
|
3630
|
+
generation.current++;
|
|
3631
|
+
};
|
|
3632
|
+
}, [refresh]);
|
|
3633
|
+
return { subscription: dataKey === `${owner}:${id}` ? subscription : null, loading, error, refresh, api };
|
|
3634
|
+
}
|
|
3635
|
+
function SubscriptionList({ href, locale }) {
|
|
3636
|
+
const t = useT();
|
|
3637
|
+
const { subscriptions, loading, error } = useCustomerSubscriptions();
|
|
3638
|
+
return /* @__PURE__ */ jsxs("section", { style: { fontFamily: "var(--nv-font)", color: "var(--nv-fg)" }, children: [
|
|
3639
|
+
/* @__PURE__ */ jsx("h2", { style: { fontSize: "1.125rem", fontWeight: 500 }, children: t("subscriptions.title") }),
|
|
3640
|
+
loading ? /* @__PURE__ */ jsx("p", { children: t("checkout.loading") }) : error ? /* @__PURE__ */ jsx("p", { role: "alert", children: error.message }) : subscriptions.length === 0 ? /* @__PURE__ */ jsx("p", { children: t("subscriptions.empty") }) : /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: subscriptions.map((sub) => /* @__PURE__ */ jsx("li", { style: { borderBottom: "1px solid var(--nv-border)", padding: "0.8rem 0" }, children: /* @__PURE__ */ jsxs("a", { href: href(sub.id), style: { color: "inherit", display: "flex", justifyContent: "space-between", gap: "1rem", flexWrap: "wrap" }, children: [
|
|
3641
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
3642
|
+
"#",
|
|
3643
|
+
sub.id
|
|
3644
|
+
] }),
|
|
3645
|
+
/* @__PURE__ */ jsx("span", { children: t(`subscriptions.status.${sub.status}`) }),
|
|
3646
|
+
sub.status === "active" && sub.next_at ? /* @__PURE__ */ jsx("time", { dateTime: sub.next_at, children: new Date(sub.next_at).toLocaleDateString(locale) }) : null
|
|
3647
|
+
] }) }, sub.id)) })
|
|
3648
|
+
] });
|
|
3649
|
+
}
|
|
3447
3650
|
function serializeJsonLd(data) {
|
|
3448
3651
|
return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
3449
3652
|
}
|
|
@@ -3548,6 +3751,6 @@ function OrganizationJsonLd({
|
|
|
3548
3751
|
return /* @__PURE__ */ jsx(JsonLd, { data });
|
|
3549
3752
|
}
|
|
3550
3753
|
|
|
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 };
|
|
3754
|
+
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, PurchaseOptions, ShippingPicker, SortSelect, StatusBanner, StockBadge, SubscriptionList, SubscriptionTerms, TagFilter, ThemeProvider, TransferInstructions, VariantPicker, absoluteUrl, bundledMessages, cs, en, formatCents, formatMoney, isTerminal, serializeJsonLd, stripHtml, themeToCssVars, useAccount, useCart, useCheckout, useCustomerSubscription, useCustomerSubscriptions, useI18n, useMagicLinkVerify, useOrderStatus, usePayment, useStorefront, useStorefrontOptional, useT };
|
|
3552
3755
|
//# sourceMappingURL=index.js.map
|
|
3553
3756
|
//# sourceMappingURL=index.js.map
|