@nevios/storefront-kit 1.2.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 +480 -126
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -7
- package/dist/index.d.ts +133 -7
- package/dist/index.js +475 -127
- 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
|
-
import { imageSrcSet } from '@nevios/storefront-js';
|
|
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",
|
|
@@ -226,6 +236,20 @@ var en = {
|
|
|
226
236
|
"account.signOut": "Sign out",
|
|
227
237
|
"account.greeting": "Signed in as {email}",
|
|
228
238
|
"account.orders": "Your orders",
|
|
239
|
+
"account.loadError": "Could not load your account. Please try again.",
|
|
240
|
+
"account.retry": "Try again",
|
|
241
|
+
"account.loadMore": "Load more",
|
|
242
|
+
"account.signInError": "Could not request a sign-in link. Please try again shortly.",
|
|
243
|
+
"account.security": "Account security",
|
|
244
|
+
"account.newEmail": "New email",
|
|
245
|
+
"account.changeEmail": "Verify new email",
|
|
246
|
+
"account.recentLogin": "Sign in again by email before making this change.",
|
|
247
|
+
"account.sessions": "Sessions",
|
|
248
|
+
"account.currentSession": "Current session",
|
|
249
|
+
"account.revokeSession": "Sign out",
|
|
250
|
+
"account.closeAccount": "Close account",
|
|
251
|
+
"account.closeWarning": "Closing your account signs out all devices and disables account access. Orders and invoices are retained. Contact the shop to restore access.",
|
|
252
|
+
"account.confirmClose": "Confirm account closure",
|
|
229
253
|
"account.noOrders": "No orders yet.",
|
|
230
254
|
"account.addresses": "Saved addresses",
|
|
231
255
|
"account.noAddresses": "No saved addresses yet.",
|
|
@@ -264,6 +288,16 @@ var en = {
|
|
|
264
288
|
|
|
265
289
|
// src/i18n/cs.ts
|
|
266
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",
|
|
267
301
|
"common.free": "Zdarma",
|
|
268
302
|
"common.close": "Zav\u0159\xEDt",
|
|
269
303
|
"common.loadingShort": "\u2026",
|
|
@@ -407,6 +441,20 @@ var cs = {
|
|
|
407
441
|
"account.signOut": "Odhl\xE1sit se",
|
|
408
442
|
"account.greeting": "P\u0159ihl\xE1\u0161en jako {email}",
|
|
409
443
|
"account.orders": "Va\u0161e objedn\xE1vky",
|
|
444
|
+
"account.loadError": "\xDAdaje se nepoda\u0159ilo na\u010D\xEDst. Zkuste to znovu.",
|
|
445
|
+
"account.retry": "Zkusit znovu",
|
|
446
|
+
"account.loadMore": "Na\u010D\xEDst dal\u0161\xED",
|
|
447
|
+
"account.signInError": "P\u0159ihl\xE1\u0161en\xED se nepoda\u0159ilo zah\xE1jit. Zkuste to za chv\xEDli znovu.",
|
|
448
|
+
"account.security": "Zabezpe\u010Den\xED \xFA\u010Dtu",
|
|
449
|
+
"account.newEmail": "Nov\xFD e-mail",
|
|
450
|
+
"account.changeEmail": "Ov\u011B\u0159it nov\xFD e-mail",
|
|
451
|
+
"account.recentLogin": "Pro tuto zm\u011Bnu se nejprve znovu p\u0159ihlaste p\u0159es e-mail.",
|
|
452
|
+
"account.sessions": "P\u0159ihl\xE1\u0161en\xED",
|
|
453
|
+
"account.currentSession": "Toto p\u0159ihl\xE1\u0161en\xED",
|
|
454
|
+
"account.revokeSession": "Odhl\xE1sit",
|
|
455
|
+
"account.closeAccount": "Uzav\u0159\xEDt \xFA\u010Det",
|
|
456
|
+
"account.closeWarning": "Uzav\u0159en\xEDm \xFA\u010Dtu ukon\u010D\xEDte p\u0159\xEDstup a odhl\xE1s\xEDte v\u0161echna za\u0159\xEDzen\xED. Objedn\xE1vky a doklady z\u016Fstanou zachov\xE1ny. Obnoven\xED p\u0159\xEDstupu \u0159e\u0161\xED podpora obchodu.",
|
|
457
|
+
"account.confirmClose": "Potvrdit uzav\u0159en\xED \xFA\u010Dtu",
|
|
410
458
|
"account.noOrders": "Zat\xEDm \u017E\xE1dn\xE9 objedn\xE1vky.",
|
|
411
459
|
"account.addresses": "Ulo\u017Een\xE9 adresy",
|
|
412
460
|
"account.noAddresses": "Zat\xEDm \u017E\xE1dn\xE9 ulo\u017Een\xE9 adresy.",
|
|
@@ -491,13 +539,13 @@ function makeI18n(locale, messages) {
|
|
|
491
539
|
return { locale, messages, t };
|
|
492
540
|
}
|
|
493
541
|
var DEFAULT_I18N = makeI18n("en", en);
|
|
494
|
-
var I18nContext =
|
|
542
|
+
var I18nContext = React27.createContext(null);
|
|
495
543
|
function I18nProvider({
|
|
496
544
|
locale,
|
|
497
545
|
messages,
|
|
498
546
|
children
|
|
499
547
|
}) {
|
|
500
|
-
const value =
|
|
548
|
+
const value = React27.useMemo(() => {
|
|
501
549
|
const lang = langOf(locale);
|
|
502
550
|
if (locale && !(lang in BUNDLED) && !messages) {
|
|
503
551
|
devWarn(
|
|
@@ -511,7 +559,7 @@ function I18nProvider({
|
|
|
511
559
|
return /* @__PURE__ */ jsx(I18nContext.Provider, { value, children });
|
|
512
560
|
}
|
|
513
561
|
function useI18n() {
|
|
514
|
-
return
|
|
562
|
+
return React27.useContext(I18nContext) ?? DEFAULT_I18N;
|
|
515
563
|
}
|
|
516
564
|
function useT() {
|
|
517
565
|
return useI18n().t;
|
|
@@ -635,7 +683,7 @@ function ProductCard({ product, href, locale }) {
|
|
|
635
683
|
var MAIN_WIDTHS = [600, 800, 1e3];
|
|
636
684
|
var THUMB_WIDTHS = [56, 112];
|
|
637
685
|
function ProductGallery({ media, alt }) {
|
|
638
|
-
const [active, setActive] =
|
|
686
|
+
const [active, setActive] = React27.useState(0);
|
|
639
687
|
const imageBase = useStorefrontOptional()?.imageBase;
|
|
640
688
|
if (media.length === 0) {
|
|
641
689
|
return /* @__PURE__ */ jsx(
|
|
@@ -723,10 +771,10 @@ function matchVariant(product, selected) {
|
|
|
723
771
|
) ?? null;
|
|
724
772
|
}
|
|
725
773
|
function VariantPicker({ product, onChange }) {
|
|
726
|
-
const [selected, setSelected] =
|
|
774
|
+
const [selected, setSelected] = React27.useState(
|
|
727
775
|
() => product.variants[0]?.options ?? []
|
|
728
776
|
);
|
|
729
|
-
|
|
777
|
+
React27.useEffect(() => {
|
|
730
778
|
onChange?.(matchVariant(product, selected));
|
|
731
779
|
}, [product, selected]);
|
|
732
780
|
if (product.options.length === 0) return null;
|
|
@@ -767,8 +815,8 @@ function BackInStockForm({
|
|
|
767
815
|
}) {
|
|
768
816
|
const t = useT();
|
|
769
817
|
const client = useStorefront();
|
|
770
|
-
const [email, setEmail] =
|
|
771
|
-
const [state, setState] =
|
|
818
|
+
const [email, setEmail] = React27.useState(defaultEmail);
|
|
819
|
+
const [state, setState] = React27.useState("idle");
|
|
772
820
|
if (!variant || variant.availability?.status !== "out_of_stock") return null;
|
|
773
821
|
async function submit(e) {
|
|
774
822
|
e.preventDefault();
|
|
@@ -868,7 +916,7 @@ function ProductDetailPage({
|
|
|
868
916
|
locale,
|
|
869
917
|
addToCart
|
|
870
918
|
}) {
|
|
871
|
-
const [variant, setVariant] =
|
|
919
|
+
const [variant, setVariant] = React27.useState(
|
|
872
920
|
product.variants[0] ?? null
|
|
873
921
|
);
|
|
874
922
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1100,12 +1148,26 @@ function TagFilter({ tags, selected = [], onChange }) {
|
|
|
1100
1148
|
}) })
|
|
1101
1149
|
] });
|
|
1102
1150
|
}
|
|
1103
|
-
|
|
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);
|
|
1104
1166
|
function CartProvider({ children }) {
|
|
1105
1167
|
const client = useStorefront();
|
|
1106
|
-
const [cart, setCart] =
|
|
1107
|
-
const [loading, setLoading] =
|
|
1108
|
-
const run =
|
|
1168
|
+
const [cart, setCart] = React27.useState(null);
|
|
1169
|
+
const [loading, setLoading] = React27.useState(false);
|
|
1170
|
+
const run = React27.useCallback(
|
|
1109
1171
|
async (op) => {
|
|
1110
1172
|
setLoading(true);
|
|
1111
1173
|
try {
|
|
@@ -1118,7 +1180,7 @@ function CartProvider({ children }) {
|
|
|
1118
1180
|
},
|
|
1119
1181
|
[]
|
|
1120
1182
|
);
|
|
1121
|
-
const refresh =
|
|
1183
|
+
const refresh = React27.useCallback(async () => {
|
|
1122
1184
|
setLoading(true);
|
|
1123
1185
|
try {
|
|
1124
1186
|
setCart(await client.cart.get());
|
|
@@ -1126,16 +1188,17 @@ function CartProvider({ children }) {
|
|
|
1126
1188
|
setLoading(false);
|
|
1127
1189
|
}
|
|
1128
1190
|
}, [client]);
|
|
1129
|
-
|
|
1191
|
+
React27.useEffect(() => {
|
|
1130
1192
|
void refresh();
|
|
1131
1193
|
}, [refresh]);
|
|
1132
|
-
const value =
|
|
1194
|
+
const value = React27.useMemo(
|
|
1133
1195
|
() => ({
|
|
1134
1196
|
cart,
|
|
1135
1197
|
loading,
|
|
1136
1198
|
itemCount: cart?.totals.item_count ?? 0,
|
|
1137
1199
|
addLine: (input) => run(() => client.cart.addLine(input)),
|
|
1138
1200
|
updateLine: (lineId, quantity) => run(() => client.cart.updateLine(lineId, quantity)),
|
|
1201
|
+
clearSubscription: () => run(() => client.cart.clearSubscription()),
|
|
1139
1202
|
removeLine: (lineId) => run(() => client.cart.removeLine(lineId)),
|
|
1140
1203
|
refresh
|
|
1141
1204
|
}),
|
|
@@ -1144,7 +1207,7 @@ function CartProvider({ children }) {
|
|
|
1144
1207
|
return /* @__PURE__ */ jsx(CartContext.Provider, { value, children });
|
|
1145
1208
|
}
|
|
1146
1209
|
function useCart() {
|
|
1147
|
-
const ctx =
|
|
1210
|
+
const ctx = React27.useContext(CartContext);
|
|
1148
1211
|
if (!ctx) {
|
|
1149
1212
|
throw new Error("useCart must be used inside <CartProvider>.");
|
|
1150
1213
|
}
|
|
@@ -1153,13 +1216,14 @@ function useCart() {
|
|
|
1153
1216
|
function AddToCart({
|
|
1154
1217
|
variant,
|
|
1155
1218
|
quantity = 1,
|
|
1219
|
+
subscriptionPlanId,
|
|
1156
1220
|
children,
|
|
1157
1221
|
onAdded
|
|
1158
1222
|
}) {
|
|
1159
1223
|
const t = useT();
|
|
1160
1224
|
const { addLine } = useCart();
|
|
1161
|
-
const [busy, setBusy] =
|
|
1162
|
-
const [error, setError] =
|
|
1225
|
+
const [busy, setBusy] = React27.useState(false);
|
|
1226
|
+
const [error, setError] = React27.useState(null);
|
|
1163
1227
|
const disabled = variant == null || busy;
|
|
1164
1228
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
1165
1229
|
/* @__PURE__ */ jsx(
|
|
@@ -1172,7 +1236,7 @@ function AddToCart({
|
|
|
1172
1236
|
setBusy(true);
|
|
1173
1237
|
setError(null);
|
|
1174
1238
|
try {
|
|
1175
|
-
await addLine({ variant_id: variant.id, quantity });
|
|
1239
|
+
await addLine({ variant_id: variant.id, quantity, ...subscriptionPlanId ? { subscription_plan_id: subscriptionPlanId } : {} });
|
|
1176
1240
|
onAdded?.();
|
|
1177
1241
|
} catch (err) {
|
|
1178
1242
|
const code = err?.code;
|
|
@@ -1201,7 +1265,7 @@ function AddToCart({
|
|
|
1201
1265
|
function CartLineItem({ line, locale }) {
|
|
1202
1266
|
const t = useT();
|
|
1203
1267
|
const loc = useFormatLocale(locale);
|
|
1204
|
-
const { updateLine, removeLine } = useCart();
|
|
1268
|
+
const { cart, updateLine, removeLine } = useCart();
|
|
1205
1269
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.85rem", alignItems: "flex-start", padding: "0.75rem 0" }, children: [
|
|
1206
1270
|
/* @__PURE__ */ jsx(
|
|
1207
1271
|
"div",
|
|
@@ -1230,6 +1294,7 @@ function CartLineItem({ line, locale }) {
|
|
|
1230
1294
|
/* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1231
1295
|
/* @__PURE__ */ jsx("div", { style: { fontWeight: 500, lineHeight: 1.3 }, children: line.title }),
|
|
1232
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,
|
|
1233
1298
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem", marginTop: "0.5rem" }, children: [
|
|
1234
1299
|
/* @__PURE__ */ jsxs(
|
|
1235
1300
|
"div",
|
|
@@ -1308,7 +1373,7 @@ function CartSummary({
|
|
|
1308
1373
|
const { cart } = useCart();
|
|
1309
1374
|
if (cart == null) return null;
|
|
1310
1375
|
const t = cart.totals;
|
|
1311
|
-
const empty = t.item_count === 0;
|
|
1376
|
+
const empty = t.item_count === 0 || cart.subscription?.available === false;
|
|
1312
1377
|
const cta = checkoutLabel ?? tr("cart.checkout");
|
|
1313
1378
|
const ctaStyle = {
|
|
1314
1379
|
display: "block",
|
|
@@ -1329,6 +1394,7 @@ function CartSummary({
|
|
|
1329
1394
|
t.tax_cents > 0 ? /* @__PURE__ */ jsx(Row, { label: tr("cart.tax"), value: formatCents(t.tax_cents, t.currency, loc), muted: true }) : null,
|
|
1330
1395
|
/* @__PURE__ */ jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
|
|
1331
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,
|
|
1332
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 })
|
|
1333
1399
|
] });
|
|
1334
1400
|
}
|
|
@@ -1435,15 +1501,15 @@ function CartDrawer({
|
|
|
1435
1501
|
}
|
|
1436
1502
|
);
|
|
1437
1503
|
}
|
|
1438
|
-
var CheckoutContext =
|
|
1504
|
+
var CheckoutContext = React27.createContext(null);
|
|
1439
1505
|
function CheckoutProvider({
|
|
1440
1506
|
children
|
|
1441
1507
|
}) {
|
|
1442
1508
|
const client = useStorefront();
|
|
1443
|
-
const [checkout, setCheckout] =
|
|
1444
|
-
const [loading, setLoading] =
|
|
1445
|
-
const [error, setError] =
|
|
1446
|
-
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(
|
|
1447
1513
|
async (op, store) => {
|
|
1448
1514
|
setLoading(true);
|
|
1449
1515
|
try {
|
|
@@ -1461,23 +1527,36 @@ function CheckoutProvider({
|
|
|
1461
1527
|
},
|
|
1462
1528
|
[]
|
|
1463
1529
|
);
|
|
1464
|
-
const refresh =
|
|
1530
|
+
const refresh = React27.useCallback(async () => {
|
|
1465
1531
|
setLoading(true);
|
|
1466
1532
|
try {
|
|
1467
1533
|
setCheckout(await client.checkout.get());
|
|
1468
1534
|
setError(null);
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1469
1537
|
} finally {
|
|
1470
1538
|
setLoading(false);
|
|
1471
1539
|
}
|
|
1472
1540
|
}, [client]);
|
|
1473
|
-
|
|
1541
|
+
React27.useEffect(() => {
|
|
1474
1542
|
void refresh();
|
|
1475
1543
|
}, [refresh]);
|
|
1476
|
-
const pay =
|
|
1544
|
+
const pay = React27.useCallback(
|
|
1477
1545
|
async (opts = {}) => {
|
|
1478
1546
|
const co = checkout;
|
|
1479
1547
|
if (!co) throw new Error("pay() called before a checkout exists");
|
|
1480
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
|
+
}
|
|
1481
1560
|
if (co.payment_category === "gateway") {
|
|
1482
1561
|
const start = await run(
|
|
1483
1562
|
() => client.checkout.startPayment({ return_url: opts.returnUrl, cancel_url: opts.cancelUrl }),
|
|
@@ -1496,11 +1575,18 @@ function CheckoutProvider({
|
|
|
1496
1575
|
},
|
|
1497
1576
|
[checkout, client, run]
|
|
1498
1577
|
);
|
|
1499
|
-
const value =
|
|
1578
|
+
const value = React27.useMemo(
|
|
1500
1579
|
() => ({
|
|
1501
1580
|
checkout,
|
|
1502
1581
|
loading,
|
|
1503
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),
|
|
1504
1590
|
create: (cartToken, input) => run(() => client.checkout.create(cartToken, input), (r) => r),
|
|
1505
1591
|
setAddress: (input) => run(() => client.checkout.setAddress(input), (r) => r),
|
|
1506
1592
|
shippingQuotes: () => run(() => client.checkout.shippingQuotes(), () => null),
|
|
@@ -1521,7 +1607,7 @@ function CheckoutProvider({
|
|
|
1521
1607
|
return /* @__PURE__ */ jsx(CheckoutContext.Provider, { value, children });
|
|
1522
1608
|
}
|
|
1523
1609
|
function useCheckout() {
|
|
1524
|
-
const ctx =
|
|
1610
|
+
const ctx = React27.useContext(CheckoutContext);
|
|
1525
1611
|
if (!ctx) {
|
|
1526
1612
|
throw new Error("useCheckout must be used inside <CheckoutProvider>.");
|
|
1527
1613
|
}
|
|
@@ -1680,10 +1766,25 @@ function AddressForm({
|
|
|
1680
1766
|
}) {
|
|
1681
1767
|
const t = useT();
|
|
1682
1768
|
const { checkout, setAddress, loading } = useCheckout();
|
|
1683
|
-
const [form, setForm] =
|
|
1769
|
+
const [form, setForm] = React27.useState(
|
|
1684
1770
|
() => seed(checkout?.email ?? null, checkout?.shipping_address ?? null)
|
|
1685
1771
|
);
|
|
1686
|
-
const
|
|
1772
|
+
const touched = React27.useRef(/* @__PURE__ */ new Set());
|
|
1773
|
+
React27.useEffect(() => {
|
|
1774
|
+
const prefill = checkout?.customer_prefill;
|
|
1775
|
+
const addresses = prefill?.addresses.filter((a) => !a.is_pickup);
|
|
1776
|
+
const saved = addresses?.find((a) => a.is_default) ?? addresses?.[0];
|
|
1777
|
+
const address = checkout?.shipping_address ?? (saved ? { ...saved, postal_code: saved.zip } : null);
|
|
1778
|
+
const defaults = seed(checkout?.email ?? prefill?.email ?? null, address);
|
|
1779
|
+
defaults.first_name || (defaults.first_name = prefill?.first_name ?? "");
|
|
1780
|
+
defaults.last_name || (defaults.last_name = prefill?.last_name ?? "");
|
|
1781
|
+
defaults.phone || (defaults.phone = prefill?.phone ?? "");
|
|
1782
|
+
setForm((current) => Object.fromEntries(Object.entries(current).map(([key, value]) => [key, touched.current.has(key) ? value : value || defaults[key]])));
|
|
1783
|
+
}, [checkout?.token]);
|
|
1784
|
+
const set = (key) => (e) => {
|
|
1785
|
+
touched.current.add(key);
|
|
1786
|
+
setForm((f) => ({ ...f, [key]: e.target.value }));
|
|
1787
|
+
};
|
|
1687
1788
|
async function submit(e) {
|
|
1688
1789
|
e.preventDefault();
|
|
1689
1790
|
const { email, ...rest } = form;
|
|
@@ -1734,11 +1835,11 @@ function ShippingPicker({
|
|
|
1734
1835
|
const loc = useFormatLocale(locale);
|
|
1735
1836
|
const { checkout, shippingQuotes, setShippingMethod, loading } = useCheckout();
|
|
1736
1837
|
const heading = title ?? t("checkout.shippingMethod");
|
|
1737
|
-
const [quotes, setQuotes] =
|
|
1738
|
-
const [selected, setSelected] =
|
|
1739
|
-
const [fetching, setFetching] =
|
|
1838
|
+
const [quotes, setQuotes] = React27.useState(null);
|
|
1839
|
+
const [selected, setSelected] = React27.useState(null);
|
|
1840
|
+
const [fetching, setFetching] = React27.useState(true);
|
|
1740
1841
|
const addressKey = checkout?.shipping_address ? `${checkout.shipping_address.country_code}:${checkout.shipping_address.postal_code}` : null;
|
|
1741
|
-
|
|
1842
|
+
React27.useEffect(() => {
|
|
1742
1843
|
let live = true;
|
|
1743
1844
|
setFetching(true);
|
|
1744
1845
|
shippingQuotes().then((qs) => {
|
|
@@ -1766,17 +1867,13 @@ function ShippingPicker({
|
|
|
1766
1867
|
}
|
|
1767
1868
|
return /* @__PURE__ */ jsxs(Section, { title: heading, children: [
|
|
1768
1869
|
/* @__PURE__ */ jsx("div", { role: "radiogroup", style: { display: "flex", flexDirection: "column", gap: "0.6rem" }, children: quotes.map((q) => {
|
|
1769
|
-
const days = estDays(q, t);
|
|
1770
1870
|
return /* @__PURE__ */ jsx(
|
|
1771
1871
|
SelectRow,
|
|
1772
1872
|
{
|
|
1773
1873
|
selected: selected === q.rate_id,
|
|
1774
1874
|
onSelect: () => setSelected(q.rate_id),
|
|
1775
1875
|
trailing: q.is_free ? t("common.free") : formatCents(q.price_cents, q.currency, loc),
|
|
1776
|
-
children: /* @__PURE__ */
|
|
1777
|
-
/* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: q.label }),
|
|
1778
|
-
days ? /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: days }) : null
|
|
1779
|
-
] })
|
|
1876
|
+
children: /* @__PURE__ */ jsx("span", { style: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: q.label }) })
|
|
1780
1877
|
},
|
|
1781
1878
|
q.rate_id
|
|
1782
1879
|
);
|
|
@@ -1784,15 +1881,6 @@ function ShippingPicker({
|
|
|
1784
1881
|
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { onClick: submit, busy: loading, disabled: selected == null, children: submitLabel ?? t("checkout.continueToPayment") }) })
|
|
1785
1882
|
] });
|
|
1786
1883
|
}
|
|
1787
|
-
function estDays(q, t) {
|
|
1788
|
-
const { estimated_days_min: lo, estimated_days_max: hi } = q;
|
|
1789
|
-
if (lo == null && hi == null) return null;
|
|
1790
|
-
if (lo != null && hi != null) {
|
|
1791
|
-
return lo === hi ? t("shipping.days", { count: lo }) : t("shipping.daysRange", { min: lo, max: hi });
|
|
1792
|
-
}
|
|
1793
|
-
const n = lo ?? hi;
|
|
1794
|
-
return t("shipping.days", { count: n });
|
|
1795
|
-
}
|
|
1796
1884
|
function PaymentMethodPicker({
|
|
1797
1885
|
methods,
|
|
1798
1886
|
value,
|
|
@@ -1826,20 +1914,45 @@ function PaymentMethodPicker({
|
|
|
1826
1914
|
}
|
|
1827
1915
|
);
|
|
1828
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
|
+
}
|
|
1829
1934
|
function PaymentSection({
|
|
1830
1935
|
title,
|
|
1831
1936
|
returnUrl,
|
|
1832
1937
|
cancelUrl,
|
|
1833
1938
|
onOrderPlaced,
|
|
1834
|
-
locale
|
|
1939
|
+
locale,
|
|
1940
|
+
onSubscriptionPayment
|
|
1835
1941
|
}) {
|
|
1836
1942
|
const t = useT();
|
|
1837
1943
|
const heading = title ?? t("checkout.payment");
|
|
1838
|
-
const { checkout, paymentMethods, setPaymentMethod, pay, loading } = useCheckout();
|
|
1839
|
-
const [methods, setMethods] =
|
|
1840
|
-
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);
|
|
1841
1947
|
const selected = checkout?.payment_method ?? null;
|
|
1842
|
-
|
|
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(() => {
|
|
1843
1956
|
let live = true;
|
|
1844
1957
|
setFetching(true);
|
|
1845
1958
|
paymentMethods().then((ms) => {
|
|
@@ -1852,12 +1965,30 @@ function PaymentSection({
|
|
|
1852
1965
|
return () => {
|
|
1853
1966
|
live = false;
|
|
1854
1967
|
};
|
|
1855
|
-
}, []);
|
|
1968
|
+
}, [checkout?.token, checkout?.shipping_method_id, checkout?.subscription?.selection.plan_id]);
|
|
1856
1969
|
const selectedMethod = methods?.find((m) => m.method === selected) ?? null;
|
|
1857
1970
|
const isGateway = selectedMethod?.category === "gateway";
|
|
1858
1971
|
async function placeOrder() {
|
|
1859
|
-
|
|
1860
|
-
|
|
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
|
+
}
|
|
1861
1992
|
}
|
|
1862
1993
|
if (fetching && methods == null) {
|
|
1863
1994
|
return /* @__PURE__ */ jsx(Section, { title: heading, children: /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.loadingPayment") }) });
|
|
@@ -1877,9 +2008,12 @@ function PaymentSection({
|
|
|
1877
2008
|
label: heading
|
|
1878
2009
|
}
|
|
1879
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,
|
|
1880
2014
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1881
|
-
/* @__PURE__ */ jsx(Button, { onClick: placeOrder, busy: loading, disabled: selected == null, children: isGateway ? t("checkout.payNow") : t("checkout.placeOrder") }),
|
|
1882
|
-
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
|
|
1883
2017
|
] })
|
|
1884
2018
|
] });
|
|
1885
2019
|
}
|
|
@@ -1967,6 +2101,7 @@ function reachedStep(checkout) {
|
|
|
1967
2101
|
}
|
|
1968
2102
|
function CheckoutFlow({
|
|
1969
2103
|
cartToken,
|
|
2104
|
+
onSubscriptionPayment,
|
|
1970
2105
|
onOrderPlaced,
|
|
1971
2106
|
emptyState,
|
|
1972
2107
|
renderConfirmation,
|
|
@@ -1976,19 +2111,19 @@ function CheckoutFlow({
|
|
|
1976
2111
|
}) {
|
|
1977
2112
|
const t = useT();
|
|
1978
2113
|
const { checkout, loading, create } = useCheckout();
|
|
1979
|
-
const [step, setStep] =
|
|
1980
|
-
const [bootstrapped, setBootstrapped] =
|
|
1981
|
-
|
|
2114
|
+
const [step, setStep] = React27.useState("address");
|
|
2115
|
+
const [bootstrapped, setBootstrapped] = React27.useState(false);
|
|
2116
|
+
React27.useEffect(() => {
|
|
1982
2117
|
if (checkout || !cartToken || bootstrapped) return;
|
|
1983
2118
|
setBootstrapped(true);
|
|
1984
2119
|
void create(cartToken).catch(() => {
|
|
1985
2120
|
});
|
|
1986
2121
|
}, [checkout, cartToken, bootstrapped, create]);
|
|
1987
2122
|
const serverStep = reachedStep(checkout);
|
|
1988
|
-
|
|
2123
|
+
React27.useEffect(() => {
|
|
1989
2124
|
setStep((cur) => rank(serverStep) > rank(cur) ? serverStep : cur);
|
|
1990
2125
|
}, [serverStep]);
|
|
1991
|
-
if (checkout?.order_id) {
|
|
2126
|
+
if (checkout?.order_id && (!checkout.subscription || checkout.phase === "success")) {
|
|
1992
2127
|
return /* @__PURE__ */ jsx(Shell, { summary: /* @__PURE__ */ jsx(OrderSummary, { locale }), children: renderConfirmation ? renderConfirmation(checkout) : /* @__PURE__ */ jsx(DefaultConfirmation, { checkout }) });
|
|
1993
2128
|
}
|
|
1994
2129
|
if (!checkout) {
|
|
@@ -2002,6 +2137,7 @@ function CheckoutFlow({
|
|
|
2002
2137
|
step === "address" ? /* @__PURE__ */ jsx(AddressForm, { onComplete: () => setStep("shipping") }) : step === "shipping" ? /* @__PURE__ */ jsx(ShippingPicker, { onComplete: () => setStep("payment"), locale }) : /* @__PURE__ */ jsx(
|
|
2003
2138
|
PaymentSection,
|
|
2004
2139
|
{
|
|
2140
|
+
onSubscriptionPayment,
|
|
2005
2141
|
returnUrl,
|
|
2006
2142
|
cancelUrl,
|
|
2007
2143
|
onOrderPlaced,
|
|
@@ -2106,10 +2242,10 @@ function DefaultConfirmation({ checkout }) {
|
|
|
2106
2242
|
}
|
|
2107
2243
|
function useOrderStatus(statusToken, initial) {
|
|
2108
2244
|
const client = useStorefront();
|
|
2109
|
-
const [order, setOrder] =
|
|
2110
|
-
const [loading, setLoading] =
|
|
2111
|
-
const [error, setError] =
|
|
2112
|
-
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 () => {
|
|
2113
2249
|
if (!statusToken) return;
|
|
2114
2250
|
setLoading(true);
|
|
2115
2251
|
try {
|
|
@@ -2121,7 +2257,7 @@ function useOrderStatus(statusToken, initial) {
|
|
|
2121
2257
|
setLoading(false);
|
|
2122
2258
|
}
|
|
2123
2259
|
}, [client, statusToken]);
|
|
2124
|
-
|
|
2260
|
+
React27.useEffect(() => {
|
|
2125
2261
|
if (initial) return;
|
|
2126
2262
|
void load();
|
|
2127
2263
|
}, [load, initial]);
|
|
@@ -2129,12 +2265,12 @@ function useOrderStatus(statusToken, initial) {
|
|
|
2129
2265
|
}
|
|
2130
2266
|
function usePayment(token, initial) {
|
|
2131
2267
|
const client = useStorefront();
|
|
2132
|
-
const [payment, setPayment] =
|
|
2133
|
-
const [methods, setMethods] =
|
|
2134
|
-
const [loading, setLoading] =
|
|
2135
|
-
const [busy, setBusy] =
|
|
2136
|
-
const [error, setError] =
|
|
2137
|
-
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 () => {
|
|
2138
2274
|
if (!token) return;
|
|
2139
2275
|
setLoading(true);
|
|
2140
2276
|
try {
|
|
@@ -2151,7 +2287,7 @@ function usePayment(token, initial) {
|
|
|
2151
2287
|
setLoading(false);
|
|
2152
2288
|
}
|
|
2153
2289
|
}, [client, token]);
|
|
2154
|
-
|
|
2290
|
+
React27.useEffect(() => {
|
|
2155
2291
|
if (!token) return;
|
|
2156
2292
|
if (initial && initial.token === token) {
|
|
2157
2293
|
setPayment(initial);
|
|
@@ -2160,7 +2296,7 @@ function usePayment(token, initial) {
|
|
|
2160
2296
|
}
|
|
2161
2297
|
void load();
|
|
2162
2298
|
}, [token, load]);
|
|
2163
|
-
const selectMethod =
|
|
2299
|
+
const selectMethod = React27.useCallback(
|
|
2164
2300
|
async (method) => {
|
|
2165
2301
|
if (!token) return null;
|
|
2166
2302
|
setBusy(true);
|
|
@@ -2178,7 +2314,7 @@ function usePayment(token, initial) {
|
|
|
2178
2314
|
},
|
|
2179
2315
|
[client, token]
|
|
2180
2316
|
);
|
|
2181
|
-
const startPayment =
|
|
2317
|
+
const startPayment = React27.useCallback(
|
|
2182
2318
|
async (input) => {
|
|
2183
2319
|
if (!token) return null;
|
|
2184
2320
|
setBusy(true);
|
|
@@ -2615,8 +2751,8 @@ function Row4({
|
|
|
2615
2751
|
}
|
|
2616
2752
|
function CopyRow({ label, value }) {
|
|
2617
2753
|
const t = useT();
|
|
2618
|
-
const [copied, setCopied] =
|
|
2619
|
-
|
|
2754
|
+
const [copied, setCopied] = React27.useState(false);
|
|
2755
|
+
React27.useEffect(() => {
|
|
2620
2756
|
if (!copied) return;
|
|
2621
2757
|
const id = setTimeout(() => setCopied(false), 1800);
|
|
2622
2758
|
return () => clearTimeout(id);
|
|
@@ -2700,12 +2836,12 @@ function PaymentPage({
|
|
|
2700
2836
|
const t = useT();
|
|
2701
2837
|
const loc = useFormatLocale(locale);
|
|
2702
2838
|
const { payment, methods, loading, busy, error, reload, selectMethod, startPayment } = usePayment(token, initial);
|
|
2703
|
-
const [verifying, setVerifying] =
|
|
2704
|
-
|
|
2839
|
+
const [verifying, setVerifying] = React27.useState(false);
|
|
2840
|
+
React27.useEffect(() => {
|
|
2705
2841
|
if (token) setVerifying(consumeHandoff(token));
|
|
2706
2842
|
}, [token]);
|
|
2707
2843
|
const settled = payment != null && !payment.can_pay && payment.blocked_reason === "already_paid";
|
|
2708
|
-
|
|
2844
|
+
React27.useEffect(() => {
|
|
2709
2845
|
if (!verifying || !token || settled) return;
|
|
2710
2846
|
let attempts = 0;
|
|
2711
2847
|
const id = setInterval(() => {
|
|
@@ -2720,7 +2856,7 @@ function PaymentPage({
|
|
|
2720
2856
|
}, POLL_MS);
|
|
2721
2857
|
return () => clearInterval(id);
|
|
2722
2858
|
}, [verifying, token, settled, reload]);
|
|
2723
|
-
|
|
2859
|
+
React27.useEffect(() => {
|
|
2724
2860
|
if (!settled || !token || !payment) return;
|
|
2725
2861
|
setVerifying(false);
|
|
2726
2862
|
forgetHandoff(token);
|
|
@@ -2801,27 +2937,32 @@ function Amount({ label, value }) {
|
|
|
2801
2937
|
function Link({ href, children }) {
|
|
2802
2938
|
return /* @__PURE__ */ jsx("a", { href, style: { color: "var(--nv-brand)", fontSize: "0.875rem" }, children });
|
|
2803
2939
|
}
|
|
2804
|
-
var AccountContext =
|
|
2940
|
+
var AccountContext = React27.createContext(null);
|
|
2805
2941
|
function AccountProvider({ children }) {
|
|
2806
2942
|
const client = useStorefront();
|
|
2807
|
-
const [customer, setCustomer] =
|
|
2808
|
-
const [loading, setLoading] =
|
|
2809
|
-
const
|
|
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 () => {
|
|
2810
2947
|
setLoading(true);
|
|
2948
|
+
setError(false);
|
|
2811
2949
|
try {
|
|
2812
2950
|
setCustomer(await client.account.me());
|
|
2951
|
+
} catch {
|
|
2952
|
+
setError(true);
|
|
2813
2953
|
} finally {
|
|
2814
2954
|
setLoading(false);
|
|
2815
2955
|
}
|
|
2816
2956
|
}, [client]);
|
|
2817
|
-
|
|
2957
|
+
React27.useEffect(() => {
|
|
2818
2958
|
void refresh();
|
|
2819
2959
|
}, [refresh]);
|
|
2820
|
-
const value =
|
|
2960
|
+
const value = React27.useMemo(
|
|
2821
2961
|
() => ({
|
|
2822
2962
|
customer,
|
|
2823
2963
|
signedIn: customer != null,
|
|
2824
2964
|
loading,
|
|
2965
|
+
error,
|
|
2825
2966
|
requestMagicLink: (email) => client.account.requestMagicLink(email),
|
|
2826
2967
|
verify: async (token) => {
|
|
2827
2968
|
const c = await client.account.verify(token);
|
|
@@ -2834,12 +2975,12 @@ function AccountProvider({ children }) {
|
|
|
2834
2975
|
},
|
|
2835
2976
|
refresh
|
|
2836
2977
|
}),
|
|
2837
|
-
[customer, loading, client, refresh]
|
|
2978
|
+
[customer, loading, error, client, refresh]
|
|
2838
2979
|
);
|
|
2839
2980
|
return /* @__PURE__ */ jsx(AccountContext.Provider, { value, children });
|
|
2840
2981
|
}
|
|
2841
2982
|
function useAccount() {
|
|
2842
|
-
const ctx =
|
|
2983
|
+
const ctx = React27.useContext(AccountContext);
|
|
2843
2984
|
if (!ctx) {
|
|
2844
2985
|
throw new Error("useAccount must be used inside <AccountProvider>.");
|
|
2845
2986
|
}
|
|
@@ -2847,9 +2988,9 @@ function useAccount() {
|
|
|
2847
2988
|
}
|
|
2848
2989
|
function useMagicLinkVerify(token) {
|
|
2849
2990
|
const { verify } = useAccount();
|
|
2850
|
-
const [status, setStatus] =
|
|
2851
|
-
const done =
|
|
2852
|
-
|
|
2991
|
+
const [status, setStatus] = React27.useState("idle");
|
|
2992
|
+
const done = React27.useRef(false);
|
|
2993
|
+
React27.useEffect(() => {
|
|
2853
2994
|
if (!token || done.current) return;
|
|
2854
2995
|
done.current = true;
|
|
2855
2996
|
setStatus("verifying");
|
|
@@ -2860,18 +3001,22 @@ function useMagicLinkVerify(token) {
|
|
|
2860
3001
|
function LoginForm({ verifyPath = "/account/verify", onSent }) {
|
|
2861
3002
|
const t = useT();
|
|
2862
3003
|
const { requestMagicLink } = useAccount();
|
|
2863
|
-
const [email, setEmail] =
|
|
2864
|
-
const [busy, setBusy] =
|
|
2865
|
-
const [sent, setSent] =
|
|
2866
|
-
const [
|
|
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);
|
|
2867
3009
|
async function submit(e) {
|
|
2868
3010
|
e.preventDefault();
|
|
2869
3011
|
setBusy(true);
|
|
3012
|
+
setError(false);
|
|
2870
3013
|
try {
|
|
2871
3014
|
const r = await requestMagicLink(email);
|
|
2872
3015
|
setSent(true);
|
|
2873
3016
|
if (r.devToken) setDevLink(`${verifyPath}?token=${encodeURIComponent(r.devToken)}`);
|
|
2874
3017
|
onSent?.(email);
|
|
3018
|
+
} catch {
|
|
3019
|
+
setError(true);
|
|
2875
3020
|
} finally {
|
|
2876
3021
|
setBusy(false);
|
|
2877
3022
|
}
|
|
@@ -2910,6 +3055,7 @@ function LoginForm({ verifyPath = "/account/verify", onSent }) {
|
|
|
2910
3055
|
}
|
|
2911
3056
|
)
|
|
2912
3057
|
] }),
|
|
3058
|
+
error ? /* @__PURE__ */ jsx("p", { role: "alert", children: t("account.signInError") }) : null,
|
|
2913
3059
|
/* @__PURE__ */ jsx(
|
|
2914
3060
|
"button",
|
|
2915
3061
|
{
|
|
@@ -2971,20 +3117,47 @@ function OrderHistory({
|
|
|
2971
3117
|
const loc = useFormatLocale(locale);
|
|
2972
3118
|
const { signedIn } = useAccount();
|
|
2973
3119
|
const client = useStorefront();
|
|
2974
|
-
const [orders, setOrders] =
|
|
2975
|
-
const [
|
|
2976
|
-
const [
|
|
2977
|
-
|
|
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(() => {
|
|
2978
3128
|
let live = true;
|
|
2979
3129
|
if (!signedIn) {
|
|
2980
3130
|
setOrders([]);
|
|
2981
3131
|
return;
|
|
2982
3132
|
}
|
|
2983
|
-
|
|
3133
|
+
setError(false);
|
|
3134
|
+
setOrders(null);
|
|
3135
|
+
client.account.ordersPage().then((page) => {
|
|
3136
|
+
if (live) {
|
|
3137
|
+
setOrders(page.orders);
|
|
3138
|
+
setNextCursor(page.nextCursor);
|
|
3139
|
+
}
|
|
3140
|
+
}).catch(() => {
|
|
3141
|
+
if (live) setError(true);
|
|
3142
|
+
});
|
|
2984
3143
|
return () => {
|
|
2985
3144
|
live = false;
|
|
2986
3145
|
};
|
|
2987
|
-
}, [signedIn, client]);
|
|
3146
|
+
}, [signedIn, client, reload]);
|
|
3147
|
+
async function loadMore() {
|
|
3148
|
+
if (!nextCursor || pageLoading) return;
|
|
3149
|
+
setPageLoading(true);
|
|
3150
|
+
setError(false);
|
|
3151
|
+
try {
|
|
3152
|
+
const page = await client.account.ordersPage({ beforeId: nextCursor });
|
|
3153
|
+
setOrders((previous) => [...previous ?? [], ...page.orders]);
|
|
3154
|
+
setNextCursor(page.nextCursor);
|
|
3155
|
+
} catch {
|
|
3156
|
+
setError(true);
|
|
3157
|
+
} finally {
|
|
3158
|
+
setPageLoading(false);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
2988
3161
|
async function reorder(statusToken) {
|
|
2989
3162
|
setReordering(statusToken);
|
|
2990
3163
|
setNotice(null);
|
|
@@ -3005,7 +3178,11 @@ function OrderHistory({
|
|
|
3005
3178
|
}
|
|
3006
3179
|
return /* @__PURE__ */ jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
|
|
3007
3180
|
/* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.orders") }),
|
|
3008
|
-
|
|
3181
|
+
error ? /* @__PURE__ */ jsxs("div", { role: "alert", children: [
|
|
3182
|
+
/* @__PURE__ */ jsx("p", { children: t("account.loadError") }),
|
|
3183
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => orders ? void loadMore() : setReload((x) => x + 1), children: t("account.retry") })
|
|
3184
|
+
] }) : null,
|
|
3185
|
+
orders == null ? error ? null : /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("common.loadingShort") }) : orders.length === 0 ? /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("account.noOrders") }) : /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.5rem" }, children: orders.map((o) => /* @__PURE__ */ jsxs(
|
|
3009
3186
|
"li",
|
|
3010
3187
|
{
|
|
3011
3188
|
style: {
|
|
@@ -3052,12 +3229,13 @@ function OrderHistory({
|
|
|
3052
3229
|
]
|
|
3053
3230
|
},
|
|
3054
3231
|
o.status_token
|
|
3055
|
-
)) })
|
|
3232
|
+
)) }),
|
|
3233
|
+
nextCursor ? /* @__PURE__ */ jsx("button", { type: "button", disabled: pageLoading, onClick: () => void loadMore(), children: pageLoading ? t("common.loadingShort") : t("account.loadMore") }) : null
|
|
3056
3234
|
] });
|
|
3057
3235
|
}
|
|
3058
3236
|
function AddressForm2({ initial, onSubmit, onCancel, busy }) {
|
|
3059
3237
|
const t = useT();
|
|
3060
|
-
const [form, setForm] =
|
|
3238
|
+
const [form, setForm] = React27.useState(() => ({
|
|
3061
3239
|
first_name: initial?.first_name ?? "",
|
|
3062
3240
|
last_name: initial?.last_name ?? "",
|
|
3063
3241
|
company: initial?.company ?? "",
|
|
@@ -3155,17 +3333,17 @@ function AddressBook() {
|
|
|
3155
3333
|
const t = useT();
|
|
3156
3334
|
const { signedIn } = useAccount();
|
|
3157
3335
|
const client = useStorefront();
|
|
3158
|
-
const [addresses, setAddresses] =
|
|
3159
|
-
const [mode, setMode] =
|
|
3160
|
-
const [busy, setBusy] =
|
|
3161
|
-
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(() => {
|
|
3162
3340
|
if (!signedIn) {
|
|
3163
3341
|
setAddresses([]);
|
|
3164
3342
|
return;
|
|
3165
3343
|
}
|
|
3166
3344
|
client.account.addresses().then(setAddresses).catch(() => setAddresses([]));
|
|
3167
3345
|
}, [signedIn, client]);
|
|
3168
|
-
|
|
3346
|
+
React27.useEffect(() => {
|
|
3169
3347
|
load();
|
|
3170
3348
|
}, [load]);
|
|
3171
3349
|
async function run(fn) {
|
|
@@ -3219,10 +3397,10 @@ function ProfileEditor() {
|
|
|
3219
3397
|
const t = useT();
|
|
3220
3398
|
const client = useStorefront();
|
|
3221
3399
|
const { customer, refresh } = useAccount();
|
|
3222
|
-
const [editing, setEditing] =
|
|
3223
|
-
const [busy, setBusy] =
|
|
3224
|
-
const [error, setError] =
|
|
3225
|
-
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({});
|
|
3226
3404
|
if (!customer) return null;
|
|
3227
3405
|
function startEdit() {
|
|
3228
3406
|
setForm({
|
|
@@ -3299,6 +3477,176 @@ function ProfileEditor() {
|
|
|
3299
3477
|
] })
|
|
3300
3478
|
] });
|
|
3301
3479
|
}
|
|
3480
|
+
function AccountSecurity() {
|
|
3481
|
+
const client = useStorefront();
|
|
3482
|
+
const account = useAccount();
|
|
3483
|
+
const t = useT();
|
|
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(() => {
|
|
3491
|
+
let live = true;
|
|
3492
|
+
if (account.signedIn) client.account.sessions().then((s) => {
|
|
3493
|
+
if (live) setSessions(s);
|
|
3494
|
+
}).catch(() => {
|
|
3495
|
+
if (live) setNotice(t("account.loadError"));
|
|
3496
|
+
});
|
|
3497
|
+
return () => {
|
|
3498
|
+
live = false;
|
|
3499
|
+
};
|
|
3500
|
+
}, [client, account.signedIn, retry, t]);
|
|
3501
|
+
async function act(operation) {
|
|
3502
|
+
setBusy(true);
|
|
3503
|
+
setNotice(null);
|
|
3504
|
+
try {
|
|
3505
|
+
await operation();
|
|
3506
|
+
} catch (e) {
|
|
3507
|
+
setNotice(e instanceof StorefrontError && e.status === 401 ? t("account.recentLogin") : t("account.loadError"));
|
|
3508
|
+
} finally {
|
|
3509
|
+
setBusy(false);
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
if (!account.signedIn) return null;
|
|
3513
|
+
return /* @__PURE__ */ jsxs("section", { style: { display: "grid", gap: "0.75rem" }, children: [
|
|
3514
|
+
/* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.security") }),
|
|
3515
|
+
notice ? /* @__PURE__ */ jsx("p", { role: "status", children: notice }) : null,
|
|
3516
|
+
/* @__PURE__ */ jsxs(
|
|
3517
|
+
"form",
|
|
3518
|
+
{
|
|
3519
|
+
style: { display: "flex", gap: "0.75rem", flexWrap: "wrap", alignItems: "end" },
|
|
3520
|
+
onSubmit: (e) => {
|
|
3521
|
+
e.preventDefault();
|
|
3522
|
+
void act(async () => {
|
|
3523
|
+
await client.account.requestEmailChange(email);
|
|
3524
|
+
setNotice(t("account.checkEmail"));
|
|
3525
|
+
});
|
|
3526
|
+
},
|
|
3527
|
+
children: [
|
|
3528
|
+
/* @__PURE__ */ jsx(Field, { label: t("account.newEmail"), type: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value) }),
|
|
3529
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", busy, children: t("account.changeEmail") })
|
|
3530
|
+
]
|
|
3531
|
+
}
|
|
3532
|
+
),
|
|
3533
|
+
/* @__PURE__ */ jsx("h3", { children: t("account.sessions") }),
|
|
3534
|
+
sessions ? sessions.map((s) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem", justifyContent: "space-between" }, children: [
|
|
3535
|
+
/* @__PURE__ */ jsx("span", { children: s.current ? t("account.currentSession") : /* @__PURE__ */ jsx("time", { dateTime: s.created_at, children: s.created_at.slice(0, 10) }) }),
|
|
3536
|
+
/* @__PURE__ */ jsx(Button, { busy, onClick: () => void act(async () => {
|
|
3537
|
+
await client.account.revokeSession(s.id);
|
|
3538
|
+
if (s.current) await account.logout();
|
|
3539
|
+
else setSessions((old) => old?.filter((row) => row.id !== s.id) ?? null);
|
|
3540
|
+
}), children: t("account.revokeSession") })
|
|
3541
|
+
] }, s.id)) : /* @__PURE__ */ jsx(Button, { onClick: () => setRetry((x) => x + 1), children: t("account.retry") }),
|
|
3542
|
+
confirmClose ? /* @__PURE__ */ jsxs("div", { children: [
|
|
3543
|
+
/* @__PURE__ */ jsx("p", { children: t("account.closeWarning") }),
|
|
3544
|
+
/* @__PURE__ */ jsx(Button, { busy, onClick: () => void act(async () => {
|
|
3545
|
+
await client.account.close();
|
|
3546
|
+
await account.refresh();
|
|
3547
|
+
}), children: t("account.confirmClose") }),
|
|
3548
|
+
" ",
|
|
3549
|
+
/* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => setConfirmClose(false), children: t("account.cancel") })
|
|
3550
|
+
] }) : /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => setConfirmClose(true), children: t("account.closeAccount") })
|
|
3551
|
+
] });
|
|
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
|
+
}
|
|
3302
3650
|
function serializeJsonLd(data) {
|
|
3303
3651
|
return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
3304
3652
|
}
|
|
@@ -3403,6 +3751,6 @@ function OrganizationJsonLd({
|
|
|
3403
3751
|
return /* @__PURE__ */ jsx(JsonLd, { data });
|
|
3404
3752
|
}
|
|
3405
3753
|
|
|
3406
|
-
export { AccountDashboard, AccountProvider, AddToCart, AddressBook, AddressForm, AvailabilityFilter, BackInStockForm, BreadcrumbJsonLd, CartDrawer, CartLineItem, CartProvider, CartSummary, CheckoutFlow, CheckoutProvider, CollectionPage, I18nProvider, JsonLd, LoginForm, MarketSwitcher, NeviosProvider, OrderConfirmation, OrderHistory, OrderLines, OrderStatusPage, OrderSummary, OrderTotals, OrganizationJsonLd, PaymentMethodPicker, PaymentPage, PaymentSection, PhaseTimeline, PriceMoney, PriceRangeFilter, ProductCard, ProductDetailPage, ProductGallery, ProductJsonLd, ProfileEditor, ShippingPicker, SortSelect, StatusBanner, StockBadge, TagFilter, ThemeProvider, TransferInstructions, VariantPicker, absoluteUrl, bundledMessages, cs, en, formatCents, formatMoney, isTerminal, serializeJsonLd, stripHtml, themeToCssVars, useAccount, useCart, useCheckout, useI18n, useMagicLinkVerify, useOrderStatus, usePayment, useStorefront, useStorefrontOptional, useT };
|
|
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 };
|
|
3407
3755
|
//# sourceMappingURL=index.js.map
|
|
3408
3756
|
//# sourceMappingURL=index.js.map
|