@kerne/react 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/dist/chunk-3R66S7BY.cjs +3312 -0
- package/dist/chunk-ZVL47U4B.js +3312 -0
- package/dist/{i18n-QilNgwqe.d.cts → fr-E5mmylSH.d.cts} +135 -3
- package/dist/{i18n-QilNgwqe.d.ts → fr-E5mmylSH.d.ts} +135 -3
- package/dist/index.cjs +51 -27
- package/dist/index.d.cts +61 -41
- package/dist/index.d.ts +61 -41
- package/dist/index.js +44 -20
- package/dist/ui/index.cjs +984 -1906
- package/dist/ui/index.d.cts +42 -96
- package/dist/ui/index.d.ts +42 -96
- package/dist/ui/index.js +1091 -2013
- package/package.json +11 -5
- package/dist/chunk-IZMRK3WK.js +0 -1285
- package/dist/chunk-TYNZ2WU7.cjs +0 -1285
package/dist/ui/index.js
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
|
+
Button,
|
|
4
|
+
Divider,
|
|
5
|
+
Field,
|
|
6
|
+
FormSkeleton,
|
|
7
|
+
KerneBranding,
|
|
8
|
+
Masthead,
|
|
9
|
+
MethodSwitch,
|
|
10
|
+
OAuthButtons,
|
|
11
|
+
Panel,
|
|
12
|
+
PasswordStrength,
|
|
13
|
+
Shell,
|
|
14
|
+
Status,
|
|
15
|
+
Subsection,
|
|
16
|
+
TextLink,
|
|
17
|
+
appearanceToStyle,
|
|
3
18
|
defaultLocalization,
|
|
19
|
+
defaultTokens,
|
|
20
|
+
ensureStylesInjected,
|
|
4
21
|
fill,
|
|
5
22
|
fillNode,
|
|
23
|
+
fr,
|
|
24
|
+
meetsPolicy,
|
|
25
|
+
mergeLocalization,
|
|
26
|
+
resolvePolicy,
|
|
27
|
+
resolveTheme,
|
|
6
28
|
useAccess,
|
|
7
29
|
useAuth,
|
|
8
30
|
useAuthConfig,
|
|
@@ -17,1262 +39,32 @@ import {
|
|
|
17
39
|
useSubscription,
|
|
18
40
|
useUser,
|
|
19
41
|
useWaitlist
|
|
20
|
-
} from "../chunk-
|
|
42
|
+
} from "../chunk-ZVL47U4B.js";
|
|
21
43
|
|
|
22
44
|
// src/ui/AuthFlow.tsx
|
|
23
|
-
import { useState as
|
|
45
|
+
import { useState as useState9 } from "react";
|
|
24
46
|
|
|
25
47
|
// src/ui/useUrlToken.ts
|
|
26
48
|
import { useState, useEffect } from "react";
|
|
27
49
|
function useUrlToken(param = "token", override) {
|
|
28
|
-
const [token, setToken] = useState(override ?? null);
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
if (override) {
|
|
31
|
-
setToken(override);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (typeof window === "undefined") return;
|
|
35
|
-
setToken(new URLSearchParams(window.location.search).get(param) ?? "");
|
|
36
|
-
}, [param, override]);
|
|
37
|
-
return token;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// src/ui/LoginForm.tsx
|
|
41
|
-
import { useState as useState3 } from "react";
|
|
42
|
-
|
|
43
|
-
// src/ui/primitives.tsx
|
|
44
|
-
import { useId, useState as useState2, useContext, createContext } from "react";
|
|
45
|
-
|
|
46
|
-
// src/ui/styles.ts
|
|
47
|
-
var STYLE_ELEMENT_ID = "kerne-ui-styles";
|
|
48
|
-
var DARK_TOKENS = `
|
|
49
|
-
--kerne-fg: #f0f1f3;
|
|
50
|
-
--kerne-fg-muted: #9ba2ad;
|
|
51
|
-
--kerne-fg-subtle: #838b96;
|
|
52
|
-
--kerne-border: #2a2e36;
|
|
53
|
-
--kerne-border-strong: #363b45;
|
|
54
|
-
--kerne-panel: #1b1f26;
|
|
55
|
-
--kerne-surface: #14171c;
|
|
56
|
-
--kerne-surface-raised: #2a2f38;
|
|
57
|
-
--kerne-track: #313743;
|
|
58
|
-
--kerne-primary: #f0f1f3;
|
|
59
|
-
--kerne-primary-fg: #16181d;
|
|
60
|
-
--kerne-primary-hover: #d8dade;
|
|
61
|
-
--kerne-accent: #91a7ff;
|
|
62
|
-
--kerne-danger: #f0837a;
|
|
63
|
-
--kerne-success: #5cbd8d;
|
|
64
|
-
--kerne-warning: #d9ab5e;
|
|
65
|
-
--kerne-shadow: 0 4px 6px -2px rgba(0,0,0,.4), 0 12px 24px -8px rgba(0,0,0,.6);
|
|
66
|
-
`;
|
|
67
|
-
var CSS = `
|
|
68
|
-
.kerne-root {
|
|
69
|
-
--kerne-fg: #16171a;
|
|
70
|
-
--kerne-fg-muted: #6b7280;
|
|
71
|
-
/* Meets 4.5:1 on white - this carries placeholders and the legal footnote,
|
|
72
|
-
which used to sit around 2.5:1. */
|
|
73
|
-
--kerne-fg-subtle: #6f7681;
|
|
74
|
-
--kerne-border: #e3e5e9;
|
|
75
|
-
--kerne-border-strong: #d6dade;
|
|
76
|
-
--kerne-field-bg: transparent;
|
|
77
|
-
--kerne-panel: #f4f5f7;
|
|
78
|
-
/* The ground the component sits on. Transparent so an embedded instance
|
|
79
|
-
inherits the host's page; set it (appearance.background) when the component
|
|
80
|
-
owns the viewport, so text and background can never resolve independently. */
|
|
81
|
-
--kerne-bg: transparent;
|
|
82
|
-
/* An opaque surface for the things that must not show through: the card
|
|
83
|
-
variant, the dropdown floating over host content, a pressed segment. */
|
|
84
|
-
--kerne-surface: #ffffff;
|
|
85
|
-
--kerne-surface-raised: #ffffff;
|
|
86
|
-
/* Unfilled part of a meter, and the ground of a disabled button. Distinct
|
|
87
|
-
from --kerne-panel, which is tuned to sit on the page: on a card the two
|
|
88
|
-
are nearly the same colour, so a meter drawn in panel had invisible empty
|
|
89
|
-
segments and read as having fewer steps than it has. */
|
|
90
|
-
--kerne-track: #e3e5e9;
|
|
91
|
-
--kerne-primary: #16171a;
|
|
92
|
-
--kerne-primary-fg: #ffffff;
|
|
93
|
-
--kerne-primary-hover: #2c2e33;
|
|
94
|
-
--kerne-accent: #3b5bdb;
|
|
95
|
-
--kerne-danger: #c0392f;
|
|
96
|
-
--kerne-success: #1f7a4d;
|
|
97
|
-
--kerne-warning: #9a6b12;
|
|
98
|
-
--kerne-radius: 8px;
|
|
99
|
-
--kerne-control-height: 44px;
|
|
100
|
-
--kerne-shadow: 0 4px 6px -2px rgba(0,0,0,.06), 0 12px 24px -8px rgba(0,0,0,.16);
|
|
101
|
-
--kerne-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
102
|
-
|
|
103
|
-
box-sizing: border-box;
|
|
104
|
-
font-family: var(--kerne-font);
|
|
105
|
-
color: var(--kerne-fg);
|
|
106
|
-
background: var(--kerne-bg);
|
|
107
|
-
-webkit-font-smoothing: antialiased;
|
|
108
|
-
display: flex;
|
|
109
|
-
justify-content: center;
|
|
110
|
-
width: 100%;
|
|
111
|
-
}
|
|
112
|
-
.kerne-root *, .kerne-root *::before, .kerne-root *::after { box-sizing: inherit; }
|
|
113
|
-
|
|
114
|
-
.kerne-root[data-kerne-theme="dark"] {${DARK_TOKENS}}
|
|
115
|
-
/* Only an explicit opt-in follows the visitor's OS. Defaulting to it meant a
|
|
116
|
-
light-only host got a dark form on any machine set to dark, without ever
|
|
117
|
-
asking for one - and with no way for the host to know it had happened. */
|
|
118
|
-
@media (prefers-color-scheme: dark) {
|
|
119
|
-
.kerne-root[data-kerne-theme="system"] {${DARK_TOKENS}}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.kerne-shell {
|
|
123
|
-
width: 100%;
|
|
124
|
-
max-width: 384px;
|
|
125
|
-
display: flex;
|
|
126
|
-
flex-direction: column;
|
|
127
|
-
}
|
|
128
|
-
/* Auth screens are a single column of fields; an account manager is a
|
|
129
|
-
navigation plus a content pane and suffocates at 384px. */
|
|
130
|
-
.kerne-shell[data-kerne-size="wide"] { max-width: 800px; }
|
|
131
|
-
|
|
132
|
-
.kerne-shell[data-kerne-surface="card"] {
|
|
133
|
-
background: var(--kerne-surface);
|
|
134
|
-
border: 1px solid var(--kerne-border);
|
|
135
|
-
border-radius: calc(var(--kerne-radius) + 6px);
|
|
136
|
-
box-shadow: var(--kerne-shadow);
|
|
137
|
-
padding: 32px 28px;
|
|
138
|
-
/* The card is the ground from here down, so nested surfaces stop inheriting
|
|
139
|
-
the page behind it. */
|
|
140
|
-
--kerne-bg: var(--kerne-surface);
|
|
141
|
-
}
|
|
142
|
-
@media (max-width: 480px) {
|
|
143
|
-
/* Full-bleed on a phone: a card with 16px of page showing on each side wastes
|
|
144
|
-
the only axis a login form has. */
|
|
145
|
-
.kerne-shell[data-kerne-surface="card"] {
|
|
146
|
-
border: none;
|
|
147
|
-
box-shadow: none;
|
|
148
|
-
padding: 8px 0;
|
|
149
|
-
background: transparent;
|
|
150
|
-
/* The card no longer paints, so the ground is the page again. */
|
|
151
|
-
--kerne-bg: transparent;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/* ---------- Tabbed account manager ---------- */
|
|
156
|
-
.kerne-profile { display: grid; grid-template-columns: 188px minmax(0, 1fr); gap: 40px; align-items: start; }
|
|
157
|
-
@media (max-width: 640px) { .kerne-profile { grid-template-columns: 1fr; gap: 20px; } }
|
|
158
|
-
|
|
159
|
-
.kerne-nav { display: flex; flex-direction: column; gap: 2px; position: sticky; top: 24px; }
|
|
160
|
-
@media (max-width: 640px) {
|
|
161
|
-
.kerne-nav {
|
|
162
|
-
position: static;
|
|
163
|
-
flex-direction: row;
|
|
164
|
-
overflow-x: auto;
|
|
165
|
-
gap: 4px;
|
|
166
|
-
border-bottom: 1px solid var(--kerne-border);
|
|
167
|
-
padding-bottom: 8px;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
.kerne-nav-item {
|
|
171
|
-
font: inherit; font-size: 14px; text-align: left;
|
|
172
|
-
background: none; border: none; cursor: pointer;
|
|
173
|
-
padding: 8px 12px; border-radius: 6px;
|
|
174
|
-
color: var(--kerne-fg-muted); white-space: nowrap;
|
|
175
|
-
transition: background-color .12s ease, color .12s ease;
|
|
176
|
-
}
|
|
177
|
-
.kerne-nav-item:hover { color: var(--kerne-fg); }
|
|
178
|
-
.kerne-nav-item[aria-current="page"] { background: var(--kerne-panel); color: var(--kerne-fg); font-weight: 500; }
|
|
179
|
-
.kerne-nav-item:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; }
|
|
180
|
-
|
|
181
|
-
.kerne-pane { display: flex; flex-direction: column; min-width: 0; }
|
|
182
|
-
.kerne-pane-title { font-size: 20px; font-weight: 600; letter-spacing: -.01em; margin: 0 0 4px; }
|
|
183
|
-
.kerne-pane-desc { font-size: 14px; color: var(--kerne-fg-muted); margin: 0 0 8px; }
|
|
184
|
-
|
|
185
|
-
.kerne-subsection { padding: 24px 0; display: flex; flex-direction: column; gap: 16px; }
|
|
186
|
-
.kerne-subsection:first-of-type { padding-top: 16px; }
|
|
187
|
-
.kerne-subsection + .kerne-subsection { border-top: 1px solid var(--kerne-border); }
|
|
188
|
-
.kerne-subsection-head { display: flex; flex-direction: column; gap: 3px; }
|
|
189
|
-
.kerne-subsection-title { font-size: 15px; font-weight: 600; margin: 0; }
|
|
190
|
-
.kerne-subsection-desc { font-size: 13px; color: var(--kerne-fg-muted); margin: 0; }
|
|
191
|
-
/* Buttons inside a settings pane are actions, not page-wide calls to action. */
|
|
192
|
-
.kerne-subsection .kerne-button { width: auto; align-self: flex-start; }
|
|
193
|
-
|
|
194
|
-
/* ---------- Plan options ---------- */
|
|
195
|
-
.kerne-plan-list { display: flex; flex-direction: column; gap: 10px; }
|
|
196
|
-
.kerne-plan-option {
|
|
197
|
-
display: flex; justify-content: space-between; align-items: center; gap: 16px;
|
|
198
|
-
padding: 14px 16px; border: 1px solid var(--kerne-border); border-radius: var(--kerne-radius);
|
|
199
|
-
}
|
|
200
|
-
.kerne-plan-option[data-current="true"] { border-color: var(--kerne-fg); }
|
|
201
|
-
.kerne-plan-option-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
202
|
-
.kerne-plan-option-name { font-size: 14px; font-weight: 500; display: flex; align-items: center; gap: 8px; }
|
|
203
|
-
.kerne-plan-option-price { font-size: 13px; color: var(--kerne-fg-muted); font-variant-numeric: tabular-nums; }
|
|
204
|
-
.kerne-plan-option .kerne-button { width: auto; flex-shrink: 0; height: 36px; font-size: 13px; }
|
|
205
|
-
|
|
206
|
-
/* Segmented control for billing interval. */
|
|
207
|
-
.kerne-segmented { display: inline-flex; padding: 3px; gap: 2px; background: var(--kerne-panel); border-radius: var(--kerne-radius); align-self: flex-start; }
|
|
208
|
-
.kerne-segment {
|
|
209
|
-
font: inherit; font-size: 13px; border: none; background: none; cursor: pointer;
|
|
210
|
-
padding: 5px 12px; border-radius: calc(var(--kerne-radius) - 3px); color: var(--kerne-fg-muted);
|
|
211
|
-
}
|
|
212
|
-
.kerne-segment[aria-pressed="true"] { background: var(--kerne-surface-raised); color: var(--kerne-fg); font-weight: 500; }
|
|
213
|
-
.kerne-segment:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; }
|
|
214
|
-
|
|
215
|
-
/* ---------- Masthead ---------- */
|
|
216
|
-
.kerne-masthead {
|
|
217
|
-
display: flex;
|
|
218
|
-
flex-direction: column;
|
|
219
|
-
align-items: center;
|
|
220
|
-
gap: 16px;
|
|
221
|
-
text-align: center;
|
|
222
|
-
margin-bottom: 32px;
|
|
223
|
-
}
|
|
224
|
-
.kerne-logo { display: flex; align-items: center; justify-content: center; }
|
|
225
|
-
.kerne-heading { display: flex; flex-direction: column; gap: 6px; }
|
|
226
|
-
.kerne-title {
|
|
227
|
-
margin: 0;
|
|
228
|
-
font-size: 24px;
|
|
229
|
-
font-weight: 600;
|
|
230
|
-
letter-spacing: -.02em;
|
|
231
|
-
line-height: 1.2;
|
|
232
|
-
text-wrap: balance;
|
|
233
|
-
}
|
|
234
|
-
.kerne-subtitle { margin: 0; font-size: 14px; color: var(--kerne-fg-muted); line-height: 1.5; }
|
|
235
|
-
|
|
236
|
-
/* Moving between steps (sign-in -> sign-up -> forgot password) used to be an
|
|
237
|
-
instant swap: the content and the height both jumped at once, which reads
|
|
238
|
-
as a page reload rather than one flow. */
|
|
239
|
-
@keyframes kerne-step-in {
|
|
240
|
-
from { opacity: 0; transform: translateY(4px); }
|
|
241
|
-
to { opacity: 1; transform: none; }
|
|
242
|
-
}
|
|
243
|
-
.kerne-step { animation: kerne-step-in .18s ease-out; }
|
|
244
|
-
@media (prefers-reduced-motion: reduce) {
|
|
245
|
-
.kerne-step { animation: none; }
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/* ---------- Stack ---------- */
|
|
249
|
-
.kerne-stack { display: flex; flex-direction: column; gap: 16px; }
|
|
250
|
-
.kerne-inline-actions { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
251
|
-
.kerne-form { display: flex; flex-direction: column; gap: 12px; }
|
|
252
|
-
.kerne-field { display: flex; flex-direction: column; gap: 6px; }
|
|
253
|
-
.kerne-label { font-size: 14px; font-weight: 500; color: var(--kerne-fg); }
|
|
254
|
-
.kerne-label-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
|
|
255
|
-
|
|
256
|
-
/* Positioning context for the reveal toggle, which sits inside the input box. */
|
|
257
|
-
.kerne-control { position: relative; display: flex; }
|
|
258
|
-
|
|
259
|
-
.kerne-input {
|
|
260
|
-
font: inherit;
|
|
261
|
-
font-size: 14px;
|
|
262
|
-
height: var(--kerne-control-height);
|
|
263
|
-
color: var(--kerne-fg);
|
|
264
|
-
background: var(--kerne-field-bg);
|
|
265
|
-
border: 1px solid var(--kerne-border-strong);
|
|
266
|
-
border-radius: var(--kerne-radius);
|
|
267
|
-
padding: 0 13px;
|
|
268
|
-
width: 100%;
|
|
269
|
-
transition: border-color .12s ease, box-shadow .12s ease;
|
|
270
|
-
}
|
|
271
|
-
/* Room for the toggle, so a long password never runs underneath it. */
|
|
272
|
-
.kerne-input[data-kerne-reveal="true"] { padding-right: 42px; }
|
|
273
|
-
|
|
274
|
-
.kerne-reveal {
|
|
275
|
-
position: absolute;
|
|
276
|
-
top: 0; right: 0; bottom: 0;
|
|
277
|
-
width: 40px;
|
|
278
|
-
display: flex; align-items: center; justify-content: center;
|
|
279
|
-
background: none; border: none; padding: 0; cursor: pointer;
|
|
280
|
-
color: var(--kerne-fg-subtle);
|
|
281
|
-
border-radius: var(--kerne-radius);
|
|
282
|
-
transition: color .12s ease;
|
|
283
|
-
}
|
|
284
|
-
.kerne-reveal:hover:not(:disabled) { color: var(--kerne-fg); }
|
|
285
|
-
.kerne-reveal:disabled { cursor: not-allowed; opacity: .5; }
|
|
286
|
-
.kerne-reveal:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: -2px; }
|
|
287
|
-
|
|
288
|
-
.kerne-field-note { font-size: 12.5px; line-height: 1.4; color: var(--kerne-fg-muted); margin: 0; }
|
|
289
|
-
.kerne-field-error { font-size: 12.5px; line-height: 1.4; color: var(--kerne-danger); margin: 0; }
|
|
290
|
-
/* Caps Lock is a "you are about to get this wrong" signal, not background
|
|
291
|
-
guidance - in muted grey it sat at the same weight as a hint and got read
|
|
292
|
-
as one. */
|
|
293
|
-
.kerne-field-note[data-kerne-warn="true"] { color: var(--kerne-warning); }
|
|
294
|
-
|
|
295
|
-
/* ---------- Password rules ---------- */
|
|
296
|
-
/* Unmet stays at hint weight rather than turning red: nothing is wrong yet,
|
|
297
|
-
the user is still typing. Only the tick earns a color. */
|
|
298
|
-
.kerne-rules { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
|
|
299
|
-
.kerne-rule {
|
|
300
|
-
display: flex; align-items: center; gap: 6px;
|
|
301
|
-
font-size: 12.5px; line-height: 1.4; color: var(--kerne-fg-muted);
|
|
302
|
-
}
|
|
303
|
-
.kerne-rule[data-met] { color: var(--kerne-success); }
|
|
304
|
-
|
|
305
|
-
/* ---------- Password strength ---------- */
|
|
306
|
-
.kerne-strength { display: flex; flex-direction: column; gap: 5px; }
|
|
307
|
-
.kerne-strength-track { display: flex; gap: 4px; }
|
|
308
|
-
.kerne-strength-step {
|
|
309
|
-
flex: 1; height: 3px; border-radius: 999px;
|
|
310
|
-
background: var(--kerne-track);
|
|
311
|
-
transition: background-color .18s ease;
|
|
312
|
-
}
|
|
313
|
-
.kerne-strength-label { font-size: 12px; color: var(--kerne-fg-muted); }
|
|
314
|
-
.kerne-strength[data-score="0"] .kerne-strength-step[data-on],
|
|
315
|
-
.kerne-strength[data-score="1"] .kerne-strength-step[data-on] { background: var(--kerne-danger); }
|
|
316
|
-
.kerne-strength[data-score="2"] .kerne-strength-step[data-on] { background: var(--kerne-warning); }
|
|
317
|
-
.kerne-strength[data-score="3"] .kerne-strength-step[data-on],
|
|
318
|
-
.kerne-strength[data-score="4"] .kerne-strength-step[data-on] { background: var(--kerne-success); }
|
|
319
|
-
@media (prefers-reduced-motion: reduce) {
|
|
320
|
-
.kerne-strength-step { transition: none; }
|
|
321
|
-
}
|
|
322
|
-
.kerne-input::placeholder { color: var(--kerne-fg-subtle); }
|
|
323
|
-
.kerne-input:hover:not(:disabled):not(:focus) { border-color: var(--kerne-fg-subtle); }
|
|
324
|
-
.kerne-input:focus {
|
|
325
|
-
outline: none;
|
|
326
|
-
border-color: var(--kerne-accent);
|
|
327
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--kerne-accent) 15%, transparent);
|
|
328
|
-
}
|
|
329
|
-
.kerne-input:disabled { opacity: .6; cursor: not-allowed; }
|
|
330
|
-
.kerne-input[aria-invalid="true"] { border-color: var(--kerne-danger); }
|
|
331
|
-
.kerne-input[data-kerne-code="true"] {
|
|
332
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
333
|
-
letter-spacing: .3em;
|
|
334
|
-
text-transform: uppercase;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/* ---------- Buttons ---------- */
|
|
338
|
-
.kerne-button {
|
|
339
|
-
font: inherit;
|
|
340
|
-
font-size: 14px;
|
|
341
|
-
font-weight: 500;
|
|
342
|
-
height: var(--kerne-control-height);
|
|
343
|
-
border-radius: var(--kerne-radius);
|
|
344
|
-
padding: 0 16px;
|
|
345
|
-
cursor: pointer;
|
|
346
|
-
border: 1px solid transparent;
|
|
347
|
-
display: inline-flex;
|
|
348
|
-
align-items: center;
|
|
349
|
-
justify-content: center;
|
|
350
|
-
gap: 10px;
|
|
351
|
-
width: 100%;
|
|
352
|
-
transition: background-color .12s ease, border-color .12s ease, opacity .12s ease;
|
|
353
|
-
}
|
|
354
|
-
.kerne-button:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; }
|
|
355
|
-
.kerne-button:disabled { opacity: .5; cursor: not-allowed; }
|
|
356
|
-
/* A disabled primary drops the brand entirely rather than wearing it at half
|
|
357
|
-
opacity: on a dark theme the light primary faded into an opaque grey slab
|
|
358
|
-
that read as an enabled button, and a brand colour has no business marking
|
|
359
|
-
an action you cannot take. Loading is excluded - it is still the real
|
|
360
|
-
button, just busy. */
|
|
361
|
-
.kerne-button-primary:disabled:not([data-kerne-loading="true"]) {
|
|
362
|
-
opacity: 1;
|
|
363
|
-
background: var(--kerne-track);
|
|
364
|
-
color: var(--kerne-fg-subtle);
|
|
365
|
-
}
|
|
366
|
-
/* A button mid-request is busy, not unavailable - dimming it to the disabled
|
|
367
|
-
opacity reads as "this stopped working". */
|
|
368
|
-
.kerne-button[data-kerne-loading="true"] { opacity: 1; cursor: progress; }
|
|
369
|
-
.kerne-button .kerne-spinner { width: 15px; height: 15px; border-width: 2px; }
|
|
370
|
-
.kerne-button-primary { background: var(--kerne-primary); color: var(--kerne-primary-fg); }
|
|
371
|
-
.kerne-button-primary:hover:not(:disabled) { background: var(--kerne-primary-hover); }
|
|
372
|
-
.kerne-button-outline {
|
|
373
|
-
background: transparent;
|
|
374
|
-
color: var(--kerne-fg);
|
|
375
|
-
border-color: var(--kerne-border-strong);
|
|
376
|
-
}
|
|
377
|
-
.kerne-button-outline:hover:not(:disabled) { background: var(--kerne-panel); }
|
|
378
|
-
|
|
379
|
-
/* Method switch - a quiet text control, not a second call to action competing
|
|
380
|
-
with the primary button above it. */
|
|
381
|
-
.kerne-switch {
|
|
382
|
-
font: inherit;
|
|
383
|
-
font-size: 14px;
|
|
384
|
-
width: 100%;
|
|
385
|
-
text-align: center;
|
|
386
|
-
background: none;
|
|
387
|
-
border: none;
|
|
388
|
-
padding: 4px 0;
|
|
389
|
-
cursor: pointer;
|
|
390
|
-
color: var(--kerne-fg-muted);
|
|
391
|
-
text-underline-offset: 4px;
|
|
392
|
-
transition: color .12s ease;
|
|
393
|
-
}
|
|
394
|
-
.kerne-switch:hover { color: var(--kerne-fg); text-decoration: underline; }
|
|
395
|
-
.kerne-switch:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; border-radius: 4px; }
|
|
396
|
-
|
|
397
|
-
.kerne-link {
|
|
398
|
-
font-size: inherit;
|
|
399
|
-
color: var(--kerne-accent);
|
|
400
|
-
background: none;
|
|
401
|
-
border: none;
|
|
402
|
-
padding: 0;
|
|
403
|
-
cursor: pointer;
|
|
404
|
-
font-family: inherit;
|
|
405
|
-
text-decoration: none;
|
|
406
|
-
text-underline-offset: 3px;
|
|
407
|
-
}
|
|
408
|
-
.kerne-link:hover { text-decoration: underline; }
|
|
409
|
-
.kerne-link:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; border-radius: 2px; }
|
|
410
|
-
|
|
411
|
-
/* ---------- Divider ---------- */
|
|
412
|
-
.kerne-divider {
|
|
413
|
-
display: flex;
|
|
414
|
-
align-items: center;
|
|
415
|
-
gap: 12px;
|
|
416
|
-
color: var(--kerne-fg-muted);
|
|
417
|
-
font-size: 11px;
|
|
418
|
-
text-transform: uppercase;
|
|
419
|
-
letter-spacing: .06em;
|
|
420
|
-
padding: 2px 0;
|
|
421
|
-
}
|
|
422
|
-
.kerne-divider::before, .kerne-divider::after {
|
|
423
|
-
content: "";
|
|
424
|
-
flex: 1;
|
|
425
|
-
height: 1px;
|
|
426
|
-
background: var(--kerne-border);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/* ---------- Panels ---------- */
|
|
430
|
-
.kerne-panel {
|
|
431
|
-
display: flex;
|
|
432
|
-
align-items: flex-start;
|
|
433
|
-
gap: 10px;
|
|
434
|
-
font-size: 14px;
|
|
435
|
-
line-height: 1.5;
|
|
436
|
-
padding: 12px 14px;
|
|
437
|
-
border-radius: var(--kerne-radius);
|
|
438
|
-
background: var(--kerne-panel);
|
|
439
|
-
color: var(--kerne-fg-muted);
|
|
440
|
-
}
|
|
441
|
-
.kerne-panel-danger { color: var(--kerne-danger); }
|
|
442
|
-
.kerne-panel-success { color: var(--kerne-success); }
|
|
443
|
-
.kerne-panel svg { flex-shrink: 0; margin-top: 1px; }
|
|
444
|
-
.kerne-panel strong { color: var(--kerne-fg); font-weight: 500; }
|
|
445
|
-
|
|
446
|
-
.kerne-oauth-group { display: flex; flex-direction: column; gap: 10px; }
|
|
447
|
-
|
|
448
|
-
/* A closing line under a form, e.g. "Remembered it? Back to sign in". */
|
|
449
|
-
.kerne-footer { font-size: 14px; color: var(--kerne-fg-muted); text-align: center; margin: 4px 0 0; }
|
|
450
|
-
|
|
451
|
-
/* ---------- Footnotes ---------- */
|
|
452
|
-
.kerne-legal {
|
|
453
|
-
font-size: 12px;
|
|
454
|
-
line-height: 1.5;
|
|
455
|
-
color: var(--kerne-fg-subtle);
|
|
456
|
-
text-align: center;
|
|
457
|
-
margin: 32px 0 0;
|
|
458
|
-
}
|
|
459
|
-
.kerne-branding {
|
|
460
|
-
display: flex;
|
|
461
|
-
align-items: center;
|
|
462
|
-
justify-content: center;
|
|
463
|
-
gap: 5px;
|
|
464
|
-
margin-top: 24px;
|
|
465
|
-
font-size: 12px;
|
|
466
|
-
color: var(--kerne-fg-subtle);
|
|
467
|
-
text-decoration: none;
|
|
468
|
-
}
|
|
469
|
-
.kerne-branding:hover { color: var(--kerne-fg-muted); }
|
|
470
|
-
|
|
471
|
-
/* Centred outcome screen for the flows that resolve on their own (magic-link
|
|
472
|
-
landing, email verification by link) - there is no form to show, only what
|
|
473
|
-
happened. */
|
|
474
|
-
.kerne-status { display: flex; flex-direction: column; align-items: center; gap: 14px; text-align: center; padding: 8px 0; }
|
|
475
|
-
.kerne-status-icon { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 50%; background: var(--kerne-panel); }
|
|
476
|
-
.kerne-status-icon[data-tone="danger"] { color: var(--kerne-danger); }
|
|
477
|
-
.kerne-status-icon[data-tone="success"] { color: var(--kerne-success); }
|
|
478
|
-
.kerne-status-icon[data-tone="muted"] { color: var(--kerne-fg-muted); }
|
|
479
|
-
.kerne-status-text { font-size: 14px; color: var(--kerne-fg-muted); line-height: 1.5; margin: 0; max-width: 34ch; }
|
|
480
|
-
|
|
481
|
-
.kerne-spinner {
|
|
482
|
-
width: 20px; height: 20px;
|
|
483
|
-
border: 2px solid currentColor;
|
|
484
|
-
border-right-color: transparent;
|
|
485
|
-
border-radius: 50%;
|
|
486
|
-
animation: kerne-spin .6s linear infinite;
|
|
487
|
-
}
|
|
488
|
-
@keyframes kerne-spin { to { transform: rotate(360deg); } }
|
|
489
|
-
|
|
490
|
-
.kerne-button-danger {
|
|
491
|
-
background: transparent;
|
|
492
|
-
color: var(--kerne-danger);
|
|
493
|
-
border-color: color-mix(in srgb, var(--kerne-danger) 40%, transparent);
|
|
494
|
-
}
|
|
495
|
-
.kerne-button-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--kerne-danger) 8%, transparent); }
|
|
496
|
-
|
|
497
|
-
/* ---------- Usage meters ---------- */
|
|
498
|
-
.kerne-meters { display: flex; flex-direction: column; gap: 18px; }
|
|
499
|
-
.kerne-meter { display: flex; flex-direction: column; gap: 7px; }
|
|
500
|
-
.kerne-meter-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
|
|
501
|
-
.kerne-meter-name { font-size: 14px; font-weight: 500; }
|
|
502
|
-
.kerne-meter-value {
|
|
503
|
-
font-size: 13px;
|
|
504
|
-
color: var(--kerne-fg-muted);
|
|
505
|
-
/* Digits line up between rows instead of jittering as counts change. */
|
|
506
|
-
font-variant-numeric: tabular-nums;
|
|
507
|
-
}
|
|
508
|
-
.kerne-meter-track { height: 6px; border-radius: 999px; background: var(--kerne-track); overflow: hidden; }
|
|
509
|
-
.kerne-meter-fill { height: 100%; border-radius: 999px; background: var(--kerne-fg); transition: width .3s ease; }
|
|
510
|
-
.kerne-meter[data-level="warning"] .kerne-meter-fill { background: var(--kerne-warning); }
|
|
511
|
-
.kerne-meter[data-level="critical"] .kerne-meter-fill { background: var(--kerne-danger); }
|
|
512
|
-
.kerne-meter[data-level="warning"] .kerne-meter-value { color: var(--kerne-warning); }
|
|
513
|
-
.kerne-meter[data-level="critical"] .kerne-meter-value { color: var(--kerne-danger); }
|
|
514
|
-
.kerne-meter-note { font-size: 12px; color: var(--kerne-fg-subtle); margin: 0; }
|
|
515
|
-
@media (prefers-reduced-motion: reduce) { .kerne-meter-fill { transition: none; } }
|
|
516
|
-
|
|
517
|
-
/* ---------- Pricing table ---------- */
|
|
518
|
-
.kerne-pricing-skeleton { width: 100%; }
|
|
519
|
-
|
|
520
|
-
.kerne-pricing-switch-wrap { display: flex; justify-content: center; }
|
|
521
|
-
.kerne-pricing-switch {
|
|
522
|
-
position: relative;
|
|
523
|
-
display: inline-flex;
|
|
524
|
-
gap: 2px;
|
|
525
|
-
padding: 4px;
|
|
526
|
-
background: var(--kerne-panel);
|
|
527
|
-
border-radius: 999px;
|
|
528
|
-
}
|
|
529
|
-
.kerne-pricing-switch-thumb {
|
|
530
|
-
position: absolute;
|
|
531
|
-
top: 4px;
|
|
532
|
-
bottom: 4px;
|
|
533
|
-
left: 0;
|
|
534
|
-
border-radius: 999px;
|
|
535
|
-
background: var(--kerne-surface);
|
|
536
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
537
|
-
transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1), width 0.32s cubic-bezier(0.16, 1, 0.3, 1);
|
|
538
|
-
}
|
|
539
|
-
.kerne-pricing-switch-option {
|
|
540
|
-
position: relative;
|
|
541
|
-
z-index: 1;
|
|
542
|
-
display: inline-flex;
|
|
543
|
-
align-items: center;
|
|
544
|
-
gap: 7px;
|
|
545
|
-
font: inherit;
|
|
546
|
-
font-size: 13.5px;
|
|
547
|
-
font-weight: 500;
|
|
548
|
-
white-space: nowrap;
|
|
549
|
-
color: var(--kerne-fg-muted);
|
|
550
|
-
background: none;
|
|
551
|
-
border: none;
|
|
552
|
-
border-radius: 999px;
|
|
553
|
-
padding: 7px 16px;
|
|
554
|
-
cursor: pointer;
|
|
555
|
-
transition: color 0.18s ease;
|
|
556
|
-
}
|
|
557
|
-
.kerne-pricing-switch-option[aria-pressed="true"] { color: var(--kerne-fg); }
|
|
558
|
-
.kerne-pricing-switch-option:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; }
|
|
559
|
-
.kerne-pricing-switch-save {
|
|
560
|
-
font-size: 10.5px;
|
|
561
|
-
font-weight: 600;
|
|
562
|
-
letter-spacing: 0.01em;
|
|
563
|
-
color: var(--kerne-success);
|
|
564
|
-
background: color-mix(in srgb, var(--kerne-success) 14%, transparent);
|
|
565
|
-
padding: 2px 6px;
|
|
566
|
-
border-radius: 999px;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
.kerne-pricing-grid {
|
|
570
|
-
display: grid;
|
|
571
|
-
grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
|
|
572
|
-
gap: 20px;
|
|
573
|
-
align-items: stretch;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
.kerne-pricing-card {
|
|
577
|
-
position: relative;
|
|
578
|
-
display: flex;
|
|
579
|
-
flex-direction: column;
|
|
580
|
-
gap: 24px;
|
|
581
|
-
height: 100%;
|
|
582
|
-
padding: 28px 26px;
|
|
583
|
-
background: var(--kerne-surface);
|
|
584
|
-
border: 1px solid var(--kerne-border);
|
|
585
|
-
border-radius: calc(var(--kerne-radius) + 8px);
|
|
586
|
-
}
|
|
587
|
-
.kerne-pricing-card[data-featured="true"] {
|
|
588
|
-
background: var(--kerne-surface-raised);
|
|
589
|
-
border-color: var(--kerne-fg);
|
|
590
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 24px 48px -28px rgba(0, 0, 0, 0.22);
|
|
591
|
-
}
|
|
592
|
-
.kerne-pricing-card[data-current="true"]:not([data-featured="true"]) { border-color: var(--kerne-border-strong); }
|
|
593
|
-
|
|
594
|
-
/* A quiet inline pill, not a loud badge escaping the card border - that
|
|
595
|
-
shouted-uppercase-chip-on-the-edge look is the single most template-y
|
|
596
|
-
tell a pricing page can have. Reads as confirmation of what the card's
|
|
597
|
-
own elevation already signals, never as the primary signal itself. */
|
|
598
|
-
.kerne-pricing-badge {
|
|
599
|
-
align-self: flex-start;
|
|
600
|
-
font-size: 11.5px;
|
|
601
|
-
font-weight: 600;
|
|
602
|
-
letter-spacing: 0.01em;
|
|
603
|
-
color: var(--kerne-fg);
|
|
604
|
-
background: var(--kerne-panel);
|
|
605
|
-
padding: 3px 9px;
|
|
606
|
-
border-radius: 999px;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
.kerne-pricing-card-head { display: flex; flex-direction: column; gap: 8px; }
|
|
610
|
-
.kerne-pricing-card-name { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
|
|
611
|
-
.kerne-pricing-card-desc { margin: 0; font-size: 13px; line-height: 1.5; color: var(--kerne-fg-muted); }
|
|
612
|
-
|
|
613
|
-
.kerne-pricing-price { display: flex; flex-direction: column; gap: 3px; min-height: 52px; justify-content: center; }
|
|
614
|
-
.kerne-pricing-price-row { display: flex; align-items: baseline; gap: 5px; }
|
|
615
|
-
.kerne-pricing-amount {
|
|
616
|
-
font-size: 38px;
|
|
617
|
-
font-weight: 600;
|
|
618
|
-
letter-spacing: -0.025em;
|
|
619
|
-
line-height: 1;
|
|
620
|
-
font-variant-numeric: tabular-nums;
|
|
621
|
-
}
|
|
622
|
-
.kerne-pricing-interval { font-size: 14px; color: var(--kerne-fg-muted); }
|
|
623
|
-
.kerne-pricing-billed { margin: 0; font-size: 12px; color: var(--kerne-fg-subtle); }
|
|
624
|
-
|
|
625
|
-
.kerne-pricing-features { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; flex: 1; }
|
|
626
|
-
.kerne-pricing-feature { display: flex; align-items: flex-start; gap: 9px; font-size: 13.5px; line-height: 1.4; color: var(--kerne-fg); }
|
|
627
|
-
.kerne-pricing-feature svg { flex-shrink: 0; margin-top: 2px; color: var(--kerne-fg-subtle); }
|
|
628
|
-
.kerne-pricing-feature-more { font-size: 12.5px; color: var(--kerne-fg-subtle); padding-left: 24px; }
|
|
629
|
-
|
|
630
|
-
.kerne-pricing-cta { margin-top: auto; }
|
|
631
|
-
.kerne-pricing-cta .kerne-button { width: 100%; }
|
|
632
|
-
|
|
633
|
-
@media (prefers-reduced-motion: reduce) {
|
|
634
|
-
.kerne-pricing-switch-thumb { transition: none; }
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
/* ---------- Sections (profile, settings) ---------- */
|
|
638
|
-
.kerne-section-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
|
|
639
|
-
.kerne-section-title { font-size: 15px; font-weight: 600; margin: 0; }
|
|
640
|
-
|
|
641
|
-
.kerne-rows { display: flex; flex-direction: column; gap: 10px; }
|
|
642
|
-
.kerne-row { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; font-size: 14px; }
|
|
643
|
-
.kerne-row dt { color: var(--kerne-fg-muted); margin: 0; }
|
|
644
|
-
.kerne-row dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
|
|
645
|
-
|
|
646
|
-
.kerne-badge {
|
|
647
|
-
display: inline-flex;
|
|
648
|
-
align-items: center;
|
|
649
|
-
font-size: 12px;
|
|
650
|
-
font-weight: 500;
|
|
651
|
-
padding: 2px 8px;
|
|
652
|
-
border-radius: 999px;
|
|
653
|
-
background: var(--kerne-panel);
|
|
654
|
-
color: var(--kerne-fg-muted);
|
|
655
|
-
white-space: nowrap;
|
|
656
|
-
}
|
|
657
|
-
.kerne-badge[data-tone="success"] { color: var(--kerne-success); }
|
|
658
|
-
.kerne-badge[data-tone="warning"] { color: var(--kerne-warning); }
|
|
659
|
-
.kerne-badge[data-tone="danger"] { color: var(--kerne-danger); }
|
|
660
|
-
|
|
661
|
-
/* ---------- Identity ---------- */
|
|
662
|
-
.kerne-avatar {
|
|
663
|
-
width: 32px; height: 32px; border-radius: 50%;
|
|
664
|
-
background: var(--kerne-panel); color: var(--kerne-fg-muted);
|
|
665
|
-
display: flex; align-items: center; justify-content: center;
|
|
666
|
-
font-size: 12px; font-weight: 600; letter-spacing: .02em; flex-shrink: 0;
|
|
667
|
-
object-fit: cover;
|
|
668
|
-
}
|
|
669
|
-
.kerne-identity { display: flex; align-items: center; gap: 12px; }
|
|
670
|
-
.kerne-identity-text { display: flex; flex-direction: column; min-width: 0; }
|
|
671
|
-
.kerne-identity-name { font-size: 14px; font-weight: 500; }
|
|
672
|
-
.kerne-identity-mail { font-size: 13px; color: var(--kerne-fg-muted); overflow: hidden; text-overflow: ellipsis; }
|
|
673
|
-
|
|
674
|
-
.kerne-menu-wrap { position: relative; display: inline-flex; }
|
|
675
|
-
.kerne-menu-trigger {
|
|
676
|
-
background: none; border: none; padding: 0; cursor: pointer;
|
|
677
|
-
border-radius: 50%; display: inline-flex; font: inherit;
|
|
678
|
-
}
|
|
679
|
-
.kerne-menu-trigger:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; }
|
|
680
|
-
.kerne-menu {
|
|
681
|
-
position: absolute; top: calc(100% + 8px); right: 0; z-index: 50;
|
|
682
|
-
min-width: 240px; padding: 8px;
|
|
683
|
-
display: flex; flex-direction: column; gap: 2px;
|
|
684
|
-
/* Needs an opaque ground of its own: it floats over host content. */
|
|
685
|
-
background: var(--kerne-surface);
|
|
686
|
-
border: 1px solid var(--kerne-border);
|
|
687
|
-
border-radius: var(--kerne-radius);
|
|
688
|
-
box-shadow: var(--kerne-shadow);
|
|
689
|
-
}
|
|
690
|
-
.kerne-menu-header { padding: 8px 10px 10px; border-bottom: 1px solid var(--kerne-border); margin-bottom: 6px; }
|
|
691
|
-
.kerne-menu-item {
|
|
692
|
-
font: inherit; font-size: 14px; text-align: left;
|
|
693
|
-
background: none; border: none; cursor: pointer;
|
|
694
|
-
padding: 8px 10px; border-radius: 6px; color: var(--kerne-fg);
|
|
695
|
-
display: block; width: 100%; text-decoration: none;
|
|
696
|
-
}
|
|
697
|
-
.kerne-menu-item:hover { background: var(--kerne-panel); }
|
|
698
|
-
.kerne-menu-item:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: -2px; }
|
|
699
|
-
.kerne-menu-item[data-tone="danger"] { color: var(--kerne-danger); }
|
|
700
|
-
|
|
701
|
-
/* ---------- Loading ---------- */
|
|
702
|
-
.kerne-skeleton {
|
|
703
|
-
background: var(--kerne-panel);
|
|
704
|
-
border-radius: var(--kerne-radius);
|
|
705
|
-
height: var(--kerne-control-height);
|
|
706
|
-
animation: kerne-pulse 1.6s ease-in-out infinite;
|
|
707
|
-
}
|
|
708
|
-
@keyframes kerne-pulse { 50% { opacity: .55; } }
|
|
709
|
-
@media (prefers-reduced-motion: reduce) {
|
|
710
|
-
.kerne-skeleton { animation: none; }
|
|
711
|
-
.kerne-spinner { animation-duration: 2s; }
|
|
712
|
-
.kerne-input, .kerne-button, .kerne-switch { transition: none; }
|
|
713
|
-
}
|
|
714
|
-
`;
|
|
715
|
-
function ensureStylesInjected() {
|
|
716
|
-
if (typeof document === "undefined") return;
|
|
717
|
-
if (document.getElementById(STYLE_ELEMENT_ID)) return;
|
|
718
|
-
const style = document.createElement("style");
|
|
719
|
-
style.id = STYLE_ELEMENT_ID;
|
|
720
|
-
style.textContent = CSS;
|
|
721
|
-
document.head.appendChild(style);
|
|
722
|
-
}
|
|
723
|
-
function parseColor(input) {
|
|
724
|
-
const value = input.trim();
|
|
725
|
-
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(value);
|
|
726
|
-
if (hex) {
|
|
727
|
-
const digits = hex[1].length === 3 ? hex[1].split("").map((c) => c + c).join("") : hex[1];
|
|
728
|
-
return [
|
|
729
|
-
parseInt(digits.slice(0, 2), 16),
|
|
730
|
-
parseInt(digits.slice(2, 4), 16),
|
|
731
|
-
parseInt(digits.slice(4, 6), 16)
|
|
732
|
-
];
|
|
733
|
-
}
|
|
734
|
-
const rgb = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(value);
|
|
735
|
-
if (rgb) return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])];
|
|
736
|
-
return null;
|
|
737
|
-
}
|
|
738
|
-
function luminance([r, g, b]) {
|
|
739
|
-
const channel = (v) => {
|
|
740
|
-
const s = Math.min(Math.max(v, 0), 255) / 255;
|
|
741
|
-
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
742
|
-
};
|
|
743
|
-
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
744
|
-
}
|
|
745
|
-
function toHex([r, g, b]) {
|
|
746
|
-
const pair = (v) => Math.round(Math.min(Math.max(v, 0), 255)).toString(16).padStart(2, "0");
|
|
747
|
-
return `#${pair(r)}${pair(g)}${pair(b)}`;
|
|
748
|
-
}
|
|
749
|
-
function mix(color, toward, amount) {
|
|
750
|
-
return color.map((c) => c + (toward - c) * amount);
|
|
751
|
-
}
|
|
752
|
-
var LABEL_CROSSOVER = 0.2;
|
|
753
|
-
function appearanceToStyle(appearance) {
|
|
754
|
-
if (!appearance) return {};
|
|
755
|
-
const vars = {};
|
|
756
|
-
if (appearance.primaryColor) {
|
|
757
|
-
vars["--kerne-primary"] = appearance.primaryColor;
|
|
758
|
-
vars["--kerne-accent"] = appearance.accentColor ?? appearance.primaryColor;
|
|
759
|
-
const rgb = parseColor(appearance.primaryColor);
|
|
760
|
-
if (rgb) {
|
|
761
|
-
const light = luminance(rgb) > LABEL_CROSSOVER;
|
|
762
|
-
vars["--kerne-primary-fg"] = appearance.primaryTextColor ?? (light ? "#16171a" : "#ffffff");
|
|
763
|
-
vars["--kerne-primary-hover"] = toHex(mix(rgb, light ? 0 : 255, 0.14));
|
|
764
|
-
} else if (appearance.primaryTextColor) {
|
|
765
|
-
vars["--kerne-primary-fg"] = appearance.primaryTextColor;
|
|
766
|
-
}
|
|
767
|
-
} else {
|
|
768
|
-
if (appearance.accentColor) vars["--kerne-accent"] = appearance.accentColor;
|
|
769
|
-
if (appearance.primaryTextColor) vars["--kerne-primary-fg"] = appearance.primaryTextColor;
|
|
770
|
-
}
|
|
771
|
-
if (appearance.textColor) vars["--kerne-fg"] = appearance.textColor;
|
|
772
|
-
if (appearance.borderColor) vars["--kerne-border-strong"] = appearance.borderColor;
|
|
773
|
-
if (appearance.panelColor) vars["--kerne-panel"] = appearance.panelColor;
|
|
774
|
-
if (appearance.background) vars["--kerne-bg"] = appearance.background;
|
|
775
|
-
if (appearance.surfaceColor) {
|
|
776
|
-
vars["--kerne-surface"] = appearance.surfaceColor;
|
|
777
|
-
vars["--kerne-surface-raised"] = appearance.surfaceColor;
|
|
778
|
-
}
|
|
779
|
-
if (appearance.fontFamily) vars["--kerne-font"] = appearance.fontFamily;
|
|
780
|
-
if (appearance.radius) vars["--kerne-radius"] = appearance.radius;
|
|
781
|
-
if (appearance.controlHeight) vars["--kerne-control-height"] = appearance.controlHeight;
|
|
782
|
-
return vars;
|
|
783
|
-
}
|
|
784
|
-
function resolveTheme(appearance) {
|
|
785
|
-
return appearance?.theme ?? "light";
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// src/ui/password.ts
|
|
789
|
-
var MIN_PASSWORD_LENGTH = 8;
|
|
790
|
-
var BASELINE_POLICY = {
|
|
791
|
-
min_length: MIN_PASSWORD_LENGTH,
|
|
792
|
-
require_uppercase: false,
|
|
793
|
-
require_number: false,
|
|
794
|
-
require_symbol: false
|
|
795
|
-
};
|
|
796
|
-
function resolvePolicy(config) {
|
|
797
|
-
return config?.password_policy ?? BASELINE_POLICY;
|
|
798
|
-
}
|
|
799
|
-
function passwordRules(password, policy) {
|
|
800
|
-
const rules = [{ key: "min_length", met: password.length >= policy.min_length }];
|
|
801
|
-
if (policy.require_uppercase) {
|
|
802
|
-
rules.push({ key: "require_uppercase", met: /[A-Z]/.test(password) });
|
|
803
|
-
}
|
|
804
|
-
if (policy.require_number) {
|
|
805
|
-
rules.push({ key: "require_number", met: /\d/.test(password) });
|
|
806
|
-
}
|
|
807
|
-
if (policy.require_symbol) {
|
|
808
|
-
rules.push({ key: "require_symbol", met: /[^A-Za-z0-9]/.test(password) });
|
|
809
|
-
}
|
|
810
|
-
return rules;
|
|
811
|
-
}
|
|
812
|
-
function meetsPolicy(password, policy) {
|
|
813
|
-
return passwordRules(password, policy).every((rule) => rule.met);
|
|
814
|
-
}
|
|
815
|
-
function passwordStrength(password) {
|
|
816
|
-
if (password.length < MIN_PASSWORD_LENGTH) return 0;
|
|
817
|
-
const variety = [/[a-z]/, /[A-Z]/, /\d/, /[^A-Za-z0-9]/].filter((re) => re.test(password)).length;
|
|
818
|
-
let score = 1;
|
|
819
|
-
if (password.length >= 12) score++;
|
|
820
|
-
if (password.length >= 16) score++;
|
|
821
|
-
if (variety >= 3) score++;
|
|
822
|
-
return Math.min(score, 4);
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// src/ui/primitives.tsx
|
|
826
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
827
|
-
var InsideShell = createContext(false);
|
|
828
|
-
function Shell({
|
|
829
|
-
appearance,
|
|
830
|
-
className,
|
|
831
|
-
size = "auth",
|
|
832
|
-
children
|
|
833
|
-
}) {
|
|
834
|
-
ensureStylesInjected();
|
|
835
|
-
const nested = useContext(InsideShell);
|
|
836
|
-
if (nested) {
|
|
837
|
-
return /* @__PURE__ */ jsx("div", { className, children });
|
|
838
|
-
}
|
|
839
|
-
return /* @__PURE__ */ jsx(InsideShell.Provider, { value: true, children: /* @__PURE__ */ jsx(
|
|
840
|
-
"div",
|
|
841
|
-
{
|
|
842
|
-
className: `kerne-root${className ? ` ${className}` : ""}`,
|
|
843
|
-
style: appearanceToStyle(appearance),
|
|
844
|
-
"data-kerne-theme": resolveTheme(appearance),
|
|
845
|
-
children: /* @__PURE__ */ jsx(
|
|
846
|
-
"div",
|
|
847
|
-
{
|
|
848
|
-
className: "kerne-shell",
|
|
849
|
-
"data-kerne-size": size === "wide" ? "wide" : void 0,
|
|
850
|
-
"data-kerne-surface": appearance?.surface === "card" ? "card" : void 0,
|
|
851
|
-
children
|
|
852
|
-
}
|
|
853
|
-
)
|
|
854
|
-
}
|
|
855
|
-
) });
|
|
856
|
-
}
|
|
857
|
-
function Subsection({
|
|
858
|
-
title,
|
|
859
|
-
description,
|
|
860
|
-
children
|
|
861
|
-
}) {
|
|
862
|
-
return /* @__PURE__ */ jsxs("section", { className: "kerne-subsection", children: [
|
|
863
|
-
/* @__PURE__ */ jsxs("div", { className: "kerne-subsection-head", children: [
|
|
864
|
-
/* @__PURE__ */ jsx("h3", { className: "kerne-subsection-title", children: title }),
|
|
865
|
-
description ? /* @__PURE__ */ jsx("p", { className: "kerne-subsection-desc", children: description }) : null
|
|
866
|
-
] }),
|
|
867
|
-
children
|
|
868
|
-
] });
|
|
869
|
-
}
|
|
870
|
-
function Masthead({
|
|
871
|
-
logo,
|
|
872
|
-
title,
|
|
873
|
-
subtitle
|
|
874
|
-
}) {
|
|
875
|
-
return /* @__PURE__ */ jsxs("div", { className: "kerne-masthead", children: [
|
|
876
|
-
logo ? /* @__PURE__ */ jsx("div", { className: "kerne-logo", children: logo }) : null,
|
|
877
|
-
/* @__PURE__ */ jsxs("div", { className: "kerne-heading", children: [
|
|
878
|
-
/* @__PURE__ */ jsx("h1", { className: "kerne-title", children: title }),
|
|
879
|
-
subtitle ? /* @__PURE__ */ jsx("p", { className: "kerne-subtitle", children: subtitle }) : null
|
|
880
|
-
] })
|
|
881
|
-
] });
|
|
882
|
-
}
|
|
883
|
-
function prefersAutoFocus() {
|
|
884
|
-
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
885
|
-
return window.matchMedia("(pointer: fine)").matches;
|
|
886
|
-
}
|
|
887
|
-
function Field({
|
|
888
|
-
label,
|
|
889
|
-
type,
|
|
890
|
-
value,
|
|
891
|
-
onChange,
|
|
892
|
-
autoComplete,
|
|
893
|
-
placeholder,
|
|
894
|
-
disabled,
|
|
895
|
-
required,
|
|
896
|
-
invalid,
|
|
897
|
-
error,
|
|
898
|
-
action,
|
|
899
|
-
code,
|
|
900
|
-
autoFocus,
|
|
901
|
-
children
|
|
902
|
-
}) {
|
|
903
|
-
const id = useId();
|
|
904
|
-
const errorId = `${id}-error`;
|
|
905
|
-
const noteId = `${id}-note`;
|
|
906
|
-
const l10n = useLocalization();
|
|
907
|
-
const isPassword = type === "password";
|
|
908
|
-
const [revealed, setRevealed] = useState2(false);
|
|
909
|
-
const [capsLock, setCapsLock] = useState2(false);
|
|
910
|
-
const trackCapsLock = (e) => {
|
|
911
|
-
if (!isPassword) return;
|
|
912
|
-
setCapsLock(e.getModifierState?.("CapsLock") ?? false);
|
|
913
|
-
};
|
|
914
|
-
const describedBy = [error ? errorId : null, children ? noteId : null].filter(Boolean).join(" ");
|
|
915
|
-
return /* @__PURE__ */ jsxs("div", { className: "kerne-field", children: [
|
|
916
|
-
action ? /* @__PURE__ */ jsxs("div", { className: "kerne-label-row", children: [
|
|
917
|
-
/* @__PURE__ */ jsx("label", { className: "kerne-label", htmlFor: id, children: label }),
|
|
918
|
-
action
|
|
919
|
-
] }) : /* @__PURE__ */ jsx("label", { className: "kerne-label", htmlFor: id, children: label }),
|
|
920
|
-
/* @__PURE__ */ jsxs("div", { className: "kerne-control", children: [
|
|
921
|
-
/* @__PURE__ */ jsx(
|
|
922
|
-
"input",
|
|
923
|
-
{
|
|
924
|
-
id,
|
|
925
|
-
className: "kerne-input",
|
|
926
|
-
type: isPassword && revealed ? "text" : type,
|
|
927
|
-
value,
|
|
928
|
-
onChange: (e) => onChange(e.target.value),
|
|
929
|
-
onKeyUp: trackCapsLock,
|
|
930
|
-
onBlur: () => setCapsLock(false),
|
|
931
|
-
autoComplete,
|
|
932
|
-
placeholder,
|
|
933
|
-
disabled,
|
|
934
|
-
required,
|
|
935
|
-
"aria-invalid": invalid || !!error || void 0,
|
|
936
|
-
"aria-describedby": describedBy || void 0,
|
|
937
|
-
"data-kerne-code": code ? "true" : void 0,
|
|
938
|
-
"data-kerne-reveal": isPassword ? "true" : void 0,
|
|
939
|
-
autoFocus: autoFocus && prefersAutoFocus()
|
|
940
|
-
}
|
|
941
|
-
),
|
|
942
|
-
isPassword ? /* @__PURE__ */ jsx(
|
|
943
|
-
"button",
|
|
944
|
-
{
|
|
945
|
-
type: "button",
|
|
946
|
-
className: "kerne-reveal",
|
|
947
|
-
onClick: () => setRevealed((v) => !v),
|
|
948
|
-
disabled,
|
|
949
|
-
"aria-label": revealed ? l10n.common.hidePassword : l10n.common.showPassword,
|
|
950
|
-
"aria-pressed": revealed,
|
|
951
|
-
tabIndex: -1,
|
|
952
|
-
children: revealed ? /* @__PURE__ */ jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsx(EyeIcon, {})
|
|
953
|
-
}
|
|
954
|
-
) : null
|
|
955
|
-
] }),
|
|
956
|
-
children ? /* @__PURE__ */ jsx("div", { id: noteId, children }) : null,
|
|
957
|
-
capsLock ? /* @__PURE__ */ jsx("p", { className: "kerne-field-note", "data-kerne-warn": "true", role: "status", children: l10n.common.capsLockOn }) : null,
|
|
958
|
-
error ? /* @__PURE__ */ jsx("p", { className: "kerne-field-error", id: errorId, role: "alert", children: error }) : null
|
|
959
|
-
] });
|
|
960
|
-
}
|
|
961
|
-
function EyeIcon() {
|
|
962
|
-
return /* @__PURE__ */ jsxs(
|
|
963
|
-
"svg",
|
|
964
|
-
{
|
|
965
|
-
width: "16",
|
|
966
|
-
height: "16",
|
|
967
|
-
viewBox: "0 0 24 24",
|
|
968
|
-
fill: "none",
|
|
969
|
-
stroke: "currentColor",
|
|
970
|
-
strokeWidth: "2",
|
|
971
|
-
"aria-hidden": "true",
|
|
972
|
-
children: [
|
|
973
|
-
/* @__PURE__ */ jsx("path", { d: "M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7z", strokeLinejoin: "round" }),
|
|
974
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
|
|
975
|
-
]
|
|
976
|
-
}
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
function EyeOffIcon() {
|
|
980
|
-
return /* @__PURE__ */ jsxs(
|
|
981
|
-
"svg",
|
|
982
|
-
{
|
|
983
|
-
width: "16",
|
|
984
|
-
height: "16",
|
|
985
|
-
viewBox: "0 0 24 24",
|
|
986
|
-
fill: "none",
|
|
987
|
-
stroke: "currentColor",
|
|
988
|
-
strokeWidth: "2",
|
|
989
|
-
"aria-hidden": "true",
|
|
990
|
-
children: [
|
|
991
|
-
/* @__PURE__ */ jsx(
|
|
992
|
-
"path",
|
|
993
|
-
{
|
|
994
|
-
d: "M10.6 6.2A9.9 9.9 0 0 1 12 6c6.4 0 10 7 10 7a15.7 15.7 0 0 1-3 3.7M6.2 7.4A15.6 15.6 0 0 0 2 13s3.6 7 10 7a9.7 9.7 0 0 0 4.3-1",
|
|
995
|
-
strokeLinecap: "round",
|
|
996
|
-
strokeLinejoin: "round"
|
|
997
|
-
}
|
|
998
|
-
),
|
|
999
|
-
/* @__PURE__ */ jsx("path", { d: "m3 3 18 18", strokeLinecap: "round" })
|
|
1000
|
-
]
|
|
1001
|
-
}
|
|
1002
|
-
);
|
|
1003
|
-
}
|
|
1004
|
-
function Button({
|
|
1005
|
-
children,
|
|
1006
|
-
variant = "primary",
|
|
1007
|
-
disabled,
|
|
1008
|
-
loading,
|
|
1009
|
-
type = "submit",
|
|
1010
|
-
onClick,
|
|
1011
|
-
ariaLabel
|
|
1012
|
-
}) {
|
|
1013
|
-
return /* @__PURE__ */ jsxs(
|
|
1014
|
-
"button",
|
|
1015
|
-
{
|
|
1016
|
-
type,
|
|
1017
|
-
className: `kerne-button kerne-button-${variant}`,
|
|
1018
|
-
disabled: disabled || loading,
|
|
1019
|
-
onClick,
|
|
1020
|
-
"aria-label": ariaLabel,
|
|
1021
|
-
"aria-busy": loading || void 0,
|
|
1022
|
-
"data-kerne-loading": loading ? "true" : void 0,
|
|
1023
|
-
children: [
|
|
1024
|
-
loading ? /* @__PURE__ */ jsx("span", { className: "kerne-spinner", "aria-hidden": "true" }) : null,
|
|
1025
|
-
children
|
|
1026
|
-
]
|
|
1027
|
-
}
|
|
1028
|
-
);
|
|
1029
|
-
}
|
|
1030
|
-
function PasswordStrength({
|
|
1031
|
-
password,
|
|
1032
|
-
policy
|
|
1033
|
-
}) {
|
|
1034
|
-
const l10n = useLocalization();
|
|
1035
|
-
const resolved = policy ?? resolvePolicy(null);
|
|
1036
|
-
if (!meetsPolicy(password, resolved)) {
|
|
1037
|
-
const labels2 = {
|
|
1038
|
-
min_length: fill(l10n.common.passwordHint, { count: resolved.min_length }),
|
|
1039
|
-
require_uppercase: l10n.common.passwordRequireUppercase,
|
|
1040
|
-
require_number: l10n.common.passwordRequireNumber,
|
|
1041
|
-
require_symbol: l10n.common.passwordRequireSymbol
|
|
1042
|
-
};
|
|
1043
|
-
const rules = passwordRules(password, resolved);
|
|
1044
|
-
if (rules.length === 1) {
|
|
1045
|
-
return /* @__PURE__ */ jsx("p", { className: "kerne-field-note", children: labels2[rules[0].key] });
|
|
1046
|
-
}
|
|
1047
|
-
return /* @__PURE__ */ jsx("ul", { className: "kerne-rules", children: rules.map((rule) => /* @__PURE__ */ jsxs("li", { className: "kerne-rule", "data-met": rule.met ? "true" : void 0, children: [
|
|
1048
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: rule.met ? "\u2713" : "\xB7" }),
|
|
1049
|
-
labels2[rule.key]
|
|
1050
|
-
] }, rule.key)) });
|
|
1051
|
-
}
|
|
1052
|
-
const score = passwordStrength(password);
|
|
1053
|
-
const labels = [
|
|
1054
|
-
l10n.common.strengthWeak,
|
|
1055
|
-
l10n.common.strengthWeak,
|
|
1056
|
-
l10n.common.strengthFair,
|
|
1057
|
-
l10n.common.strengthGood,
|
|
1058
|
-
l10n.common.strengthStrong
|
|
1059
|
-
];
|
|
1060
|
-
return /* @__PURE__ */ jsxs("div", { className: "kerne-strength", "data-score": score, children: [
|
|
1061
|
-
/* @__PURE__ */ jsx("div", { className: "kerne-strength-track", "aria-hidden": "true", children: [1, 2, 3, 4].map((step) => /* @__PURE__ */ jsx(
|
|
1062
|
-
"span",
|
|
1063
|
-
{
|
|
1064
|
-
className: "kerne-strength-step",
|
|
1065
|
-
"data-on": score >= step ? "true" : void 0
|
|
1066
|
-
},
|
|
1067
|
-
step
|
|
1068
|
-
)) }),
|
|
1069
|
-
/* @__PURE__ */ jsxs("span", { className: "kerne-strength-label", children: [
|
|
1070
|
-
l10n.common.strengthLabel,
|
|
1071
|
-
": ",
|
|
1072
|
-
labels[score]
|
|
1073
|
-
] })
|
|
1074
|
-
] });
|
|
1075
|
-
}
|
|
1076
|
-
function MethodSwitch({
|
|
1077
|
-
onClick,
|
|
1078
|
-
children
|
|
1079
|
-
}) {
|
|
1080
|
-
return /* @__PURE__ */ jsx("button", { type: "button", className: "kerne-switch", onClick, children });
|
|
1081
|
-
}
|
|
1082
|
-
function AlertIcon() {
|
|
1083
|
-
return /* @__PURE__ */ jsxs(
|
|
1084
|
-
"svg",
|
|
1085
|
-
{
|
|
1086
|
-
width: "16",
|
|
1087
|
-
height: "16",
|
|
1088
|
-
viewBox: "0 0 24 24",
|
|
1089
|
-
fill: "none",
|
|
1090
|
-
stroke: "currentColor",
|
|
1091
|
-
strokeWidth: "2",
|
|
1092
|
-
"aria-hidden": "true",
|
|
1093
|
-
children: [
|
|
1094
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1095
|
-
/* @__PURE__ */ jsx("path", { d: "M12 8v4M12 16h.01", strokeLinecap: "round" })
|
|
1096
|
-
]
|
|
1097
|
-
}
|
|
1098
|
-
);
|
|
1099
|
-
}
|
|
1100
|
-
function MailIcon() {
|
|
1101
|
-
return /* @__PURE__ */ jsxs(
|
|
1102
|
-
"svg",
|
|
1103
|
-
{
|
|
1104
|
-
width: "16",
|
|
1105
|
-
height: "16",
|
|
1106
|
-
viewBox: "0 0 24 24",
|
|
1107
|
-
fill: "none",
|
|
1108
|
-
stroke: "currentColor",
|
|
1109
|
-
strokeWidth: "2",
|
|
1110
|
-
"aria-hidden": "true",
|
|
1111
|
-
children: [
|
|
1112
|
-
/* @__PURE__ */ jsx("rect", { x: "2", y: "4", width: "20", height: "16", rx: "2" }),
|
|
1113
|
-
/* @__PURE__ */ jsx("path", { d: "m2 7 10 6 10-6", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1114
|
-
]
|
|
1115
|
-
}
|
|
1116
|
-
);
|
|
1117
|
-
}
|
|
1118
|
-
function Panel({
|
|
1119
|
-
children,
|
|
1120
|
-
variant = "info"
|
|
1121
|
-
}) {
|
|
1122
|
-
const danger = variant === "danger";
|
|
1123
|
-
const icon = danger ? /* @__PURE__ */ jsx(AlertIcon, {}) : variant === "success" ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(MailIcon, {});
|
|
1124
|
-
return /* @__PURE__ */ jsxs(
|
|
1125
|
-
"div",
|
|
1126
|
-
{
|
|
1127
|
-
className: `kerne-panel${danger ? " kerne-panel-danger" : ""}${variant === "success" ? " kerne-panel-success" : ""}`,
|
|
1128
|
-
...danger ? { role: "alert" } : { role: "status", "aria-live": "polite" },
|
|
1129
|
-
children: [
|
|
1130
|
-
icon,
|
|
1131
|
-
/* @__PURE__ */ jsx("span", { children })
|
|
1132
|
-
]
|
|
1133
|
-
}
|
|
1134
|
-
);
|
|
1135
|
-
}
|
|
1136
|
-
function CheckIcon() {
|
|
1137
|
-
return /* @__PURE__ */ jsx(
|
|
1138
|
-
"svg",
|
|
1139
|
-
{
|
|
1140
|
-
width: "16",
|
|
1141
|
-
height: "16",
|
|
1142
|
-
viewBox: "0 0 24 24",
|
|
1143
|
-
fill: "none",
|
|
1144
|
-
stroke: "currentColor",
|
|
1145
|
-
strokeWidth: "2",
|
|
1146
|
-
"aria-hidden": "true",
|
|
1147
|
-
children: /* @__PURE__ */ jsx("path", { d: "m5 13 4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1148
|
-
}
|
|
1149
|
-
);
|
|
1150
|
-
}
|
|
1151
|
-
function Status({
|
|
1152
|
-
tone,
|
|
1153
|
-
title,
|
|
1154
|
-
children,
|
|
1155
|
-
action
|
|
1156
|
-
}) {
|
|
1157
|
-
return /* @__PURE__ */ jsxs("div", { className: "kerne-status", ...tone === "danger" ? { role: "alert" } : {}, children: [
|
|
1158
|
-
/* @__PURE__ */ jsx("div", { className: "kerne-status-icon", "data-tone": tone === "loading" ? "muted" : tone, children: tone === "loading" ? /* @__PURE__ */ jsx("span", { className: "kerne-spinner", "aria-hidden": "true" }) : tone === "success" ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(AlertIcon, {}) }),
|
|
1159
|
-
/* @__PURE__ */ jsxs("div", { className: "kerne-heading", children: [
|
|
1160
|
-
/* @__PURE__ */ jsx("h1", { className: "kerne-title", children: title }),
|
|
1161
|
-
children ? /* @__PURE__ */ jsx("p", { className: "kerne-status-text", children }) : null
|
|
1162
|
-
] }),
|
|
1163
|
-
action
|
|
1164
|
-
] });
|
|
1165
|
-
}
|
|
1166
|
-
function Divider({ label }) {
|
|
1167
|
-
return /* @__PURE__ */ jsx("div", { className: "kerne-divider", children: label });
|
|
1168
|
-
}
|
|
1169
|
-
function TextLink({
|
|
1170
|
-
href,
|
|
1171
|
-
onClick,
|
|
1172
|
-
children
|
|
1173
|
-
}) {
|
|
1174
|
-
if (onClick) {
|
|
1175
|
-
return /* @__PURE__ */ jsx("button", { type: "button", className: "kerne-link", onClick, children });
|
|
1176
|
-
}
|
|
1177
|
-
return /* @__PURE__ */ jsx("a", { className: "kerne-link", href: href ?? "#", children });
|
|
1178
|
-
}
|
|
1179
|
-
var OAUTH_LABELS = {
|
|
1180
|
-
google: "Google",
|
|
1181
|
-
github: "GitHub",
|
|
1182
|
-
microsoft: "Microsoft",
|
|
1183
|
-
apple: "Apple",
|
|
1184
|
-
gitlab: "GitLab",
|
|
1185
|
-
discord: "Discord"
|
|
1186
|
-
};
|
|
1187
|
-
function OAuthIcon({ provider }) {
|
|
1188
|
-
const common = { width: 18, height: 18, viewBox: "0 0 24 24", "aria-hidden": true };
|
|
1189
|
-
switch (provider) {
|
|
1190
|
-
case "google":
|
|
1191
|
-
return /* @__PURE__ */ jsxs("svg", { ...common, children: [
|
|
1192
|
-
/* @__PURE__ */ jsx(
|
|
1193
|
-
"path",
|
|
1194
|
-
{
|
|
1195
|
-
fill: "#4285F4",
|
|
1196
|
-
d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
1197
|
-
}
|
|
1198
|
-
),
|
|
1199
|
-
/* @__PURE__ */ jsx(
|
|
1200
|
-
"path",
|
|
1201
|
-
{
|
|
1202
|
-
fill: "#34A853",
|
|
1203
|
-
d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
1204
|
-
}
|
|
1205
|
-
),
|
|
1206
|
-
/* @__PURE__ */ jsx(
|
|
1207
|
-
"path",
|
|
1208
|
-
{
|
|
1209
|
-
fill: "#FBBC05",
|
|
1210
|
-
d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
1211
|
-
}
|
|
1212
|
-
),
|
|
1213
|
-
/* @__PURE__ */ jsx(
|
|
1214
|
-
"path",
|
|
1215
|
-
{
|
|
1216
|
-
fill: "#EA4335",
|
|
1217
|
-
d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
1218
|
-
}
|
|
1219
|
-
)
|
|
1220
|
-
] });
|
|
1221
|
-
case "github":
|
|
1222
|
-
return /* @__PURE__ */ jsx("svg", { ...common, fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M12 .3a12 12 0 0 0-3.8 23.4c.6.1.8-.26.8-.57v-2c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.75.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.83 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.14-.3-.54-1.52.1-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.64 1.66.24 2.88.12 3.18.77.84 1.23 1.91 1.23 3.22 0 4.61-2.8 5.63-5.48 5.92.43.37.81 1.1.81 2.22v3.29c0 .31.2.68.81.57A12 12 0 0 0 12 .3z" }) });
|
|
1223
|
-
case "apple":
|
|
1224
|
-
return /* @__PURE__ */ jsx("svg", { ...common, fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M17.05 12.75c-.03-2.6 2.12-3.85 2.22-3.91-1.21-1.77-3.09-2.01-3.76-2.04-1.6-.16-3.12.94-3.93.94-.81 0-2.06-.92-3.39-.9-1.74.03-3.35 1.01-4.25 2.57-1.81 3.14-.46 7.79 1.3 10.34.86 1.25 1.89 2.65 3.24 2.6 1.3-.05 1.79-.84 3.36-.84 1.57 0 2.01.84 3.38.81 1.4-.02 2.28-1.27 3.13-2.53.99-1.45 1.4-2.86 1.42-2.93-.03-.01-2.72-1.04-2.75-4.13zM14.5 4.9c.71-.87 1.19-2.07 1.06-3.27-1.02.04-2.26.68-3 1.55-.66.76-1.24 1.98-1.09 3.15 1.14.09 2.31-.58 3.03-1.43z" }) });
|
|
1225
|
-
default:
|
|
1226
|
-
return null;
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
function OAuthButtons({
|
|
1230
|
-
providers,
|
|
1231
|
-
disabled,
|
|
1232
|
-
onSelect
|
|
1233
|
-
}) {
|
|
1234
|
-
const l10n = useLocalization();
|
|
1235
|
-
if (providers.length === 0) return null;
|
|
1236
|
-
return /* @__PURE__ */ jsx("div", { className: "kerne-oauth-group", children: providers.map((provider) => {
|
|
1237
|
-
const label = fill(l10n.common.continueWith, {
|
|
1238
|
-
provider: OAUTH_LABELS[provider] ?? provider
|
|
1239
|
-
});
|
|
1240
|
-
return /* @__PURE__ */ jsxs(
|
|
1241
|
-
"button",
|
|
1242
|
-
{
|
|
1243
|
-
type: "button",
|
|
1244
|
-
className: "kerne-button kerne-button-outline",
|
|
1245
|
-
disabled,
|
|
1246
|
-
onClick: () => onSelect?.(provider),
|
|
1247
|
-
"aria-label": label,
|
|
1248
|
-
children: [
|
|
1249
|
-
/* @__PURE__ */ jsx(OAuthIcon, { provider }),
|
|
1250
|
-
label
|
|
1251
|
-
]
|
|
1252
|
-
},
|
|
1253
|
-
provider
|
|
1254
|
-
);
|
|
1255
|
-
}) });
|
|
1256
|
-
}
|
|
1257
|
-
function KerneBranding({ show }) {
|
|
1258
|
-
const l10n = useLocalization();
|
|
1259
|
-
if (!show) return null;
|
|
1260
|
-
return /* @__PURE__ */ jsx("a", { className: "kerne-branding", href: "https://kerne.io", target: "_blank", rel: "noopener noreferrer", children: l10n.common.securedByKerne });
|
|
1261
|
-
}
|
|
1262
|
-
function FormSkeleton() {
|
|
1263
|
-
const l10n = useLocalization();
|
|
1264
|
-
return /* @__PURE__ */ jsxs("div", { className: "kerne-form", "aria-busy": "true", "aria-label": l10n.common.loading, children: [
|
|
1265
|
-
/* @__PURE__ */ jsx("div", { className: "kerne-skeleton" }),
|
|
1266
|
-
/* @__PURE__ */ jsx("div", { className: "kerne-skeleton" })
|
|
1267
|
-
] });
|
|
50
|
+
const [token, setToken] = useState(override ?? null);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (override) {
|
|
53
|
+
setToken(override);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (typeof window === "undefined") return;
|
|
57
|
+
setToken(new URLSearchParams(window.location.search).get(param) ?? "");
|
|
58
|
+
}, [param, override]);
|
|
59
|
+
return token;
|
|
1268
60
|
}
|
|
1269
61
|
|
|
1270
62
|
// src/ui/LoginForm.tsx
|
|
1271
|
-
import {
|
|
63
|
+
import { useState as useState2 } from "react";
|
|
64
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
1272
65
|
function LoginForm({
|
|
1273
66
|
onSuccess,
|
|
1274
67
|
redirectUrl,
|
|
1275
|
-
magicLinkCallbackUrl,
|
|
1276
68
|
signUpUrl,
|
|
1277
69
|
onSignUpClick,
|
|
1278
70
|
forgotPasswordUrl,
|
|
@@ -1290,13 +82,13 @@ function LoginForm({
|
|
|
1290
82
|
const { config, isLoading: configLoading, error: configError } = useAuthConfig();
|
|
1291
83
|
const l10n = useLocalization();
|
|
1292
84
|
const showError = useErrorResolver();
|
|
1293
|
-
const [email, setEmail] =
|
|
1294
|
-
const [password, setPassword] =
|
|
1295
|
-
const [submitting, setSubmitting] =
|
|
1296
|
-
const [error, setError] =
|
|
1297
|
-
const [linkSentTo, setLinkSentTo] =
|
|
1298
|
-
const [passwordMode, setPasswordMode] =
|
|
1299
|
-
const shell = (children) => /* @__PURE__ */
|
|
85
|
+
const [email, setEmail] = useState2("");
|
|
86
|
+
const [password, setPassword] = useState2("");
|
|
87
|
+
const [submitting, setSubmitting] = useState2(false);
|
|
88
|
+
const [error, setError] = useState2(null);
|
|
89
|
+
const [linkSentTo, setLinkSentTo] = useState2(null);
|
|
90
|
+
const [passwordMode, setPasswordMode] = useState2(false);
|
|
91
|
+
const shell = (children) => /* @__PURE__ */ jsx(Shell, { appearance, className, children });
|
|
1300
92
|
const hasOAuth = (config?.oauth_providers.length ?? 0) > 0;
|
|
1301
93
|
const hasCredentials = config?.enable_credentials ?? false;
|
|
1302
94
|
const hasMagicLink = config?.enable_magic_link ?? false;
|
|
@@ -1315,7 +107,7 @@ function LoginForm({
|
|
|
1315
107
|
onSuccess?.();
|
|
1316
108
|
}
|
|
1317
109
|
} else {
|
|
1318
|
-
await startPasswordless(email
|
|
110
|
+
await startPasswordless(email);
|
|
1319
111
|
setLinkSentTo(email);
|
|
1320
112
|
}
|
|
1321
113
|
} catch (err) {
|
|
@@ -1327,43 +119,43 @@ function LoginForm({
|
|
|
1327
119
|
const heading = title ?? l10n.signIn.title;
|
|
1328
120
|
if (configLoading) {
|
|
1329
121
|
return shell(
|
|
1330
|
-
/* @__PURE__ */
|
|
1331
|
-
/* @__PURE__ */
|
|
1332
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
123
|
+
/* @__PURE__ */ jsx(Masthead, { logo, title: heading }),
|
|
124
|
+
/* @__PURE__ */ jsx(FormSkeleton, {})
|
|
1333
125
|
] })
|
|
1334
126
|
);
|
|
1335
127
|
}
|
|
1336
128
|
if (configError || !config) {
|
|
1337
129
|
return shell(
|
|
1338
|
-
/* @__PURE__ */
|
|
1339
|
-
/* @__PURE__ */
|
|
1340
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
131
|
+
/* @__PURE__ */ jsx(Masthead, { logo, title: heading }),
|
|
132
|
+
/* @__PURE__ */ jsx(Panel, { variant: "danger", children: l10n.signIn.loadFailed })
|
|
1341
133
|
] })
|
|
1342
134
|
);
|
|
1343
135
|
}
|
|
1344
136
|
if (!hasEmailMethod && !hasOAuth) {
|
|
1345
137
|
return shell(
|
|
1346
|
-
/* @__PURE__ */
|
|
1347
|
-
/* @__PURE__ */
|
|
1348
|
-
/* @__PURE__ */
|
|
138
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx(Masthead, { logo, title: heading }),
|
|
140
|
+
/* @__PURE__ */ jsx(Panel, { variant: "danger", children: l10n.signIn.unavailable })
|
|
1349
141
|
] })
|
|
1350
142
|
);
|
|
1351
143
|
}
|
|
1352
144
|
const canSignUp = config.registration_mode !== "CLOSED";
|
|
1353
|
-
const signUpLine = canSignUp && (signUpUrl || onSignUpClick) ? /* @__PURE__ */
|
|
145
|
+
const signUpLine = canSignUp && (signUpUrl || onSignUpClick) ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1354
146
|
l10n.signIn.signUpPrompt,
|
|
1355
147
|
" ",
|
|
1356
|
-
/* @__PURE__ */
|
|
148
|
+
/* @__PURE__ */ jsx(TextLink, { href: signUpUrl, onClick: onSignUpClick, children: appName ? fill(l10n.signIn.signUpForApp, { app: appName }) : l10n.signIn.signUpLink }),
|
|
1357
149
|
"."
|
|
1358
150
|
] }) : void 0;
|
|
1359
151
|
const submitLabel = submitting ? usePassword ? l10n.signIn.submitting : l10n.signIn.magicSubmitting : usePassword ? l10n.signIn.submit : l10n.signIn.magicSubmit;
|
|
1360
152
|
return shell(
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
/* @__PURE__ */
|
|
1363
|
-
/* @__PURE__ */
|
|
1364
|
-
error ? /* @__PURE__ */
|
|
1365
|
-
linkSentTo ? /* @__PURE__ */
|
|
1366
|
-
/* @__PURE__ */
|
|
153
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
154
|
+
/* @__PURE__ */ jsx(Masthead, { logo, title: heading, subtitle: subtitle ?? signUpLine }),
|
|
155
|
+
/* @__PURE__ */ jsxs("div", { className: "kerne-stack", children: [
|
|
156
|
+
error ? /* @__PURE__ */ jsx(Panel, { variant: "danger", children: error }) : null,
|
|
157
|
+
linkSentTo ? /* @__PURE__ */ jsx(Panel, { children: fillNode(l10n.signIn.linkSent, { email: /* @__PURE__ */ jsx("strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ jsxs("form", { className: "kerne-form", onSubmit: handleSubmit, children: [
|
|
158
|
+
/* @__PURE__ */ jsx(
|
|
1367
159
|
Field,
|
|
1368
160
|
{
|
|
1369
161
|
label: l10n.common.email,
|
|
@@ -1377,7 +169,7 @@ function LoginForm({
|
|
|
1377
169
|
autoFocus: true
|
|
1378
170
|
}
|
|
1379
171
|
),
|
|
1380
|
-
usePassword ? /* @__PURE__ */
|
|
172
|
+
usePassword ? /* @__PURE__ */ jsx(
|
|
1381
173
|
Field,
|
|
1382
174
|
{
|
|
1383
175
|
label: l10n.common.password,
|
|
@@ -1388,11 +180,11 @@ function LoginForm({
|
|
|
1388
180
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1389
181
|
disabled: submitting,
|
|
1390
182
|
required: true,
|
|
1391
|
-
action: forgotPasswordUrl || onForgotPasswordClick ? /* @__PURE__ */
|
|
183
|
+
action: forgotPasswordUrl || onForgotPasswordClick ? /* @__PURE__ */ jsx(TextLink, { href: forgotPasswordUrl, onClick: onForgotPasswordClick, children: l10n.signIn.forgotPassword }) : void 0
|
|
1392
184
|
}
|
|
1393
185
|
) : null,
|
|
1394
|
-
/* @__PURE__ */
|
|
1395
|
-
hasCredentials && hasMagicLink ? /* @__PURE__ */
|
|
186
|
+
/* @__PURE__ */ jsx(Button, { loading: submitting, disabled: !email || usePassword && !password, children: submitLabel }),
|
|
187
|
+
hasCredentials && hasMagicLink ? /* @__PURE__ */ jsx(
|
|
1396
188
|
MethodSwitch,
|
|
1397
189
|
{
|
|
1398
190
|
onClick: () => {
|
|
@@ -1403,8 +195,8 @@ function LoginForm({
|
|
|
1403
195
|
}
|
|
1404
196
|
) : null
|
|
1405
197
|
] }) : null,
|
|
1406
|
-
hasOAuth && hasEmailMethod ? /* @__PURE__ */
|
|
1407
|
-
/* @__PURE__ */
|
|
198
|
+
hasOAuth && hasEmailMethod ? /* @__PURE__ */ jsx(Divider, { label: l10n.common.or }) : null,
|
|
199
|
+
/* @__PURE__ */ jsx(
|
|
1408
200
|
OAuthButtons,
|
|
1409
201
|
{
|
|
1410
202
|
providers: config.oauth_providers,
|
|
@@ -1413,19 +205,18 @@ function LoginForm({
|
|
|
1413
205
|
}
|
|
1414
206
|
)
|
|
1415
207
|
] }),
|
|
1416
|
-
legal ? /* @__PURE__ */
|
|
1417
|
-
/* @__PURE__ */
|
|
208
|
+
legal ? /* @__PURE__ */ jsx("p", { className: "kerne-legal", children: legal }) : null,
|
|
209
|
+
/* @__PURE__ */ jsx(KerneBranding, { show: config.show_kerne_branding })
|
|
1418
210
|
] })
|
|
1419
211
|
);
|
|
1420
212
|
}
|
|
1421
213
|
|
|
1422
214
|
// src/ui/RegisterForm.tsx
|
|
1423
|
-
import { useState as
|
|
1424
|
-
import { Fragment as Fragment2, jsx as
|
|
215
|
+
import { useState as useState3 } from "react";
|
|
216
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1425
217
|
function RegisterForm({
|
|
1426
218
|
onSuccess,
|
|
1427
219
|
redirectUrl,
|
|
1428
|
-
magicLinkCallbackUrl,
|
|
1429
220
|
invitationToken: invitationTokenProp,
|
|
1430
221
|
signInUrl,
|
|
1431
222
|
onSignInClick,
|
|
@@ -1445,45 +236,50 @@ function RegisterForm({
|
|
|
1445
236
|
const invitationToken = useUrlToken("token", invitationTokenProp) || void 0;
|
|
1446
237
|
const l10n = useLocalization();
|
|
1447
238
|
const showError = useErrorResolver();
|
|
1448
|
-
const [email, setEmail] =
|
|
1449
|
-
const [password, setPassword] =
|
|
1450
|
-
const [name, setName] =
|
|
1451
|
-
const [invitationCode, setInvitationCode] =
|
|
1452
|
-
const [submitting, setSubmitting] =
|
|
1453
|
-
const [error, setError] =
|
|
1454
|
-
const [linkSentTo, setLinkSentTo] =
|
|
1455
|
-
const [passwordMode, setPasswordMode] =
|
|
1456
|
-
const shell = (children) => /* @__PURE__ */
|
|
239
|
+
const [email, setEmail] = useState3("");
|
|
240
|
+
const [password, setPassword] = useState3("");
|
|
241
|
+
const [name, setName] = useState3("");
|
|
242
|
+
const [invitationCode, setInvitationCode] = useState3("");
|
|
243
|
+
const [submitting, setSubmitting] = useState3(false);
|
|
244
|
+
const [error, setError] = useState3(null);
|
|
245
|
+
const [linkSentTo, setLinkSentTo] = useState3(null);
|
|
246
|
+
const [passwordMode, setPasswordMode] = useState3(false);
|
|
247
|
+
const shell = (children) => /* @__PURE__ */ jsx2(Shell, { appearance, className, children });
|
|
1457
248
|
const appName = config?.app_name;
|
|
1458
249
|
const heading = title ?? (appName ? fill(l10n.signUp.titleWithApp, { app: appName }) : l10n.signUp.title);
|
|
1459
|
-
const
|
|
250
|
+
const invitationNeeded = /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
251
|
+
l10n.signUp.invitationNeeded,
|
|
252
|
+
" ",
|
|
253
|
+
/* @__PURE__ */ jsx2(TextLink, { href: signInUrl, onClick: onSignInClick, children: l10n.signUp.signInLink })
|
|
254
|
+
] });
|
|
255
|
+
const signInLine = signInUrl || onSignInClick ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1460
256
|
l10n.signUp.signInPrompt,
|
|
1461
257
|
" ",
|
|
1462
|
-
/* @__PURE__ */
|
|
258
|
+
/* @__PURE__ */ jsx2(TextLink, { href: signInUrl, onClick: onSignInClick, children: l10n.signUp.signInLink }),
|
|
1463
259
|
"."
|
|
1464
260
|
] }) : void 0;
|
|
1465
261
|
if (configLoading) {
|
|
1466
262
|
return shell(
|
|
1467
|
-
/* @__PURE__ */
|
|
1468
|
-
/* @__PURE__ */
|
|
1469
|
-
/* @__PURE__ */
|
|
263
|
+
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
264
|
+
/* @__PURE__ */ jsx2(Masthead, { logo, title: heading }),
|
|
265
|
+
/* @__PURE__ */ jsx2(FormSkeleton, {})
|
|
1470
266
|
] })
|
|
1471
267
|
);
|
|
1472
268
|
}
|
|
1473
269
|
if (configError || !config) {
|
|
1474
270
|
return shell(
|
|
1475
|
-
/* @__PURE__ */
|
|
1476
|
-
/* @__PURE__ */
|
|
1477
|
-
/* @__PURE__ */
|
|
271
|
+
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
272
|
+
/* @__PURE__ */ jsx2(Masthead, { logo, title: heading }),
|
|
273
|
+
/* @__PURE__ */ jsx2(Panel, { variant: "danger", children: l10n.signUp.loadFailed })
|
|
1478
274
|
] })
|
|
1479
275
|
);
|
|
1480
276
|
}
|
|
1481
277
|
if (config.registration_mode === "CLOSED") {
|
|
1482
278
|
return shell(
|
|
1483
|
-
/* @__PURE__ */
|
|
1484
|
-
/* @__PURE__ */
|
|
1485
|
-
/* @__PURE__ */
|
|
1486
|
-
/* @__PURE__ */
|
|
279
|
+
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
280
|
+
/* @__PURE__ */ jsx2(Masthead, { logo, title: l10n.signUp.closedTitle, subtitle: signInLine }),
|
|
281
|
+
/* @__PURE__ */ jsx2(Panel, { children: fill(l10n.signUp.closedBody, { app: appName ?? l10n.common.thisApp }) }),
|
|
282
|
+
/* @__PURE__ */ jsx2(KerneBranding, { show: config.show_kerne_branding })
|
|
1487
283
|
] })
|
|
1488
284
|
);
|
|
1489
285
|
}
|
|
@@ -1512,7 +308,7 @@ function RegisterForm({
|
|
|
1512
308
|
onSuccess?.();
|
|
1513
309
|
}
|
|
1514
310
|
} else {
|
|
1515
|
-
await startPasswordless(email,
|
|
311
|
+
await startPasswordless(email, {
|
|
1516
312
|
invitationToken,
|
|
1517
313
|
invitationCode: invitationCode || void 0
|
|
1518
314
|
});
|
|
@@ -1525,27 +321,27 @@ function RegisterForm({
|
|
|
1525
321
|
};
|
|
1526
322
|
if (!hasEmailMethod && !hasOAuth) {
|
|
1527
323
|
return shell(
|
|
1528
|
-
/* @__PURE__ */
|
|
1529
|
-
/* @__PURE__ */
|
|
1530
|
-
/* @__PURE__ */
|
|
324
|
+
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
325
|
+
/* @__PURE__ */ jsx2(Masthead, { logo, title: heading }),
|
|
326
|
+
/* @__PURE__ */ jsx2(Panel, { variant: "danger", children: l10n.signUp.unavailable })
|
|
1531
327
|
] })
|
|
1532
328
|
);
|
|
1533
329
|
}
|
|
1534
330
|
const submitLabel = submitting ? usePassword ? l10n.signUp.submitting : l10n.signUp.magicSubmitting : usePassword ? l10n.signUp.submit : l10n.signUp.magicSubmit;
|
|
1535
331
|
return shell(
|
|
1536
|
-
/* @__PURE__ */
|
|
1537
|
-
/* @__PURE__ */
|
|
332
|
+
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
333
|
+
/* @__PURE__ */ jsx2(
|
|
1538
334
|
Masthead,
|
|
1539
335
|
{
|
|
1540
336
|
logo,
|
|
1541
337
|
title: heading,
|
|
1542
|
-
subtitle: subtitle ?? (needsCode ?
|
|
338
|
+
subtitle: subtitle ?? (needsCode ? invitationNeeded : signInLine)
|
|
1543
339
|
}
|
|
1544
340
|
),
|
|
1545
|
-
/* @__PURE__ */
|
|
1546
|
-
error ? /* @__PURE__ */
|
|
1547
|
-
linkSentTo ? /* @__PURE__ */
|
|
1548
|
-
needsCode ? /* @__PURE__ */
|
|
341
|
+
/* @__PURE__ */ jsxs2("div", { className: "kerne-stack", children: [
|
|
342
|
+
error ? /* @__PURE__ */ jsx2(Panel, { variant: "danger", children: error }) : null,
|
|
343
|
+
linkSentTo ? /* @__PURE__ */ jsx2(Panel, { children: fillNode(l10n.signUp.linkSent, { email: /* @__PURE__ */ jsx2("strong", { children: linkSentTo }) }) }) : hasEmailMethod ? /* @__PURE__ */ jsxs2("form", { className: "kerne-form", onSubmit: handleSubmit, children: [
|
|
344
|
+
needsCode ? /* @__PURE__ */ jsx2(
|
|
1549
345
|
Field,
|
|
1550
346
|
{
|
|
1551
347
|
label: l10n.signUp.invitationCode,
|
|
@@ -1560,7 +356,7 @@ function RegisterForm({
|
|
|
1560
356
|
autoFocus: true
|
|
1561
357
|
}
|
|
1562
358
|
) : null,
|
|
1563
|
-
collectName && usePassword ? /* @__PURE__ */
|
|
359
|
+
collectName && usePassword ? /* @__PURE__ */ jsx2(
|
|
1564
360
|
Field,
|
|
1565
361
|
{
|
|
1566
362
|
label: l10n.common.name,
|
|
@@ -1572,7 +368,7 @@ function RegisterForm({
|
|
|
1572
368
|
disabled: submitting
|
|
1573
369
|
}
|
|
1574
370
|
) : null,
|
|
1575
|
-
/* @__PURE__ */
|
|
371
|
+
/* @__PURE__ */ jsx2(
|
|
1576
372
|
Field,
|
|
1577
373
|
{
|
|
1578
374
|
label: l10n.common.email,
|
|
@@ -1586,7 +382,7 @@ function RegisterForm({
|
|
|
1586
382
|
autoFocus: !needsCode
|
|
1587
383
|
}
|
|
1588
384
|
),
|
|
1589
|
-
usePassword ? /* @__PURE__ */
|
|
385
|
+
usePassword ? /* @__PURE__ */ jsx2(
|
|
1590
386
|
Field,
|
|
1591
387
|
{
|
|
1592
388
|
label: l10n.common.password,
|
|
@@ -1597,10 +393,10 @@ function RegisterForm({
|
|
|
1597
393
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1598
394
|
disabled: submitting,
|
|
1599
395
|
required: true,
|
|
1600
|
-
children: /* @__PURE__ */
|
|
396
|
+
children: /* @__PURE__ */ jsx2(PasswordStrength, { password, policy })
|
|
1601
397
|
}
|
|
1602
398
|
) : null,
|
|
1603
|
-
/* @__PURE__ */
|
|
399
|
+
/* @__PURE__ */ jsx2(
|
|
1604
400
|
Button,
|
|
1605
401
|
{
|
|
1606
402
|
loading: submitting,
|
|
@@ -1608,7 +404,7 @@ function RegisterForm({
|
|
|
1608
404
|
children: submitLabel
|
|
1609
405
|
}
|
|
1610
406
|
),
|
|
1611
|
-
hasCredentials && hasMagicLink ? /* @__PURE__ */
|
|
407
|
+
hasCredentials && hasMagicLink ? /* @__PURE__ */ jsx2(
|
|
1612
408
|
MethodSwitch,
|
|
1613
409
|
{
|
|
1614
410
|
onClick: () => {
|
|
@@ -1619,8 +415,8 @@ function RegisterForm({
|
|
|
1619
415
|
}
|
|
1620
416
|
) : null
|
|
1621
417
|
] }) : null,
|
|
1622
|
-
hasOAuth && hasEmailMethod ? /* @__PURE__ */
|
|
1623
|
-
/* @__PURE__ */
|
|
418
|
+
hasOAuth && hasEmailMethod ? /* @__PURE__ */ jsx2(Divider, { label: l10n.common.or }) : null,
|
|
419
|
+
/* @__PURE__ */ jsx2(
|
|
1624
420
|
OAuthButtons,
|
|
1625
421
|
{
|
|
1626
422
|
providers: config.oauth_providers,
|
|
@@ -1629,17 +425,16 @@ function RegisterForm({
|
|
|
1629
425
|
}
|
|
1630
426
|
)
|
|
1631
427
|
] }),
|
|
1632
|
-
legal ? /* @__PURE__ */
|
|
1633
|
-
/* @__PURE__ */
|
|
428
|
+
legal ? /* @__PURE__ */ jsx2("p", { className: "kerne-legal", children: legal }) : null,
|
|
429
|
+
/* @__PURE__ */ jsx2(KerneBranding, { show: config.show_kerne_branding })
|
|
1634
430
|
] })
|
|
1635
431
|
);
|
|
1636
432
|
}
|
|
1637
433
|
|
|
1638
434
|
// src/ui/ForgotPasswordForm.tsx
|
|
1639
|
-
import { useState as
|
|
1640
|
-
import { Fragment as Fragment3, jsx as
|
|
435
|
+
import { useState as useState4 } from "react";
|
|
436
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1641
437
|
function ForgotPasswordForm({
|
|
1642
|
-
callbackUrl,
|
|
1643
438
|
signInUrl,
|
|
1644
439
|
onSignInClick,
|
|
1645
440
|
onSent,
|
|
@@ -1653,16 +448,16 @@ function ForgotPasswordForm({
|
|
|
1653
448
|
const { config } = useAuthConfig();
|
|
1654
449
|
const l10n = useLocalization();
|
|
1655
450
|
const showError = useErrorResolver();
|
|
1656
|
-
const [email, setEmail] =
|
|
1657
|
-
const [submitting, setSubmitting] =
|
|
1658
|
-
const [error, setError] =
|
|
1659
|
-
const [sentTo, setSentTo] =
|
|
451
|
+
const [email, setEmail] = useState4("");
|
|
452
|
+
const [submitting, setSubmitting] = useState4(false);
|
|
453
|
+
const [error, setError] = useState4(null);
|
|
454
|
+
const [sentTo, setSentTo] = useState4(null);
|
|
1660
455
|
const handleSubmit = async (e) => {
|
|
1661
456
|
e.preventDefault();
|
|
1662
457
|
setError(null);
|
|
1663
458
|
setSubmitting(true);
|
|
1664
459
|
try {
|
|
1665
|
-
await requestPasswordReset(email
|
|
460
|
+
await requestPasswordReset(email);
|
|
1666
461
|
setSentTo(email);
|
|
1667
462
|
onSent?.(email);
|
|
1668
463
|
} catch (err) {
|
|
@@ -1671,14 +466,14 @@ function ForgotPasswordForm({
|
|
|
1671
466
|
setSubmitting(false);
|
|
1672
467
|
}
|
|
1673
468
|
};
|
|
1674
|
-
const backLine = signInUrl || onSignInClick ? /* @__PURE__ */
|
|
469
|
+
const backLine = signInUrl || onSignInClick ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1675
470
|
l10n.resetPassword.rememberedPrompt,
|
|
1676
471
|
" ",
|
|
1677
|
-
/* @__PURE__ */
|
|
472
|
+
/* @__PURE__ */ jsx3(TextLink, { href: signInUrl, onClick: onSignInClick, children: l10n.common.backToSignIn }),
|
|
1678
473
|
"."
|
|
1679
474
|
] }) : void 0;
|
|
1680
|
-
return /* @__PURE__ */
|
|
1681
|
-
/* @__PURE__ */
|
|
475
|
+
return /* @__PURE__ */ jsxs3(Shell, { appearance, className, children: [
|
|
476
|
+
/* @__PURE__ */ jsx3(
|
|
1682
477
|
Masthead,
|
|
1683
478
|
{
|
|
1684
479
|
logo,
|
|
@@ -1686,10 +481,10 @@ function ForgotPasswordForm({
|
|
|
1686
481
|
subtitle: subtitle ?? (sentTo ? backLine : l10n.resetPassword.requestSubtitle)
|
|
1687
482
|
}
|
|
1688
483
|
),
|
|
1689
|
-
/* @__PURE__ */
|
|
1690
|
-
error ? /* @__PURE__ */
|
|
1691
|
-
sentTo ? /* @__PURE__ */
|
|
1692
|
-
/* @__PURE__ */
|
|
484
|
+
/* @__PURE__ */ jsxs3("div", { className: "kerne-stack", children: [
|
|
485
|
+
error ? /* @__PURE__ */ jsx3(Panel, { variant: "danger", children: error }) : null,
|
|
486
|
+
sentTo ? /* @__PURE__ */ jsx3(Panel, { children: fillNode(l10n.resetPassword.requestSent, { email: /* @__PURE__ */ jsx3("strong", { children: sentTo }) }) }) : /* @__PURE__ */ jsxs3("form", { className: "kerne-form", onSubmit: handleSubmit, children: [
|
|
487
|
+
/* @__PURE__ */ jsx3(
|
|
1693
488
|
Field,
|
|
1694
489
|
{
|
|
1695
490
|
label: l10n.common.email,
|
|
@@ -1703,17 +498,17 @@ function ForgotPasswordForm({
|
|
|
1703
498
|
autoFocus: true
|
|
1704
499
|
}
|
|
1705
500
|
),
|
|
1706
|
-
/* @__PURE__ */
|
|
1707
|
-
backLine ? /* @__PURE__ */
|
|
501
|
+
/* @__PURE__ */ jsx3(Button, { loading: submitting, disabled: !email, children: submitting ? l10n.resetPassword.requestSubmitting : l10n.resetPassword.requestSubmit }),
|
|
502
|
+
backLine ? /* @__PURE__ */ jsx3("p", { className: "kerne-footer", children: backLine }) : null
|
|
1708
503
|
] })
|
|
1709
504
|
] }),
|
|
1710
|
-
/* @__PURE__ */
|
|
505
|
+
/* @__PURE__ */ jsx3(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
1711
506
|
] });
|
|
1712
507
|
}
|
|
1713
508
|
|
|
1714
509
|
// src/ui/ResetPasswordForm.tsx
|
|
1715
|
-
import { useState as
|
|
1716
|
-
import { jsx as
|
|
510
|
+
import { useState as useState5 } from "react";
|
|
511
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1717
512
|
function ResetPasswordForm({
|
|
1718
513
|
token: tokenProp,
|
|
1719
514
|
signInUrl,
|
|
@@ -1731,14 +526,14 @@ function ResetPasswordForm({
|
|
|
1731
526
|
const token = useUrlToken("token", tokenProp);
|
|
1732
527
|
const l10n = useLocalization();
|
|
1733
528
|
const showError = useErrorResolver();
|
|
1734
|
-
const [password, setPassword] =
|
|
1735
|
-
const [confirm, setConfirm] =
|
|
1736
|
-
const [submitting, setSubmitting] =
|
|
1737
|
-
const [error, setError] =
|
|
1738
|
-
const [done, setDone] =
|
|
529
|
+
const [password, setPassword] = useState5("");
|
|
530
|
+
const [confirm, setConfirm] = useState5("");
|
|
531
|
+
const [submitting, setSubmitting] = useState5(false);
|
|
532
|
+
const [error, setError] = useState5(null);
|
|
533
|
+
const [done, setDone] = useState5(false);
|
|
1739
534
|
const heading = title ?? l10n.resetPassword.title;
|
|
1740
535
|
const mismatch = confirm.length > 0 && password !== confirm;
|
|
1741
|
-
const signInAction = signInUrl || onSignInClick ? /* @__PURE__ */
|
|
536
|
+
const signInAction = signInUrl || onSignInClick ? /* @__PURE__ */ jsx4(
|
|
1742
537
|
Button,
|
|
1743
538
|
{
|
|
1744
539
|
type: "button",
|
|
@@ -1764,31 +559,31 @@ function ResetPasswordForm({
|
|
|
1764
559
|
}
|
|
1765
560
|
};
|
|
1766
561
|
if (token === null) {
|
|
1767
|
-
return /* @__PURE__ */
|
|
1768
|
-
/* @__PURE__ */
|
|
1769
|
-
/* @__PURE__ */
|
|
562
|
+
return /* @__PURE__ */ jsxs4(Shell, { appearance, className, children: [
|
|
563
|
+
/* @__PURE__ */ jsx4(Masthead, { logo, title: heading }),
|
|
564
|
+
/* @__PURE__ */ jsx4(FormSkeleton, {})
|
|
1770
565
|
] });
|
|
1771
566
|
}
|
|
1772
567
|
if (token === "") {
|
|
1773
|
-
return /* @__PURE__ */
|
|
1774
|
-
logo ? /* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
568
|
+
return /* @__PURE__ */ jsxs4(Shell, { appearance, className, children: [
|
|
569
|
+
logo ? /* @__PURE__ */ jsx4("div", { className: "kerne-masthead", children: logo }) : null,
|
|
570
|
+
/* @__PURE__ */ jsx4(Status, { tone: "danger", title: l10n.resetPassword.incompleteTitle, action: signInAction, children: l10n.resetPassword.incompleteBody }),
|
|
571
|
+
/* @__PURE__ */ jsx4(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
1777
572
|
] });
|
|
1778
573
|
}
|
|
1779
574
|
if (done) {
|
|
1780
|
-
return /* @__PURE__ */
|
|
1781
|
-
logo ? /* @__PURE__ */
|
|
1782
|
-
/* @__PURE__ */
|
|
1783
|
-
/* @__PURE__ */
|
|
575
|
+
return /* @__PURE__ */ jsxs4(Shell, { appearance, className, children: [
|
|
576
|
+
logo ? /* @__PURE__ */ jsx4("div", { className: "kerne-masthead", children: logo }) : null,
|
|
577
|
+
/* @__PURE__ */ jsx4(Status, { tone: "success", title: l10n.resetPassword.doneTitle, action: signInAction, children: l10n.resetPassword.doneBody }),
|
|
578
|
+
/* @__PURE__ */ jsx4(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
1784
579
|
] });
|
|
1785
580
|
}
|
|
1786
|
-
return /* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
1788
|
-
/* @__PURE__ */
|
|
1789
|
-
error ? /* @__PURE__ */
|
|
1790
|
-
/* @__PURE__ */
|
|
1791
|
-
/* @__PURE__ */
|
|
581
|
+
return /* @__PURE__ */ jsxs4(Shell, { appearance, className, children: [
|
|
582
|
+
/* @__PURE__ */ jsx4(Masthead, { logo, title: heading, subtitle: subtitle ?? l10n.resetPassword.subtitle }),
|
|
583
|
+
/* @__PURE__ */ jsxs4("div", { className: "kerne-stack", children: [
|
|
584
|
+
error ? /* @__PURE__ */ jsx4(Panel, { variant: "danger", children: error }) : null,
|
|
585
|
+
/* @__PURE__ */ jsxs4("form", { className: "kerne-form", onSubmit: handleSubmit, children: [
|
|
586
|
+
/* @__PURE__ */ jsx4(
|
|
1792
587
|
Field,
|
|
1793
588
|
{
|
|
1794
589
|
label: l10n.resetPassword.newPassword,
|
|
@@ -1800,10 +595,10 @@ function ResetPasswordForm({
|
|
|
1800
595
|
disabled: submitting,
|
|
1801
596
|
required: true,
|
|
1802
597
|
autoFocus: true,
|
|
1803
|
-
children: /* @__PURE__ */
|
|
598
|
+
children: /* @__PURE__ */ jsx4(PasswordStrength, { password, policy })
|
|
1804
599
|
}
|
|
1805
600
|
),
|
|
1806
|
-
/* @__PURE__ */
|
|
601
|
+
/* @__PURE__ */ jsx4(
|
|
1807
602
|
Field,
|
|
1808
603
|
{
|
|
1809
604
|
label: l10n.resetPassword.confirmPassword,
|
|
@@ -1817,7 +612,7 @@ function ResetPasswordForm({
|
|
|
1817
612
|
error: mismatch ? l10n.resetPassword.mismatch : void 0
|
|
1818
613
|
}
|
|
1819
614
|
),
|
|
1820
|
-
/* @__PURE__ */
|
|
615
|
+
/* @__PURE__ */ jsx4(
|
|
1821
616
|
Button,
|
|
1822
617
|
{
|
|
1823
618
|
loading: submitting,
|
|
@@ -1825,16 +620,16 @@ function ResetPasswordForm({
|
|
|
1825
620
|
children: submitting ? l10n.resetPassword.submitting : l10n.resetPassword.submit
|
|
1826
621
|
}
|
|
1827
622
|
),
|
|
1828
|
-
signInUrl || onSignInClick ? /* @__PURE__ */
|
|
623
|
+
signInUrl || onSignInClick ? /* @__PURE__ */ jsx4("p", { className: "kerne-footer", children: /* @__PURE__ */ jsx4(TextLink, { href: signInUrl, onClick: onSignInClick, children: l10n.common.backToSignIn }) }) : null
|
|
1829
624
|
] })
|
|
1830
625
|
] }),
|
|
1831
|
-
/* @__PURE__ */
|
|
626
|
+
/* @__PURE__ */ jsx4(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
1832
627
|
] });
|
|
1833
628
|
}
|
|
1834
629
|
|
|
1835
630
|
// src/ui/MagicLinkCallback.tsx
|
|
1836
|
-
import { useEffect as useEffect2, useRef, useState as
|
|
1837
|
-
import { jsx as
|
|
631
|
+
import { useEffect as useEffect2, useRef, useState as useState6 } from "react";
|
|
632
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1838
633
|
function MagicLinkCallback({
|
|
1839
634
|
token: tokenProp,
|
|
1840
635
|
onSuccess,
|
|
@@ -1851,8 +646,8 @@ function MagicLinkCallback({
|
|
|
1851
646
|
const token = useUrlToken("token", tokenProp);
|
|
1852
647
|
const l10n = useLocalization();
|
|
1853
648
|
const showError = useErrorResolver();
|
|
1854
|
-
const [state, setState] =
|
|
1855
|
-
const [message, setMessage] =
|
|
649
|
+
const [state, setState] = useState6("pending");
|
|
650
|
+
const [message, setMessage] = useState6(null);
|
|
1856
651
|
const consumed = useRef(false);
|
|
1857
652
|
useEffect2(() => {
|
|
1858
653
|
if (token === null || consumed.current) return;
|
|
@@ -1873,7 +668,7 @@ function MagicLinkCallback({
|
|
|
1873
668
|
setMessage(showError(e));
|
|
1874
669
|
});
|
|
1875
670
|
}, [token, loginWithMagicLink, redirectUrl, client, onSuccess]);
|
|
1876
|
-
const retry = signInUrl || onSignInClick ? /* @__PURE__ */
|
|
671
|
+
const retry = signInUrl || onSignInClick ? /* @__PURE__ */ jsx5(
|
|
1877
672
|
Button,
|
|
1878
673
|
{
|
|
1879
674
|
variant: "outline",
|
|
@@ -1884,19 +679,18 @@ function MagicLinkCallback({
|
|
|
1884
679
|
children: l10n.common.backToSignIn
|
|
1885
680
|
}
|
|
1886
681
|
) : void 0;
|
|
1887
|
-
return /* @__PURE__ */
|
|
1888
|
-
logo ? /* @__PURE__ */
|
|
1889
|
-
state === "pending" ? /* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
682
|
+
return /* @__PURE__ */ jsxs5(Shell, { appearance, className, children: [
|
|
683
|
+
logo ? /* @__PURE__ */ jsx5("div", { className: "kerne-masthead", children: logo }) : null,
|
|
684
|
+
state === "pending" ? /* @__PURE__ */ jsx5(Status, { tone: "loading", title: l10n.magicLink.pendingTitle, children: l10n.magicLink.pendingBody }) : state === "done" ? /* @__PURE__ */ jsx5(Status, { tone: "success", title: l10n.magicLink.doneTitle, children: l10n.magicLink.doneBody }) : /* @__PURE__ */ jsx5(Status, { tone: "danger", title: l10n.magicLink.failedTitle, action: retry, children: message ?? l10n.magicLink.missingToken }),
|
|
685
|
+
/* @__PURE__ */ jsx5(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
1891
686
|
] });
|
|
1892
687
|
}
|
|
1893
688
|
|
|
1894
689
|
// src/ui/VerifyEmailForm.tsx
|
|
1895
|
-
import { useEffect as useEffect3, useRef as useRef2, useState as
|
|
1896
|
-
import { jsx as
|
|
690
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState7 } from "react";
|
|
691
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1897
692
|
function VerifyEmailForm({
|
|
1898
693
|
token: tokenProp,
|
|
1899
|
-
callbackUrl,
|
|
1900
694
|
onVerified,
|
|
1901
695
|
title,
|
|
1902
696
|
subtitle,
|
|
@@ -1909,12 +703,12 @@ function VerifyEmailForm({
|
|
|
1909
703
|
const token = useUrlToken("token", tokenProp);
|
|
1910
704
|
const l10n = useLocalization();
|
|
1911
705
|
const showError = useErrorResolver();
|
|
1912
|
-
const [code, setCode] =
|
|
1913
|
-
const [submitting, setSubmitting] =
|
|
1914
|
-
const [error, setError] =
|
|
1915
|
-
const [resent, setResent] =
|
|
1916
|
-
const [linkState, setLinkState] =
|
|
1917
|
-
const [verified, setVerified] =
|
|
706
|
+
const [code, setCode] = useState7("");
|
|
707
|
+
const [submitting, setSubmitting] = useState7(false);
|
|
708
|
+
const [error, setError] = useState7(null);
|
|
709
|
+
const [resent, setResent] = useState7(false);
|
|
710
|
+
const [linkState, setLinkState] = useState7("idle");
|
|
711
|
+
const [verified, setVerified] = useState7(false);
|
|
1918
712
|
const consumed = useRef2(false);
|
|
1919
713
|
const heading = title ?? l10n.verifyEmail.title;
|
|
1920
714
|
const settle = async (needsTokenRefresh) => {
|
|
@@ -1952,48 +746,48 @@ function VerifyEmailForm({
|
|
|
1952
746
|
const handleResend = async () => {
|
|
1953
747
|
setError(null);
|
|
1954
748
|
try {
|
|
1955
|
-
await sendVerificationEmail("code"
|
|
749
|
+
await sendVerificationEmail("code");
|
|
1956
750
|
setResent(true);
|
|
1957
751
|
} catch (err) {
|
|
1958
752
|
setError(showError(err));
|
|
1959
753
|
}
|
|
1960
754
|
};
|
|
1961
755
|
if (verified) {
|
|
1962
|
-
return /* @__PURE__ */
|
|
1963
|
-
logo ? /* @__PURE__ */
|
|
1964
|
-
/* @__PURE__ */
|
|
1965
|
-
/* @__PURE__ */
|
|
756
|
+
return /* @__PURE__ */ jsxs6(Shell, { appearance, className, children: [
|
|
757
|
+
logo ? /* @__PURE__ */ jsx6("div", { className: "kerne-masthead", children: logo }) : null,
|
|
758
|
+
/* @__PURE__ */ jsx6(Status, { tone: "success", title: l10n.verifyEmail.doneTitle, children: l10n.verifyEmail.doneBody }),
|
|
759
|
+
/* @__PURE__ */ jsx6(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
1966
760
|
] });
|
|
1967
761
|
}
|
|
1968
762
|
if (linkState === "pending") {
|
|
1969
|
-
return /* @__PURE__ */
|
|
1970
|
-
logo ? /* @__PURE__ */
|
|
1971
|
-
/* @__PURE__ */
|
|
763
|
+
return /* @__PURE__ */ jsxs6(Shell, { appearance, className, children: [
|
|
764
|
+
logo ? /* @__PURE__ */ jsx6("div", { className: "kerne-masthead", children: logo }) : null,
|
|
765
|
+
/* @__PURE__ */ jsx6(Status, { tone: "loading", title: l10n.verifyEmail.verifyingTitle, children: l10n.verifyEmail.verifyingBody })
|
|
1972
766
|
] });
|
|
1973
767
|
}
|
|
1974
768
|
if (linkState === "failed") {
|
|
1975
|
-
return /* @__PURE__ */
|
|
1976
|
-
logo ? /* @__PURE__ */
|
|
1977
|
-
/* @__PURE__ */
|
|
769
|
+
return /* @__PURE__ */ jsxs6(Shell, { appearance, className, children: [
|
|
770
|
+
logo ? /* @__PURE__ */ jsx6("div", { className: "kerne-masthead", children: logo }) : null,
|
|
771
|
+
/* @__PURE__ */ jsx6(
|
|
1978
772
|
Status,
|
|
1979
773
|
{
|
|
1980
774
|
tone: "danger",
|
|
1981
775
|
title: l10n.verifyEmail.failedTitle,
|
|
1982
|
-
action: /* @__PURE__ */
|
|
776
|
+
action: /* @__PURE__ */ jsx6(Button, { variant: "outline", type: "button", onClick: handleResend, children: l10n.verifyEmail.newCode }),
|
|
1983
777
|
children: error
|
|
1984
778
|
}
|
|
1985
779
|
),
|
|
1986
|
-
resent ? /* @__PURE__ */
|
|
1987
|
-
/* @__PURE__ */
|
|
780
|
+
resent ? /* @__PURE__ */ jsx6(Panel, { variant: "success", children: l10n.verifyEmail.resent }) : null,
|
|
781
|
+
/* @__PURE__ */ jsx6(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
1988
782
|
] });
|
|
1989
783
|
}
|
|
1990
|
-
return /* @__PURE__ */
|
|
1991
|
-
/* @__PURE__ */
|
|
1992
|
-
/* @__PURE__ */
|
|
1993
|
-
error ? /* @__PURE__ */
|
|
1994
|
-
resent ? /* @__PURE__ */
|
|
1995
|
-
/* @__PURE__ */
|
|
1996
|
-
/* @__PURE__ */
|
|
784
|
+
return /* @__PURE__ */ jsxs6(Shell, { appearance, className, children: [
|
|
785
|
+
/* @__PURE__ */ jsx6(Masthead, { logo, title: heading, subtitle: subtitle ?? l10n.verifyEmail.subtitle }),
|
|
786
|
+
/* @__PURE__ */ jsxs6("div", { className: "kerne-stack", children: [
|
|
787
|
+
error ? /* @__PURE__ */ jsx6(Panel, { variant: "danger", children: error }) : null,
|
|
788
|
+
resent ? /* @__PURE__ */ jsx6(Panel, { variant: "success", children: l10n.verifyEmail.resent }) : null,
|
|
789
|
+
/* @__PURE__ */ jsxs6("form", { className: "kerne-form", onSubmit: handleSubmit, children: [
|
|
790
|
+
/* @__PURE__ */ jsx6(
|
|
1997
791
|
Field,
|
|
1998
792
|
{
|
|
1999
793
|
label: l10n.verifyEmail.code,
|
|
@@ -2008,17 +802,17 @@ function VerifyEmailForm({
|
|
|
2008
802
|
autoFocus: true
|
|
2009
803
|
}
|
|
2010
804
|
),
|
|
2011
|
-
/* @__PURE__ */
|
|
2012
|
-
/* @__PURE__ */
|
|
805
|
+
/* @__PURE__ */ jsx6(Button, { loading: submitting, disabled: !code, children: submitting ? l10n.verifyEmail.submitting : l10n.verifyEmail.submit }),
|
|
806
|
+
/* @__PURE__ */ jsx6(MethodSwitch, { onClick: handleResend, children: l10n.verifyEmail.resend })
|
|
2013
807
|
] })
|
|
2014
808
|
] }),
|
|
2015
|
-
/* @__PURE__ */
|
|
809
|
+
/* @__PURE__ */ jsx6(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
2016
810
|
] });
|
|
2017
811
|
}
|
|
2018
812
|
|
|
2019
813
|
// src/ui/ActivateAccountForm.tsx
|
|
2020
|
-
import { useEffect as useEffect4, useState as
|
|
2021
|
-
import { Fragment as Fragment4, jsx as
|
|
814
|
+
import { useEffect as useEffect4, useState as useState8 } from "react";
|
|
815
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2022
816
|
function ActivateAccountForm({
|
|
2023
817
|
token: tokenProp,
|
|
2024
818
|
onSuccess,
|
|
@@ -2037,12 +831,12 @@ function ActivateAccountForm({
|
|
|
2037
831
|
const token = useUrlToken("token", tokenProp);
|
|
2038
832
|
const l10n = useLocalization();
|
|
2039
833
|
const showError = useErrorResolver();
|
|
2040
|
-
const [context, setContext] =
|
|
2041
|
-
const [loadError, setLoadError] =
|
|
2042
|
-
const [password, setPassword] =
|
|
2043
|
-
const [confirm, setConfirm] =
|
|
2044
|
-
const [submitting, setSubmitting] =
|
|
2045
|
-
const [error, setError] =
|
|
834
|
+
const [context, setContext] = useState8(null);
|
|
835
|
+
const [loadError, setLoadError] = useState8(null);
|
|
836
|
+
const [password, setPassword] = useState8("");
|
|
837
|
+
const [confirm, setConfirm] = useState8("");
|
|
838
|
+
const [submitting, setSubmitting] = useState8(false);
|
|
839
|
+
const [error, setError] = useState8(null);
|
|
2046
840
|
const heading = title ?? l10n.activate.title;
|
|
2047
841
|
const mismatch = confirm.length > 0 && password !== confirm;
|
|
2048
842
|
useEffect4(() => {
|
|
@@ -2053,7 +847,7 @@ function ActivateAccountForm({
|
|
|
2053
847
|
mounted = false;
|
|
2054
848
|
};
|
|
2055
849
|
}, [token, getActivationContext]);
|
|
2056
|
-
const signInAction = signInUrl || onSignInClick ? /* @__PURE__ */
|
|
850
|
+
const signInAction = signInUrl || onSignInClick ? /* @__PURE__ */ jsx7(
|
|
2057
851
|
Button,
|
|
2058
852
|
{
|
|
2059
853
|
type: "button",
|
|
@@ -2081,42 +875,42 @@ function ActivateAccountForm({
|
|
|
2081
875
|
}
|
|
2082
876
|
};
|
|
2083
877
|
if (token === null) {
|
|
2084
|
-
return /* @__PURE__ */
|
|
2085
|
-
/* @__PURE__ */
|
|
2086
|
-
/* @__PURE__ */
|
|
878
|
+
return /* @__PURE__ */ jsxs7(Shell, { appearance, className, children: [
|
|
879
|
+
/* @__PURE__ */ jsx7(Masthead, { logo, title: heading }),
|
|
880
|
+
/* @__PURE__ */ jsx7(FormSkeleton, {})
|
|
2087
881
|
] });
|
|
2088
882
|
}
|
|
2089
883
|
if (token === "" || loadError) {
|
|
2090
|
-
return /* @__PURE__ */
|
|
2091
|
-
logo ? /* @__PURE__ */
|
|
2092
|
-
/* @__PURE__ */
|
|
2093
|
-
/* @__PURE__ */
|
|
884
|
+
return /* @__PURE__ */ jsxs7(Shell, { appearance, className, children: [
|
|
885
|
+
logo ? /* @__PURE__ */ jsx7("div", { className: "kerne-masthead", children: logo }) : null,
|
|
886
|
+
/* @__PURE__ */ jsx7(Status, { tone: "danger", title: l10n.activate.failedTitle, action: signInAction, children: loadError ?? l10n.activate.missingToken }),
|
|
887
|
+
/* @__PURE__ */ jsx7(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
2094
888
|
] });
|
|
2095
889
|
}
|
|
2096
890
|
if (!context) {
|
|
2097
|
-
return /* @__PURE__ */
|
|
2098
|
-
/* @__PURE__ */
|
|
2099
|
-
/* @__PURE__ */
|
|
891
|
+
return /* @__PURE__ */ jsxs7(Shell, { appearance, className, children: [
|
|
892
|
+
/* @__PURE__ */ jsx7(Masthead, { logo, title: heading }),
|
|
893
|
+
/* @__PURE__ */ jsx7(FormSkeleton, {})
|
|
2100
894
|
] });
|
|
2101
895
|
}
|
|
2102
896
|
const credentialsMode = context.mode === "credentials";
|
|
2103
|
-
return /* @__PURE__ */
|
|
2104
|
-
/* @__PURE__ */
|
|
897
|
+
return /* @__PURE__ */ jsxs7(Shell, { appearance, className, children: [
|
|
898
|
+
/* @__PURE__ */ jsx7(
|
|
2105
899
|
Masthead,
|
|
2106
900
|
{
|
|
2107
901
|
logo,
|
|
2108
902
|
title: heading,
|
|
2109
903
|
subtitle: fillNode(
|
|
2110
904
|
credentialsMode ? l10n.activate.credentialsSubtitle : l10n.activate.magicSubtitle,
|
|
2111
|
-
{ email: /* @__PURE__ */
|
|
905
|
+
{ email: /* @__PURE__ */ jsx7("strong", { children: context.email }) }
|
|
2112
906
|
)
|
|
2113
907
|
}
|
|
2114
908
|
),
|
|
2115
|
-
/* @__PURE__ */
|
|
2116
|
-
error ? /* @__PURE__ */
|
|
2117
|
-
/* @__PURE__ */
|
|
2118
|
-
credentialsMode ? /* @__PURE__ */
|
|
2119
|
-
/* @__PURE__ */
|
|
909
|
+
/* @__PURE__ */ jsxs7("div", { className: "kerne-stack", children: [
|
|
910
|
+
error ? /* @__PURE__ */ jsx7(Panel, { variant: "danger", children: error }) : null,
|
|
911
|
+
/* @__PURE__ */ jsxs7("form", { className: "kerne-form", onSubmit: handleSubmit, children: [
|
|
912
|
+
credentialsMode ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
913
|
+
/* @__PURE__ */ jsx7(
|
|
2120
914
|
Field,
|
|
2121
915
|
{
|
|
2122
916
|
label: l10n.common.password,
|
|
@@ -2128,10 +922,10 @@ function ActivateAccountForm({
|
|
|
2128
922
|
disabled: submitting,
|
|
2129
923
|
required: true,
|
|
2130
924
|
autoFocus: true,
|
|
2131
|
-
children: /* @__PURE__ */
|
|
925
|
+
children: /* @__PURE__ */ jsx7(PasswordStrength, { password, policy })
|
|
2132
926
|
}
|
|
2133
927
|
),
|
|
2134
|
-
/* @__PURE__ */
|
|
928
|
+
/* @__PURE__ */ jsx7(
|
|
2135
929
|
Field,
|
|
2136
930
|
{
|
|
2137
931
|
label: l10n.activate.confirmPassword,
|
|
@@ -2146,7 +940,7 @@ function ActivateAccountForm({
|
|
|
2146
940
|
}
|
|
2147
941
|
)
|
|
2148
942
|
] }) : null,
|
|
2149
|
-
/* @__PURE__ */
|
|
943
|
+
/* @__PURE__ */ jsx7(
|
|
2150
944
|
Button,
|
|
2151
945
|
{
|
|
2152
946
|
loading: submitting,
|
|
@@ -2156,12 +950,12 @@ function ActivateAccountForm({
|
|
|
2156
950
|
)
|
|
2157
951
|
] })
|
|
2158
952
|
] }),
|
|
2159
|
-
/* @__PURE__ */
|
|
953
|
+
/* @__PURE__ */ jsx7(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
2160
954
|
] });
|
|
2161
955
|
}
|
|
2162
956
|
|
|
2163
957
|
// src/ui/AuthFlow.tsx
|
|
2164
|
-
import { jsx as
|
|
958
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2165
959
|
var TOKEN_STEPS = ["reset-password", "magic-link", "activate", "verify-email"];
|
|
2166
960
|
function AuthFlow({
|
|
2167
961
|
initialStep,
|
|
@@ -2169,9 +963,6 @@ function AuthFlow({
|
|
|
2169
963
|
onStepChange,
|
|
2170
964
|
onSuccess,
|
|
2171
965
|
redirectUrl,
|
|
2172
|
-
magicLinkCallbackUrl,
|
|
2173
|
-
resetPasswordCallbackUrl,
|
|
2174
|
-
verificationCallbackUrl,
|
|
2175
966
|
allowSignUp = true,
|
|
2176
967
|
legal,
|
|
2177
968
|
appearance,
|
|
@@ -2180,7 +971,7 @@ function AuthFlow({
|
|
|
2180
971
|
}) {
|
|
2181
972
|
const { config } = useAuthConfig();
|
|
2182
973
|
const token = useUrlToken("token");
|
|
2183
|
-
const [internalStep, setInternalStep] =
|
|
974
|
+
const [internalStep, setInternalStep] = useState9(null);
|
|
2184
975
|
const go = (next) => {
|
|
2185
976
|
if (controlledStep === void 0) setInternalStep(next);
|
|
2186
977
|
onStepChange?.(next);
|
|
@@ -2195,30 +986,22 @@ function AuthFlow({
|
|
|
2195
986
|
};
|
|
2196
987
|
switch (step) {
|
|
2197
988
|
case "sign-up":
|
|
2198
|
-
return /* @__PURE__ */
|
|
989
|
+
return /* @__PURE__ */ jsx8(
|
|
2199
990
|
RegisterForm,
|
|
2200
991
|
{
|
|
2201
992
|
...shared,
|
|
2202
993
|
onSuccess,
|
|
2203
994
|
redirectUrl,
|
|
2204
|
-
magicLinkCallbackUrl,
|
|
2205
995
|
onSignInClick: () => go("sign-in"),
|
|
2206
996
|
legal
|
|
2207
997
|
}
|
|
2208
998
|
);
|
|
2209
999
|
case "forgot-password":
|
|
2210
|
-
return /* @__PURE__ */
|
|
2211
|
-
ForgotPasswordForm,
|
|
2212
|
-
{
|
|
2213
|
-
...shared,
|
|
2214
|
-
callbackUrl: resetPasswordCallbackUrl,
|
|
2215
|
-
onSignInClick: () => go("sign-in")
|
|
2216
|
-
}
|
|
2217
|
-
);
|
|
1000
|
+
return /* @__PURE__ */ jsx8(ForgotPasswordForm, { ...shared, onSignInClick: () => go("sign-in") });
|
|
2218
1001
|
case "reset-password":
|
|
2219
|
-
return /* @__PURE__ */
|
|
1002
|
+
return /* @__PURE__ */ jsx8(ResetPasswordForm, { ...shared, onSignInClick: () => go("sign-in"), onSuccess });
|
|
2220
1003
|
case "magic-link":
|
|
2221
|
-
return /* @__PURE__ */
|
|
1004
|
+
return /* @__PURE__ */ jsx8(
|
|
2222
1005
|
MagicLinkCallback,
|
|
2223
1006
|
{
|
|
2224
1007
|
...shared,
|
|
@@ -2228,9 +1011,9 @@ function AuthFlow({
|
|
|
2228
1011
|
}
|
|
2229
1012
|
);
|
|
2230
1013
|
case "verify-email":
|
|
2231
|
-
return /* @__PURE__ */
|
|
1014
|
+
return /* @__PURE__ */ jsx8(VerifyEmailForm, { ...shared, onVerified: onSuccess });
|
|
2232
1015
|
case "activate":
|
|
2233
|
-
return /* @__PURE__ */
|
|
1016
|
+
return /* @__PURE__ */ jsx8(
|
|
2234
1017
|
ActivateAccountForm,
|
|
2235
1018
|
{
|
|
2236
1019
|
...shared,
|
|
@@ -2241,13 +1024,12 @@ function AuthFlow({
|
|
|
2241
1024
|
);
|
|
2242
1025
|
case "sign-in":
|
|
2243
1026
|
default:
|
|
2244
|
-
return /* @__PURE__ */
|
|
1027
|
+
return /* @__PURE__ */ jsx8(
|
|
2245
1028
|
LoginForm,
|
|
2246
1029
|
{
|
|
2247
1030
|
...shared,
|
|
2248
1031
|
onSuccess,
|
|
2249
1032
|
redirectUrl,
|
|
2250
|
-
magicLinkCallbackUrl,
|
|
2251
1033
|
onSignUpClick: canSignUp ? () => go("sign-up") : void 0,
|
|
2252
1034
|
onForgotPasswordClick: () => go("forgot-password"),
|
|
2253
1035
|
legal
|
|
@@ -2257,8 +1039,8 @@ function AuthFlow({
|
|
|
2257
1039
|
}
|
|
2258
1040
|
|
|
2259
1041
|
// src/ui/ChangePasswordForm.tsx
|
|
2260
|
-
import { useState as
|
|
2261
|
-
import { jsx as
|
|
1042
|
+
import { useState as useState10 } from "react";
|
|
1043
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2262
1044
|
function ChangePasswordForm({
|
|
2263
1045
|
onSuccess,
|
|
2264
1046
|
title,
|
|
@@ -2271,12 +1053,12 @@ function ChangePasswordForm({
|
|
|
2271
1053
|
const l10n = useLocalization();
|
|
2272
1054
|
const showError = useErrorResolver();
|
|
2273
1055
|
const heading = title === null ? null : title ?? l10n.security.submit;
|
|
2274
|
-
const [currentPassword, setCurrentPassword] =
|
|
2275
|
-
const [newPassword, setNewPassword] =
|
|
2276
|
-
const [confirm, setConfirm] =
|
|
2277
|
-
const [submitting, setSubmitting] =
|
|
2278
|
-
const [error, setError] =
|
|
2279
|
-
const [done, setDone] =
|
|
1056
|
+
const [currentPassword, setCurrentPassword] = useState10("");
|
|
1057
|
+
const [newPassword, setNewPassword] = useState10("");
|
|
1058
|
+
const [confirm, setConfirm] = useState10("");
|
|
1059
|
+
const [submitting, setSubmitting] = useState10(false);
|
|
1060
|
+
const [error, setError] = useState10(null);
|
|
1061
|
+
const [done, setDone] = useState10(false);
|
|
2280
1062
|
const mismatch = confirm.length > 0 && newPassword !== confirm;
|
|
2281
1063
|
const unchanged = newPassword.length > 0 && newPassword === currentPassword;
|
|
2282
1064
|
const handleSubmit = async (e) => {
|
|
@@ -2298,13 +1080,13 @@ function ChangePasswordForm({
|
|
|
2298
1080
|
setSubmitting(false);
|
|
2299
1081
|
}
|
|
2300
1082
|
};
|
|
2301
|
-
return /* @__PURE__ */
|
|
2302
|
-
heading ? /* @__PURE__ */
|
|
2303
|
-
/* @__PURE__ */
|
|
2304
|
-
error ? /* @__PURE__ */
|
|
2305
|
-
done ? /* @__PURE__ */
|
|
2306
|
-
/* @__PURE__ */
|
|
2307
|
-
/* @__PURE__ */
|
|
1083
|
+
return /* @__PURE__ */ jsxs8(Shell, { appearance, className, children: [
|
|
1084
|
+
heading ? /* @__PURE__ */ jsx9("div", { className: "kerne-heading", style: { marginBottom: 20 }, children: /* @__PURE__ */ jsx9("h1", { className: "kerne-title", children: heading }) }) : null,
|
|
1085
|
+
/* @__PURE__ */ jsxs8("div", { className: "kerne-stack", children: [
|
|
1086
|
+
error ? /* @__PURE__ */ jsx9(Panel, { variant: "danger", children: error }) : null,
|
|
1087
|
+
done ? /* @__PURE__ */ jsx9(Panel, { variant: "success", children: l10n.security.changed }) : null,
|
|
1088
|
+
/* @__PURE__ */ jsxs8("form", { className: "kerne-form", onSubmit: handleSubmit, children: [
|
|
1089
|
+
/* @__PURE__ */ jsx9(
|
|
2308
1090
|
Field,
|
|
2309
1091
|
{
|
|
2310
1092
|
label: l10n.security.currentPassword,
|
|
@@ -2317,7 +1099,7 @@ function ChangePasswordForm({
|
|
|
2317
1099
|
required: true
|
|
2318
1100
|
}
|
|
2319
1101
|
),
|
|
2320
|
-
/* @__PURE__ */
|
|
1102
|
+
/* @__PURE__ */ jsx9(
|
|
2321
1103
|
Field,
|
|
2322
1104
|
{
|
|
2323
1105
|
label: l10n.security.newPassword,
|
|
@@ -2329,10 +1111,10 @@ function ChangePasswordForm({
|
|
|
2329
1111
|
disabled: submitting,
|
|
2330
1112
|
required: true,
|
|
2331
1113
|
error: unchanged ? l10n.security.sameAsCurrent : void 0,
|
|
2332
|
-
children: /* @__PURE__ */
|
|
1114
|
+
children: /* @__PURE__ */ jsx9(PasswordStrength, { password: newPassword, policy })
|
|
2333
1115
|
}
|
|
2334
1116
|
),
|
|
2335
|
-
/* @__PURE__ */
|
|
1117
|
+
/* @__PURE__ */ jsx9(
|
|
2336
1118
|
Field,
|
|
2337
1119
|
{
|
|
2338
1120
|
label: l10n.security.confirmPassword,
|
|
@@ -2346,7 +1128,7 @@ function ChangePasswordForm({
|
|
|
2346
1128
|
error: mismatch ? l10n.security.mismatch : void 0
|
|
2347
1129
|
}
|
|
2348
1130
|
),
|
|
2349
|
-
/* @__PURE__ */
|
|
1131
|
+
/* @__PURE__ */ jsx9(
|
|
2350
1132
|
Button,
|
|
2351
1133
|
{
|
|
2352
1134
|
loading: submitting,
|
|
@@ -2360,8 +1142,8 @@ function ChangePasswordForm({
|
|
|
2360
1142
|
}
|
|
2361
1143
|
|
|
2362
1144
|
// src/ui/WaitlistForm.tsx
|
|
2363
|
-
import { useState as
|
|
2364
|
-
import { jsx as
|
|
1145
|
+
import { useState as useState11 } from "react";
|
|
1146
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2365
1147
|
function WaitlistForm({
|
|
2366
1148
|
title,
|
|
2367
1149
|
subtitle,
|
|
@@ -2379,11 +1161,11 @@ function WaitlistForm({
|
|
|
2379
1161
|
const { config } = useAuthConfig();
|
|
2380
1162
|
const l10n = useLocalization();
|
|
2381
1163
|
const showError = useErrorResolver();
|
|
2382
|
-
const [email, setEmail] =
|
|
2383
|
-
const [name, setName] =
|
|
2384
|
-
const [submitting, setSubmitting] =
|
|
2385
|
-
const [error, setError] =
|
|
2386
|
-
const [joined, setJoined] =
|
|
1164
|
+
const [email, setEmail] = useState11("");
|
|
1165
|
+
const [name, setName] = useState11("");
|
|
1166
|
+
const [submitting, setSubmitting] = useState11(false);
|
|
1167
|
+
const [error, setError] = useState11(null);
|
|
1168
|
+
const [joined, setJoined] = useState11(false);
|
|
2387
1169
|
const handleSubmit = async (e) => {
|
|
2388
1170
|
e.preventDefault();
|
|
2389
1171
|
setError(null);
|
|
@@ -2402,8 +1184,8 @@ function WaitlistForm({
|
|
|
2402
1184
|
setSubmitting(false);
|
|
2403
1185
|
}
|
|
2404
1186
|
};
|
|
2405
|
-
return /* @__PURE__ */
|
|
2406
|
-
/* @__PURE__ */
|
|
1187
|
+
return /* @__PURE__ */ jsxs9(Shell, { appearance, className, children: [
|
|
1188
|
+
/* @__PURE__ */ jsx10(
|
|
2407
1189
|
Masthead,
|
|
2408
1190
|
{
|
|
2409
1191
|
logo,
|
|
@@ -2411,10 +1193,10 @@ function WaitlistForm({
|
|
|
2411
1193
|
subtitle: joined ? void 0 : subtitle ?? l10n.waitlist.subtitle
|
|
2412
1194
|
}
|
|
2413
1195
|
),
|
|
2414
|
-
/* @__PURE__ */
|
|
2415
|
-
error ? /* @__PURE__ */
|
|
2416
|
-
joined ? /* @__PURE__ */
|
|
2417
|
-
collectName ? /* @__PURE__ */
|
|
1196
|
+
/* @__PURE__ */ jsxs9("div", { className: "kerne-stack", children: [
|
|
1197
|
+
error ? /* @__PURE__ */ jsx10(Panel, { variant: "danger", children: error }) : null,
|
|
1198
|
+
joined ? /* @__PURE__ */ jsx10(Panel, { variant: "success", children: successMessage ?? l10n.waitlist.joined }) : /* @__PURE__ */ jsxs9("form", { className: "kerne-form", onSubmit: handleSubmit, children: [
|
|
1199
|
+
collectName ? /* @__PURE__ */ jsx10(
|
|
2418
1200
|
Field,
|
|
2419
1201
|
{
|
|
2420
1202
|
label: l10n.common.name,
|
|
@@ -2426,7 +1208,7 @@ function WaitlistForm({
|
|
|
2426
1208
|
disabled: submitting
|
|
2427
1209
|
}
|
|
2428
1210
|
) : null,
|
|
2429
|
-
/* @__PURE__ */
|
|
1211
|
+
/* @__PURE__ */ jsx10(
|
|
2430
1212
|
Field,
|
|
2431
1213
|
{
|
|
2432
1214
|
label: l10n.common.email,
|
|
@@ -2440,17 +1222,17 @@ function WaitlistForm({
|
|
|
2440
1222
|
autoFocus: true
|
|
2441
1223
|
}
|
|
2442
1224
|
),
|
|
2443
|
-
/* @__PURE__ */
|
|
1225
|
+
/* @__PURE__ */ jsx10(Button, { loading: submitting, disabled: !email, children: submitting ? l10n.waitlist.submitting : submitLabel ?? l10n.waitlist.submit })
|
|
2444
1226
|
] })
|
|
2445
1227
|
] }),
|
|
2446
|
-
legal ? /* @__PURE__ */
|
|
2447
|
-
/* @__PURE__ */
|
|
1228
|
+
legal ? /* @__PURE__ */ jsx10("p", { className: "kerne-legal", children: legal }) : null,
|
|
1229
|
+
/* @__PURE__ */ jsx10(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
2448
1230
|
] });
|
|
2449
1231
|
}
|
|
2450
1232
|
|
|
2451
1233
|
// src/ui/UserButton.tsx
|
|
2452
|
-
import { useEffect as useEffect5, useRef as useRef3, useState as
|
|
2453
|
-
import { Fragment as Fragment5, jsx as
|
|
1234
|
+
import { useEffect as useEffect5, useRef as useRef3, useState as useState12 } from "react";
|
|
1235
|
+
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2454
1236
|
function SignOutButton({
|
|
2455
1237
|
children,
|
|
2456
1238
|
onSignedOut,
|
|
@@ -2466,13 +1248,13 @@ function SignOutButton({
|
|
|
2466
1248
|
onSignedOut?.();
|
|
2467
1249
|
if (redirectUrl && typeof window !== "undefined") window.location.href = redirectUrl;
|
|
2468
1250
|
};
|
|
2469
|
-
return /* @__PURE__ */
|
|
1251
|
+
return /* @__PURE__ */ jsx11(
|
|
2470
1252
|
"div",
|
|
2471
1253
|
{
|
|
2472
1254
|
className: "kerne-root",
|
|
2473
1255
|
style: { ...appearanceToStyle(appearance), display: "inline-flex", width: "auto" },
|
|
2474
1256
|
"data-kerne-theme": resolveTheme(appearance),
|
|
2475
|
-
children: /* @__PURE__ */
|
|
1257
|
+
children: /* @__PURE__ */ jsx11(
|
|
2476
1258
|
"button",
|
|
2477
1259
|
{
|
|
2478
1260
|
type: "button",
|
|
@@ -2497,24 +1279,48 @@ function UserButton({
|
|
|
2497
1279
|
const { logout, isAuthenticated } = useAuth();
|
|
2498
1280
|
const { user, fullName, initials, email } = useUser();
|
|
2499
1281
|
const l10n = useLocalization();
|
|
2500
|
-
const [open, setOpen] =
|
|
1282
|
+
const [open, setOpen] = useState12(false);
|
|
2501
1283
|
const wrapRef = useRef3(null);
|
|
1284
|
+
const triggerRef = useRef3(null);
|
|
1285
|
+
const itemRefs = useRef3([]);
|
|
2502
1286
|
useEffect5(() => {
|
|
2503
1287
|
if (!open) return;
|
|
2504
1288
|
const onPointerDown = (e) => {
|
|
2505
1289
|
if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
|
|
2506
1290
|
};
|
|
2507
1291
|
const onKeyDown = (e) => {
|
|
2508
|
-
if (e.key === "Escape")
|
|
1292
|
+
if (e.key === "Escape") {
|
|
1293
|
+
setOpen(false);
|
|
1294
|
+
triggerRef.current?.focus();
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
const onFocusOut = (e) => {
|
|
1298
|
+
if (wrapRef.current && !wrapRef.current.contains(e.relatedTarget)) setOpen(false);
|
|
2509
1299
|
};
|
|
2510
1300
|
document.addEventListener("mousedown", onPointerDown);
|
|
2511
1301
|
document.addEventListener("keydown", onKeyDown);
|
|
1302
|
+
wrapRef.current?.addEventListener("focusout", onFocusOut);
|
|
1303
|
+
itemRefs.current[0]?.focus();
|
|
2512
1304
|
return () => {
|
|
2513
1305
|
document.removeEventListener("mousedown", onPointerDown);
|
|
2514
1306
|
document.removeEventListener("keydown", onKeyDown);
|
|
1307
|
+
wrapRef.current?.removeEventListener("focusout", onFocusOut);
|
|
2515
1308
|
};
|
|
2516
1309
|
}, [open]);
|
|
2517
|
-
|
|
1310
|
+
const onMenuKeyDown = (e) => {
|
|
1311
|
+
const entries = itemRefs.current.filter((el) => el !== null);
|
|
1312
|
+
if (entries.length === 0) return;
|
|
1313
|
+
const currentIndex = entries.indexOf(document.activeElement);
|
|
1314
|
+
let nextIndex = null;
|
|
1315
|
+
if (e.key === "ArrowDown") nextIndex = (currentIndex + 1) % entries.length;
|
|
1316
|
+
else if (e.key === "ArrowUp") nextIndex = (currentIndex - 1 + entries.length) % entries.length;
|
|
1317
|
+
else if (e.key === "Home") nextIndex = 0;
|
|
1318
|
+
else if (e.key === "End") nextIndex = entries.length - 1;
|
|
1319
|
+
else return;
|
|
1320
|
+
e.preventDefault();
|
|
1321
|
+
entries[nextIndex]?.focus();
|
|
1322
|
+
};
|
|
1323
|
+
if (!isAuthenticated) return /* @__PURE__ */ jsx11(Fragment5, { children: signedOutFallback });
|
|
2518
1324
|
const handleSignOut = () => {
|
|
2519
1325
|
setOpen(false);
|
|
2520
1326
|
logout();
|
|
@@ -2523,82 +1329,102 @@ function UserButton({
|
|
|
2523
1329
|
window.location.href = signOutRedirectUrl;
|
|
2524
1330
|
}
|
|
2525
1331
|
};
|
|
2526
|
-
return /* @__PURE__ */
|
|
1332
|
+
return /* @__PURE__ */ jsx11(
|
|
2527
1333
|
"div",
|
|
2528
1334
|
{
|
|
2529
1335
|
className: `kerne-root${className ? ` ${className}` : ""}`,
|
|
2530
1336
|
style: { ...appearanceToStyle(appearance), display: "inline-flex", width: "auto" },
|
|
2531
1337
|
"data-kerne-theme": resolveTheme(appearance),
|
|
2532
|
-
children: /* @__PURE__ */
|
|
2533
|
-
/* @__PURE__ */
|
|
1338
|
+
children: /* @__PURE__ */ jsxs10("div", { className: "kerne-menu-wrap", ref: wrapRef, children: [
|
|
1339
|
+
/* @__PURE__ */ jsx11(
|
|
2534
1340
|
"button",
|
|
2535
1341
|
{
|
|
2536
1342
|
type: "button",
|
|
1343
|
+
ref: triggerRef,
|
|
2537
1344
|
className: "kerne-menu-trigger",
|
|
2538
1345
|
"aria-haspopup": "menu",
|
|
2539
1346
|
"aria-expanded": open,
|
|
2540
1347
|
"aria-label": l10n.account.menuLabel,
|
|
2541
1348
|
onClick: () => setOpen((v) => !v),
|
|
2542
|
-
children: user?.avatar ? /* @__PURE__ */
|
|
1349
|
+
children: user?.avatar ? /* @__PURE__ */ jsx11("img", { className: "kerne-avatar", src: user.avatar, alt: "" }) : /* @__PURE__ */ jsx11("span", { className: "kerne-avatar", children: initials || email?.[0]?.toUpperCase() || "?" })
|
|
2543
1350
|
}
|
|
2544
1351
|
),
|
|
2545
|
-
open ?
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
1352
|
+
open ? (() => {
|
|
1353
|
+
itemRefs.current = [];
|
|
1354
|
+
let itemIndex = 0;
|
|
1355
|
+
const registerItem = (el) => {
|
|
1356
|
+
itemRefs.current[itemIndex++] = el;
|
|
1357
|
+
};
|
|
1358
|
+
return /* @__PURE__ */ jsxs10("div", { className: "kerne-menu", role: "menu", onKeyDown: onMenuKeyDown, children: [
|
|
1359
|
+
/* @__PURE__ */ jsxs10("div", { className: "kerne-menu-header", children: [
|
|
1360
|
+
fullName ? /* @__PURE__ */ jsx11("div", { className: "kerne-identity-name", children: fullName }) : null,
|
|
1361
|
+
/* @__PURE__ */ jsx11("div", { className: "kerne-identity-mail", children: email })
|
|
1362
|
+
] }),
|
|
1363
|
+
items.map(
|
|
1364
|
+
(item) => item.href ? /* @__PURE__ */ jsx11(
|
|
1365
|
+
"a",
|
|
1366
|
+
{
|
|
1367
|
+
ref: registerItem,
|
|
1368
|
+
role: "menuitem",
|
|
1369
|
+
className: "kerne-menu-item",
|
|
1370
|
+
href: item.href,
|
|
1371
|
+
children: item.label
|
|
1372
|
+
},
|
|
1373
|
+
item.label
|
|
1374
|
+
) : /* @__PURE__ */ jsx11(
|
|
1375
|
+
"button",
|
|
1376
|
+
{
|
|
1377
|
+
ref: registerItem,
|
|
1378
|
+
type: "button",
|
|
1379
|
+
role: "menuitem",
|
|
1380
|
+
className: "kerne-menu-item",
|
|
1381
|
+
onClick: () => {
|
|
1382
|
+
setOpen(false);
|
|
1383
|
+
item.onClick?.();
|
|
1384
|
+
},
|
|
1385
|
+
children: item.label
|
|
1386
|
+
},
|
|
1387
|
+
item.label
|
|
1388
|
+
)
|
|
1389
|
+
),
|
|
1390
|
+
/* @__PURE__ */ jsx11(
|
|
2552
1391
|
"button",
|
|
2553
1392
|
{
|
|
2554
1393
|
type: "button",
|
|
1394
|
+
ref: registerItem,
|
|
2555
1395
|
role: "menuitem",
|
|
2556
1396
|
className: "kerne-menu-item",
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
children: item.label
|
|
2562
|
-
},
|
|
2563
|
-
item.label
|
|
1397
|
+
"data-tone": "danger",
|
|
1398
|
+
onClick: handleSignOut,
|
|
1399
|
+
children: l10n.account.signOut
|
|
1400
|
+
}
|
|
2564
1401
|
)
|
|
2565
|
-
)
|
|
2566
|
-
|
|
2567
|
-
"button",
|
|
2568
|
-
{
|
|
2569
|
-
type: "button",
|
|
2570
|
-
role: "menuitem",
|
|
2571
|
-
className: "kerne-menu-item",
|
|
2572
|
-
"data-tone": "danger",
|
|
2573
|
-
onClick: handleSignOut,
|
|
2574
|
-
children: l10n.account.signOut
|
|
2575
|
-
}
|
|
2576
|
-
)
|
|
2577
|
-
] }) : null
|
|
1402
|
+
] });
|
|
1403
|
+
})() : null
|
|
2578
1404
|
] })
|
|
2579
1405
|
}
|
|
2580
1406
|
);
|
|
2581
1407
|
}
|
|
2582
1408
|
|
|
2583
1409
|
// src/ui/UserProfile.tsx
|
|
2584
|
-
import { useState as
|
|
1410
|
+
import { useId, useRef as useRef5, useState as useState17 } from "react";
|
|
2585
1411
|
|
|
2586
1412
|
// src/ui/profile/ProfileSection.tsx
|
|
2587
|
-
import { useEffect as useEffect6, useState as
|
|
2588
|
-
import { Fragment as Fragment6, jsx as
|
|
2589
|
-
function ProfileSection({
|
|
1413
|
+
import { useEffect as useEffect6, useState as useState13 } from "react";
|
|
1414
|
+
import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1415
|
+
function ProfileSection({ onSaved }) {
|
|
2590
1416
|
const { updateProfile, sendVerificationEmail, getVerificationStatus } = useAuth();
|
|
2591
1417
|
const { user, fullName, initials, email } = useUser();
|
|
2592
1418
|
const l10n = useLocalization();
|
|
2593
1419
|
const showError = useErrorResolver();
|
|
2594
|
-
const [firstName, setFirstName] =
|
|
2595
|
-
const [lastName, setLastName] =
|
|
2596
|
-
const [saving, setSaving] =
|
|
2597
|
-
const [saved, setSaved] =
|
|
2598
|
-
const [error, setError] =
|
|
2599
|
-
const [verified, setVerified] =
|
|
2600
|
-
const [sending, setSending] =
|
|
2601
|
-
const [sent, setSent] =
|
|
1420
|
+
const [firstName, setFirstName] = useState13("");
|
|
1421
|
+
const [lastName, setLastName] = useState13("");
|
|
1422
|
+
const [saving, setSaving] = useState13(false);
|
|
1423
|
+
const [saved, setSaved] = useState13(false);
|
|
1424
|
+
const [error, setError] = useState13(null);
|
|
1425
|
+
const [verified, setVerified] = useState13(null);
|
|
1426
|
+
const [sending, setSending] = useState13(false);
|
|
1427
|
+
const [sent, setSent] = useState13(false);
|
|
2602
1428
|
useEffect6(() => {
|
|
2603
1429
|
if (!user) return;
|
|
2604
1430
|
setFirstName(user.first_name ?? "");
|
|
@@ -2630,7 +1456,7 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
|
2630
1456
|
setError(null);
|
|
2631
1457
|
setSending(true);
|
|
2632
1458
|
try {
|
|
2633
|
-
await sendVerificationEmail("link"
|
|
1459
|
+
await sendVerificationEmail("link");
|
|
2634
1460
|
setSent(true);
|
|
2635
1461
|
} catch (err) {
|
|
2636
1462
|
setError(showError(err));
|
|
@@ -2638,19 +1464,19 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
|
2638
1464
|
setSending(false);
|
|
2639
1465
|
}
|
|
2640
1466
|
};
|
|
2641
|
-
return /* @__PURE__ */
|
|
2642
|
-
/* @__PURE__ */
|
|
2643
|
-
/* @__PURE__ */
|
|
2644
|
-
user?.avatar ? /* @__PURE__ */
|
|
2645
|
-
/* @__PURE__ */
|
|
2646
|
-
/* @__PURE__ */
|
|
2647
|
-
/* @__PURE__ */
|
|
1467
|
+
return /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1468
|
+
/* @__PURE__ */ jsxs11(Subsection, { title: l10n.profile.profileTitle, children: [
|
|
1469
|
+
/* @__PURE__ */ jsxs11("div", { className: "kerne-identity", children: [
|
|
1470
|
+
user?.avatar ? /* @__PURE__ */ jsx12("img", { className: "kerne-avatar", src: user.avatar, alt: "" }) : /* @__PURE__ */ jsx12("div", { className: "kerne-avatar", "aria-hidden": "true", children: initials || email?.[0]?.toUpperCase() || "?" }),
|
|
1471
|
+
/* @__PURE__ */ jsxs11("div", { className: "kerne-identity-text", children: [
|
|
1472
|
+
/* @__PURE__ */ jsx12("span", { className: "kerne-identity-name", children: fullName || l10n.profile.unnamed }),
|
|
1473
|
+
/* @__PURE__ */ jsx12("span", { className: "kerne-identity-mail", children: email })
|
|
2648
1474
|
] })
|
|
2649
1475
|
] }),
|
|
2650
|
-
error ? /* @__PURE__ */
|
|
2651
|
-
saved ? /* @__PURE__ */
|
|
2652
|
-
/* @__PURE__ */
|
|
2653
|
-
/* @__PURE__ */
|
|
1476
|
+
error ? /* @__PURE__ */ jsx12(Panel, { variant: "danger", children: error }) : null,
|
|
1477
|
+
saved ? /* @__PURE__ */ jsx12(Panel, { variant: "success", children: l10n.profile.saved }) : null,
|
|
1478
|
+
/* @__PURE__ */ jsxs11("form", { className: "kerne-form", onSubmit: handleSave, children: [
|
|
1479
|
+
/* @__PURE__ */ jsx12(
|
|
2654
1480
|
Field,
|
|
2655
1481
|
{
|
|
2656
1482
|
label: l10n.profile.firstName,
|
|
@@ -2662,7 +1488,7 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
|
2662
1488
|
disabled: saving
|
|
2663
1489
|
}
|
|
2664
1490
|
),
|
|
2665
|
-
/* @__PURE__ */
|
|
1491
|
+
/* @__PURE__ */ jsx12(
|
|
2666
1492
|
Field,
|
|
2667
1493
|
{
|
|
2668
1494
|
label: l10n.profile.lastName,
|
|
@@ -2674,23 +1500,23 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
|
2674
1500
|
disabled: saving
|
|
2675
1501
|
}
|
|
2676
1502
|
),
|
|
2677
|
-
/* @__PURE__ */
|
|
1503
|
+
/* @__PURE__ */ jsx12(Button, { loading: saving, children: saving ? l10n.profile.saving : l10n.profile.save })
|
|
2678
1504
|
] })
|
|
2679
1505
|
] }),
|
|
2680
|
-
/* @__PURE__ */
|
|
1506
|
+
/* @__PURE__ */ jsxs11(
|
|
2681
1507
|
Subsection,
|
|
2682
1508
|
{
|
|
2683
1509
|
title: l10n.profile.emailSectionTitle,
|
|
2684
1510
|
description: l10n.profile.emailSectionDesc,
|
|
2685
1511
|
children: [
|
|
2686
|
-
/* @__PURE__ */
|
|
2687
|
-
/* @__PURE__ */
|
|
1512
|
+
/* @__PURE__ */ jsxs11("div", { className: "kerne-plan-option", children: [
|
|
1513
|
+
/* @__PURE__ */ jsx12("div", { className: "kerne-plan-option-text", children: /* @__PURE__ */ jsxs11("span", { className: "kerne-plan-option-name", children: [
|
|
2688
1514
|
email,
|
|
2689
|
-
verified === true ? /* @__PURE__ */
|
|
1515
|
+
verified === true ? /* @__PURE__ */ jsx12("span", { className: "kerne-badge", "data-tone": "success", children: l10n.profile.verified }) : verified === false ? /* @__PURE__ */ jsx12("span", { className: "kerne-badge", "data-tone": "warning", children: l10n.profile.unverified }) : null
|
|
2690
1516
|
] }) }),
|
|
2691
|
-
verified === false ? /* @__PURE__ */
|
|
1517
|
+
verified === false ? /* @__PURE__ */ jsx12(Button, { variant: "outline", type: "button", onClick: handleResend, loading: sending, children: sending ? l10n.common.sending : l10n.profile.verify }) : null
|
|
2692
1518
|
] }),
|
|
2693
|
-
sent ? /* @__PURE__ */
|
|
1519
|
+
sent ? /* @__PURE__ */ jsx12(Panel, { variant: "success", children: l10n.profile.verificationSent }) : null
|
|
2694
1520
|
]
|
|
2695
1521
|
}
|
|
2696
1522
|
)
|
|
@@ -2698,16 +1524,16 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
|
2698
1524
|
}
|
|
2699
1525
|
|
|
2700
1526
|
// src/ui/profile/SecuritySection.tsx
|
|
2701
|
-
import { useState as
|
|
2702
|
-
import { Fragment as Fragment7, jsx as
|
|
1527
|
+
import { useState as useState14 } from "react";
|
|
1528
|
+
import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2703
1529
|
function SecuritySection({ showDeleteAccount = true, onDeleted }) {
|
|
2704
1530
|
const { requestAccountDeletion } = useAuth();
|
|
2705
1531
|
const { config } = useAuthConfig();
|
|
2706
1532
|
const l10n = useLocalization();
|
|
2707
1533
|
const showError = useErrorResolver();
|
|
2708
|
-
const [error, setError] =
|
|
2709
|
-
const [confirmingDelete, setConfirmingDelete] =
|
|
2710
|
-
const [deleting, setDeleting] =
|
|
1534
|
+
const [error, setError] = useState14(null);
|
|
1535
|
+
const [confirmingDelete, setConfirmingDelete] = useState14(false);
|
|
1536
|
+
const [deleting, setDeleting] = useState14(false);
|
|
2711
1537
|
const hasPassword = config?.enable_credentials ?? true;
|
|
2712
1538
|
const handleDelete = async () => {
|
|
2713
1539
|
setError(null);
|
|
@@ -2720,12 +1546,12 @@ function SecuritySection({ showDeleteAccount = true, onDeleted }) {
|
|
|
2720
1546
|
setDeleting(false);
|
|
2721
1547
|
}
|
|
2722
1548
|
};
|
|
2723
|
-
return /* @__PURE__ */
|
|
2724
|
-
error ? /* @__PURE__ */
|
|
2725
|
-
hasPassword ? /* @__PURE__ */
|
|
2726
|
-
showDeleteAccount ? /* @__PURE__ */
|
|
2727
|
-
/* @__PURE__ */
|
|
2728
|
-
/* @__PURE__ */
|
|
1549
|
+
return /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1550
|
+
error ? /* @__PURE__ */ jsx13(Panel, { variant: "danger", children: error }) : null,
|
|
1551
|
+
hasPassword ? /* @__PURE__ */ jsx13(Subsection, { title: l10n.security.passwordTitle, description: l10n.security.passwordDesc, children: /* @__PURE__ */ jsx13(ChangePasswordForm, { title: null }) }) : null,
|
|
1552
|
+
showDeleteAccount ? /* @__PURE__ */ jsx13(Subsection, { title: l10n.security.deleteTitle, description: l10n.security.deleteDesc, children: confirmingDelete ? /* @__PURE__ */ jsxs12("div", { className: "kerne-inline-actions", children: [
|
|
1553
|
+
/* @__PURE__ */ jsx13(Button, { variant: "danger", type: "button", onClick: handleDelete, loading: deleting, children: deleting ? l10n.security.deleting : l10n.security.deleteConfirm }),
|
|
1554
|
+
/* @__PURE__ */ jsx13(
|
|
2729
1555
|
Button,
|
|
2730
1556
|
{
|
|
2731
1557
|
variant: "outline",
|
|
@@ -2735,17 +1561,47 @@ function SecuritySection({ showDeleteAccount = true, onDeleted }) {
|
|
|
2735
1561
|
children: l10n.common.cancel
|
|
2736
1562
|
}
|
|
2737
1563
|
)
|
|
2738
|
-
] }) : /* @__PURE__ */
|
|
1564
|
+
] }) : /* @__PURE__ */ jsx13(Button, { variant: "danger", type: "button", onClick: () => setConfirmingDelete(true), children: l10n.security.deleteAction }) }) : null
|
|
2739
1565
|
] });
|
|
2740
1566
|
}
|
|
2741
1567
|
|
|
2742
|
-
// src/ui/profile/BillingSection.tsx
|
|
2743
|
-
import { useMemo, useState as useState17 } from "react";
|
|
2744
|
-
|
|
2745
1568
|
// src/ui/format.ts
|
|
2746
1569
|
function humanizeFeatureKey(key) {
|
|
2747
|
-
const words = key.replace(/[_-]+/g, " ").trim();
|
|
2748
|
-
return words.
|
|
1570
|
+
const words = key.replace(/[_-]+/g, " ").trim().split(" ");
|
|
1571
|
+
return words.map((word, index) => {
|
|
1572
|
+
const lower = word.toLowerCase();
|
|
1573
|
+
if (COMMON_ACRONYMS.has(lower)) return lower.toUpperCase();
|
|
1574
|
+
return index === 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word;
|
|
1575
|
+
}).join(" ");
|
|
1576
|
+
}
|
|
1577
|
+
var COMMON_ACRONYMS = /* @__PURE__ */ new Set([
|
|
1578
|
+
"ai",
|
|
1579
|
+
"api",
|
|
1580
|
+
"cdn",
|
|
1581
|
+
"cpu",
|
|
1582
|
+
"db",
|
|
1583
|
+
"gb",
|
|
1584
|
+
"gpu",
|
|
1585
|
+
"id",
|
|
1586
|
+
"ip",
|
|
1587
|
+
"kb",
|
|
1588
|
+
"mb",
|
|
1589
|
+
"saas",
|
|
1590
|
+
"sdk",
|
|
1591
|
+
"sla",
|
|
1592
|
+
"sms",
|
|
1593
|
+
"sql",
|
|
1594
|
+
"sso",
|
|
1595
|
+
"tb",
|
|
1596
|
+
"ui",
|
|
1597
|
+
"url",
|
|
1598
|
+
"ux"
|
|
1599
|
+
]);
|
|
1600
|
+
function lowercaseFeatureName(name) {
|
|
1601
|
+
return name.split(" ").map((word) => /^[A-Z0-9]{2,}$/.test(word) ? word : word.toLowerCase()).join(" ");
|
|
1602
|
+
}
|
|
1603
|
+
function capitalize(text) {
|
|
1604
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
2749
1605
|
}
|
|
2750
1606
|
function formatMoney(amountMinorUnits, currency) {
|
|
2751
1607
|
try {
|
|
@@ -2848,6 +1704,7 @@ function resolveLabelSavings(plans, labels) {
|
|
|
2848
1704
|
const shortest = sorted[0];
|
|
2849
1705
|
const longest = sorted[sorted.length - 1];
|
|
2850
1706
|
if (shortest.slug === longest.slug) return null;
|
|
1707
|
+
let smallest = null;
|
|
2851
1708
|
for (const plan of plans) {
|
|
2852
1709
|
const shortPrice = plan.prices.find((p) => p.label.slug === shortest.slug);
|
|
2853
1710
|
const longPrice = plan.prices.find((p) => p.label.slug === longest.slug);
|
|
@@ -2856,13 +1713,16 @@ function resolveLabelSavings(plans, labels) {
|
|
|
2856
1713
|
const longMonths = approxMonths(longPrice.interval);
|
|
2857
1714
|
if (!shortMonths || !longMonths) continue;
|
|
2858
1715
|
const savings = 1 - longPrice.amount / longMonths / (shortPrice.amount / shortMonths);
|
|
2859
|
-
if (savings
|
|
1716
|
+
if (savings <= 0.01) return null;
|
|
1717
|
+
smallest = smallest === null ? savings : Math.min(smallest, savings);
|
|
2860
1718
|
}
|
|
2861
|
-
return null;
|
|
1719
|
+
if (smallest === null) return null;
|
|
1720
|
+
const percent = Math.floor(smallest * 100);
|
|
1721
|
+
return percent > 0 ? { labelSlug: longest.slug, percent } : null;
|
|
2862
1722
|
}
|
|
2863
1723
|
|
|
2864
1724
|
// src/ui/UsageMeters.tsx
|
|
2865
|
-
import { Fragment as Fragment8, jsx as
|
|
1725
|
+
import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2866
1726
|
function isSoftOverage(overage) {
|
|
2867
1727
|
return overage === "ALLOW" || overage === "BILL";
|
|
2868
1728
|
}
|
|
@@ -2895,20 +1755,20 @@ function UsageMeters({
|
|
|
2895
1755
|
const fromPlan = subscription?.entitlements.find((e) => e.feature_key === key)?.feature_name;
|
|
2896
1756
|
return fromPlan ?? humanizeFeatureKey(key);
|
|
2897
1757
|
};
|
|
2898
|
-
const shell = (children) => /* @__PURE__ */
|
|
2899
|
-
heading ? /* @__PURE__ */
|
|
1758
|
+
const shell = (children) => /* @__PURE__ */ jsxs13(Shell, { appearance, className, children: [
|
|
1759
|
+
heading ? /* @__PURE__ */ jsx14("div", { className: "kerne-section-head", style: { marginBottom: 16 }, children: /* @__PURE__ */ jsx14("h2", { className: "kerne-section-title", children: heading }) }) : null,
|
|
2900
1760
|
children
|
|
2901
1761
|
] });
|
|
2902
1762
|
if (isLoading) {
|
|
2903
1763
|
return shell(
|
|
2904
|
-
/* @__PURE__ */
|
|
2905
|
-
/* @__PURE__ */
|
|
2906
|
-
/* @__PURE__ */
|
|
1764
|
+
/* @__PURE__ */ jsxs13("div", { className: "kerne-meters", "aria-busy": "true", "aria-label": l10n.usage.loading, children: [
|
|
1765
|
+
/* @__PURE__ */ jsx14("div", { className: "kerne-skeleton", style: { height: 34 } }),
|
|
1766
|
+
/* @__PURE__ */ jsx14("div", { className: "kerne-skeleton", style: { height: 34 } })
|
|
2907
1767
|
] })
|
|
2908
1768
|
);
|
|
2909
1769
|
}
|
|
2910
1770
|
if (error || !entitlements) {
|
|
2911
|
-
return shell(/* @__PURE__ */
|
|
1771
|
+
return shell(/* @__PURE__ */ jsx14(Panel, { variant: "danger", children: l10n.usage.loadFailed }));
|
|
2912
1772
|
}
|
|
2913
1773
|
const metered = entitlements.entitlements.filter((e) => {
|
|
2914
1774
|
if (e.feature_type !== "QUOTA") return false;
|
|
@@ -2924,10 +1784,10 @@ function UsageMeters({
|
|
|
2924
1784
|
});
|
|
2925
1785
|
const rows = maxRows ? metered.slice(0, maxRows) : metered;
|
|
2926
1786
|
if (rows.length === 0) {
|
|
2927
|
-
return shell(emptyState ?? /* @__PURE__ */
|
|
1787
|
+
return shell(emptyState ?? /* @__PURE__ */ jsx14("p", { className: "kerne-meter-note", children: l10n.usage.nothingMetered }));
|
|
2928
1788
|
}
|
|
2929
1789
|
return shell(
|
|
2930
|
-
/* @__PURE__ */
|
|
1790
|
+
/* @__PURE__ */ jsx14("div", { className: "kerne-meters", children: rows.map((e, index) => {
|
|
2931
1791
|
const unlimited = e.limit === null || e.limit === void 0;
|
|
2932
1792
|
const softOverage = isSoftOverage(e.overage_behavior);
|
|
2933
1793
|
const billed = e.overage_behavior === "BILL";
|
|
@@ -2936,12 +1796,12 @@ function UsageMeters({
|
|
|
2936
1796
|
const over = !unlimited && e.used > e.limit;
|
|
2937
1797
|
const standing = billed ? subscription?.entitlements.find((se) => se.feature_key === e.feature_key) : void 0;
|
|
2938
1798
|
const capReached = billed && (standing?.cap_reached ?? false);
|
|
2939
|
-
return /* @__PURE__ */
|
|
2940
|
-
/* @__PURE__ */
|
|
2941
|
-
/* @__PURE__ */
|
|
2942
|
-
/* @__PURE__ */
|
|
1799
|
+
return /* @__PURE__ */ jsxs13("div", { className: "kerne-meter", "data-level": level, children: [
|
|
1800
|
+
/* @__PURE__ */ jsxs13("div", { className: "kerne-meter-head", children: [
|
|
1801
|
+
/* @__PURE__ */ jsx14("span", { className: "kerne-meter-name", children: nameFor(e.feature_key) }),
|
|
1802
|
+
/* @__PURE__ */ jsx14("span", { className: "kerne-meter-value", children: unlimited ? fill(l10n.usage.used, { count: formatNumber(e.used) }) : `${formatNumber(e.used)} / ${formatNumber(e.limit)}` })
|
|
2943
1803
|
] }),
|
|
2944
|
-
!unlimited ? /* @__PURE__ */
|
|
1804
|
+
!unlimited ? /* @__PURE__ */ jsx14(
|
|
2945
1805
|
"div",
|
|
2946
1806
|
{
|
|
2947
1807
|
className: "kerne-meter-track",
|
|
@@ -2950,29 +1810,35 @@ function UsageMeters({
|
|
|
2950
1810
|
"aria-valuemin": 0,
|
|
2951
1811
|
"aria-valuemax": e.limit,
|
|
2952
1812
|
"aria-label": nameFor(e.feature_key),
|
|
2953
|
-
children: /* @__PURE__ */
|
|
1813
|
+
children: /* @__PURE__ */ jsx14(
|
|
1814
|
+
"div",
|
|
1815
|
+
{
|
|
1816
|
+
className: "kerne-meter-fill",
|
|
1817
|
+
style: { width: `${ratio * 100}%`, animationDelay: `${Math.min(index * 40, 200)}ms` }
|
|
1818
|
+
}
|
|
1819
|
+
)
|
|
2954
1820
|
}
|
|
2955
1821
|
) : null,
|
|
2956
|
-
over && softOverage ? /* @__PURE__ */
|
|
2957
|
-
/* @__PURE__ */
|
|
1822
|
+
over && softOverage ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1823
|
+
/* @__PURE__ */ jsx14("p", { className: "kerne-meter-note", children: fill(
|
|
2958
1824
|
// capReached inverts overageBilled's meaning (billing stopped,
|
|
2959
1825
|
// access didn't) - a variant string would say the opposite of
|
|
2960
1826
|
// the truth for the one plan that just stopped costing money.
|
|
2961
1827
|
capReached ? l10n.usage.capReached : billed ? l10n.usage.overageBilled : l10n.usage.overage,
|
|
2962
1828
|
{ count: formatNumber(e.used - e.limit) }
|
|
2963
1829
|
) }),
|
|
2964
|
-
billed && standing?.billed_amount != null && standing.currency ? /* @__PURE__ */
|
|
1830
|
+
billed && standing?.billed_amount != null && standing.currency ? /* @__PURE__ */ jsx14("p", { className: "kerne-meter-note kerne-meter-cost", children: fill(l10n.usage.billedAmount, {
|
|
2965
1831
|
amount: formatMoney(standing.billed_amount, standing.currency)
|
|
2966
1832
|
}) }) : null
|
|
2967
|
-
] }) : over ? /* @__PURE__ */
|
|
1833
|
+
] }) : over ? /* @__PURE__ */ jsx14("p", { className: "kerne-meter-note", children: l10n.usage.limitReached }) : null
|
|
2968
1834
|
] }, e.feature_key);
|
|
2969
1835
|
}) })
|
|
2970
1836
|
);
|
|
2971
1837
|
}
|
|
2972
1838
|
|
|
2973
1839
|
// src/ui/SubscriptionCard.tsx
|
|
2974
|
-
import { useState as
|
|
2975
|
-
import { Fragment as Fragment9, jsx as
|
|
1840
|
+
import { useState as useState15 } from "react";
|
|
1841
|
+
import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2976
1842
|
var STATUS_TONES = {
|
|
2977
1843
|
ACTIVE: "success",
|
|
2978
1844
|
TRIALING: "success",
|
|
@@ -2984,7 +1850,6 @@ var STATUS_TONES = {
|
|
|
2984
1850
|
function SubscriptionCard({
|
|
2985
1851
|
productSlug,
|
|
2986
1852
|
title,
|
|
2987
|
-
portalReturnUrl,
|
|
2988
1853
|
upgradeUrl,
|
|
2989
1854
|
onUpgradeClick,
|
|
2990
1855
|
hidePortalButton = false,
|
|
@@ -3006,15 +1871,15 @@ function SubscriptionCard({
|
|
|
3006
1871
|
UNPAID: l10n.billing.statusUnpaid,
|
|
3007
1872
|
PAUSED: l10n.billing.statusPaused
|
|
3008
1873
|
};
|
|
3009
|
-
const [opening, setOpening] =
|
|
3010
|
-
const [portalError, setPortalError] =
|
|
3011
|
-
const [confirmingCancel, setConfirmingCancel] =
|
|
3012
|
-
const [cancelError, setCancelError] =
|
|
1874
|
+
const [opening, setOpening] = useState15(false);
|
|
1875
|
+
const [portalError, setPortalError] = useState15(null);
|
|
1876
|
+
const [confirmingCancel, setConfirmingCancel] = useState15(false);
|
|
1877
|
+
const [cancelError, setCancelError] = useState15(null);
|
|
3013
1878
|
const handlePortal = async () => {
|
|
3014
1879
|
setPortalError(null);
|
|
3015
1880
|
setOpening(true);
|
|
3016
1881
|
try {
|
|
3017
|
-
await openPortal(
|
|
1882
|
+
await openPortal();
|
|
3018
1883
|
} catch (e) {
|
|
3019
1884
|
setPortalError(showError(e));
|
|
3020
1885
|
setOpening(false);
|
|
@@ -3031,26 +1896,26 @@ function SubscriptionCard({
|
|
|
3031
1896
|
setCancelError(showError(e));
|
|
3032
1897
|
}
|
|
3033
1898
|
};
|
|
3034
|
-
const shell = (children) => /* @__PURE__ */
|
|
3035
|
-
heading ? /* @__PURE__ */
|
|
1899
|
+
const shell = (children) => /* @__PURE__ */ jsx15(Shell, { appearance, className, children: /* @__PURE__ */ jsxs14("div", { className: "kerne-stack", children: [
|
|
1900
|
+
heading ? /* @__PURE__ */ jsx15("h2", { className: "kerne-section-title", children: heading }) : null,
|
|
3036
1901
|
children
|
|
3037
1902
|
] }) });
|
|
3038
1903
|
if (isLoading) {
|
|
3039
1904
|
return shell(
|
|
3040
|
-
/* @__PURE__ */
|
|
3041
|
-
/* @__PURE__ */
|
|
3042
|
-
/* @__PURE__ */
|
|
1905
|
+
/* @__PURE__ */ jsxs14("div", { className: "kerne-rows", "aria-busy": "true", "aria-label": l10n.billing.loadingPlan, children: [
|
|
1906
|
+
/* @__PURE__ */ jsx15("div", { className: "kerne-skeleton", style: { height: 20 } }),
|
|
1907
|
+
/* @__PURE__ */ jsx15("div", { className: "kerne-skeleton", style: { height: 20 } })
|
|
3043
1908
|
] })
|
|
3044
1909
|
);
|
|
3045
1910
|
}
|
|
3046
1911
|
if (error) {
|
|
3047
|
-
return shell(/* @__PURE__ */
|
|
1912
|
+
return shell(/* @__PURE__ */ jsx15(Panel, { variant: "danger", children: l10n.billing.loadFailed }));
|
|
3048
1913
|
}
|
|
3049
1914
|
if (!subscription) {
|
|
3050
1915
|
return shell(
|
|
3051
|
-
/* @__PURE__ */
|
|
3052
|
-
/* @__PURE__ */
|
|
3053
|
-
upgradeUrl || onUpgradeClick ? /* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1917
|
+
/* @__PURE__ */ jsx15("p", { className: "kerne-subsection-desc", children: l10n.billing.freePlan }),
|
|
1918
|
+
upgradeUrl || onUpgradeClick ? /* @__PURE__ */ jsx15(
|
|
3054
1919
|
Button,
|
|
3055
1920
|
{
|
|
3056
1921
|
type: "button",
|
|
@@ -3067,304 +1932,239 @@ function SubscriptionCard({
|
|
|
3067
1932
|
const renews = formatDate(current_period_end);
|
|
3068
1933
|
const trialEnds = formatDate(trial_end);
|
|
3069
1934
|
return shell(
|
|
3070
|
-
/* @__PURE__ */
|
|
3071
|
-
portalError ? /* @__PURE__ */
|
|
3072
|
-
/* @__PURE__ */
|
|
3073
|
-
/* @__PURE__ */
|
|
3074
|
-
/* @__PURE__ */
|
|
3075
|
-
/* @__PURE__ */
|
|
1935
|
+
/* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1936
|
+
portalError ? /* @__PURE__ */ jsx15(Panel, { variant: "danger", children: portalError }) : null,
|
|
1937
|
+
/* @__PURE__ */ jsxs14("dl", { className: "kerne-rows", children: [
|
|
1938
|
+
/* @__PURE__ */ jsxs14("div", { className: "kerne-row", children: [
|
|
1939
|
+
/* @__PURE__ */ jsx15("dt", { children: l10n.billing.plan }),
|
|
1940
|
+
/* @__PURE__ */ jsx15("dd", { children: plan.name })
|
|
3076
1941
|
] }),
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
/* @__PURE__ */
|
|
3079
|
-
/* @__PURE__ */
|
|
1942
|
+
/* @__PURE__ */ jsxs14("div", { className: "kerne-row", children: [
|
|
1943
|
+
/* @__PURE__ */ jsx15("dt", { children: l10n.billing.price }),
|
|
1944
|
+
/* @__PURE__ */ jsxs14("dd", { children: [
|
|
3080
1945
|
formatMoney(plan_price.amount, plan_price.currency),
|
|
3081
1946
|
" /",
|
|
3082
|
-
" ",
|
|
3083
|
-
formatInterval(plan_price.interval, l10n.billing)
|
|
3084
|
-
] })
|
|
3085
|
-
] }),
|
|
3086
|
-
/* @__PURE__ */
|
|
3087
|
-
/* @__PURE__ */
|
|
3088
|
-
/* @__PURE__ */
|
|
3089
|
-
] }),
|
|
3090
|
-
status === "TRIALING" && trialEnds ? /* @__PURE__ */
|
|
3091
|
-
/* @__PURE__ */
|
|
3092
|
-
/* @__PURE__ */
|
|
3093
|
-
] }) : renews ? /* @__PURE__ */
|
|
3094
|
-
/* @__PURE__ */
|
|
3095
|
-
/* @__PURE__ */
|
|
3096
|
-
] }) : null
|
|
3097
|
-
] }),
|
|
3098
|
-
canceled_at ? /* @__PURE__ */
|
|
3099
|
-
cancelError ? /* @__PURE__ */
|
|
3100
|
-
confirmingCancel ? /* @__PURE__ */
|
|
3101
|
-
/* @__PURE__ */
|
|
3102
|
-
/* @__PURE__ */
|
|
3103
|
-
/* @__PURE__ */
|
|
3104
|
-
/* @__PURE__ */
|
|
3105
|
-
Button,
|
|
3106
|
-
{
|
|
3107
|
-
variant: "outline",
|
|
3108
|
-
type: "button",
|
|
3109
|
-
onClick: () => setConfirmingCancel(false),
|
|
3110
|
-
disabled: isCanceling,
|
|
3111
|
-
children: l10n.billing.cancelConfirmDismiss
|
|
3112
|
-
}
|
|
3113
|
-
),
|
|
3114
|
-
/* @__PURE__ */
|
|
3115
|
-
] })
|
|
3116
|
-
] }) : /* @__PURE__ */
|
|
3117
|
-
!hidePortalButton ? /* @__PURE__ */
|
|
3118
|
-
!hideCancelButton && !canceled_at && status !== "CANCELED" ? /* @__PURE__ */
|
|
3119
|
-
] })
|
|
3120
|
-
] })
|
|
3121
|
-
);
|
|
3122
|
-
}
|
|
3123
|
-
|
|
3124
|
-
// src/ui/profile/BillingSection.tsx
|
|
3125
|
-
import { Fragment as Fragment10, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3126
|
-
function intervalsOf(plans) {
|
|
3127
|
-
const seen = [];
|
|
3128
|
-
for (const plan of plans) {
|
|
3129
|
-
for (const price of plan.prices) {
|
|
3130
|
-
if (!seen.includes(price.interval)) seen.push(price.interval);
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
return seen;
|
|
3134
|
-
}
|
|
3135
|
-
function BillingSection({
|
|
3136
|
-
productSlug,
|
|
3137
|
-
pricingProductIdOrSlug,
|
|
3138
|
-
portalReturnUrl,
|
|
3139
|
-
checkoutSuccessUrl,
|
|
3140
|
-
checkoutCancelUrl,
|
|
3141
|
-
showPlanSwitcher = true,
|
|
3142
|
-
showUsage = true,
|
|
3143
|
-
usageFeatureKeys,
|
|
3144
|
-
usageLabels
|
|
3145
|
-
}) {
|
|
3146
|
-
const { subscription, refetch: refetchSubscription } = useSubscription(productSlug);
|
|
3147
|
-
const { plans, isLoading: plansLoading } = usePlans(pricingProductIdOrSlug);
|
|
3148
|
-
const l10n = useLocalization();
|
|
3149
|
-
const showError = useErrorResolver();
|
|
3150
|
-
const {
|
|
3151
|
-
pending: pendingPlan,
|
|
3152
|
-
switching,
|
|
3153
|
-
switchError,
|
|
3154
|
-
checkoutPriceId: busy,
|
|
3155
|
-
checkoutError,
|
|
3156
|
-
selectPlan,
|
|
3157
|
-
confirmSwitch,
|
|
3158
|
-
dismissSwitch
|
|
3159
|
-
} = usePlanSwitch(subscription, refetchSubscription, {
|
|
3160
|
-
successUrl: checkoutSuccessUrl,
|
|
3161
|
-
cancelUrl: checkoutCancelUrl
|
|
3162
|
-
});
|
|
3163
|
-
const error = checkoutError ?? switchError;
|
|
3164
|
-
const intervals = useMemo(() => intervalsOf(plans), [plans]);
|
|
3165
|
-
const [interval, setInterval] = useState17(null);
|
|
3166
|
-
const activeInterval = interval ?? subscription?.plan_price.interval ?? intervals[0] ?? null;
|
|
3167
|
-
return /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
3168
|
-
error ? /* @__PURE__ */ jsx17(Panel, { variant: "danger", children: showError(error) }) : null,
|
|
3169
|
-
pendingPlan ? /* @__PURE__ */ jsxs16(Panel, { children: [
|
|
3170
|
-
/* @__PURE__ */ jsx17("p", { children: fill(l10n.billing.switchConfirmTitle, { plan: pendingPlan.plan.name }) }),
|
|
3171
|
-
/* @__PURE__ */ jsx17("p", { className: "kerne-subsection-desc", children: l10n.billing.switchConfirmBody }),
|
|
3172
|
-
/* @__PURE__ */ jsxs16("div", { className: "kerne-inline-actions", children: [
|
|
3173
|
-
/* @__PURE__ */ jsx17(Button, { variant: "outline", type: "button", onClick: dismissSwitch, disabled: switching, children: l10n.billing.switchConfirmDismiss }),
|
|
3174
|
-
/* @__PURE__ */ jsx17(Button, { type: "button", onClick: confirmSwitch, loading: switching, children: switching ? l10n.billing.switching : l10n.billing.switchConfirmButton })
|
|
3175
|
-
] })
|
|
3176
|
-
] }) : null,
|
|
3177
|
-
/* @__PURE__ */ jsx17(Subsection, { title: l10n.billing.currentPlan, children: /* @__PURE__ */ jsx17(
|
|
3178
|
-
SubscriptionCard,
|
|
3179
|
-
{
|
|
3180
|
-
title: null,
|
|
3181
|
-
productSlug,
|
|
3182
|
-
portalReturnUrl
|
|
3183
|
-
}
|
|
3184
|
-
) }),
|
|
3185
|
-
showUsage ? /* @__PURE__ */ jsx17(Subsection, { title: l10n.usage.title, description: l10n.usage.subtitle, children: /* @__PURE__ */ jsx17(UsageMeters, { title: null, featureKeys: usageFeatureKeys, labels: usageLabels }) }) : null,
|
|
3186
|
-
showPlanSwitcher ? /* @__PURE__ */ jsx17(Subsection, { title: l10n.billing.changePlan, children: plansLoading ? /* @__PURE__ */ jsx17(
|
|
3187
|
-
"div",
|
|
3188
|
-
{
|
|
3189
|
-
className: "kerne-skeleton",
|
|
3190
|
-
style: { height: 64 },
|
|
3191
|
-
"aria-label": l10n.billing.loadingPlans
|
|
3192
|
-
}
|
|
3193
|
-
) : plans.length === 0 ? /* @__PURE__ */ jsx17("p", { className: "kerne-subsection-desc", children: l10n.billing.noPlans }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
3194
|
-
intervals.length > 1 ? /* @__PURE__ */ jsx17(
|
|
3195
|
-
"div",
|
|
3196
|
-
{
|
|
3197
|
-
className: "kerne-segmented",
|
|
3198
|
-
role: "group",
|
|
3199
|
-
"aria-label": l10n.billing.intervalGroupLabel,
|
|
3200
|
-
children: intervals.map((i) => /* @__PURE__ */ jsx17(
|
|
3201
|
-
"button",
|
|
3202
|
-
{
|
|
3203
|
-
type: "button",
|
|
3204
|
-
className: "kerne-segment",
|
|
3205
|
-
"aria-pressed": i === activeInterval,
|
|
3206
|
-
onClick: () => setInterval(i),
|
|
3207
|
-
children: formatInterval(i, l10n.billing)
|
|
3208
|
-
},
|
|
3209
|
-
i
|
|
3210
|
-
))
|
|
3211
|
-
}
|
|
3212
|
-
) : null,
|
|
3213
|
-
/* @__PURE__ */ jsx17("div", { className: "kerne-plan-list", children: plans.map((plan) => {
|
|
3214
|
-
const price = plan.prices.find((p) => p.interval === activeInterval) ?? plan.prices[0];
|
|
3215
|
-
const isCurrent = subscription?.plan.slug === plan.slug;
|
|
3216
|
-
return /* @__PURE__ */ jsxs16(
|
|
3217
|
-
"div",
|
|
3218
|
-
{
|
|
3219
|
-
className: "kerne-plan-option",
|
|
3220
|
-
"data-current": isCurrent ? "true" : void 0,
|
|
3221
|
-
children: [
|
|
3222
|
-
/* @__PURE__ */ jsxs16("div", { className: "kerne-plan-option-text", children: [
|
|
3223
|
-
/* @__PURE__ */ jsxs16("span", { className: "kerne-plan-option-name", children: [
|
|
3224
|
-
plan.name,
|
|
3225
|
-
isCurrent ? /* @__PURE__ */ jsx17("span", { className: "kerne-badge", children: l10n.billing.current }) : null
|
|
3226
|
-
] }),
|
|
3227
|
-
/* @__PURE__ */ jsx17("span", { className: "kerne-plan-option-price", children: price ? `${formatMoney(price.amount, price.currency)} / ${formatInterval(price.interval, l10n.billing)}` : l10n.billing.notOnInterval })
|
|
3228
|
-
] }),
|
|
3229
|
-
!isCurrent && price ? /* @__PURE__ */ jsx17(
|
|
3230
|
-
Button,
|
|
3231
|
-
{
|
|
3232
|
-
variant: "outline",
|
|
3233
|
-
type: "button",
|
|
3234
|
-
onClick: () => selectPlan(plan, price),
|
|
3235
|
-
loading: busy === price.id,
|
|
3236
|
-
disabled: busy !== null,
|
|
3237
|
-
children: busy === price.id ? l10n.billing.opening : l10n.billing.switchTo
|
|
3238
|
-
}
|
|
3239
|
-
) : null
|
|
3240
|
-
]
|
|
3241
|
-
},
|
|
3242
|
-
plan.id
|
|
3243
|
-
);
|
|
3244
|
-
}) })
|
|
3245
|
-
] }) }) : null
|
|
3246
|
-
] });
|
|
1947
|
+
" ",
|
|
1948
|
+
formatInterval(plan_price.interval, l10n.billing)
|
|
1949
|
+
] })
|
|
1950
|
+
] }),
|
|
1951
|
+
/* @__PURE__ */ jsxs14("div", { className: "kerne-row", children: [
|
|
1952
|
+
/* @__PURE__ */ jsx15("dt", { children: l10n.billing.status }),
|
|
1953
|
+
/* @__PURE__ */ jsx15("dd", { children: /* @__PURE__ */ jsx15("span", { className: "kerne-badge", "data-tone": STATUS_TONES[status], children: statusLabels[status] ?? status }) })
|
|
1954
|
+
] }),
|
|
1955
|
+
status === "TRIALING" && trialEnds ? /* @__PURE__ */ jsxs14("div", { className: "kerne-row", children: [
|
|
1956
|
+
/* @__PURE__ */ jsx15("dt", { children: l10n.billing.trialEnds }),
|
|
1957
|
+
/* @__PURE__ */ jsx15("dd", { children: trialEnds })
|
|
1958
|
+
] }) : renews ? /* @__PURE__ */ jsxs14("div", { className: "kerne-row", children: [
|
|
1959
|
+
/* @__PURE__ */ jsx15("dt", { children: canceled_at ? l10n.billing.ends : l10n.billing.renews }),
|
|
1960
|
+
/* @__PURE__ */ jsx15("dd", { children: renews })
|
|
1961
|
+
] }) : null
|
|
1962
|
+
] }),
|
|
1963
|
+
canceled_at ? /* @__PURE__ */ jsx15(Panel, { children: l10n.billing.cancelNotice }) : null,
|
|
1964
|
+
cancelError ? /* @__PURE__ */ jsx15(Panel, { variant: "danger", children: cancelError }) : null,
|
|
1965
|
+
confirmingCancel ? /* @__PURE__ */ jsxs14(Panel, { children: [
|
|
1966
|
+
/* @__PURE__ */ jsx15("p", { children: l10n.billing.cancelConfirmTitle }),
|
|
1967
|
+
/* @__PURE__ */ jsx15("p", { className: "kerne-subsection-desc", children: l10n.billing.cancelConfirmBody }),
|
|
1968
|
+
/* @__PURE__ */ jsxs14("div", { className: "kerne-inline-actions", children: [
|
|
1969
|
+
/* @__PURE__ */ jsx15(
|
|
1970
|
+
Button,
|
|
1971
|
+
{
|
|
1972
|
+
variant: "outline",
|
|
1973
|
+
type: "button",
|
|
1974
|
+
onClick: () => setConfirmingCancel(false),
|
|
1975
|
+
disabled: isCanceling,
|
|
1976
|
+
children: l10n.billing.cancelConfirmDismiss
|
|
1977
|
+
}
|
|
1978
|
+
),
|
|
1979
|
+
/* @__PURE__ */ jsx15(Button, { type: "button", onClick: handleCancel, loading: isCanceling, children: isCanceling ? l10n.billing.canceling : l10n.billing.cancelConfirmButton })
|
|
1980
|
+
] })
|
|
1981
|
+
] }) : /* @__PURE__ */ jsxs14("div", { className: "kerne-inline-actions", children: [
|
|
1982
|
+
!hidePortalButton ? /* @__PURE__ */ jsx15(Button, { variant: "outline", type: "button", onClick: handlePortal, loading: opening, children: opening ? l10n.billing.opening : l10n.billing.managePortal }) : null,
|
|
1983
|
+
!hideCancelButton && !canceled_at && status !== "CANCELED" ? /* @__PURE__ */ jsx15(Button, { variant: "outline", type: "button", onClick: () => setConfirmingCancel(true), children: l10n.billing.cancelSubscription }) : null
|
|
1984
|
+
] })
|
|
1985
|
+
] })
|
|
1986
|
+
);
|
|
3247
1987
|
}
|
|
3248
1988
|
|
|
3249
|
-
// src/ui/
|
|
3250
|
-
import {
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
1989
|
+
// src/ui/PricingTable.tsx
|
|
1990
|
+
import { createContext, useContext, useEffect as useEffect7, useMemo, useRef as useRef4, useState as useState16 } from "react";
|
|
1991
|
+
|
|
1992
|
+
// src/ui/rate.ts
|
|
1993
|
+
function transformQuantity(rate, units) {
|
|
1994
|
+
const { block_size: size, mode, round } = rate.transform;
|
|
1995
|
+
if (size <= 1) return Math.max(0, units);
|
|
1996
|
+
const blocks = Math.max(0, units) / size;
|
|
1997
|
+
if (mode === "proportional") return blocks;
|
|
1998
|
+
return round === "down" ? Math.floor(blocks) : Math.ceil(blocks);
|
|
1999
|
+
}
|
|
2000
|
+
function cost(rate, units) {
|
|
2001
|
+
const quantity = transformQuantity(rate, units);
|
|
2002
|
+
if (quantity <= 0) return 0;
|
|
2003
|
+
const { selection } = rate;
|
|
2004
|
+
if (selection.kind === "flat") return Math.round(quantity * (selection.unit_amount ?? 0));
|
|
2005
|
+
const tiers = selection.tiers ?? [];
|
|
2006
|
+
if (tiers.length === 0) return 0;
|
|
2007
|
+
if (selection.kind === "volume") {
|
|
2008
|
+
const reached = tiers.find((t) => t.up_to === null || quantity <= t.up_to) ?? tiers[tiers.length - 1];
|
|
2009
|
+
return Math.round(quantity * reached.unit_amount);
|
|
2010
|
+
}
|
|
2011
|
+
let remaining = quantity;
|
|
2012
|
+
let previousBound = 0;
|
|
2013
|
+
let total = 0;
|
|
2014
|
+
for (const tier of tiers) {
|
|
2015
|
+
if (remaining <= 0) break;
|
|
2016
|
+
const span = tier.up_to === null ? remaining : Math.max(0, tier.up_to - previousBound);
|
|
2017
|
+
const taken = Math.min(remaining, span);
|
|
2018
|
+
total += taken * tier.unit_amount;
|
|
2019
|
+
remaining -= taken;
|
|
2020
|
+
previousBound = tier.up_to ?? previousBound;
|
|
2021
|
+
}
|
|
2022
|
+
return Math.round(total);
|
|
2023
|
+
}
|
|
2024
|
+
function rateFloor(rate) {
|
|
2025
|
+
const { selection } = rate;
|
|
2026
|
+
if (selection.kind === "flat") return { amount: selection.unit_amount ?? 0, declines: false };
|
|
2027
|
+
const amounts = (selection.tiers ?? []).map((t) => t.unit_amount);
|
|
2028
|
+
if (amounts.length === 0) return { amount: 0, declines: false };
|
|
2029
|
+
const low = Math.min(...amounts);
|
|
2030
|
+
return { amount: low, declines: low < Math.max(...amounts) };
|
|
2031
|
+
}
|
|
2032
|
+
var GAMMA = 2.2;
|
|
2033
|
+
var TICKS = 1e3;
|
|
2034
|
+
var TICK_LINEAR = 24;
|
|
2035
|
+
var FLAT_REACH = 100;
|
|
2036
|
+
var MAX_REACH = 1e5;
|
|
2037
|
+
function trackTicks(max) {
|
|
2038
|
+
return max > TICK_LINEAR ? TICKS : Math.max(1, max);
|
|
2039
|
+
}
|
|
2040
|
+
function ticksToSteps(tick, max) {
|
|
2041
|
+
if (max <= TICK_LINEAR) return Math.min(max, Math.max(0, tick));
|
|
2042
|
+
if (tick <= 0) return 0;
|
|
2043
|
+
return Math.round(1 + (max - 1) * Math.pow((tick - 1) / (TICKS - 1), GAMMA));
|
|
2044
|
+
}
|
|
2045
|
+
function stepsToTicks(steps, max) {
|
|
2046
|
+
if (max <= TICK_LINEAR) return Math.min(max, Math.max(0, steps));
|
|
2047
|
+
if (steps <= 0) return 0;
|
|
2048
|
+
return 1 + Math.round((TICKS - 1) * Math.pow((steps - 1) / (max - 1), 1 / GAMMA));
|
|
2049
|
+
}
|
|
2050
|
+
function sizingReach(rate) {
|
|
2051
|
+
const bounded = (rate.selection.tiers ?? []).filter((t) => t.up_to !== null);
|
|
2052
|
+
const reach = bounded.length === 0 ? FLAT_REACH : bounded[bounded.length - 1].up_to * 2;
|
|
2053
|
+
return Math.min(MAX_REACH, Math.max(10, Math.round(reach)));
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
// src/ui/PricingTable.tsx
|
|
2057
|
+
import { Fragment as Fragment10, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2058
|
+
function compactLimit(value) {
|
|
2059
|
+
const abs = Math.abs(value);
|
|
2060
|
+
if (abs >= 1e6) return `${trimZero(value / 1e6)}M`;
|
|
2061
|
+
if (abs >= 1e4) return `${trimZero(value / 1e3)}k`;
|
|
2062
|
+
return formatNumber(value);
|
|
2063
|
+
}
|
|
2064
|
+
function trimZero(value) {
|
|
2065
|
+
return String(Math.round(value * 10) / 10);
|
|
2066
|
+
}
|
|
2067
|
+
function entitlementRow(e, l10n, granted = 0) {
|
|
2068
|
+
const label = e.feature_name;
|
|
2069
|
+
if (e.feature_type === "BOOLEAN") {
|
|
2070
|
+
return { label, value: e.value === "1" ? l10n.included : l10n.notIncluded };
|
|
2071
|
+
}
|
|
2072
|
+
if (e.feature_type === "STATIC") return { label, value: e.value || l10n.included };
|
|
2073
|
+
if (e.limit === null) return { label, value: l10n.included };
|
|
2074
|
+
if (e.limit === -1) return { label, value: perSlice(l10n.unlimited, e, l10n) };
|
|
2075
|
+
const total = e.limit + granted;
|
|
2076
|
+
if (total === 0 && e.overage === "BILL") return { label, value: "" };
|
|
2077
|
+
return {
|
|
2078
|
+
label,
|
|
2079
|
+
value: perSlice(withUnit(total, compactLimit(total), e.unit_label), e, l10n)
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
function perSlice(amount, e, l10n) {
|
|
2083
|
+
if (e.limit_binding !== "DIMENSION" || !e.dimension_key) return amount;
|
|
2084
|
+
return fill(l10n.perSlice, { amount, slice: e.dimension_key });
|
|
2085
|
+
}
|
|
2086
|
+
function withUnit(count, amount, unitLabel) {
|
|
2087
|
+
if (!unitLabel) return amount;
|
|
2088
|
+
const unit = count === 1 && unitLabel.endsWith("s") ? unitLabel.slice(0, -1) : unitLabel;
|
|
2089
|
+
return `${amount} ${unit}`;
|
|
2090
|
+
}
|
|
2091
|
+
function rateLead(e, from) {
|
|
2092
|
+
const rate = e.meter?.pricing;
|
|
2093
|
+
if (!rate) return null;
|
|
2094
|
+
const { amount, declines } = rateFloor(rate);
|
|
2095
|
+
const size = rate.transform.block_size;
|
|
2096
|
+
const per = rate.transform.block_label ?? (size === 1 ? e.unit_label ?? "unit" : `${formatNumber(size)} ${e.unit_label ?? "units"}`);
|
|
2097
|
+
return `${declines ? `${from} ` : ""}${formatMoney(amount, rate.currency)} / ${per}`;
|
|
2098
|
+
}
|
|
2099
|
+
function MeterNote({
|
|
2100
|
+
entitlement,
|
|
2101
|
+
lead,
|
|
2102
|
+
interval,
|
|
2103
|
+
children
|
|
3272
2104
|
}) {
|
|
3273
2105
|
const l10n = useLocalization();
|
|
3274
|
-
const
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
2106
|
+
const rate = entitlement.meter?.pricing;
|
|
2107
|
+
const id = `kerne-meter-${entitlement.feature_key}`;
|
|
2108
|
+
const countedPer = entitlement.limit_binding === "DIMENSION" && entitlement.dimension_key ? entitlement.dimension_key : l10n.pricing.meterPerCustomer;
|
|
2109
|
+
const rows = [];
|
|
2110
|
+
if (rate) {
|
|
2111
|
+
const size = rate.transform.block_size;
|
|
2112
|
+
const per = rate.transform.block_label ?? (size === 1 ? entitlement.unit_label ?? "unit" : `${formatNumber(size)} ${entitlement.unit_label ?? "units"}`);
|
|
2113
|
+
const tiers = rate.selection.tiers ?? [];
|
|
2114
|
+
const first = rate.selection.unit_amount ?? tiers[0]?.unit_amount ?? 0;
|
|
2115
|
+
rows.push({
|
|
2116
|
+
key: fill(l10n.pricing.meterRatePer, { thing: countedPer }),
|
|
2117
|
+
value: `${formatMoney(first, rate.currency)} / ${per}`
|
|
2118
|
+
});
|
|
2119
|
+
if (tiers.length > 1) {
|
|
2120
|
+
rows.push({
|
|
2121
|
+
key: l10n.pricing.meterShape,
|
|
2122
|
+
value: `${fill(l10n.pricing.meterTiers, {
|
|
2123
|
+
count: tiers.length,
|
|
2124
|
+
amount: formatMoney(rateFloor(rate).amount, rate.currency)
|
|
2125
|
+
})}, ${rate.selection.kind === "volume" ? l10n.pricing.meterVolume : l10n.pricing.meterGraduated}`
|
|
2126
|
+
});
|
|
3289
2127
|
}
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
label: t.label,
|
|
3296
|
-
title: t.title ?? t.label,
|
|
3297
|
-
description: t.description
|
|
3298
|
-
}))
|
|
3299
|
-
];
|
|
3300
|
-
const [internalTab, setInternalTab] = useState18(defaultTab ?? allTabs[0]?.id ?? "profile");
|
|
3301
|
-
const current = controlledTab ?? internalTab;
|
|
3302
|
-
const select = (id) => {
|
|
3303
|
-
if (controlledTab === void 0) setInternalTab(id);
|
|
3304
|
-
onTabChange?.(id);
|
|
3305
|
-
};
|
|
3306
|
-
const active = allTabs.find((t) => t.id === current) ?? allTabs[0];
|
|
3307
|
-
const renderPane = () => {
|
|
3308
|
-
switch (active?.id) {
|
|
3309
|
-
case "profile":
|
|
3310
|
-
return /* @__PURE__ */ jsx18(ProfileSection, { verificationCallbackUrl, onSaved });
|
|
3311
|
-
case "security":
|
|
3312
|
-
return /* @__PURE__ */ jsx18(SecuritySection, { showDeleteAccount, onDeleted });
|
|
3313
|
-
case "billing":
|
|
3314
|
-
return /* @__PURE__ */ jsx18(
|
|
3315
|
-
BillingSection,
|
|
3316
|
-
{
|
|
3317
|
-
productSlug,
|
|
3318
|
-
pricingProductIdOrSlug,
|
|
3319
|
-
portalReturnUrl,
|
|
3320
|
-
checkoutSuccessUrl,
|
|
3321
|
-
checkoutCancelUrl,
|
|
3322
|
-
showPlanSwitcher,
|
|
3323
|
-
showUsage,
|
|
3324
|
-
usageFeatureKeys,
|
|
3325
|
-
usageLabels
|
|
3326
|
-
}
|
|
3327
|
-
);
|
|
3328
|
-
default:
|
|
3329
|
-
return extraTabs.find((t) => t.id === active?.id)?.content ?? null;
|
|
2128
|
+
if (size > 1) {
|
|
2129
|
+
rows.push({
|
|
2130
|
+
key: l10n.pricing.meterCounted,
|
|
2131
|
+
value: rate.transform.round === "down" ? l10n.pricing.meterRoundedDown : l10n.pricing.meterRoundedUp
|
|
2132
|
+
});
|
|
3330
2133
|
}
|
|
3331
|
-
}
|
|
3332
|
-
|
|
3333
|
-
|
|
2134
|
+
}
|
|
2135
|
+
const resets = entitlement.reset_policy === "ACCUMULATE" ? l10n.pricing.meterResetsNever : entitlement.quota_period === "BILLING_CYCLE" ? interval ? fill(l10n.pricing.meterResetsEvery, {
|
|
2136
|
+
interval: formatInterval(interval, l10n.billing)
|
|
2137
|
+
}) : l10n.pricing.meterResetsCycle : entitlement.quota_period ? entitlement.quota_period.toLowerCase() : null;
|
|
2138
|
+
if (resets) rows.push({ key: l10n.pricing.meterResets, value: resets });
|
|
2139
|
+
if (entitlement.overage_cap_amount != null && rate) {
|
|
2140
|
+
rows.push({
|
|
2141
|
+
key: l10n.pricing.meterCapped,
|
|
2142
|
+
value: formatMoney(entitlement.overage_cap_amount, rate.currency)
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
return /* @__PURE__ */ jsxs15("span", { className: "kerne-pricing-pop-host", children: [
|
|
2146
|
+
/* @__PURE__ */ jsx16(
|
|
3334
2147
|
"button",
|
|
3335
2148
|
{
|
|
3336
2149
|
type: "button",
|
|
3337
|
-
className: "kerne-
|
|
3338
|
-
"
|
|
3339
|
-
|
|
3340
|
-
children
|
|
3341
|
-
}
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
/* @__PURE__ */
|
|
3347
|
-
|
|
3348
|
-
] })
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
] }) });
|
|
3352
|
-
}
|
|
3353
|
-
|
|
3354
|
-
// src/ui/PricingTable.tsx
|
|
3355
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect7, useMemo as useMemo2, useRef as useRef4, useState as useState19 } from "react";
|
|
3356
|
-
import { Fragment as Fragment12, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
3357
|
-
function formatEntitlement(e, unlimitedLabel) {
|
|
3358
|
-
if (e.feature_type === "BOOLEAN") return e.feature_name;
|
|
3359
|
-
if (e.feature_type === "STATIC")
|
|
3360
|
-
return e.value ? `${e.feature_name}: ${e.value}` : e.feature_name;
|
|
3361
|
-
if (e.limit === null) return e.feature_name;
|
|
3362
|
-
if (e.limit === -1) return `${unlimitedLabel} ${e.feature_name.toLowerCase()}`;
|
|
3363
|
-
return `${formatNumber(e.limit)} ${e.feature_name.toLowerCase()}`;
|
|
2150
|
+
className: "kerne-pricing-feature-note",
|
|
2151
|
+
"data-lead": lead ? "true" : void 0,
|
|
2152
|
+
"aria-describedby": id,
|
|
2153
|
+
children
|
|
2154
|
+
}
|
|
2155
|
+
),
|
|
2156
|
+
rows.length > 0 ? /* @__PURE__ */ jsxs15("span", { className: "kerne-pricing-pop", id, role: "tooltip", children: [
|
|
2157
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-pop-title", children: l10n.pricing.meterTitle }),
|
|
2158
|
+
rows.map((row) => /* @__PURE__ */ jsxs15("span", { className: "kerne-pricing-pop-row", children: [
|
|
2159
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-pop-key", children: row.key }),
|
|
2160
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-pop-value", children: row.value })
|
|
2161
|
+
] }, row.key))
|
|
2162
|
+
] }) : null
|
|
2163
|
+
] });
|
|
3364
2164
|
}
|
|
3365
|
-
var PricingTableContext =
|
|
2165
|
+
var PricingTableContext = createContext(null);
|
|
3366
2166
|
function usePricingTable() {
|
|
3367
|
-
const ctx =
|
|
2167
|
+
const ctx = useContext(PricingTableContext);
|
|
3368
2168
|
if (!ctx) {
|
|
3369
2169
|
throw new Error("usePricingTable() must be called inside <PricingTable>.");
|
|
3370
2170
|
}
|
|
@@ -3373,8 +2173,6 @@ function usePricingTable() {
|
|
|
3373
2173
|
function PricingTable({
|
|
3374
2174
|
product,
|
|
3375
2175
|
defaultLabel,
|
|
3376
|
-
checkoutSuccessUrl,
|
|
3377
|
-
checkoutCancelUrl,
|
|
3378
2176
|
onSelectPlan,
|
|
3379
2177
|
appearance,
|
|
3380
2178
|
className,
|
|
@@ -3384,7 +2182,7 @@ function PricingTable({
|
|
|
3384
2182
|
const { subscription, refetch: refetchSubscription } = useSubscription(product);
|
|
3385
2183
|
const l10n = useLocalization();
|
|
3386
2184
|
const showError = useErrorResolver();
|
|
3387
|
-
const [selectedLabelSlug, setSelectedLabelSlug] =
|
|
2185
|
+
const [selectedLabelSlug, setSelectedLabelSlug] = useState16(defaultLabel ?? null);
|
|
3388
2186
|
const {
|
|
3389
2187
|
pending: pendingSwitch,
|
|
3390
2188
|
switching,
|
|
@@ -3394,25 +2192,22 @@ function PricingTable({
|
|
|
3394
2192
|
selectPlan,
|
|
3395
2193
|
confirmSwitch,
|
|
3396
2194
|
dismissSwitch
|
|
3397
|
-
} = usePlanSwitch(subscription, refetchSubscription
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
});
|
|
3401
|
-
const labels = useMemo2(() => collectPriceLabels(plans), [plans]);
|
|
3402
|
-
const savings = useMemo2(() => resolveLabelSavings(plans, labels), [plans, labels]);
|
|
2195
|
+
} = usePlanSwitch(subscription, refetchSubscription);
|
|
2196
|
+
const labels = useMemo(() => collectPriceLabels(plans), [plans]);
|
|
2197
|
+
const savings = useMemo(() => resolveLabelSavings(plans, labels), [plans, labels]);
|
|
3403
2198
|
useEffect7(() => {
|
|
3404
2199
|
if (labels.length === 0) return;
|
|
3405
2200
|
if (selectedLabelSlug && labels.some((l) => l.slug === selectedLabelSlug)) return;
|
|
3406
2201
|
setSelectedLabelSlug(labels[0].slug);
|
|
3407
2202
|
}, [labels]);
|
|
3408
|
-
const handleSelectPlan = (plan) => {
|
|
3409
|
-
const price = plan.prices.find((p) => p.label.slug === selectedLabelSlug)
|
|
2203
|
+
const handleSelectPlan = (plan, drivers) => {
|
|
2204
|
+
const price = selectedLabelSlug ? plan.prices.find((p) => p.label.slug === selectedLabelSlug) : plan.prices[0];
|
|
3410
2205
|
if (!price) return;
|
|
3411
2206
|
if (onSelectPlan) {
|
|
3412
|
-
onSelectPlan(plan, price);
|
|
2207
|
+
onSelectPlan(plan, price, drivers);
|
|
3413
2208
|
return;
|
|
3414
2209
|
}
|
|
3415
|
-
void selectPlan(plan, price);
|
|
2210
|
+
void selectPlan(plan, price, drivers);
|
|
3416
2211
|
};
|
|
3417
2212
|
const ctx = {
|
|
3418
2213
|
plans,
|
|
@@ -3421,17 +2216,31 @@ function PricingTable({
|
|
|
3421
2216
|
setSelectedLabelSlug,
|
|
3422
2217
|
savings,
|
|
3423
2218
|
currentPlanId: subscription?.plan?.id ?? null,
|
|
2219
|
+
currentPriceCurrency: subscription?.plan_price.currency ?? null,
|
|
3424
2220
|
busyPriceId,
|
|
3425
2221
|
onSelectPlan: handleSelectPlan
|
|
3426
2222
|
};
|
|
3427
|
-
return /* @__PURE__ */
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
/* @__PURE__ */
|
|
3432
|
-
|
|
3433
|
-
/* @__PURE__ */
|
|
3434
|
-
|
|
2223
|
+
return /* @__PURE__ */ jsx16(Shell, { appearance, className, size: "wide", children: isLoading ? (
|
|
2224
|
+
// The shape of what is coming, not one grey slab: a card-sized
|
|
2225
|
+
// placeholder per column means the page does not jump when plans land.
|
|
2226
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-pricing-grid", "aria-busy": "true", "aria-label": l10n.pricing.loading, children: [0, 1, 2].map((index) => /* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-card kerne-pricing-card-ghost", children: [
|
|
2227
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-skeleton", style: { height: 20, width: "45%" } }),
|
|
2228
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-skeleton", style: { height: 38, width: "60%" } }),
|
|
2229
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-skeleton", style: { height: 40 } }),
|
|
2230
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-ghost-rows", children: [
|
|
2231
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-skeleton", style: { height: 12 } }),
|
|
2232
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-skeleton", style: { height: 12 } }),
|
|
2233
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-skeleton", style: { height: 12, width: "70%" } })
|
|
2234
|
+
] })
|
|
2235
|
+
] }, index)) })
|
|
2236
|
+
) : error ? /* @__PURE__ */ jsx16(Panel, { variant: "danger", children: l10n.pricing.loadFailed }) : /* @__PURE__ */ jsxs15(PricingTableContext.Provider, { value: ctx, children: [
|
|
2237
|
+
checkoutError ? /* @__PURE__ */ jsx16(Panel, { variant: "danger", children: showError(checkoutError) }) : null,
|
|
2238
|
+
pendingSwitch ? /* @__PURE__ */ jsxs15(Panel, { children: [
|
|
2239
|
+
/* @__PURE__ */ jsx16("p", { children: fill(l10n.billing.switchConfirmTitle, { plan: pendingSwitch.plan.name }) }),
|
|
2240
|
+
/* @__PURE__ */ jsx16("p", { className: "kerne-subsection-desc", children: l10n.billing.switchConfirmBody }),
|
|
2241
|
+
switchError ? /* @__PURE__ */ jsx16(Panel, { variant: "danger", children: showError(switchError) }) : null,
|
|
2242
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-inline-actions", children: [
|
|
2243
|
+
/* @__PURE__ */ jsx16(
|
|
3435
2244
|
Button,
|
|
3436
2245
|
{
|
|
3437
2246
|
variant: "outline",
|
|
@@ -3441,7 +2250,7 @@ function PricingTable({
|
|
|
3441
2250
|
children: l10n.billing.switchConfirmDismiss
|
|
3442
2251
|
}
|
|
3443
2252
|
),
|
|
3444
|
-
/* @__PURE__ */
|
|
2253
|
+
/* @__PURE__ */ jsx16(Button, { type: "button", onClick: confirmSwitch, loading: switching, children: switching ? l10n.billing.switching : l10n.billing.switchConfirmButton })
|
|
3445
2254
|
] })
|
|
3446
2255
|
] }) : null,
|
|
3447
2256
|
children
|
|
@@ -3452,7 +2261,7 @@ function LabelSwitch() {
|
|
|
3452
2261
|
const l10n = useLocalization();
|
|
3453
2262
|
const containerRef = useRef4(null);
|
|
3454
2263
|
const buttonRefs = useRef4(/* @__PURE__ */ new Map());
|
|
3455
|
-
const [thumb, setThumb] =
|
|
2264
|
+
const [thumb, setThumb] = useState16(null);
|
|
3456
2265
|
useEffect7(() => {
|
|
3457
2266
|
const measure = () => {
|
|
3458
2267
|
const container = containerRef.current;
|
|
@@ -3467,7 +2276,7 @@ function LabelSwitch() {
|
|
|
3467
2276
|
return () => window.removeEventListener("resize", measure);
|
|
3468
2277
|
}, [selectedLabelSlug, labels]);
|
|
3469
2278
|
if (labels.length < 2) return null;
|
|
3470
|
-
return /* @__PURE__ */
|
|
2279
|
+
return /* @__PURE__ */ jsx16("div", { className: "kerne-pricing-switch-wrap", children: /* @__PURE__ */ jsxs15(
|
|
3471
2280
|
"div",
|
|
3472
2281
|
{
|
|
3473
2282
|
ref: containerRef,
|
|
@@ -3475,7 +2284,7 @@ function LabelSwitch() {
|
|
|
3475
2284
|
role: "group",
|
|
3476
2285
|
"aria-label": l10n.billing.intervalGroupLabel,
|
|
3477
2286
|
children: [
|
|
3478
|
-
thumb ? /* @__PURE__ */
|
|
2287
|
+
thumb ? /* @__PURE__ */ jsx16(
|
|
3479
2288
|
"span",
|
|
3480
2289
|
{
|
|
3481
2290
|
className: "kerne-pricing-switch-thumb",
|
|
@@ -3483,7 +2292,7 @@ function LabelSwitch() {
|
|
|
3483
2292
|
"aria-hidden": "true"
|
|
3484
2293
|
}
|
|
3485
2294
|
) : null,
|
|
3486
|
-
labels.map((label) => /* @__PURE__ */
|
|
2295
|
+
labels.map((label) => /* @__PURE__ */ jsxs15(
|
|
3487
2296
|
"button",
|
|
3488
2297
|
{
|
|
3489
2298
|
ref: (el) => {
|
|
@@ -3496,7 +2305,7 @@ function LabelSwitch() {
|
|
|
3496
2305
|
onClick: () => setSelectedLabelSlug(label.slug),
|
|
3497
2306
|
children: [
|
|
3498
2307
|
label.name,
|
|
3499
|
-
savings && savings.labelSlug === label.slug ? /* @__PURE__ */
|
|
2308
|
+
savings && savings.labelSlug === label.slug ? /* @__PURE__ */ jsx16("span", { className: "kerne-pricing-switch-save", children: fill(l10n.pricing.save, { percent: savings.percent }) }) : null
|
|
3500
2309
|
]
|
|
3501
2310
|
},
|
|
3502
2311
|
label.slug
|
|
@@ -3506,7 +2315,7 @@ function LabelSwitch() {
|
|
|
3506
2315
|
) });
|
|
3507
2316
|
}
|
|
3508
2317
|
function useAnimatedAmount(target, durationMs = 420) {
|
|
3509
|
-
const [displayed, setDisplayed] =
|
|
2318
|
+
const [displayed, setDisplayed] = useState16(target);
|
|
3510
2319
|
const displayedRef = useRef4(target);
|
|
3511
2320
|
const frameRef = useRef4();
|
|
3512
2321
|
useEffect7(() => {
|
|
@@ -3535,7 +2344,14 @@ function useAnimatedAmount(target, durationMs = 420) {
|
|
|
3535
2344
|
return displayed;
|
|
3536
2345
|
}
|
|
3537
2346
|
function Plan({ slug, featured, badge, maxFeatures }) {
|
|
3538
|
-
const {
|
|
2347
|
+
const {
|
|
2348
|
+
plans,
|
|
2349
|
+
selectedLabelSlug,
|
|
2350
|
+
currentPlanId,
|
|
2351
|
+
currentPriceCurrency,
|
|
2352
|
+
busyPriceId,
|
|
2353
|
+
onSelectPlan
|
|
2354
|
+
} = usePricingTable();
|
|
3539
2355
|
const l10n = useLocalization();
|
|
3540
2356
|
const plan = plans.find((p) => p.slug === slug);
|
|
3541
2357
|
useEffect7(() => {
|
|
@@ -3545,80 +2361,158 @@ function Plan({ slug, featured, badge, maxFeatures }) {
|
|
|
3545
2361
|
);
|
|
3546
2362
|
}
|
|
3547
2363
|
}, [plan, slug]);
|
|
3548
|
-
const price = plan?.prices.find((p) => p.label.slug === selectedLabelSlug)
|
|
2364
|
+
const price = selectedLabelSlug ? plan?.prices.find((p) => p.label.slug === selectedLabelSlug) : plan?.prices[0];
|
|
3549
2365
|
const resolved = price ? resolvePriceDisplay(price, price.label.display_interval) : null;
|
|
3550
|
-
const
|
|
2366
|
+
const [sizing, setSizing] = useState16({});
|
|
2367
|
+
const sizable = price && plan ? plan.entitlements.map((e) => ({ e, driver: e.drivers.find((d) => d.interval === price.interval) })).filter(
|
|
2368
|
+
(row) => Boolean(row.driver)
|
|
2369
|
+
) : [];
|
|
2370
|
+
const granted = (featureKey) => {
|
|
2371
|
+
const row = sizable.find((r) => r.e.feature_key === featureKey);
|
|
2372
|
+
return row ? (sizing[featureKey] ?? 0) * row.driver.step_units : 0;
|
|
2373
|
+
};
|
|
2374
|
+
const restate = resolved && resolved.billedAmount > 0 ? resolved.displayAmount / resolved.billedAmount : 1;
|
|
2375
|
+
const added = sizable.reduce(
|
|
2376
|
+
(sum, { e, driver }) => sum + cost(driver.pricing, sizing[e.feature_key] ?? 0) * restate,
|
|
2377
|
+
0
|
|
2378
|
+
);
|
|
2379
|
+
const animatedAmount = useAnimatedAmount((resolved?.displayAmount ?? 0) + added);
|
|
2380
|
+
const selectedDrivers = () => sizable.filter(({ e }) => (sizing[e.feature_key] ?? 0) > 0).map(({ e }) => ({ feature_key: e.feature_key, steps: sizing[e.feature_key] }));
|
|
3551
2381
|
if (!plan) return null;
|
|
3552
2382
|
const isCurrent = plan.id === currentPlanId;
|
|
3553
2383
|
const isBusy = !!price && busyPriceId === price.id;
|
|
2384
|
+
const currencyMismatch = !!price && !!currentPriceCurrency && price.currency.toLowerCase() !== currentPriceCurrency.toLowerCase();
|
|
3554
2385
|
const shownEntitlements = maxFeatures ? plan.entitlements.slice(0, maxFeatures) : plan.entitlements;
|
|
3555
2386
|
const remaining = plan.entitlements.length - shownEntitlements.length;
|
|
2387
|
+
const isFree = !!resolved && resolved.billedAmount === 0 && added === 0;
|
|
2388
|
+
const trialDays = price?.trial_days ?? plan.trial_days;
|
|
3556
2389
|
const billedCaption = resolved && resolved.displayInterval !== resolved.billedInterval ? fill(l10n.pricing.billedAt, {
|
|
3557
2390
|
amount: formatMoney(resolved.billedAmount, price.currency),
|
|
3558
2391
|
interval: formatInterval(resolved.billedInterval, l10n.billing)
|
|
3559
2392
|
}) : null;
|
|
3560
|
-
return /* @__PURE__ */
|
|
2393
|
+
return /* @__PURE__ */ jsxs15(
|
|
3561
2394
|
"div",
|
|
3562
2395
|
{
|
|
3563
2396
|
className: "kerne-pricing-card",
|
|
3564
2397
|
"data-featured": featured ? "true" : void 0,
|
|
3565
2398
|
"data-current": isCurrent ? "true" : void 0,
|
|
3566
2399
|
children: [
|
|
3567
|
-
/* @__PURE__ */
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
] }),
|
|
3572
|
-
/* @__PURE__ */ jsx19("div", { className: "kerne-pricing-price", children: resolved ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
|
|
3573
|
-
/* @__PURE__ */ jsxs18("div", { className: "kerne-pricing-price-row", children: [
|
|
3574
|
-
/* @__PURE__ */ jsx19("span", { className: "kerne-pricing-amount", children: formatMoney(animatedAmount, price.currency) }),
|
|
3575
|
-
/* @__PURE__ */ jsxs18("span", { className: "kerne-pricing-interval", children: [
|
|
3576
|
-
"/ ",
|
|
3577
|
-
formatInterval(resolved.displayInterval, l10n.billing)
|
|
3578
|
-
] })
|
|
2400
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-card-head", children: [
|
|
2401
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-card-titles", children: [
|
|
2402
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-card-name", children: plan.name }),
|
|
2403
|
+
badge ? /* @__PURE__ */ jsx16("span", { className: "kerne-pricing-badge", children: badge }) : null
|
|
3579
2404
|
] }),
|
|
3580
|
-
|
|
3581
|
-
] })
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
/* @__PURE__ */
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
2405
|
+
plan.description ? /* @__PURE__ */ jsx16("p", { className: "kerne-pricing-card-desc", children: plan.description }) : null
|
|
2406
|
+
] }),
|
|
2407
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-body", children: [
|
|
2408
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-pricing-price", children: resolved ? /* @__PURE__ */ jsxs15(Fragment10, { children: [
|
|
2409
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-price-row", children: [
|
|
2410
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-amount", children: isFree ? l10n.pricing.free : formatMoney(animatedAmount, price.currency) }),
|
|
2411
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-interval", children: isFree ? l10n.pricing.forever : `/ ${formatInterval(resolved.displayInterval, l10n.billing)}` })
|
|
2412
|
+
] }),
|
|
2413
|
+
billedCaption && !isFree ? /* @__PURE__ */ jsx16("p", { className: "kerne-pricing-billed", children: billedCaption }) : null
|
|
2414
|
+
] }) : /* @__PURE__ */ jsx16("p", { className: "kerne-pricing-billed", children: l10n.billing.notOnInterval }) }),
|
|
2415
|
+
sizable.length > 0 && !currentPlanId ? /* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-sizer", children: [
|
|
2416
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-sizer-head", children: [
|
|
2417
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-sizer-title", children: l10n.pricing.sizeUpFront }),
|
|
2418
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-sizer-note", children: l10n.pricing.billedWithPlan })
|
|
2419
|
+
] }),
|
|
2420
|
+
sizable.map(({ e, driver }) => {
|
|
2421
|
+
const steps = sizing[e.feature_key] ?? 0;
|
|
2422
|
+
const max = Math.max(
|
|
2423
|
+
1,
|
|
2424
|
+
driver.max_steps ?? Math.max(sizingReach(driver.pricing), steps)
|
|
2425
|
+
);
|
|
2426
|
+
const units = steps * driver.step_units;
|
|
2427
|
+
const total = (e.limit ?? 0) + units;
|
|
2428
|
+
return /* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-sizer-row", children: [
|
|
2429
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-sizer-line", children: [
|
|
2430
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-sizer-name", children: e.feature_name }),
|
|
2431
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-sizer-value", children: withUnit(total, formatNumber(total), e.unit_label) })
|
|
2432
|
+
] }),
|
|
2433
|
+
/* @__PURE__ */ jsx16(
|
|
2434
|
+
"input",
|
|
2435
|
+
{
|
|
2436
|
+
className: "kerne-pricing-range",
|
|
2437
|
+
type: "range",
|
|
2438
|
+
min: 0,
|
|
2439
|
+
max: trackTicks(max),
|
|
2440
|
+
step: 1,
|
|
2441
|
+
value: stepsToTicks(steps, max),
|
|
2442
|
+
"aria-label": fill(l10n.pricing.sizeUpFront, {}),
|
|
2443
|
+
onChange: (event) => setSizing((current) => ({
|
|
2444
|
+
...current,
|
|
2445
|
+
[e.feature_key]: ticksToSteps(Number(event.target.value) || 0, max)
|
|
2446
|
+
})),
|
|
2447
|
+
style: {
|
|
2448
|
+
"--kerne-fill": `${Math.round(
|
|
2449
|
+
stepsToTicks(steps, max) / trackTicks(max) * 100
|
|
2450
|
+
)}%`
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
),
|
|
2454
|
+
/* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-sizer-line", children: [
|
|
2455
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-sizer-foot", children: fill(
|
|
2456
|
+
steps === 0 ? l10n.pricing.includedAmount : l10n.pricing.prepaidAmount,
|
|
2457
|
+
{
|
|
2458
|
+
amount: withUnit(
|
|
2459
|
+
steps === 0 ? e.limit ?? 0 : units,
|
|
2460
|
+
formatNumber(steps === 0 ? e.limit ?? 0 : units),
|
|
2461
|
+
e.unit_label
|
|
2462
|
+
)
|
|
2463
|
+
}
|
|
2464
|
+
) }),
|
|
2465
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-sizer-foot", "data-strong": "true", children: steps === 0 ? l10n.pricing.included : `+${formatMoney(cost(driver.pricing, steps) * restate, price.currency)}` })
|
|
2466
|
+
] })
|
|
2467
|
+
] }, e.feature_key);
|
|
2468
|
+
})
|
|
2469
|
+
] }) : null,
|
|
2470
|
+
/* @__PURE__ */ jsx16("div", { className: "kerne-pricing-cta", children: isCurrent ? /* @__PURE__ */ jsx16(Button, { variant: "outline", type: "button", disabled: true, children: l10n.billing.current }) : currencyMismatch ? /* @__PURE__ */ jsx16("p", { className: "kerne-pricing-billed", children: l10n.billing.differentCurrency }) : /* @__PURE__ */ jsx16(
|
|
2471
|
+
Button,
|
|
2472
|
+
{
|
|
2473
|
+
variant: featured ? "primary" : "outline",
|
|
2474
|
+
type: "button",
|
|
2475
|
+
disabled: !price || busyPriceId !== null && !isBusy,
|
|
2476
|
+
loading: isBusy,
|
|
2477
|
+
onClick: () => onSelectPlan(plan, selectedDrivers()),
|
|
2478
|
+
children: currentPlanId ? l10n.billing.switchTo : isFree ? l10n.pricing.startFree : l10n.pricing.getStarted
|
|
2479
|
+
}
|
|
2480
|
+
) }),
|
|
2481
|
+
trialDays > 0 && !isCurrent ? /* @__PURE__ */ jsx16("p", { className: "kerne-pricing-trial", children: fill(l10n.pricing.trialNote, { days: trialDays }) }) : null
|
|
2482
|
+
] }),
|
|
2483
|
+
/* @__PURE__ */ jsxs15("ul", { className: "kerne-pricing-features", children: [
|
|
2484
|
+
shownEntitlements.length === 0 ? /* @__PURE__ */ jsx16("li", { className: "kerne-pricing-feature-empty", children: l10n.pricing.nothingListed }) : shownEntitlements.map((e) => {
|
|
2485
|
+
const row = entitlementRow(e, l10n.pricing, granted(e.feature_key));
|
|
2486
|
+
const note = e.overage === "BILL" ? row.value === "" ? rateLead(e, l10n.pricing.from) ?? l10n.pricing.metered : l10n.pricing.thenMetered : null;
|
|
2487
|
+
return /* @__PURE__ */ jsxs15("li", { className: "kerne-pricing-feature", children: [
|
|
2488
|
+
/* @__PURE__ */ jsx16("span", { className: "kerne-pricing-feature-name", children: row.label }),
|
|
2489
|
+
/* @__PURE__ */ jsxs15("span", { className: "kerne-pricing-feature-side", children: [
|
|
2490
|
+
row.value ? /* @__PURE__ */ jsx16("span", { className: "kerne-pricing-feature-value", children: row.value }) : null,
|
|
2491
|
+
note ? /* @__PURE__ */ jsx16(
|
|
2492
|
+
MeterNote,
|
|
2493
|
+
{
|
|
2494
|
+
entitlement: e,
|
|
2495
|
+
lead: row.value === "",
|
|
2496
|
+
interval: price?.interval ?? null,
|
|
2497
|
+
children: note
|
|
2498
|
+
}
|
|
2499
|
+
) : null
|
|
2500
|
+
] })
|
|
2501
|
+
] }, e.feature_key);
|
|
2502
|
+
}),
|
|
2503
|
+
remaining > 0 ? /* @__PURE__ */ jsx16("li", { className: "kerne-pricing-feature-more", children: fill(l10n.pricing.moreFeatures, { count: remaining }) }) : null
|
|
2504
|
+
] })
|
|
3600
2505
|
]
|
|
3601
2506
|
}
|
|
3602
2507
|
);
|
|
3603
2508
|
}
|
|
3604
|
-
function CheckIcon2() {
|
|
3605
|
-
return /* @__PURE__ */ jsx19(
|
|
3606
|
-
"svg",
|
|
3607
|
-
{
|
|
3608
|
-
width: "15",
|
|
3609
|
-
height: "15",
|
|
3610
|
-
viewBox: "0 0 24 24",
|
|
3611
|
-
fill: "none",
|
|
3612
|
-
stroke: "currentColor",
|
|
3613
|
-
strokeWidth: "2",
|
|
3614
|
-
"aria-hidden": "true",
|
|
3615
|
-
children: /* @__PURE__ */ jsx19("path", { d: "m5 13 4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
3616
|
-
}
|
|
3617
|
-
);
|
|
3618
|
-
}
|
|
3619
2509
|
function Plans({ featuredSlug, featuredBadge, maxFeatures }) {
|
|
3620
2510
|
const { plans } = usePricingTable();
|
|
3621
|
-
|
|
2511
|
+
const l10n = useLocalization();
|
|
2512
|
+
if (plans.length === 0) {
|
|
2513
|
+
return /* @__PURE__ */ jsx16("p", { className: "kerne-subsection-desc", children: l10n.billing.noPlans });
|
|
2514
|
+
}
|
|
2515
|
+
return /* @__PURE__ */ jsx16("div", { className: "kerne-pricing-grid", children: plans.map((plan) => /* @__PURE__ */ jsx16(
|
|
3622
2516
|
Plan,
|
|
3623
2517
|
{
|
|
3624
2518
|
slug: plan.slug,
|
|
@@ -3629,13 +2523,192 @@ function Plans({ featuredSlug, featuredBadge, maxFeatures }) {
|
|
|
3629
2523
|
plan.id
|
|
3630
2524
|
)) });
|
|
3631
2525
|
}
|
|
2526
|
+
function Header({ eyebrow, headline, lede }) {
|
|
2527
|
+
return /* @__PURE__ */ jsxs15("div", { className: "kerne-pricing-header", children: [
|
|
2528
|
+
eyebrow ? /* @__PURE__ */ jsx16("span", { className: "kerne-pricing-eyebrow", children: eyebrow }) : null,
|
|
2529
|
+
/* @__PURE__ */ jsx16("h2", { className: "kerne-pricing-headline", children: headline }),
|
|
2530
|
+
lede ? /* @__PURE__ */ jsx16("p", { className: "kerne-pricing-lede", children: lede }) : null
|
|
2531
|
+
] });
|
|
2532
|
+
}
|
|
2533
|
+
PricingTable.Header = Header;
|
|
3632
2534
|
PricingTable.LabelSwitch = LabelSwitch;
|
|
3633
2535
|
PricingTable.Plan = Plan;
|
|
3634
2536
|
PricingTable.Plans = Plans;
|
|
3635
2537
|
|
|
2538
|
+
// src/ui/profile/BillingSection.tsx
|
|
2539
|
+
import { Fragment as Fragment11, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2540
|
+
function BillingSection({
|
|
2541
|
+
productSlug,
|
|
2542
|
+
pricingProductIdOrSlug,
|
|
2543
|
+
showPlanSwitcher = true,
|
|
2544
|
+
showUsage = true,
|
|
2545
|
+
usageFeatureKeys,
|
|
2546
|
+
usageLabels
|
|
2547
|
+
}) {
|
|
2548
|
+
const l10n = useLocalization();
|
|
2549
|
+
return /* @__PURE__ */ jsxs16(Fragment11, { children: [
|
|
2550
|
+
/* @__PURE__ */ jsx17(Subsection, { title: l10n.billing.currentPlan, children: /* @__PURE__ */ jsx17(SubscriptionCard, { title: null, productSlug }) }),
|
|
2551
|
+
showUsage ? /* @__PURE__ */ jsx17(Subsection, { title: l10n.usage.title, description: l10n.usage.subtitle, children: /* @__PURE__ */ jsx17(UsageMeters, { title: null, featureKeys: usageFeatureKeys, labels: usageLabels }) }) : null,
|
|
2552
|
+
showPlanSwitcher ? /* @__PURE__ */ jsx17(Subsection, { title: l10n.billing.changePlan, children: /* @__PURE__ */ jsxs16(PricingTable, { product: pricingProductIdOrSlug, children: [
|
|
2553
|
+
/* @__PURE__ */ jsx17(PricingTable.LabelSwitch, {}),
|
|
2554
|
+
/* @__PURE__ */ jsx17(PricingTable.Plans, {})
|
|
2555
|
+
] }) }) : null
|
|
2556
|
+
] });
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
// src/ui/UserProfile.tsx
|
|
2560
|
+
import { Fragment as Fragment12, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2561
|
+
function UserProfile({
|
|
2562
|
+
tabs = ["profile", "security", "billing"],
|
|
2563
|
+
extraTabs = [],
|
|
2564
|
+
defaultTab,
|
|
2565
|
+
activeTab: controlledTab,
|
|
2566
|
+
onTabChange,
|
|
2567
|
+
showDeleteAccount = true,
|
|
2568
|
+
onSaved,
|
|
2569
|
+
onDeleted,
|
|
2570
|
+
productSlug,
|
|
2571
|
+
pricingProductIdOrSlug,
|
|
2572
|
+
showPlanSwitcher = true,
|
|
2573
|
+
showUsage = true,
|
|
2574
|
+
usageFeatureKeys,
|
|
2575
|
+
usageLabels,
|
|
2576
|
+
appearance,
|
|
2577
|
+
className
|
|
2578
|
+
}) {
|
|
2579
|
+
const l10n = useLocalization();
|
|
2580
|
+
const builtins = {
|
|
2581
|
+
profile: {
|
|
2582
|
+
label: l10n.profile.tabProfile,
|
|
2583
|
+
title: l10n.profile.profileTitle,
|
|
2584
|
+
description: l10n.profile.profileDesc
|
|
2585
|
+
},
|
|
2586
|
+
security: {
|
|
2587
|
+
label: l10n.profile.tabSecurity,
|
|
2588
|
+
title: l10n.profile.securityTitle,
|
|
2589
|
+
description: l10n.profile.securityDesc
|
|
2590
|
+
},
|
|
2591
|
+
billing: {
|
|
2592
|
+
label: l10n.profile.tabBilling,
|
|
2593
|
+
title: l10n.profile.billingTitle,
|
|
2594
|
+
description: l10n.profile.billingDesc
|
|
2595
|
+
}
|
|
2596
|
+
};
|
|
2597
|
+
const allTabs = [
|
|
2598
|
+
...tabs.map((id) => ({ id, ...builtins[id] })),
|
|
2599
|
+
...extraTabs.map((t) => ({
|
|
2600
|
+
id: t.id,
|
|
2601
|
+
label: t.label,
|
|
2602
|
+
title: t.title ?? t.label,
|
|
2603
|
+
description: t.description
|
|
2604
|
+
}))
|
|
2605
|
+
];
|
|
2606
|
+
const [internalTab, setInternalTab] = useState17(defaultTab ?? allTabs[0]?.id ?? "profile");
|
|
2607
|
+
const current = controlledTab ?? internalTab;
|
|
2608
|
+
const select = (id) => {
|
|
2609
|
+
if (controlledTab === void 0) setInternalTab(id);
|
|
2610
|
+
onTabChange?.(id);
|
|
2611
|
+
};
|
|
2612
|
+
const active = allTabs.find((t) => t.id === current) ?? allTabs[0];
|
|
2613
|
+
const baseId = useId();
|
|
2614
|
+
const tabId = (id) => `${baseId}-tab-${id}`;
|
|
2615
|
+
const panelId = (id) => `${baseId}-panel-${id}`;
|
|
2616
|
+
const tabRefs = useRef5(/* @__PURE__ */ new Map());
|
|
2617
|
+
const onTabKeyDown = (e, index) => {
|
|
2618
|
+
let nextIndex = null;
|
|
2619
|
+
switch (e.key) {
|
|
2620
|
+
case "ArrowRight":
|
|
2621
|
+
case "ArrowDown":
|
|
2622
|
+
nextIndex = (index + 1) % allTabs.length;
|
|
2623
|
+
break;
|
|
2624
|
+
case "ArrowLeft":
|
|
2625
|
+
case "ArrowUp":
|
|
2626
|
+
nextIndex = (index - 1 + allTabs.length) % allTabs.length;
|
|
2627
|
+
break;
|
|
2628
|
+
case "Home":
|
|
2629
|
+
nextIndex = 0;
|
|
2630
|
+
break;
|
|
2631
|
+
case "End":
|
|
2632
|
+
nextIndex = allTabs.length - 1;
|
|
2633
|
+
break;
|
|
2634
|
+
default:
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
e.preventDefault();
|
|
2638
|
+
const next = allTabs[nextIndex];
|
|
2639
|
+
if (!next) return;
|
|
2640
|
+
select(next.id);
|
|
2641
|
+
tabRefs.current.get(next.id)?.focus();
|
|
2642
|
+
};
|
|
2643
|
+
const renderPane = () => {
|
|
2644
|
+
switch (active?.id) {
|
|
2645
|
+
case "profile":
|
|
2646
|
+
return /* @__PURE__ */ jsx18(ProfileSection, { onSaved });
|
|
2647
|
+
case "security":
|
|
2648
|
+
return /* @__PURE__ */ jsx18(SecuritySection, { showDeleteAccount, onDeleted });
|
|
2649
|
+
case "billing":
|
|
2650
|
+
return /* @__PURE__ */ jsx18(
|
|
2651
|
+
BillingSection,
|
|
2652
|
+
{
|
|
2653
|
+
productSlug,
|
|
2654
|
+
pricingProductIdOrSlug,
|
|
2655
|
+
showPlanSwitcher,
|
|
2656
|
+
showUsage,
|
|
2657
|
+
usageFeatureKeys,
|
|
2658
|
+
usageLabels
|
|
2659
|
+
}
|
|
2660
|
+
);
|
|
2661
|
+
default:
|
|
2662
|
+
return extraTabs.find((t) => t.id === active?.id)?.content ?? null;
|
|
2663
|
+
}
|
|
2664
|
+
};
|
|
2665
|
+
return /* @__PURE__ */ jsx18(Shell, { appearance, className, size: "wide", children: /* @__PURE__ */ jsxs17("div", { className: "kerne-profile", children: [
|
|
2666
|
+
/* @__PURE__ */ jsx18("div", { className: "kerne-nav", role: "tablist", "aria-label": l10n.profile.navLabel, children: allTabs.map((tab, index) => {
|
|
2667
|
+
const selected = tab.id === active?.id;
|
|
2668
|
+
return /* @__PURE__ */ jsx18(
|
|
2669
|
+
"button",
|
|
2670
|
+
{
|
|
2671
|
+
ref: (el) => {
|
|
2672
|
+
if (el) tabRefs.current.set(tab.id, el);
|
|
2673
|
+
else tabRefs.current.delete(tab.id);
|
|
2674
|
+
},
|
|
2675
|
+
type: "button",
|
|
2676
|
+
role: "tab",
|
|
2677
|
+
id: tabId(tab.id),
|
|
2678
|
+
className: "kerne-nav-item",
|
|
2679
|
+
"aria-selected": selected,
|
|
2680
|
+
"aria-controls": panelId(tab.id),
|
|
2681
|
+
tabIndex: selected ? 0 : -1,
|
|
2682
|
+
onClick: () => select(tab.id),
|
|
2683
|
+
onKeyDown: (e) => onTabKeyDown(e, index),
|
|
2684
|
+
children: tab.label
|
|
2685
|
+
},
|
|
2686
|
+
tab.id
|
|
2687
|
+
);
|
|
2688
|
+
}) }),
|
|
2689
|
+
/* @__PURE__ */ jsxs17(
|
|
2690
|
+
"div",
|
|
2691
|
+
{
|
|
2692
|
+
className: "kerne-pane",
|
|
2693
|
+
role: "tabpanel",
|
|
2694
|
+
id: active ? panelId(active.id) : void 0,
|
|
2695
|
+
"aria-labelledby": active ? tabId(active.id) : void 0,
|
|
2696
|
+
tabIndex: 0,
|
|
2697
|
+
children: [
|
|
2698
|
+
active ? /* @__PURE__ */ jsxs17(Fragment12, { children: [
|
|
2699
|
+
/* @__PURE__ */ jsx18("h2", { className: "kerne-pane-title", children: active.title }),
|
|
2700
|
+
active.description ? /* @__PURE__ */ jsx18("p", { className: "kerne-pane-desc", children: active.description }) : null
|
|
2701
|
+
] }) : null,
|
|
2702
|
+
renderPane()
|
|
2703
|
+
]
|
|
2704
|
+
}
|
|
2705
|
+
)
|
|
2706
|
+
] }) });
|
|
2707
|
+
}
|
|
2708
|
+
|
|
3636
2709
|
// src/ui/CheckoutResult.tsx
|
|
3637
|
-
import { useEffect as useEffect8, useRef as
|
|
3638
|
-
import { jsx as
|
|
2710
|
+
import { useEffect as useEffect8, useRef as useRef6, useState as useState18 } from "react";
|
|
2711
|
+
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
3639
2712
|
var POLL_INTERVAL_MS = 1500;
|
|
3640
2713
|
function CheckoutResult({
|
|
3641
2714
|
outcome = "success",
|
|
@@ -3653,8 +2726,8 @@ function CheckoutResult({
|
|
|
3653
2726
|
const client = useClient();
|
|
3654
2727
|
const { config } = useAuthConfig();
|
|
3655
2728
|
const l10n = useLocalization();
|
|
3656
|
-
const [state, setState] =
|
|
3657
|
-
const confirmed =
|
|
2729
|
+
const [state, setState] = useState18("checking");
|
|
2730
|
+
const confirmed = useRef6(false);
|
|
3658
2731
|
useEffect8(() => {
|
|
3659
2732
|
if (outcome !== "success") return;
|
|
3660
2733
|
let stopped = false;
|
|
@@ -3686,20 +2759,20 @@ function CheckoutResult({
|
|
|
3686
2759
|
const go = (url, handler) => handler ?? (() => {
|
|
3687
2760
|
if (url) window.location.href = url;
|
|
3688
2761
|
});
|
|
3689
|
-
const continueAction = continueUrl || onContinue ? /* @__PURE__ */
|
|
3690
|
-
const shell = (children) => /* @__PURE__ */
|
|
3691
|
-
logo ? /* @__PURE__ */
|
|
2762
|
+
const continueAction = continueUrl || onContinue ? /* @__PURE__ */ jsx19(Button, { type: "button", onClick: go(continueUrl, onContinue), children: l10n.common.continue }) : void 0;
|
|
2763
|
+
const shell = (children) => /* @__PURE__ */ jsxs18(Shell, { appearance, className, children: [
|
|
2764
|
+
logo ? /* @__PURE__ */ jsx19("div", { className: "kerne-masthead", children: logo }) : null,
|
|
3692
2765
|
children,
|
|
3693
|
-
/* @__PURE__ */
|
|
2766
|
+
/* @__PURE__ */ jsx19(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
3694
2767
|
] });
|
|
3695
2768
|
if (outcome === "cancel") {
|
|
3696
2769
|
return shell(
|
|
3697
|
-
/* @__PURE__ */
|
|
2770
|
+
/* @__PURE__ */ jsx19(
|
|
3698
2771
|
Status,
|
|
3699
2772
|
{
|
|
3700
2773
|
tone: "danger",
|
|
3701
2774
|
title: l10n.checkout.cancelTitle,
|
|
3702
|
-
action: pricingUrl || onRetry ? /* @__PURE__ */
|
|
2775
|
+
action: pricingUrl || onRetry ? /* @__PURE__ */ jsx19(Button, { variant: "outline", type: "button", onClick: go(pricingUrl, onRetry), children: l10n.checkout.backToPlans }) : void 0,
|
|
3703
2776
|
children: l10n.checkout.cancelBody
|
|
3704
2777
|
}
|
|
3705
2778
|
)
|
|
@@ -3707,21 +2780,21 @@ function CheckoutResult({
|
|
|
3707
2780
|
}
|
|
3708
2781
|
if (state === "checking") {
|
|
3709
2782
|
return shell(
|
|
3710
|
-
/* @__PURE__ */
|
|
2783
|
+
/* @__PURE__ */ jsx19(Status, { tone: "loading", title: l10n.checkout.confirmingTitle, children: l10n.checkout.confirmingBody })
|
|
3711
2784
|
);
|
|
3712
2785
|
}
|
|
3713
2786
|
if (state === "pending") {
|
|
3714
2787
|
return shell(
|
|
3715
|
-
/* @__PURE__ */
|
|
2788
|
+
/* @__PURE__ */ jsx19(Status, { tone: "success", title: l10n.checkout.pendingTitle, action: continueAction, children: l10n.checkout.pendingBody })
|
|
3716
2789
|
);
|
|
3717
2790
|
}
|
|
3718
2791
|
return shell(
|
|
3719
|
-
/* @__PURE__ */
|
|
2792
|
+
/* @__PURE__ */ jsx19(Status, { tone: "success", title: l10n.checkout.doneTitle, action: continueAction, children: l10n.checkout.doneBody })
|
|
3720
2793
|
);
|
|
3721
2794
|
}
|
|
3722
2795
|
|
|
3723
2796
|
// src/ui/UpgradePrompt.tsx
|
|
3724
|
-
import { Fragment as Fragment13, jsx as
|
|
2797
|
+
import { Fragment as Fragment13, jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
3725
2798
|
function UpgradePrompt({
|
|
3726
2799
|
featureKey,
|
|
3727
2800
|
requested,
|
|
@@ -3740,12 +2813,14 @@ function UpgradePrompt({
|
|
|
3740
2813
|
const l10n = useLocalization();
|
|
3741
2814
|
if (isLoading) {
|
|
3742
2815
|
if (hideWhileLoading) return null;
|
|
3743
|
-
return /* @__PURE__ */
|
|
2816
|
+
return /* @__PURE__ */ jsx20(Shell, { appearance, className, children: /* @__PURE__ */ jsx20("div", { className: "kerne-skeleton", style: { height: 80 }, "aria-label": l10n.upgrade.checking }) });
|
|
3744
2817
|
}
|
|
3745
|
-
if (allowed) return /* @__PURE__ */
|
|
3746
|
-
const name = featureName ?? humanizeFeatureKey(featureKey)
|
|
2818
|
+
if (allowed) return /* @__PURE__ */ jsx20(Fragment13, { children });
|
|
2819
|
+
const name = featureName ?? lowercaseFeatureName(humanizeFeatureKey(featureKey));
|
|
3747
2820
|
const quotaExhausted = details?.feature_type === "QUOTA" && typeof details.limit === "number" && typeof details.used === "number";
|
|
3748
|
-
const heading = title ??
|
|
2821
|
+
const heading = title ?? capitalize(
|
|
2822
|
+
fill(quotaExhausted ? l10n.upgrade.quotaTitle : l10n.upgrade.featureTitle, { feature: name })
|
|
2823
|
+
);
|
|
3749
2824
|
const body = description ?? (quotaExhausted ? fill(l10n.upgrade.quotaBody, {
|
|
3750
2825
|
used: formatNumber(details.used),
|
|
3751
2826
|
// `access_limit` is where access actually stops; `limit` is only the
|
|
@@ -3753,12 +2828,12 @@ function UpgradePrompt({
|
|
|
3753
2828
|
// Falls back to `limit` defensively - should never be needed here.
|
|
3754
2829
|
limit: formatNumber(details.access_limit ?? details.limit)
|
|
3755
2830
|
}) : fill(l10n.upgrade.featureBody, { feature: name }));
|
|
3756
|
-
return /* @__PURE__ */
|
|
3757
|
-
/* @__PURE__ */
|
|
3758
|
-
/* @__PURE__ */
|
|
3759
|
-
/* @__PURE__ */
|
|
2831
|
+
return /* @__PURE__ */ jsx20(Shell, { appearance, className, children: /* @__PURE__ */ jsxs19("div", { className: "kerne-status", children: [
|
|
2832
|
+
/* @__PURE__ */ jsxs19("div", { className: "kerne-heading", children: [
|
|
2833
|
+
/* @__PURE__ */ jsx20("h2", { className: "kerne-section-title", children: heading }),
|
|
2834
|
+
/* @__PURE__ */ jsx20("p", { className: "kerne-status-text", children: body })
|
|
3760
2835
|
] }),
|
|
3761
|
-
upgradeUrl || onUpgradeClick ? /* @__PURE__ */
|
|
2836
|
+
upgradeUrl || onUpgradeClick ? /* @__PURE__ */ jsx20(
|
|
3762
2837
|
Button,
|
|
3763
2838
|
{
|
|
3764
2839
|
type: "button",
|
|
@@ -3794,11 +2869,14 @@ export {
|
|
|
3794
2869
|
WaitlistForm,
|
|
3795
2870
|
collectPriceLabels,
|
|
3796
2871
|
defaultLocalization,
|
|
2872
|
+
defaultTokens,
|
|
3797
2873
|
formatDate,
|
|
3798
2874
|
formatInterval,
|
|
3799
2875
|
formatMoney,
|
|
3800
2876
|
formatNumber,
|
|
2877
|
+
fr,
|
|
3801
2878
|
humanizeFeatureKey,
|
|
2879
|
+
mergeLocalization,
|
|
3802
2880
|
resolveLabelSavings,
|
|
3803
2881
|
resolvePriceDisplay,
|
|
3804
2882
|
useLocalization,
|