@usethrottle/auth 0.1.2 → 0.2.1

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.
@@ -0,0 +1,4558 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/react/components/index.tsx
22
+ var components_exports = {};
23
+ __export(components_exports, {
24
+ ACCOUNT_TABS: () => ACCOUNT_TABS,
25
+ AccountDashboard: () => AccountDashboard,
26
+ AccountModal: () => AccountModal,
27
+ AddressesPanel: () => AddressesPanel,
28
+ AuthFlow: () => AuthFlow,
29
+ AuthModal: () => AuthModal,
30
+ ForgotPassword: () => ForgotPassword,
31
+ InvoiceDetail: () => InvoiceDetail,
32
+ InvoicesPanel: () => InvoicesPanel,
33
+ OrderDetail: () => OrderDetail,
34
+ OrdersPanel: () => OrdersPanel,
35
+ PaymentMethodsPanel: () => PaymentMethodsPanel,
36
+ ProfilePanel: () => ProfilePanel,
37
+ Protect: () => Protect,
38
+ ResetPassword: () => ResetPassword,
39
+ SecurityPanel: () => SecurityPanel,
40
+ SignIn: () => SignIn,
41
+ SignInButton: () => SignInButton,
42
+ SignInPage: () => SignInPage,
43
+ SignOutButton: () => SignOutButton,
44
+ SignUp: () => SignUp,
45
+ SignUpButton: () => SignUpButton,
46
+ SignUpPage: () => SignUpPage,
47
+ SignedIn: () => SignedIn,
48
+ SignedOut: () => SignedOut,
49
+ SubscriptionsPanel: () => SubscriptionsPanel,
50
+ THROTTLE_ELEMENT_KEYS: () => THROTTLE_ELEMENT_KEYS,
51
+ THROTTLE_STYLES: () => THROTTLE_STYLES,
52
+ THROTTLE_STYLE_ID: () => THROTTLE_STYLE_ID,
53
+ ThrottleAuthError: () => ThrottleAuthError,
54
+ ThrottleAuthProvider: () => ThrottleAuthProvider,
55
+ ThrottleProvider: () => ThrottleProvider,
56
+ UserButton: () => UserButton,
57
+ VerifyEmail: () => VerifyEmail,
58
+ VerifyEmailBanner: () => VerifyEmailBanner,
59
+ createAccount: () => createAccount,
60
+ createThrottleAuth: () => createThrottleAuth,
61
+ enUS: () => enUS,
62
+ formatDate: () => formatDate,
63
+ formatMoney: () => formatMoney,
64
+ localStorageAdapter: () => localStorageAdapter,
65
+ memoryStorage: () => memoryStorage,
66
+ useAddresses: () => useAddresses,
67
+ useAuth: () => useAuth,
68
+ useAuthContext: () => useAuthContext,
69
+ useCustomer: () => useCustomer,
70
+ useInvoice: () => useInvoice,
71
+ useInvoices: () => useInvoices,
72
+ useOrder: () => useOrder,
73
+ useOrders: () => useOrders,
74
+ useSavedCards: () => useSavedCards,
75
+ useSubscriptions: () => useSubscriptions,
76
+ useThrottle: () => useThrottle
77
+ });
78
+ module.exports = __toCommonJS(components_exports);
79
+
80
+ // src/react/components/provider.tsx
81
+ var import_react29 = require("react");
82
+
83
+ // src/errors.ts
84
+ var ThrottleAuthError = class extends Error {
85
+ code;
86
+ statusCode;
87
+ retryAfter;
88
+ details;
89
+ constructor(args) {
90
+ super(args.message);
91
+ this.name = "ThrottleAuthError";
92
+ this.code = args.code;
93
+ this.statusCode = args.statusCode;
94
+ this.retryAfter = args.retryAfter;
95
+ this.details = args.details;
96
+ }
97
+ };
98
+
99
+ // src/request.ts
100
+ async function request(input) {
101
+ const headers = { "X-API-Key": input.publishableKey };
102
+ if (input.body !== void 0) headers["Content-Type"] = "application/json";
103
+ if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;
104
+ if (input.stepUpToken) headers["X-Step-Up"] = input.stepUpToken;
105
+ const res = await fetch(`${input.baseUrl}/v1/storefront${input.path}`, {
106
+ method: input.method,
107
+ headers,
108
+ ...input.body !== void 0 ? { body: JSON.stringify(input.body) } : {}
109
+ });
110
+ const text = await res.text();
111
+ let parsed = {};
112
+ try {
113
+ parsed = text ? JSON.parse(text) : {};
114
+ } catch {
115
+ }
116
+ if (!res.ok) {
117
+ const e = parsed?.error ?? {};
118
+ const ra = Number(res.headers.get("retry-after"));
119
+ throw new ThrottleAuthError({
120
+ code: e.code ?? "request_failed",
121
+ message: e.message ?? `Request failed with ${res.status}`,
122
+ statusCode: res.status,
123
+ retryAfter: Number.isFinite(ra) && ra >= 0 ? ra : void 0,
124
+ details: e.details
125
+ });
126
+ }
127
+ return parsed && typeof parsed === "object" && "data" in parsed ? parsed.data : parsed ?? {};
128
+ }
129
+
130
+ // src/session.ts
131
+ var SessionState = class {
132
+ #snapshot = { status: "loading", customer: null, emailVerified: false };
133
+ #accessToken = null;
134
+ #expiresAt = null;
135
+ #listeners = /* @__PURE__ */ new Set();
136
+ get() {
137
+ return this.#snapshot;
138
+ }
139
+ accessToken() {
140
+ return this.#accessToken;
141
+ }
142
+ expiresAt() {
143
+ return this.#expiresAt;
144
+ }
145
+ setSession(session) {
146
+ this.#accessToken = session.accessToken;
147
+ this.#expiresAt = Date.parse(session.expiresAt);
148
+ this.#publish({
149
+ status: "authenticated",
150
+ customer: session.customer,
151
+ emailVerified: Boolean(session.customer?.emailVerified)
152
+ });
153
+ }
154
+ setStatus(status) {
155
+ this.#publish({ ...this.#snapshot, status });
156
+ }
157
+ clear() {
158
+ this.#accessToken = null;
159
+ this.#expiresAt = null;
160
+ this.#publish({ status: "unauthenticated", customer: null, emailVerified: false });
161
+ }
162
+ subscribe(fn) {
163
+ this.#listeners.add(fn);
164
+ return () => {
165
+ this.#listeners.delete(fn);
166
+ };
167
+ }
168
+ #publish(next) {
169
+ const same = next.status === this.#snapshot.status && next.emailVerified === this.#snapshot.emailVerified && customerEquals(next.customer, this.#snapshot.customer);
170
+ if (same) return;
171
+ this.#snapshot = next;
172
+ for (const fn of [...this.#listeners]) {
173
+ try {
174
+ fn(next);
175
+ } catch {
176
+ }
177
+ }
178
+ }
179
+ };
180
+ function customerEquals(a, b) {
181
+ if (a === b) return true;
182
+ if (!a || !b) return false;
183
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
184
+ for (const k of keys) if (a[k] !== b[k]) return false;
185
+ return true;
186
+ }
187
+
188
+ // src/storage.ts
189
+ function memoryStorage() {
190
+ const map = /* @__PURE__ */ new Map();
191
+ return {
192
+ get: (k) => map.get(k) ?? null,
193
+ set: (k, v) => void map.set(k, v),
194
+ remove: (k) => void map.delete(k)
195
+ };
196
+ }
197
+ function localStorageAdapter() {
198
+ const fallback = memoryStorage();
199
+ const ls = () => {
200
+ try {
201
+ return typeof localStorage !== "undefined" ? localStorage : null;
202
+ } catch {
203
+ return null;
204
+ }
205
+ };
206
+ return {
207
+ get(k) {
208
+ try {
209
+ const v = ls()?.getItem(k);
210
+ if (v != null) return v;
211
+ } catch {
212
+ }
213
+ return fallback.get(k);
214
+ },
215
+ set(k, v) {
216
+ fallback.set(k, v);
217
+ try {
218
+ ls()?.setItem(k, v);
219
+ } catch {
220
+ }
221
+ },
222
+ remove(k) {
223
+ fallback.remove(k);
224
+ try {
225
+ ls()?.removeItem(k);
226
+ } catch {
227
+ }
228
+ }
229
+ };
230
+ }
231
+
232
+ // src/client.ts
233
+ var DEFAULT_BASE_URL = "https://api.usethrottle.dev";
234
+ var REFRESH_SKEW_MS = 3e4;
235
+ function createThrottleAuth(config) {
236
+ const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
237
+ const storage = config.storage ?? localStorageAdapter();
238
+ const storageKey = `throttle_auth_rt:${config.publishableKey}`;
239
+ const lockName = `throttle_auth_refresh:${config.publishableKey}`;
240
+ const state = new SessionState();
241
+ let refreshInFlight = null;
242
+ let stepUpToken = null;
243
+ let stepUpExpiresAt = 0;
244
+ const base = { baseUrl, publishableKey: config.publishableKey };
245
+ async function persist(session) {
246
+ state.setSession(session);
247
+ await storage.set(storageKey, session.refreshToken);
248
+ }
249
+ async function forget() {
250
+ stepUpToken = null;
251
+ state.clear();
252
+ await storage.remove(storageKey);
253
+ }
254
+ function withLock(fn) {
255
+ const locks = globalThis.navigator?.locks;
256
+ return locks?.request ? locks.request(lockName, fn) : fn();
257
+ }
258
+ async function doRefresh() {
259
+ await withLock(async () => {
260
+ const refreshToken = await storage.get(storageKey);
261
+ if (!refreshToken) {
262
+ await forget();
263
+ throw new ThrottleAuthError({ code: "session_revoked", message: "No session", statusCode: 401 });
264
+ }
265
+ try {
266
+ const data = await request({ ...base, method: "POST", path: "/auth/refresh", body: { refreshToken } });
267
+ await persist(data);
268
+ } catch (err) {
269
+ const refused = err instanceof ThrottleAuthError && (err.statusCode === 401 || err.statusCode === 403);
270
+ if (refused) await forget();
271
+ throw err;
272
+ }
273
+ });
274
+ }
275
+ function refresh() {
276
+ if (!refreshInFlight) {
277
+ refreshInFlight = doRefresh().finally(() => {
278
+ refreshInFlight = null;
279
+ });
280
+ }
281
+ return refreshInFlight;
282
+ }
283
+ async function accessToken() {
284
+ const expires = state.expiresAt();
285
+ if (state.accessToken() && expires && expires - Date.now() > REFRESH_SKEW_MS) {
286
+ return state.accessToken();
287
+ }
288
+ if (await storage.get(storageKey)) {
289
+ await refresh();
290
+ return state.accessToken();
291
+ }
292
+ return state.accessToken();
293
+ }
294
+ async function ensureStepUp() {
295
+ if (stepUpToken && stepUpExpiresAt > Date.now()) return true;
296
+ if (!config.onStepUpRequired) return false;
297
+ const password = await config.onStepUpRequired();
298
+ if (!password) return false;
299
+ await stepUp(password);
300
+ return true;
301
+ }
302
+ async function stepUp(password) {
303
+ const data = await request({
304
+ ...base,
305
+ method: "POST",
306
+ path: "/auth/step-up",
307
+ accessToken: await accessToken() ?? void 0,
308
+ body: { password }
309
+ });
310
+ stepUpToken = data.stepUpToken;
311
+ stepUpExpiresAt = Date.now() + (Number(data.expiresIn) || 300) * 1e3 - 5e3;
312
+ }
313
+ async function call(method, path, body) {
314
+ let retriedAuth = false;
315
+ let retriedStepUp = false;
316
+ let sendStepUp = false;
317
+ for (; ; ) {
318
+ const token = await accessToken();
319
+ try {
320
+ return await request({
321
+ ...base,
322
+ method,
323
+ path,
324
+ body,
325
+ accessToken: token ?? void 0,
326
+ stepUpToken: sendStepUp ? stepUpToken ?? void 0 : void 0
327
+ });
328
+ } catch (err) {
329
+ const e = err;
330
+ const expired = e.statusCode === 401 && (e.code === "unauthorized" || e.code === "token_expired" || e.code === "session_revoked");
331
+ if (expired && !retriedAuth) {
332
+ retriedAuth = true;
333
+ await refresh();
334
+ continue;
335
+ }
336
+ if (e.code === "step_up_required" && !retriedStepUp) {
337
+ retriedStepUp = true;
338
+ if (await ensureStepUp()) {
339
+ sendStepUp = true;
340
+ continue;
341
+ }
342
+ }
343
+ throw err;
344
+ }
345
+ }
346
+ }
347
+ return {
348
+ async register(email, password) {
349
+ const data = await request({ ...base, method: "POST", path: "/auth/register", body: { email, password } });
350
+ if (data?.status === "pending") return { status: "pending" };
351
+ await persist(data);
352
+ return { status: "active" };
353
+ },
354
+ async signIn(email, password) {
355
+ const data = await request({ ...base, method: "POST", path: "/auth/login", body: { email, password } });
356
+ await persist(data);
357
+ },
358
+ async signOut() {
359
+ try {
360
+ await call("POST", "/auth/logout");
361
+ } catch {
362
+ }
363
+ await forget();
364
+ },
365
+ async signOutEverywhere() {
366
+ try {
367
+ await call("POST", "/auth/logout-all");
368
+ } catch {
369
+ }
370
+ await forget();
371
+ },
372
+ async restore() {
373
+ if (!await storage.get(storageKey)) {
374
+ state.setStatus("unauthenticated");
375
+ return;
376
+ }
377
+ try {
378
+ await refresh();
379
+ } catch {
380
+ }
381
+ },
382
+ getSnapshot: () => state.get(),
383
+ subscribe: (fn) => state.subscribe(fn),
384
+ stepUp,
385
+ call,
386
+ async verifyEmail(token) {
387
+ await request({ ...base, method: "POST", path: "/auth/verify-email", body: { token } });
388
+ if (await storage.get(storageKey)) {
389
+ try {
390
+ await refresh();
391
+ } catch {
392
+ }
393
+ }
394
+ },
395
+ async resendVerification() {
396
+ await call("POST", "/auth/verify-email/resend");
397
+ },
398
+ async forgotPassword(email) {
399
+ await request({ ...base, method: "POST", path: "/auth/forgot-password", body: { email } });
400
+ },
401
+ async resetPassword(token, password) {
402
+ await request({ ...base, method: "POST", path: "/auth/reset-password", body: { token, password } });
403
+ await forget();
404
+ },
405
+ async changePassword(password) {
406
+ await call("POST", "/me/change-password", { password });
407
+ try {
408
+ await refresh();
409
+ } catch {
410
+ }
411
+ },
412
+ async listSessions() {
413
+ return call("GET", "/me/sessions");
414
+ },
415
+ async revokeSession(id) {
416
+ const mine = await call("GET", "/me/sessions");
417
+ const current = mine.find((s) => s.current);
418
+ await call("DELETE", `/me/sessions/${encodeURIComponent(id)}`);
419
+ if (current?.id === id) await forget();
420
+ }
421
+ };
422
+ }
423
+
424
+ // src/account.ts
425
+ var seg = (v) => encodeURIComponent(v);
426
+ function query(page) {
427
+ const params = new URLSearchParams();
428
+ if (page?.cursor) params.set("cursor", page.cursor);
429
+ if (page?.limit != null) params.set("limit", String(page.limit));
430
+ const s = params.toString();
431
+ return s ? `?${s}` : "";
432
+ }
433
+ function createAccount(auth) {
434
+ const { call } = auth;
435
+ return {
436
+ getProfile: () => call("GET", "/me"),
437
+ updateProfile: (patch) => call("PATCH", "/me", patch),
438
+ listAddresses: () => call("GET", "/me/addresses"),
439
+ createAddress: (input) => call("POST", "/me/addresses", input),
440
+ updateAddress: (id, patch) => call("PATCH", `/me/addresses/${seg(id)}`, patch),
441
+ deleteAddress: (id) => call("DELETE", `/me/addresses/${seg(id)}`),
442
+ listPaymentMethods: () => call("GET", "/me/payment-methods"),
443
+ // The platform has no "change this subscription's card" capability, so
444
+ // this is how a buyer changes what they are billed on: move the default.
445
+ setDefaultPaymentMethod: (id) => call("PATCH", `/me/payment-methods/${seg(id)}`, { isDefault: true }),
446
+ deletePaymentMethod: (id) => call("DELETE", `/me/payment-methods/${seg(id)}`),
447
+ listOrders: (page) => call("GET", `/me/orders${query(page)}`),
448
+ getOrder: (id) => call("GET", `/me/orders/${seg(id)}`),
449
+ listInvoices: (page) => call("GET", `/me/invoices${query(page)}`),
450
+ getInvoice: (id) => call("GET", `/me/invoices/${seg(id)}`),
451
+ getInvoicePdfUrl: (id) => call("GET", `/me/invoices/${seg(id)}/pdf`),
452
+ listSubscriptions: (page) => call("GET", `/me/subscriptions${query(page)}`),
453
+ getSubscription: (id) => call("GET", `/me/subscriptions/${seg(id)}`),
454
+ cancelSubscription: (id, opts) => call("POST", `/me/subscriptions/${seg(id)}/cancel`, opts),
455
+ pauseSubscription: (id) => call("POST", `/me/subscriptions/${seg(id)}/pause`),
456
+ resumeSubscription: (id) => call("POST", `/me/subscriptions/${seg(id)}/resume`),
457
+ claimCart: (cartId) => call("POST", `/me/carts/${seg(cartId)}/claim`)
458
+ };
459
+ }
460
+
461
+ // src/react/context.tsx
462
+ var import_react = require("react");
463
+ var import_jsx_runtime = require("react/jsx-runtime");
464
+ var Ctx = (0, import_react.createContext)(null);
465
+ function ThrottleAuthProvider(props) {
466
+ const { auth: injected, publishableKey, baseUrl, storage, onStepUpRequired, children } = props;
467
+ const value = (0, import_react.useMemo)(() => {
468
+ const auth = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired });
469
+ return { auth, account: createAccount(auth) };
470
+ }, [injected, publishableKey, baseUrl]);
471
+ const restored = (0, import_react.useRef)(null);
472
+ (0, import_react.useEffect)(() => {
473
+ if (restored.current === value.auth) return;
474
+ restored.current = value.auth;
475
+ void value.auth.restore();
476
+ }, [value]);
477
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Ctx.Provider, { value, children });
478
+ }
479
+ function useCtx() {
480
+ const ctx = (0, import_react.useContext)(Ctx);
481
+ if (!ctx) throw new Error("useAuth must be used inside a <ThrottleAuthProvider>");
482
+ return ctx;
483
+ }
484
+ function useAuthContext() {
485
+ return useCtx();
486
+ }
487
+ function useAuth() {
488
+ const { auth, account } = useCtx();
489
+ const snapshot = (0, import_react.useSyncExternalStore)(auth.subscribe, auth.getSnapshot, auth.getSnapshot);
490
+ return {
491
+ ...snapshot,
492
+ isLoading: snapshot.status === "loading",
493
+ isAuthenticated: snapshot.status === "authenticated",
494
+ auth,
495
+ account
496
+ };
497
+ }
498
+
499
+ // src/react/components/appearance.tsx
500
+ var import_react2 = require("react");
501
+
502
+ // src/react/components/contrast.ts
503
+ var DEFAULT_DANGER = { light: "#c2321f", dark: "#ff6b58" };
504
+ var DEFAULT_PRIMARY_FOREGROUND = { light: "#ffffff", dark: "#0d1117" };
505
+ function channel(value) {
506
+ const c = value / 255;
507
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
508
+ }
509
+ function luminance(hex) {
510
+ const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
511
+ if (!m) return null;
512
+ const h = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
513
+ const [r, g, b] = [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));
514
+ return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
515
+ }
516
+ function contrastRatio(a, b) {
517
+ const la = luminance(a);
518
+ const lb = luminance(b);
519
+ if (la === null || lb === null) return null;
520
+ return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
521
+ }
522
+ function primaryContrastWarning(variables, theme) {
523
+ const primary = variables.colorPrimary;
524
+ if (!primary) return null;
525
+ const themes = theme === "auto" ? ["light", "dark"] : [theme];
526
+ const failures = [];
527
+ for (const t of themes) {
528
+ const fg = variables.colorPrimaryForeground ?? DEFAULT_PRIMARY_FOREGROUND[t];
529
+ const ratio = contrastRatio(primary, fg);
530
+ if (ratio === null) return null;
531
+ if (ratio < 4.5) failures.push(`${t} ${ratio.toFixed(2)}:1 against ${fg}`);
532
+ }
533
+ if (failures.length === 0) return null;
534
+ const advice = variables.colorPrimaryForeground ? "Pick a foreground with more contrast against it." : "You set `colorPrimary` without `colorPrimaryForeground`, so the theme default is being used. Set both.";
535
+ return `[@usethrottle/auth] Primary button text may be hard to read on ${primary}: ${failures.join("; ")}. WCAG asks for 4.5:1. ${advice}`;
536
+ }
537
+ function toLab(hex) {
538
+ const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
539
+ if (!m) return null;
540
+ const h = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
541
+ const [r, g, b] = [0, 2, 4].map((i) => {
542
+ const c = parseInt(h.slice(i, i + 2), 16) / 255;
543
+ return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
544
+ });
545
+ const X = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
546
+ const Y = r * 0.2126 + g * 0.7152 + b * 0.0722;
547
+ const Z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
548
+ const f = (t) => t > 8856e-6 ? Math.cbrt(t) : 7.787 * t + 16 / 116;
549
+ const [fx, fy, fz] = [f(X), f(Y), f(Z)];
550
+ return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)];
551
+ }
552
+ function colourDistance(a, b) {
553
+ const la = toLab(a);
554
+ const lb = toLab(b);
555
+ if (!la || !lb) return null;
556
+ return Math.hypot(la[0] - lb[0], la[1] - lb[1], la[2] - lb[2]);
557
+ }
558
+ var MIN_DISTINCT = 25;
559
+ function dangerCollisionWarning(variables, theme) {
560
+ const primary = variables.colorPrimary;
561
+ if (!primary) return null;
562
+ const themes = theme === "auto" ? ["light", "dark"] : [theme];
563
+ const failures = [];
564
+ for (const t of themes) {
565
+ const danger = variables.colorDanger ?? DEFAULT_DANGER[t];
566
+ const d = colourDistance(primary, danger);
567
+ if (d === null) return null;
568
+ if (d < MIN_DISTINCT) failures.push(`${t} \u0394E ${d.toFixed(1)} against ${danger}`);
569
+ }
570
+ if (failures.length === 0) return null;
571
+ return `[@usethrottle/auth] Your brand colour ${primary} is too close to the destructive colour to tell apart: ${failures.join("; ")}. Delete and Edit sit side by side in the account panels and a buyer distinguishes them by colour alone, so pick a more distinct \`colorPrimary\`, or override \`colorDanger\` to something further from it.`;
572
+ }
573
+
574
+ // src/react/components/appearance.tsx
575
+ var import_jsx_runtime2 = require("react/jsx-runtime");
576
+ var warnedPairings = /* @__PURE__ */ new Set();
577
+ function warnOnUnreadablePrimary(variables, theme) {
578
+ if (process.env.NODE_ENV === "production") return;
579
+ const key = `${variables.colorPrimary}|${variables.colorPrimaryForeground}|${variables.colorDanger}|${theme}`;
580
+ if (warnedPairings.has(key)) return;
581
+ const messages = [
582
+ primaryContrastWarning(variables, theme),
583
+ dangerCollisionWarning(variables, theme)
584
+ ].filter(Boolean);
585
+ if (messages.length === 0) return;
586
+ warnedPairings.add(key);
587
+ for (const m of messages) console.warn(m);
588
+ }
589
+ var VARIABLE_PROPERTY = {
590
+ colorPrimary: "--throttle-color-primary",
591
+ colorPrimaryForeground: "--throttle-color-primary-foreground",
592
+ colorBackground: "--throttle-color-background",
593
+ colorSurface: "--throttle-color-surface",
594
+ colorForeground: "--throttle-color-foreground",
595
+ colorMuted: "--throttle-color-muted",
596
+ colorMutedForeground: "--throttle-color-muted-foreground",
597
+ colorBorder: "--throttle-color-border",
598
+ colorDanger: "--throttle-color-danger",
599
+ colorDangerSurface: "--throttle-color-danger-surface",
600
+ colorDangerBorder: "--throttle-color-danger-border",
601
+ colorSuccess: "--throttle-color-success",
602
+ colorSuccessSurface: "--throttle-color-success-surface",
603
+ colorSuccessBorder: "--throttle-color-success-border",
604
+ colorWarning: "--throttle-color-warning",
605
+ colorWarningSurface: "--throttle-color-warning-surface",
606
+ colorWarningBorder: "--throttle-color-warning-border",
607
+ fontFamily: "--throttle-font-family",
608
+ fontSize: "--throttle-font-size",
609
+ borderRadius: "--throttle-radius",
610
+ borderWidth: "--throttle-border-width",
611
+ spacing: "--throttle-spacing",
612
+ shadowCard: "--throttle-shadow-card",
613
+ shadowPopover: "--throttle-shadow-popover",
614
+ transition: "--throttle-transition",
615
+ zModal: "--throttle-z-modal",
616
+ zPopover: "--throttle-z-popover"
617
+ };
618
+ var THROTTLE_ELEMENT_KEYS = [
619
+ // shared primitives
620
+ "card",
621
+ "card.header",
622
+ "card.title",
623
+ "card.description",
624
+ "card.body",
625
+ "card.footer",
626
+ "card.logo",
627
+ "field",
628
+ "field.label",
629
+ "field.input",
630
+ "field.hint",
631
+ "field.error",
632
+ "field.group",
633
+ "button",
634
+ "alert",
635
+ "spinner",
636
+ "skeleton",
637
+ "avatar",
638
+ "badge",
639
+ "emptyState",
640
+ "emptyState.title",
641
+ "emptyState.description",
642
+ "modal.overlay",
643
+ "modal.container",
644
+ "modal.closeButton",
645
+ "dropdown.trigger",
646
+ "dropdown.menu",
647
+ "dropdown.item",
648
+ "dropdown.separator",
649
+ "tabs.list",
650
+ "tabs.tab",
651
+ "table",
652
+ "table.head",
653
+ "table.row",
654
+ "table.cell",
655
+ // auth
656
+ "signIn.root",
657
+ "signIn.title",
658
+ "signIn.logo",
659
+ "signIn.emailField",
660
+ "signIn.passwordField",
661
+ "signIn.submitButton",
662
+ "signIn.forgotLink",
663
+ "signIn.switchLink",
664
+ "signUp.root",
665
+ "signUp.title",
666
+ "signUp.logo",
667
+ "signUp.emailField",
668
+ "signUp.passwordField",
669
+ "signUp.submitButton",
670
+ "signUp.switchLink",
671
+ "forgotPassword.root",
672
+ "forgotPassword.title",
673
+ "forgotPassword.logo",
674
+ "forgotPassword.emailField",
675
+ "forgotPassword.submitButton",
676
+ "forgotPassword.backLink",
677
+ "resetPassword.root",
678
+ "resetPassword.title",
679
+ "resetPassword.logo",
680
+ "resetPassword.passwordField",
681
+ "resetPassword.submitButton",
682
+ "verifyEmail.root",
683
+ "verifyEmail.title",
684
+ "verifyEmail.logo",
685
+ "verifyEmail.resendButton",
686
+ "authFlow.root",
687
+ "authModal.overlay",
688
+ "authModal.container",
689
+ "signInButton.root",
690
+ "signUpButton.root",
691
+ "signOutButton.root",
692
+ "stepUp.root",
693
+ "stepUp.title",
694
+ "stepUp.passwordField",
695
+ "stepUp.submitButton",
696
+ "stepUp.cancelButton",
697
+ "signInPage.root",
698
+ "signInPage.logo",
699
+ "signInPage.footer",
700
+ "signUpPage.root",
701
+ "signUpPage.logo",
702
+ "signUpPage.footer",
703
+ // user button
704
+ "userButton.root",
705
+ "userButton.trigger",
706
+ "userButton.menu",
707
+ "userButton.identity",
708
+ "userButton.email",
709
+ "userButton.badge",
710
+ "userButton.item",
711
+ "userButton.manageButton",
712
+ "userButton.signOutButton",
713
+ "userButton.signOutAllButton",
714
+ // account
715
+ "accountDashboard.root",
716
+ "accountDashboard.tabs",
717
+ "accountDashboard.sidebar",
718
+ "accountDashboard.panel",
719
+ "accountModal.overlay",
720
+ "accountModal.container",
721
+ "accountModal.header",
722
+ "accountModal.title",
723
+ "accountModal.body",
724
+ "verifyBanner.root",
725
+ "verifyBanner.message",
726
+ "verifyBanner.resendButton",
727
+ "verifyBanner.dismissButton",
728
+ "profilePanel.root",
729
+ "profilePanel.form",
730
+ "profilePanel.submitButton",
731
+ "addressesPanel.root",
732
+ "addressesPanel.list",
733
+ "addressesPanel.item",
734
+ "addressesPanel.addButton",
735
+ "addressesPanel.form",
736
+ "addressesPanel.defaultButton",
737
+ "addressesPanel.editButton",
738
+ "addressesPanel.deleteButton",
739
+ "addressesPanel.table",
740
+ "addressesPanel.row",
741
+ "paymentMethodsPanel.root",
742
+ "paymentMethodsPanel.list",
743
+ "paymentMethodsPanel.item",
744
+ "paymentMethodsPanel.defaultButton",
745
+ "paymentMethodsPanel.deleteButton",
746
+ "paymentMethodsPanel.table",
747
+ "paymentMethodsPanel.row",
748
+ "ordersPanel.root",
749
+ "ordersPanel.table",
750
+ "ordersPanel.row",
751
+ "ordersPanel.list",
752
+ "ordersPanel.item",
753
+ "ordersPanel.viewButton",
754
+ "orderDetail.root",
755
+ "orderDetail.backButton",
756
+ "orderDetail.lineItems",
757
+ "orderDetail.lineItem",
758
+ "orderDetail.totals",
759
+ "invoicesPanel.root",
760
+ "invoicesPanel.table",
761
+ "invoicesPanel.row",
762
+ "invoicesPanel.list",
763
+ "invoicesPanel.item",
764
+ "invoicesPanel.viewButton",
765
+ "invoiceDetail.root",
766
+ "invoiceDetail.backButton",
767
+ "invoiceDetail.downloadButton",
768
+ "invoiceDetail.totals",
769
+ "subscriptionsPanel.root",
770
+ "subscriptionsPanel.list",
771
+ "subscriptionsPanel.item",
772
+ "subscriptionsPanel.cancelButton",
773
+ "subscriptionsPanel.cancelNowButton",
774
+ "subscriptionsPanel.pauseButton",
775
+ "subscriptionsPanel.resumeButton",
776
+ "subscriptionsPanel.table",
777
+ "subscriptionsPanel.row",
778
+ "securityPanel.root",
779
+ "securityPanel.passwordForm",
780
+ "securityPanel.passwordButton",
781
+ "securityPanel.sessionList",
782
+ "securityPanel.sessionItem",
783
+ "securityPanel.revokeButton",
784
+ "securityPanel.revokeAllButton",
785
+ "securityPanel.table",
786
+ "securityPanel.row"
787
+ ];
788
+ var Ctx2 = (0, import_react2.createContext)(null);
789
+ function AppearanceProvider(props) {
790
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Ctx2.Provider, { value: props.value, children: props.children });
791
+ }
792
+ function useAppearance(local) {
793
+ const provider = (0, import_react2.useContext)(Ctx2);
794
+ return (0, import_react2.useMemo)(() => {
795
+ const theme = local?.theme ?? provider?.theme ?? "light";
796
+ const variables = { ...provider?.variables, ...local?.variables };
797
+ const elements = { ...provider?.elements, ...local?.elements };
798
+ const slots = { ...provider?.components, ...local?.components };
799
+ const style = {};
800
+ for (const [key, value] of Object.entries(variables)) {
801
+ const property = VARIABLE_PROPERTY[key];
802
+ if (property && value != null) style[property] = value;
803
+ }
804
+ warnOnUnreadablePrimary(variables, theme);
805
+ return {
806
+ theme,
807
+ rootProps: { className: "throttle-root", style, "data-throttle-theme": theme },
808
+ el(key, base) {
809
+ const extra = elements[key];
810
+ return {
811
+ className: [base, extra].filter(Boolean).join(" ") || void 0,
812
+ "data-throttle-element": key
813
+ };
814
+ },
815
+ slots
816
+ };
817
+ }, [provider, local]);
818
+ }
819
+ function useAppearanceProviderValue(local) {
820
+ const provider = (0, import_react2.useContext)(Ctx2);
821
+ return (0, import_react2.useMemo)(
822
+ () => ({
823
+ theme: local?.theme ?? provider?.theme,
824
+ variables: { ...provider?.variables, ...local?.variables },
825
+ elements: { ...provider?.elements, ...local?.elements },
826
+ components: { ...provider?.components, ...local?.components }
827
+ }),
828
+ [provider, local]
829
+ );
830
+ }
831
+
832
+ // src/react/components/branding.tsx
833
+ var import_react3 = require("react");
834
+ var import_jsx_runtime3 = require("react/jsx-runtime");
835
+ var Ctx3 = (0, import_react3.createContext)(null);
836
+ function BrandLogoProvider(props) {
837
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Ctx3.Provider, { value: props.value, children: props.children });
838
+ }
839
+ function useBrandLogo(local) {
840
+ const provider = (0, import_react3.useContext)(Ctx3);
841
+ return local ?? provider ?? null;
842
+ }
843
+
844
+ // src/react/components/localization.tsx
845
+ var import_react4 = require("react");
846
+
847
+ // src/react/forms/messages.ts
848
+ function waitCopy(seconds) {
849
+ if (!seconds || !Number.isFinite(seconds)) return "Please wait a moment and try again.";
850
+ if (seconds < 60) return `Please wait ${Math.ceil(seconds)} seconds and try again.`;
851
+ const minutes = Math.ceil(seconds / 60);
852
+ return `Please wait ${minutes} ${minutes === 1 ? "minute" : "minutes"} and try again.`;
853
+ }
854
+ function messageForError(err) {
855
+ const e = err;
856
+ switch (e?.code) {
857
+ case "invalid_credentials":
858
+ return "That email or password is not correct.";
859
+ case "weak_password":
860
+ return e.message || "Please choose a stronger password.";
861
+ case "too_many_attempts":
862
+ return `Too many attempts. ${waitCopy(e.retryAfter)}`;
863
+ case "verification_required":
864
+ return "Please verify your email address to continue.";
865
+ case "step_up_required":
866
+ return "Please re-enter your password to continue.";
867
+ case "session_revoked":
868
+ return "Your session has ended. Please sign in again.";
869
+ case "token_expired":
870
+ case "token_invalid":
871
+ case "token_already_used":
872
+ return "That link is no longer valid. Please request a new link.";
873
+ case "auth_unavailable":
874
+ return "Accounts are briefly unavailable. Please try again shortly.";
875
+ case "origin_not_allowed":
876
+ case "auth_not_enabled":
877
+ case "invalid_api_key":
878
+ case "environment_mismatch":
879
+ return "Accounts are not available right now. Please contact support.";
880
+ case "cart_already_claimed":
881
+ return "That cart belongs to a different account.";
882
+ case "not_found":
883
+ return "We could not find that.";
884
+ case "payment_method_in_use":
885
+ return e.message || "That payment method is in use and cannot be removed.";
886
+ default:
887
+ return "Something went wrong. Please try again.";
888
+ }
889
+ }
890
+
891
+ // src/react/components/localization.tsx
892
+ var import_jsx_runtime4 = require("react/jsx-runtime");
893
+ var enUS = {
894
+ locale: "en-US",
895
+ common: {
896
+ save: "Save",
897
+ cancel: "Cancel",
898
+ delete: "Delete",
899
+ edit: "Edit",
900
+ back: "Back",
901
+ retry: "Try again",
902
+ loading: "Loading\u2026",
903
+ close: "Close",
904
+ confirm: "Are you sure?",
905
+ somethingWentWrong: "Something went wrong.",
906
+ actions: "Actions",
907
+ view: "View"
908
+ },
909
+ signIn: {
910
+ title: "Sign in",
911
+ description: "Sign in to your account.",
912
+ email: "Email",
913
+ password: "Password",
914
+ submitButton: "Sign in",
915
+ forgotLink: "Forgot your password?",
916
+ switchPrompt: "Don't have an account?",
917
+ switchLink: "Sign up"
918
+ },
919
+ signUp: {
920
+ title: "Create your account",
921
+ description: "",
922
+ email: "Email",
923
+ password: "Password",
924
+ submitButton: "Create account",
925
+ switchPrompt: "Already have an account?",
926
+ switchLink: "Sign in",
927
+ // Deliberately conditional. Registering onto an address that already has
928
+ // an account returns the same `pending` status and sends no email, so
929
+ // asserting one was sent would be false for that buyer.
930
+ pendingNotice: "If that email is new, check your inbox for a link to finish signing up."
931
+ },
932
+ forgotPassword: {
933
+ title: "Reset your password",
934
+ description: "We will email you a link if an account exists.",
935
+ email: "Email",
936
+ submitButton: "Send reset link",
937
+ sentNotice: "If an account exists for that email, a reset link is on its way.",
938
+ backLink: "Back to sign in"
939
+ },
940
+ resetPassword: {
941
+ title: "Choose a new password",
942
+ password: "New password",
943
+ submitButton: "Update password",
944
+ doneNotice: "Your password has been updated.",
945
+ missingToken: "That link is missing its token. Request a new one."
946
+ },
947
+ verifyEmail: {
948
+ title: "Verify your email",
949
+ verifying: "Confirming your email\u2026",
950
+ success: "Your email is verified.",
951
+ resendButton: "Resend the link",
952
+ resentNotice: "We sent a new link.",
953
+ instructions: "Check your inbox for a verification link."
954
+ },
955
+ stepUp: {
956
+ title: "Confirm your password",
957
+ description: "For your security, please re-enter your password to continue.",
958
+ password: "Password",
959
+ submitButton: "Confirm",
960
+ cancelButton: "Cancel"
961
+ },
962
+ userButton: {
963
+ manage: "Manage account",
964
+ signOut: "Sign out",
965
+ signOutAll: "Sign out of all devices",
966
+ verified: "Verified",
967
+ unverified: "Unverified",
968
+ openMenu: "Account menu"
969
+ },
970
+ verifyBanner: {
971
+ message: "Verify your email to see your full account history.",
972
+ resendButton: "Resend link",
973
+ dismissButton: "Dismiss"
974
+ },
975
+ account: {
976
+ title: "Your account",
977
+ tabs: {
978
+ profile: "Profile",
979
+ addresses: "Addresses",
980
+ paymentMethods: "Saved cards",
981
+ orders: "Orders",
982
+ invoices: "Invoices",
983
+ subscriptions: "Subscriptions",
984
+ security: "Security"
985
+ }
986
+ },
987
+ profile: {
988
+ firstName: "First name",
989
+ lastName: "Last name",
990
+ phone: "Phone",
991
+ company: "Company",
992
+ acceptsMarketing: "Send me product news and offers",
993
+ email: "Email",
994
+ emailReadOnly: "Your email address cannot be changed here.",
995
+ saveButton: "Save changes",
996
+ savedNotice: "Profile updated."
997
+ },
998
+ addresses: {
999
+ addButton: "Add address",
1000
+ defaultBadge: "Default",
1001
+ makeDefault: "Make default",
1002
+ empty: "No saved addresses",
1003
+ emptyDescription: "Addresses you use at checkout can be saved here.",
1004
+ label: "Label",
1005
+ firstName: "First name",
1006
+ lastName: "Last name",
1007
+ company: "Company",
1008
+ addressLine1: "Address",
1009
+ addressLine2: "Apartment, suite, etc.",
1010
+ city: "City",
1011
+ stateProvince: "State / Province",
1012
+ postalCode: "Postal code",
1013
+ countryCode: "Country code",
1014
+ phone: "Phone",
1015
+ deleteConfirm: "Delete this address?",
1016
+ columnAddress: "Address"
1017
+ },
1018
+ paymentMethods: {
1019
+ empty: "No saved cards",
1020
+ // True today and still true once a buyer-plane add-card route exists.
1021
+ emptyDescription: "Cards you use at checkout will be saved here.",
1022
+ // Shown whenever the panel is empty with no error and the buyer is not
1023
+ // verified — including simply being signed out — matching the copy
1024
+ // orders/invoices/subscriptions already show in that state.
1025
+ gatedDescription: "Verify your email to see your saved cards.",
1026
+ defaultBadge: "Default",
1027
+ makeDefault: "Make default",
1028
+ expires: "Expires",
1029
+ deleteConfirm: "Remove this card?",
1030
+ columnCard: "Card"
1031
+ },
1032
+ orders: {
1033
+ empty: "No orders yet",
1034
+ emptyDescription: "Orders you place will appear here.",
1035
+ gatedDescription: "Verify your email to see your order history.",
1036
+ number: "Order",
1037
+ date: "Date",
1038
+ status: "Status",
1039
+ total: "Total",
1040
+ /**
1041
+ * Display copy for `order.status`. Mirrors ORDER_STATUS_META in
1042
+ * `@commerce/orders`, which is the server's single source of status copy —
1043
+ * copied rather than imported because this package ships with no runtime
1044
+ * dependencies and is a client for the wire API, not for server packages.
1045
+ * If a status is added there, add it here.
1046
+ */
1047
+ statusLabels: {
1048
+ draft: "Draft",
1049
+ pending: "Pending",
1050
+ processing: "Processing",
1051
+ partiallyFulfilled: "Partially fulfilled",
1052
+ fulfilled: "Fulfilled",
1053
+ cancelled: "Cancelled",
1054
+ closed: "Closed"
1055
+ },
1056
+ /**
1057
+ * The money half of the label, shown as a second badge beside the
1058
+ * lifecycle one. `captured` is deliberately empty: paid in full is the
1059
+ * assumed happy path, and announcing it on every settled order is noise.
1060
+ */
1061
+ paymentLabels: {
1062
+ pending: "Awaiting payment",
1063
+ authorized: "Authorized",
1064
+ captured: "",
1065
+ partiallyPaid: "Partially paid",
1066
+ partiallyRefunded: "Partially refunded",
1067
+ refunded: "Refunded",
1068
+ failed: "Payment failed",
1069
+ voided: "Voided",
1070
+ processing: "Payment processing",
1071
+ disputed: "Disputed",
1072
+ expired: "Authorization expired"
1073
+ },
1074
+ items: "Items",
1075
+ subtotal: "Subtotal",
1076
+ discount: "Discount",
1077
+ shipping: "Shipping",
1078
+ tax: "Tax",
1079
+ quantity: "Qty",
1080
+ shippingAddress: "Shipping address",
1081
+ billingAddress: "Billing address",
1082
+ viewSubscription: "View subscription"
1083
+ },
1084
+ invoices: {
1085
+ empty: "No invoices yet",
1086
+ emptyDescription: "Invoices will appear here.",
1087
+ gatedDescription: "Verify your email to see your invoices.",
1088
+ number: "Invoice",
1089
+ issued: "Issued",
1090
+ due: "Due",
1091
+ status: "Status",
1092
+ total: "Total",
1093
+ refunded: "Refunded",
1094
+ period: "Period",
1095
+ downloadButton: "Download PDF",
1096
+ /** Display copy for `invoice.status` — the wire values are paid|open|void. */
1097
+ statusLabels: { paid: "Paid", open: "Open", void: "Void" },
1098
+ downloadUnavailable: "This invoice is not available to download right now."
1099
+ },
1100
+ subscriptions: {
1101
+ empty: "No subscriptions",
1102
+ emptyDescription: "Subscriptions will appear here.",
1103
+ gatedDescription: "Verify your email to see your subscriptions.",
1104
+ renewsOn: "Renews on",
1105
+ endsOn: "Ends on",
1106
+ cancelledOn: "Cancelled on",
1107
+ trialEnds: "Trial ends",
1108
+ cancelButton: "Cancel",
1109
+ cancelAtPeriodEnd: "Cancel at period end",
1110
+ cancelNow: "Cancel immediately",
1111
+ pauseButton: "Pause",
1112
+ resumeButton: "Resume",
1113
+ cancelConfirm: "Cancel this subscription?",
1114
+ cancelScheduled: "Cancels at the end of the period",
1115
+ columnPlan: "Plan",
1116
+ columnPrice: "Price",
1117
+ columnRenews: "Renews",
1118
+ columnStatus: "Status",
1119
+ /**
1120
+ * Display copy for `subscription.status`. Mirrors SubscriptionStatus in
1121
+ * `@commerce/subscriptions`; copied rather than imported because this
1122
+ * package has no runtime dependencies. Without it a buyer in dunning saw
1123
+ * the literal pill `past_due`.
1124
+ */
1125
+ statusLabels: {
1126
+ active: "Active",
1127
+ paused: "Paused",
1128
+ cancelled: "Cancelled",
1129
+ pastDue: "Payment overdue",
1130
+ trialing: "Trial",
1131
+ unpaid: "Unpaid"
1132
+ }
1133
+ },
1134
+ security: {
1135
+ passwordTitle: "Change password",
1136
+ newPassword: "New password",
1137
+ changeButton: "Update password",
1138
+ changedNotice: "Password updated.",
1139
+ sessionsTitle: "Devices",
1140
+ thisDevice: "This device",
1141
+ lastActive: "Last active",
1142
+ revokeButton: "Sign out",
1143
+ revokeAllButton: "Sign out of all other devices",
1144
+ sessionsEmpty: "No other devices",
1145
+ columnDevice: "Device"
1146
+ },
1147
+ errors: {}
1148
+ };
1149
+ function isPlainObject(value) {
1150
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1151
+ }
1152
+ function deepMerge(base, over, path = "") {
1153
+ if (!over) return base;
1154
+ const out = Array.isArray(base) ? [...base] : { ...base };
1155
+ for (const [key, value] of Object.entries(over)) {
1156
+ if (value == null) continue;
1157
+ const current = base[key];
1158
+ const keyPath = path ? `${path}.${key}` : key;
1159
+ const currentIsObject = isPlainObject(current);
1160
+ const valueIsObject = isPlainObject(value);
1161
+ if (current !== void 0 && currentIsObject !== valueIsObject) {
1162
+ if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
1163
+ console.warn(
1164
+ `[useCopy] ignoring localization override at "${keyPath}": expected ${currentIsObject ? "an object" : "a string"}, got ${valueIsObject ? "an object" : typeof value}`
1165
+ );
1166
+ }
1167
+ continue;
1168
+ }
1169
+ out[key] = currentIsObject && valueIsObject ? deepMerge(current, value, keyPath) : value;
1170
+ }
1171
+ return out;
1172
+ }
1173
+ var Ctx4 = (0, import_react4.createContext)(null);
1174
+ function LocalizationProvider(props) {
1175
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Ctx4.Provider, { value: props.value, children: props.children });
1176
+ }
1177
+ function useCopy(local) {
1178
+ const provider = (0, import_react4.useContext)(Ctx4);
1179
+ return (0, import_react4.useMemo)(() => {
1180
+ const t = deepMerge(deepMerge(enUS, provider), local);
1181
+ return {
1182
+ t,
1183
+ locale: t.locale,
1184
+ /**
1185
+ * A merchant's `errors` map wins; otherwise the shipped mapper decides.
1186
+ * `messageForError` stays the floor because it encodes two deliberate
1187
+ * non-passthroughs — `invalid_credentials` stays vague to avoid account
1188
+ * enumeration, and unknown codes stay generic.
1189
+ */
1190
+ errorMessage(err) {
1191
+ const code = err?.code;
1192
+ const override = code ? t.errors[code] : void 0;
1193
+ return override ?? messageForError(err);
1194
+ }
1195
+ };
1196
+ }, [provider, local]);
1197
+ }
1198
+
1199
+ // src/react/components/styles.ts
1200
+ var import_react5 = require("react");
1201
+ var THROTTLE_STYLE_ID = "throttle-auth-styles";
1202
+ var THROTTLE_STYLES = `
1203
+ .throttle-root {
1204
+ --throttle-color-primary: #18181b;
1205
+ --throttle-color-primary-foreground: #ffffff;
1206
+ --throttle-color-background: #ffffff;
1207
+ --throttle-color-surface: #f8fafc;
1208
+ --throttle-color-foreground: #0e1116;
1209
+ --throttle-color-muted: #eceef2;
1210
+ --throttle-color-muted-foreground: #5b6472;
1211
+ --throttle-color-border: #e2e8f0;
1212
+ --throttle-color-danger: #c2321f;
1213
+ --throttle-color-danger-surface: #fef2f2;
1214
+ --throttle-color-danger-border: #fecaca;
1215
+ --throttle-color-success: #127a4a;
1216
+ --throttle-color-success-surface: #ecfdf5;
1217
+ --throttle-color-success-border: #a7f3d0;
1218
+ --throttle-color-warning: #92400e;
1219
+ --throttle-color-warning-surface: #fffbeb;
1220
+ --throttle-color-warning-border: #fde68a;
1221
+ --throttle-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
1222
+ --throttle-font-size: 16px;
1223
+ --throttle-font-size-xs: 12px;
1224
+ --throttle-font-size-sm: 14px;
1225
+ --throttle-font-size-lg: 20px;
1226
+ --throttle-font-size-xl: 24px;
1227
+ --throttle-line-height: 1.5;
1228
+ --throttle-line-height-tight: 1.25;
1229
+ --throttle-radius: 12px;
1230
+ --throttle-radius-sm: 8px;
1231
+ --throttle-radius-full: 999px;
1232
+ --throttle-border-width: 1px;
1233
+ --throttle-spacing: 8px;
1234
+ --throttle-space-1: 4px;
1235
+ --throttle-space-2: var(--throttle-spacing);
1236
+ --throttle-space-3: 12px;
1237
+ --throttle-space-4: 16px;
1238
+ --throttle-space-5: 24px;
1239
+ --throttle-space-6: 32px;
1240
+ --throttle-control-height: 48px;
1241
+ --throttle-control-padding-x: 16px;
1242
+ --throttle-shadow-card: 0 1px 3px rgba(15,23,42,.08), 0 1px 2px rgba(15,23,42,.04);
1243
+ --throttle-shadow-popover: 0 10px 24px rgba(15,23,42,.12), 0 2px 6px rgba(15,23,42,.06);
1244
+ --throttle-transition: 150ms cubic-bezier(.2,0,.2,1);
1245
+ --throttle-z-modal: 2147483000;
1246
+ --throttle-z-popover: 2147482000;
1247
+ font-family: var(--throttle-font-family);
1248
+ font-size: var(--throttle-font-size);
1249
+ line-height: var(--throttle-line-height);
1250
+ color: var(--throttle-color-foreground);
1251
+ box-sizing: border-box;
1252
+ }
1253
+ .throttle-root *, .throttle-root *::before, .throttle-root *::after { box-sizing: inherit; }
1254
+
1255
+ .throttle-root[data-throttle-theme="dark"] {
1256
+ --throttle-color-primary: #e6edf3;
1257
+ --throttle-color-primary-foreground: #0d1117;
1258
+ --throttle-color-background: #0d1117;
1259
+ --throttle-color-surface: #1c2129;
1260
+ --throttle-color-foreground: #e6edf3;
1261
+ --throttle-color-muted: #21262d;
1262
+ --throttle-color-muted-foreground: #9aa4b2;
1263
+ --throttle-color-border: #30363d;
1264
+ --throttle-color-danger: #ff6b58;
1265
+ --throttle-color-danger-surface: #2a1513;
1266
+ --throttle-color-danger-border: #5c2b25;
1267
+ --throttle-color-success: #45c08a;
1268
+ --throttle-color-success-surface: #0f2419;
1269
+ --throttle-color-success-border: #1f4d36;
1270
+ --throttle-color-warning: #e0a53a;
1271
+ --throttle-color-warning-surface: #2a2113;
1272
+ --throttle-color-warning-border: #5c4a25;
1273
+ --throttle-shadow-card: 0 1px 3px rgba(0,0,0,.6), 0 1px 2px rgba(0,0,0,.4);
1274
+ --throttle-shadow-popover: 0 10px 24px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.35);
1275
+ }
1276
+ @media (prefers-color-scheme: dark) {
1277
+ .throttle-root[data-throttle-theme="auto"] {
1278
+ --throttle-color-primary: #e6edf3;
1279
+ --throttle-color-primary-foreground: #0d1117;
1280
+ --throttle-color-background: #0d1117;
1281
+ --throttle-color-surface: #1c2129;
1282
+ --throttle-color-foreground: #e6edf3;
1283
+ --throttle-color-muted: #21262d;
1284
+ --throttle-color-muted-foreground: #9aa4b2;
1285
+ --throttle-color-border: #30363d;
1286
+ --throttle-color-danger: #ff6b58;
1287
+ --throttle-color-danger-surface: #2a1513;
1288
+ --throttle-color-danger-border: #5c2b25;
1289
+ --throttle-color-success: #45c08a;
1290
+ --throttle-color-success-surface: #0f2419;
1291
+ --throttle-color-success-border: #1f4d36;
1292
+ --throttle-color-warning: #e0a53a;
1293
+ --throttle-color-warning-surface: #2a2113;
1294
+ --throttle-color-warning-border: #5c4a25;
1295
+ --throttle-shadow-card: 0 1px 3px rgba(0,0,0,.6), 0 1px 2px rgba(0,0,0,.4);
1296
+ --throttle-shadow-popover: 0 10px 24px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.35);
1297
+ }
1298
+ }
1299
+
1300
+ .throttle-card {
1301
+ background: var(--throttle-color-background);
1302
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
1303
+ border-radius: var(--throttle-radius);
1304
+ box-shadow: var(--throttle-shadow-card);
1305
+ padding: var(--throttle-space-5);
1306
+ width: 100%;
1307
+ max-width: 400px;
1308
+ margin-inline: auto;
1309
+ }
1310
+ /* Inside a Modal the dialog already provides the surface \u2014 background, radius
1311
+ and elevation. A card that draws its own on top is the "box inside a box":
1312
+ two shadowed rounded rectangles nested, with the card's max-width stranding
1313
+ it in the middle of a much wider dialog. */
1314
+ .throttle-card--bare { border: none; box-shadow: none; background: none; max-width: none; }
1315
+ .throttle-card__header { margin-bottom: var(--throttle-space-4); }
1316
+ .throttle-card__header--branded { text-align: center; }
1317
+ .throttle-card__logo { display: flex; justify-content: center; margin-bottom: var(--throttle-space-3); }
1318
+ .throttle-card__title { font-size: var(--throttle-font-size-xl); line-height: var(--throttle-line-height-tight); font-weight: 600; letter-spacing: -0.02em; margin: 0; }
1319
+ .throttle-card__title--branded { font-size: var(--throttle-font-size-xl); }
1320
+ .throttle-card__description { color: var(--throttle-color-muted-foreground); margin: var(--throttle-space-1) 0 0; }
1321
+ .throttle-card__body { display: flex; flex-direction: column; gap: var(--throttle-space-3); }
1322
+ .throttle-card__footer { margin-top: var(--throttle-space-4); text-align: center; }
1323
+ .throttle-card__footer--ruled { border-top: var(--throttle-border-width) solid var(--throttle-color-border); padding-top: var(--throttle-space-4); }
1324
+
1325
+ .throttle-field { display: flex; flex-direction: column; gap: var(--throttle-space-1); }
1326
+ .throttle-field__labelrow { display: flex; align-items: baseline; justify-content: space-between; gap: var(--throttle-space-2); }
1327
+ .throttle-field__label { font-weight: 500; }
1328
+ .throttle-field__input {
1329
+ font: inherit;
1330
+ font-size: var(--throttle-font-size);
1331
+ color: var(--throttle-color-foreground);
1332
+ background: var(--throttle-color-background);
1333
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
1334
+ border-radius: var(--throttle-radius-sm);
1335
+ /* padding-block stays 0 so min-height alone owns the height, exactly as
1336
+ .throttle-button does. With 12px vertical padding the box computed to
1337
+ 50px against the button's 48px \u2014 a 2px mismatch, measured in a browser
1338
+ across 19 controls. An input element centres its own text vertically. */
1339
+ padding-block: 0;
1340
+ padding-inline: var(--throttle-control-padding-x);
1341
+ min-height: var(--throttle-control-height);
1342
+ width: 100%;
1343
+ transition: border-color var(--throttle-transition), box-shadow var(--throttle-transition);
1344
+ }
1345
+ .throttle-field__input:focus-visible {
1346
+ outline: none;
1347
+ border-color: var(--throttle-color-primary);
1348
+ box-shadow: 0 0 0 2px var(--throttle-color-background), 0 0 0 4px var(--throttle-color-primary);
1349
+ }
1350
+ .throttle-field__input:disabled { background: var(--throttle-color-muted); color: var(--throttle-color-muted-foreground); cursor: not-allowed; }
1351
+ .throttle-field__input::placeholder { color: var(--throttle-color-muted-foreground); opacity: 1; }
1352
+ .throttle-field__input--grouped { border: none; border-radius: 0; padding-inline: 0; }
1353
+ .throttle-field__hint { color: var(--throttle-color-muted-foreground); font-size: var(--throttle-font-size-xs); }
1354
+ /* Always mounted, so it reserves its line whether or not there is an error:
1355
+ an error appearing on submit must not push the submit button out from
1356
+ under the buyer's pointer. */
1357
+ .throttle-field__error { color: var(--throttle-color-danger); font-size: var(--throttle-font-size-xs); min-height: 1rem; line-height: 1rem; }
1358
+
1359
+ .throttle-field-group {
1360
+ display: flex;
1361
+ flex-direction: column;
1362
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
1363
+ border-radius: var(--throttle-radius-sm);
1364
+ overflow: hidden;
1365
+ }
1366
+ .throttle-field-group__item { padding: var(--throttle-space-3) var(--throttle-control-padding-x); }
1367
+ .throttle-field-group__divider { height: var(--throttle-border-width); background: var(--throttle-color-border); }
1368
+ .throttle-field-group__forgot { margin-left: auto; }
1369
+
1370
+ .throttle-button {
1371
+ font: inherit;
1372
+ font-weight: 500;
1373
+ border-radius: var(--throttle-radius-sm);
1374
+ border: var(--throttle-border-width) solid transparent;
1375
+ padding-inline: var(--throttle-control-padding-x);
1376
+ padding-block: 0;
1377
+ min-height: var(--throttle-control-height);
1378
+ display: inline-flex;
1379
+ align-items: center;
1380
+ justify-content: center;
1381
+ cursor: pointer;
1382
+ transition: opacity var(--throttle-transition), background-color var(--throttle-transition), border-color var(--throttle-transition), filter var(--throttle-transition), box-shadow var(--throttle-transition);
1383
+ }
1384
+ .throttle-button:disabled { opacity: .55; cursor: not-allowed; }
1385
+ .throttle-button:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--throttle-color-background), 0 0 0 4px var(--throttle-color-primary); }
1386
+ .throttle-button--primary { background: var(--throttle-color-primary); color: var(--throttle-color-primary-foreground); width: 100%; }
1387
+ .throttle-button--primary:hover:not(:disabled) { filter: brightness(0.92); }
1388
+ .throttle-button--secondary { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); border-color: var(--throttle-color-border); }
1389
+ .throttle-button--secondary:hover:not(:disabled) { background: var(--throttle-color-muted); }
1390
+ .throttle-button--danger { background: transparent; color: var(--throttle-color-danger); border-color: var(--throttle-color-border); }
1391
+ .throttle-button--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--throttle-color-danger) 10%, transparent); }
1392
+ /* Link-style actions are the rows panel default, and they carry Delete,
1393
+ Remove, Cancel and Sign out. Zero padding with min-height 0 made those
1394
+ roughly 20px tall and ~8px apart \u2014 a mis-tap on a phone lands on something
1395
+ irreversible. The target is expanded with vertical padding and pulled back
1396
+ with an equal negative margin, so the hit box clears 44px while the text
1397
+ sits exactly where it did. Collapsing this back to zero padding shrinks the
1398
+ target again. */
1399
+ .throttle-button--link { background: none; border: none; color: var(--throttle-color-primary); padding-inline: 0; padding-block: var(--throttle-space-3); margin-block: calc(var(--throttle-space-3) * -1); min-height: 0; text-decoration: underline; width: auto; }
1400
+ .throttle-button--link:hover:not(:disabled) { opacity: .8; }
1401
+ .throttle-button--linkDanger { background: none; border: none; color: var(--throttle-color-danger); padding-inline: 0; padding-block: var(--throttle-space-3); margin-block: calc(var(--throttle-space-3) * -1); min-height: 0; text-decoration: underline; width: auto; }
1402
+ .throttle-button--linkDanger:hover:not(:disabled) { opacity: .8; }
1403
+ .throttle-button--xs { font-size: var(--throttle-font-size-xs); }
1404
+
1405
+ .throttle-alert { border-radius: var(--throttle-radius-sm); padding: var(--throttle-space-3); font-size: var(--throttle-font-size-sm); }
1406
+ .throttle-alert--error { background: var(--throttle-color-danger-surface); border: var(--throttle-border-width) solid var(--throttle-color-danger-border); color: var(--throttle-color-danger); }
1407
+ .throttle-alert--success { background: var(--throttle-color-success-surface); border: var(--throttle-border-width) solid var(--throttle-color-success-border); color: var(--throttle-color-success); }
1408
+ .throttle-alert--info { background: var(--throttle-color-surface); color: var(--throttle-color-muted-foreground); }
1409
+
1410
+ .throttle-spinner {
1411
+ display: inline-block; width: 1em; height: 1em;
1412
+ border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
1413
+ animation: throttle-spin 700ms linear infinite;
1414
+ }
1415
+ @keyframes throttle-spin { to { transform: rotate(360deg); } }
1416
+ @media (prefers-reduced-motion: reduce) { .throttle-spinner { animation-duration: 2.4s; } }
1417
+
1418
+ .throttle-skeleton {
1419
+ background: var(--throttle-color-muted);
1420
+ border-radius: var(--throttle-radius-sm);
1421
+ animation: throttle-pulse 1.4s ease-in-out infinite; height: 2.5em; }
1422
+ @keyframes throttle-pulse { 50% { opacity: .5; } }
1423
+ @media (prefers-reduced-motion: reduce) { .throttle-skeleton { animation: none; } }
1424
+
1425
+ .throttle-modal__overlay {
1426
+ position: fixed; inset: 0; z-index: var(--throttle-z-modal);
1427
+ background: rgba(14,17,22,.45);
1428
+ display: flex; align-items: center; justify-content: center;
1429
+ padding: var(--throttle-space-4);
1430
+ overflow-y: auto;
1431
+ }
1432
+ .throttle-modal__container {
1433
+ background: var(--throttle-color-background);
1434
+ border-radius: var(--throttle-radius);
1435
+ box-shadow: var(--throttle-shadow-popover);
1436
+ max-width: 720px; width: 100%; max-height: 90vh; overflow-y: auto;
1437
+ position: relative;
1438
+ }
1439
+ /* After the base rule on purpose: both are single-class, so equal specificity
1440
+ means source order decides. Declared before it, this silently lost. */
1441
+ .throttle-modal__container--compact { max-width: 440px; }
1442
+ .throttle-modal__close {
1443
+ position: absolute; top: var(--throttle-space-3); right: var(--throttle-space-3);
1444
+ background: none; border: none; cursor: pointer; font-size: var(--throttle-font-size-lg);
1445
+ color: var(--throttle-color-muted-foreground);
1446
+ transition: color var(--throttle-transition);
1447
+ }
1448
+ .throttle-modal__close:hover { color: var(--throttle-color-foreground); }
1449
+ .throttle-modal__header { padding: var(--throttle-space-4) var(--throttle-space-4) 0; }
1450
+ .throttle-modal__title { font-size: var(--throttle-font-size-lg); line-height: var(--throttle-line-height-tight); font-weight: 600; margin: 0; }
1451
+ .throttle-modal__body { padding: var(--throttle-space-4); }
1452
+
1453
+ .throttle-dropdown__trigger { background: none; border: none; cursor: pointer; padding: 0; border-radius: var(--throttle-radius-full); transition: background-color var(--throttle-transition); }
1454
+ .throttle-dropdown__trigger:hover { background: var(--throttle-color-surface); }
1455
+ .throttle-dropdown__trigger:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: 2px; }
1456
+ .throttle-dropdown__menu {
1457
+ position: absolute; right: 0; top: calc(100% + var(--throttle-space-2));
1458
+ z-index: var(--throttle-z-popover);
1459
+ min-width: 260px;
1460
+ background: var(--throttle-color-background);
1461
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
1462
+ border-radius: var(--throttle-radius);
1463
+ box-shadow: var(--throttle-shadow-popover);
1464
+ padding: var(--throttle-space-1);
1465
+ }
1466
+ .throttle-dropdown__item {
1467
+ display: flex; align-items: center; gap: var(--throttle-space-2);
1468
+ width: 100%; text-align: left; font: inherit; color: inherit;
1469
+ background: none; border: none; cursor: pointer;
1470
+ padding: var(--throttle-space-3);
1471
+ border-radius: var(--throttle-radius-sm);
1472
+ text-decoration: none;
1473
+ transition: background-color var(--throttle-transition);
1474
+ }
1475
+ .throttle-dropdown__item:hover { background: var(--throttle-color-surface); }
1476
+ .throttle-dropdown__item:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
1477
+ .throttle-dropdown__separator { height: 1px; background: var(--throttle-color-border); margin: var(--throttle-space-1) 0; border: 0; }
1478
+
1479
+ .throttle-tabs__list {
1480
+ display: flex; gap: var(--throttle-space-1);
1481
+ border-bottom: var(--throttle-border-width) solid var(--throttle-color-border);
1482
+ overflow-x: auto; scrollbar-width: thin;
1483
+ }
1484
+ .throttle-tabs__tab {
1485
+ font: inherit; background: none; border: none; cursor: pointer; white-space: nowrap;
1486
+ padding: var(--throttle-space-3);
1487
+ color: var(--throttle-color-muted-foreground);
1488
+ border-bottom: 2px solid transparent;
1489
+ transition: color var(--throttle-transition), border-color var(--throttle-transition);
1490
+ }
1491
+ .throttle-tabs__tab:hover { color: var(--throttle-color-foreground); }
1492
+ .throttle-tabs__tab--active { color: var(--throttle-color-foreground); border-bottom-color: var(--throttle-color-primary); }
1493
+ .throttle-tabs__panel { padding-top: var(--throttle-space-4); }
1494
+
1495
+ /* Paints its own ground. A UserButton in a navbar should stay transparent, but
1496
+ the dashboard is a large surface of themed text: in dark theme its foreground
1497
+ is near-white, so on a light host page it was rendering white-on-white. */
1498
+ .throttle-account-root { container-type: inline-size; background: var(--throttle-color-background); color: var(--throttle-color-foreground); }
1499
+ .throttle-account { display: flex; gap: var(--throttle-space-5); align-items: flex-start; }
1500
+ .throttle-account__content { flex: 1; min-width: 0; }
1501
+ .throttle-tabs__rail { flex: none; width: 220px; display: flex; flex-direction: column; gap: 2px; }
1502
+ .throttle-tabs__railitem {
1503
+ font: inherit;
1504
+ font-size: var(--throttle-font-size-sm);
1505
+ display: flex;
1506
+ align-items: center;
1507
+ gap: var(--throttle-space-3);
1508
+ width: 100%;
1509
+ text-align: left;
1510
+ background: none;
1511
+ border: none;
1512
+ border-radius: var(--throttle-radius-sm);
1513
+ padding: 0 var(--throttle-space-3);
1514
+ min-height: var(--throttle-control-height);
1515
+ color: var(--throttle-color-muted-foreground);
1516
+ cursor: pointer;
1517
+ transition: background var(--throttle-transition), color var(--throttle-transition);
1518
+ }
1519
+ .throttle-tabs__railitem:hover { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); }
1520
+ .throttle-tabs__railitem:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
1521
+ .throttle-tabs__tab:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
1522
+ .throttle-tabs__railitem--active { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); font-weight: 500; }
1523
+ .throttle-tabs__railicon { flex: none; opacity: .75; }
1524
+
1525
+ /* A CONTAINER query, not a viewport one: these components render inside a
1526
+ merchant's page, so the viewport says nothing about how much room the
1527
+ dashboard actually has \u2014 it could sit in a 400px column on a 1920px screen.
1528
+ Below ~560px of container the rail becomes a full-width list above the
1529
+ panel: all seven still visible, each still a 44px target. */
1530
+ @container (max-width: 560px) {
1531
+ /* align-items: flex-start is right in row direction and wrong in column \u2014
1532
+ it shrinks the rail to its content width, so width:100% alone cannot make
1533
+ it span. Stretch it explicitly when stacked. */
1534
+ .throttle-account { flex-direction: column; align-items: stretch; gap: var(--throttle-space-4); }
1535
+ .throttle-tabs__rail { width: 100%; }
1536
+ }
1537
+
1538
+ .throttle-table { width: 100%; border-collapse: collapse; text-align: left; }
1539
+ .throttle-table__head { color: var(--throttle-color-muted-foreground); font-weight: 500; font-size: var(--throttle-font-size-sm); }
1540
+ .throttle-table__row { border-top: var(--throttle-border-width) solid var(--throttle-color-border); cursor: pointer; transition: background-color var(--throttle-transition); }
1541
+ .throttle-table__row:hover { background: var(--throttle-color-surface); }
1542
+ .throttle-table__row:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
1543
+ .throttle-table__cell { padding: var(--throttle-space-3) var(--throttle-space-2); height: 56px; box-sizing: border-box; }
1544
+ .throttle-table__scroll { overflow-x: auto; }
1545
+
1546
+ .throttle-empty { text-align: center; padding: var(--throttle-space-6) var(--throttle-space-4); }
1547
+ .throttle-empty__title { font-weight: 600; margin: 0 0 var(--throttle-space-1); }
1548
+ .throttle-empty__description { color: var(--throttle-color-muted-foreground); margin: 0; }
1549
+
1550
+ .throttle-avatar {
1551
+ display: inline-flex; align-items: center; justify-content: center;
1552
+ width: 32px; height: 32px; border-radius: var(--throttle-radius-full);
1553
+ background: var(--throttle-color-primary); color: var(--throttle-color-primary-foreground);
1554
+ font-size: var(--throttle-font-size-xs); font-weight: 600; text-transform: uppercase;
1555
+ }
1556
+
1557
+ .throttle-badge {
1558
+ display: inline-block; font-size: var(--throttle-font-size-xs); font-weight: 500;
1559
+ padding: 2px var(--throttle-space-2); border-radius: var(--throttle-radius-full);
1560
+ background: var(--throttle-color-muted); color: var(--throttle-color-muted-foreground);
1561
+ }
1562
+ .throttle-badge--muted { background: none; border: var(--throttle-border-width) solid var(--throttle-color-border); color: var(--throttle-color-muted-foreground); font-weight: 400; padding: 1px calc(var(--throttle-space-2) - 1px); }
1563
+ .throttle-badge--success { background: color-mix(in srgb, var(--throttle-color-success) 15%, transparent); color: var(--throttle-color-success); }
1564
+ .throttle-badge--warning { background: color-mix(in srgb, var(--throttle-color-warning) 15%, transparent); color: var(--throttle-color-warning); }
1565
+ .throttle-badge--danger { background: color-mix(in srgb, var(--throttle-color-danger) 15%, transparent); color: var(--throttle-color-danger); }
1566
+
1567
+ .throttle-stack { display: flex; flex-direction: column; gap: var(--throttle-space-3); }
1568
+ .throttle-row { display: flex; align-items: center; justify-content: space-between; gap: var(--throttle-space-2); flex-wrap: wrap; }
1569
+ .throttle-muted { color: var(--throttle-color-muted-foreground); }
1570
+ .throttle-banner {
1571
+ display: flex; align-items: center; justify-content: space-between; gap: var(--throttle-space-2);
1572
+ flex-wrap: wrap;
1573
+ background: color-mix(in srgb, var(--throttle-color-warning) 12%, transparent);
1574
+ color: var(--throttle-color-warning);
1575
+ border-radius: var(--throttle-radius-sm);
1576
+ padding: var(--throttle-space-3);
1577
+ margin-bottom: var(--throttle-space-4);
1578
+ }
1579
+ .throttle-panel { display: flex; flex-direction: column; gap: var(--throttle-space-4); }
1580
+ /* 100dvh, not 100vh: on mobile browsers 100vh includes the space behind the
1581
+ dynamic address/toolbar, so the page would be taller than the visible
1582
+ viewport and the card would sit off-centre until the toolbar collapsed. */
1583
+ .throttle-page {
1584
+ min-height: 100dvh;
1585
+ width: 100%;
1586
+ background: var(--throttle-color-surface);
1587
+ display: flex;
1588
+ flex-direction: column;
1589
+ align-items: center;
1590
+ justify-content: center;
1591
+ gap: var(--throttle-space-5);
1592
+ padding: var(--throttle-space-5);
1593
+ }
1594
+ .throttle-page__logo { display: flex; justify-content: center; }
1595
+ .throttle-page__footer { color: var(--throttle-color-muted-foreground); text-align: center; font-size: var(--throttle-font-size-sm); }
1596
+ .throttle-listitem {
1597
+ display: flex; align-items: flex-start; justify-content: space-between; gap: var(--throttle-space-2);
1598
+ flex-wrap: wrap;
1599
+ border: var(--throttle-border-width) solid var(--throttle-color-border);
1600
+ border-radius: var(--throttle-radius-sm);
1601
+ padding: var(--throttle-space-3);
1602
+ }
1603
+ /* 'rows' variant: hairline rules between items instead of each item carrying
1604
+ its own border+radius, so a panel does not read as a card inside a card. */
1605
+ .throttle-rows { display: flex; flex-direction: column; }
1606
+ .throttle-rowitem {
1607
+ display: flex; align-items: flex-start; justify-content: space-between; gap: var(--throttle-space-2);
1608
+ flex-wrap: wrap;
1609
+ border-top: var(--throttle-border-width) solid var(--throttle-color-border);
1610
+ padding: var(--throttle-space-3) 0;
1611
+ }
1612
+ .throttle-rowitem:first-child { border-top: none; padding-top: 0; }
1613
+ .throttle-actions { display: flex; gap: var(--throttle-space-2); flex-wrap: wrap; }
1614
+ .throttle-table__money { display: block; text-align: right; font-variant-numeric: tabular-nums; }
1615
+ .throttle-lineitem { display: flex; align-items: center; gap: var(--throttle-space-3); padding: var(--throttle-space-2) 0; }
1616
+ .throttle-lineitem__image { width: 48px; height: 48px; object-fit: cover; border-radius: var(--throttle-radius-sm); flex: none; }
1617
+ .throttle-totals { display: flex; flex-direction: column; gap: var(--throttle-space-1); }
1618
+
1619
+ @media (prefers-reduced-motion: reduce) {
1620
+ .throttle-button, .throttle-field__input, .throttle-dropdown__item, .throttle-dropdown__trigger, .throttle-tabs__tab, .throttle-table__row, .throttle-modal__close {
1621
+ transition: none;
1622
+ }
1623
+ }
1624
+ `;
1625
+ function useInjectStyles(enabled) {
1626
+ (0, import_react5.useInsertionEffect)(() => {
1627
+ if (!enabled) return;
1628
+ if (typeof document === "undefined") return;
1629
+ if (document.getElementById(THROTTLE_STYLE_ID)) return;
1630
+ const tag = document.createElement("style");
1631
+ tag.id = THROTTLE_STYLE_ID;
1632
+ tag.textContent = THROTTLE_STYLES;
1633
+ document.head.appendChild(tag);
1634
+ }, [enabled]);
1635
+ }
1636
+
1637
+ // src/react/components/auth/StepUpDialog.tsx
1638
+ var import_react10 = require("react");
1639
+
1640
+ // src/react/components/surface.tsx
1641
+ var import_react6 = require("react");
1642
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1643
+ var Ctx5 = (0, import_react6.createContext)("standalone");
1644
+ function SurfaceProvider(props) {
1645
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Ctx5.Provider, { value: props.value, children: props.children });
1646
+ }
1647
+ function useSurface() {
1648
+ return (0, import_react6.useContext)(Ctx5);
1649
+ }
1650
+ function useCardClass() {
1651
+ return useSurface() === "modal" ? "throttle-card throttle-card--bare" : "throttle-card";
1652
+ }
1653
+
1654
+ // src/react/components/primitives/Card.tsx
1655
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1656
+ function CardHeader(props) {
1657
+ const a = useAppearance();
1658
+ const headerClass = `throttle-card__header${props.branded ? " throttle-card__header--branded" : ""}`;
1659
+ const titleClass = `throttle-card__title${props.branded ? " throttle-card__title--branded" : ""}`;
1660
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ...a.el("card.header", headerClass), children: [
1661
+ props.logo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ...a.el(props.logoKey ?? "card.logo", "throttle-card__logo"), children: props.logo }) : null,
1662
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { ...a.el(props.titleKey ?? "card.title", titleClass), children: props.title }),
1663
+ props.description ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { ...a.el("card.description", "throttle-card__description"), children: props.description }) : null
1664
+ ] });
1665
+ }
1666
+ function CardBody(props) {
1667
+ const a = useAppearance();
1668
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ...a.el("card.body", "throttle-card__body"), children: props.children });
1669
+ }
1670
+ function CardFooter(props) {
1671
+ const a = useAppearance();
1672
+ const className = `throttle-card__footer${props.ruled ? " throttle-card__footer--ruled" : ""}`;
1673
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ...a.el("card.footer", className), children: props.children });
1674
+ }
1675
+
1676
+ // src/react/components/primitives/Spinner.tsx
1677
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1678
+ function Spinner() {
1679
+ const a = useAppearance();
1680
+ const Slot = a.slots.Spinner;
1681
+ if (Slot) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Slot, {});
1682
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { ...a.el("spinner", "throttle-spinner"), "aria-hidden": "true" });
1683
+ }
1684
+
1685
+ // src/react/components/primitives/Button.tsx
1686
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1687
+ function Button({ variant = "primary", size = "default", elementKey, loading, children, ...rest }) {
1688
+ const a = useAppearance();
1689
+ const Slot = a.slots.Button;
1690
+ if (Slot) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slot, { variant, loading, ...rest, children });
1691
+ const base = `throttle-button throttle-button--${variant}${size === "xs" ? " throttle-button--xs" : ""}`;
1692
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1693
+ "button",
1694
+ {
1695
+ type: "button",
1696
+ ...rest,
1697
+ ...a.el(elementKey ?? "button", base),
1698
+ disabled: rest.disabled || loading,
1699
+ children: [
1700
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner, {}) : null,
1701
+ children
1702
+ ]
1703
+ }
1704
+ );
1705
+ }
1706
+
1707
+ // src/react/components/primitives/Field.tsx
1708
+ var import_react7 = require("react");
1709
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1710
+ function Field(props) {
1711
+ const a = useAppearance();
1712
+ const base = (0, import_react7.useId)();
1713
+ const inputId = `${base}-input`;
1714
+ const hintId = `${base}-hint`;
1715
+ const errorId = `${base}-error`;
1716
+ const describedBy = [props.hint ? hintId : null, props.error ? errorId : null].filter(Boolean).join(" ");
1717
+ const label = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { ...a.el("field.label", "throttle-field__label"), htmlFor: inputId, children: props.label });
1718
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ...a.el(props.elementKey ?? "field", "throttle-field"), children: [
1719
+ props.labelExtra ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "throttle-field__labelrow", children: [
1720
+ label,
1721
+ props.labelExtra
1722
+ ] }) : label,
1723
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1724
+ "input",
1725
+ {
1726
+ ...a.el("field.input", `throttle-field__input${props.grouped ? " throttle-field__input--grouped" : ""}`),
1727
+ id: inputId,
1728
+ name: props.name,
1729
+ type: props.type ?? "text",
1730
+ value: props.value,
1731
+ required: props.required,
1732
+ disabled: props.disabled,
1733
+ autoComplete: props.autoComplete,
1734
+ "aria-describedby": describedBy || void 0,
1735
+ "aria-invalid": props.error ? true : void 0,
1736
+ onChange: (e) => props.onChange(e.target.value)
1737
+ }
1738
+ ),
1739
+ props.hint ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { ...a.el("field.hint", "throttle-field__hint"), id: hintId, children: props.hint }) : null,
1740
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { ...a.el("field.error", "throttle-field__error"), id: errorId, role: "alert", children: props.error ?? "" })
1741
+ ] });
1742
+ }
1743
+ function FieldGroup(props) {
1744
+ const a = useAppearance();
1745
+ const items = import_react7.Children.toArray(props.children).filter(Boolean);
1746
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ...a.el(props.elementKey ?? "field.group", "throttle-field-group"), children: items.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react7.Fragment, { children: [
1747
+ i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "throttle-field-group__divider" }) : null,
1748
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "throttle-field-group__item", children: child })
1749
+ ] }, i)) });
1750
+ }
1751
+
1752
+ // src/react/components/primitives/Alert.tsx
1753
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1754
+ function Alert(props) {
1755
+ const a = useAppearance();
1756
+ const Slot = a.slots.Alert;
1757
+ if (Slot) return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Slot, { tone: props.tone, children: props.children });
1758
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1759
+ "p",
1760
+ {
1761
+ ...a.el("alert", `throttle-alert throttle-alert--${props.tone}`),
1762
+ role: props.tone === "error" ? "alert" : "status",
1763
+ children: props.children
1764
+ }
1765
+ );
1766
+ }
1767
+
1768
+ // src/react/components/primitives/Skeleton.tsx
1769
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1770
+ function Skeleton(props) {
1771
+ const a = useAppearance();
1772
+ const rows = Array.from({ length: props.count ?? 1 });
1773
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { "aria-hidden": "true", className: "throttle-stack", children: rows.map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { ...a.el("skeleton", "throttle-skeleton") }, i)) });
1774
+ }
1775
+
1776
+ // src/react/components/primitives/Modal.tsx
1777
+ var import_react8 = require("react");
1778
+ var import_react_dom = require("react-dom");
1779
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1780
+ var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
1781
+ function Modal(props) {
1782
+ const a = useAppearance();
1783
+ const { t } = useCopy();
1784
+ const panel = (0, import_react8.useRef)(null);
1785
+ const returnFocusTo = (0, import_react8.useRef)(null);
1786
+ const [mounted, setMounted] = (0, import_react8.useState)(false);
1787
+ (0, import_react8.useEffect)(() => {
1788
+ setMounted(true);
1789
+ }, []);
1790
+ const onCloseRef = (0, import_react8.useRef)(props.onClose);
1791
+ (0, import_react8.useEffect)(() => {
1792
+ onCloseRef.current = props.onClose;
1793
+ });
1794
+ (0, import_react8.useEffect)(() => {
1795
+ if (!props.open || !mounted) return;
1796
+ returnFocusTo.current = props.restoreFocusTo ?? document.activeElement;
1797
+ const previousOverflow = document.body.style.overflow;
1798
+ document.body.style.overflow = "hidden";
1799
+ panel.current?.focus();
1800
+ const onKeyDown = (e) => {
1801
+ if (e.key === "Escape") {
1802
+ onCloseRef.current();
1803
+ return;
1804
+ }
1805
+ if (e.key !== "Tab" || !panel.current) return;
1806
+ const items = Array.from(panel.current.querySelectorAll(FOCUSABLE));
1807
+ if (items.length === 0) {
1808
+ e.preventDefault();
1809
+ return;
1810
+ }
1811
+ const first = items[0];
1812
+ const last = items[items.length - 1];
1813
+ if (e.shiftKey && document.activeElement === first) {
1814
+ e.preventDefault();
1815
+ last.focus();
1816
+ } else if (!e.shiftKey && document.activeElement === last) {
1817
+ e.preventDefault();
1818
+ first.focus();
1819
+ }
1820
+ };
1821
+ document.addEventListener("keydown", onKeyDown);
1822
+ return () => {
1823
+ document.removeEventListener("keydown", onKeyDown);
1824
+ document.body.style.overflow = previousOverflow;
1825
+ returnFocusTo.current?.focus?.();
1826
+ };
1827
+ }, [props.open, mounted]);
1828
+ if (!props.open || !mounted) return null;
1829
+ return (0, import_react_dom.createPortal)(
1830
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1831
+ "div",
1832
+ {
1833
+ ...a.el(props.overlayKey ?? "modal.overlay", "throttle-modal__overlay"),
1834
+ "data-testid": "throttle-modal-overlay",
1835
+ onClick: (e) => {
1836
+ if (e.target === e.currentTarget) props.onClose();
1837
+ },
1838
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1839
+ "div",
1840
+ {
1841
+ ...a.el(props.containerKey ?? "modal.container", props.size === "compact" ? "throttle-modal__container throttle-modal__container--compact" : "throttle-modal__container"),
1842
+ ref: panel,
1843
+ role: "dialog",
1844
+ "aria-modal": "true",
1845
+ "aria-labelledby": props.labelledBy,
1846
+ tabIndex: -1,
1847
+ children: [
1848
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1849
+ "button",
1850
+ {
1851
+ ...a.el("modal.closeButton", "throttle-modal__close"),
1852
+ type: "button",
1853
+ "aria-label": t.common.close,
1854
+ onClick: props.onClose,
1855
+ children: "\xD7"
1856
+ }
1857
+ ),
1858
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SurfaceProvider, { value: "modal", children: props.children })
1859
+ ]
1860
+ }
1861
+ )
1862
+ }
1863
+ ) }),
1864
+ document.body
1865
+ );
1866
+ }
1867
+
1868
+ // src/react/components/primitives/Dropdown.tsx
1869
+ var import_react9 = require("react");
1870
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1871
+ function Dropdown(props) {
1872
+ const a = useAppearance();
1873
+ const [open, setOpen] = (0, import_react9.useState)(false);
1874
+ const root = (0, import_react9.useRef)(null);
1875
+ const trigger = (0, import_react9.useRef)(null);
1876
+ const menu = (0, import_react9.useRef)(null);
1877
+ (0, import_react9.useEffect)(() => {
1878
+ if (!open) return;
1879
+ menu.current?.querySelector('[role="menuitem"]')?.focus();
1880
+ const onDown = (e) => {
1881
+ if (root.current && !root.current.contains(e.target)) setOpen(false);
1882
+ };
1883
+ const onKey = (e) => {
1884
+ if (e.key === "Escape") {
1885
+ setOpen(false);
1886
+ trigger.current?.focus();
1887
+ }
1888
+ };
1889
+ document.addEventListener("mousedown", onDown);
1890
+ document.addEventListener("keydown", onKey);
1891
+ return () => {
1892
+ document.removeEventListener("mousedown", onDown);
1893
+ document.removeEventListener("keydown", onKey);
1894
+ };
1895
+ }, [open]);
1896
+ const move = (delta) => {
1897
+ const items = Array.from(menu.current?.querySelectorAll('[role="menuitem"]') ?? []);
1898
+ if (items.length === 0) return;
1899
+ const at = items.indexOf(document.activeElement);
1900
+ const next = (at + delta + items.length) % items.length;
1901
+ items[next < 0 ? items.length - 1 : next].focus();
1902
+ };
1903
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { ref: root, style: { position: "relative", display: "inline-block" }, children: [
1904
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1905
+ "button",
1906
+ {
1907
+ ref: trigger,
1908
+ type: "button",
1909
+ ...a.el(props.triggerKey ?? "dropdown.trigger", "throttle-dropdown__trigger"),
1910
+ "aria-haspopup": "menu",
1911
+ "aria-expanded": open,
1912
+ "aria-label": props.label,
1913
+ onClick: () => setOpen((v) => !v),
1914
+ children: props.trigger
1915
+ }
1916
+ ),
1917
+ open ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1918
+ "div",
1919
+ {
1920
+ ref: menu,
1921
+ ...a.el(props.menuKey ?? "dropdown.menu", "throttle-dropdown__menu"),
1922
+ role: "menu",
1923
+ onClick: () => setOpen(false),
1924
+ onKeyDown: (e) => {
1925
+ if (e.key === "ArrowDown") {
1926
+ e.preventDefault();
1927
+ move(1);
1928
+ }
1929
+ if (e.key === "ArrowUp") {
1930
+ e.preventDefault();
1931
+ move(-1);
1932
+ }
1933
+ },
1934
+ children: props.children
1935
+ }
1936
+ ) : null
1937
+ ] });
1938
+ }
1939
+ function DropdownItem(props) {
1940
+ const a = useAppearance();
1941
+ const attrs = a.el(props.itemKey ?? "dropdown.item", "throttle-dropdown__item");
1942
+ if (props.href) {
1943
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("a", { ...attrs, role: "menuitem", href: props.href, tabIndex: -1, children: props.children });
1944
+ }
1945
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { ...attrs, role: "menuitem", type: "button", tabIndex: -1, onClick: props.onClick, children: props.children });
1946
+ }
1947
+ function DropdownSeparator() {
1948
+ const a = useAppearance();
1949
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("hr", { ...a.el("dropdown.separator", "throttle-dropdown__separator") });
1950
+ }
1951
+
1952
+ // src/react/components/primitives/Tabs.tsx
1953
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1954
+ function Tabs(props) {
1955
+ const a = useAppearance();
1956
+ const at = props.tabs.findIndex((t) => t.id === props.active);
1957
+ const vertical = props.orientation === "vertical";
1958
+ const listClass = vertical ? "throttle-tabs__rail" : "throttle-tabs__list";
1959
+ const itemClass = vertical ? "throttle-tabs__railitem" : "throttle-tabs__tab";
1960
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1961
+ "div",
1962
+ {
1963
+ ...a.el("tabs.list", listClass),
1964
+ role: "tablist",
1965
+ "aria-orientation": vertical ? "vertical" : void 0,
1966
+ onKeyDown: (e) => {
1967
+ const next = vertical ? "ArrowDown" : "ArrowRight";
1968
+ const prev = vertical ? "ArrowUp" : "ArrowLeft";
1969
+ const delta = e.key === next ? 1 : e.key === prev ? -1 : 0;
1970
+ if (!delta) return;
1971
+ e.preventDefault();
1972
+ const nextIndex = (at + delta + props.tabs.length) % props.tabs.length;
1973
+ props.onChange(props.tabs[nextIndex].id);
1974
+ e.currentTarget.querySelectorAll('[role="tab"]')[nextIndex]?.focus();
1975
+ },
1976
+ children: props.tabs.map((tab) => {
1977
+ const selected = tab.id === props.active;
1978
+ const { className, ...rest } = a.el("tabs.tab", itemClass);
1979
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1980
+ "button",
1981
+ {
1982
+ ...rest,
1983
+ className: [className, selected ? `${itemClass}--active` : null].filter(Boolean).join(" "),
1984
+ role: "tab",
1985
+ type: "button",
1986
+ id: props.idBase ? `${props.idBase}-tab-${tab.id}` : void 0,
1987
+ "aria-controls": props.idBase ? `${props.idBase}-panel-${tab.id}` : void 0,
1988
+ "aria-selected": selected,
1989
+ tabIndex: selected ? 0 : -1,
1990
+ onClick: () => props.onChange(tab.id),
1991
+ children: [
1992
+ tab.icon,
1993
+ tab.label
1994
+ ]
1995
+ },
1996
+ tab.id
1997
+ );
1998
+ })
1999
+ }
2000
+ );
2001
+ }
2002
+
2003
+ // src/react/components/primitives/DataTable.tsx
2004
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2005
+ function DataTable(props) {
2006
+ const a = useAppearance();
2007
+ const clickable = Boolean(props.onRowClick);
2008
+ return (
2009
+ // Tables are the one thing allowed to be wider than the panel; it scrolls
2010
+ // rather than forcing the whole page to.
2011
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "throttle-table__scroll", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("table", { ...a.el(props.tableKey ?? "table", "throttle-table"), children: [
2012
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("tr", { children: props.columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("th", { ...a.el("table.head", "throttle-table__head"), scope: "col", children: col.label }, col.key)) }) }),
2013
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("tbody", { children: props.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2014
+ "tr",
2015
+ {
2016
+ ...a.el(props.rowElementKey ?? "table.row", "throttle-table__row"),
2017
+ tabIndex: clickable ? 0 : void 0,
2018
+ onClick: clickable ? () => props.onRowClick(row) : void 0,
2019
+ onKeyDown: clickable ? (e) => {
2020
+ if (e.key === "Enter" || e.key === " ") {
2021
+ e.preventDefault();
2022
+ props.onRowClick(row);
2023
+ }
2024
+ } : void 0,
2025
+ children: props.columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("td", { ...a.el("table.cell", "throttle-table__cell"), children: col.render ? col.render(row) : String(row[col.key] ?? "") }, col.key))
2026
+ },
2027
+ props.rowKey(row)
2028
+ )) })
2029
+ ] }) })
2030
+ );
2031
+ }
2032
+
2033
+ // src/react/components/primitives/EmptyState.tsx
2034
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2035
+ function EmptyState(props) {
2036
+ const a = useAppearance();
2037
+ const Slot = a.slots.EmptyState;
2038
+ if (Slot) return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Slot, { title: props.title, description: props.description });
2039
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { ...a.el("emptyState", "throttle-empty"), children: [
2040
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { ...a.el("emptyState.title", "throttle-empty__title"), children: props.title }),
2041
+ props.description ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { ...a.el("emptyState.description", "throttle-empty__description"), children: props.description }) : null
2042
+ ] });
2043
+ }
2044
+
2045
+ // src/react/components/primitives/Avatar.tsx
2046
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2047
+ function Avatar(props) {
2048
+ const a = useAppearance();
2049
+ const Slot = a.slots.Avatar;
2050
+ if (Slot) return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Slot, { name: props.name, email: props.email });
2051
+ const initials = props.name.split(" ").map((part) => part.trim()[0]).filter(Boolean).slice(0, 2).join("") || props.email.trim()[0] || "?";
2052
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { ...a.el("avatar", "throttle-avatar"), title: props.email, children: initials.toUpperCase() });
2053
+ }
2054
+
2055
+ // src/react/components/primitives/Badge.tsx
2056
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2057
+ function Badge(props) {
2058
+ const a = useAppearance();
2059
+ const tone = props.tone ?? "neutral";
2060
+ const base = tone === "neutral" ? "throttle-badge" : `throttle-badge throttle-badge--${tone}`;
2061
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { ...a.el(props.elementKey ?? "badge", base), children: props.children });
2062
+ }
2063
+
2064
+ // src/react/components/auth/StepUpDialog.tsx
2065
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2066
+ function StepUpDialog(props) {
2067
+ const a = useAppearance();
2068
+ const cardClass = useCardClass();
2069
+ const { t } = useCopy();
2070
+ const [password, setPassword] = (0, import_react10.useState)("");
2071
+ const titleId = (0, import_react10.useId)();
2072
+ (0, import_react10.useEffect)(() => {
2073
+ if (!props.open) setPassword("");
2074
+ }, [props.open]);
2075
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Modal, { open: props.open, onClose: () => props.onResolve(null), labelledBy: titleId, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { ...a.el("stepUp.root", cardClass), children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2076
+ "form",
2077
+ {
2078
+ onSubmit: (e) => {
2079
+ e.preventDefault();
2080
+ props.onResolve(password);
2081
+ },
2082
+ children: [
2083
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "throttle-card__header", children: [
2084
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { id: titleId, ...a.el("stepUp.title", "throttle-card__title"), children: t.stepUp.title }),
2085
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "throttle-card__description", children: t.stepUp.description })
2086
+ ] }),
2087
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(CardBody, { children: [
2088
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2089
+ Field,
2090
+ {
2091
+ label: t.stepUp.password,
2092
+ type: "password",
2093
+ autoComplete: "current-password",
2094
+ value: password,
2095
+ onChange: setPassword,
2096
+ required: true,
2097
+ elementKey: "stepUp.passwordField"
2098
+ }
2099
+ ),
2100
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { type: "submit", elementKey: "stepUp.submitButton", children: t.stepUp.submitButton }),
2101
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { variant: "link", elementKey: "stepUp.cancelButton", onClick: () => props.onResolve(null), children: t.stepUp.cancelButton })
2102
+ ] })
2103
+ ]
2104
+ }
2105
+ ) }) });
2106
+ }
2107
+
2108
+ // src/react/components/auth/AuthModal.tsx
2109
+ var import_react18 = require("react");
2110
+
2111
+ // src/react/components/auth/AuthFlow.tsx
2112
+ var import_react17 = require("react");
2113
+
2114
+ // src/react/components/auth/SignIn.tsx
2115
+ var import_react12 = require("react");
2116
+
2117
+ // src/react/components/useAction.ts
2118
+ var import_react11 = require("react");
2119
+ function useAction() {
2120
+ const { errorMessage } = useCopy();
2121
+ const [submitting, setSubmitting] = (0, import_react11.useState)(false);
2122
+ const [error, setError] = (0, import_react11.useState)(null);
2123
+ const [notice, setNotice] = (0, import_react11.useState)(null);
2124
+ const inFlight = (0, import_react11.useRef)(false);
2125
+ const run = (0, import_react11.useCallback)(
2126
+ async (fn) => {
2127
+ if (inFlight.current) return;
2128
+ inFlight.current = true;
2129
+ setSubmitting(true);
2130
+ setError(null);
2131
+ setNotice(null);
2132
+ try {
2133
+ const message = await fn();
2134
+ if (typeof message === "string") setNotice(message);
2135
+ } catch (err) {
2136
+ setError(errorMessage(err));
2137
+ } finally {
2138
+ inFlight.current = false;
2139
+ setSubmitting(false);
2140
+ }
2141
+ },
2142
+ [errorMessage]
2143
+ );
2144
+ return { submitting, error, notice, run };
2145
+ }
2146
+
2147
+ // src/react/components/auth/SignIn.tsx
2148
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2149
+ function SignIn(props) {
2150
+ const a = useAppearance(props.appearance);
2151
+ const cardClass = useCardClass();
2152
+ const scoped = useAppearanceProviderValue(props.appearance);
2153
+ const { t } = useCopy(props.localization);
2154
+ const { auth } = useAuth();
2155
+ const { submitting, error, run } = useAction();
2156
+ const [email, setEmail] = (0, import_react12.useState)("");
2157
+ const [password, setPassword] = (0, import_react12.useState)("");
2158
+ const variant = props.variant ?? "quiet";
2159
+ const branded = variant === "branded";
2160
+ const grouped = variant === "grouped";
2161
+ const logo = useBrandLogo(props.logo);
2162
+ const forgotLink = props.onForgotPassword ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Button, { variant: "link", size: "xs", elementKey: "signIn.forgotLink", onClick: props.onForgotPassword, children: t.signIn.forgotLink }) : null;
2163
+ const emailField = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2164
+ Field,
2165
+ {
2166
+ label: t.signIn.email,
2167
+ type: "email",
2168
+ autoComplete: "email",
2169
+ required: true,
2170
+ value: email,
2171
+ onChange: setEmail,
2172
+ elementKey: "signIn.emailField",
2173
+ grouped
2174
+ }
2175
+ );
2176
+ const passwordField = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2177
+ Field,
2178
+ {
2179
+ label: t.signIn.password,
2180
+ type: "password",
2181
+ autoComplete: "current-password",
2182
+ required: true,
2183
+ value: password,
2184
+ onChange: setPassword,
2185
+ elementKey: "signIn.passwordField",
2186
+ grouped,
2187
+ labelExtra: grouped ? void 0 : forgotLink
2188
+ }
2189
+ );
2190
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2191
+ "form",
2192
+ {
2193
+ ...a.el("signIn.root", cardClass),
2194
+ onSubmit: (e) => {
2195
+ e.preventDefault();
2196
+ void run(async () => {
2197
+ await auth.signIn(email, password);
2198
+ props.onSuccess?.();
2199
+ });
2200
+ },
2201
+ children: [
2202
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2203
+ CardHeader,
2204
+ {
2205
+ title: t.signIn.title,
2206
+ description: branded ? t.signIn.description : void 0,
2207
+ titleKey: "signIn.title",
2208
+ logo: branded ? logo : null,
2209
+ logoKey: "signIn.logo",
2210
+ branded
2211
+ }
2212
+ ),
2213
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(CardBody, { children: [
2214
+ grouped ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(FieldGroup, { children: [
2215
+ emailField,
2216
+ passwordField
2217
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
2218
+ emailField,
2219
+ passwordField
2220
+ ] }),
2221
+ grouped && forgotLink ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "throttle-field-group__forgot", children: forgotLink }) : null,
2222
+ error ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Alert, { tone: "error", children: error }) : null,
2223
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Button, { type: "submit", loading: submitting, elementKey: "signIn.submitButton", children: t.signIn.submitButton })
2224
+ ] }),
2225
+ props.onSignUp ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(CardFooter, { ruled: !branded, children: [
2226
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "throttle-muted", children: [
2227
+ t.signIn.switchPrompt,
2228
+ " "
2229
+ ] }),
2230
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Button, { variant: "link", elementKey: "signIn.switchLink", onClick: props.onSignUp, children: t.signIn.switchLink })
2231
+ ] }) : null
2232
+ ]
2233
+ }
2234
+ ) }) });
2235
+ }
2236
+
2237
+ // src/react/components/auth/SignUp.tsx
2238
+ var import_react13 = require("react");
2239
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2240
+ function SignUp(props) {
2241
+ const a = useAppearance(props.appearance);
2242
+ const cardClass = useCardClass();
2243
+ const scoped = useAppearanceProviderValue(props.appearance);
2244
+ const { t } = useCopy(props.localization);
2245
+ const { auth } = useAuth();
2246
+ const { submitting, error, notice, run } = useAction();
2247
+ const [email, setEmail] = (0, import_react13.useState)("");
2248
+ const [password, setPassword] = (0, import_react13.useState)("");
2249
+ const variant = props.variant ?? "quiet";
2250
+ const branded = variant === "branded";
2251
+ const grouped = variant === "grouped";
2252
+ const logo = useBrandLogo(props.logo);
2253
+ const emailField = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2254
+ Field,
2255
+ {
2256
+ label: t.signUp.email,
2257
+ type: "email",
2258
+ autoComplete: "email",
2259
+ required: true,
2260
+ value: email,
2261
+ onChange: setEmail,
2262
+ elementKey: "signUp.emailField",
2263
+ grouped
2264
+ }
2265
+ );
2266
+ const passwordField = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2267
+ Field,
2268
+ {
2269
+ label: t.signUp.password,
2270
+ type: "password",
2271
+ autoComplete: "new-password",
2272
+ required: true,
2273
+ value: password,
2274
+ onChange: setPassword,
2275
+ elementKey: "signUp.passwordField",
2276
+ grouped
2277
+ }
2278
+ );
2279
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2280
+ "form",
2281
+ {
2282
+ ...a.el("signUp.root", cardClass),
2283
+ onSubmit: (e) => {
2284
+ e.preventDefault();
2285
+ void run(async () => {
2286
+ const result = await auth.register(email, password);
2287
+ if (result.status === "active") {
2288
+ props.onSuccess?.();
2289
+ return;
2290
+ }
2291
+ return t.signUp.pendingNotice;
2292
+ });
2293
+ },
2294
+ children: [
2295
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2296
+ CardHeader,
2297
+ {
2298
+ title: t.signUp.title,
2299
+ description: branded ? t.signUp.description : void 0,
2300
+ titleKey: "signUp.title",
2301
+ logo: branded ? logo : null,
2302
+ logoKey: "signUp.logo",
2303
+ branded
2304
+ }
2305
+ ),
2306
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(CardBody, { children: [
2307
+ grouped ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(FieldGroup, { children: [
2308
+ emailField,
2309
+ passwordField
2310
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
2311
+ emailField,
2312
+ passwordField
2313
+ ] }),
2314
+ error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Alert, { tone: "error", children: error }) : null,
2315
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Alert, { tone: "info", children: notice }) : null,
2316
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Button, { type: "submit", loading: submitting, elementKey: "signUp.submitButton", children: t.signUp.submitButton })
2317
+ ] }),
2318
+ props.onSignIn ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(CardFooter, { ruled: !branded, children: [
2319
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "throttle-muted", children: [
2320
+ t.signUp.switchPrompt,
2321
+ " "
2322
+ ] }),
2323
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Button, { variant: "link", elementKey: "signUp.switchLink", onClick: props.onSignIn, children: t.signUp.switchLink })
2324
+ ] }) : null
2325
+ ]
2326
+ }
2327
+ ) }) });
2328
+ }
2329
+
2330
+ // src/react/components/auth/ForgotPassword.tsx
2331
+ var import_react14 = require("react");
2332
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2333
+ function ForgotPassword(props) {
2334
+ const a = useAppearance(props.appearance);
2335
+ const cardClass = useCardClass();
2336
+ const scoped = useAppearanceProviderValue(props.appearance);
2337
+ const { t } = useCopy(props.localization);
2338
+ const { auth } = useAuth();
2339
+ const { submitting, error, notice, run } = useAction();
2340
+ const [email, setEmail] = (0, import_react14.useState)("");
2341
+ const branded = (props.variant ?? "quiet") === "branded";
2342
+ const logo = useBrandLogo(props.logo);
2343
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2344
+ "form",
2345
+ {
2346
+ ...a.el("forgotPassword.root", cardClass),
2347
+ onSubmit: (e) => {
2348
+ e.preventDefault();
2349
+ void run(async () => {
2350
+ await auth.forgotPassword(email);
2351
+ return t.forgotPassword.sentNotice;
2352
+ });
2353
+ },
2354
+ children: [
2355
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2356
+ CardHeader,
2357
+ {
2358
+ title: t.forgotPassword.title,
2359
+ description: branded ? t.forgotPassword.description : void 0,
2360
+ titleKey: "forgotPassword.title",
2361
+ logo: branded ? logo : null,
2362
+ logoKey: "forgotPassword.logo",
2363
+ branded
2364
+ }
2365
+ ),
2366
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CardBody, { children: [
2367
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2368
+ Field,
2369
+ {
2370
+ label: t.forgotPassword.email,
2371
+ type: "email",
2372
+ autoComplete: "email",
2373
+ required: true,
2374
+ value: email,
2375
+ onChange: setEmail,
2376
+ elementKey: "forgotPassword.emailField"
2377
+ }
2378
+ ),
2379
+ error ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Alert, { tone: "error", children: error }) : null,
2380
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Alert, { tone: "success", children: notice }) : null,
2381
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Button, { type: "submit", loading: submitting, elementKey: "forgotPassword.submitButton", children: t.forgotPassword.submitButton })
2382
+ ] }),
2383
+ props.onBack ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CardFooter, { ruled: !branded, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Button, { variant: "link", elementKey: "forgotPassword.backLink", onClick: props.onBack, children: t.forgotPassword.backLink }) }) : null
2384
+ ]
2385
+ }
2386
+ ) }) });
2387
+ }
2388
+
2389
+ // src/react/components/auth/ResetPassword.tsx
2390
+ var import_react15 = require("react");
2391
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2392
+ function ResetPassword(props) {
2393
+ const a = useAppearance(props.appearance);
2394
+ const cardClass = useCardClass();
2395
+ const scoped = useAppearanceProviderValue(props.appearance);
2396
+ const { t } = useCopy(props.localization);
2397
+ const { auth } = useAuth();
2398
+ const { submitting, error, notice, run } = useAction();
2399
+ const [password, setPassword] = (0, import_react15.useState)("");
2400
+ const branded = (props.variant ?? "quiet") === "branded";
2401
+ const logo = useBrandLogo(props.logo);
2402
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { ...a.el("resetPassword.root", cardClass), children: [
2403
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2404
+ CardHeader,
2405
+ {
2406
+ title: t.resetPassword.title,
2407
+ titleKey: "resetPassword.title",
2408
+ logo: branded ? logo : null,
2409
+ logoKey: "resetPassword.logo",
2410
+ branded
2411
+ }
2412
+ ),
2413
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CardBody, { children: !props.token ? (
2414
+ // No form at all without a token. A submit button that can only
2415
+ // fail is worse than an explanation.
2416
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Alert, { tone: "error", children: t.resetPassword.missingToken })
2417
+ ) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2418
+ "form",
2419
+ {
2420
+ onSubmit: (e) => {
2421
+ e.preventDefault();
2422
+ void run(async () => {
2423
+ await auth.resetPassword(props.token, password);
2424
+ props.onSuccess?.();
2425
+ return t.resetPassword.doneNotice;
2426
+ });
2427
+ },
2428
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "throttle-stack", children: [
2429
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2430
+ Field,
2431
+ {
2432
+ label: t.resetPassword.password,
2433
+ type: "password",
2434
+ autoComplete: "new-password",
2435
+ required: true,
2436
+ value: password,
2437
+ onChange: setPassword,
2438
+ elementKey: "resetPassword.passwordField"
2439
+ }
2440
+ ),
2441
+ error ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Alert, { tone: "error", children: error }) : null,
2442
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Alert, { tone: "success", children: notice }) : null,
2443
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Button, { type: "submit", loading: submitting, elementKey: "resetPassword.submitButton", children: t.resetPassword.submitButton })
2444
+ ] })
2445
+ }
2446
+ ) })
2447
+ ] }) }) });
2448
+ }
2449
+
2450
+ // src/react/components/auth/VerifyEmail.tsx
2451
+ var import_react16 = require("react");
2452
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2453
+ function VerifyEmail(props) {
2454
+ const a = useAppearance(props.appearance);
2455
+ const cardClass = useCardClass();
2456
+ const scoped = useAppearanceProviderValue(props.appearance);
2457
+ const { t, errorMessage } = useCopy(props.localization);
2458
+ const { auth } = useAuth();
2459
+ const { submitting, notice, run } = useAction();
2460
+ const [state, setState] = (0, import_react16.useState)(props.token ? "verifying" : "idle");
2461
+ const [error, setError] = (0, import_react16.useState)(null);
2462
+ const branded = (props.variant ?? "quiet") === "branded";
2463
+ const logo = useBrandLogo(props.logo);
2464
+ const attempted = (0, import_react16.useRef)(null);
2465
+ (0, import_react16.useEffect)(() => {
2466
+ const token = props.token;
2467
+ if (!token || attempted.current === token) return;
2468
+ attempted.current = token;
2469
+ setState("verifying");
2470
+ auth.verifyEmail(token).then(() => {
2471
+ setState("done");
2472
+ props.onSuccess?.();
2473
+ }).catch((err) => {
2474
+ setError(errorMessage(err));
2475
+ setState("failed");
2476
+ });
2477
+ }, [props.token, auth]);
2478
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { ...a.el("verifyEmail.root", cardClass), children: [
2479
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2480
+ CardHeader,
2481
+ {
2482
+ title: t.verifyEmail.title,
2483
+ titleKey: "verifyEmail.title",
2484
+ logo: branded ? logo : null,
2485
+ logoKey: "verifyEmail.logo",
2486
+ branded
2487
+ }
2488
+ ),
2489
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CardBody, { children: [
2490
+ state === "verifying" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("p", { children: [
2491
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Spinner, {}),
2492
+ " ",
2493
+ t.verifyEmail.verifying
2494
+ ] }) : null,
2495
+ state === "done" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Alert, { tone: "success", children: t.verifyEmail.success }) : null,
2496
+ state === "failed" && error ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Alert, { tone: "error", children: error }) : null,
2497
+ state === "idle" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "throttle-muted", children: t.verifyEmail.instructions }) : null,
2498
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Alert, { tone: "success", children: notice }) : null,
2499
+ state !== "done" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2500
+ Button,
2501
+ {
2502
+ variant: "secondary",
2503
+ loading: submitting,
2504
+ elementKey: "verifyEmail.resendButton",
2505
+ onClick: () => void run(async () => {
2506
+ await auth.resendVerification();
2507
+ return t.verifyEmail.resentNotice;
2508
+ }),
2509
+ children: t.verifyEmail.resendButton
2510
+ }
2511
+ ) : null
2512
+ ] })
2513
+ ] }) }) });
2514
+ }
2515
+
2516
+ // src/react/components/auth/AuthFlow.tsx
2517
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2518
+ var HASH_FOR = {
2519
+ signIn: "#/sign-in",
2520
+ signUp: "#/sign-up",
2521
+ forgotPassword: "#/forgot-password",
2522
+ resetPassword: "#/reset-password",
2523
+ verifyEmail: "#/verify-email"
2524
+ };
2525
+ var VIEW_FOR_HASH = Object.fromEntries(
2526
+ Object.entries(HASH_FOR).map(([view, hash]) => [hash, view])
2527
+ );
2528
+ function readLocation() {
2529
+ if (typeof window === "undefined") return { view: null };
2530
+ const params = new URLSearchParams(window.location.search);
2531
+ const token = params.get("token") ?? void 0;
2532
+ const hashView = VIEW_FOR_HASH[window.location.hash];
2533
+ if (hashView) return { view: hashView, token };
2534
+ if (!token) return { view: null };
2535
+ const path = window.location.pathname.toLowerCase();
2536
+ if (path.includes("reset")) return { view: "resetPassword", token };
2537
+ if (path.includes("verif")) return { view: "verifyEmail", token };
2538
+ return { view: null, token };
2539
+ }
2540
+ function AuthFlow(props) {
2541
+ const a = useAppearance(props.appearance);
2542
+ const controlled = props.view !== void 0;
2543
+ const [internal, setInternal] = (0, import_react17.useState)(props.initialView ?? "signIn");
2544
+ const [urlToken, setUrlToken] = (0, import_react17.useState)(void 0);
2545
+ (0, import_react17.useEffect)(() => {
2546
+ const found = readLocation();
2547
+ if (found.token) setUrlToken(found.token);
2548
+ if (found.view && !controlled && props.initialView === void 0) setInternal(found.view);
2549
+ }, []);
2550
+ const view = controlled ? props.view : internal;
2551
+ const token = props.token ?? urlToken;
2552
+ const go = (next) => {
2553
+ props.onViewChange?.(next);
2554
+ if (!controlled) setInternal(next);
2555
+ if (props.routing === "hash" && typeof window !== "undefined") {
2556
+ window.location.hash = HASH_FOR[next];
2557
+ }
2558
+ };
2559
+ (0, import_react17.useEffect)(() => {
2560
+ if (props.routing !== "hash" || typeof window === "undefined") return;
2561
+ const onHashChange = () => {
2562
+ if (controlled) return;
2563
+ const hash = window.location.hash;
2564
+ if (hash === "") {
2565
+ setInternal(props.initialView ?? "signIn");
2566
+ return;
2567
+ }
2568
+ const next = VIEW_FOR_HASH[hash];
2569
+ if (next) setInternal(next);
2570
+ };
2571
+ window.addEventListener("hashchange", onHashChange);
2572
+ return () => window.removeEventListener("hashchange", onHashChange);
2573
+ }, [props.routing, controlled]);
2574
+ const shared = { appearance: props.appearance, localization: props.localization };
2575
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { ...a.el("authFlow.root"), children: [
2576
+ view === "signIn" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2577
+ SignIn,
2578
+ {
2579
+ ...shared,
2580
+ onSuccess: props.afterSignIn,
2581
+ onForgotPassword: () => go("forgotPassword"),
2582
+ onSignUp: () => go("signUp")
2583
+ }
2584
+ ) : null,
2585
+ view === "signUp" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SignUp, { ...shared, onSuccess: props.afterSignUp, onSignIn: () => go("signIn") }) : null,
2586
+ view === "forgotPassword" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ForgotPassword, { ...shared, onBack: () => go("signIn") }) : null,
2587
+ view === "resetPassword" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ResetPassword, { ...shared, token, onSuccess: () => go("signIn") }) : null,
2588
+ view === "verifyEmail" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(VerifyEmail, { ...shared, token }) : null
2589
+ ] }) });
2590
+ }
2591
+
2592
+ // src/react/components/auth/AuthModal.tsx
2593
+ var import_jsx_runtime26 = require("react/jsx-runtime");
2594
+ function AuthModal(props) {
2595
+ const scoped = useAppearanceProviderValue(props.appearance);
2596
+ const titleId = (0, import_react18.useId)();
2597
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2598
+ Modal,
2599
+ {
2600
+ open: props.open,
2601
+ onClose: props.onClose,
2602
+ labelledBy: titleId,
2603
+ size: "compact",
2604
+ overlayKey: "authModal.overlay",
2605
+ containerKey: "authModal.container",
2606
+ children: [
2607
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h2", { id: titleId, style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: "Account" }),
2608
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2609
+ AuthFlow,
2610
+ {
2611
+ initialView: props.initialView,
2612
+ afterSignIn: props.afterSignIn,
2613
+ afterSignUp: props.afterSignUp,
2614
+ appearance: props.appearance,
2615
+ localization: props.localization
2616
+ }
2617
+ )
2618
+ ]
2619
+ }
2620
+ ) });
2621
+ }
2622
+
2623
+ // src/react/components/account/AccountModal.tsx
2624
+ var import_react28 = require("react");
2625
+
2626
+ // src/react/components/account/AccountDashboard.tsx
2627
+ var import_react27 = require("react");
2628
+
2629
+ // src/react/components/account/VerifyEmailBanner.tsx
2630
+ var import_react19 = require("react");
2631
+ var import_jsx_runtime27 = require("react/jsx-runtime");
2632
+ var DISMISS_KEY = "throttle-auth:verify-banner-dismissed";
2633
+ function readDismissed() {
2634
+ try {
2635
+ return sessionStorage.getItem(DISMISS_KEY) === "1";
2636
+ } catch {
2637
+ return false;
2638
+ }
2639
+ }
2640
+ function writeDismissed() {
2641
+ try {
2642
+ sessionStorage.setItem(DISMISS_KEY, "1");
2643
+ } catch {
2644
+ }
2645
+ }
2646
+ function VerifyEmailBanner(props) {
2647
+ const a = useAppearance(props.appearance);
2648
+ const scoped = useAppearanceProviderValue(props.appearance);
2649
+ const { t } = useCopy(props.localization);
2650
+ const { auth, status, emailVerified } = useAuth();
2651
+ const { submitting, notice, error, run } = useAction();
2652
+ const [dismissed, setDismissed] = (0, import_react19.useState)(readDismissed);
2653
+ if (status !== "authenticated" || emailVerified || dismissed) return null;
2654
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { ...a.el("verifyBanner.root", "throttle-banner"), children: [
2655
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { ...a.el("verifyBanner.message"), children: t.verifyBanner.message }),
2656
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Alert, { tone: "success", children: notice }) : null,
2657
+ error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Alert, { tone: "error", children: error }) : null,
2658
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "throttle-actions", children: [
2659
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2660
+ Button,
2661
+ {
2662
+ variant: "secondary",
2663
+ loading: submitting,
2664
+ elementKey: "verifyBanner.resendButton",
2665
+ onClick: () => void run(async () => {
2666
+ await auth.resendVerification();
2667
+ return t.verifyEmail.resentNotice;
2668
+ }),
2669
+ children: t.verifyBanner.resendButton
2670
+ }
2671
+ ),
2672
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2673
+ Button,
2674
+ {
2675
+ variant: "link",
2676
+ elementKey: "verifyBanner.dismissButton",
2677
+ onClick: () => {
2678
+ writeDismissed();
2679
+ setDismissed(true);
2680
+ },
2681
+ children: t.verifyBanner.dismissButton
2682
+ }
2683
+ )
2684
+ ] })
2685
+ ] }) }) });
2686
+ }
2687
+
2688
+ // src/react/components/account/icons.tsx
2689
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2690
+ function Glyph(props) {
2691
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2692
+ "svg",
2693
+ {
2694
+ className: "throttle-tabs__railicon",
2695
+ width: "18",
2696
+ height: "18",
2697
+ viewBox: "0 0 24 24",
2698
+ fill: "none",
2699
+ stroke: "currentColor",
2700
+ strokeWidth: "1.6",
2701
+ strokeLinecap: "round",
2702
+ strokeLinejoin: "round",
2703
+ "aria-hidden": "true",
2704
+ focusable: "false",
2705
+ children: props.children
2706
+ }
2707
+ );
2708
+ }
2709
+ var ACCOUNT_ICONS = {
2710
+ profile: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Glyph, { children: [
2711
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "8", r: "4" }),
2712
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M4 21c0-4 3.6-6 8-6s8 2 8 6" })
2713
+ ] }),
2714
+ addresses: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Glyph, { children: [
2715
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M12 21s7-5.3 7-11a7 7 0 1 0-14 0c0 5.7 7 11 7 11Z" }),
2716
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "10", r: "2.5" })
2717
+ ] }),
2718
+ paymentMethods: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Glyph, { children: [
2719
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("rect", { x: "2.5", y: "5.5", width: "19", height: "13", rx: "2.5" }),
2720
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M2.5 10h19" })
2721
+ ] }),
2722
+ orders: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Glyph, { children: [
2723
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M4 7h16v13H4z" }),
2724
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M8 7V4h8v3" }),
2725
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M8 12h8" })
2726
+ ] }),
2727
+ invoices: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Glyph, { children: [
2728
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M6 3h12v18l-3-2-3 2-3-2-3 2Z" }),
2729
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M9 8h6M9 12h6" })
2730
+ ] }),
2731
+ subscriptions: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Glyph, { children: [
2732
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M3 12a9 9 0 0 1 15.5-6.2" }),
2733
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M21 12a9 9 0 0 1-15.5 6.2" }),
2734
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M18 3v4h-4M6 21v-4h4" })
2735
+ ] }),
2736
+ security: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Glyph, { children: [
2737
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M12 3 4 6.5V12c0 5 3.4 8.3 8 9 4.6-.7 8-4 8-9V6.5Z" }),
2738
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "m9 12 2 2 4-4" })
2739
+ ] })
2740
+ };
2741
+
2742
+ // src/react/components/account/ProfilePanel.tsx
2743
+ var import_react21 = require("react");
2744
+
2745
+ // src/react/hooks.ts
2746
+ var import_react20 = require("react");
2747
+ function asError(err) {
2748
+ if (err instanceof ThrottleAuthError) return err;
2749
+ const anyErr = err;
2750
+ return new ThrottleAuthError({
2751
+ code: anyErr?.code ?? "request_failed",
2752
+ message: anyErr?.message ?? String(err),
2753
+ statusCode: anyErr?.statusCode ?? 0
2754
+ });
2755
+ }
2756
+ function useResource(empty, load, deps) {
2757
+ const { status, emailVerified } = useAuth();
2758
+ const [data, setData] = (0, import_react20.useState)(empty);
2759
+ const [isLoading, setIsLoading] = (0, import_react20.useState)(status === "loading");
2760
+ const [error, setError] = (0, import_react20.useState)(null);
2761
+ const gen = (0, import_react20.useRef)(0);
2762
+ const alive = (0, import_react20.useRef)(true);
2763
+ (0, import_react20.useEffect)(() => {
2764
+ alive.current = true;
2765
+ return () => {
2766
+ alive.current = false;
2767
+ gen.current++;
2768
+ };
2769
+ }, []);
2770
+ const refetch = (0, import_react20.useCallback)(async () => {
2771
+ const mine = ++gen.current;
2772
+ setIsLoading(true);
2773
+ try {
2774
+ const next = await load();
2775
+ if (!alive.current || gen.current !== mine) return;
2776
+ setData(next);
2777
+ setError(null);
2778
+ } catch (err) {
2779
+ if (!alive.current || gen.current !== mine) return;
2780
+ setError(asError(err));
2781
+ } finally {
2782
+ if (alive.current && gen.current === mine) setIsLoading(false);
2783
+ }
2784
+ }, deps);
2785
+ (0, import_react20.useEffect)(() => {
2786
+ if (status !== "authenticated") {
2787
+ gen.current++;
2788
+ setData(empty);
2789
+ setError(null);
2790
+ setIsLoading(status === "loading");
2791
+ return;
2792
+ }
2793
+ void refetch();
2794
+ }, [status, emailVerified, refetch]);
2795
+ return { data, isLoading, error, refetch };
2796
+ }
2797
+ function useCustomer() {
2798
+ const { account } = useAuth();
2799
+ const res = useResource(null, () => account.getProfile(), [account]);
2800
+ const update = (0, import_react20.useCallback)(async (patch) => {
2801
+ await account.updateProfile(patch);
2802
+ await res.refetch();
2803
+ }, [account, res]);
2804
+ return { ...res, update };
2805
+ }
2806
+ function useAddresses() {
2807
+ const { account } = useAuth();
2808
+ const res = useResource([], () => account.listAddresses(), [account]);
2809
+ const after = async (p) => {
2810
+ await p;
2811
+ await res.refetch();
2812
+ };
2813
+ return {
2814
+ ...res,
2815
+ create: (input) => after(account.createAddress(input)),
2816
+ update: (id, patch) => after(account.updateAddress(id, patch)),
2817
+ remove: (id) => after(account.deleteAddress(id))
2818
+ };
2819
+ }
2820
+ function useSavedCards() {
2821
+ const { account } = useAuth();
2822
+ const res = useResource([], () => account.listPaymentMethods(), [account]);
2823
+ const after = async (p) => {
2824
+ await p;
2825
+ await res.refetch();
2826
+ };
2827
+ return {
2828
+ ...res,
2829
+ setDefault: (id) => after(account.setDefaultPaymentMethod(id)),
2830
+ remove: (id) => after(account.deletePaymentMethod(id))
2831
+ };
2832
+ }
2833
+ function useOrders(page) {
2834
+ const { account } = useAuth();
2835
+ return useResource([], () => account.listOrders(page), [account, page?.cursor, page?.limit]);
2836
+ }
2837
+ function useInvoices(page) {
2838
+ const { account } = useAuth();
2839
+ return useResource([], () => account.listInvoices(page), [account, page?.cursor, page?.limit]);
2840
+ }
2841
+ function useSubscriptions(page) {
2842
+ const { account } = useAuth();
2843
+ const res = useResource(
2844
+ [],
2845
+ () => account.listSubscriptions(page),
2846
+ [account, page?.cursor, page?.limit]
2847
+ );
2848
+ const after = async (p) => {
2849
+ await p;
2850
+ await res.refetch();
2851
+ };
2852
+ return {
2853
+ ...res,
2854
+ cancel: (id, opts) => after(account.cancelSubscription(id, opts)),
2855
+ pause: (id) => after(account.pauseSubscription(id)),
2856
+ resume: (id) => after(account.resumeSubscription(id))
2857
+ };
2858
+ }
2859
+ function useOrder(id) {
2860
+ const { account } = useAuth();
2861
+ return useResource(null, async () => id ? account.getOrder(id) : null, [account, id]);
2862
+ }
2863
+ function useInvoice(id) {
2864
+ const { account } = useAuth();
2865
+ return useResource(null, async () => id ? account.getInvoice(id) : null, [account, id]);
2866
+ }
2867
+
2868
+ // src/react/components/account/PanelState.tsx
2869
+ var import_jsx_runtime29 = require("react/jsx-runtime");
2870
+ function PanelState(props) {
2871
+ const { t, errorMessage } = useCopy();
2872
+ const { data, isLoading, error, refetch } = props.resource;
2873
+ const empty = props.isEmpty(data);
2874
+ if (isLoading && empty) return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Skeleton, { count: props.skeletonRows ?? 3 });
2875
+ const errorBanner = error ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "throttle-stack", children: [
2876
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Alert, { tone: "error", children: errorMessage(error) }),
2877
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Button, { variant: "secondary", onClick: () => void refetch(), children: t.common.retry })
2878
+ ] }) : null;
2879
+ const gatedByVerification = error?.code === "verification_required";
2880
+ if (gatedByVerification && empty) {
2881
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(EmptyState, { title: props.emptyTitle, description: props.gatedDescription ?? props.emptyDescription });
2882
+ }
2883
+ if (error && empty) return errorBanner;
2884
+ if (empty) {
2885
+ return props.gated ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(EmptyState, { title: props.emptyTitle, description: props.gatedDescription }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(EmptyState, { title: props.emptyTitle, description: props.emptyDescription });
2886
+ }
2887
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
2888
+ errorBanner,
2889
+ props.children(data)
2890
+ ] });
2891
+ }
2892
+
2893
+ // src/react/components/account/ProfilePanel.tsx
2894
+ var import_jsx_runtime30 = require("react/jsx-runtime");
2895
+ function ProfilePanel(props) {
2896
+ const a = useAppearance(props.appearance);
2897
+ const scoped = useAppearanceProviderValue(props.appearance);
2898
+ const { t } = useCopy(props.localization);
2899
+ const { status } = useAuth();
2900
+ const resource = useCustomer();
2901
+ const { submitting, error, notice, run } = useAction();
2902
+ const [form, setForm] = (0, import_react21.useState)({ firstName: "", lastName: "", phone: "", company: "", acceptsMarketing: false });
2903
+ (0, import_react21.useEffect)(() => {
2904
+ const c = resource.data;
2905
+ if (!c) return;
2906
+ setForm({
2907
+ firstName: c.firstName ?? "",
2908
+ lastName: c.lastName ?? "",
2909
+ phone: c.phone ?? "",
2910
+ company: c.company ?? "",
2911
+ acceptsMarketing: Boolean(c.acceptsMarketing)
2912
+ });
2913
+ }, [resource.data]);
2914
+ if (status === "unauthenticated") return null;
2915
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { ...a.el("profilePanel.root", "throttle-panel"), children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2916
+ PanelState,
2917
+ {
2918
+ resource,
2919
+ isEmpty: (c) => c === null,
2920
+ emptyTitle: t.common.somethingWentWrong,
2921
+ skeletonRows: 4,
2922
+ children: (customer) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
2923
+ "form",
2924
+ {
2925
+ ...a.el("profilePanel.form", "throttle-stack"),
2926
+ onSubmit: (e) => {
2927
+ e.preventDefault();
2928
+ void run(async () => {
2929
+ await resource.update({
2930
+ firstName: form.firstName,
2931
+ lastName: form.lastName,
2932
+ phone: form.phone,
2933
+ company: form.company || null,
2934
+ acceptsMarketing: form.acceptsMarketing
2935
+ });
2936
+ return t.profile.savedNotice;
2937
+ });
2938
+ },
2939
+ children: [
2940
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2941
+ Field,
2942
+ {
2943
+ label: t.profile.email,
2944
+ type: "email",
2945
+ value: customer.email,
2946
+ onChange: () => {
2947
+ },
2948
+ disabled: true,
2949
+ hint: t.profile.emailReadOnly
2950
+ }
2951
+ ),
2952
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2953
+ Field,
2954
+ {
2955
+ label: t.profile.firstName,
2956
+ value: form.firstName,
2957
+ autoComplete: "given-name",
2958
+ onChange: (v) => setForm((f) => ({ ...f, firstName: v }))
2959
+ }
2960
+ ),
2961
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2962
+ Field,
2963
+ {
2964
+ label: t.profile.lastName,
2965
+ value: form.lastName,
2966
+ autoComplete: "family-name",
2967
+ onChange: (v) => setForm((f) => ({ ...f, lastName: v }))
2968
+ }
2969
+ ),
2970
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2971
+ Field,
2972
+ {
2973
+ label: t.profile.phone,
2974
+ type: "tel",
2975
+ value: form.phone,
2976
+ autoComplete: "tel",
2977
+ onChange: (v) => setForm((f) => ({ ...f, phone: v }))
2978
+ }
2979
+ ),
2980
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2981
+ Field,
2982
+ {
2983
+ label: t.profile.company,
2984
+ value: form.company,
2985
+ autoComplete: "organization",
2986
+ onChange: (v) => setForm((f) => ({ ...f, company: v }))
2987
+ }
2988
+ ),
2989
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("label", { className: "throttle-row", children: [
2990
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2991
+ "input",
2992
+ {
2993
+ type: "checkbox",
2994
+ checked: form.acceptsMarketing,
2995
+ onChange: (e) => setForm((f) => ({ ...f, acceptsMarketing: e.target.checked }))
2996
+ }
2997
+ ),
2998
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { children: t.profile.acceptsMarketing })
2999
+ ] }),
3000
+ error ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Alert, { tone: "error", children: error }) : null,
3001
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Alert, { tone: "success", children: notice }) : null,
3002
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Button, { type: "submit", loading: submitting, elementKey: "profilePanel.submitButton", children: t.profile.saveButton })
3003
+ ]
3004
+ }
3005
+ )
3006
+ }
3007
+ ) }) }) });
3008
+ }
3009
+
3010
+ // src/react/components/account/AddressesPanel.tsx
3011
+ var import_react23 = require("react");
3012
+
3013
+ // src/react/components/account/AddressForm.tsx
3014
+ var import_react22 = require("react");
3015
+ var import_jsx_runtime31 = require("react/jsx-runtime");
3016
+ function AddressForm(props) {
3017
+ const a = useAppearance();
3018
+ const { t } = useCopy();
3019
+ const i = props.initial;
3020
+ const [form, setForm] = (0, import_react22.useState)({
3021
+ label: i?.label ?? "",
3022
+ firstName: i?.firstName ?? "",
3023
+ lastName: i?.lastName ?? "",
3024
+ company: i?.company ?? "",
3025
+ addressLine1: i?.addressLine1 ?? "",
3026
+ addressLine2: i?.addressLine2 ?? "",
3027
+ city: i?.city ?? "",
3028
+ stateProvince: i?.stateProvince ?? "",
3029
+ postalCode: i?.postalCode ?? "",
3030
+ countryCode: i?.countryCode ?? "",
3031
+ phone: i?.phone ?? ""
3032
+ });
3033
+ const set = (key) => (value) => setForm((f) => ({ ...f, [key]: value }));
3034
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
3035
+ "form",
3036
+ {
3037
+ ...a.el("addressesPanel.form", "throttle-stack"),
3038
+ onSubmit: (e) => {
3039
+ e.preventDefault();
3040
+ props.onSubmit({
3041
+ label: form.label || void 0,
3042
+ firstName: form.firstName || void 0,
3043
+ lastName: form.lastName || void 0,
3044
+ company: form.company || void 0,
3045
+ addressLine1: form.addressLine1,
3046
+ addressLine2: form.addressLine2 || void 0,
3047
+ city: form.city,
3048
+ stateProvince: form.stateProvince || void 0,
3049
+ postalCode: form.postalCode || void 0,
3050
+ // The API wants ISO-3166 alpha-2; uppercase so "gb" is not rejected.
3051
+ countryCode: form.countryCode.toUpperCase(),
3052
+ phone: form.phone || void 0
3053
+ });
3054
+ },
3055
+ children: [
3056
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Field, { label: t.addresses.label, value: form.label, onChange: set("label") }),
3057
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Field, { label: t.addresses.firstName, value: form.firstName, onChange: set("firstName"), autoComplete: "given-name" }),
3058
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Field, { label: t.addresses.lastName, value: form.lastName, onChange: set("lastName"), autoComplete: "family-name" }),
3059
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Field, { label: t.addresses.company, value: form.company, onChange: set("company"), autoComplete: "organization" }),
3060
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3061
+ Field,
3062
+ {
3063
+ label: t.addresses.addressLine1,
3064
+ value: form.addressLine1,
3065
+ onChange: set("addressLine1"),
3066
+ required: true,
3067
+ autoComplete: "address-line1"
3068
+ }
3069
+ ),
3070
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3071
+ Field,
3072
+ {
3073
+ label: t.addresses.addressLine2,
3074
+ value: form.addressLine2,
3075
+ onChange: set("addressLine2"),
3076
+ autoComplete: "address-line2"
3077
+ }
3078
+ ),
3079
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Field, { label: t.addresses.city, value: form.city, onChange: set("city"), required: true, autoComplete: "address-level2" }),
3080
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3081
+ Field,
3082
+ {
3083
+ label: t.addresses.stateProvince,
3084
+ value: form.stateProvince,
3085
+ onChange: set("stateProvince"),
3086
+ autoComplete: "address-level1"
3087
+ }
3088
+ ),
3089
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3090
+ Field,
3091
+ {
3092
+ label: t.addresses.postalCode,
3093
+ value: form.postalCode,
3094
+ onChange: set("postalCode"),
3095
+ autoComplete: "postal-code"
3096
+ }
3097
+ ),
3098
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3099
+ Field,
3100
+ {
3101
+ label: t.addresses.countryCode,
3102
+ value: form.countryCode,
3103
+ onChange: set("countryCode"),
3104
+ required: true,
3105
+ autoComplete: "country",
3106
+ hint: "ISO 3166-1 alpha-2, e.g. GB"
3107
+ }
3108
+ ),
3109
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Field, { label: t.addresses.phone, type: "tel", value: form.phone, onChange: set("phone"), autoComplete: "tel" }),
3110
+ props.error ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Alert, { tone: "error", children: props.error }) : null,
3111
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "throttle-actions", children: [
3112
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Button, { type: "submit", loading: props.submitting, children: t.common.save }),
3113
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Button, { variant: "link", onClick: props.onCancel, children: t.common.cancel })
3114
+ ] })
3115
+ ]
3116
+ }
3117
+ );
3118
+ }
3119
+
3120
+ // src/react/components/account/AddressesPanel.tsx
3121
+ var import_jsx_runtime32 = require("react/jsx-runtime");
3122
+ function AddressesPanel(props) {
3123
+ const a = useAppearance(props.appearance);
3124
+ const scoped = useAppearanceProviderValue(props.appearance);
3125
+ const { t } = useCopy(props.localization);
3126
+ const resource = useAddresses();
3127
+ const { submitting, error, run } = useAction();
3128
+ const [editing, setEditing] = (0, import_react23.useState)(null);
3129
+ const variant = props.variant ?? "rows";
3130
+ const rowsVariant = variant === "rows";
3131
+ const listBase = rowsVariant ? "throttle-rows" : "throttle-stack";
3132
+ const itemBase = rowsVariant ? "throttle-rowitem" : "throttle-listitem";
3133
+ const actionVariant = (base) => rowsVariant ? base === "danger" ? "linkDanger" : "link" : base;
3134
+ const close = () => setEditing(null);
3135
+ const formatAddress = (address) => [address.addressLine2, address.city, address.stateProvince, address.postalCode, address.countryCode].filter(Boolean).join(", ");
3136
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { ...a.el("addressesPanel.root", "throttle-panel"), children: [
3137
+ !editing && error ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Alert, { tone: "error", children: error }) : null,
3138
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3139
+ AddressForm,
3140
+ {
3141
+ initial: editing === "new" ? void 0 : editing,
3142
+ submitting,
3143
+ error,
3144
+ onCancel: close,
3145
+ onSubmit: (input) => void run(async () => {
3146
+ if (editing === "new") await resource.create(input);
3147
+ else await resource.update(editing.id, input);
3148
+ close();
3149
+ })
3150
+ }
3151
+ ) : /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
3152
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Button, { elementKey: "addressesPanel.addButton", variant: "secondary", onClick: () => setEditing("new"), children: t.addresses.addButton }),
3153
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3154
+ PanelState,
3155
+ {
3156
+ resource,
3157
+ isEmpty: (rows) => rows.length === 0,
3158
+ emptyTitle: t.addresses.empty,
3159
+ emptyDescription: t.addresses.emptyDescription,
3160
+ children: (rows) => variant === "table" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3161
+ DataTable,
3162
+ {
3163
+ tableKey: "addressesPanel.table",
3164
+ rowElementKey: "addressesPanel.row",
3165
+ rowKey: (address) => address.id,
3166
+ rows,
3167
+ columns: [
3168
+ { key: "label", label: t.addresses.label, render: (address) => address.label || address.addressLine1 },
3169
+ { key: "address", label: t.addresses.columnAddress, render: (address) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
3170
+ address.addressLine1,
3171
+ formatAddress(address) ? `, ${formatAddress(address)}` : ""
3172
+ ] }) },
3173
+ { key: "default", label: t.addresses.defaultBadge, render: (address) => address.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Badge, { tone: "success", children: t.addresses.defaultBadge }) : null },
3174
+ { key: "actions", label: t.common.actions, render: (address) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "throttle-actions", children: [
3175
+ !address.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3176
+ Button,
3177
+ {
3178
+ variant: "secondary",
3179
+ elementKey: "addressesPanel.defaultButton",
3180
+ onClick: () => void run(() => resource.update(address.id, { isDefault: true })),
3181
+ children: t.addresses.makeDefault
3182
+ }
3183
+ ) : null,
3184
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3185
+ Button,
3186
+ {
3187
+ variant: "secondary",
3188
+ elementKey: "addressesPanel.editButton",
3189
+ onClick: () => setEditing(address),
3190
+ children: t.common.edit
3191
+ }
3192
+ ),
3193
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3194
+ Button,
3195
+ {
3196
+ variant: "danger",
3197
+ elementKey: "addressesPanel.deleteButton",
3198
+ disabled: submitting,
3199
+ onClick: () => {
3200
+ if (!window.confirm(t.addresses.deleteConfirm)) return;
3201
+ void run(() => resource.remove(address.id));
3202
+ },
3203
+ children: t.common.delete
3204
+ }
3205
+ )
3206
+ ] }) }
3207
+ ]
3208
+ }
3209
+ ) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { ...a.el("addressesPanel.list", listBase), children: rows.map((address) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { ...a.el("addressesPanel.item", itemBase), children: [
3210
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { children: [
3211
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "throttle-row", children: [
3212
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("strong", { children: address.label || address.addressLine1 }),
3213
+ address.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Badge, { tone: "success", children: t.addresses.defaultBadge }) : null
3214
+ ] }),
3215
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "throttle-muted", children: address.addressLine1 }),
3216
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "throttle-muted", children: formatAddress(address) })
3217
+ ] }),
3218
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "throttle-actions", children: [
3219
+ !address.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3220
+ Button,
3221
+ {
3222
+ variant: actionVariant("secondary"),
3223
+ elementKey: "addressesPanel.defaultButton",
3224
+ onClick: () => void run(() => resource.update(address.id, { isDefault: true })),
3225
+ children: t.addresses.makeDefault
3226
+ }
3227
+ ) : null,
3228
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3229
+ Button,
3230
+ {
3231
+ variant: actionVariant("secondary"),
3232
+ elementKey: "addressesPanel.editButton",
3233
+ onClick: () => setEditing(address),
3234
+ children: t.common.edit
3235
+ }
3236
+ ),
3237
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3238
+ Button,
3239
+ {
3240
+ variant: actionVariant("danger"),
3241
+ elementKey: "addressesPanel.deleteButton",
3242
+ disabled: submitting,
3243
+ onClick: () => {
3244
+ if (!window.confirm(t.addresses.deleteConfirm)) return;
3245
+ void run(() => resource.remove(address.id));
3246
+ },
3247
+ children: t.common.delete
3248
+ }
3249
+ )
3250
+ ] })
3251
+ ] }, address.id)) })
3252
+ }
3253
+ )
3254
+ ] })
3255
+ ] }) }) });
3256
+ }
3257
+
3258
+ // src/react/components/account/PaymentMethodsPanel.tsx
3259
+ var import_jsx_runtime33 = require("react/jsx-runtime");
3260
+ function PaymentMethodsPanel(props) {
3261
+ const a = useAppearance(props.appearance);
3262
+ const scoped = useAppearanceProviderValue(props.appearance);
3263
+ const { t } = useCopy(props.localization);
3264
+ const { emailVerified } = useAuth();
3265
+ const resource = useSavedCards();
3266
+ const { submitting, error, run } = useAction();
3267
+ const variant = props.variant ?? "rows";
3268
+ const rowsVariant = variant === "rows";
3269
+ const listBase = rowsVariant ? "throttle-rows" : "throttle-stack";
3270
+ const itemBase = rowsVariant ? "throttle-rowitem" : "throttle-listitem";
3271
+ const actionVariant = (base) => rowsVariant ? base === "danger" ? "linkDanger" : "link" : base;
3272
+ const cardLabel = (card) => `${(card.cardBrand ?? card.methodType).toUpperCase()} \u2022\u2022\u2022\u2022 ${card.cardLastFour ?? "____"}`;
3273
+ const expiryLabel = (card) => card.cardExpMonth && card.cardExpYear ? `${String(card.cardExpMonth).padStart(2, "0")}/${card.cardExpYear}` : "";
3274
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { ...a.el("paymentMethodsPanel.root", "throttle-panel"), children: [
3275
+ error ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Alert, { tone: "error", children: error }) : null,
3276
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3277
+ PanelState,
3278
+ {
3279
+ resource,
3280
+ gated: !emailVerified,
3281
+ gatedDescription: t.paymentMethods.gatedDescription,
3282
+ isEmpty: (rows) => rows.length === 0,
3283
+ emptyTitle: t.paymentMethods.empty,
3284
+ emptyDescription: t.paymentMethods.emptyDescription,
3285
+ children: (rows) => variant === "table" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3286
+ DataTable,
3287
+ {
3288
+ tableKey: "paymentMethodsPanel.table",
3289
+ rowElementKey: "paymentMethodsPanel.row",
3290
+ rowKey: (card) => card.id,
3291
+ rows,
3292
+ columns: [
3293
+ { key: "card", label: t.paymentMethods.columnCard, render: (card) => cardLabel(card) },
3294
+ { key: "expires", label: t.paymentMethods.expires, render: (card) => expiryLabel(card) },
3295
+ { key: "default", label: t.paymentMethods.defaultBadge, render: (card) => card.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Badge, { tone: "success", children: t.paymentMethods.defaultBadge }) : null },
3296
+ { key: "actions", label: t.common.actions, render: (card) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "throttle-actions", children: [
3297
+ !card.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3298
+ Button,
3299
+ {
3300
+ variant: "secondary",
3301
+ loading: submitting,
3302
+ elementKey: "paymentMethodsPanel.defaultButton",
3303
+ onClick: () => void run(() => resource.setDefault(card.id)),
3304
+ children: t.paymentMethods.makeDefault
3305
+ }
3306
+ ) : null,
3307
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3308
+ Button,
3309
+ {
3310
+ variant: "danger",
3311
+ elementKey: "paymentMethodsPanel.deleteButton",
3312
+ disabled: submitting,
3313
+ onClick: () => {
3314
+ if (!window.confirm(t.paymentMethods.deleteConfirm)) return;
3315
+ void run(() => resource.remove(card.id));
3316
+ },
3317
+ children: t.common.delete
3318
+ }
3319
+ )
3320
+ ] }) }
3321
+ ]
3322
+ }
3323
+ ) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ...a.el("paymentMethodsPanel.list", listBase), children: rows.map((card) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { ...a.el("paymentMethodsPanel.item", itemBase), children: [
3324
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { children: [
3325
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "throttle-row", children: [
3326
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: cardLabel(card) }),
3327
+ card.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Badge, { tone: "success", children: t.paymentMethods.defaultBadge }) : null
3328
+ ] }),
3329
+ card.cardExpMonth && card.cardExpYear ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: "throttle-muted", children: [
3330
+ t.paymentMethods.expires,
3331
+ " ",
3332
+ String(card.cardExpMonth).padStart(2, "0"),
3333
+ "/",
3334
+ card.cardExpYear
3335
+ ] }) : null
3336
+ ] }),
3337
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "throttle-actions", children: [
3338
+ !card.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3339
+ Button,
3340
+ {
3341
+ variant: actionVariant("secondary"),
3342
+ loading: submitting,
3343
+ elementKey: "paymentMethodsPanel.defaultButton",
3344
+ onClick: () => void run(() => resource.setDefault(card.id)),
3345
+ children: t.paymentMethods.makeDefault
3346
+ }
3347
+ ) : null,
3348
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3349
+ Button,
3350
+ {
3351
+ variant: actionVariant("danger"),
3352
+ elementKey: "paymentMethodsPanel.deleteButton",
3353
+ disabled: submitting,
3354
+ onClick: () => {
3355
+ if (!window.confirm(t.paymentMethods.deleteConfirm)) return;
3356
+ void run(() => resource.remove(card.id));
3357
+ },
3358
+ children: t.common.delete
3359
+ }
3360
+ )
3361
+ ] })
3362
+ ] }, card.id)) })
3363
+ }
3364
+ )
3365
+ ] }) }) });
3366
+ }
3367
+
3368
+ // src/react/components/account/OrdersPanel.tsx
3369
+ var import_react24 = require("react");
3370
+
3371
+ // src/react/components/format.ts
3372
+ function formatMoney(minorUnits, currency, locale) {
3373
+ const code = (currency || "USD").toUpperCase();
3374
+ let digits = 2;
3375
+ try {
3376
+ digits = new Intl.NumberFormat("en-US", { style: "currency", currency: code }).resolvedOptions().maximumFractionDigits ?? 2;
3377
+ } catch {
3378
+ }
3379
+ const major = minorUnits / 10 ** digits;
3380
+ try {
3381
+ return new Intl.NumberFormat(locale, { style: "currency", currency: code }).format(major);
3382
+ } catch {
3383
+ try {
3384
+ return new Intl.NumberFormat("en-US", { style: "currency", currency: code }).format(major);
3385
+ } catch {
3386
+ return `${code} ${major.toFixed(digits)}`;
3387
+ }
3388
+ }
3389
+ }
3390
+ function formatDate(value, locale) {
3391
+ const date = value instanceof Date ? value : new Date(value);
3392
+ if (Number.isNaN(date.getTime())) return "";
3393
+ try {
3394
+ return new Intl.DateTimeFormat(locale, { dateStyle: "medium" }).format(date);
3395
+ } catch {
3396
+ return date.toISOString().slice(0, 10);
3397
+ }
3398
+ }
3399
+
3400
+ // src/react/components/account/OrderStatus.tsx
3401
+ var import_jsx_runtime34 = require("react/jsx-runtime");
3402
+ function copyKey(wire) {
3403
+ return wire.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
3404
+ }
3405
+ var ATTENTION_PAYMENT_STATES = /* @__PURE__ */ new Set(["pending", "failed", "disputed", "expired"]);
3406
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["cancelled", "closed", "void", "expired", "voided"]);
3407
+ function StatusBadge(props) {
3408
+ const label = props.labels[copyKey(props.status)] ?? props.status.replace(/_/g, " ");
3409
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Badge, { tone: TERMINAL_STATUSES.has(props.status) ? "muted" : "neutral", children: label });
3410
+ }
3411
+ function OrderStatus(props) {
3412
+ const labels = props.t.orders.statusLabels;
3413
+ const payLabels = props.t.orders.paymentLabels;
3414
+ const lifecycle = labels[copyKey(props.status)] ?? props.status.replace(/_/g, " ");
3415
+ const money = props.paymentStatus ? payLabels[copyKey(props.paymentStatus)] : void 0;
3416
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
3417
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Badge, { tone: TERMINAL_STATUSES.has(props.status) ? "muted" : "neutral", children: lifecycle }),
3418
+ money ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Badge, { tone: ATTENTION_PAYMENT_STATES.has(props.paymentStatus) ? "warning" : "neutral", children: money }) : null
3419
+ ] });
3420
+ }
3421
+
3422
+ // src/react/components/account/OrderDetail.tsx
3423
+ var import_jsx_runtime35 = require("react/jsx-runtime");
3424
+ function Line(props) {
3425
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "throttle-row", children: [
3426
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "throttle-muted", children: props.label }),
3427
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: props.value })
3428
+ ] });
3429
+ }
3430
+ function OrderDetail(props) {
3431
+ const a = useAppearance(props.appearance);
3432
+ const scoped = useAppearanceProviderValue(props.appearance);
3433
+ const { t, locale } = useCopy(props.localization);
3434
+ const resource = useOrder(props.orderId);
3435
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { ...a.el("orderDetail.root", "throttle-panel"), children: [
3436
+ props.onBack ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Button, { variant: "link", elementKey: "orderDetail.backButton", onClick: props.onBack, children: t.common.back }) : null,
3437
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
3438
+ PanelState,
3439
+ {
3440
+ resource,
3441
+ isEmpty: (o) => o === null,
3442
+ emptyTitle: t.orders.empty,
3443
+ skeletonRows: 5,
3444
+ children: (order) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
3445
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "throttle-row", children: [
3446
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h3", { children: order.orderNumber }),
3447
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(OrderStatus, { status: order.status, paymentStatus: order.paymentStatus, t })
3448
+ ] }),
3449
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "throttle-muted", children: formatDate(order.createdAt, locale) }),
3450
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { ...a.el("orderDetail.lineItems", "throttle-stack"), children: order.lineItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { ...a.el("orderDetail.lineItem", "throttle-lineitem"), children: [
3451
+ item.imageUrl ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("img", { className: "throttle-lineitem__image", src: item.imageUrl, alt: "" }) : null,
3452
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { style: { flex: 1 }, children: [
3453
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("strong", { children: item.name }),
3454
+ item.description ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "throttle-muted", children: item.description }) : null,
3455
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { className: "throttle-muted", children: [
3456
+ t.orders.quantity,
3457
+ " ",
3458
+ item.quantity,
3459
+ " \xD7 ",
3460
+ formatMoney(item.unitPrice, order.currency, locale)
3461
+ ] })
3462
+ ] }),
3463
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: formatMoney(item.total, order.currency, locale) })
3464
+ ] }, item.id)) }),
3465
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { ...a.el("orderDetail.totals", "throttle-totals"), children: [
3466
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Line, { label: t.orders.subtotal, value: formatMoney(order.subtotal, order.currency, locale) }),
3467
+ order.discountTotal ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Line, { label: t.orders.discount, value: formatMoney(-order.discountTotal, order.currency, locale) }) : null,
3468
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Line, { label: t.orders.shipping, value: formatMoney(order.shippingTotal, order.currency, locale) }),
3469
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Line, { label: t.orders.tax, value: formatMoney(order.taxTotal, order.currency, locale) }),
3470
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Line, { label: t.orders.total, value: formatMoney(order.total, order.currency, locale) })
3471
+ ] }),
3472
+ order.subscriptionId && props.onViewSubscription ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Button, { variant: "secondary", onClick: () => props.onViewSubscription(order.subscriptionId), children: t.orders.viewSubscription }) : null
3473
+ ] })
3474
+ }
3475
+ )
3476
+ ] }) }) });
3477
+ }
3478
+
3479
+ // src/react/components/account/OrdersPanel.tsx
3480
+ var import_jsx_runtime36 = require("react/jsx-runtime");
3481
+ function OrdersPanel(props) {
3482
+ const a = useAppearance(props.appearance);
3483
+ const scoped = useAppearanceProviderValue(props.appearance);
3484
+ const { t, locale } = useCopy(props.localization);
3485
+ const { emailVerified } = useAuth();
3486
+ const resource = useOrders();
3487
+ const [openId, setOpenId] = (0, import_react24.useState)(null);
3488
+ const variant = props.variant ?? "table";
3489
+ const rowsVariant = variant === "rows";
3490
+ const listBase = rowsVariant ? "throttle-rows" : "throttle-stack";
3491
+ const itemBase = rowsVariant ? "throttle-rowitem" : "throttle-listitem";
3492
+ if (openId) {
3493
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
3494
+ OrderDetail,
3495
+ {
3496
+ orderId: openId,
3497
+ onBack: () => setOpenId(null),
3498
+ onViewSubscription: props.onViewSubscription,
3499
+ appearance: props.appearance,
3500
+ localization: props.localization
3501
+ }
3502
+ );
3503
+ }
3504
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { ...a.el("ordersPanel.root", "throttle-panel"), children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
3505
+ PanelState,
3506
+ {
3507
+ resource,
3508
+ gated: !emailVerified,
3509
+ gatedDescription: t.orders.gatedDescription,
3510
+ isEmpty: (rows) => rows.length === 0,
3511
+ emptyTitle: t.orders.empty,
3512
+ emptyDescription: t.orders.emptyDescription,
3513
+ children: (rows) => variant === "table" ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
3514
+ DataTable,
3515
+ {
3516
+ tableKey: "ordersPanel.table",
3517
+ rowElementKey: "ordersPanel.row",
3518
+ rowKey: (o) => o.id,
3519
+ onRowClick: (o) => setOpenId(o.id),
3520
+ rows,
3521
+ columns: [
3522
+ { key: "orderNumber", label: t.orders.number },
3523
+ { key: "createdAt", label: t.orders.date, render: (o) => formatDate(o.createdAt, locale) },
3524
+ {
3525
+ key: "status",
3526
+ label: t.orders.status,
3527
+ render: (o) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(OrderStatus, { status: o.status, paymentStatus: o.paymentStatus, t })
3528
+ },
3529
+ {
3530
+ key: "total",
3531
+ label: t.orders.total,
3532
+ render: (o) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "throttle-table__money", children: formatMoney(o.total, o.currency, locale) })
3533
+ }
3534
+ ]
3535
+ }
3536
+ ) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { ...a.el("ordersPanel.list", listBase), children: rows.map((o) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { ...a.el("ordersPanel.item", itemBase), children: [
3537
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { children: [
3538
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "throttle-row", children: [
3539
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("strong", { children: o.orderNumber }),
3540
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(OrderStatus, { status: o.status, paymentStatus: o.paymentStatus, t })
3541
+ ] }),
3542
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "throttle-muted", children: formatDate(o.createdAt, locale) }),
3543
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "throttle-muted", children: formatMoney(o.total, o.currency, locale) })
3544
+ ] }),
3545
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "throttle-actions", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
3546
+ Button,
3547
+ {
3548
+ variant: rowsVariant ? "link" : "secondary",
3549
+ elementKey: "ordersPanel.viewButton",
3550
+ onClick: () => setOpenId(o.id),
3551
+ children: t.common.view
3552
+ }
3553
+ ) })
3554
+ ] }, o.id)) })
3555
+ }
3556
+ ) }) }) });
3557
+ }
3558
+
3559
+ // src/react/components/account/InvoicesPanel.tsx
3560
+ var import_react25 = require("react");
3561
+
3562
+ // src/react/components/account/InvoiceDetail.tsx
3563
+ var import_jsx_runtime37 = require("react/jsx-runtime");
3564
+ function Line2(props) {
3565
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "throttle-row", children: [
3566
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "throttle-muted", children: props.label }),
3567
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { children: props.value })
3568
+ ] });
3569
+ }
3570
+ function InvoiceDetail(props) {
3571
+ const a = useAppearance(props.appearance);
3572
+ const scoped = useAppearanceProviderValue(props.appearance);
3573
+ const { t, locale } = useCopy(props.localization);
3574
+ const { account } = useAuth();
3575
+ const resource = useInvoice(props.invoiceId);
3576
+ const { submitting, error, run } = useAction();
3577
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { ...a.el("invoiceDetail.root", "throttle-panel"), children: [
3578
+ props.onBack ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { variant: "link", elementKey: "invoiceDetail.backButton", onClick: props.onBack, children: t.common.back }) : null,
3579
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PanelState, { resource, isEmpty: (i) => i === null, emptyTitle: t.invoices.empty, skeletonRows: 4, children: (invoice) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
3580
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "throttle-row", children: [
3581
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h3", { children: invoice.number }),
3582
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatusBadge, { status: invoice.status, labels: t.invoices.statusLabels })
3583
+ ] }),
3584
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { ...a.el("invoiceDetail.totals", "throttle-totals"), children: [
3585
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Line2, { label: t.invoices.issued, value: formatDate(invoice.issuedAt, locale) }),
3586
+ invoice.dueAt ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Line2, { label: t.invoices.due, value: formatDate(invoice.dueAt, locale) }) : null,
3587
+ invoice.periodStart && invoice.periodEnd ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
3588
+ Line2,
3589
+ {
3590
+ label: t.invoices.period,
3591
+ value: `${formatDate(invoice.periodStart, locale)} \u2013 ${formatDate(invoice.periodEnd, locale)}`
3592
+ }
3593
+ ) : null,
3594
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Line2, { label: t.invoices.total, value: formatMoney(invoice.total, invoice.currency, locale) }),
3595
+ invoice.amountRefunded ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
3596
+ Line2,
3597
+ {
3598
+ label: t.invoices.refunded,
3599
+ value: formatMoney(invoice.amountRefunded, invoice.currency, locale)
3600
+ }
3601
+ ) : null
3602
+ ] }),
3603
+ error ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Alert, { tone: "error", children: error }) : null,
3604
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
3605
+ Button,
3606
+ {
3607
+ variant: "secondary",
3608
+ loading: submitting,
3609
+ elementKey: "invoiceDetail.downloadButton",
3610
+ onClick: () => void run(async () => {
3611
+ const { url } = await account.getInvoicePdfUrl(invoice.id);
3612
+ const opened = window.open(url, "_blank", "noopener,noreferrer");
3613
+ if (!opened) window.location.assign(url);
3614
+ }),
3615
+ children: t.invoices.downloadButton
3616
+ }
3617
+ )
3618
+ ] }) })
3619
+ ] }) }) });
3620
+ }
3621
+
3622
+ // src/react/components/account/InvoicesPanel.tsx
3623
+ var import_jsx_runtime38 = require("react/jsx-runtime");
3624
+ function InvoicesPanel(props) {
3625
+ const a = useAppearance(props.appearance);
3626
+ const scoped = useAppearanceProviderValue(props.appearance);
3627
+ const { t, locale } = useCopy(props.localization);
3628
+ const { emailVerified } = useAuth();
3629
+ const resource = useInvoices();
3630
+ const [openId, setOpenId] = (0, import_react25.useState)(null);
3631
+ const variant = props.variant ?? "table";
3632
+ const rowsVariant = variant === "rows";
3633
+ const listBase = rowsVariant ? "throttle-rows" : "throttle-stack";
3634
+ const itemBase = rowsVariant ? "throttle-rowitem" : "throttle-listitem";
3635
+ if (openId) {
3636
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
3637
+ InvoiceDetail,
3638
+ {
3639
+ invoiceId: openId,
3640
+ onBack: () => setOpenId(null),
3641
+ appearance: props.appearance,
3642
+ localization: props.localization
3643
+ }
3644
+ );
3645
+ }
3646
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { ...a.el("invoicesPanel.root", "throttle-panel"), children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
3647
+ PanelState,
3648
+ {
3649
+ resource,
3650
+ gated: !emailVerified,
3651
+ gatedDescription: t.invoices.gatedDescription,
3652
+ isEmpty: (rows) => rows.length === 0,
3653
+ emptyTitle: t.invoices.empty,
3654
+ emptyDescription: t.invoices.emptyDescription,
3655
+ children: (rows) => variant === "table" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
3656
+ DataTable,
3657
+ {
3658
+ tableKey: "invoicesPanel.table",
3659
+ rowElementKey: "invoicesPanel.row",
3660
+ rowKey: (i) => i.id,
3661
+ onRowClick: (i) => setOpenId(i.id),
3662
+ rows,
3663
+ columns: [
3664
+ { key: "number", label: t.invoices.number },
3665
+ { key: "issuedAt", label: t.invoices.issued, render: (i) => formatDate(i.issuedAt, locale) },
3666
+ { key: "status", label: t.invoices.status, render: (i) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(StatusBadge, { status: i.status, labels: t.invoices.statusLabels }) },
3667
+ {
3668
+ key: "total",
3669
+ label: t.invoices.total,
3670
+ render: (i) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "throttle-table__money", children: formatMoney(i.total, i.currency, locale) })
3671
+ }
3672
+ ]
3673
+ }
3674
+ ) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { ...a.el("invoicesPanel.list", listBase), children: rows.map((i) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { ...a.el("invoicesPanel.item", itemBase), children: [
3675
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { children: [
3676
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "throttle-row", children: [
3677
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("strong", { children: i.number }),
3678
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(StatusBadge, { status: i.status, labels: t.invoices.statusLabels })
3679
+ ] }),
3680
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "throttle-muted", children: formatDate(i.issuedAt, locale) }),
3681
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "throttle-muted", children: formatMoney(i.total, i.currency, locale) })
3682
+ ] }),
3683
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "throttle-actions", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
3684
+ Button,
3685
+ {
3686
+ variant: rowsVariant ? "link" : "secondary",
3687
+ elementKey: "invoicesPanel.viewButton",
3688
+ onClick: () => setOpenId(i.id),
3689
+ children: t.common.view
3690
+ }
3691
+ ) })
3692
+ ] }, i.id)) })
3693
+ }
3694
+ ) }) }) });
3695
+ }
3696
+
3697
+ // src/react/components/account/SubscriptionsPanel.tsx
3698
+ var import_jsx_runtime39 = require("react/jsx-runtime");
3699
+ var CAN_PAUSE = /* @__PURE__ */ new Set(["active", "trialing"]);
3700
+ var CAN_RESUME = /* @__PURE__ */ new Set(["paused"]);
3701
+ var CAN_CANCEL = /* @__PURE__ */ new Set(["active", "trialing", "paused", "past_due", "unpaid"]);
3702
+ function SubscriptionsPanel(props) {
3703
+ const a = useAppearance(props.appearance);
3704
+ const scoped = useAppearanceProviderValue(props.appearance);
3705
+ const { t, locale } = useCopy(props.localization);
3706
+ const { emailVerified } = useAuth();
3707
+ const resource = useSubscriptions();
3708
+ const { submitting, error, run } = useAction();
3709
+ const variant = props.variant ?? "rows";
3710
+ const rowsVariant = variant === "rows";
3711
+ const listBase = rowsVariant ? "throttle-rows" : "throttle-stack";
3712
+ const itemBase = rowsVariant ? "throttle-rowitem" : "throttle-listitem";
3713
+ const actionVariant = (base) => rowsVariant ? base === "danger" ? "linkDanger" : "link" : base;
3714
+ const confirmThen = (fn) => () => {
3715
+ if (!window.confirm(t.subscriptions.cancelConfirm)) return;
3716
+ void run(fn);
3717
+ };
3718
+ const priceLabel = (sub) => `${formatMoney(sub.amount, sub.currency, locale)} / ${sub.interval}${sub.quantity > 1 ? ` \xD7 ${sub.quantity}` : ""}`;
3719
+ const renewsLabel = (sub) => formatDate(sub.status === "cancelled" ? sub.cancelledAt ?? sub.currentPeriodEnd : sub.currentPeriodEnd, locale);
3720
+ const lifecycleActions = (sub, variantFor) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "throttle-actions", children: [
3721
+ CAN_PAUSE.has(sub.status) ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
3722
+ Button,
3723
+ {
3724
+ variant: variantFor("secondary"),
3725
+ loading: submitting,
3726
+ elementKey: "subscriptionsPanel.pauseButton",
3727
+ onClick: () => void run(() => resource.pause(sub.id)),
3728
+ children: t.subscriptions.pauseButton
3729
+ }
3730
+ ) : null,
3731
+ CAN_RESUME.has(sub.status) ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
3732
+ Button,
3733
+ {
3734
+ variant: variantFor("secondary"),
3735
+ loading: submitting,
3736
+ elementKey: "subscriptionsPanel.resumeButton",
3737
+ onClick: () => void run(() => resource.resume(sub.id)),
3738
+ children: t.subscriptions.resumeButton
3739
+ }
3740
+ ) : null,
3741
+ CAN_CANCEL.has(sub.status) && !sub.cancelAtPeriodEnd ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
3742
+ Button,
3743
+ {
3744
+ variant: variantFor("secondary"),
3745
+ elementKey: "subscriptionsPanel.cancelButton",
3746
+ disabled: submitting,
3747
+ onClick: confirmThen(() => resource.cancel(sub.id, { atPeriodEnd: true })),
3748
+ children: t.subscriptions.cancelAtPeriodEnd
3749
+ }
3750
+ ) : null,
3751
+ CAN_CANCEL.has(sub.status) ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
3752
+ Button,
3753
+ {
3754
+ variant: variantFor("danger"),
3755
+ elementKey: "subscriptionsPanel.cancelNowButton",
3756
+ disabled: submitting,
3757
+ onClick: confirmThen(() => resource.cancel(sub.id, { atPeriodEnd: false })),
3758
+ children: t.subscriptions.cancelNow
3759
+ }
3760
+ ) : null
3761
+ ] });
3762
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { ...a.el("subscriptionsPanel.root", "throttle-panel"), children: [
3763
+ error ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Alert, { tone: "error", children: error }) : null,
3764
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
3765
+ PanelState,
3766
+ {
3767
+ resource,
3768
+ gated: !emailVerified,
3769
+ gatedDescription: t.subscriptions.gatedDescription,
3770
+ isEmpty: (rows) => rows.length === 0,
3771
+ emptyTitle: t.subscriptions.empty,
3772
+ emptyDescription: t.subscriptions.emptyDescription,
3773
+ children: (rows) => variant === "table" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
3774
+ DataTable,
3775
+ {
3776
+ tableKey: "subscriptionsPanel.table",
3777
+ rowElementKey: "subscriptionsPanel.row",
3778
+ rowKey: (sub) => sub.id,
3779
+ rows,
3780
+ columns: [
3781
+ { key: "plan", label: t.subscriptions.columnPlan, render: (sub) => sub.planName ?? sub.planReference },
3782
+ { key: "price", label: t.subscriptions.columnPrice, render: (sub) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "throttle-table__money", children: priceLabel(sub) }) },
3783
+ { key: "renews", label: t.subscriptions.columnRenews, render: (sub) => renewsLabel(sub) },
3784
+ { key: "status", label: t.subscriptions.columnStatus, render: (sub) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "throttle-row", children: [
3785
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(StatusBadge, { status: sub.status, labels: t.subscriptions.statusLabels }),
3786
+ sub.cancelAtPeriodEnd ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Badge, { tone: "warning", children: t.subscriptions.cancelScheduled }) : null
3787
+ ] }) },
3788
+ { key: "actions", label: t.common.actions, render: (sub) => lifecycleActions(sub, (base) => base) }
3789
+ ]
3790
+ }
3791
+ ) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { ...a.el("subscriptionsPanel.list", listBase), children: rows.map((sub) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { ...a.el("subscriptionsPanel.item", itemBase), children: [
3792
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { children: [
3793
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "throttle-row", children: [
3794
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("strong", { children: sub.planName ?? sub.planReference }),
3795
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(StatusBadge, { status: sub.status, labels: t.subscriptions.statusLabels })
3796
+ ] }),
3797
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "throttle-muted", children: [
3798
+ formatMoney(sub.amount, sub.currency, locale),
3799
+ " / ",
3800
+ sub.interval,
3801
+ sub.quantity > 1 ? ` \xD7 ${sub.quantity}` : ""
3802
+ ] }),
3803
+ sub.status === "cancelled" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "throttle-muted", children: [
3804
+ t.subscriptions.cancelledOn,
3805
+ " ",
3806
+ formatDate(sub.cancelledAt ?? sub.currentPeriodEnd, locale)
3807
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "throttle-muted", children: [
3808
+ sub.cancelAtPeriodEnd ? t.subscriptions.endsOn : t.subscriptions.renewsOn,
3809
+ " ",
3810
+ formatDate(sub.currentPeriodEnd, locale)
3811
+ ] }),
3812
+ sub.trialEnd ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "throttle-muted", children: [
3813
+ t.subscriptions.trialEnds,
3814
+ " ",
3815
+ formatDate(sub.trialEnd, locale)
3816
+ ] }) : null,
3817
+ sub.cancelAtPeriodEnd ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Badge, { tone: "warning", children: t.subscriptions.cancelScheduled }) : null
3818
+ ] }),
3819
+ lifecycleActions(sub, actionVariant)
3820
+ ] }, sub.id)) })
3821
+ }
3822
+ )
3823
+ ] }) }) });
3824
+ }
3825
+
3826
+ // src/react/components/account/SecurityPanel.tsx
3827
+ var import_react26 = require("react");
3828
+ var import_jsx_runtime40 = require("react/jsx-runtime");
3829
+ function SecurityPanel(props) {
3830
+ const a = useAppearance(props.appearance);
3831
+ const scoped = useAppearanceProviderValue(props.appearance);
3832
+ const { t, locale, errorMessage } = useCopy(props.localization);
3833
+ const { auth } = useAuth();
3834
+ const variant = props.variant ?? "rows";
3835
+ const rowsVariant = variant === "rows";
3836
+ const itemBase = rowsVariant ? "throttle-rowitem" : "throttle-listitem";
3837
+ const revokeVariant = rowsVariant ? "linkDanger" : "danger";
3838
+ const [password, setPassword] = (0, import_react26.useState)("");
3839
+ const passwordAction = useAction();
3840
+ const sessionAction = useAction();
3841
+ const [sessions, setSessions] = (0, import_react26.useState)(null);
3842
+ const [listError, setListError] = (0, import_react26.useState)(null);
3843
+ const errorMessageRef = (0, import_react26.useRef)(errorMessage);
3844
+ (0, import_react26.useEffect)(() => {
3845
+ errorMessageRef.current = errorMessage;
3846
+ });
3847
+ const load = (0, import_react26.useCallback)(async () => {
3848
+ try {
3849
+ setSessions(await auth.listSessions());
3850
+ setListError(null);
3851
+ } catch (err) {
3852
+ setListError(errorMessageRef.current(err));
3853
+ }
3854
+ }, [auth]);
3855
+ (0, import_react26.useEffect)(() => {
3856
+ void load();
3857
+ }, [load]);
3858
+ const others = (sessions ?? []).filter((s) => !s.current);
3859
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { ...a.el("securityPanel.root", "throttle-panel"), children: [
3860
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
3861
+ "form",
3862
+ {
3863
+ ...a.el("securityPanel.passwordForm", "throttle-stack"),
3864
+ onSubmit: (e) => {
3865
+ e.preventDefault();
3866
+ void passwordAction.run(async () => {
3867
+ await auth.changePassword(password);
3868
+ setPassword("");
3869
+ return t.security.changedNotice;
3870
+ });
3871
+ },
3872
+ children: [
3873
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h3", { children: t.security.passwordTitle }),
3874
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
3875
+ Field,
3876
+ {
3877
+ label: t.security.newPassword,
3878
+ type: "password",
3879
+ autoComplete: "new-password",
3880
+ required: true,
3881
+ value: password,
3882
+ onChange: setPassword
3883
+ }
3884
+ ),
3885
+ passwordAction.error ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Alert, { tone: "error", children: passwordAction.error }) : null,
3886
+ passwordAction.notice ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Alert, { tone: "success", children: passwordAction.notice }) : null,
3887
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Button, { type: "submit", loading: passwordAction.submitting, elementKey: "securityPanel.passwordButton", children: t.security.changeButton })
3888
+ ]
3889
+ }
3890
+ ),
3891
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { ...a.el("securityPanel.sessionList", "throttle-stack"), children: [
3892
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h3", { children: t.security.sessionsTitle }),
3893
+ listError ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
3894
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Alert, { tone: "error", children: listError }),
3895
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Button, { variant: "secondary", onClick: () => void load(), children: t.common.retry })
3896
+ ] }) : null,
3897
+ sessionAction.error ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Alert, { tone: "error", children: sessionAction.error }) : null,
3898
+ sessions === null && !listError ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Skeleton, { count: 2 }) : null,
3899
+ sessions !== null && !listError && variant === "table" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
3900
+ DataTable,
3901
+ {
3902
+ tableKey: "securityPanel.table",
3903
+ rowElementKey: "securityPanel.row",
3904
+ rowKey: (session) => session.id,
3905
+ rows: sessions,
3906
+ columns: [
3907
+ { key: "device", label: t.security.columnDevice, render: (session) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "throttle-row", children: [
3908
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { children: session.userAgent || session.ipPrefix || session.id }),
3909
+ session.current ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Badge, { tone: "success", children: t.security.thisDevice }) : null
3910
+ ] }) },
3911
+ { key: "lastActive", label: t.security.lastActive, render: (session) => formatDate(session.lastUsedAt, locale) },
3912
+ { key: "actions", label: t.common.actions, render: (session) => !session.current ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
3913
+ Button,
3914
+ {
3915
+ variant: "danger",
3916
+ loading: sessionAction.submitting,
3917
+ elementKey: "securityPanel.revokeButton",
3918
+ onClick: () => void sessionAction.run(async () => {
3919
+ await auth.revokeSession(session.id);
3920
+ await load();
3921
+ }),
3922
+ children: t.security.revokeButton
3923
+ }
3924
+ ) : null }
3925
+ ]
3926
+ }
3927
+ ) : (sessions ?? []).map((session) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { ...a.el("securityPanel.sessionItem", itemBase), children: [
3928
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { children: [
3929
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "throttle-row", children: [
3930
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: session.userAgent || session.ipPrefix || session.id }),
3931
+ session.current ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Badge, { tone: "success", children: t.security.thisDevice }) : null
3932
+ ] }),
3933
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "throttle-muted", children: [
3934
+ t.security.lastActive,
3935
+ " ",
3936
+ formatDate(session.lastUsedAt, locale)
3937
+ ] })
3938
+ ] }),
3939
+ !session.current ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
3940
+ Button,
3941
+ {
3942
+ variant: revokeVariant,
3943
+ loading: sessionAction.submitting,
3944
+ elementKey: "securityPanel.revokeButton",
3945
+ onClick: () => void sessionAction.run(async () => {
3946
+ await auth.revokeSession(session.id);
3947
+ await load();
3948
+ }),
3949
+ children: t.security.revokeButton
3950
+ }
3951
+ ) : null
3952
+ ] }, session.id)),
3953
+ sessions !== null && others.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "throttle-muted", children: t.security.sessionsEmpty }) : null,
3954
+ others.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
3955
+ Button,
3956
+ {
3957
+ variant: "danger",
3958
+ loading: sessionAction.submitting,
3959
+ elementKey: "securityPanel.revokeAllButton",
3960
+ onClick: () => void sessionAction.run(async () => {
3961
+ for (const session of others) {
3962
+ await auth.revokeSession(session.id);
3963
+ }
3964
+ await load();
3965
+ }),
3966
+ children: t.security.revokeAllButton
3967
+ }
3968
+ ) : null
3969
+ ] })
3970
+ ] }) }) });
3971
+ }
3972
+
3973
+ // src/react/components/account/AccountDashboard.tsx
3974
+ var import_jsx_runtime41 = require("react/jsx-runtime");
3975
+ var ACCOUNT_TABS = [
3976
+ "profile",
3977
+ "addresses",
3978
+ "paymentMethods",
3979
+ "orders",
3980
+ "invoices",
3981
+ "subscriptions",
3982
+ "security"
3983
+ ];
3984
+ var HASH_FOR2 = {
3985
+ profile: "#/profile",
3986
+ addresses: "#/addresses",
3987
+ paymentMethods: "#/payment-methods",
3988
+ orders: "#/orders",
3989
+ invoices: "#/invoices",
3990
+ subscriptions: "#/subscriptions",
3991
+ security: "#/security"
3992
+ };
3993
+ var TAB_FOR_HASH = Object.fromEntries(
3994
+ Object.entries(HASH_FOR2).map(([tab, hash]) => [hash, tab])
3995
+ );
3996
+ function AccountDashboard(props) {
3997
+ const a = useAppearance(props.appearance);
3998
+ const scoped = useAppearanceProviderValue(props.appearance);
3999
+ const { t } = useCopy(props.localization);
4000
+ const { status } = useAuth();
4001
+ const shown = props.tabs ?? [...ACCOUNT_TABS];
4002
+ const controlled = props.tab !== void 0;
4003
+ const [internal, setInternal] = (0, import_react27.useState)(shown[0] ?? "profile");
4004
+ const idBase = (0, import_react27.useId)();
4005
+ (0, import_react27.useEffect)(() => {
4006
+ if (props.routing !== "hash" || typeof window === "undefined") return;
4007
+ const apply = () => {
4008
+ const next = TAB_FOR_HASH[window.location.hash] ?? shown[0];
4009
+ if (!next || !shown.includes(next)) return;
4010
+ if (controlled) props.onTabChange?.(next);
4011
+ else setInternal(next);
4012
+ };
4013
+ apply();
4014
+ window.addEventListener("hashchange", apply);
4015
+ return () => window.removeEventListener("hashchange", apply);
4016
+ }, [props.routing, controlled, shown.join(",")]);
4017
+ if (status !== "authenticated") return null;
4018
+ const active = controlled ? props.tab : internal;
4019
+ const go = (next) => {
4020
+ props.onTabChange?.(next);
4021
+ if (!controlled) setInternal(next);
4022
+ if (props.routing === "hash" && typeof window !== "undefined") window.location.hash = HASH_FOR2[next];
4023
+ };
4024
+ const shared = { appearance: props.appearance, localization: props.localization };
4025
+ const sidebar = (props.layout ?? "sidebar") === "sidebar";
4026
+ const nav = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4027
+ Tabs,
4028
+ {
4029
+ idBase,
4030
+ orientation: sidebar ? "vertical" : "horizontal",
4031
+ active,
4032
+ onChange: (id) => go(id),
4033
+ tabs: shown.map((id) => ({
4034
+ id,
4035
+ label: t.account.tabs[id],
4036
+ // Icons ride the rail only. In the horizontal strip they cost
4037
+ // horizontal room, which is the scarce axis there.
4038
+ icon: sidebar ? ACCOUNT_ICONS[id] : void 0
4039
+ }))
4040
+ }
4041
+ );
4042
+ const panel = /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
4043
+ "div",
4044
+ {
4045
+ ...a.el("accountDashboard.panel", sidebar ? "throttle-account__content" : "throttle-tabs__panel"),
4046
+ role: "tabpanel",
4047
+ id: `${idBase}-panel-${active}`,
4048
+ "aria-labelledby": `${idBase}-tab-${active}`,
4049
+ tabIndex: 0,
4050
+ children: [
4051
+ active === "profile" ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ProfilePanel, { ...shared }) : null,
4052
+ active === "addresses" ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AddressesPanel, { ...shared, variant: props.panelVariant }) : null,
4053
+ active === "paymentMethods" ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PaymentMethodsPanel, { ...shared, variant: props.panelVariant }) : null,
4054
+ active === "orders" ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(OrdersPanel, { ...shared, variant: props.panelVariant, onViewSubscription: () => go("subscriptions") }) : null,
4055
+ active === "invoices" ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(InvoicesPanel, { ...shared, variant: props.panelVariant }) : null,
4056
+ active === "subscriptions" ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SubscriptionsPanel, { ...shared, variant: props.panelVariant }) : null,
4057
+ active === "security" ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SecurityPanel, { ...shared, variant: props.panelVariant }) : null
4058
+ ]
4059
+ }
4060
+ );
4061
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { ...a.el("accountDashboard.root", "throttle-panel throttle-account-root"), children: [
4062
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(VerifyEmailBanner, { ...shared }),
4063
+ sidebar ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "throttle-account", children: [
4064
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { ...a.el("accountDashboard.sidebar"), children: nav }),
4065
+ panel
4066
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
4067
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { ...a.el("accountDashboard.tabs"), children: nav }),
4068
+ panel
4069
+ ] })
4070
+ ] }) }) });
4071
+ }
4072
+
4073
+ // src/react/components/account/AccountModal.tsx
4074
+ var import_jsx_runtime42 = require("react/jsx-runtime");
4075
+ function AccountModal(props) {
4076
+ const a = useAppearance(props.appearance);
4077
+ const scoped = useAppearanceProviderValue(props.appearance);
4078
+ const { t } = useCopy(props.localization);
4079
+ const titleId = (0, import_react28.useId)();
4080
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
4081
+ Modal,
4082
+ {
4083
+ open: props.open,
4084
+ onClose: props.onClose,
4085
+ labelledBy: titleId,
4086
+ restoreFocusTo: props.restoreFocusTo,
4087
+ overlayKey: "accountModal.overlay",
4088
+ containerKey: "accountModal.container",
4089
+ children: [
4090
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { ...a.el("accountModal.header", "throttle-modal__header"), children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h2", { id: titleId, ...a.el("accountModal.title", "throttle-modal__title"), children: t.account.title }) }),
4091
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { ...a.el("accountModal.body", "throttle-modal__body"), children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(AccountDashboard, { tabs: props.tabs, appearance: props.appearance, localization: props.localization }) })
4092
+ ]
4093
+ }
4094
+ ) });
4095
+ }
4096
+
4097
+ // src/react/components/provider.tsx
4098
+ var import_jsx_runtime43 = require("react/jsx-runtime");
4099
+ var warned = /* @__PURE__ */ new Set();
4100
+ function warnOnce(key, message) {
4101
+ if (process.env.NODE_ENV === "production") return;
4102
+ if (warned.has(key)) return;
4103
+ warned.add(key);
4104
+ console.warn(`[@usethrottle/auth] ${message}`);
4105
+ }
4106
+ var ThrottleUIContext = (0, import_react29.createContext)(null);
4107
+ function useThrottle() {
4108
+ const ctx = (0, import_react29.useContext)(ThrottleUIContext);
4109
+ if (!ctx) {
4110
+ warnOnce(
4111
+ "use-throttle-no-provider",
4112
+ "useThrottle() was called without a <ThrottleProvider> above it in the tree. openSignIn/openSignUp/openAccount will do nothing \u2014 wrap your app in <ThrottleProvider> to use them."
4113
+ );
4114
+ return { openSignIn: () => {
4115
+ }, openSignUp: () => {
4116
+ }, openAccount: () => {
4117
+ }, close: () => {
4118
+ }, isOpen: false };
4119
+ }
4120
+ return {
4121
+ openSignIn: () => ctx.openSignIn(),
4122
+ openSignUp: () => ctx.openSignUp(),
4123
+ openAccount: ctx.openAccount,
4124
+ close: ctx.close,
4125
+ isOpen: ctx.overlay !== null
4126
+ };
4127
+ }
4128
+ function ThrottleProvider(props) {
4129
+ const {
4130
+ publishableKey,
4131
+ baseUrl,
4132
+ storage,
4133
+ appearance,
4134
+ localization,
4135
+ logo,
4136
+ injectStyles = true,
4137
+ claimCartOnSignIn,
4138
+ auth: injected,
4139
+ children,
4140
+ __captureStepUp
4141
+ } = props;
4142
+ useInjectStyles(injectStyles);
4143
+ const [stepUpOpen, setStepUpOpen] = (0, import_react29.useState)(false);
4144
+ const resolveStepUp = (0, import_react29.useRef)(null);
4145
+ const claimRef = (0, import_react29.useRef)(claimCartOnSignIn);
4146
+ claimRef.current = claimCartOnSignIn;
4147
+ const [overlay, setOverlay] = (0, import_react29.useState)(null);
4148
+ const overlayRestoreFocus = (0, import_react29.useRef)(null);
4149
+ const [accountTabs, setAccountTabs] = (0, import_react29.useState)(void 0);
4150
+ const onOverlaySuccess = (0, import_react29.useRef)(null);
4151
+ const openSignIn = (0, import_react29.useCallback)((onSuccess) => {
4152
+ onOverlaySuccess.current = onSuccess ?? null;
4153
+ setOverlay("signIn");
4154
+ }, []);
4155
+ const openSignUp = (0, import_react29.useCallback)((onSuccess) => {
4156
+ onOverlaySuccess.current = onSuccess ?? null;
4157
+ setOverlay("signUp");
4158
+ }, []);
4159
+ const openAccount = (0, import_react29.useCallback)((opts) => {
4160
+ onOverlaySuccess.current = null;
4161
+ overlayRestoreFocus.current = opts?.restoreFocusTo ?? null;
4162
+ setAccountTabs(opts?.tabs);
4163
+ setOverlay("account");
4164
+ }, []);
4165
+ const closeOverlay = (0, import_react29.useCallback)(() => {
4166
+ onOverlaySuccess.current = null;
4167
+ overlayRestoreFocus.current = null;
4168
+ setOverlay(null);
4169
+ }, []);
4170
+ const handleOverlaySuccess = (0, import_react29.useCallback)(() => {
4171
+ const fn = onOverlaySuccess.current;
4172
+ onOverlaySuccess.current = null;
4173
+ setOverlay(null);
4174
+ fn?.();
4175
+ }, []);
4176
+ const ui = (0, import_react29.useMemo)(
4177
+ () => ({ overlay, openSignIn, openSignUp, openAccount, close: closeOverlay }),
4178
+ [overlay, openSignIn, openSignUp, openAccount, closeOverlay]
4179
+ );
4180
+ const askForStepUp = (0, import_react29.useCallback)(
4181
+ () => new Promise((resolve) => {
4182
+ resolveStepUp.current = resolve;
4183
+ setStepUpOpen(true);
4184
+ }),
4185
+ []
4186
+ );
4187
+ const auth = (0, import_react29.useMemo)(() => {
4188
+ __captureStepUp?.(askForStepUp);
4189
+ const client = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired: askForStepUp });
4190
+ const account = createAccount(client);
4191
+ const claim = async () => {
4192
+ const cartId = claimRef.current?.();
4193
+ if (!cartId) return;
4194
+ try {
4195
+ await account.claimCart(cartId);
4196
+ } catch (err) {
4197
+ warnOnce("claim-cart", `claimCartOnSignIn failed: ${err?.message ?? err}`);
4198
+ }
4199
+ };
4200
+ return {
4201
+ ...client,
4202
+ async signIn(email, password) {
4203
+ await client.signIn(email, password);
4204
+ await claim();
4205
+ },
4206
+ async register(email, password) {
4207
+ const result = await client.register(email, password);
4208
+ if (result.status === "active") await claim();
4209
+ return result;
4210
+ }
4211
+ };
4212
+ }, [injected, publishableKey, baseUrl]);
4213
+ const finishStepUp = (password) => {
4214
+ setStepUpOpen(false);
4215
+ const resolve = resolveStepUp.current;
4216
+ resolveStepUp.current = null;
4217
+ resolve?.(password);
4218
+ };
4219
+ const authOverlayOpen = overlay === "signIn" || overlay === "signUp";
4220
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(AppearanceProvider, { value: appearance ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(LocalizationProvider, { value: localization ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(BrandLogoProvider, { value: logo ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ThrottleAuthProvider, { auth, publishableKey, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(ThrottleUIContext.Provider, { value: ui, children: [
4221
+ children,
4222
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(StepUpDialog, { open: stepUpOpen, onResolve: finishStepUp }),
4223
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4224
+ AuthModal,
4225
+ {
4226
+ open: authOverlayOpen,
4227
+ onClose: closeOverlay,
4228
+ initialView: overlay === "signUp" ? "signUp" : "signIn",
4229
+ afterSignIn: handleOverlaySuccess,
4230
+ afterSignUp: handleOverlaySuccess
4231
+ },
4232
+ overlay === "signUp" ? "signUp" : "signIn"
4233
+ ),
4234
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(AccountModal, { open: overlay === "account", onClose: closeOverlay, tabs: accountTabs, restoreFocusTo: overlayRestoreFocus.current })
4235
+ ] }) }) }) }) });
4236
+ }
4237
+
4238
+ // src/react/components/auth/SignInButton.tsx
4239
+ var import_react30 = require("react");
4240
+ var import_jsx_runtime44 = require("react/jsx-runtime");
4241
+ function SignInButton(props) {
4242
+ const a = useAppearance(props.appearance);
4243
+ const scoped = useAppearanceProviderValue(props.appearance);
4244
+ const { t } = useCopy(props.localization);
4245
+ const ui = (0, import_react30.useContext)(ThrottleUIContext);
4246
+ const [localOpen, setLocalOpen] = (0, import_react30.useState)(false);
4247
+ const useShared = ui !== null && props.initialView === void 0;
4248
+ const label = props.children ?? t.signIn.submitButton;
4249
+ if (props.mode === "redirect") {
4250
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("a", { ...a.el("signInButton.root", "throttle-button throttle-button--primary"), href: props.url ?? "#", children: label }) }) });
4251
+ }
4252
+ const openModal = () => {
4253
+ if (useShared) ui.openSignIn(props.afterSignIn);
4254
+ else setLocalOpen(true);
4255
+ };
4256
+ const closeModal = () => {
4257
+ if (useShared) ui.close();
4258
+ else setLocalOpen(false);
4259
+ };
4260
+ const trigger = props.asChild && (0, import_react30.isValidElement)(props.children) ? (0, import_react30.cloneElement)(props.children, { onClick: openModal }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Button, { elementKey: "signInButton.root", onClick: openModal, children: label });
4261
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(AppearanceProvider, { value: scoped, children: [
4262
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { ...a.rootProps, children: trigger }),
4263
+ !useShared ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
4264
+ AuthModal,
4265
+ {
4266
+ open: localOpen,
4267
+ onClose: closeModal,
4268
+ initialView: props.initialView,
4269
+ appearance: props.appearance,
4270
+ localization: props.localization,
4271
+ afterSignIn: () => {
4272
+ closeModal();
4273
+ props.afterSignIn?.();
4274
+ }
4275
+ }
4276
+ ) : null
4277
+ ] });
4278
+ }
4279
+
4280
+ // src/react/components/auth/SignUpButton.tsx
4281
+ var import_react31 = require("react");
4282
+ var import_jsx_runtime45 = require("react/jsx-runtime");
4283
+ function SignUpButton(props) {
4284
+ const a = useAppearance(props.appearance);
4285
+ const scoped = useAppearanceProviderValue(props.appearance);
4286
+ const { t } = useCopy(props.localization);
4287
+ const ui = (0, import_react31.useContext)(ThrottleUIContext);
4288
+ const [localOpen, setLocalOpen] = (0, import_react31.useState)(false);
4289
+ const useShared = ui !== null && props.initialView === void 0;
4290
+ const label = props.children ?? t.signUp.submitButton;
4291
+ if (props.mode === "redirect") {
4292
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("a", { ...a.el("signUpButton.root", "throttle-button throttle-button--primary"), href: props.url ?? "#", children: label }) }) });
4293
+ }
4294
+ const openModal = () => {
4295
+ if (useShared) ui.openSignUp(props.afterSignUp);
4296
+ else setLocalOpen(true);
4297
+ };
4298
+ const closeModal = () => {
4299
+ if (useShared) ui.close();
4300
+ else setLocalOpen(false);
4301
+ };
4302
+ const trigger = props.asChild && (0, import_react31.isValidElement)(props.children) ? (0, import_react31.cloneElement)(props.children, { onClick: openModal }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { elementKey: "signUpButton.root", onClick: openModal, children: label });
4303
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(AppearanceProvider, { value: scoped, children: [
4304
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ...a.rootProps, children: trigger }),
4305
+ !useShared ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4306
+ AuthModal,
4307
+ {
4308
+ open: localOpen,
4309
+ onClose: closeModal,
4310
+ initialView: props.initialView ?? "signUp",
4311
+ appearance: props.appearance,
4312
+ localization: props.localization,
4313
+ afterSignUp: () => {
4314
+ closeModal();
4315
+ props.afterSignUp?.();
4316
+ }
4317
+ }
4318
+ ) : null
4319
+ ] });
4320
+ }
4321
+
4322
+ // src/react/components/auth/SignOutButton.tsx
4323
+ var import_jsx_runtime46 = require("react/jsx-runtime");
4324
+ function SignOutButton(props) {
4325
+ const a = useAppearance();
4326
+ const { auth } = useAuth();
4327
+ const { t } = useCopy(props.localization);
4328
+ const { submitting, run } = useAction();
4329
+ const label = props.children ?? (props.everywhere ? t.userButton.signOutAll : t.userButton.signOut);
4330
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
4331
+ Button,
4332
+ {
4333
+ variant: "secondary",
4334
+ loading: submitting,
4335
+ elementKey: "signOutButton.root",
4336
+ onClick: () => void run(async () => {
4337
+ if (props.everywhere) await auth.signOutEverywhere();
4338
+ else await auth.signOut();
4339
+ props.onSignedOut?.();
4340
+ }),
4341
+ children: label
4342
+ }
4343
+ ) });
4344
+ }
4345
+
4346
+ // src/react/components/auth/SignInPage.tsx
4347
+ var import_jsx_runtime47 = require("react/jsx-runtime");
4348
+ function SignInPage(props) {
4349
+ const a = useAppearance(props.appearance);
4350
+ const scoped = useAppearanceProviderValue(props.appearance);
4351
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { ...a.el("signInPage.root", "throttle-page"), children: [
4352
+ props.logo ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { ...a.el("signInPage.logo", "throttle-page__logo"), children: props.logo }) : null,
4353
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
4354
+ SignIn,
4355
+ {
4356
+ onSuccess: props.onSuccess,
4357
+ onForgotPassword: props.onForgotPassword,
4358
+ onSignUp: props.onSignUp,
4359
+ appearance: props.appearance,
4360
+ localization: props.localization
4361
+ }
4362
+ ),
4363
+ props.footer ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { ...a.el("signInPage.footer", "throttle-page__footer"), children: props.footer }) : null
4364
+ ] }) }) });
4365
+ }
4366
+
4367
+ // src/react/components/auth/SignUpPage.tsx
4368
+ var import_jsx_runtime48 = require("react/jsx-runtime");
4369
+ function SignUpPage(props) {
4370
+ const a = useAppearance(props.appearance);
4371
+ const scoped = useAppearanceProviderValue(props.appearance);
4372
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { ...a.el("signUpPage.root", "throttle-page"), children: [
4373
+ props.logo ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { ...a.el("signUpPage.logo", "throttle-page__logo"), children: props.logo }) : null,
4374
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
4375
+ SignUp,
4376
+ {
4377
+ onSuccess: props.onSuccess,
4378
+ onSignIn: props.onSignIn,
4379
+ appearance: props.appearance,
4380
+ localization: props.localization
4381
+ }
4382
+ ),
4383
+ props.footer ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { ...a.el("signUpPage.footer", "throttle-page__footer"), children: props.footer }) : null
4384
+ ] }) }) });
4385
+ }
4386
+
4387
+ // src/react/components/guards.tsx
4388
+ var import_jsx_runtime49 = require("react/jsx-runtime");
4389
+ function SignedIn(props) {
4390
+ const { status } = useAuth();
4391
+ return status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_jsx_runtime49.Fragment, { children: props.children }) : null;
4392
+ }
4393
+ function SignedOut(props) {
4394
+ const { status } = useAuth();
4395
+ return status === "unauthenticated" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_jsx_runtime49.Fragment, { children: props.children }) : null;
4396
+ }
4397
+ function Protect(props) {
4398
+ const { status, emailVerified } = useAuth();
4399
+ if (status === "loading") return null;
4400
+ if (status !== "authenticated") return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_jsx_runtime49.Fragment, { children: props.fallback ?? null });
4401
+ if (props.requireVerified && !emailVerified) return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_jsx_runtime49.Fragment, { children: props.fallback ?? null });
4402
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_jsx_runtime49.Fragment, { children: props.children });
4403
+ }
4404
+
4405
+ // src/react/components/UserButton.tsx
4406
+ var import_react32 = require("react");
4407
+ var import_jsx_runtime50 = require("react/jsx-runtime");
4408
+ function UserButtonRoot(props) {
4409
+ const a = useAppearance(props.appearance);
4410
+ const scoped = useAppearanceProviderValue(props.appearance);
4411
+ const { t } = useCopy(props.localization);
4412
+ const { auth, status, customer, emailVerified } = useAuth();
4413
+ const ui = (0, import_react32.useContext)(ThrottleUIContext);
4414
+ const [localOpen, setLocalOpen] = (0, import_react32.useState)(false);
4415
+ const accountOpen = ui ? false : localOpen;
4416
+ const titleId = (0, import_react32.useId)();
4417
+ const rootRef = (0, import_react32.useRef)(null);
4418
+ const openerRef = (0, import_react32.useRef)(null);
4419
+ const openAccount = () => {
4420
+ const opener = rootRef.current?.querySelector('button[aria-haspopup="menu"]') ?? null;
4421
+ openerRef.current = opener;
4422
+ if (ui) ui.openAccount({ tabs: props.tabs, restoreFocusTo: opener });
4423
+ else setLocalOpen(true);
4424
+ };
4425
+ if (status !== "authenticated" || !customer) return null;
4426
+ const name = [customer.firstName, customer.lastName].filter(Boolean).join(" ");
4427
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(AppearanceProvider, { value: scoped, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { ...a.rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { ref: rootRef, ...a.el("userButton.root"), children: [
4428
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
4429
+ Dropdown,
4430
+ {
4431
+ label: t.userButton.openMenu,
4432
+ triggerKey: "userButton.trigger",
4433
+ menuKey: "userButton.menu",
4434
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Avatar, { name, email: customer.email }),
4435
+ children: [
4436
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { ...a.el("userButton.identity", "throttle-dropdown__item"), children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { children: [
4437
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { ...a.el("userButton.email"), children: customer.email }),
4438
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("br", {}),
4439
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Badge, { tone: emailVerified ? "success" : "warning", elementKey: "userButton.badge", children: emailVerified ? t.userButton.verified : t.userButton.unverified })
4440
+ ] }) }),
4441
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(DropdownSeparator, {}),
4442
+ props.mode === "navigate" ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(DropdownItem, { itemKey: "userButton.manageButton", href: props.accountUrl ?? "#", children: t.userButton.manage }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(DropdownItem, { itemKey: "userButton.manageButton", onClick: openAccount, children: t.userButton.manage }),
4443
+ props.children,
4444
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(DropdownSeparator, {}),
4445
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
4446
+ DropdownItem,
4447
+ {
4448
+ itemKey: "userButton.signOutButton",
4449
+ onClick: () => void auth.signOut().then(() => props.afterSignOut?.()),
4450
+ children: t.userButton.signOut
4451
+ }
4452
+ ),
4453
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
4454
+ DropdownItem,
4455
+ {
4456
+ itemKey: "userButton.signOutAllButton",
4457
+ onClick: () => void auth.signOutEverywhere().then(() => props.afterSignOut?.()),
4458
+ children: t.userButton.signOutAll
4459
+ }
4460
+ )
4461
+ ]
4462
+ }
4463
+ ),
4464
+ props.mode !== "navigate" && !ui ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
4465
+ Modal,
4466
+ {
4467
+ open: accountOpen,
4468
+ onClose: () => setLocalOpen(false),
4469
+ labelledBy: titleId,
4470
+ restoreFocusTo: openerRef.current,
4471
+ children: [
4472
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("h2", { id: titleId, style: { padding: "16px 16px 0", margin: 0 }, children: t.account.title }),
4473
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { style: { padding: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
4474
+ AccountDashboard,
4475
+ {
4476
+ tabs: props.tabs,
4477
+ appearance: props.appearance,
4478
+ localization: props.localization
4479
+ }
4480
+ ) })
4481
+ ]
4482
+ }
4483
+ ) : null
4484
+ ] }) }) });
4485
+ }
4486
+ function UserButtonLink(props) {
4487
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(DropdownItem, { itemKey: "userButton.item", href: props.href, children: [
4488
+ props.icon,
4489
+ props.label
4490
+ ] });
4491
+ }
4492
+ function UserButtonAction(props) {
4493
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(DropdownItem, { itemKey: "userButton.item", onClick: props.onClick, children: [
4494
+ props.icon,
4495
+ props.label
4496
+ ] });
4497
+ }
4498
+ var UserButton = Object.assign(UserButtonRoot, {
4499
+ Link: UserButtonLink,
4500
+ Action: UserButtonAction
4501
+ });
4502
+ // Annotate the CommonJS export names for ESM import in node:
4503
+ 0 && (module.exports = {
4504
+ ACCOUNT_TABS,
4505
+ AccountDashboard,
4506
+ AccountModal,
4507
+ AddressesPanel,
4508
+ AuthFlow,
4509
+ AuthModal,
4510
+ ForgotPassword,
4511
+ InvoiceDetail,
4512
+ InvoicesPanel,
4513
+ OrderDetail,
4514
+ OrdersPanel,
4515
+ PaymentMethodsPanel,
4516
+ ProfilePanel,
4517
+ Protect,
4518
+ ResetPassword,
4519
+ SecurityPanel,
4520
+ SignIn,
4521
+ SignInButton,
4522
+ SignInPage,
4523
+ SignOutButton,
4524
+ SignUp,
4525
+ SignUpButton,
4526
+ SignUpPage,
4527
+ SignedIn,
4528
+ SignedOut,
4529
+ SubscriptionsPanel,
4530
+ THROTTLE_ELEMENT_KEYS,
4531
+ THROTTLE_STYLES,
4532
+ THROTTLE_STYLE_ID,
4533
+ ThrottleAuthError,
4534
+ ThrottleAuthProvider,
4535
+ ThrottleProvider,
4536
+ UserButton,
4537
+ VerifyEmail,
4538
+ VerifyEmailBanner,
4539
+ createAccount,
4540
+ createThrottleAuth,
4541
+ enUS,
4542
+ formatDate,
4543
+ formatMoney,
4544
+ localStorageAdapter,
4545
+ memoryStorage,
4546
+ useAddresses,
4547
+ useAuth,
4548
+ useAuthContext,
4549
+ useCustomer,
4550
+ useInvoice,
4551
+ useInvoices,
4552
+ useOrder,
4553
+ useOrders,
4554
+ useSavedCards,
4555
+ useSubscriptions,
4556
+ useThrottle
4557
+ });
4558
+ //# sourceMappingURL=index.cjs.map