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