@nevios/storefront-kit 1.0.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/dist/index.cjs +704 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +202 -3
- package/dist/index.d.ts +202 -3
- package/dist/index.js +695 -121
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as React19 from 'react';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { imageSrcSet } from '@nevios/storefront-js';
|
|
3
|
+
import { imageSrcSet, StorefrontError } from '@nevios/storefront-js';
|
|
4
4
|
|
|
5
5
|
// src/provider.tsx
|
|
6
|
-
var StorefrontContext =
|
|
6
|
+
var StorefrontContext = React19.createContext(null);
|
|
7
7
|
function NeviosProvider({ client, children }) {
|
|
8
8
|
return /* @__PURE__ */ jsx(StorefrontContext.Provider, { value: client, children });
|
|
9
9
|
}
|
|
10
10
|
function useStorefront() {
|
|
11
|
-
const client =
|
|
11
|
+
const client = React19.useContext(StorefrontContext);
|
|
12
12
|
if (!client) {
|
|
13
13
|
throw new Error(
|
|
14
14
|
"useStorefront must be used inside <NeviosProvider>. Wrap your app root with it."
|
|
@@ -17,7 +17,7 @@ function useStorefront() {
|
|
|
17
17
|
return client;
|
|
18
18
|
}
|
|
19
19
|
function useStorefrontOptional() {
|
|
20
|
-
return
|
|
20
|
+
return React19.useContext(StorefrontContext);
|
|
21
21
|
}
|
|
22
22
|
var DEFAULTS = {
|
|
23
23
|
brand: "#111111",
|
|
@@ -53,7 +53,7 @@ function ThemeProvider({
|
|
|
53
53
|
as: Tag = "div"
|
|
54
54
|
}) {
|
|
55
55
|
const style = themeToCssVars(theme);
|
|
56
|
-
return
|
|
56
|
+
return React19.createElement(
|
|
57
57
|
Tag,
|
|
58
58
|
{ "data-nevios-theme": "", style },
|
|
59
59
|
children
|
|
@@ -179,6 +179,37 @@ var en = {
|
|
|
179
179
|
"order.terminal.cancelled": "This order was cancelled.",
|
|
180
180
|
"order.terminal.refunded": "This order was refunded.",
|
|
181
181
|
"order.terminal.returned": "This order was returned.",
|
|
182
|
+
// payment page — the four situations it can be in
|
|
183
|
+
"payment.title": "Pay for order {order}",
|
|
184
|
+
"payment.amountDue": "Amount due",
|
|
185
|
+
"payment.methodLabel": "Payment method",
|
|
186
|
+
"payment.payNow": "Pay now",
|
|
187
|
+
"payment.finish": "Finish payment",
|
|
188
|
+
"payment.loading": "Loading payment\u2026",
|
|
189
|
+
"payment.loadError": "Couldn't load this payment.",
|
|
190
|
+
"payment.actionError": "That didn't go through. Please try again.",
|
|
191
|
+
// …paid
|
|
192
|
+
"payment.paidTitle": "Paid",
|
|
193
|
+
"payment.paidBody": "Order {order} is paid. Nothing further is needed.",
|
|
194
|
+
"payment.paidAmount": "Paid",
|
|
195
|
+
// …returning from a gateway, callback still in flight
|
|
196
|
+
"payment.verifyingTitle": "Checking your payment\u2026",
|
|
197
|
+
"payment.verifyingBody": "This takes a few seconds. Don't pay again \u2014 we'll update this page.",
|
|
198
|
+
// …locked, one sentence per reason
|
|
199
|
+
"payment.blockedTitle": "This order can't be paid",
|
|
200
|
+
"payment.blocked.orderCancelled": "This order was cancelled, so there is nothing to pay.",
|
|
201
|
+
"payment.blocked.alreadyPaid": "This order is already paid.",
|
|
202
|
+
"payment.blocked.authorizationHeld": "Your bank is holding the funds for this order. Once the payment settles this page will show it as paid.",
|
|
203
|
+
"payment.blocked.documentIssued": "An invoice has been issued for this order, so the amount is fixed. Contact us to change how you pay.",
|
|
204
|
+
// …bank transfer
|
|
205
|
+
"payment.account": "Account",
|
|
206
|
+
"payment.beneficiary": "Beneficiary",
|
|
207
|
+
"payment.variableSymbol": "Variable symbol",
|
|
208
|
+
"payment.amount": "Amount",
|
|
209
|
+
"payment.copy": "Copy",
|
|
210
|
+
"payment.copied": "Copied",
|
|
211
|
+
"payment.qrAlt": "QR code for the payment",
|
|
212
|
+
"payment.qrCaption": "Scan in your banking app",
|
|
182
213
|
// error pages
|
|
183
214
|
"error.notFound.title": "Page not found",
|
|
184
215
|
"error.notFound.body": "This page doesn't exist or isn't available in your region.",
|
|
@@ -195,6 +226,20 @@ var en = {
|
|
|
195
226
|
"account.signOut": "Sign out",
|
|
196
227
|
"account.greeting": "Signed in as {email}",
|
|
197
228
|
"account.orders": "Your orders",
|
|
229
|
+
"account.loadError": "Could not load your account. Please try again.",
|
|
230
|
+
"account.retry": "Try again",
|
|
231
|
+
"account.loadMore": "Load more",
|
|
232
|
+
"account.signInError": "Could not request a sign-in link. Please try again shortly.",
|
|
233
|
+
"account.security": "Account security",
|
|
234
|
+
"account.newEmail": "New email",
|
|
235
|
+
"account.changeEmail": "Verify new email",
|
|
236
|
+
"account.recentLogin": "Sign in again by email before making this change.",
|
|
237
|
+
"account.sessions": "Sessions",
|
|
238
|
+
"account.currentSession": "Current session",
|
|
239
|
+
"account.revokeSession": "Sign out",
|
|
240
|
+
"account.closeAccount": "Close account",
|
|
241
|
+
"account.closeWarning": "Closing your account signs out all devices and disables account access. Orders and invoices are retained. Contact the shop to restore access.",
|
|
242
|
+
"account.confirmClose": "Confirm account closure",
|
|
198
243
|
"account.noOrders": "No orders yet.",
|
|
199
244
|
"account.addresses": "Saved addresses",
|
|
200
245
|
"account.noAddresses": "No saved addresses yet.",
|
|
@@ -335,6 +380,33 @@ var cs = {
|
|
|
335
380
|
"order.terminal.cancelled": "Tato objedn\xE1vka byla zru\u0161ena.",
|
|
336
381
|
"order.terminal.refunded": "Tato objedn\xE1vka byla refundov\xE1na.",
|
|
337
382
|
"order.terminal.returned": "Tato objedn\xE1vka byla vr\xE1cena.",
|
|
383
|
+
// platební stránka — čtyři situace, ve kterých může být
|
|
384
|
+
"payment.title": "Zaplatit objedn\xE1vku {order}",
|
|
385
|
+
"payment.amountDue": "K \xFAhrad\u011B",
|
|
386
|
+
"payment.methodLabel": "Zp\u016Fsob platby",
|
|
387
|
+
"payment.payNow": "Zaplatit",
|
|
388
|
+
"payment.finish": "Dokon\u010Dit platbu",
|
|
389
|
+
"payment.loading": "Na\u010D\xEDt\xE1n\xED platby\u2026",
|
|
390
|
+
"payment.loadError": "Platbu se nepoda\u0159ilo na\u010D\xEDst.",
|
|
391
|
+
"payment.actionError": "Nepovedlo se to. Zkuste to pros\xEDm znovu.",
|
|
392
|
+
"payment.paidTitle": "Zaplaceno",
|
|
393
|
+
"payment.paidBody": "Objedn\xE1vka {order} je zaplacen\xE1. Nic dal\u0161\xEDho d\u011Blat nemus\xEDte.",
|
|
394
|
+
"payment.paidAmount": "Uhrazeno",
|
|
395
|
+
"payment.verifyingTitle": "Ov\u011B\u0159ujeme platbu\u2026",
|
|
396
|
+
"payment.verifyingBody": "Chvilku to trv\xE1. Nepla\u0165te pros\xEDm znovu \u2014 str\xE1nka se sama aktualizuje.",
|
|
397
|
+
"payment.blockedTitle": "Tuhle objedn\xE1vku te\u010F zaplatit nejde",
|
|
398
|
+
"payment.blocked.orderCancelled": "Objedn\xE1vka byla zru\u0161en\xE1, tak\u017Ee nen\xED co platit.",
|
|
399
|
+
"payment.blocked.alreadyPaid": "Objedn\xE1vka je u\u017E zaplacen\xE1.",
|
|
400
|
+
"payment.blocked.authorizationHeld": "Banka na tuhle objedn\xE1vku dr\u017E\xED pen\xEDze. Jakmile se platba dokon\u010D\xED, uvid\xEDte to tady.",
|
|
401
|
+
"payment.blocked.documentIssued": "K objedn\xE1vce je vystaven\xE1 faktura, tak\u017Ee je \u010D\xE1stka pevn\xE1. Pokud chcete platit jinak, ozv\u011Bte se n\xE1m.",
|
|
402
|
+
"payment.account": "\xDA\u010Det",
|
|
403
|
+
"payment.beneficiary": "P\u0159\xEDjemce",
|
|
404
|
+
"payment.variableSymbol": "Variabiln\xED symbol",
|
|
405
|
+
"payment.amount": "\u010C\xE1stka",
|
|
406
|
+
"payment.copy": "Kop\xEDrovat",
|
|
407
|
+
"payment.copied": "Zkop\xEDrov\xE1no",
|
|
408
|
+
"payment.qrAlt": "QR k\xF3d k platb\u011B",
|
|
409
|
+
"payment.qrCaption": "Na\u010Dt\u011Bte v bankovn\xED aplikaci",
|
|
338
410
|
"error.notFound.title": "Str\xE1nka nenalezena",
|
|
339
411
|
"error.notFound.body": "Tato str\xE1nka neexistuje nebo nen\xED ve va\u0161em regionu dostupn\xE1.",
|
|
340
412
|
"error.generic.title": "N\u011Bco se pokazilo",
|
|
@@ -349,6 +421,20 @@ var cs = {
|
|
|
349
421
|
"account.signOut": "Odhl\xE1sit se",
|
|
350
422
|
"account.greeting": "P\u0159ihl\xE1\u0161en jako {email}",
|
|
351
423
|
"account.orders": "Va\u0161e objedn\xE1vky",
|
|
424
|
+
"account.loadError": "\xDAdaje se nepoda\u0159ilo na\u010D\xEDst. Zkuste to znovu.",
|
|
425
|
+
"account.retry": "Zkusit znovu",
|
|
426
|
+
"account.loadMore": "Na\u010D\xEDst dal\u0161\xED",
|
|
427
|
+
"account.signInError": "P\u0159ihl\xE1\u0161en\xED se nepoda\u0159ilo zah\xE1jit. Zkuste to za chv\xEDli znovu.",
|
|
428
|
+
"account.security": "Zabezpe\u010Den\xED \xFA\u010Dtu",
|
|
429
|
+
"account.newEmail": "Nov\xFD e-mail",
|
|
430
|
+
"account.changeEmail": "Ov\u011B\u0159it nov\xFD e-mail",
|
|
431
|
+
"account.recentLogin": "Pro tuto zm\u011Bnu se nejprve znovu p\u0159ihlaste p\u0159es e-mail.",
|
|
432
|
+
"account.sessions": "P\u0159ihl\xE1\u0161en\xED",
|
|
433
|
+
"account.currentSession": "Toto p\u0159ihl\xE1\u0161en\xED",
|
|
434
|
+
"account.revokeSession": "Odhl\xE1sit",
|
|
435
|
+
"account.closeAccount": "Uzav\u0159\xEDt \xFA\u010Det",
|
|
436
|
+
"account.closeWarning": "Uzav\u0159en\xEDm \xFA\u010Dtu ukon\u010D\xEDte p\u0159\xEDstup a odhl\xE1s\xEDte v\u0161echna za\u0159\xEDzen\xED. Objedn\xE1vky a doklady z\u016Fstanou zachov\xE1ny. Obnoven\xED p\u0159\xEDstupu \u0159e\u0161\xED podpora obchodu.",
|
|
437
|
+
"account.confirmClose": "Potvrdit uzav\u0159en\xED \xFA\u010Dtu",
|
|
352
438
|
"account.noOrders": "Zat\xEDm \u017E\xE1dn\xE9 objedn\xE1vky.",
|
|
353
439
|
"account.addresses": "Ulo\u017Een\xE9 adresy",
|
|
354
440
|
"account.noAddresses": "Zat\xEDm \u017E\xE1dn\xE9 ulo\u017Een\xE9 adresy.",
|
|
@@ -433,13 +519,13 @@ function makeI18n(locale, messages) {
|
|
|
433
519
|
return { locale, messages, t };
|
|
434
520
|
}
|
|
435
521
|
var DEFAULT_I18N = makeI18n("en", en);
|
|
436
|
-
var I18nContext =
|
|
522
|
+
var I18nContext = React19.createContext(null);
|
|
437
523
|
function I18nProvider({
|
|
438
524
|
locale,
|
|
439
525
|
messages,
|
|
440
526
|
children
|
|
441
527
|
}) {
|
|
442
|
-
const value =
|
|
528
|
+
const value = React19.useMemo(() => {
|
|
443
529
|
const lang = langOf(locale);
|
|
444
530
|
if (locale && !(lang in BUNDLED) && !messages) {
|
|
445
531
|
devWarn(
|
|
@@ -453,7 +539,7 @@ function I18nProvider({
|
|
|
453
539
|
return /* @__PURE__ */ jsx(I18nContext.Provider, { value, children });
|
|
454
540
|
}
|
|
455
541
|
function useI18n() {
|
|
456
|
-
return
|
|
542
|
+
return React19.useContext(I18nContext) ?? DEFAULT_I18N;
|
|
457
543
|
}
|
|
458
544
|
function useT() {
|
|
459
545
|
return useI18n().t;
|
|
@@ -577,7 +663,7 @@ function ProductCard({ product, href, locale }) {
|
|
|
577
663
|
var MAIN_WIDTHS = [600, 800, 1e3];
|
|
578
664
|
var THUMB_WIDTHS = [56, 112];
|
|
579
665
|
function ProductGallery({ media, alt }) {
|
|
580
|
-
const [active, setActive] =
|
|
666
|
+
const [active, setActive] = React19.useState(0);
|
|
581
667
|
const imageBase = useStorefrontOptional()?.imageBase;
|
|
582
668
|
if (media.length === 0) {
|
|
583
669
|
return /* @__PURE__ */ jsx(
|
|
@@ -665,10 +751,10 @@ function matchVariant(product, selected) {
|
|
|
665
751
|
) ?? null;
|
|
666
752
|
}
|
|
667
753
|
function VariantPicker({ product, onChange }) {
|
|
668
|
-
const [selected, setSelected] =
|
|
754
|
+
const [selected, setSelected] = React19.useState(
|
|
669
755
|
() => product.variants[0]?.options ?? []
|
|
670
756
|
);
|
|
671
|
-
|
|
757
|
+
React19.useEffect(() => {
|
|
672
758
|
onChange?.(matchVariant(product, selected));
|
|
673
759
|
}, [product, selected]);
|
|
674
760
|
if (product.options.length === 0) return null;
|
|
@@ -709,8 +795,8 @@ function BackInStockForm({
|
|
|
709
795
|
}) {
|
|
710
796
|
const t = useT();
|
|
711
797
|
const client = useStorefront();
|
|
712
|
-
const [email, setEmail] =
|
|
713
|
-
const [state, setState] =
|
|
798
|
+
const [email, setEmail] = React19.useState(defaultEmail);
|
|
799
|
+
const [state, setState] = React19.useState("idle");
|
|
714
800
|
if (!variant || variant.availability?.status !== "out_of_stock") return null;
|
|
715
801
|
async function submit(e) {
|
|
716
802
|
e.preventDefault();
|
|
@@ -810,7 +896,7 @@ function ProductDetailPage({
|
|
|
810
896
|
locale,
|
|
811
897
|
addToCart
|
|
812
898
|
}) {
|
|
813
|
-
const [variant, setVariant] =
|
|
899
|
+
const [variant, setVariant] = React19.useState(
|
|
814
900
|
product.variants[0] ?? null
|
|
815
901
|
);
|
|
816
902
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1042,12 +1128,12 @@ function TagFilter({ tags, selected = [], onChange }) {
|
|
|
1042
1128
|
}) })
|
|
1043
1129
|
] });
|
|
1044
1130
|
}
|
|
1045
|
-
var CartContext =
|
|
1131
|
+
var CartContext = React19.createContext(null);
|
|
1046
1132
|
function CartProvider({ children }) {
|
|
1047
1133
|
const client = useStorefront();
|
|
1048
|
-
const [cart, setCart] =
|
|
1049
|
-
const [loading, setLoading] =
|
|
1050
|
-
const run =
|
|
1134
|
+
const [cart, setCart] = React19.useState(null);
|
|
1135
|
+
const [loading, setLoading] = React19.useState(false);
|
|
1136
|
+
const run = React19.useCallback(
|
|
1051
1137
|
async (op) => {
|
|
1052
1138
|
setLoading(true);
|
|
1053
1139
|
try {
|
|
@@ -1060,7 +1146,7 @@ function CartProvider({ children }) {
|
|
|
1060
1146
|
},
|
|
1061
1147
|
[]
|
|
1062
1148
|
);
|
|
1063
|
-
const refresh =
|
|
1149
|
+
const refresh = React19.useCallback(async () => {
|
|
1064
1150
|
setLoading(true);
|
|
1065
1151
|
try {
|
|
1066
1152
|
setCart(await client.cart.get());
|
|
@@ -1068,10 +1154,10 @@ function CartProvider({ children }) {
|
|
|
1068
1154
|
setLoading(false);
|
|
1069
1155
|
}
|
|
1070
1156
|
}, [client]);
|
|
1071
|
-
|
|
1157
|
+
React19.useEffect(() => {
|
|
1072
1158
|
void refresh();
|
|
1073
1159
|
}, [refresh]);
|
|
1074
|
-
const value =
|
|
1160
|
+
const value = React19.useMemo(
|
|
1075
1161
|
() => ({
|
|
1076
1162
|
cart,
|
|
1077
1163
|
loading,
|
|
@@ -1086,7 +1172,7 @@ function CartProvider({ children }) {
|
|
|
1086
1172
|
return /* @__PURE__ */ jsx(CartContext.Provider, { value, children });
|
|
1087
1173
|
}
|
|
1088
1174
|
function useCart() {
|
|
1089
|
-
const ctx =
|
|
1175
|
+
const ctx = React19.useContext(CartContext);
|
|
1090
1176
|
if (!ctx) {
|
|
1091
1177
|
throw new Error("useCart must be used inside <CartProvider>.");
|
|
1092
1178
|
}
|
|
@@ -1100,8 +1186,8 @@ function AddToCart({
|
|
|
1100
1186
|
}) {
|
|
1101
1187
|
const t = useT();
|
|
1102
1188
|
const { addLine } = useCart();
|
|
1103
|
-
const [busy, setBusy] =
|
|
1104
|
-
const [error, setError] =
|
|
1189
|
+
const [busy, setBusy] = React19.useState(false);
|
|
1190
|
+
const [error, setError] = React19.useState(null);
|
|
1105
1191
|
const disabled = variant == null || busy;
|
|
1106
1192
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
1107
1193
|
/* @__PURE__ */ jsx(
|
|
@@ -1142,6 +1228,7 @@ function AddToCart({
|
|
|
1142
1228
|
}
|
|
1143
1229
|
function CartLineItem({ line, locale }) {
|
|
1144
1230
|
const t = useT();
|
|
1231
|
+
const loc = useFormatLocale(locale);
|
|
1145
1232
|
const { updateLine, removeLine } = useCart();
|
|
1146
1233
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.85rem", alignItems: "flex-start", padding: "0.75rem 0" }, children: [
|
|
1147
1234
|
/* @__PURE__ */ jsx(
|
|
@@ -1213,7 +1300,7 @@ function CartLineItem({ line, locale }) {
|
|
|
1213
1300
|
)
|
|
1214
1301
|
] })
|
|
1215
1302
|
] }),
|
|
1216
|
-
/* @__PURE__ */ jsx("div", { style: { fontWeight: 500, whiteSpace: "nowrap" }, children: formatCents(line.line_total_cents, line.unit_price.currency,
|
|
1303
|
+
/* @__PURE__ */ jsx("div", { style: { fontWeight: 500, whiteSpace: "nowrap" }, children: formatCents(line.line_total_cents, line.unit_price.currency, loc) })
|
|
1217
1304
|
] });
|
|
1218
1305
|
}
|
|
1219
1306
|
function StepBtn({ label, onClick }) {
|
|
@@ -1245,6 +1332,7 @@ function CartSummary({
|
|
|
1245
1332
|
locale
|
|
1246
1333
|
}) {
|
|
1247
1334
|
const tr = useT();
|
|
1335
|
+
const loc = useFormatLocale(locale);
|
|
1248
1336
|
const { cart } = useCart();
|
|
1249
1337
|
if (cart == null) return null;
|
|
1250
1338
|
const t = cart.totals;
|
|
@@ -1265,10 +1353,10 @@ function CartSummary({
|
|
|
1265
1353
|
opacity: empty ? 0.55 : 1
|
|
1266
1354
|
};
|
|
1267
1355
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
|
|
1268
|
-
/* @__PURE__ */ jsx(Row, { label: tr("cart.subtotal"), value: formatCents(t.subtotal_with_tax_cents, t.currency,
|
|
1269
|
-
t.tax_cents > 0 ? /* @__PURE__ */ jsx(Row, { label: tr("cart.tax"), value: formatCents(t.tax_cents, t.currency,
|
|
1356
|
+
/* @__PURE__ */ jsx(Row, { label: tr("cart.subtotal"), value: formatCents(t.subtotal_with_tax_cents, t.currency, loc) }),
|
|
1357
|
+
t.tax_cents > 0 ? /* @__PURE__ */ jsx(Row, { label: tr("cart.tax"), value: formatCents(t.tax_cents, t.currency, loc), muted: true }) : null,
|
|
1270
1358
|
/* @__PURE__ */ jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
|
|
1271
|
-
/* @__PURE__ */ jsx(Row, { label: tr("cart.total"), value: formatCents(t.total_cents, t.currency,
|
|
1359
|
+
/* @__PURE__ */ jsx(Row, { label: tr("cart.total"), value: formatCents(t.total_cents, t.currency, loc), strong: true }),
|
|
1272
1360
|
checkoutHref && !empty ? /* @__PURE__ */ jsx("a", { href: checkoutHref, style: ctaStyle, children: cta }) : /* @__PURE__ */ jsx("button", { type: "button", disabled: empty, onClick: onCheckout, style: ctaStyle, children: cta })
|
|
1273
1361
|
] });
|
|
1274
1362
|
}
|
|
@@ -1375,15 +1463,15 @@ function CartDrawer({
|
|
|
1375
1463
|
}
|
|
1376
1464
|
);
|
|
1377
1465
|
}
|
|
1378
|
-
var CheckoutContext =
|
|
1466
|
+
var CheckoutContext = React19.createContext(null);
|
|
1379
1467
|
function CheckoutProvider({
|
|
1380
1468
|
children
|
|
1381
1469
|
}) {
|
|
1382
1470
|
const client = useStorefront();
|
|
1383
|
-
const [checkout, setCheckout] =
|
|
1384
|
-
const [loading, setLoading] =
|
|
1385
|
-
const [error, setError] =
|
|
1386
|
-
const run =
|
|
1471
|
+
const [checkout, setCheckout] = React19.useState(null);
|
|
1472
|
+
const [loading, setLoading] = React19.useState(false);
|
|
1473
|
+
const [error, setError] = React19.useState(null);
|
|
1474
|
+
const run = React19.useCallback(
|
|
1387
1475
|
async (op, store) => {
|
|
1388
1476
|
setLoading(true);
|
|
1389
1477
|
try {
|
|
@@ -1401,7 +1489,7 @@ function CheckoutProvider({
|
|
|
1401
1489
|
},
|
|
1402
1490
|
[]
|
|
1403
1491
|
);
|
|
1404
|
-
const refresh =
|
|
1492
|
+
const refresh = React19.useCallback(async () => {
|
|
1405
1493
|
setLoading(true);
|
|
1406
1494
|
try {
|
|
1407
1495
|
setCheckout(await client.checkout.get());
|
|
@@ -1410,10 +1498,10 @@ function CheckoutProvider({
|
|
|
1410
1498
|
setLoading(false);
|
|
1411
1499
|
}
|
|
1412
1500
|
}, [client]);
|
|
1413
|
-
|
|
1501
|
+
React19.useEffect(() => {
|
|
1414
1502
|
void refresh();
|
|
1415
1503
|
}, [refresh]);
|
|
1416
|
-
const pay =
|
|
1504
|
+
const pay = React19.useCallback(
|
|
1417
1505
|
async (opts = {}) => {
|
|
1418
1506
|
const co = checkout;
|
|
1419
1507
|
if (!co) throw new Error("pay() called before a checkout exists");
|
|
@@ -1436,7 +1524,7 @@ function CheckoutProvider({
|
|
|
1436
1524
|
},
|
|
1437
1525
|
[checkout, client, run]
|
|
1438
1526
|
);
|
|
1439
|
-
const value =
|
|
1527
|
+
const value = React19.useMemo(
|
|
1440
1528
|
() => ({
|
|
1441
1529
|
checkout,
|
|
1442
1530
|
loading,
|
|
@@ -1461,7 +1549,7 @@ function CheckoutProvider({
|
|
|
1461
1549
|
return /* @__PURE__ */ jsx(CheckoutContext.Provider, { value, children });
|
|
1462
1550
|
}
|
|
1463
1551
|
function useCheckout() {
|
|
1464
|
-
const ctx =
|
|
1552
|
+
const ctx = React19.useContext(CheckoutContext);
|
|
1465
1553
|
if (!ctx) {
|
|
1466
1554
|
throw new Error("useCheckout must be used inside <CheckoutProvider>.");
|
|
1467
1555
|
}
|
|
@@ -1620,10 +1708,25 @@ function AddressForm({
|
|
|
1620
1708
|
}) {
|
|
1621
1709
|
const t = useT();
|
|
1622
1710
|
const { checkout, setAddress, loading } = useCheckout();
|
|
1623
|
-
const [form, setForm] =
|
|
1711
|
+
const [form, setForm] = React19.useState(
|
|
1624
1712
|
() => seed(checkout?.email ?? null, checkout?.shipping_address ?? null)
|
|
1625
1713
|
);
|
|
1626
|
-
const
|
|
1714
|
+
const touched = React19.useRef(/* @__PURE__ */ new Set());
|
|
1715
|
+
React19.useEffect(() => {
|
|
1716
|
+
const prefill = checkout?.customer_prefill;
|
|
1717
|
+
const addresses = prefill?.addresses.filter((a) => !a.is_pickup);
|
|
1718
|
+
const saved = addresses?.find((a) => a.is_default) ?? addresses?.[0];
|
|
1719
|
+
const address = checkout?.shipping_address ?? (saved ? { ...saved, postal_code: saved.zip } : null);
|
|
1720
|
+
const defaults = seed(checkout?.email ?? prefill?.email ?? null, address);
|
|
1721
|
+
defaults.first_name || (defaults.first_name = prefill?.first_name ?? "");
|
|
1722
|
+
defaults.last_name || (defaults.last_name = prefill?.last_name ?? "");
|
|
1723
|
+
defaults.phone || (defaults.phone = prefill?.phone ?? "");
|
|
1724
|
+
setForm((current) => Object.fromEntries(Object.entries(current).map(([key, value]) => [key, touched.current.has(key) ? value : value || defaults[key]])));
|
|
1725
|
+
}, [checkout?.token]);
|
|
1726
|
+
const set = (key) => (e) => {
|
|
1727
|
+
touched.current.add(key);
|
|
1728
|
+
setForm((f) => ({ ...f, [key]: e.target.value }));
|
|
1729
|
+
};
|
|
1627
1730
|
async function submit(e) {
|
|
1628
1731
|
e.preventDefault();
|
|
1629
1732
|
const { email, ...rest } = form;
|
|
@@ -1671,13 +1774,14 @@ function ShippingPicker({
|
|
|
1671
1774
|
locale
|
|
1672
1775
|
}) {
|
|
1673
1776
|
const t = useT();
|
|
1777
|
+
const loc = useFormatLocale(locale);
|
|
1674
1778
|
const { checkout, shippingQuotes, setShippingMethod, loading } = useCheckout();
|
|
1675
1779
|
const heading = title ?? t("checkout.shippingMethod");
|
|
1676
|
-
const [quotes, setQuotes] =
|
|
1677
|
-
const [selected, setSelected] =
|
|
1678
|
-
const [fetching, setFetching] =
|
|
1780
|
+
const [quotes, setQuotes] = React19.useState(null);
|
|
1781
|
+
const [selected, setSelected] = React19.useState(null);
|
|
1782
|
+
const [fetching, setFetching] = React19.useState(true);
|
|
1679
1783
|
const addressKey = checkout?.shipping_address ? `${checkout.shipping_address.country_code}:${checkout.shipping_address.postal_code}` : null;
|
|
1680
|
-
|
|
1784
|
+
React19.useEffect(() => {
|
|
1681
1785
|
let live = true;
|
|
1682
1786
|
setFetching(true);
|
|
1683
1787
|
shippingQuotes().then((qs) => {
|
|
@@ -1705,17 +1809,13 @@ function ShippingPicker({
|
|
|
1705
1809
|
}
|
|
1706
1810
|
return /* @__PURE__ */ jsxs(Section, { title: heading, children: [
|
|
1707
1811
|
/* @__PURE__ */ jsx("div", { role: "radiogroup", style: { display: "flex", flexDirection: "column", gap: "0.6rem" }, children: quotes.map((q) => {
|
|
1708
|
-
const days = estDays(q, t);
|
|
1709
1812
|
return /* @__PURE__ */ jsx(
|
|
1710
1813
|
SelectRow,
|
|
1711
1814
|
{
|
|
1712
1815
|
selected: selected === q.rate_id,
|
|
1713
1816
|
onSelect: () => setSelected(q.rate_id),
|
|
1714
|
-
trailing: q.is_free ? t("common.free") : formatCents(q.price_cents, q.currency,
|
|
1715
|
-
children: /* @__PURE__ */
|
|
1716
|
-
/* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: q.label }),
|
|
1717
|
-
days ? /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: days }) : null
|
|
1718
|
-
] })
|
|
1817
|
+
trailing: q.is_free ? t("common.free") : formatCents(q.price_cents, q.currency, loc),
|
|
1818
|
+
children: /* @__PURE__ */ jsx("span", { style: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: q.label }) })
|
|
1719
1819
|
},
|
|
1720
1820
|
q.rate_id
|
|
1721
1821
|
);
|
|
@@ -1723,14 +1823,38 @@ function ShippingPicker({
|
|
|
1723
1823
|
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { onClick: submit, busy: loading, disabled: selected == null, children: submitLabel ?? t("checkout.continueToPayment") }) })
|
|
1724
1824
|
] });
|
|
1725
1825
|
}
|
|
1726
|
-
function
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1826
|
+
function PaymentMethodPicker({
|
|
1827
|
+
methods,
|
|
1828
|
+
value,
|
|
1829
|
+
onSelect,
|
|
1830
|
+
currency,
|
|
1831
|
+
locale,
|
|
1832
|
+
disabled,
|
|
1833
|
+
label
|
|
1834
|
+
}) {
|
|
1835
|
+
const loc = useFormatLocale(locale);
|
|
1836
|
+
return /* @__PURE__ */ jsx(
|
|
1837
|
+
"div",
|
|
1838
|
+
{
|
|
1839
|
+
role: "radiogroup",
|
|
1840
|
+
"aria-label": label,
|
|
1841
|
+
style: { display: "flex", flexDirection: "column", gap: "0.6rem" },
|
|
1842
|
+
children: methods.map((m) => /* @__PURE__ */ jsx(
|
|
1843
|
+
SelectRow,
|
|
1844
|
+
{
|
|
1845
|
+
selected: value === m.method,
|
|
1846
|
+
disabled,
|
|
1847
|
+
onSelect: () => onSelect(m.method),
|
|
1848
|
+
trailing: m.surcharge_cents > 0 ? `+${formatCents(m.surcharge_cents, currency, loc)}` : void 0,
|
|
1849
|
+
children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
|
|
1850
|
+
/* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: m.display_label }),
|
|
1851
|
+
m.display_description ? /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: m.display_description }) : null
|
|
1852
|
+
] })
|
|
1853
|
+
},
|
|
1854
|
+
m.method
|
|
1855
|
+
))
|
|
1856
|
+
}
|
|
1857
|
+
);
|
|
1734
1858
|
}
|
|
1735
1859
|
function PaymentSection({
|
|
1736
1860
|
title,
|
|
@@ -1742,10 +1866,10 @@ function PaymentSection({
|
|
|
1742
1866
|
const t = useT();
|
|
1743
1867
|
const heading = title ?? t("checkout.payment");
|
|
1744
1868
|
const { checkout, paymentMethods, setPaymentMethod, pay, loading } = useCheckout();
|
|
1745
|
-
const [methods, setMethods] =
|
|
1746
|
-
const [fetching, setFetching] =
|
|
1869
|
+
const [methods, setMethods] = React19.useState(null);
|
|
1870
|
+
const [fetching, setFetching] = React19.useState(true);
|
|
1747
1871
|
const selected = checkout?.payment_method ?? null;
|
|
1748
|
-
|
|
1872
|
+
React19.useEffect(() => {
|
|
1749
1873
|
let live = true;
|
|
1750
1874
|
setFetching(true);
|
|
1751
1875
|
paymentMethods().then((ms) => {
|
|
@@ -1772,19 +1896,17 @@ function PaymentSection({
|
|
|
1772
1896
|
return /* @__PURE__ */ jsx(Section, { title: heading, children: /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.noPayment") }) });
|
|
1773
1897
|
}
|
|
1774
1898
|
return /* @__PURE__ */ jsxs(Section, { title: heading, children: [
|
|
1775
|
-
/* @__PURE__ */ jsx(
|
|
1776
|
-
|
|
1899
|
+
/* @__PURE__ */ jsx(
|
|
1900
|
+
PaymentMethodPicker,
|
|
1777
1901
|
{
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
m.method
|
|
1787
|
-
)) }),
|
|
1902
|
+
methods,
|
|
1903
|
+
value: selected,
|
|
1904
|
+
onSelect: (method) => void setPaymentMethod(method),
|
|
1905
|
+
currency: checkout?.totals.currency ?? "CZK",
|
|
1906
|
+
locale,
|
|
1907
|
+
label: heading
|
|
1908
|
+
}
|
|
1909
|
+
),
|
|
1788
1910
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1789
1911
|
/* @__PURE__ */ jsx(Button, { onClick: placeOrder, busy: loading, disabled: selected == null, children: isGateway ? t("checkout.payNow") : t("checkout.placeOrder") }),
|
|
1790
1912
|
isGateway ? /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
|
|
@@ -1797,12 +1919,13 @@ function OrderSummary({
|
|
|
1797
1919
|
locale
|
|
1798
1920
|
}) {
|
|
1799
1921
|
const tr = useT();
|
|
1922
|
+
const loc = useFormatLocale(locale);
|
|
1800
1923
|
const ctx = useCheckout();
|
|
1801
1924
|
const checkout = override ?? ctx.checkout;
|
|
1802
1925
|
if (checkout == null) return null;
|
|
1803
1926
|
const t = checkout.totals;
|
|
1804
1927
|
const c = t.currency;
|
|
1805
|
-
const money = (cents) => formatCents(cents, c,
|
|
1928
|
+
const money = (cents) => formatCents(cents, c, loc);
|
|
1806
1929
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1rem" }, children: [
|
|
1807
1930
|
!totalsOnly && checkout.line_items.length > 0 ? /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.75rem" }, children: checkout.line_items.map((li) => /* @__PURE__ */ jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
|
|
1808
1931
|
li.image_url ? /* @__PURE__ */ jsx(
|
|
@@ -1883,16 +2006,16 @@ function CheckoutFlow({
|
|
|
1883
2006
|
}) {
|
|
1884
2007
|
const t = useT();
|
|
1885
2008
|
const { checkout, loading, create } = useCheckout();
|
|
1886
|
-
const [step, setStep] =
|
|
1887
|
-
const [bootstrapped, setBootstrapped] =
|
|
1888
|
-
|
|
2009
|
+
const [step, setStep] = React19.useState("address");
|
|
2010
|
+
const [bootstrapped, setBootstrapped] = React19.useState(false);
|
|
2011
|
+
React19.useEffect(() => {
|
|
1889
2012
|
if (checkout || !cartToken || bootstrapped) return;
|
|
1890
2013
|
setBootstrapped(true);
|
|
1891
2014
|
void create(cartToken).catch(() => {
|
|
1892
2015
|
});
|
|
1893
2016
|
}, [checkout, cartToken, bootstrapped, create]);
|
|
1894
2017
|
const serverStep = reachedStep(checkout);
|
|
1895
|
-
|
|
2018
|
+
React19.useEffect(() => {
|
|
1896
2019
|
setStep((cur) => rank(serverStep) > rank(cur) ? serverStep : cur);
|
|
1897
2020
|
}, [serverStep]);
|
|
1898
2021
|
if (checkout?.order_id) {
|
|
@@ -2013,10 +2136,10 @@ function DefaultConfirmation({ checkout }) {
|
|
|
2013
2136
|
}
|
|
2014
2137
|
function useOrderStatus(statusToken, initial) {
|
|
2015
2138
|
const client = useStorefront();
|
|
2016
|
-
const [order, setOrder] =
|
|
2017
|
-
const [loading, setLoading] =
|
|
2018
|
-
const [error, setError] =
|
|
2019
|
-
const load =
|
|
2139
|
+
const [order, setOrder] = React19.useState(initial ?? null);
|
|
2140
|
+
const [loading, setLoading] = React19.useState(false);
|
|
2141
|
+
const [error, setError] = React19.useState(null);
|
|
2142
|
+
const load = React19.useCallback(async () => {
|
|
2020
2143
|
if (!statusToken) return;
|
|
2021
2144
|
setLoading(true);
|
|
2022
2145
|
try {
|
|
@@ -2028,12 +2151,85 @@ function useOrderStatus(statusToken, initial) {
|
|
|
2028
2151
|
setLoading(false);
|
|
2029
2152
|
}
|
|
2030
2153
|
}, [client, statusToken]);
|
|
2031
|
-
|
|
2154
|
+
React19.useEffect(() => {
|
|
2032
2155
|
if (initial) return;
|
|
2033
2156
|
void load();
|
|
2034
2157
|
}, [load, initial]);
|
|
2035
2158
|
return { order, loading, error, reload: () => void load() };
|
|
2036
2159
|
}
|
|
2160
|
+
function usePayment(token, initial) {
|
|
2161
|
+
const client = useStorefront();
|
|
2162
|
+
const [payment, setPayment] = React19.useState(initial ?? null);
|
|
2163
|
+
const [methods, setMethods] = React19.useState([]);
|
|
2164
|
+
const [loading, setLoading] = React19.useState(false);
|
|
2165
|
+
const [busy, setBusy] = React19.useState(false);
|
|
2166
|
+
const [error, setError] = React19.useState(null);
|
|
2167
|
+
const load = React19.useCallback(async () => {
|
|
2168
|
+
if (!token) return;
|
|
2169
|
+
setLoading(true);
|
|
2170
|
+
try {
|
|
2171
|
+
const [state, list] = await Promise.all([
|
|
2172
|
+
client.payment.get(token),
|
|
2173
|
+
client.payment.methods(token)
|
|
2174
|
+
]);
|
|
2175
|
+
setPayment(state);
|
|
2176
|
+
setMethods(list);
|
|
2177
|
+
setError(null);
|
|
2178
|
+
} catch (e) {
|
|
2179
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
2180
|
+
} finally {
|
|
2181
|
+
setLoading(false);
|
|
2182
|
+
}
|
|
2183
|
+
}, [client, token]);
|
|
2184
|
+
React19.useEffect(() => {
|
|
2185
|
+
if (!token) return;
|
|
2186
|
+
if (initial && initial.token === token) {
|
|
2187
|
+
setPayment(initial);
|
|
2188
|
+
client.payment.methods(token).then(setMethods).catch(() => setMethods([]));
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
void load();
|
|
2192
|
+
}, [token, load]);
|
|
2193
|
+
const selectMethod = React19.useCallback(
|
|
2194
|
+
async (method) => {
|
|
2195
|
+
if (!token) return null;
|
|
2196
|
+
setBusy(true);
|
|
2197
|
+
try {
|
|
2198
|
+
const next = await client.payment.selectMethod(token, method);
|
|
2199
|
+
setPayment(next);
|
|
2200
|
+
setError(null);
|
|
2201
|
+
return next;
|
|
2202
|
+
} catch (e) {
|
|
2203
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
2204
|
+
return null;
|
|
2205
|
+
} finally {
|
|
2206
|
+
setBusy(false);
|
|
2207
|
+
}
|
|
2208
|
+
},
|
|
2209
|
+
[client, token]
|
|
2210
|
+
);
|
|
2211
|
+
const startPayment = React19.useCallback(
|
|
2212
|
+
async (input) => {
|
|
2213
|
+
if (!token) return null;
|
|
2214
|
+
setBusy(true);
|
|
2215
|
+
try {
|
|
2216
|
+
const result = await client.payment.start(token, {
|
|
2217
|
+
return_url: input?.returnUrl,
|
|
2218
|
+
cancel_url: input?.cancelUrl
|
|
2219
|
+
});
|
|
2220
|
+
setError(null);
|
|
2221
|
+
return result.redirect_url;
|
|
2222
|
+
} catch (e) {
|
|
2223
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
2224
|
+
return null;
|
|
2225
|
+
} finally {
|
|
2226
|
+
setBusy(false);
|
|
2227
|
+
}
|
|
2228
|
+
},
|
|
2229
|
+
[client, token]
|
|
2230
|
+
);
|
|
2231
|
+
return { payment, methods, loading, busy, error, reload: load, selectMethod, startPayment };
|
|
2232
|
+
}
|
|
2037
2233
|
var PHASE_RANK = {
|
|
2038
2234
|
placed: 0,
|
|
2039
2235
|
paid: 1,
|
|
@@ -2129,6 +2325,7 @@ function OrderLines({
|
|
|
2129
2325
|
order,
|
|
2130
2326
|
locale
|
|
2131
2327
|
}) {
|
|
2328
|
+
const loc = useFormatLocale(locale);
|
|
2132
2329
|
return /* @__PURE__ */ jsxs("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
|
|
2133
2330
|
order.line_items.map((li) => /* @__PURE__ */ jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
|
|
2134
2331
|
li.image_url ? /* @__PURE__ */ jsx(
|
|
@@ -2149,11 +2346,11 @@ function OrderLines({
|
|
|
2149
2346
|
li.quantity
|
|
2150
2347
|
] })
|
|
2151
2348
|
] }),
|
|
2152
|
-
/* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(li.total_with_tax_cents, order.currency,
|
|
2349
|
+
/* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(li.total_with_tax_cents, order.currency, loc) })
|
|
2153
2350
|
] }, li.id)),
|
|
2154
2351
|
order.fee_lines.map((f, i) => /* @__PURE__ */ jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
|
|
2155
2352
|
/* @__PURE__ */ jsx("span", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsx("span", { style: { display: "block", fontWeight: 500 }, children: f.title }) }),
|
|
2156
|
-
/* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(f.total_with_tax_cents, order.currency,
|
|
2353
|
+
/* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(f.total_with_tax_cents, order.currency, loc) })
|
|
2157
2354
|
] }, `fee-${i}`))
|
|
2158
2355
|
] });
|
|
2159
2356
|
}
|
|
@@ -2162,9 +2359,10 @@ function OrderTotals({
|
|
|
2162
2359
|
locale
|
|
2163
2360
|
}) {
|
|
2164
2361
|
const tr = useT();
|
|
2362
|
+
const loc = useFormatLocale(locale);
|
|
2165
2363
|
const t = order.totals;
|
|
2166
2364
|
const c = order.currency;
|
|
2167
|
-
const money = (cents) => formatCents(cents, c,
|
|
2365
|
+
const money = (cents) => formatCents(cents, c, loc);
|
|
2168
2366
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
2169
2367
|
/* @__PURE__ */ jsx(Row3, { label: tr("totals.subtotal"), value: money(t.subtotal_cents) }),
|
|
2170
2368
|
t.discount_cents > 0 ? /* @__PURE__ */ jsx(Row3, { label: tr("totals.discount"), value: `\u2212${money(t.discount_cents)}`, muted: true }) : null,
|
|
@@ -2270,6 +2468,7 @@ function OrderStatusPage({
|
|
|
2270
2468
|
statusToken,
|
|
2271
2469
|
order: initial,
|
|
2272
2470
|
onContactSupport,
|
|
2471
|
+
payHref,
|
|
2273
2472
|
locale
|
|
2274
2473
|
}) {
|
|
2275
2474
|
const t = useT();
|
|
@@ -2321,6 +2520,7 @@ function OrderStatusPage({
|
|
|
2321
2520
|
/* @__PURE__ */ jsxs(Card, { title: t("order.payment"), children: [
|
|
2322
2521
|
/* @__PURE__ */ jsx(Line, { label: t("order.method"), value: order.payment.method ?? "\u2014" }),
|
|
2323
2522
|
/* @__PURE__ */ jsx(Line, { label: t("order.status"), value: order.payment.status }),
|
|
2523
|
+
order.payment.token && payHref ? /* @__PURE__ */ jsx("a", { href: payHref(order.payment.token), style: { color: "var(--nv-brand)", fontWeight: 500 }, children: t("payment.finish") }) : null,
|
|
2324
2524
|
order.payment.invoice_available && order.payment.invoice_url ? /* @__PURE__ */ jsx("a", { href: order.payment.invoice_url, style: { color: "var(--nv-brand)" }, children: t("order.downloadInvoice") }) : null
|
|
2325
2525
|
] }),
|
|
2326
2526
|
order.actions.can_contact_support && onContactSupport ? /* @__PURE__ */ jsx(
|
|
@@ -2372,27 +2572,291 @@ function Line({ label, value }) {
|
|
|
2372
2572
|
/* @__PURE__ */ jsx("span", { style: { textAlign: "right" }, children: value })
|
|
2373
2573
|
] });
|
|
2374
2574
|
}
|
|
2375
|
-
|
|
2575
|
+
function TransferInstructions({
|
|
2576
|
+
instructions: i,
|
|
2577
|
+
locale
|
|
2578
|
+
}) {
|
|
2579
|
+
const t = useT();
|
|
2580
|
+
const loc = useFormatLocale(locale);
|
|
2581
|
+
const account = i.iban || i.account_number;
|
|
2582
|
+
return /* @__PURE__ */ jsxs(
|
|
2583
|
+
"div",
|
|
2584
|
+
{
|
|
2585
|
+
style: {
|
|
2586
|
+
display: "flex",
|
|
2587
|
+
flexWrap: "wrap",
|
|
2588
|
+
gap: "1.25rem",
|
|
2589
|
+
alignItems: "flex-start",
|
|
2590
|
+
padding: "1rem",
|
|
2591
|
+
borderRadius: "var(--nv-radius)",
|
|
2592
|
+
border: "1px solid var(--nv-border)"
|
|
2593
|
+
},
|
|
2594
|
+
children: [
|
|
2595
|
+
/* @__PURE__ */ jsxs("dl", { style: { flex: "1 1 15rem", margin: 0, display: "flex", flexDirection: "column", gap: "0.7rem" }, children: [
|
|
2596
|
+
account ? /* @__PURE__ */ jsx(CopyRow, { label: t("payment.account"), value: account }) : null,
|
|
2597
|
+
i.beneficiary_name ? /* @__PURE__ */ jsx(Row4, { label: t("payment.beneficiary"), value: i.beneficiary_name }) : null,
|
|
2598
|
+
/* @__PURE__ */ jsx(CopyRow, { label: t("payment.variableSymbol"), value: i.variable_symbol }),
|
|
2599
|
+
/* @__PURE__ */ jsx(
|
|
2600
|
+
Row4,
|
|
2601
|
+
{
|
|
2602
|
+
label: t("payment.amount"),
|
|
2603
|
+
value: formatCents(i.amount_cents, i.currency, loc),
|
|
2604
|
+
strong: true
|
|
2605
|
+
}
|
|
2606
|
+
)
|
|
2607
|
+
] }),
|
|
2608
|
+
i.qr_svg_data_uri ? /* @__PURE__ */ jsxs("figure", { style: { margin: 0, textAlign: "center" }, children: [
|
|
2609
|
+
/* @__PURE__ */ jsx(
|
|
2610
|
+
"img",
|
|
2611
|
+
{
|
|
2612
|
+
src: i.qr_svg_data_uri,
|
|
2613
|
+
alt: t("payment.qrAlt"),
|
|
2614
|
+
width: 148,
|
|
2615
|
+
height: 148,
|
|
2616
|
+
style: { display: "block", borderRadius: "var(--nv-radius)" }
|
|
2617
|
+
}
|
|
2618
|
+
),
|
|
2619
|
+
/* @__PURE__ */ jsx("figcaption", { style: { fontSize: "0.75rem", color: "var(--nv-muted)", marginTop: "0.4rem" }, children: t("payment.qrCaption") })
|
|
2620
|
+
] }) : null,
|
|
2621
|
+
i.instructions ? /* @__PURE__ */ jsx("p", { style: { flexBasis: "100%", margin: 0, fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: i.instructions }) : null
|
|
2622
|
+
]
|
|
2623
|
+
}
|
|
2624
|
+
);
|
|
2625
|
+
}
|
|
2626
|
+
function Row4({
|
|
2627
|
+
label,
|
|
2628
|
+
value,
|
|
2629
|
+
strong
|
|
2630
|
+
}) {
|
|
2631
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.1rem" }, children: [
|
|
2632
|
+
/* @__PURE__ */ jsx("dt", { style: { fontSize: "0.75rem", color: "var(--nv-muted)" }, children: label }),
|
|
2633
|
+
/* @__PURE__ */ jsx(
|
|
2634
|
+
"dd",
|
|
2635
|
+
{
|
|
2636
|
+
style: {
|
|
2637
|
+
margin: 0,
|
|
2638
|
+
fontWeight: strong ? 600 : 500,
|
|
2639
|
+
fontVariantNumeric: "tabular-nums"
|
|
2640
|
+
},
|
|
2641
|
+
children: value
|
|
2642
|
+
}
|
|
2643
|
+
)
|
|
2644
|
+
] });
|
|
2645
|
+
}
|
|
2646
|
+
function CopyRow({ label, value }) {
|
|
2647
|
+
const t = useT();
|
|
2648
|
+
const [copied, setCopied] = React19.useState(false);
|
|
2649
|
+
React19.useEffect(() => {
|
|
2650
|
+
if (!copied) return;
|
|
2651
|
+
const id = setTimeout(() => setCopied(false), 1800);
|
|
2652
|
+
return () => clearTimeout(id);
|
|
2653
|
+
}, [copied]);
|
|
2654
|
+
async function copy() {
|
|
2655
|
+
try {
|
|
2656
|
+
await navigator.clipboard.writeText(value);
|
|
2657
|
+
setCopied(true);
|
|
2658
|
+
} catch {
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.1rem" }, children: [
|
|
2662
|
+
/* @__PURE__ */ jsx("dt", { style: { fontSize: "0.75rem", color: "var(--nv-muted)" }, children: label }),
|
|
2663
|
+
/* @__PURE__ */ jsxs("dd", { style: { margin: 0, display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
2664
|
+
/* @__PURE__ */ jsx("span", { style: { fontWeight: 500, fontVariantNumeric: "tabular-nums" }, children: value }),
|
|
2665
|
+
/* @__PURE__ */ jsx(
|
|
2666
|
+
"button",
|
|
2667
|
+
{
|
|
2668
|
+
type: "button",
|
|
2669
|
+
onClick: () => void copy(),
|
|
2670
|
+
style: {
|
|
2671
|
+
font: "inherit",
|
|
2672
|
+
fontSize: "0.75rem",
|
|
2673
|
+
cursor: "pointer",
|
|
2674
|
+
padding: "0.15rem 0.5rem",
|
|
2675
|
+
borderRadius: "var(--nv-radius)",
|
|
2676
|
+
border: "1px solid var(--nv-border)",
|
|
2677
|
+
background: "transparent",
|
|
2678
|
+
color: "var(--nv-muted)"
|
|
2679
|
+
},
|
|
2680
|
+
children: copied ? t("payment.copied") : t("payment.copy")
|
|
2681
|
+
}
|
|
2682
|
+
)
|
|
2683
|
+
] })
|
|
2684
|
+
] });
|
|
2685
|
+
}
|
|
2686
|
+
var HANDOFF_TTL_MS = 30 * 60 * 1e3;
|
|
2687
|
+
var POLL_MS = 2e3;
|
|
2688
|
+
var POLL_ATTEMPTS = 6;
|
|
2689
|
+
var BLOCKED_MESSAGE = {
|
|
2690
|
+
order_cancelled: "payment.blocked.orderCancelled",
|
|
2691
|
+
already_paid: "payment.blocked.alreadyPaid",
|
|
2692
|
+
authorization_held: "payment.blocked.authorizationHeld",
|
|
2693
|
+
document_issued: "payment.blocked.documentIssued"
|
|
2694
|
+
};
|
|
2695
|
+
function handoffKey(token) {
|
|
2696
|
+
return `nv_payment_handoff_${token}`;
|
|
2697
|
+
}
|
|
2698
|
+
function rememberHandoff(token) {
|
|
2699
|
+
try {
|
|
2700
|
+
sessionStorage.setItem(handoffKey(token), String(Date.now()));
|
|
2701
|
+
} catch {
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
function consumeHandoff(token) {
|
|
2705
|
+
try {
|
|
2706
|
+
const raw = sessionStorage.getItem(handoffKey(token));
|
|
2707
|
+
if (!raw) return false;
|
|
2708
|
+
const fresh = Date.now() - Number(raw) < HANDOFF_TTL_MS;
|
|
2709
|
+
if (!fresh) sessionStorage.removeItem(handoffKey(token));
|
|
2710
|
+
return fresh;
|
|
2711
|
+
} catch {
|
|
2712
|
+
return false;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
function forgetHandoff(token) {
|
|
2716
|
+
try {
|
|
2717
|
+
sessionStorage.removeItem(handoffKey(token));
|
|
2718
|
+
} catch {
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
function PaymentPage({
|
|
2722
|
+
token,
|
|
2723
|
+
payment: initial,
|
|
2724
|
+
returnUrl,
|
|
2725
|
+
cancelUrl,
|
|
2726
|
+
statusHref,
|
|
2727
|
+
locale,
|
|
2728
|
+
onPaid
|
|
2729
|
+
}) {
|
|
2730
|
+
const t = useT();
|
|
2731
|
+
const loc = useFormatLocale(locale);
|
|
2732
|
+
const { payment, methods, loading, busy, error, reload, selectMethod, startPayment } = usePayment(token, initial);
|
|
2733
|
+
const [verifying, setVerifying] = React19.useState(false);
|
|
2734
|
+
React19.useEffect(() => {
|
|
2735
|
+
if (token) setVerifying(consumeHandoff(token));
|
|
2736
|
+
}, [token]);
|
|
2737
|
+
const settled = payment != null && !payment.can_pay && payment.blocked_reason === "already_paid";
|
|
2738
|
+
React19.useEffect(() => {
|
|
2739
|
+
if (!verifying || !token || settled) return;
|
|
2740
|
+
let attempts = 0;
|
|
2741
|
+
const id = setInterval(() => {
|
|
2742
|
+
attempts += 1;
|
|
2743
|
+
if (attempts > POLL_ATTEMPTS) {
|
|
2744
|
+
setVerifying(false);
|
|
2745
|
+
forgetHandoff(token);
|
|
2746
|
+
clearInterval(id);
|
|
2747
|
+
return;
|
|
2748
|
+
}
|
|
2749
|
+
void reload();
|
|
2750
|
+
}, POLL_MS);
|
|
2751
|
+
return () => clearInterval(id);
|
|
2752
|
+
}, [verifying, token, settled, reload]);
|
|
2753
|
+
React19.useEffect(() => {
|
|
2754
|
+
if (!settled || !token || !payment) return;
|
|
2755
|
+
setVerifying(false);
|
|
2756
|
+
forgetHandoff(token);
|
|
2757
|
+
onPaid?.(payment);
|
|
2758
|
+
}, [settled, token, payment, onPaid]);
|
|
2759
|
+
if (!token) return null;
|
|
2760
|
+
if (payment == null) {
|
|
2761
|
+
return /* @__PURE__ */ jsx(Shell2, { children: /* @__PURE__ */ jsx("p", { style: { margin: 0, color: "var(--nv-muted)" }, children: loading ? t("payment.loading") : t("payment.loadError") }) });
|
|
2762
|
+
}
|
|
2763
|
+
const money = (cents) => formatCents(cents, payment.currency, loc);
|
|
2764
|
+
if (settled) {
|
|
2765
|
+
return /* @__PURE__ */ jsxs(Shell2, { title: t("payment.paidTitle"), children: [
|
|
2766
|
+
/* @__PURE__ */ jsx("p", { style: { margin: 0 }, children: t("payment.paidBody", { order: payment.order_name ?? payment.order_number }) }),
|
|
2767
|
+
/* @__PURE__ */ jsx(Amount, { label: t("payment.paidAmount"), value: money(payment.total_cents) }),
|
|
2768
|
+
statusHref ? /* @__PURE__ */ jsx(Link, { href: statusHref, children: t("order.trackOrder") }) : null
|
|
2769
|
+
] });
|
|
2770
|
+
}
|
|
2771
|
+
if (payment.blocked_reason) {
|
|
2772
|
+
return /* @__PURE__ */ jsxs(Shell2, { title: t("payment.blockedTitle"), children: [
|
|
2773
|
+
/* @__PURE__ */ jsx("p", { style: { margin: 0 }, children: t(BLOCKED_MESSAGE[payment.blocked_reason]) }),
|
|
2774
|
+
statusHref ? /* @__PURE__ */ jsx(Link, { href: statusHref, children: t("order.trackOrder") }) : null
|
|
2775
|
+
] });
|
|
2776
|
+
}
|
|
2777
|
+
if (verifying) {
|
|
2778
|
+
return /* @__PURE__ */ jsx(Shell2, { title: t("payment.verifyingTitle"), children: /* @__PURE__ */ jsx("p", { style: { margin: 0, color: "var(--nv-muted)" }, children: t("payment.verifyingBody") }) });
|
|
2779
|
+
}
|
|
2780
|
+
const isBank = payment.category === "bank";
|
|
2781
|
+
const isGateway = payment.category === "gateway";
|
|
2782
|
+
async function pay() {
|
|
2783
|
+
if (!token) return;
|
|
2784
|
+
rememberHandoff(token);
|
|
2785
|
+
const url = await startPayment({ returnUrl, cancelUrl });
|
|
2786
|
+
if (url) {
|
|
2787
|
+
window.location.assign(url);
|
|
2788
|
+
return;
|
|
2789
|
+
}
|
|
2790
|
+
forgetHandoff(token);
|
|
2791
|
+
await reload();
|
|
2792
|
+
}
|
|
2793
|
+
return /* @__PURE__ */ jsxs(Shell2, { title: t("payment.title", { order: payment.order_name ?? payment.order_number }), children: [
|
|
2794
|
+
/* @__PURE__ */ jsx(Amount, { label: t("payment.amountDue"), value: money(payment.amount_due_cents) }),
|
|
2795
|
+
methods.length > 0 ? /* @__PURE__ */ jsx(
|
|
2796
|
+
PaymentMethodPicker,
|
|
2797
|
+
{
|
|
2798
|
+
methods,
|
|
2799
|
+
value: payment.method,
|
|
2800
|
+
onSelect: (method) => void selectMethod(method),
|
|
2801
|
+
currency: payment.currency,
|
|
2802
|
+
locale: loc,
|
|
2803
|
+
disabled: busy,
|
|
2804
|
+
label: t("payment.methodLabel")
|
|
2805
|
+
}
|
|
2806
|
+
) : null,
|
|
2807
|
+
isBank && payment.instructions ? /* @__PURE__ */ jsx(TransferInstructions, { instructions: payment.instructions, locale: loc }) : null,
|
|
2808
|
+
!isBank ? /* @__PURE__ */ jsxs("div", { children: [
|
|
2809
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void pay(), busy, children: t("payment.payNow") }),
|
|
2810
|
+
isGateway ? /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
|
|
2811
|
+
] }) : null,
|
|
2812
|
+
error ? /* @__PURE__ */ jsx("p", { role: "alert", style: { margin: 0, color: "var(--nv-danger, #b3261e)" }, children: t("payment.actionError") }) : null,
|
|
2813
|
+
statusHref ? /* @__PURE__ */ jsx(Link, { href: statusHref, children: t("order.trackOrder") }) : null
|
|
2814
|
+
] });
|
|
2815
|
+
}
|
|
2816
|
+
function Shell2({
|
|
2817
|
+
title,
|
|
2818
|
+
children
|
|
2819
|
+
}) {
|
|
2820
|
+
return /* @__PURE__ */ jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "1rem", maxWidth: "34rem" }, children: [
|
|
2821
|
+
title ? /* @__PURE__ */ jsx("h1", { style: { margin: 0, fontSize: "1.25rem", fontWeight: 600 }, children: title }) : null,
|
|
2822
|
+
children
|
|
2823
|
+
] });
|
|
2824
|
+
}
|
|
2825
|
+
function Amount({ label, value }) {
|
|
2826
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline" }, children: [
|
|
2827
|
+
/* @__PURE__ */ jsx("span", { style: { color: "var(--nv-muted)" }, children: label }),
|
|
2828
|
+
/* @__PURE__ */ jsx("strong", { style: { fontSize: "1.35rem", fontVariantNumeric: "tabular-nums" }, children: value })
|
|
2829
|
+
] });
|
|
2830
|
+
}
|
|
2831
|
+
function Link({ href, children }) {
|
|
2832
|
+
return /* @__PURE__ */ jsx("a", { href, style: { color: "var(--nv-brand)", fontSize: "0.875rem" }, children });
|
|
2833
|
+
}
|
|
2834
|
+
var AccountContext = React19.createContext(null);
|
|
2376
2835
|
function AccountProvider({ children }) {
|
|
2377
2836
|
const client = useStorefront();
|
|
2378
|
-
const [customer, setCustomer] =
|
|
2379
|
-
const [loading, setLoading] =
|
|
2380
|
-
const
|
|
2837
|
+
const [customer, setCustomer] = React19.useState(null);
|
|
2838
|
+
const [loading, setLoading] = React19.useState(true);
|
|
2839
|
+
const [error, setError] = React19.useState(false);
|
|
2840
|
+
const refresh = React19.useCallback(async () => {
|
|
2381
2841
|
setLoading(true);
|
|
2842
|
+
setError(false);
|
|
2382
2843
|
try {
|
|
2383
2844
|
setCustomer(await client.account.me());
|
|
2845
|
+
} catch {
|
|
2846
|
+
setError(true);
|
|
2384
2847
|
} finally {
|
|
2385
2848
|
setLoading(false);
|
|
2386
2849
|
}
|
|
2387
2850
|
}, [client]);
|
|
2388
|
-
|
|
2851
|
+
React19.useEffect(() => {
|
|
2389
2852
|
void refresh();
|
|
2390
2853
|
}, [refresh]);
|
|
2391
|
-
const value =
|
|
2854
|
+
const value = React19.useMemo(
|
|
2392
2855
|
() => ({
|
|
2393
2856
|
customer,
|
|
2394
2857
|
signedIn: customer != null,
|
|
2395
2858
|
loading,
|
|
2859
|
+
error,
|
|
2396
2860
|
requestMagicLink: (email) => client.account.requestMagicLink(email),
|
|
2397
2861
|
verify: async (token) => {
|
|
2398
2862
|
const c = await client.account.verify(token);
|
|
@@ -2405,12 +2869,12 @@ function AccountProvider({ children }) {
|
|
|
2405
2869
|
},
|
|
2406
2870
|
refresh
|
|
2407
2871
|
}),
|
|
2408
|
-
[customer, loading, client, refresh]
|
|
2872
|
+
[customer, loading, error, client, refresh]
|
|
2409
2873
|
);
|
|
2410
2874
|
return /* @__PURE__ */ jsx(AccountContext.Provider, { value, children });
|
|
2411
2875
|
}
|
|
2412
2876
|
function useAccount() {
|
|
2413
|
-
const ctx =
|
|
2877
|
+
const ctx = React19.useContext(AccountContext);
|
|
2414
2878
|
if (!ctx) {
|
|
2415
2879
|
throw new Error("useAccount must be used inside <AccountProvider>.");
|
|
2416
2880
|
}
|
|
@@ -2418,9 +2882,9 @@ function useAccount() {
|
|
|
2418
2882
|
}
|
|
2419
2883
|
function useMagicLinkVerify(token) {
|
|
2420
2884
|
const { verify } = useAccount();
|
|
2421
|
-
const [status, setStatus] =
|
|
2422
|
-
const done =
|
|
2423
|
-
|
|
2885
|
+
const [status, setStatus] = React19.useState("idle");
|
|
2886
|
+
const done = React19.useRef(false);
|
|
2887
|
+
React19.useEffect(() => {
|
|
2424
2888
|
if (!token || done.current) return;
|
|
2425
2889
|
done.current = true;
|
|
2426
2890
|
setStatus("verifying");
|
|
@@ -2431,18 +2895,22 @@ function useMagicLinkVerify(token) {
|
|
|
2431
2895
|
function LoginForm({ verifyPath = "/account/verify", onSent }) {
|
|
2432
2896
|
const t = useT();
|
|
2433
2897
|
const { requestMagicLink } = useAccount();
|
|
2434
|
-
const [email, setEmail] =
|
|
2435
|
-
const [busy, setBusy] =
|
|
2436
|
-
const [sent, setSent] =
|
|
2437
|
-
const [
|
|
2898
|
+
const [email, setEmail] = React19.useState("");
|
|
2899
|
+
const [busy, setBusy] = React19.useState(false);
|
|
2900
|
+
const [sent, setSent] = React19.useState(false);
|
|
2901
|
+
const [error, setError] = React19.useState(false);
|
|
2902
|
+
const [devLink, setDevLink] = React19.useState(null);
|
|
2438
2903
|
async function submit(e) {
|
|
2439
2904
|
e.preventDefault();
|
|
2440
2905
|
setBusy(true);
|
|
2906
|
+
setError(false);
|
|
2441
2907
|
try {
|
|
2442
2908
|
const r = await requestMagicLink(email);
|
|
2443
2909
|
setSent(true);
|
|
2444
2910
|
if (r.devToken) setDevLink(`${verifyPath}?token=${encodeURIComponent(r.devToken)}`);
|
|
2445
2911
|
onSent?.(email);
|
|
2912
|
+
} catch {
|
|
2913
|
+
setError(true);
|
|
2446
2914
|
} finally {
|
|
2447
2915
|
setBusy(false);
|
|
2448
2916
|
}
|
|
@@ -2481,6 +2949,7 @@ function LoginForm({ verifyPath = "/account/verify", onSent }) {
|
|
|
2481
2949
|
}
|
|
2482
2950
|
)
|
|
2483
2951
|
] }),
|
|
2952
|
+
error ? /* @__PURE__ */ jsx("p", { role: "alert", children: t("account.signInError") }) : null,
|
|
2484
2953
|
/* @__PURE__ */ jsx(
|
|
2485
2954
|
"button",
|
|
2486
2955
|
{
|
|
@@ -2542,20 +3011,47 @@ function OrderHistory({
|
|
|
2542
3011
|
const loc = useFormatLocale(locale);
|
|
2543
3012
|
const { signedIn } = useAccount();
|
|
2544
3013
|
const client = useStorefront();
|
|
2545
|
-
const [orders, setOrders] =
|
|
2546
|
-
const [
|
|
2547
|
-
const [
|
|
2548
|
-
|
|
3014
|
+
const [orders, setOrders] = React19.useState(null);
|
|
3015
|
+
const [error, setError] = React19.useState(false);
|
|
3016
|
+
const [nextCursor, setNextCursor] = React19.useState(null);
|
|
3017
|
+
const [pageLoading, setPageLoading] = React19.useState(false);
|
|
3018
|
+
const [reload, setReload] = React19.useState(0);
|
|
3019
|
+
const [reordering, setReordering] = React19.useState(null);
|
|
3020
|
+
const [notice, setNotice] = React19.useState(null);
|
|
3021
|
+
React19.useEffect(() => {
|
|
2549
3022
|
let live = true;
|
|
2550
3023
|
if (!signedIn) {
|
|
2551
3024
|
setOrders([]);
|
|
2552
3025
|
return;
|
|
2553
3026
|
}
|
|
2554
|
-
|
|
3027
|
+
setError(false);
|
|
3028
|
+
setOrders(null);
|
|
3029
|
+
client.account.ordersPage().then((page) => {
|
|
3030
|
+
if (live) {
|
|
3031
|
+
setOrders(page.orders);
|
|
3032
|
+
setNextCursor(page.nextCursor);
|
|
3033
|
+
}
|
|
3034
|
+
}).catch(() => {
|
|
3035
|
+
if (live) setError(true);
|
|
3036
|
+
});
|
|
2555
3037
|
return () => {
|
|
2556
3038
|
live = false;
|
|
2557
3039
|
};
|
|
2558
|
-
}, [signedIn, client]);
|
|
3040
|
+
}, [signedIn, client, reload]);
|
|
3041
|
+
async function loadMore() {
|
|
3042
|
+
if (!nextCursor || pageLoading) return;
|
|
3043
|
+
setPageLoading(true);
|
|
3044
|
+
setError(false);
|
|
3045
|
+
try {
|
|
3046
|
+
const page = await client.account.ordersPage({ beforeId: nextCursor });
|
|
3047
|
+
setOrders((previous) => [...previous ?? [], ...page.orders]);
|
|
3048
|
+
setNextCursor(page.nextCursor);
|
|
3049
|
+
} catch {
|
|
3050
|
+
setError(true);
|
|
3051
|
+
} finally {
|
|
3052
|
+
setPageLoading(false);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
2559
3055
|
async function reorder(statusToken) {
|
|
2560
3056
|
setReordering(statusToken);
|
|
2561
3057
|
setNotice(null);
|
|
@@ -2576,7 +3072,11 @@ function OrderHistory({
|
|
|
2576
3072
|
}
|
|
2577
3073
|
return /* @__PURE__ */ jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
|
|
2578
3074
|
/* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.orders") }),
|
|
2579
|
-
|
|
3075
|
+
error ? /* @__PURE__ */ jsxs("div", { role: "alert", children: [
|
|
3076
|
+
/* @__PURE__ */ jsx("p", { children: t("account.loadError") }),
|
|
3077
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => orders ? void loadMore() : setReload((x) => x + 1), children: t("account.retry") })
|
|
3078
|
+
] }) : null,
|
|
3079
|
+
orders == null ? error ? null : /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("common.loadingShort") }) : orders.length === 0 ? /* @__PURE__ */ jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("account.noOrders") }) : /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.5rem" }, children: orders.map((o) => /* @__PURE__ */ jsxs(
|
|
2580
3080
|
"li",
|
|
2581
3081
|
{
|
|
2582
3082
|
style: {
|
|
@@ -2623,12 +3123,13 @@ function OrderHistory({
|
|
|
2623
3123
|
]
|
|
2624
3124
|
},
|
|
2625
3125
|
o.status_token
|
|
2626
|
-
)) })
|
|
3126
|
+
)) }),
|
|
3127
|
+
nextCursor ? /* @__PURE__ */ jsx("button", { type: "button", disabled: pageLoading, onClick: () => void loadMore(), children: pageLoading ? t("common.loadingShort") : t("account.loadMore") }) : null
|
|
2627
3128
|
] });
|
|
2628
3129
|
}
|
|
2629
3130
|
function AddressForm2({ initial, onSubmit, onCancel, busy }) {
|
|
2630
3131
|
const t = useT();
|
|
2631
|
-
const [form, setForm] =
|
|
3132
|
+
const [form, setForm] = React19.useState(() => ({
|
|
2632
3133
|
first_name: initial?.first_name ?? "",
|
|
2633
3134
|
last_name: initial?.last_name ?? "",
|
|
2634
3135
|
company: initial?.company ?? "",
|
|
@@ -2726,17 +3227,17 @@ function AddressBook() {
|
|
|
2726
3227
|
const t = useT();
|
|
2727
3228
|
const { signedIn } = useAccount();
|
|
2728
3229
|
const client = useStorefront();
|
|
2729
|
-
const [addresses, setAddresses] =
|
|
2730
|
-
const [mode, setMode] =
|
|
2731
|
-
const [busy, setBusy] =
|
|
2732
|
-
const load =
|
|
3230
|
+
const [addresses, setAddresses] = React19.useState(null);
|
|
3231
|
+
const [mode, setMode] = React19.useState({ kind: "list" });
|
|
3232
|
+
const [busy, setBusy] = React19.useState(false);
|
|
3233
|
+
const load = React19.useCallback(() => {
|
|
2733
3234
|
if (!signedIn) {
|
|
2734
3235
|
setAddresses([]);
|
|
2735
3236
|
return;
|
|
2736
3237
|
}
|
|
2737
3238
|
client.account.addresses().then(setAddresses).catch(() => setAddresses([]));
|
|
2738
3239
|
}, [signedIn, client]);
|
|
2739
|
-
|
|
3240
|
+
React19.useEffect(() => {
|
|
2740
3241
|
load();
|
|
2741
3242
|
}, [load]);
|
|
2742
3243
|
async function run(fn) {
|
|
@@ -2790,10 +3291,10 @@ function ProfileEditor() {
|
|
|
2790
3291
|
const t = useT();
|
|
2791
3292
|
const client = useStorefront();
|
|
2792
3293
|
const { customer, refresh } = useAccount();
|
|
2793
|
-
const [editing, setEditing] =
|
|
2794
|
-
const [busy, setBusy] =
|
|
2795
|
-
const [error, setError] =
|
|
2796
|
-
const [form, setForm] =
|
|
3294
|
+
const [editing, setEditing] = React19.useState(false);
|
|
3295
|
+
const [busy, setBusy] = React19.useState(false);
|
|
3296
|
+
const [error, setError] = React19.useState(false);
|
|
3297
|
+
const [form, setForm] = React19.useState({});
|
|
2797
3298
|
if (!customer) return null;
|
|
2798
3299
|
function startEdit() {
|
|
2799
3300
|
setForm({
|
|
@@ -2870,6 +3371,79 @@ function ProfileEditor() {
|
|
|
2870
3371
|
] })
|
|
2871
3372
|
] });
|
|
2872
3373
|
}
|
|
3374
|
+
function AccountSecurity() {
|
|
3375
|
+
const client = useStorefront();
|
|
3376
|
+
const account = useAccount();
|
|
3377
|
+
const t = useT();
|
|
3378
|
+
const [sessions, setSessions] = React19.useState(null);
|
|
3379
|
+
const [email, setEmail] = React19.useState("");
|
|
3380
|
+
const [busy, setBusy] = React19.useState(false);
|
|
3381
|
+
const [notice, setNotice] = React19.useState(null);
|
|
3382
|
+
const [confirmClose, setConfirmClose] = React19.useState(false);
|
|
3383
|
+
const [retry, setRetry] = React19.useState(0);
|
|
3384
|
+
React19.useEffect(() => {
|
|
3385
|
+
let live = true;
|
|
3386
|
+
if (account.signedIn) client.account.sessions().then((s) => {
|
|
3387
|
+
if (live) setSessions(s);
|
|
3388
|
+
}).catch(() => {
|
|
3389
|
+
if (live) setNotice(t("account.loadError"));
|
|
3390
|
+
});
|
|
3391
|
+
return () => {
|
|
3392
|
+
live = false;
|
|
3393
|
+
};
|
|
3394
|
+
}, [client, account.signedIn, retry, t]);
|
|
3395
|
+
async function act(operation) {
|
|
3396
|
+
setBusy(true);
|
|
3397
|
+
setNotice(null);
|
|
3398
|
+
try {
|
|
3399
|
+
await operation();
|
|
3400
|
+
} catch (e) {
|
|
3401
|
+
setNotice(e instanceof StorefrontError && e.status === 401 ? t("account.recentLogin") : t("account.loadError"));
|
|
3402
|
+
} finally {
|
|
3403
|
+
setBusy(false);
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
if (!account.signedIn) return null;
|
|
3407
|
+
return /* @__PURE__ */ jsxs("section", { style: { display: "grid", gap: "0.75rem" }, children: [
|
|
3408
|
+
/* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.security") }),
|
|
3409
|
+
notice ? /* @__PURE__ */ jsx("p", { role: "status", children: notice }) : null,
|
|
3410
|
+
/* @__PURE__ */ jsxs(
|
|
3411
|
+
"form",
|
|
3412
|
+
{
|
|
3413
|
+
style: { display: "flex", gap: "0.75rem", flexWrap: "wrap", alignItems: "end" },
|
|
3414
|
+
onSubmit: (e) => {
|
|
3415
|
+
e.preventDefault();
|
|
3416
|
+
void act(async () => {
|
|
3417
|
+
await client.account.requestEmailChange(email);
|
|
3418
|
+
setNotice(t("account.checkEmail"));
|
|
3419
|
+
});
|
|
3420
|
+
},
|
|
3421
|
+
children: [
|
|
3422
|
+
/* @__PURE__ */ jsx(Field, { label: t("account.newEmail"), type: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value) }),
|
|
3423
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", busy, children: t("account.changeEmail") })
|
|
3424
|
+
]
|
|
3425
|
+
}
|
|
3426
|
+
),
|
|
3427
|
+
/* @__PURE__ */ jsx("h3", { children: t("account.sessions") }),
|
|
3428
|
+
sessions ? sessions.map((s) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem", justifyContent: "space-between" }, children: [
|
|
3429
|
+
/* @__PURE__ */ jsx("span", { children: s.current ? t("account.currentSession") : /* @__PURE__ */ jsx("time", { dateTime: s.created_at, children: s.created_at.slice(0, 10) }) }),
|
|
3430
|
+
/* @__PURE__ */ jsx(Button, { busy, onClick: () => void act(async () => {
|
|
3431
|
+
await client.account.revokeSession(s.id);
|
|
3432
|
+
if (s.current) await account.logout();
|
|
3433
|
+
else setSessions((old) => old?.filter((row) => row.id !== s.id) ?? null);
|
|
3434
|
+
}), children: t("account.revokeSession") })
|
|
3435
|
+
] }, s.id)) : /* @__PURE__ */ jsx(Button, { onClick: () => setRetry((x) => x + 1), children: t("account.retry") }),
|
|
3436
|
+
confirmClose ? /* @__PURE__ */ jsxs("div", { children: [
|
|
3437
|
+
/* @__PURE__ */ jsx("p", { children: t("account.closeWarning") }),
|
|
3438
|
+
/* @__PURE__ */ jsx(Button, { busy, onClick: () => void act(async () => {
|
|
3439
|
+
await client.account.close();
|
|
3440
|
+
await account.refresh();
|
|
3441
|
+
}), children: t("account.confirmClose") }),
|
|
3442
|
+
" ",
|
|
3443
|
+
/* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => setConfirmClose(false), children: t("account.cancel") })
|
|
3444
|
+
] }) : /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => setConfirmClose(true), children: t("account.closeAccount") })
|
|
3445
|
+
] });
|
|
3446
|
+
}
|
|
2873
3447
|
function serializeJsonLd(data) {
|
|
2874
3448
|
return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
2875
3449
|
}
|
|
@@ -2974,6 +3548,6 @@ function OrganizationJsonLd({
|
|
|
2974
3548
|
return /* @__PURE__ */ jsx(JsonLd, { data });
|
|
2975
3549
|
}
|
|
2976
3550
|
|
|
2977
|
-
export { AccountDashboard, AccountProvider, AddToCart, AddressBook, AddressForm, AvailabilityFilter, BackInStockForm, BreadcrumbJsonLd, CartDrawer, CartLineItem, CartProvider, CartSummary, CheckoutFlow, CheckoutProvider, CollectionPage, I18nProvider, JsonLd, LoginForm, MarketSwitcher, NeviosProvider, OrderConfirmation, OrderHistory, OrderStatusPage, OrderSummary, OrganizationJsonLd, PaymentSection, PriceMoney, PriceRangeFilter, ProductCard, ProductDetailPage, ProductGallery, ProductJsonLd, ProfileEditor, ShippingPicker, SortSelect, StockBadge, TagFilter, ThemeProvider, VariantPicker, absoluteUrl, bundledMessages, cs, en, formatCents, formatMoney, serializeJsonLd, stripHtml, themeToCssVars, useAccount, useCart, useCheckout, useI18n, useMagicLinkVerify, useOrderStatus, useStorefront, useStorefrontOptional, useT };
|
|
3551
|
+
export { AccountDashboard, AccountProvider, AccountSecurity, AddToCart, AddressBook, AddressForm, AvailabilityFilter, BackInStockForm, BreadcrumbJsonLd, CartDrawer, CartLineItem, CartProvider, CartSummary, CheckoutFlow, CheckoutProvider, CollectionPage, I18nProvider, JsonLd, LoginForm, MarketSwitcher, NeviosProvider, OrderConfirmation, OrderHistory, OrderLines, OrderStatusPage, OrderSummary, OrderTotals, OrganizationJsonLd, PaymentMethodPicker, PaymentPage, PaymentSection, PhaseTimeline, PriceMoney, PriceRangeFilter, ProductCard, ProductDetailPage, ProductGallery, ProductJsonLd, ProfileEditor, ShippingPicker, SortSelect, StatusBanner, StockBadge, TagFilter, ThemeProvider, TransferInstructions, VariantPicker, absoluteUrl, bundledMessages, cs, en, formatCents, formatMoney, isTerminal, serializeJsonLd, stripHtml, themeToCssVars, useAccount, useCart, useCheckout, useI18n, useMagicLinkVerify, useOrderStatus, usePayment, useStorefront, useStorefrontOptional, useT };
|
|
2978
3552
|
//# sourceMappingURL=index.js.map
|
|
2979
3553
|
//# sourceMappingURL=index.js.map
|