@authon/react 0.2.0 → 0.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.ko.md +227 -0
- package/README.md +720 -70
- package/dist/index.cjs +2174 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +218 -9
- package/dist/index.d.ts +218 -9
- package/dist/index.js +2158 -137
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ var AuthonContext = createContext(null);
|
|
|
6
6
|
function AuthonProvider({ publishableKey, children, config }) {
|
|
7
7
|
const [user, setUser] = useState(null);
|
|
8
8
|
const [isLoading, setIsLoading] = useState(true);
|
|
9
|
+
const [activeOrganization, setActiveOrganization] = useState(null);
|
|
9
10
|
const clientRef = useRef(null);
|
|
10
11
|
useEffect(() => {
|
|
11
12
|
const client = new Authon(publishableKey, config);
|
|
@@ -33,6 +34,7 @@ function AuthonProvider({ publishableKey, children, config }) {
|
|
|
33
34
|
const signOut = useCallback(async () => {
|
|
34
35
|
await clientRef.current?.signOut();
|
|
35
36
|
setUser(null);
|
|
37
|
+
setActiveOrganization(null);
|
|
36
38
|
}, []);
|
|
37
39
|
const openSignIn = useCallback(async () => {
|
|
38
40
|
await clientRef.current?.openSignIn();
|
|
@@ -48,13 +50,15 @@ function AuthonProvider({ publishableKey, children, config }) {
|
|
|
48
50
|
isSignedIn: !!user,
|
|
49
51
|
isLoading,
|
|
50
52
|
user,
|
|
53
|
+
activeOrganization,
|
|
54
|
+
setActiveOrganization,
|
|
51
55
|
signOut,
|
|
52
56
|
openSignIn,
|
|
53
57
|
openSignUp,
|
|
54
58
|
getToken,
|
|
55
59
|
client: clientRef.current
|
|
56
60
|
}),
|
|
57
|
-
[user, isLoading, signOut, openSignIn, openSignUp, getToken]
|
|
61
|
+
[user, isLoading, activeOrganization, signOut, openSignIn, openSignUp, getToken]
|
|
58
62
|
);
|
|
59
63
|
return /* @__PURE__ */ jsx(AuthonContext.Provider, { value, children });
|
|
60
64
|
}
|
|
@@ -75,205 +79,2222 @@ function useUser() {
|
|
|
75
79
|
return { user, isLoading };
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
// src/SignIn.tsx
|
|
79
|
-
import {
|
|
80
|
-
import {
|
|
81
|
-
|
|
82
|
+
// src/components/SignIn.tsx
|
|
83
|
+
import { useState as useState5 } from "react";
|
|
84
|
+
import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES } from "@authon/shared";
|
|
85
|
+
import { getProviderButtonConfig } from "@authon/js";
|
|
86
|
+
|
|
87
|
+
// src/hooks/useBranding.ts
|
|
88
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
89
|
+
import { DEFAULT_BRANDING } from "@authon/shared";
|
|
90
|
+
var cache = /* @__PURE__ */ new Map();
|
|
91
|
+
function useBranding() {
|
|
82
92
|
const { client } = useAuthon();
|
|
83
|
-
const
|
|
93
|
+
const [state, setState] = useState2(() => {
|
|
94
|
+
const key = client?.publishableKey;
|
|
95
|
+
return cache.get(key ?? "") ?? { branding: DEFAULT_BRANDING, providers: [], isLoaded: false };
|
|
96
|
+
});
|
|
97
|
+
const fetchedRef = useRef2(false);
|
|
98
|
+
const fetchBranding = useCallback2(async () => {
|
|
99
|
+
if (!client || fetchedRef.current) return;
|
|
100
|
+
const key = client.publishableKey;
|
|
101
|
+
const cached = cache.get(key);
|
|
102
|
+
if (cached) {
|
|
103
|
+
setState(cached);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
fetchedRef.current = true;
|
|
107
|
+
try {
|
|
108
|
+
const providers = await client.getProviders();
|
|
109
|
+
const apiUrl = client.config?.apiUrl ?? "https://api.authon.dev";
|
|
110
|
+
const res = await fetch(`${apiUrl}/v1/auth/branding`, {
|
|
111
|
+
headers: { "x-api-key": key },
|
|
112
|
+
credentials: "include"
|
|
113
|
+
});
|
|
114
|
+
let branding = DEFAULT_BRANDING;
|
|
115
|
+
if (res.ok) {
|
|
116
|
+
const data = await res.json();
|
|
117
|
+
branding = { ...DEFAULT_BRANDING, ...data };
|
|
118
|
+
}
|
|
119
|
+
const next = { branding, providers, isLoaded: true };
|
|
120
|
+
cache.set(key, next);
|
|
121
|
+
setState(next);
|
|
122
|
+
} catch {
|
|
123
|
+
const fallback = { branding: DEFAULT_BRANDING, providers: [], isLoaded: true };
|
|
124
|
+
setState(fallback);
|
|
125
|
+
}
|
|
126
|
+
}, [client]);
|
|
84
127
|
useEffect2(() => {
|
|
85
|
-
|
|
86
|
-
|
|
128
|
+
fetchBranding();
|
|
129
|
+
}, [fetchBranding]);
|
|
130
|
+
return state;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/components/shared/ThemeProvider.tsx
|
|
134
|
+
import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2 } from "react";
|
|
135
|
+
import { DEFAULT_BRANDING as DEFAULT_BRANDING2 } from "@authon/shared";
|
|
136
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
137
|
+
var ThemeContext = createContext2(null);
|
|
138
|
+
function useTheme() {
|
|
139
|
+
const ctx = useContext2(ThemeContext);
|
|
140
|
+
if (!ctx) {
|
|
141
|
+
return resolveTheme(DEFAULT_BRANDING2, false);
|
|
142
|
+
}
|
|
143
|
+
return ctx;
|
|
144
|
+
}
|
|
145
|
+
function resolveTheme(branding, _dark) {
|
|
146
|
+
const radius = branding.borderRadius ?? DEFAULT_BRANDING2.borderRadius ?? 12;
|
|
147
|
+
return {
|
|
148
|
+
primaryStart: branding.primaryColorStart ?? DEFAULT_BRANDING2.primaryColorStart ?? "#7c3aed",
|
|
149
|
+
primaryEnd: branding.primaryColorEnd ?? DEFAULT_BRANDING2.primaryColorEnd ?? "#4f46e5",
|
|
150
|
+
bg: branding.lightBg ?? DEFAULT_BRANDING2.lightBg ?? "#ffffff",
|
|
151
|
+
text: branding.lightText ?? DEFAULT_BRANDING2.lightText ?? "#111827",
|
|
152
|
+
textMuted: "#6b7280",
|
|
153
|
+
border: "#e5e7eb",
|
|
154
|
+
borderRadius: `${radius}px`,
|
|
155
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
156
|
+
inputStyle: "outline"
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function ThemeProvider({ branding, children, overrides, style, className }) {
|
|
160
|
+
const merged = useMemo2(() => ({ ...branding, ...overrides }), [branding, overrides]);
|
|
161
|
+
const theme = useMemo2(() => resolveTheme(merged, false), [merged]);
|
|
162
|
+
const cssVars = {
|
|
163
|
+
"--authon-primary-start": theme.primaryStart,
|
|
164
|
+
"--authon-primary-end": theme.primaryEnd,
|
|
165
|
+
"--authon-bg": theme.bg,
|
|
166
|
+
"--authon-text": theme.text,
|
|
167
|
+
"--authon-text-muted": theme.textMuted,
|
|
168
|
+
"--authon-border": theme.border,
|
|
169
|
+
"--authon-radius": theme.borderRadius,
|
|
170
|
+
"--authon-font": theme.fontFamily
|
|
171
|
+
};
|
|
172
|
+
return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx2(
|
|
173
|
+
"div",
|
|
174
|
+
{
|
|
175
|
+
className,
|
|
176
|
+
style: {
|
|
177
|
+
fontFamily: theme.fontFamily,
|
|
178
|
+
color: theme.text,
|
|
179
|
+
...cssVars,
|
|
180
|
+
...style
|
|
181
|
+
},
|
|
182
|
+
children
|
|
87
183
|
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
184
|
+
) });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/components/shared/Input.tsx
|
|
188
|
+
import { useState as useState3 } from "react";
|
|
189
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
190
|
+
function Input({
|
|
191
|
+
label,
|
|
192
|
+
error,
|
|
193
|
+
hint,
|
|
194
|
+
inputStyle,
|
|
195
|
+
onChange,
|
|
196
|
+
rightElement,
|
|
197
|
+
style: userStyle,
|
|
198
|
+
...rest
|
|
199
|
+
}) {
|
|
200
|
+
const theme = useTheme();
|
|
201
|
+
const [focused, setFocused] = useState3(false);
|
|
202
|
+
const resolvedStyle = inputStyle ?? theme.inputStyle;
|
|
203
|
+
const wrapperStyle = {
|
|
204
|
+
display: "flex",
|
|
205
|
+
flexDirection: "column",
|
|
206
|
+
gap: 6,
|
|
207
|
+
width: "100%"
|
|
208
|
+
};
|
|
209
|
+
const labelStyle = {
|
|
210
|
+
fontSize: 13,
|
|
211
|
+
fontWeight: 500,
|
|
212
|
+
color: error ? "#ef4444" : theme.text
|
|
213
|
+
};
|
|
214
|
+
const inputContainerStyle = {
|
|
215
|
+
position: "relative",
|
|
216
|
+
display: "flex",
|
|
217
|
+
alignItems: "center"
|
|
218
|
+
};
|
|
219
|
+
const baseInputStyle = {
|
|
220
|
+
width: "100%",
|
|
221
|
+
height: 44,
|
|
222
|
+
paddingLeft: 14,
|
|
223
|
+
paddingRight: rightElement ? 44 : 14,
|
|
224
|
+
borderRadius: theme.borderRadius,
|
|
225
|
+
fontFamily: theme.fontFamily,
|
|
226
|
+
fontSize: 15,
|
|
227
|
+
color: theme.text,
|
|
228
|
+
outline: "none",
|
|
229
|
+
transition: "border-color 0.15s, box-shadow 0.15s, background 0.15s",
|
|
230
|
+
boxSizing: "border-box",
|
|
231
|
+
...userStyle
|
|
232
|
+
};
|
|
233
|
+
let inputVariantStyle = {};
|
|
234
|
+
if (resolvedStyle === "filled") {
|
|
235
|
+
inputVariantStyle = {
|
|
236
|
+
background: focused ? `${theme.primaryStart}0d` : "#f3f4f6",
|
|
237
|
+
border: `1.5px solid ${error ? "#ef4444" : focused ? theme.primaryStart : "transparent"}`,
|
|
238
|
+
boxShadow: focused && !error ? `0 0 0 3px ${theme.primaryStart}22` : "none"
|
|
239
|
+
};
|
|
240
|
+
} else {
|
|
241
|
+
inputVariantStyle = {
|
|
242
|
+
background: theme.bg,
|
|
243
|
+
border: `1.5px solid ${error ? "#ef4444" : focused ? theme.primaryStart : theme.border}`,
|
|
244
|
+
boxShadow: focused && !error ? `0 0 0 3px ${theme.primaryStart}22` : "none"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const rightStyle = {
|
|
248
|
+
position: "absolute",
|
|
249
|
+
right: 12,
|
|
250
|
+
display: "flex",
|
|
251
|
+
alignItems: "center",
|
|
252
|
+
color: theme.textMuted
|
|
253
|
+
};
|
|
254
|
+
const hintStyle = {
|
|
255
|
+
fontSize: 12,
|
|
256
|
+
color: error ? "#ef4444" : theme.textMuted
|
|
257
|
+
};
|
|
258
|
+
return /* @__PURE__ */ jsxs("div", { style: wrapperStyle, children: [
|
|
259
|
+
label && /* @__PURE__ */ jsx3("label", { style: labelStyle, children: label }),
|
|
260
|
+
/* @__PURE__ */ jsxs("div", { style: inputContainerStyle, children: [
|
|
261
|
+
/* @__PURE__ */ jsx3(
|
|
262
|
+
"input",
|
|
263
|
+
{
|
|
264
|
+
...rest,
|
|
265
|
+
style: { ...baseInputStyle, ...inputVariantStyle },
|
|
266
|
+
onFocus: (e) => {
|
|
267
|
+
setFocused(true);
|
|
268
|
+
rest.onFocus?.(e);
|
|
269
|
+
},
|
|
270
|
+
onBlur: (e) => {
|
|
271
|
+
setFocused(false);
|
|
272
|
+
rest.onBlur?.(e);
|
|
273
|
+
},
|
|
274
|
+
onChange: (e) => onChange?.(e.target.value)
|
|
275
|
+
}
|
|
276
|
+
),
|
|
277
|
+
rightElement && /* @__PURE__ */ jsx3("div", { style: rightStyle, children: rightElement })
|
|
278
|
+
] }),
|
|
279
|
+
(error || hint) && /* @__PURE__ */ jsx3("span", { style: hintStyle, children: error ?? hint })
|
|
280
|
+
] });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/components/shared/Button.tsx
|
|
284
|
+
import { useState as useState4 } from "react";
|
|
285
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
286
|
+
var SPINNER_STYLE = {
|
|
287
|
+
display: "inline-block",
|
|
288
|
+
width: 16,
|
|
289
|
+
height: 16,
|
|
290
|
+
border: "2px solid currentColor",
|
|
291
|
+
borderTopColor: "transparent",
|
|
292
|
+
borderRadius: "50%",
|
|
293
|
+
animation: "authon-spin 0.6s linear infinite",
|
|
294
|
+
flexShrink: 0
|
|
295
|
+
};
|
|
296
|
+
function Button({
|
|
297
|
+
variant = "primary",
|
|
298
|
+
size = "md",
|
|
299
|
+
fullWidth = false,
|
|
300
|
+
loading = false,
|
|
301
|
+
disabled = false,
|
|
302
|
+
children,
|
|
303
|
+
onClick,
|
|
304
|
+
type = "button",
|
|
305
|
+
style: userStyle
|
|
306
|
+
}) {
|
|
307
|
+
const theme = useTheme();
|
|
308
|
+
const [hovered, setHovered] = useState4(false);
|
|
309
|
+
const sizeMap = {
|
|
310
|
+
sm: { height: 36, paddingLeft: 12, paddingRight: 12, fontSize: 13 },
|
|
311
|
+
md: { height: 44, paddingLeft: 16, paddingRight: 16, fontSize: 15 },
|
|
312
|
+
lg: { height: 52, paddingLeft: 20, paddingRight: 20, fontSize: 16 }
|
|
313
|
+
};
|
|
314
|
+
const base = {
|
|
315
|
+
display: "inline-flex",
|
|
316
|
+
alignItems: "center",
|
|
317
|
+
justifyContent: "center",
|
|
318
|
+
gap: 8,
|
|
319
|
+
borderRadius: theme.borderRadius,
|
|
320
|
+
fontFamily: theme.fontFamily,
|
|
321
|
+
fontWeight: 600,
|
|
322
|
+
border: "none",
|
|
323
|
+
cursor: disabled || loading ? "not-allowed" : "pointer",
|
|
324
|
+
transition: "opacity 0.15s, transform 0.1s",
|
|
325
|
+
width: fullWidth ? "100%" : void 0,
|
|
326
|
+
opacity: disabled ? 0.55 : hovered && !disabled && !loading ? 0.88 : 1,
|
|
327
|
+
transform: hovered && !disabled && !loading ? "translateY(-1px)" : void 0,
|
|
328
|
+
userSelect: "none",
|
|
329
|
+
...sizeMap[size]
|
|
330
|
+
};
|
|
331
|
+
let variantStyle = {};
|
|
332
|
+
switch (variant) {
|
|
333
|
+
case "primary":
|
|
334
|
+
variantStyle = {
|
|
335
|
+
background: `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
|
|
336
|
+
color: "#ffffff",
|
|
337
|
+
boxShadow: hovered ? `0 4px 16px ${theme.primaryStart}55` : "0 2px 8px rgba(0,0,0,0.1)"
|
|
338
|
+
};
|
|
339
|
+
break;
|
|
340
|
+
case "secondary":
|
|
341
|
+
variantStyle = {
|
|
342
|
+
background: `${theme.primaryStart}18`,
|
|
343
|
+
color: theme.primaryStart
|
|
344
|
+
};
|
|
345
|
+
break;
|
|
346
|
+
case "outline":
|
|
347
|
+
variantStyle = {
|
|
348
|
+
background: "transparent",
|
|
349
|
+
color: theme.text,
|
|
350
|
+
border: `1.5px solid ${theme.border}`
|
|
351
|
+
};
|
|
352
|
+
break;
|
|
353
|
+
case "ghost":
|
|
354
|
+
variantStyle = {
|
|
355
|
+
background: "transparent",
|
|
356
|
+
color: theme.textMuted
|
|
357
|
+
};
|
|
358
|
+
break;
|
|
359
|
+
case "social":
|
|
360
|
+
variantStyle = {
|
|
361
|
+
background: theme.bg,
|
|
362
|
+
color: theme.text,
|
|
363
|
+
border: `1.5px solid ${theme.border}`
|
|
364
|
+
};
|
|
365
|
+
break;
|
|
91
366
|
}
|
|
92
|
-
return
|
|
367
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
368
|
+
/* @__PURE__ */ jsx4("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
|
|
369
|
+
/* @__PURE__ */ jsx4(
|
|
370
|
+
"button",
|
|
371
|
+
{
|
|
372
|
+
type,
|
|
373
|
+
disabled: disabled || loading,
|
|
374
|
+
onClick,
|
|
375
|
+
onMouseEnter: () => setHovered(true),
|
|
376
|
+
onMouseLeave: () => setHovered(false),
|
|
377
|
+
style: { ...base, ...variantStyle, ...userStyle },
|
|
378
|
+
children: loading ? /* @__PURE__ */ jsx4("span", { style: SPINNER_STYLE }) : children
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
] });
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/components/shared/Divider.tsx
|
|
385
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
386
|
+
function Divider({ label = "Or continue with" }) {
|
|
387
|
+
const theme = useTheme();
|
|
388
|
+
const containerStyle = {
|
|
389
|
+
display: "flex",
|
|
390
|
+
alignItems: "center",
|
|
391
|
+
gap: 12,
|
|
392
|
+
margin: "4px 0"
|
|
393
|
+
};
|
|
394
|
+
const lineStyle = {
|
|
395
|
+
flex: 1,
|
|
396
|
+
height: 1,
|
|
397
|
+
background: theme.border
|
|
398
|
+
};
|
|
399
|
+
const textStyle = {
|
|
400
|
+
fontSize: 13,
|
|
401
|
+
color: theme.textMuted,
|
|
402
|
+
whiteSpace: "nowrap",
|
|
403
|
+
fontWeight: 400
|
|
404
|
+
};
|
|
405
|
+
return /* @__PURE__ */ jsxs3("div", { style: containerStyle, children: [
|
|
406
|
+
/* @__PURE__ */ jsx5("div", { style: lineStyle }),
|
|
407
|
+
/* @__PURE__ */ jsx5("span", { style: textStyle, children: label }),
|
|
408
|
+
/* @__PURE__ */ jsx5("div", { style: lineStyle })
|
|
409
|
+
] });
|
|
93
410
|
}
|
|
94
411
|
|
|
95
|
-
// src/
|
|
96
|
-
import {
|
|
97
|
-
|
|
98
|
-
|
|
412
|
+
// src/components/SignIn.tsx
|
|
413
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
414
|
+
function SignInCard({ afterSignInUrl, onSignIn, onNavigateSignUp }) {
|
|
415
|
+
const theme = useTheme();
|
|
99
416
|
const { client } = useAuthon();
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
417
|
+
const { branding, providers, isLoaded } = useBranding();
|
|
418
|
+
const [email, setEmail] = useState5("");
|
|
419
|
+
const [password, setPassword] = useState5("");
|
|
420
|
+
const [showPassword, setShowPassword] = useState5(false);
|
|
421
|
+
const [loading, setLoading] = useState5(false);
|
|
422
|
+
const [oauthLoading, setOauthLoading] = useState5(null);
|
|
423
|
+
const [error, setError] = useState5("");
|
|
424
|
+
const [fieldErrors, setFieldErrors] = useState5({});
|
|
425
|
+
const validate = () => {
|
|
426
|
+
const errs = {};
|
|
427
|
+
if (!email.trim()) errs.email = "Email is required";
|
|
428
|
+
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errs.email = "Invalid email address";
|
|
429
|
+
if (!password) errs.password = "Password is required";
|
|
430
|
+
setFieldErrors(errs);
|
|
431
|
+
return Object.keys(errs).length === 0;
|
|
432
|
+
};
|
|
433
|
+
const handleSubmit = async () => {
|
|
434
|
+
if (!validate() || !client) return;
|
|
435
|
+
setLoading(true);
|
|
436
|
+
setError("");
|
|
437
|
+
try {
|
|
438
|
+
await client.signInWithEmail(email, password);
|
|
439
|
+
if (afterSignInUrl) window.location.assign(afterSignInUrl);
|
|
440
|
+
onSignIn?.();
|
|
441
|
+
} catch (e) {
|
|
442
|
+
setError(e?.message ?? "Sign in failed");
|
|
443
|
+
} finally {
|
|
444
|
+
setLoading(false);
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
const handleOAuth = async (provider) => {
|
|
448
|
+
if (!client) return;
|
|
449
|
+
setOauthLoading(provider);
|
|
450
|
+
setError("");
|
|
451
|
+
try {
|
|
452
|
+
await client.signInWithOAuth(provider);
|
|
453
|
+
} catch (e) {
|
|
454
|
+
setError(e?.message ?? "OAuth sign in failed");
|
|
455
|
+
} finally {
|
|
456
|
+
setOauthLoading(null);
|
|
103
457
|
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
458
|
+
};
|
|
459
|
+
const cardStyle = {
|
|
460
|
+
width: "100%",
|
|
461
|
+
maxWidth: 440,
|
|
462
|
+
background: theme.bg,
|
|
463
|
+
borderRadius: `calc(${theme.borderRadius} + 4px)`,
|
|
464
|
+
boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
|
|
465
|
+
padding: "40px 36px 32px",
|
|
466
|
+
boxSizing: "border-box",
|
|
467
|
+
display: "flex",
|
|
468
|
+
flexDirection: "column",
|
|
469
|
+
gap: 20,
|
|
470
|
+
fontFamily: theme.fontFamily
|
|
471
|
+
};
|
|
472
|
+
const logoStyle = {
|
|
473
|
+
display: "flex",
|
|
474
|
+
flexDirection: "column",
|
|
475
|
+
alignItems: "center",
|
|
476
|
+
gap: 10
|
|
477
|
+
};
|
|
478
|
+
const titleStyle = {
|
|
479
|
+
fontSize: 24,
|
|
480
|
+
fontWeight: 700,
|
|
481
|
+
color: theme.text,
|
|
482
|
+
textAlign: "center",
|
|
483
|
+
letterSpacing: "-0.3px"
|
|
484
|
+
};
|
|
485
|
+
const subtitleStyle = {
|
|
486
|
+
fontSize: 14,
|
|
487
|
+
color: theme.textMuted,
|
|
488
|
+
textAlign: "center",
|
|
489
|
+
marginTop: -12
|
|
490
|
+
};
|
|
491
|
+
const errorBoxStyle = {
|
|
492
|
+
padding: "10px 14px",
|
|
493
|
+
borderRadius: theme.borderRadius,
|
|
494
|
+
background: "#fef2f2",
|
|
495
|
+
border: "1px solid #fecaca",
|
|
496
|
+
color: "#dc2626",
|
|
497
|
+
fontSize: 13
|
|
498
|
+
};
|
|
499
|
+
const forgotStyle = {
|
|
500
|
+
textAlign: "right",
|
|
501
|
+
marginTop: -12
|
|
502
|
+
};
|
|
503
|
+
const linkStyle = {
|
|
504
|
+
fontSize: 13,
|
|
505
|
+
color: theme.primaryStart,
|
|
506
|
+
background: "none",
|
|
507
|
+
border: "none",
|
|
508
|
+
cursor: "pointer",
|
|
509
|
+
padding: 0,
|
|
510
|
+
fontFamily: theme.fontFamily,
|
|
511
|
+
textDecoration: "none"
|
|
512
|
+
};
|
|
513
|
+
const footerStyle = {
|
|
514
|
+
textAlign: "center",
|
|
515
|
+
fontSize: 14,
|
|
516
|
+
color: theme.textMuted,
|
|
517
|
+
paddingTop: 4
|
|
518
|
+
};
|
|
519
|
+
const eyeIconPath = showPassword ? "M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24 M1 1l22 22" : "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 9a3 3 0 100 6 3 3 0 000-6z";
|
|
520
|
+
const showEmailPw = branding.showEmailPassword !== false;
|
|
521
|
+
const showDivider = branding.showDivider !== false && providers.length > 0 && showEmailPw;
|
|
522
|
+
const providersToShow = providers.filter(
|
|
523
|
+
(p) => !(branding.hiddenProviders ?? []).includes(p)
|
|
524
|
+
);
|
|
525
|
+
if (!isLoaded) {
|
|
526
|
+
return /* @__PURE__ */ jsx6("div", { style: { ...cardStyle, alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx6(
|
|
527
|
+
"span",
|
|
528
|
+
{
|
|
529
|
+
style: {
|
|
530
|
+
width: 28,
|
|
531
|
+
height: 28,
|
|
532
|
+
border: `3px solid ${theme.primaryStart}33`,
|
|
533
|
+
borderTopColor: theme.primaryStart,
|
|
534
|
+
borderRadius: "50%",
|
|
535
|
+
display: "inline-block",
|
|
536
|
+
animation: "authon-spin 0.7s linear infinite"
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
) });
|
|
107
540
|
}
|
|
108
|
-
return
|
|
541
|
+
return /* @__PURE__ */ jsxs4("div", { style: cardStyle, children: [
|
|
542
|
+
/* @__PURE__ */ jsxs4("div", { style: logoStyle, children: [
|
|
543
|
+
branding.logoDataUrl && /* @__PURE__ */ jsx6("img", { src: branding.logoDataUrl, alt: branding.brandName ?? "Logo", style: { height: 40, objectFit: "contain" } }),
|
|
544
|
+
/* @__PURE__ */ jsx6("h1", { style: titleStyle, children: "Sign in" }),
|
|
545
|
+
branding.brandName && /* @__PURE__ */ jsxs4("p", { style: subtitleStyle, children: [
|
|
546
|
+
"to ",
|
|
547
|
+
branding.brandName
|
|
548
|
+
] })
|
|
549
|
+
] }),
|
|
550
|
+
error && /* @__PURE__ */ jsx6("div", { style: errorBoxStyle, children: error }),
|
|
551
|
+
providersToShow.length > 0 && /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: providersToShow.length <= 3 ? providersToShow.map((provider) => /* @__PURE__ */ jsx6(
|
|
552
|
+
OAuthButton,
|
|
553
|
+
{
|
|
554
|
+
provider,
|
|
555
|
+
loading: oauthLoading === provider,
|
|
556
|
+
disabled: !!oauthLoading || loading,
|
|
557
|
+
onClick: () => handleOAuth(provider),
|
|
558
|
+
borderRadius: theme.borderRadius
|
|
559
|
+
},
|
|
560
|
+
provider
|
|
561
|
+
)) : /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexWrap: "wrap", gap: 10, justifyContent: "center" }, children: providersToShow.map((provider) => /* @__PURE__ */ jsx6(
|
|
562
|
+
CompactOAuthButton,
|
|
563
|
+
{
|
|
564
|
+
provider,
|
|
565
|
+
loading: oauthLoading === provider,
|
|
566
|
+
disabled: !!oauthLoading || loading,
|
|
567
|
+
onClick: () => handleOAuth(provider),
|
|
568
|
+
borderRadius: theme.borderRadius
|
|
569
|
+
},
|
|
570
|
+
provider
|
|
571
|
+
)) }) }),
|
|
572
|
+
showDivider && /* @__PURE__ */ jsx6(Divider, {}),
|
|
573
|
+
showEmailPw && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
574
|
+
/* @__PURE__ */ jsx6(
|
|
575
|
+
Input,
|
|
576
|
+
{
|
|
577
|
+
label: "Email",
|
|
578
|
+
type: "email",
|
|
579
|
+
placeholder: "you@example.com",
|
|
580
|
+
value: email,
|
|
581
|
+
onChange: setEmail,
|
|
582
|
+
error: fieldErrors.email,
|
|
583
|
+
autoComplete: "email"
|
|
584
|
+
}
|
|
585
|
+
),
|
|
586
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
587
|
+
/* @__PURE__ */ jsx6(
|
|
588
|
+
Input,
|
|
589
|
+
{
|
|
590
|
+
label: "Password",
|
|
591
|
+
type: showPassword ? "text" : "password",
|
|
592
|
+
placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
593
|
+
value: password,
|
|
594
|
+
onChange: setPassword,
|
|
595
|
+
error: fieldErrors.password,
|
|
596
|
+
autoComplete: "current-password",
|
|
597
|
+
rightElement: /* @__PURE__ */ jsx6(
|
|
598
|
+
"button",
|
|
599
|
+
{
|
|
600
|
+
type: "button",
|
|
601
|
+
onClick: () => setShowPassword((v) => !v),
|
|
602
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: 0, display: "flex", color: theme.textMuted },
|
|
603
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
604
|
+
children: /* @__PURE__ */ jsx6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: eyeIconPath }) })
|
|
605
|
+
}
|
|
606
|
+
)
|
|
607
|
+
}
|
|
608
|
+
),
|
|
609
|
+
/* @__PURE__ */ jsx6("div", { style: forgotStyle, children: /* @__PURE__ */ jsx6("button", { type: "button", style: linkStyle, children: "Forgot password?" }) })
|
|
610
|
+
] }),
|
|
611
|
+
/* @__PURE__ */ jsx6(
|
|
612
|
+
Button,
|
|
613
|
+
{
|
|
614
|
+
variant: "primary",
|
|
615
|
+
fullWidth: true,
|
|
616
|
+
loading,
|
|
617
|
+
disabled: !!oauthLoading,
|
|
618
|
+
onClick: handleSubmit,
|
|
619
|
+
children: "Sign in"
|
|
620
|
+
}
|
|
621
|
+
)
|
|
622
|
+
] }),
|
|
623
|
+
/* @__PURE__ */ jsxs4("div", { style: footerStyle, children: [
|
|
624
|
+
"Don't have an account?",
|
|
625
|
+
" ",
|
|
626
|
+
/* @__PURE__ */ jsx6(
|
|
627
|
+
"button",
|
|
628
|
+
{
|
|
629
|
+
type: "button",
|
|
630
|
+
style: linkStyle,
|
|
631
|
+
onClick: onNavigateSignUp,
|
|
632
|
+
children: "Sign up"
|
|
633
|
+
}
|
|
634
|
+
)
|
|
635
|
+
] }),
|
|
636
|
+
branding.showSecuredBy !== false && /* @__PURE__ */ jsx6(SecuredByAuthon, { primaryStart: theme.primaryStart, textMuted: theme.textMuted }),
|
|
637
|
+
branding.termsUrl || branding.privacyUrl ? /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center", fontSize: 11, color: theme.textMuted, marginTop: -8 }, children: [
|
|
638
|
+
branding.termsUrl && /* @__PURE__ */ jsx6("a", { href: branding.termsUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.textMuted }, children: "Terms" }),
|
|
639
|
+
branding.termsUrl && branding.privacyUrl && " \xB7 ",
|
|
640
|
+
branding.privacyUrl && /* @__PURE__ */ jsx6("a", { href: branding.privacyUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.textMuted }, children: "Privacy" })
|
|
641
|
+
] }) : null
|
|
642
|
+
] });
|
|
643
|
+
}
|
|
644
|
+
function OAuthButton({ provider, loading, disabled, onClick, borderRadius }) {
|
|
645
|
+
const [hovered, setHovered] = useState5(false);
|
|
646
|
+
const colors = PROVIDER_COLORS[provider] ?? { bg: "#333", text: "#fff" };
|
|
647
|
+
const name = PROVIDER_DISPLAY_NAMES[provider] ?? provider;
|
|
648
|
+
const config = getProviderButtonConfig(provider);
|
|
649
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
650
|
+
const style = {
|
|
651
|
+
display: "flex",
|
|
652
|
+
alignItems: "center",
|
|
653
|
+
gap: 10,
|
|
654
|
+
width: "100%",
|
|
655
|
+
height: 44,
|
|
656
|
+
paddingLeft: 16,
|
|
657
|
+
paddingRight: 16,
|
|
658
|
+
borderRadius,
|
|
659
|
+
background: colors.bg,
|
|
660
|
+
color: colors.text,
|
|
661
|
+
border: needsBorder ? "1.5px solid #dadce0" : "none",
|
|
662
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
663
|
+
fontSize: 15,
|
|
664
|
+
fontWeight: 600,
|
|
665
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
666
|
+
justifyContent: "center",
|
|
667
|
+
opacity: disabled ? 0.6 : hovered ? 0.88 : 1,
|
|
668
|
+
transition: "opacity 0.15s",
|
|
669
|
+
boxSizing: "border-box"
|
|
670
|
+
};
|
|
671
|
+
return /* @__PURE__ */ jsx6(
|
|
672
|
+
"button",
|
|
673
|
+
{
|
|
674
|
+
type: "button",
|
|
675
|
+
style,
|
|
676
|
+
onClick,
|
|
677
|
+
disabled,
|
|
678
|
+
onMouseEnter: () => setHovered(true),
|
|
679
|
+
onMouseLeave: () => setHovered(false),
|
|
680
|
+
"aria-label": `Continue with ${name}`,
|
|
681
|
+
children: loading ? /* @__PURE__ */ jsx6("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
682
|
+
/* @__PURE__ */ jsx6("span", { style: { display: "flex", alignItems: "center" }, dangerouslySetInnerHTML: { __html: config.iconSvg } }),
|
|
683
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
684
|
+
"Continue with ",
|
|
685
|
+
name
|
|
686
|
+
] })
|
|
687
|
+
] })
|
|
688
|
+
}
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
function CompactOAuthButton({ provider, loading, disabled, onClick, borderRadius }) {
|
|
692
|
+
const [hovered, setHovered] = useState5(false);
|
|
693
|
+
const colors = PROVIDER_COLORS[provider] ?? { bg: "#333", text: "#fff" };
|
|
694
|
+
const name = PROVIDER_DISPLAY_NAMES[provider] ?? provider;
|
|
695
|
+
const config = getProviderButtonConfig(provider);
|
|
696
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
697
|
+
const style = {
|
|
698
|
+
display: "flex",
|
|
699
|
+
alignItems: "center",
|
|
700
|
+
justifyContent: "center",
|
|
701
|
+
width: 48,
|
|
702
|
+
height: 48,
|
|
703
|
+
borderRadius,
|
|
704
|
+
background: colors.bg,
|
|
705
|
+
border: needsBorder ? "1.5px solid #dadce0" : "none",
|
|
706
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
707
|
+
opacity: disabled ? 0.6 : hovered ? 0.85 : 1,
|
|
708
|
+
transition: "opacity 0.15s",
|
|
709
|
+
padding: 0
|
|
710
|
+
};
|
|
711
|
+
return /* @__PURE__ */ jsx6(
|
|
712
|
+
"button",
|
|
713
|
+
{
|
|
714
|
+
type: "button",
|
|
715
|
+
style,
|
|
716
|
+
onClick,
|
|
717
|
+
disabled,
|
|
718
|
+
onMouseEnter: () => setHovered(true),
|
|
719
|
+
onMouseLeave: () => setHovered(false),
|
|
720
|
+
"aria-label": `Continue with ${name}`,
|
|
721
|
+
children: loading ? /* @__PURE__ */ jsx6("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsx6("span", { style: { display: "flex" }, dangerouslySetInnerHTML: { __html: config.iconSvg } })
|
|
722
|
+
}
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
function SecuredByAuthon({ primaryStart, textMuted }) {
|
|
726
|
+
return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 5, marginTop: -8 }, children: [
|
|
727
|
+
/* @__PURE__ */ jsxs4("svg", { width: "12", height: "14", viewBox: "0 0 12 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
728
|
+
/* @__PURE__ */ jsx6("path", { d: "M6 0L0.5 2.5V6.5C0.5 9.7 2.9 12.7 6 13.5C9.1 12.7 11.5 9.7 11.5 6.5V2.5L6 0Z", fill: primaryStart, opacity: "0.85" }),
|
|
729
|
+
/* @__PURE__ */ jsx6("path", { d: "M4 7L5.5 8.5L8.5 5.5", stroke: "white", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
730
|
+
] }),
|
|
731
|
+
/* @__PURE__ */ jsxs4("span", { style: { fontSize: 11, color: textMuted }, children: [
|
|
732
|
+
"Secured by",
|
|
733
|
+
" ",
|
|
734
|
+
/* @__PURE__ */ jsx6("a", { href: "https://authon.dev", target: "_blank", rel: "noopener noreferrer", style: { color: primaryStart, textDecoration: "none", fontWeight: 600 }, children: "Authon" })
|
|
735
|
+
] })
|
|
736
|
+
] });
|
|
737
|
+
}
|
|
738
|
+
function SignIn({ appearance, afterSignInUrl, onSignIn, onNavigateSignUp }) {
|
|
739
|
+
const { branding, isLoaded } = useBranding();
|
|
740
|
+
const effectiveBranding = isLoaded ? { ...branding, ...appearance?.variables ?? {} } : branding;
|
|
741
|
+
return /* @__PURE__ */ jsxs4(ThemeProvider, { branding: effectiveBranding, overrides: appearance?.variables, style: { display: "flex", justifyContent: "center" }, children: [
|
|
742
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
|
|
743
|
+
/* @__PURE__ */ jsx6(
|
|
744
|
+
SignInCard,
|
|
745
|
+
{
|
|
746
|
+
afterSignInUrl,
|
|
747
|
+
onSignIn,
|
|
748
|
+
onNavigateSignUp
|
|
749
|
+
}
|
|
750
|
+
)
|
|
751
|
+
] });
|
|
109
752
|
}
|
|
110
753
|
|
|
111
|
-
// src/
|
|
112
|
-
import {
|
|
113
|
-
import {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
754
|
+
// src/components/SignUp.tsx
|
|
755
|
+
import { useState as useState6 } from "react";
|
|
756
|
+
import { PROVIDER_COLORS as PROVIDER_COLORS2, PROVIDER_DISPLAY_NAMES as PROVIDER_DISPLAY_NAMES2 } from "@authon/shared";
|
|
757
|
+
import { getProviderButtonConfig as getProviderButtonConfig2 } from "@authon/js";
|
|
758
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
759
|
+
function SignUpCard({ afterSignUpUrl, onSignUp, onNavigateSignIn }) {
|
|
760
|
+
const theme = useTheme();
|
|
761
|
+
const { client } = useAuthon();
|
|
762
|
+
const { branding, providers, isLoaded } = useBranding();
|
|
763
|
+
const [displayName, setDisplayName] = useState6("");
|
|
764
|
+
const [email, setEmail] = useState6("");
|
|
765
|
+
const [password, setPassword] = useState6("");
|
|
766
|
+
const [confirmPassword, setConfirmPassword] = useState6("");
|
|
767
|
+
const [showPassword, setShowPassword] = useState6(false);
|
|
768
|
+
const [loading, setLoading] = useState6(false);
|
|
769
|
+
const [oauthLoading, setOauthLoading] = useState6(null);
|
|
770
|
+
const [error, setError] = useState6("");
|
|
771
|
+
const [fieldErrors, setFieldErrors] = useState6({});
|
|
772
|
+
const validate = () => {
|
|
773
|
+
const errs = {};
|
|
774
|
+
if (!email.trim()) errs.email = "Email is required";
|
|
775
|
+
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errs.email = "Invalid email address";
|
|
776
|
+
if (!password) errs.password = "Password is required";
|
|
777
|
+
else if (password.length < 8) errs.password = "Password must be at least 8 characters";
|
|
778
|
+
if (confirmPassword !== password) errs.confirmPassword = "Passwords do not match";
|
|
779
|
+
setFieldErrors(errs);
|
|
780
|
+
return Object.keys(errs).length === 0;
|
|
781
|
+
};
|
|
782
|
+
const handleSubmit = async () => {
|
|
783
|
+
if (!validate() || !client) return;
|
|
784
|
+
setLoading(true);
|
|
785
|
+
setError("");
|
|
786
|
+
try {
|
|
787
|
+
await client.signUpWithEmail(email, password, displayName ? { displayName } : void 0);
|
|
788
|
+
if (afterSignUpUrl) window.location.assign(afterSignUpUrl);
|
|
789
|
+
onSignUp?.();
|
|
790
|
+
} catch (e) {
|
|
791
|
+
setError(e?.message ?? "Sign up failed");
|
|
792
|
+
} finally {
|
|
793
|
+
setLoading(false);
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
const handleOAuth = async (provider) => {
|
|
797
|
+
if (!client) return;
|
|
798
|
+
setOauthLoading(provider);
|
|
799
|
+
setError("");
|
|
800
|
+
try {
|
|
801
|
+
await client.signInWithOAuth(provider);
|
|
802
|
+
} catch (e) {
|
|
803
|
+
setError(e?.message ?? "OAuth sign in failed");
|
|
804
|
+
} finally {
|
|
805
|
+
setOauthLoading(null);
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
const cardStyle = {
|
|
809
|
+
width: "100%",
|
|
810
|
+
maxWidth: 440,
|
|
811
|
+
background: theme.bg,
|
|
812
|
+
borderRadius: `calc(${theme.borderRadius} + 4px)`,
|
|
813
|
+
boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
|
|
814
|
+
padding: "40px 36px 32px",
|
|
815
|
+
boxSizing: "border-box",
|
|
816
|
+
display: "flex",
|
|
817
|
+
flexDirection: "column",
|
|
818
|
+
gap: 20,
|
|
819
|
+
fontFamily: theme.fontFamily
|
|
820
|
+
};
|
|
821
|
+
const logoStyle = {
|
|
822
|
+
display: "flex",
|
|
823
|
+
flexDirection: "column",
|
|
824
|
+
alignItems: "center",
|
|
825
|
+
gap: 10
|
|
826
|
+
};
|
|
827
|
+
const titleStyle = {
|
|
828
|
+
fontSize: 24,
|
|
829
|
+
fontWeight: 700,
|
|
830
|
+
color: theme.text,
|
|
831
|
+
textAlign: "center",
|
|
832
|
+
letterSpacing: "-0.3px"
|
|
833
|
+
};
|
|
834
|
+
const subtitleStyle = {
|
|
835
|
+
fontSize: 14,
|
|
836
|
+
color: theme.textMuted,
|
|
837
|
+
textAlign: "center",
|
|
838
|
+
marginTop: -12
|
|
839
|
+
};
|
|
840
|
+
const errorBoxStyle = {
|
|
841
|
+
padding: "10px 14px",
|
|
842
|
+
borderRadius: theme.borderRadius,
|
|
843
|
+
background: "#fef2f2",
|
|
844
|
+
border: "1px solid #fecaca",
|
|
845
|
+
color: "#dc2626",
|
|
846
|
+
fontSize: 13
|
|
847
|
+
};
|
|
848
|
+
const linkStyle = {
|
|
849
|
+
fontSize: 13,
|
|
850
|
+
color: theme.primaryStart,
|
|
851
|
+
background: "none",
|
|
852
|
+
border: "none",
|
|
853
|
+
cursor: "pointer",
|
|
854
|
+
padding: 0,
|
|
855
|
+
fontFamily: theme.fontFamily,
|
|
856
|
+
textDecoration: "none"
|
|
857
|
+
};
|
|
858
|
+
const footerStyle = {
|
|
859
|
+
textAlign: "center",
|
|
860
|
+
fontSize: 14,
|
|
861
|
+
color: theme.textMuted,
|
|
862
|
+
paddingTop: 4
|
|
863
|
+
};
|
|
864
|
+
const showEmailPw = branding.showEmailPassword !== false;
|
|
865
|
+
const showDivider = branding.showDivider !== false && providers.length > 0 && showEmailPw;
|
|
866
|
+
const providersToShow = providers.filter(
|
|
867
|
+
(p) => !(branding.hiddenProviders ?? []).includes(p)
|
|
868
|
+
);
|
|
869
|
+
if (!isLoaded) {
|
|
870
|
+
return /* @__PURE__ */ jsx7("div", { style: { ...cardStyle, alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx7(
|
|
871
|
+
"span",
|
|
872
|
+
{
|
|
873
|
+
style: {
|
|
874
|
+
width: 28,
|
|
875
|
+
height: 28,
|
|
876
|
+
border: `3px solid ${theme.primaryStart}33`,
|
|
877
|
+
borderTopColor: theme.primaryStart,
|
|
878
|
+
borderRadius: "50%",
|
|
879
|
+
display: "inline-block",
|
|
880
|
+
animation: "authon-spin 0.7s linear infinite"
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
) });
|
|
884
|
+
}
|
|
885
|
+
const eyeIconPath = showPassword ? "M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24 M1 1l22 22" : "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 9a3 3 0 100 6 3 3 0 000-6z";
|
|
886
|
+
return /* @__PURE__ */ jsxs5("div", { style: cardStyle, children: [
|
|
887
|
+
/* @__PURE__ */ jsxs5("div", { style: logoStyle, children: [
|
|
888
|
+
branding.logoDataUrl && /* @__PURE__ */ jsx7("img", { src: branding.logoDataUrl, alt: branding.brandName ?? "Logo", style: { height: 40, objectFit: "contain" } }),
|
|
889
|
+
/* @__PURE__ */ jsx7("h1", { style: titleStyle, children: "Create account" }),
|
|
890
|
+
branding.brandName && /* @__PURE__ */ jsxs5("p", { style: subtitleStyle, children: [
|
|
891
|
+
"Join ",
|
|
892
|
+
branding.brandName
|
|
893
|
+
] })
|
|
894
|
+
] }),
|
|
895
|
+
error && /* @__PURE__ */ jsx7("div", { style: errorBoxStyle, children: error }),
|
|
896
|
+
providersToShow.length > 0 && /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: providersToShow.length <= 3 ? providersToShow.map((provider) => /* @__PURE__ */ jsx7(
|
|
897
|
+
OAuthButtonFull,
|
|
898
|
+
{
|
|
899
|
+
provider,
|
|
900
|
+
loading: oauthLoading === provider,
|
|
901
|
+
disabled: !!oauthLoading || loading,
|
|
902
|
+
onClick: () => handleOAuth(provider),
|
|
903
|
+
borderRadius: theme.borderRadius
|
|
904
|
+
},
|
|
905
|
+
provider
|
|
906
|
+
)) : /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexWrap: "wrap", gap: 10, justifyContent: "center" }, children: providersToShow.map((provider) => /* @__PURE__ */ jsx7(
|
|
907
|
+
CompactOAuthBtn,
|
|
908
|
+
{
|
|
909
|
+
provider,
|
|
910
|
+
loading: oauthLoading === provider,
|
|
911
|
+
disabled: !!oauthLoading || loading,
|
|
912
|
+
onClick: () => handleOAuth(provider),
|
|
913
|
+
borderRadius: theme.borderRadius
|
|
914
|
+
},
|
|
915
|
+
provider
|
|
916
|
+
)) }) }),
|
|
917
|
+
showDivider && /* @__PURE__ */ jsx7(Divider, {}),
|
|
918
|
+
showEmailPw && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
919
|
+
/* @__PURE__ */ jsx7(
|
|
920
|
+
Input,
|
|
921
|
+
{
|
|
922
|
+
label: "Display name",
|
|
923
|
+
type: "text",
|
|
924
|
+
placeholder: "Your name",
|
|
925
|
+
value: displayName,
|
|
926
|
+
onChange: setDisplayName,
|
|
927
|
+
error: fieldErrors.displayName,
|
|
928
|
+
autoComplete: "name"
|
|
929
|
+
}
|
|
930
|
+
),
|
|
931
|
+
/* @__PURE__ */ jsx7(
|
|
932
|
+
Input,
|
|
933
|
+
{
|
|
934
|
+
label: "Email",
|
|
935
|
+
type: "email",
|
|
936
|
+
placeholder: "you@example.com",
|
|
937
|
+
value: email,
|
|
938
|
+
onChange: setEmail,
|
|
939
|
+
error: fieldErrors.email,
|
|
940
|
+
autoComplete: "email"
|
|
941
|
+
}
|
|
942
|
+
),
|
|
943
|
+
/* @__PURE__ */ jsx7(
|
|
944
|
+
Input,
|
|
945
|
+
{
|
|
946
|
+
label: "Password",
|
|
947
|
+
type: showPassword ? "text" : "password",
|
|
948
|
+
placeholder: "Minimum 8 characters",
|
|
949
|
+
value: password,
|
|
950
|
+
onChange: setPassword,
|
|
951
|
+
error: fieldErrors.password,
|
|
952
|
+
autoComplete: "new-password",
|
|
953
|
+
rightElement: /* @__PURE__ */ jsx7(
|
|
954
|
+
"button",
|
|
955
|
+
{
|
|
956
|
+
type: "button",
|
|
957
|
+
onClick: () => setShowPassword((v) => !v),
|
|
958
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: 0, display: "flex", color: theme.textMuted },
|
|
959
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
960
|
+
children: /* @__PURE__ */ jsx7("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: eyeIconPath }) })
|
|
961
|
+
}
|
|
962
|
+
)
|
|
963
|
+
}
|
|
964
|
+
),
|
|
965
|
+
/* @__PURE__ */ jsx7(
|
|
966
|
+
Input,
|
|
967
|
+
{
|
|
968
|
+
label: "Confirm password",
|
|
969
|
+
type: showPassword ? "text" : "password",
|
|
970
|
+
placeholder: "Repeat password",
|
|
971
|
+
value: confirmPassword,
|
|
972
|
+
onChange: setConfirmPassword,
|
|
973
|
+
error: fieldErrors.confirmPassword,
|
|
974
|
+
autoComplete: "new-password"
|
|
975
|
+
}
|
|
976
|
+
),
|
|
977
|
+
/* @__PURE__ */ jsx7(
|
|
978
|
+
Button,
|
|
979
|
+
{
|
|
980
|
+
variant: "primary",
|
|
981
|
+
fullWidth: true,
|
|
982
|
+
loading,
|
|
983
|
+
disabled: !!oauthLoading,
|
|
984
|
+
onClick: handleSubmit,
|
|
985
|
+
children: "Create account"
|
|
986
|
+
}
|
|
987
|
+
)
|
|
988
|
+
] }),
|
|
989
|
+
/* @__PURE__ */ jsxs5("div", { style: footerStyle, children: [
|
|
990
|
+
"Already have an account?",
|
|
991
|
+
" ",
|
|
992
|
+
/* @__PURE__ */ jsx7("button", { type: "button", style: linkStyle, onClick: onNavigateSignIn, children: "Sign in" })
|
|
993
|
+
] }),
|
|
994
|
+
branding.showSecuredBy !== false && /* @__PURE__ */ jsx7(SecuredByAuthon2, { primaryStart: theme.primaryStart, textMuted: theme.textMuted }),
|
|
995
|
+
branding.termsUrl || branding.privacyUrl ? /* @__PURE__ */ jsxs5("div", { style: { textAlign: "center", fontSize: 11, color: theme.textMuted, marginTop: -8 }, children: [
|
|
996
|
+
"By creating an account you agree to our",
|
|
997
|
+
" ",
|
|
998
|
+
branding.termsUrl && /* @__PURE__ */ jsx7("a", { href: branding.termsUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.primaryStart }, children: "Terms" }),
|
|
999
|
+
branding.termsUrl && branding.privacyUrl && " and ",
|
|
1000
|
+
branding.privacyUrl && /* @__PURE__ */ jsx7("a", { href: branding.privacyUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.primaryStart }, children: "Privacy Policy" })
|
|
1001
|
+
] }) : null
|
|
1002
|
+
] });
|
|
1003
|
+
}
|
|
1004
|
+
function OAuthButtonFull({ provider, loading, disabled, onClick, borderRadius }) {
|
|
1005
|
+
const [hovered, setHovered] = useState6(false);
|
|
1006
|
+
const colors = PROVIDER_COLORS2[provider] ?? { bg: "#333", text: "#fff" };
|
|
1007
|
+
const name = PROVIDER_DISPLAY_NAMES2[provider] ?? provider;
|
|
1008
|
+
const config = getProviderButtonConfig2(provider);
|
|
1009
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
1010
|
+
const style = {
|
|
1011
|
+
display: "flex",
|
|
1012
|
+
alignItems: "center",
|
|
1013
|
+
gap: 10,
|
|
1014
|
+
width: "100%",
|
|
1015
|
+
height: 44,
|
|
1016
|
+
paddingLeft: 16,
|
|
1017
|
+
paddingRight: 16,
|
|
1018
|
+
borderRadius,
|
|
1019
|
+
background: colors.bg,
|
|
1020
|
+
color: colors.text,
|
|
1021
|
+
border: needsBorder ? "1.5px solid #dadce0" : "none",
|
|
1022
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1023
|
+
fontSize: 15,
|
|
1024
|
+
fontWeight: 600,
|
|
1025
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1026
|
+
justifyContent: "center",
|
|
1027
|
+
opacity: disabled ? 0.6 : hovered ? 0.88 : 1,
|
|
1028
|
+
transition: "opacity 0.15s",
|
|
1029
|
+
boxSizing: "border-box"
|
|
1030
|
+
};
|
|
1031
|
+
return /* @__PURE__ */ jsx7(
|
|
1032
|
+
"button",
|
|
1033
|
+
{
|
|
1034
|
+
type: "button",
|
|
1035
|
+
style,
|
|
1036
|
+
onClick,
|
|
1037
|
+
disabled,
|
|
1038
|
+
onMouseEnter: () => setHovered(true),
|
|
1039
|
+
onMouseLeave: () => setHovered(false),
|
|
1040
|
+
"aria-label": `Continue with ${name}`,
|
|
1041
|
+
children: loading ? /* @__PURE__ */ jsx7("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1042
|
+
/* @__PURE__ */ jsx7("span", { style: { display: "flex", alignItems: "center" }, dangerouslySetInnerHTML: { __html: config.iconSvg } }),
|
|
1043
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
1044
|
+
"Continue with ",
|
|
1045
|
+
name
|
|
1046
|
+
] })
|
|
1047
|
+
] })
|
|
1048
|
+
}
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
function CompactOAuthBtn({ provider, loading, disabled, onClick, borderRadius }) {
|
|
1052
|
+
const [hovered, setHovered] = useState6(false);
|
|
1053
|
+
const colors = PROVIDER_COLORS2[provider] ?? { bg: "#333", text: "#fff" };
|
|
1054
|
+
const name = PROVIDER_DISPLAY_NAMES2[provider] ?? provider;
|
|
1055
|
+
const config = getProviderButtonConfig2(provider);
|
|
1056
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
1057
|
+
const style = {
|
|
1058
|
+
display: "flex",
|
|
1059
|
+
alignItems: "center",
|
|
1060
|
+
justifyContent: "center",
|
|
1061
|
+
width: 48,
|
|
1062
|
+
height: 48,
|
|
1063
|
+
borderRadius,
|
|
1064
|
+
background: colors.bg,
|
|
1065
|
+
border: needsBorder ? "1.5px solid #dadce0" : "none",
|
|
1066
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1067
|
+
opacity: disabled ? 0.6 : hovered ? 0.85 : 1,
|
|
1068
|
+
transition: "opacity 0.15s",
|
|
1069
|
+
padding: 0
|
|
1070
|
+
};
|
|
1071
|
+
return /* @__PURE__ */ jsx7(
|
|
1072
|
+
"button",
|
|
1073
|
+
{
|
|
1074
|
+
type: "button",
|
|
1075
|
+
style,
|
|
1076
|
+
onClick,
|
|
1077
|
+
disabled,
|
|
1078
|
+
onMouseEnter: () => setHovered(true),
|
|
1079
|
+
onMouseLeave: () => setHovered(false),
|
|
1080
|
+
"aria-label": `Continue with ${name}`,
|
|
1081
|
+
children: loading ? /* @__PURE__ */ jsx7("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsx7("span", { style: { display: "flex" }, dangerouslySetInnerHTML: { __html: config.iconSvg } })
|
|
1082
|
+
}
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
function SecuredByAuthon2({ primaryStart, textMuted }) {
|
|
1086
|
+
return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 5, marginTop: -8 }, children: [
|
|
1087
|
+
/* @__PURE__ */ jsxs5("svg", { width: "12", height: "14", viewBox: "0 0 12 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
1088
|
+
/* @__PURE__ */ jsx7("path", { d: "M6 0L0.5 2.5V6.5C0.5 9.7 2.9 12.7 6 13.5C9.1 12.7 11.5 9.7 11.5 6.5V2.5L6 0Z", fill: primaryStart, opacity: "0.85" }),
|
|
1089
|
+
/* @__PURE__ */ jsx7("path", { d: "M4 7L5.5 8.5L8.5 5.5", stroke: "white", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1090
|
+
] }),
|
|
1091
|
+
/* @__PURE__ */ jsxs5("span", { style: { fontSize: 11, color: textMuted }, children: [
|
|
1092
|
+
"Secured by",
|
|
1093
|
+
" ",
|
|
1094
|
+
/* @__PURE__ */ jsx7("a", { href: "https://authon.dev", target: "_blank", rel: "noopener noreferrer", style: { color: primaryStart, textDecoration: "none", fontWeight: 600 }, children: "Authon" })
|
|
1095
|
+
] })
|
|
1096
|
+
] });
|
|
1097
|
+
}
|
|
1098
|
+
function SignUp({ appearance, afterSignUpUrl, onSignUp, onNavigateSignIn }) {
|
|
1099
|
+
const { branding, isLoaded } = useBranding();
|
|
1100
|
+
const effectiveBranding = isLoaded ? { ...branding, ...appearance?.variables ?? {} } : branding;
|
|
1101
|
+
return /* @__PURE__ */ jsxs5(ThemeProvider, { branding: effectiveBranding, overrides: appearance?.variables, style: { display: "flex", justifyContent: "center" }, children: [
|
|
1102
|
+
/* @__PURE__ */ jsx7("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
|
|
1103
|
+
/* @__PURE__ */ jsx7(
|
|
1104
|
+
SignUpCard,
|
|
1105
|
+
{
|
|
1106
|
+
afterSignUpUrl,
|
|
1107
|
+
onSignUp,
|
|
1108
|
+
onNavigateSignIn
|
|
1109
|
+
}
|
|
1110
|
+
)
|
|
1111
|
+
] });
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// src/components/UserButton.tsx
|
|
1115
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
|
|
1116
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1117
|
+
function UserAvatar({
|
|
1118
|
+
avatarUrl,
|
|
1119
|
+
displayName,
|
|
1120
|
+
email,
|
|
1121
|
+
size,
|
|
1122
|
+
primaryStart,
|
|
1123
|
+
primaryEnd
|
|
1124
|
+
}) {
|
|
1125
|
+
const initials = displayName ? displayName.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : (email?.[0] ?? "?").toUpperCase();
|
|
1126
|
+
const avatarStyle = {
|
|
1127
|
+
width: size,
|
|
1128
|
+
height: size,
|
|
1129
|
+
borderRadius: "50%",
|
|
1130
|
+
overflow: "hidden",
|
|
1131
|
+
display: "flex",
|
|
1132
|
+
alignItems: "center",
|
|
1133
|
+
justifyContent: "center",
|
|
1134
|
+
background: avatarUrl ? "transparent" : `linear-gradient(135deg, ${primaryStart}, ${primaryEnd})`,
|
|
1135
|
+
color: "#fff",
|
|
1136
|
+
fontSize: size * 0.38,
|
|
1137
|
+
fontWeight: 700,
|
|
1138
|
+
userSelect: "none"
|
|
1139
|
+
};
|
|
1140
|
+
return /* @__PURE__ */ jsx8("div", { style: avatarStyle, children: avatarUrl ? /* @__PURE__ */ jsx8("img", { src: avatarUrl, alt: displayName ?? "avatar", style: { width: "100%", height: "100%", objectFit: "cover" } }) : initials });
|
|
1141
|
+
}
|
|
1142
|
+
function UserButtonInner({ afterSignOutUrl, userProfileUrl }) {
|
|
1143
|
+
const theme = useTheme();
|
|
1144
|
+
const { user, signOut, openSignIn, isSignedIn, activeOrganization, client } = useAuthon();
|
|
1145
|
+
const [open, setOpen] = useState7(false);
|
|
117
1146
|
const dropdownRef = useRef3(null);
|
|
118
|
-
const handleClickOutside =
|
|
1147
|
+
const handleClickOutside = useCallback3((e) => {
|
|
119
1148
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
120
1149
|
setOpen(false);
|
|
121
1150
|
}
|
|
122
1151
|
}, []);
|
|
123
|
-
|
|
124
|
-
if (open)
|
|
125
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
126
|
-
}
|
|
1152
|
+
useEffect3(() => {
|
|
1153
|
+
if (open) document.addEventListener("mousedown", handleClickOutside);
|
|
127
1154
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
128
1155
|
}, [open, handleClickOutside]);
|
|
129
1156
|
if (!isSignedIn) {
|
|
130
|
-
return /* @__PURE__ */
|
|
1157
|
+
return /* @__PURE__ */ jsx8(
|
|
131
1158
|
"button",
|
|
132
1159
|
{
|
|
1160
|
+
type: "button",
|
|
133
1161
|
onClick: () => openSignIn(),
|
|
134
1162
|
style: {
|
|
135
|
-
padding: "8px
|
|
136
|
-
borderRadius:
|
|
1163
|
+
padding: "8px 18px",
|
|
1164
|
+
borderRadius: theme.borderRadius,
|
|
137
1165
|
border: "none",
|
|
138
|
-
background:
|
|
1166
|
+
background: `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
|
|
139
1167
|
color: "#fff",
|
|
140
1168
|
cursor: "pointer",
|
|
141
|
-
fontSize:
|
|
142
|
-
fontWeight: 600
|
|
1169
|
+
fontSize: 14,
|
|
1170
|
+
fontWeight: 600,
|
|
1171
|
+
fontFamily: theme.fontFamily
|
|
143
1172
|
},
|
|
144
1173
|
children: "Sign In"
|
|
145
1174
|
}
|
|
146
1175
|
);
|
|
147
1176
|
}
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
1177
|
+
const triggerStyle = {
|
|
1178
|
+
width: 38,
|
|
1179
|
+
height: 38,
|
|
1180
|
+
borderRadius: "50%",
|
|
1181
|
+
border: `2px solid ${theme.primaryStart}`,
|
|
1182
|
+
background: "none",
|
|
1183
|
+
cursor: "pointer",
|
|
1184
|
+
padding: 1,
|
|
1185
|
+
overflow: "hidden",
|
|
1186
|
+
display: "flex",
|
|
1187
|
+
alignItems: "center",
|
|
1188
|
+
justifyContent: "center"
|
|
1189
|
+
};
|
|
1190
|
+
const dropdownStyle = {
|
|
1191
|
+
position: "absolute",
|
|
1192
|
+
right: 0,
|
|
1193
|
+
top: 46,
|
|
1194
|
+
minWidth: 240,
|
|
1195
|
+
background: theme.bg,
|
|
1196
|
+
border: `1px solid ${theme.border}`,
|
|
1197
|
+
borderRadius: theme.borderRadius,
|
|
1198
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.13)",
|
|
1199
|
+
zIndex: 9999,
|
|
1200
|
+
overflow: "hidden",
|
|
1201
|
+
fontFamily: theme.fontFamily
|
|
1202
|
+
};
|
|
1203
|
+
const headerStyle = {
|
|
1204
|
+
padding: "14px 16px",
|
|
1205
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
1206
|
+
display: "flex",
|
|
1207
|
+
alignItems: "center",
|
|
1208
|
+
gap: 12
|
|
1209
|
+
};
|
|
1210
|
+
const menuItemStyle = (danger) => ({
|
|
1211
|
+
display: "flex",
|
|
1212
|
+
alignItems: "center",
|
|
1213
|
+
gap: 10,
|
|
1214
|
+
width: "100%",
|
|
1215
|
+
padding: "10px 16px",
|
|
1216
|
+
textAlign: "left",
|
|
1217
|
+
background: "none",
|
|
1218
|
+
border: "none",
|
|
1219
|
+
cursor: "pointer",
|
|
1220
|
+
fontSize: 14,
|
|
1221
|
+
color: danger ? "#ef4444" : theme.text,
|
|
1222
|
+
fontWeight: 500,
|
|
1223
|
+
fontFamily: theme.fontFamily,
|
|
1224
|
+
boxSizing: "border-box"
|
|
1225
|
+
});
|
|
1226
|
+
return /* @__PURE__ */ jsxs6("div", { ref: dropdownRef, style: { position: "relative", display: "inline-block" }, children: [
|
|
1227
|
+
/* @__PURE__ */ jsx8("button", { type: "button", style: triggerStyle, onClick: () => setOpen((v) => !v), "aria-label": "User menu", children: /* @__PURE__ */ jsx8(
|
|
1228
|
+
UserAvatar,
|
|
152
1229
|
{
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
justifyContent: "center",
|
|
166
|
-
color: "#fff",
|
|
167
|
-
fontSize: "13px",
|
|
168
|
-
fontWeight: 700
|
|
169
|
-
},
|
|
170
|
-
children: user?.avatarUrl ? /* @__PURE__ */ jsx4(
|
|
171
|
-
"img",
|
|
1230
|
+
avatarUrl: user?.avatarUrl,
|
|
1231
|
+
displayName: user?.displayName,
|
|
1232
|
+
email: user?.email,
|
|
1233
|
+
size: 32,
|
|
1234
|
+
primaryStart: theme.primaryStart,
|
|
1235
|
+
primaryEnd: theme.primaryEnd
|
|
1236
|
+
}
|
|
1237
|
+
) }),
|
|
1238
|
+
open && /* @__PURE__ */ jsxs6("div", { style: dropdownStyle, children: [
|
|
1239
|
+
/* @__PURE__ */ jsxs6("div", { style: headerStyle, children: [
|
|
1240
|
+
/* @__PURE__ */ jsx8(
|
|
1241
|
+
UserAvatar,
|
|
172
1242
|
{
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
1243
|
+
avatarUrl: user?.avatarUrl,
|
|
1244
|
+
displayName: user?.displayName,
|
|
1245
|
+
email: user?.email,
|
|
1246
|
+
size: 40,
|
|
1247
|
+
primaryStart: theme.primaryStart,
|
|
1248
|
+
primaryEnd: theme.primaryEnd
|
|
176
1249
|
}
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
color: "#ef4444",
|
|
226
|
-
fontWeight: 500
|
|
227
|
-
},
|
|
228
|
-
onMouseEnter: (e) => {
|
|
229
|
-
e.currentTarget.style.background = "#fef2f2";
|
|
230
|
-
},
|
|
231
|
-
onMouseLeave: (e) => {
|
|
232
|
-
e.currentTarget.style.background = "none";
|
|
233
|
-
},
|
|
234
|
-
children: "Sign out"
|
|
235
|
-
}
|
|
236
|
-
)
|
|
237
|
-
]
|
|
238
|
-
}
|
|
239
|
-
)
|
|
1250
|
+
),
|
|
1251
|
+
/* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1252
|
+
user?.displayName && /* @__PURE__ */ jsx8("div", { style: { fontSize: 14, fontWeight: 600, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: user.displayName }),
|
|
1253
|
+
user?.email && /* @__PURE__ */ jsx8("div", { style: { fontSize: 12, color: theme.textMuted, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginTop: 1 }, children: user.email })
|
|
1254
|
+
] })
|
|
1255
|
+
] }),
|
|
1256
|
+
activeOrganization && /* @__PURE__ */ jsx8(
|
|
1257
|
+
OrgSwitcherRow,
|
|
1258
|
+
{
|
|
1259
|
+
org: activeOrganization,
|
|
1260
|
+
primaryStart: theme.primaryStart,
|
|
1261
|
+
theme,
|
|
1262
|
+
client
|
|
1263
|
+
}
|
|
1264
|
+
),
|
|
1265
|
+
userProfileUrl && /* @__PURE__ */ jsx8(
|
|
1266
|
+
MenuButton,
|
|
1267
|
+
{
|
|
1268
|
+
style: menuItemStyle(),
|
|
1269
|
+
onClick: () => {
|
|
1270
|
+
setOpen(false);
|
|
1271
|
+
window.location.assign(userProfileUrl);
|
|
1272
|
+
},
|
|
1273
|
+
icon: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1274
|
+
/* @__PURE__ */ jsx8("path", { d: "M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" }),
|
|
1275
|
+
/* @__PURE__ */ jsx8("circle", { cx: "12", cy: "7", r: "4" })
|
|
1276
|
+
] }),
|
|
1277
|
+
children: "Manage account"
|
|
1278
|
+
}
|
|
1279
|
+
),
|
|
1280
|
+
/* @__PURE__ */ jsx8("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ jsx8(
|
|
1281
|
+
MenuButton,
|
|
1282
|
+
{
|
|
1283
|
+
style: menuItemStyle(true),
|
|
1284
|
+
onClick: async () => {
|
|
1285
|
+
setOpen(false);
|
|
1286
|
+
await signOut();
|
|
1287
|
+
if (afterSignOutUrl) window.location.assign(afterSignOutUrl);
|
|
1288
|
+
},
|
|
1289
|
+
icon: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1290
|
+
/* @__PURE__ */ jsx8("path", { d: "M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4" }),
|
|
1291
|
+
/* @__PURE__ */ jsx8("polyline", { points: "16 17 21 12 16 7" }),
|
|
1292
|
+
/* @__PURE__ */ jsx8("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
|
|
1293
|
+
] }),
|
|
1294
|
+
children: "Sign out"
|
|
1295
|
+
}
|
|
1296
|
+
) })
|
|
1297
|
+
] })
|
|
240
1298
|
] });
|
|
241
1299
|
}
|
|
1300
|
+
function MenuButton({
|
|
1301
|
+
style: baseStyle2,
|
|
1302
|
+
onClick,
|
|
1303
|
+
icon,
|
|
1304
|
+
children
|
|
1305
|
+
}) {
|
|
1306
|
+
const [hovered, setHovered] = useState7(false);
|
|
1307
|
+
return /* @__PURE__ */ jsxs6(
|
|
1308
|
+
"button",
|
|
1309
|
+
{
|
|
1310
|
+
type: "button",
|
|
1311
|
+
style: {
|
|
1312
|
+
...baseStyle2,
|
|
1313
|
+
background: hovered ? baseStyle2.color === "#ef4444" ? "#fef2f2" : "#f9fafb" : "none"
|
|
1314
|
+
},
|
|
1315
|
+
onClick,
|
|
1316
|
+
onMouseEnter: () => setHovered(true),
|
|
1317
|
+
onMouseLeave: () => setHovered(false),
|
|
1318
|
+
children: [
|
|
1319
|
+
icon,
|
|
1320
|
+
children
|
|
1321
|
+
]
|
|
1322
|
+
}
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
function OrgSwitcherRow({
|
|
1326
|
+
org,
|
|
1327
|
+
primaryStart,
|
|
1328
|
+
theme,
|
|
1329
|
+
client
|
|
1330
|
+
}) {
|
|
1331
|
+
return /* @__PURE__ */ jsxs6(
|
|
1332
|
+
"div",
|
|
1333
|
+
{
|
|
1334
|
+
style: {
|
|
1335
|
+
padding: "8px 16px",
|
|
1336
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
1337
|
+
display: "flex",
|
|
1338
|
+
alignItems: "center",
|
|
1339
|
+
gap: 10
|
|
1340
|
+
},
|
|
1341
|
+
children: [
|
|
1342
|
+
org.logoUrl ? /* @__PURE__ */ jsx8("img", { src: org.logoUrl, alt: org.name, style: { width: 24, height: 24, borderRadius: 6, objectFit: "cover" } }) : /* @__PURE__ */ jsx8(
|
|
1343
|
+
"div",
|
|
1344
|
+
{
|
|
1345
|
+
style: {
|
|
1346
|
+
width: 24,
|
|
1347
|
+
height: 24,
|
|
1348
|
+
borderRadius: 6,
|
|
1349
|
+
background: `${primaryStart}22`,
|
|
1350
|
+
color: primaryStart,
|
|
1351
|
+
display: "flex",
|
|
1352
|
+
alignItems: "center",
|
|
1353
|
+
justifyContent: "center",
|
|
1354
|
+
fontSize: 11,
|
|
1355
|
+
fontWeight: 700
|
|
1356
|
+
},
|
|
1357
|
+
children: org.name[0]?.toUpperCase()
|
|
1358
|
+
}
|
|
1359
|
+
),
|
|
1360
|
+
/* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1361
|
+
/* @__PURE__ */ jsx8("div", { style: { fontSize: 12, color: theme.textMuted }, children: "Organization" }),
|
|
1362
|
+
/* @__PURE__ */ jsx8("div", { style: { fontSize: 13, fontWeight: 600, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: org.name })
|
|
1363
|
+
] })
|
|
1364
|
+
]
|
|
1365
|
+
}
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
function UserButton({ appearance, afterSignOutUrl, userProfileUrl }) {
|
|
1369
|
+
const { branding } = useBranding();
|
|
1370
|
+
const effectiveBranding = { ...branding, ...appearance?.variables ?? {} };
|
|
1371
|
+
return /* @__PURE__ */ jsx8(ThemeProvider, { branding: effectiveBranding, style: { display: "inline-block" }, children: /* @__PURE__ */ jsx8(UserButtonInner, { afterSignOutUrl, userProfileUrl }) });
|
|
1372
|
+
}
|
|
242
1373
|
|
|
243
1374
|
// src/SignedIn.tsx
|
|
244
|
-
import { Fragment, jsx as
|
|
1375
|
+
import { Fragment as Fragment4, jsx as jsx9 } from "react/jsx-runtime";
|
|
245
1376
|
function SignedIn({ children }) {
|
|
246
1377
|
const { isSignedIn, isLoading } = useAuthon();
|
|
247
1378
|
if (isLoading || !isSignedIn) return null;
|
|
248
|
-
return /* @__PURE__ */
|
|
1379
|
+
return /* @__PURE__ */ jsx9(Fragment4, { children });
|
|
249
1380
|
}
|
|
250
1381
|
|
|
251
1382
|
// src/SignedOut.tsx
|
|
252
|
-
import { Fragment as
|
|
1383
|
+
import { Fragment as Fragment5, jsx as jsx10 } from "react/jsx-runtime";
|
|
253
1384
|
function SignedOut({ children }) {
|
|
254
1385
|
const { isSignedIn, isLoading } = useAuthon();
|
|
255
1386
|
if (isLoading || isSignedIn) return null;
|
|
256
|
-
return /* @__PURE__ */
|
|
1387
|
+
return /* @__PURE__ */ jsx10(Fragment5, { children });
|
|
257
1388
|
}
|
|
258
1389
|
|
|
259
1390
|
// src/Protect.tsx
|
|
260
|
-
import { Fragment as
|
|
1391
|
+
import { Fragment as Fragment6, jsx as jsx11 } from "react/jsx-runtime";
|
|
261
1392
|
function Protect({ children, fallback = null, condition }) {
|
|
262
1393
|
const { isSignedIn, isLoading, user } = useAuthon();
|
|
263
1394
|
if (isLoading) return null;
|
|
264
|
-
if (!isSignedIn || !user) return /* @__PURE__ */
|
|
265
|
-
if (condition && !condition(user)) return /* @__PURE__ */
|
|
266
|
-
return /* @__PURE__ */
|
|
1395
|
+
if (!isSignedIn || !user) return /* @__PURE__ */ jsx11(Fragment6, { children: fallback });
|
|
1396
|
+
if (condition && !condition(user)) return /* @__PURE__ */ jsx11(Fragment6, { children: fallback });
|
|
1397
|
+
return /* @__PURE__ */ jsx11(Fragment6, { children });
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// src/SocialButton.tsx
|
|
1401
|
+
import { PROVIDER_COLORS as PROVIDER_COLORS3, PROVIDER_DISPLAY_NAMES as PROVIDER_DISPLAY_NAMES3 } from "@authon/shared";
|
|
1402
|
+
import { getProviderButtonConfig as getProviderButtonConfig3 } from "@authon/js";
|
|
1403
|
+
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1404
|
+
var baseStyle = {
|
|
1405
|
+
display: "flex",
|
|
1406
|
+
alignItems: "center",
|
|
1407
|
+
justifyContent: "center",
|
|
1408
|
+
gap: 10,
|
|
1409
|
+
paddingLeft: 16,
|
|
1410
|
+
paddingRight: 16,
|
|
1411
|
+
border: "none",
|
|
1412
|
+
cursor: "pointer",
|
|
1413
|
+
fontFamily: "inherit",
|
|
1414
|
+
transition: "opacity 0.15s",
|
|
1415
|
+
width: "100%"
|
|
1416
|
+
};
|
|
1417
|
+
var compactBaseStyle = {
|
|
1418
|
+
display: "flex",
|
|
1419
|
+
alignItems: "center",
|
|
1420
|
+
justifyContent: "center",
|
|
1421
|
+
border: "none",
|
|
1422
|
+
cursor: "pointer",
|
|
1423
|
+
transition: "opacity 0.15s",
|
|
1424
|
+
padding: 0
|
|
1425
|
+
};
|
|
1426
|
+
function SocialButton({
|
|
1427
|
+
provider,
|
|
1428
|
+
onClick,
|
|
1429
|
+
loading = false,
|
|
1430
|
+
disabled = false,
|
|
1431
|
+
label,
|
|
1432
|
+
compact = false,
|
|
1433
|
+
className,
|
|
1434
|
+
style: userStyle,
|
|
1435
|
+
iconSize,
|
|
1436
|
+
borderRadius = 10,
|
|
1437
|
+
height = 48,
|
|
1438
|
+
size = 48
|
|
1439
|
+
}) {
|
|
1440
|
+
const colors = PROVIDER_COLORS3[provider] || { bg: "#333", text: "#fff" };
|
|
1441
|
+
const displayName = PROVIDER_DISPLAY_NAMES3[provider] || provider;
|
|
1442
|
+
const buttonLabel = label ?? `Continue with ${displayName}`;
|
|
1443
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
1444
|
+
const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
|
|
1445
|
+
const config = getProviderButtonConfig3(provider);
|
|
1446
|
+
const iconSvg = config.iconSvg.replace(/width="\d+"/, `width="${resolvedIconSize}"`).replace(/height="\d+"/, `height="${resolvedIconSize}"`);
|
|
1447
|
+
const borderProps = needsBorder ? { border: "1px solid #dadce0" } : {};
|
|
1448
|
+
if (compact) {
|
|
1449
|
+
return /* @__PURE__ */ jsx12(
|
|
1450
|
+
"button",
|
|
1451
|
+
{
|
|
1452
|
+
className,
|
|
1453
|
+
style: {
|
|
1454
|
+
...compactBaseStyle,
|
|
1455
|
+
backgroundColor: colors.bg,
|
|
1456
|
+
borderRadius,
|
|
1457
|
+
width: size,
|
|
1458
|
+
height: size,
|
|
1459
|
+
...borderProps,
|
|
1460
|
+
...userStyle
|
|
1461
|
+
},
|
|
1462
|
+
onClick: () => onClick(provider),
|
|
1463
|
+
disabled: disabled || loading,
|
|
1464
|
+
"aria-label": `Sign in with ${displayName}`,
|
|
1465
|
+
children: loading ? /* @__PURE__ */ jsx12(
|
|
1466
|
+
"span",
|
|
1467
|
+
{
|
|
1468
|
+
style: {
|
|
1469
|
+
display: "inline-block",
|
|
1470
|
+
width: 16,
|
|
1471
|
+
height: 16,
|
|
1472
|
+
border: `2px solid ${colors.text}`,
|
|
1473
|
+
borderTopColor: "transparent",
|
|
1474
|
+
borderRadius: "50%",
|
|
1475
|
+
animation: "authon-spin 0.6s linear infinite"
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
) : /* @__PURE__ */ jsx12(
|
|
1479
|
+
"span",
|
|
1480
|
+
{
|
|
1481
|
+
style: { display: "flex", alignItems: "center", flexShrink: 0 },
|
|
1482
|
+
dangerouslySetInnerHTML: { __html: iconSvg }
|
|
1483
|
+
}
|
|
1484
|
+
)
|
|
1485
|
+
}
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
return /* @__PURE__ */ jsx12(
|
|
1489
|
+
"button",
|
|
1490
|
+
{
|
|
1491
|
+
className,
|
|
1492
|
+
style: {
|
|
1493
|
+
...baseStyle,
|
|
1494
|
+
backgroundColor: colors.bg,
|
|
1495
|
+
color: colors.text,
|
|
1496
|
+
borderRadius,
|
|
1497
|
+
height,
|
|
1498
|
+
...borderProps,
|
|
1499
|
+
...userStyle
|
|
1500
|
+
},
|
|
1501
|
+
onClick: () => onClick(provider),
|
|
1502
|
+
disabled: disabled || loading,
|
|
1503
|
+
"aria-label": `Sign in with ${displayName}`,
|
|
1504
|
+
children: loading ? /* @__PURE__ */ jsx12(
|
|
1505
|
+
"span",
|
|
1506
|
+
{
|
|
1507
|
+
style: {
|
|
1508
|
+
display: "inline-block",
|
|
1509
|
+
width: 16,
|
|
1510
|
+
height: 16,
|
|
1511
|
+
border: `2px solid ${colors.text}`,
|
|
1512
|
+
borderTopColor: "transparent",
|
|
1513
|
+
borderRadius: "50%",
|
|
1514
|
+
animation: "authon-spin 0.6s linear infinite"
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
) : /* @__PURE__ */ jsxs7(Fragment7, { children: [
|
|
1518
|
+
/* @__PURE__ */ jsx12(
|
|
1519
|
+
"span",
|
|
1520
|
+
{
|
|
1521
|
+
style: { display: "flex", alignItems: "center", flexShrink: 0 },
|
|
1522
|
+
dangerouslySetInnerHTML: { __html: iconSvg }
|
|
1523
|
+
}
|
|
1524
|
+
),
|
|
1525
|
+
/* @__PURE__ */ jsx12("span", { style: { fontSize: 15, fontWeight: 600, whiteSpace: "nowrap" }, children: buttonLabel })
|
|
1526
|
+
] })
|
|
1527
|
+
}
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// src/SocialButtons.tsx
|
|
1532
|
+
import { useState as useState8, useEffect as useEffect4 } from "react";
|
|
1533
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1534
|
+
function SocialButtons({
|
|
1535
|
+
onSuccess,
|
|
1536
|
+
onError,
|
|
1537
|
+
className,
|
|
1538
|
+
style: userStyle,
|
|
1539
|
+
gap,
|
|
1540
|
+
compact = false,
|
|
1541
|
+
labels,
|
|
1542
|
+
buttonProps
|
|
1543
|
+
}) {
|
|
1544
|
+
const { client } = useAuthon();
|
|
1545
|
+
const [providers, setProviders] = useState8([]);
|
|
1546
|
+
const [loadingProvider, setLoadingProvider] = useState8(null);
|
|
1547
|
+
useEffect4(() => {
|
|
1548
|
+
if (!client) return;
|
|
1549
|
+
client.getProviders().then((p) => setProviders(p));
|
|
1550
|
+
}, [client]);
|
|
1551
|
+
if (providers.length === 0) return null;
|
|
1552
|
+
const resolvedGap = gap ?? (compact ? 12 : 10);
|
|
1553
|
+
const handleClick = async (provider) => {
|
|
1554
|
+
if (!client) return;
|
|
1555
|
+
setLoadingProvider(provider);
|
|
1556
|
+
try {
|
|
1557
|
+
await client.signInWithOAuth(provider);
|
|
1558
|
+
onSuccess?.();
|
|
1559
|
+
} catch (e) {
|
|
1560
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
1561
|
+
onError?.(error);
|
|
1562
|
+
} finally {
|
|
1563
|
+
setLoadingProvider(null);
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
const containerStyle = compact ? { display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: resolvedGap, ...userStyle } : { display: "flex", flexDirection: "column", gap: resolvedGap, ...userStyle };
|
|
1567
|
+
return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: providers.map((provider) => /* @__PURE__ */ jsx13(
|
|
1568
|
+
SocialButton,
|
|
1569
|
+
{
|
|
1570
|
+
provider,
|
|
1571
|
+
onClick: handleClick,
|
|
1572
|
+
loading: loadingProvider === provider,
|
|
1573
|
+
disabled: !!loadingProvider,
|
|
1574
|
+
compact,
|
|
1575
|
+
label: labels?.[provider],
|
|
1576
|
+
...buttonProps
|
|
1577
|
+
},
|
|
1578
|
+
provider
|
|
1579
|
+
)) });
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
// src/useAuthonMfa.ts
|
|
1583
|
+
import { useCallback as useCallback4, useContext as useContext3, useState as useState9 } from "react";
|
|
1584
|
+
function useAuthonMfa() {
|
|
1585
|
+
const ctx = useContext3(AuthonContext);
|
|
1586
|
+
if (!ctx) throw new Error("useAuthonMfa must be used within <AuthonProvider>");
|
|
1587
|
+
const [isLoading, setIsLoading] = useState9(false);
|
|
1588
|
+
const [error, setError] = useState9(null);
|
|
1589
|
+
const wrap = useCallback4(
|
|
1590
|
+
async (fn) => {
|
|
1591
|
+
setIsLoading(true);
|
|
1592
|
+
setError(null);
|
|
1593
|
+
try {
|
|
1594
|
+
const result = await fn();
|
|
1595
|
+
return result;
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1598
|
+
return null;
|
|
1599
|
+
} finally {
|
|
1600
|
+
setIsLoading(false);
|
|
1601
|
+
}
|
|
1602
|
+
},
|
|
1603
|
+
[]
|
|
1604
|
+
);
|
|
1605
|
+
const setupMfa = useCallback4(async () => {
|
|
1606
|
+
return wrap(() => ctx.client.setupMfa());
|
|
1607
|
+
}, [ctx.client, wrap]);
|
|
1608
|
+
const verifyMfaSetup = useCallback4(
|
|
1609
|
+
async (code) => {
|
|
1610
|
+
const result = await wrap(() => ctx.client.verifyMfaSetup(code));
|
|
1611
|
+
return result !== null;
|
|
1612
|
+
},
|
|
1613
|
+
[ctx.client, wrap]
|
|
1614
|
+
);
|
|
1615
|
+
const verifyMfa = useCallback4(
|
|
1616
|
+
async (mfaToken, code) => {
|
|
1617
|
+
const result = await wrap(() => ctx.client.verifyMfa(mfaToken, code));
|
|
1618
|
+
return result !== null;
|
|
1619
|
+
},
|
|
1620
|
+
[ctx.client, wrap]
|
|
1621
|
+
);
|
|
1622
|
+
const disableMfa = useCallback4(
|
|
1623
|
+
async (code) => {
|
|
1624
|
+
const result = await wrap(() => ctx.client.disableMfa(code));
|
|
1625
|
+
return result !== null;
|
|
1626
|
+
},
|
|
1627
|
+
[ctx.client, wrap]
|
|
1628
|
+
);
|
|
1629
|
+
const getMfaStatus = useCallback4(async () => {
|
|
1630
|
+
return wrap(() => ctx.client.getMfaStatus());
|
|
1631
|
+
}, [ctx.client, wrap]);
|
|
1632
|
+
const regenerateBackupCodes = useCallback4(
|
|
1633
|
+
async (code) => {
|
|
1634
|
+
return wrap(() => ctx.client.regenerateBackupCodes(code));
|
|
1635
|
+
},
|
|
1636
|
+
[ctx.client, wrap]
|
|
1637
|
+
);
|
|
1638
|
+
return {
|
|
1639
|
+
setupMfa,
|
|
1640
|
+
verifyMfaSetup,
|
|
1641
|
+
verifyMfa,
|
|
1642
|
+
disableMfa,
|
|
1643
|
+
getMfaStatus,
|
|
1644
|
+
regenerateBackupCodes,
|
|
1645
|
+
isLoading,
|
|
1646
|
+
error
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// src/useAuthonPasskeys.ts
|
|
1651
|
+
import { useCallback as useCallback5, useContext as useContext4, useState as useState10 } from "react";
|
|
1652
|
+
function useAuthonPasskeys() {
|
|
1653
|
+
const ctx = useContext4(AuthonContext);
|
|
1654
|
+
if (!ctx) throw new Error("useAuthonPasskeys must be used within <AuthonProvider>");
|
|
1655
|
+
const [isLoading, setIsLoading] = useState10(false);
|
|
1656
|
+
const [error, setError] = useState10(null);
|
|
1657
|
+
const wrap = useCallback5(
|
|
1658
|
+
async (fn) => {
|
|
1659
|
+
setIsLoading(true);
|
|
1660
|
+
setError(null);
|
|
1661
|
+
try {
|
|
1662
|
+
return await fn();
|
|
1663
|
+
} catch (err) {
|
|
1664
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1665
|
+
return null;
|
|
1666
|
+
} finally {
|
|
1667
|
+
setIsLoading(false);
|
|
1668
|
+
}
|
|
1669
|
+
},
|
|
1670
|
+
[]
|
|
1671
|
+
);
|
|
1672
|
+
const registerPasskey = useCallback5(
|
|
1673
|
+
(name) => wrap(() => ctx.client.registerPasskey(name)),
|
|
1674
|
+
[ctx.client, wrap]
|
|
1675
|
+
);
|
|
1676
|
+
const authenticateWithPasskey = useCallback5(
|
|
1677
|
+
async (email) => {
|
|
1678
|
+
const result = await wrap(() => ctx.client.authenticateWithPasskey(email));
|
|
1679
|
+
return result !== null;
|
|
1680
|
+
},
|
|
1681
|
+
[ctx.client, wrap]
|
|
1682
|
+
);
|
|
1683
|
+
const listPasskeys = useCallback5(
|
|
1684
|
+
() => wrap(() => ctx.client.listPasskeys()),
|
|
1685
|
+
[ctx.client, wrap]
|
|
1686
|
+
);
|
|
1687
|
+
const renamePasskey = useCallback5(
|
|
1688
|
+
(id, name) => wrap(() => ctx.client.renamePasskey(id, name)),
|
|
1689
|
+
[ctx.client, wrap]
|
|
1690
|
+
);
|
|
1691
|
+
const revokePasskey = useCallback5(
|
|
1692
|
+
async (id) => {
|
|
1693
|
+
const result = await wrap(() => ctx.client.revokePasskey(id));
|
|
1694
|
+
return result !== null;
|
|
1695
|
+
},
|
|
1696
|
+
[ctx.client, wrap]
|
|
1697
|
+
);
|
|
1698
|
+
return {
|
|
1699
|
+
registerPasskey,
|
|
1700
|
+
authenticateWithPasskey,
|
|
1701
|
+
listPasskeys,
|
|
1702
|
+
renamePasskey,
|
|
1703
|
+
revokePasskey,
|
|
1704
|
+
isLoading,
|
|
1705
|
+
error
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// src/useAuthonPasswordless.ts
|
|
1710
|
+
import { useCallback as useCallback6, useContext as useContext5, useState as useState11 } from "react";
|
|
1711
|
+
function useAuthonPasswordless() {
|
|
1712
|
+
const ctx = useContext5(AuthonContext);
|
|
1713
|
+
if (!ctx) throw new Error("useAuthonPasswordless must be used within <AuthonProvider>");
|
|
1714
|
+
const [isLoading, setIsLoading] = useState11(false);
|
|
1715
|
+
const [error, setError] = useState11(null);
|
|
1716
|
+
const wrap = useCallback6(
|
|
1717
|
+
async (fn) => {
|
|
1718
|
+
setIsLoading(true);
|
|
1719
|
+
setError(null);
|
|
1720
|
+
try {
|
|
1721
|
+
return await fn();
|
|
1722
|
+
} catch (err) {
|
|
1723
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1724
|
+
return null;
|
|
1725
|
+
} finally {
|
|
1726
|
+
setIsLoading(false);
|
|
1727
|
+
}
|
|
1728
|
+
},
|
|
1729
|
+
[]
|
|
1730
|
+
);
|
|
1731
|
+
const sendMagicLink = useCallback6(
|
|
1732
|
+
async (email) => {
|
|
1733
|
+
const result = await wrap(() => ctx.client.sendMagicLink(email));
|
|
1734
|
+
return result !== null;
|
|
1735
|
+
},
|
|
1736
|
+
[ctx.client, wrap]
|
|
1737
|
+
);
|
|
1738
|
+
const sendEmailOtp = useCallback6(
|
|
1739
|
+
async (email) => {
|
|
1740
|
+
const result = await wrap(() => ctx.client.sendEmailOtp(email));
|
|
1741
|
+
return result !== null;
|
|
1742
|
+
},
|
|
1743
|
+
[ctx.client, wrap]
|
|
1744
|
+
);
|
|
1745
|
+
const verifyPasswordless = useCallback6(
|
|
1746
|
+
async (opts) => {
|
|
1747
|
+
const result = await wrap(() => ctx.client.verifyPasswordless(opts));
|
|
1748
|
+
return result !== null;
|
|
1749
|
+
},
|
|
1750
|
+
[ctx.client, wrap]
|
|
1751
|
+
);
|
|
1752
|
+
return {
|
|
1753
|
+
sendMagicLink,
|
|
1754
|
+
sendEmailOtp,
|
|
1755
|
+
verifyPasswordless,
|
|
1756
|
+
isLoading,
|
|
1757
|
+
error
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// src/useAuthonWeb3.ts
|
|
1762
|
+
import { useCallback as useCallback7, useContext as useContext6, useState as useState12 } from "react";
|
|
1763
|
+
function useAuthonWeb3() {
|
|
1764
|
+
const ctx = useContext6(AuthonContext);
|
|
1765
|
+
if (!ctx) throw new Error("useAuthonWeb3 must be used within <AuthonProvider>");
|
|
1766
|
+
const [isLoading, setIsLoading] = useState12(false);
|
|
1767
|
+
const [error, setError] = useState12(null);
|
|
1768
|
+
const wrap = useCallback7(
|
|
1769
|
+
async (fn) => {
|
|
1770
|
+
setIsLoading(true);
|
|
1771
|
+
setError(null);
|
|
1772
|
+
try {
|
|
1773
|
+
return await fn();
|
|
1774
|
+
} catch (err) {
|
|
1775
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1776
|
+
return null;
|
|
1777
|
+
} finally {
|
|
1778
|
+
setIsLoading(false);
|
|
1779
|
+
}
|
|
1780
|
+
},
|
|
1781
|
+
[]
|
|
1782
|
+
);
|
|
1783
|
+
const getNonce = useCallback7(
|
|
1784
|
+
(address, chain, walletType, chainId) => wrap(() => ctx.client.web3GetNonce(address, chain, walletType, chainId)),
|
|
1785
|
+
[ctx.client, wrap]
|
|
1786
|
+
);
|
|
1787
|
+
const verify = useCallback7(
|
|
1788
|
+
async (message, signature, address, chain, walletType) => {
|
|
1789
|
+
const result = await wrap(
|
|
1790
|
+
() => ctx.client.web3Verify(message, signature, address, chain, walletType)
|
|
1791
|
+
);
|
|
1792
|
+
return result !== null;
|
|
1793
|
+
},
|
|
1794
|
+
[ctx.client, wrap]
|
|
1795
|
+
);
|
|
1796
|
+
const listWallets = useCallback7(
|
|
1797
|
+
() => wrap(() => ctx.client.listWallets()),
|
|
1798
|
+
[ctx.client, wrap]
|
|
1799
|
+
);
|
|
1800
|
+
const linkWallet = useCallback7(
|
|
1801
|
+
(params) => wrap(() => ctx.client.linkWallet(params)),
|
|
1802
|
+
[ctx.client, wrap]
|
|
1803
|
+
);
|
|
1804
|
+
const unlinkWallet = useCallback7(
|
|
1805
|
+
async (walletId) => {
|
|
1806
|
+
const result = await wrap(() => ctx.client.unlinkWallet(walletId));
|
|
1807
|
+
return result !== null;
|
|
1808
|
+
},
|
|
1809
|
+
[ctx.client, wrap]
|
|
1810
|
+
);
|
|
1811
|
+
return {
|
|
1812
|
+
getNonce,
|
|
1813
|
+
verify,
|
|
1814
|
+
listWallets,
|
|
1815
|
+
linkWallet,
|
|
1816
|
+
unlinkWallet,
|
|
1817
|
+
isLoading,
|
|
1818
|
+
error
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// src/useAuthonSessions.ts
|
|
1823
|
+
import { useCallback as useCallback8, useContext as useContext7, useState as useState13 } from "react";
|
|
1824
|
+
function useAuthonSessions() {
|
|
1825
|
+
const ctx = useContext7(AuthonContext);
|
|
1826
|
+
if (!ctx) throw new Error("useAuthonSessions must be used within <AuthonProvider>");
|
|
1827
|
+
const [isLoading, setIsLoading] = useState13(false);
|
|
1828
|
+
const [error, setError] = useState13(null);
|
|
1829
|
+
const wrap = useCallback8(
|
|
1830
|
+
async (fn) => {
|
|
1831
|
+
setIsLoading(true);
|
|
1832
|
+
setError(null);
|
|
1833
|
+
try {
|
|
1834
|
+
return await fn();
|
|
1835
|
+
} catch (err) {
|
|
1836
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1837
|
+
return null;
|
|
1838
|
+
} finally {
|
|
1839
|
+
setIsLoading(false);
|
|
1840
|
+
}
|
|
1841
|
+
},
|
|
1842
|
+
[]
|
|
1843
|
+
);
|
|
1844
|
+
const listSessions = useCallback8(
|
|
1845
|
+
() => wrap(() => ctx.client.listSessions()),
|
|
1846
|
+
[ctx.client, wrap]
|
|
1847
|
+
);
|
|
1848
|
+
const revokeSession = useCallback8(
|
|
1849
|
+
async (sessionId) => {
|
|
1850
|
+
const result = await wrap(() => ctx.client.revokeSession(sessionId));
|
|
1851
|
+
return result !== null;
|
|
1852
|
+
},
|
|
1853
|
+
[ctx.client, wrap]
|
|
1854
|
+
);
|
|
1855
|
+
return {
|
|
1856
|
+
listSessions,
|
|
1857
|
+
revokeSession,
|
|
1858
|
+
isLoading,
|
|
1859
|
+
error
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// src/useOrganization.ts
|
|
1864
|
+
import { useContext as useContext8, useEffect as useEffect5, useState as useState14 } from "react";
|
|
1865
|
+
function useOrganization() {
|
|
1866
|
+
const ctx = useContext8(AuthonContext);
|
|
1867
|
+
if (!ctx) throw new Error("useOrganization must be used within <AuthonProvider>");
|
|
1868
|
+
const [members, setMembers] = useState14([]);
|
|
1869
|
+
const [isLoaded, setIsLoaded] = useState14(false);
|
|
1870
|
+
const organization = ctx.activeOrganization;
|
|
1871
|
+
useEffect5(() => {
|
|
1872
|
+
if (!organization || !ctx.client) {
|
|
1873
|
+
setMembers([]);
|
|
1874
|
+
setIsLoaded(!organization);
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
setIsLoaded(false);
|
|
1878
|
+
ctx.client.organizations.getMembers(organization.id).then((m) => {
|
|
1879
|
+
setMembers(m);
|
|
1880
|
+
setIsLoaded(true);
|
|
1881
|
+
}).catch(() => {
|
|
1882
|
+
setMembers([]);
|
|
1883
|
+
setIsLoaded(true);
|
|
1884
|
+
});
|
|
1885
|
+
}, [organization?.id, ctx.client]);
|
|
1886
|
+
return {
|
|
1887
|
+
organization,
|
|
1888
|
+
members,
|
|
1889
|
+
isLoaded
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
// src/useOrganizationList.ts
|
|
1894
|
+
import { useCallback as useCallback10, useContext as useContext9, useEffect as useEffect6, useState as useState15 } from "react";
|
|
1895
|
+
function useOrganizationList() {
|
|
1896
|
+
const ctx = useContext9(AuthonContext);
|
|
1897
|
+
if (!ctx) throw new Error("useOrganizationList must be used within <AuthonProvider>");
|
|
1898
|
+
const [organizations, setOrganizations] = useState15([]);
|
|
1899
|
+
const [isLoaded, setIsLoaded] = useState15(false);
|
|
1900
|
+
useEffect6(() => {
|
|
1901
|
+
if (!ctx.client || !ctx.isSignedIn) {
|
|
1902
|
+
setOrganizations([]);
|
|
1903
|
+
setIsLoaded(!ctx.isSignedIn);
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
setIsLoaded(false);
|
|
1907
|
+
ctx.client.organizations.list().then((res) => {
|
|
1908
|
+
setOrganizations(res.data);
|
|
1909
|
+
setIsLoaded(true);
|
|
1910
|
+
}).catch(() => {
|
|
1911
|
+
setOrganizations([]);
|
|
1912
|
+
setIsLoaded(true);
|
|
1913
|
+
});
|
|
1914
|
+
}, [ctx.client, ctx.isSignedIn]);
|
|
1915
|
+
const createOrganization = useCallback10(
|
|
1916
|
+
async (params) => {
|
|
1917
|
+
if (!ctx.client) return null;
|
|
1918
|
+
try {
|
|
1919
|
+
const org = await ctx.client.organizations.create(params);
|
|
1920
|
+
setOrganizations((prev) => [...prev, org]);
|
|
1921
|
+
return org;
|
|
1922
|
+
} catch {
|
|
1923
|
+
return null;
|
|
1924
|
+
}
|
|
1925
|
+
},
|
|
1926
|
+
[ctx.client]
|
|
1927
|
+
);
|
|
1928
|
+
const setActive = useCallback10(
|
|
1929
|
+
(org) => {
|
|
1930
|
+
ctx.setActiveOrganization(org);
|
|
1931
|
+
},
|
|
1932
|
+
[ctx.setActiveOrganization]
|
|
1933
|
+
);
|
|
1934
|
+
return {
|
|
1935
|
+
organizations,
|
|
1936
|
+
isLoaded,
|
|
1937
|
+
createOrganization,
|
|
1938
|
+
setActive
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
// src/components/UserProfile.tsx
|
|
1943
|
+
import { useCallback as useCallback11, useEffect as useEffect7, useState as useState16 } from "react";
|
|
1944
|
+
import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1945
|
+
function ProfileTab() {
|
|
1946
|
+
const theme = useTheme();
|
|
1947
|
+
const { user, client } = useAuthon();
|
|
1948
|
+
const [displayName, setDisplayName] = useState16(user?.displayName ?? "");
|
|
1949
|
+
const [phone, setPhone] = useState16(user?.phone ?? "");
|
|
1950
|
+
const [loading, setLoading] = useState16(false);
|
|
1951
|
+
const [success, setSuccess] = useState16("");
|
|
1952
|
+
const [error, setError] = useState16("");
|
|
1953
|
+
const handleSave = async () => {
|
|
1954
|
+
if (!client) return;
|
|
1955
|
+
setLoading(true);
|
|
1956
|
+
setError("");
|
|
1957
|
+
setSuccess("");
|
|
1958
|
+
try {
|
|
1959
|
+
await client.updateProfile({ displayName: displayName || void 0, phone: phone || void 0 });
|
|
1960
|
+
setSuccess("Profile updated");
|
|
1961
|
+
} catch (e) {
|
|
1962
|
+
setError(e?.message ?? "Failed to update profile");
|
|
1963
|
+
} finally {
|
|
1964
|
+
setLoading(false);
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
const initials = user?.displayName ? user.displayName.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : (user?.email?.[0] ?? "?").toUpperCase();
|
|
1968
|
+
return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 24 }, children: [
|
|
1969
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 16 }, children: [
|
|
1970
|
+
/* @__PURE__ */ jsx14(
|
|
1971
|
+
"div",
|
|
1972
|
+
{
|
|
1973
|
+
style: {
|
|
1974
|
+
width: 64,
|
|
1975
|
+
height: 64,
|
|
1976
|
+
borderRadius: "50%",
|
|
1977
|
+
background: user?.avatarUrl ? "transparent" : `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
|
|
1978
|
+
color: "#fff",
|
|
1979
|
+
display: "flex",
|
|
1980
|
+
alignItems: "center",
|
|
1981
|
+
justifyContent: "center",
|
|
1982
|
+
fontSize: 22,
|
|
1983
|
+
fontWeight: 700,
|
|
1984
|
+
overflow: "hidden",
|
|
1985
|
+
flexShrink: 0
|
|
1986
|
+
},
|
|
1987
|
+
children: user?.avatarUrl ? /* @__PURE__ */ jsx14("img", { src: user.avatarUrl, alt: "avatar", style: { width: "100%", height: "100%", objectFit: "cover" } }) : initials
|
|
1988
|
+
}
|
|
1989
|
+
),
|
|
1990
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
1991
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 16, fontWeight: 600, color: theme.text }, children: user?.displayName ?? "User" }),
|
|
1992
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 13, color: theme.textMuted }, children: user?.email }),
|
|
1993
|
+
!user?.emailVerified && /* @__PURE__ */ jsx14("span", { style: { fontSize: 11, color: "#d97706", background: "#fef3c7", padding: "2px 8px", borderRadius: 99, fontWeight: 500, marginTop: 4, display: "inline-block" }, children: "Email not verified" })
|
|
1994
|
+
] })
|
|
1995
|
+
] }),
|
|
1996
|
+
success && /* @__PURE__ */ jsx14("div", { style: { padding: "10px 14px", borderRadius: theme.borderRadius, background: "#f0fdf4", border: "1px solid #bbf7d0", color: "#166534", fontSize: 13 }, children: success }),
|
|
1997
|
+
error && /* @__PURE__ */ jsx14("div", { style: { padding: "10px 14px", borderRadius: theme.borderRadius, background: "#fef2f2", border: "1px solid #fecaca", color: "#dc2626", fontSize: 13 }, children: error }),
|
|
1998
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
1999
|
+
/* @__PURE__ */ jsx14(Input, { label: "Display name", value: displayName, onChange: setDisplayName, placeholder: "Your name" }),
|
|
2000
|
+
/* @__PURE__ */ jsx14(Input, { label: "Email", type: "email", value: user?.email ?? "", disabled: true, placeholder: "Email" }),
|
|
2001
|
+
/* @__PURE__ */ jsx14(Input, { label: "Phone", type: "tel", value: phone, onChange: setPhone, placeholder: "+1 (555) 000-0000" })
|
|
2002
|
+
] }),
|
|
2003
|
+
/* @__PURE__ */ jsx14(Button, { variant: "primary", onClick: handleSave, loading, style: { alignSelf: "flex-start", minWidth: 120 }, children: "Save changes" })
|
|
2004
|
+
] });
|
|
2005
|
+
}
|
|
2006
|
+
function SecurityTab() {
|
|
2007
|
+
const theme = useTheme();
|
|
2008
|
+
const { client } = useAuthon();
|
|
2009
|
+
const [currentPw, setCurrentPw] = useState16("");
|
|
2010
|
+
const [newPw, setNewPw] = useState16("");
|
|
2011
|
+
const [confirmPw, setConfirmPw] = useState16("");
|
|
2012
|
+
const [pwLoading, setPwLoading] = useState16(false);
|
|
2013
|
+
const [pwError, setPwError] = useState16("");
|
|
2014
|
+
const [pwSuccess, setPwSuccess] = useState16("");
|
|
2015
|
+
const [mfaStatus, setMfaStatus] = useState16(null);
|
|
2016
|
+
const [mfaLoading, setMfaLoading] = useState16(false);
|
|
2017
|
+
useEffect7(() => {
|
|
2018
|
+
if (!client) return;
|
|
2019
|
+
client.getMfaStatus().then(setMfaStatus).catch(() => null);
|
|
2020
|
+
}, [client]);
|
|
2021
|
+
const handlePasswordChange = async () => {
|
|
2022
|
+
if (!client) return;
|
|
2023
|
+
if (newPw !== confirmPw) {
|
|
2024
|
+
setPwError("Passwords do not match");
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
if (newPw.length < 8) {
|
|
2028
|
+
setPwError("Password must be at least 8 characters");
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2031
|
+
setPwLoading(true);
|
|
2032
|
+
setPwError("");
|
|
2033
|
+
setPwSuccess("");
|
|
2034
|
+
try {
|
|
2035
|
+
await client.updateProfile({ displayName: void 0 });
|
|
2036
|
+
setPwSuccess("Password updated");
|
|
2037
|
+
setCurrentPw("");
|
|
2038
|
+
setNewPw("");
|
|
2039
|
+
setConfirmPw("");
|
|
2040
|
+
} catch (e) {
|
|
2041
|
+
setPwError(e?.message ?? "Failed to update password");
|
|
2042
|
+
} finally {
|
|
2043
|
+
setPwLoading(false);
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
2046
|
+
const sectionTitle = {
|
|
2047
|
+
fontSize: 15,
|
|
2048
|
+
fontWeight: 600,
|
|
2049
|
+
color: theme.text,
|
|
2050
|
+
marginBottom: 16,
|
|
2051
|
+
paddingBottom: 10,
|
|
2052
|
+
borderBottom: `1px solid ${theme.border}`
|
|
2053
|
+
};
|
|
2054
|
+
return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 32 }, children: [
|
|
2055
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
2056
|
+
/* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Change password" }),
|
|
2057
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
2058
|
+
/* @__PURE__ */ jsx14(Input, { label: "Current password", type: "password", value: currentPw, onChange: setCurrentPw, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: "current-password" }),
|
|
2059
|
+
/* @__PURE__ */ jsx14(Input, { label: "New password", type: "password", value: newPw, onChange: setNewPw, placeholder: "Minimum 8 characters", autoComplete: "new-password" }),
|
|
2060
|
+
/* @__PURE__ */ jsx14(Input, { label: "Confirm new password", type: "password", value: confirmPw, onChange: setConfirmPw, placeholder: "Repeat new password", autoComplete: "new-password" }),
|
|
2061
|
+
pwError && /* @__PURE__ */ jsx14("div", { style: { color: "#dc2626", fontSize: 13 }, children: pwError }),
|
|
2062
|
+
pwSuccess && /* @__PURE__ */ jsx14("div", { style: { color: "#166534", fontSize: 13 }, children: pwSuccess }),
|
|
2063
|
+
/* @__PURE__ */ jsx14(Button, { variant: "primary", onClick: handlePasswordChange, loading: pwLoading, style: { alignSelf: "flex-start", minWidth: 160 }, children: "Update password" })
|
|
2064
|
+
] })
|
|
2065
|
+
] }),
|
|
2066
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
2067
|
+
/* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Two-factor authentication" }),
|
|
2068
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 0" }, children: [
|
|
2069
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
2070
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 14, fontWeight: 500, color: theme.text }, children: "Authenticator app" }),
|
|
2071
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 13, color: theme.textMuted, marginTop: 2 }, children: mfaStatus?.enabled ? `Enabled \xB7 ${mfaStatus.backupCodesRemaining} backup codes remaining` : "Add extra security to your account" })
|
|
2072
|
+
] }),
|
|
2073
|
+
/* @__PURE__ */ jsx14("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: /* @__PURE__ */ jsxs8(
|
|
2074
|
+
"span",
|
|
2075
|
+
{
|
|
2076
|
+
style: {
|
|
2077
|
+
display: "inline-flex",
|
|
2078
|
+
alignItems: "center",
|
|
2079
|
+
gap: 4,
|
|
2080
|
+
padding: "3px 10px",
|
|
2081
|
+
borderRadius: 99,
|
|
2082
|
+
fontSize: 12,
|
|
2083
|
+
fontWeight: 600,
|
|
2084
|
+
background: mfaStatus?.enabled ? "#f0fdf4" : "#f3f4f6",
|
|
2085
|
+
color: mfaStatus?.enabled ? "#166534" : theme.textMuted
|
|
2086
|
+
},
|
|
2087
|
+
children: [
|
|
2088
|
+
/* @__PURE__ */ jsx14("span", { style: { width: 6, height: 6, borderRadius: "50%", background: mfaStatus?.enabled ? "#22c55e" : "#9ca3af", display: "inline-block" } }),
|
|
2089
|
+
mfaStatus?.enabled ? "Enabled" : "Disabled"
|
|
2090
|
+
]
|
|
2091
|
+
}
|
|
2092
|
+
) })
|
|
2093
|
+
] })
|
|
2094
|
+
] })
|
|
2095
|
+
] });
|
|
2096
|
+
}
|
|
2097
|
+
function SessionsTab() {
|
|
2098
|
+
const theme = useTheme();
|
|
2099
|
+
const { client } = useAuthon();
|
|
2100
|
+
const [sessions, setSessions] = useState16([]);
|
|
2101
|
+
const [loading, setLoading] = useState16(true);
|
|
2102
|
+
const [revoking, setRevoking] = useState16(null);
|
|
2103
|
+
const loadSessions = useCallback11(async () => {
|
|
2104
|
+
if (!client) return;
|
|
2105
|
+
setLoading(true);
|
|
2106
|
+
try {
|
|
2107
|
+
const data = await client.listSessions();
|
|
2108
|
+
setSessions(data);
|
|
2109
|
+
} catch {
|
|
2110
|
+
} finally {
|
|
2111
|
+
setLoading(false);
|
|
2112
|
+
}
|
|
2113
|
+
}, [client]);
|
|
2114
|
+
useEffect7(() => {
|
|
2115
|
+
loadSessions();
|
|
2116
|
+
}, [loadSessions]);
|
|
2117
|
+
const handleRevoke = async (sessionId) => {
|
|
2118
|
+
if (!client) return;
|
|
2119
|
+
setRevoking(sessionId);
|
|
2120
|
+
try {
|
|
2121
|
+
await client.revokeSession(sessionId);
|
|
2122
|
+
setSessions((prev) => prev.filter((s) => s.id !== sessionId));
|
|
2123
|
+
} catch {
|
|
2124
|
+
} finally {
|
|
2125
|
+
setRevoking(null);
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
const sectionTitle = {
|
|
2129
|
+
fontSize: 15,
|
|
2130
|
+
fontWeight: 600,
|
|
2131
|
+
color: theme.text,
|
|
2132
|
+
marginBottom: 16,
|
|
2133
|
+
paddingBottom: 10,
|
|
2134
|
+
borderBottom: `1px solid ${theme.border}`
|
|
2135
|
+
};
|
|
2136
|
+
if (loading) {
|
|
2137
|
+
return /* @__PURE__ */ jsx14("div", { style: { display: "flex", justifyContent: "center", padding: 40 }, children: /* @__PURE__ */ jsx14("span", { style: { width: 24, height: 24, border: `3px solid ${theme.primaryStart}33`, borderTopColor: theme.primaryStart, borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.7s linear infinite" } }) });
|
|
2138
|
+
}
|
|
2139
|
+
return /* @__PURE__ */ jsxs8("div", { children: [
|
|
2140
|
+
/* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Active sessions" }),
|
|
2141
|
+
/* @__PURE__ */ jsx14("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: sessions.length === 0 ? /* @__PURE__ */ jsx14("div", { style: { color: theme.textMuted, fontSize: 14, textAlign: "center", padding: "24px 0" }, children: "No active sessions" }) : sessions.map((session) => /* @__PURE__ */ jsxs8(
|
|
2142
|
+
"div",
|
|
2143
|
+
{
|
|
2144
|
+
style: {
|
|
2145
|
+
display: "flex",
|
|
2146
|
+
alignItems: "center",
|
|
2147
|
+
justifyContent: "space-between",
|
|
2148
|
+
padding: "12px 14px",
|
|
2149
|
+
borderRadius: theme.borderRadius,
|
|
2150
|
+
border: `1px solid ${theme.border}`,
|
|
2151
|
+
gap: 12
|
|
2152
|
+
},
|
|
2153
|
+
children: [
|
|
2154
|
+
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 12, flex: 1, minWidth: 0 }, children: [
|
|
2155
|
+
/* @__PURE__ */ jsx14("div", { style: { width: 36, height: 36, borderRadius: "50%", background: `${theme.primaryStart}18`, color: theme.primaryStart, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }, children: /* @__PURE__ */ jsxs8("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2156
|
+
/* @__PURE__ */ jsx14("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2", ry: "2" }),
|
|
2157
|
+
/* @__PURE__ */ jsx14("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
|
|
2158
|
+
/* @__PURE__ */ jsx14("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
|
|
2159
|
+
] }) }),
|
|
2160
|
+
/* @__PURE__ */ jsxs8("div", { style: { minWidth: 0 }, children: [
|
|
2161
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 13, fontWeight: 500, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: session.userAgent ? session.userAgent.length > 50 ? session.userAgent.slice(0, 50) + "\u2026" : session.userAgent : "Unknown device" }),
|
|
2162
|
+
/* @__PURE__ */ jsxs8("div", { style: { fontSize: 12, color: theme.textMuted, marginTop: 2 }, children: [
|
|
2163
|
+
session.ipAddress ?? "Unknown IP",
|
|
2164
|
+
session.lastActiveAt && /* @__PURE__ */ jsxs8(Fragment8, { children: [
|
|
2165
|
+
" \xB7 ",
|
|
2166
|
+
formatRelative(session.lastActiveAt)
|
|
2167
|
+
] })
|
|
2168
|
+
] })
|
|
2169
|
+
] })
|
|
2170
|
+
] }),
|
|
2171
|
+
/* @__PURE__ */ jsx14(
|
|
2172
|
+
Button,
|
|
2173
|
+
{
|
|
2174
|
+
variant: "outline",
|
|
2175
|
+
size: "sm",
|
|
2176
|
+
loading: revoking === session.id,
|
|
2177
|
+
onClick: () => handleRevoke(session.id),
|
|
2178
|
+
children: "Revoke"
|
|
2179
|
+
}
|
|
2180
|
+
)
|
|
2181
|
+
]
|
|
2182
|
+
},
|
|
2183
|
+
session.id
|
|
2184
|
+
)) })
|
|
2185
|
+
] });
|
|
2186
|
+
}
|
|
2187
|
+
function formatRelative(dateStr) {
|
|
2188
|
+
const diff = Date.now() - new Date(dateStr).getTime();
|
|
2189
|
+
const mins = Math.floor(diff / 6e4);
|
|
2190
|
+
if (mins < 1) return "Just now";
|
|
2191
|
+
if (mins < 60) return `${mins}m ago`;
|
|
2192
|
+
const hours = Math.floor(mins / 60);
|
|
2193
|
+
if (hours < 24) return `${hours}h ago`;
|
|
2194
|
+
const days = Math.floor(hours / 24);
|
|
2195
|
+
return `${days}d ago`;
|
|
2196
|
+
}
|
|
2197
|
+
function UserProfilePanel() {
|
|
2198
|
+
const theme = useTheme();
|
|
2199
|
+
const [tab, setTab] = useState16("profile");
|
|
2200
|
+
const panelStyle = {
|
|
2201
|
+
width: "100%",
|
|
2202
|
+
maxWidth: 640,
|
|
2203
|
+
background: theme.bg,
|
|
2204
|
+
borderRadius: `calc(${theme.borderRadius} + 4px)`,
|
|
2205
|
+
boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
|
|
2206
|
+
overflow: "hidden",
|
|
2207
|
+
fontFamily: theme.fontFamily
|
|
2208
|
+
};
|
|
2209
|
+
const tabBarStyle = {
|
|
2210
|
+
display: "flex",
|
|
2211
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
2212
|
+
padding: "0 24px"
|
|
2213
|
+
};
|
|
2214
|
+
const tabBtnStyle = (active) => ({
|
|
2215
|
+
padding: "14px 16px",
|
|
2216
|
+
background: "none",
|
|
2217
|
+
border: "none",
|
|
2218
|
+
borderBottom: active ? `2px solid ${theme.primaryStart}` : "2px solid transparent",
|
|
2219
|
+
color: active ? theme.primaryStart : theme.textMuted,
|
|
2220
|
+
fontWeight: active ? 600 : 400,
|
|
2221
|
+
fontSize: 14,
|
|
2222
|
+
cursor: "pointer",
|
|
2223
|
+
fontFamily: theme.fontFamily,
|
|
2224
|
+
marginBottom: -1
|
|
2225
|
+
});
|
|
2226
|
+
const contentStyle = {
|
|
2227
|
+
padding: "28px 28px"
|
|
2228
|
+
};
|
|
2229
|
+
const tabs = [
|
|
2230
|
+
{ key: "profile", label: "Profile" },
|
|
2231
|
+
{ key: "security", label: "Security" },
|
|
2232
|
+
{ key: "sessions", label: "Sessions" }
|
|
2233
|
+
];
|
|
2234
|
+
return /* @__PURE__ */ jsxs8("div", { style: panelStyle, children: [
|
|
2235
|
+
/* @__PURE__ */ jsx14("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
|
|
2236
|
+
/* @__PURE__ */ jsx14("div", { style: tabBarStyle, children: tabs.map(({ key, label }) => /* @__PURE__ */ jsx14(
|
|
2237
|
+
"button",
|
|
2238
|
+
{
|
|
2239
|
+
type: "button",
|
|
2240
|
+
style: tabBtnStyle(tab === key),
|
|
2241
|
+
onClick: () => setTab(key),
|
|
2242
|
+
children: label
|
|
2243
|
+
},
|
|
2244
|
+
key
|
|
2245
|
+
)) }),
|
|
2246
|
+
/* @__PURE__ */ jsxs8("div", { style: contentStyle, children: [
|
|
2247
|
+
tab === "profile" && /* @__PURE__ */ jsx14(ProfileTab, {}),
|
|
2248
|
+
tab === "security" && /* @__PURE__ */ jsx14(SecurityTab, {}),
|
|
2249
|
+
tab === "sessions" && /* @__PURE__ */ jsx14(SessionsTab, {})
|
|
2250
|
+
] })
|
|
2251
|
+
] });
|
|
2252
|
+
}
|
|
2253
|
+
function UserProfile({ appearance }) {
|
|
2254
|
+
const { branding } = useBranding();
|
|
2255
|
+
const effectiveBranding = { ...branding, ...appearance?.variables ?? {} };
|
|
2256
|
+
return /* @__PURE__ */ jsx14(ThemeProvider, { branding: effectiveBranding, style: { display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx14(UserProfilePanel, {}) });
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// src/components/shared/ProviderIcon.tsx
|
|
2260
|
+
import { getProviderButtonConfig as getProviderButtonConfig4 } from "@authon/js";
|
|
2261
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2262
|
+
function ProviderIcon({ provider, size = 20 }) {
|
|
2263
|
+
const config = getProviderButtonConfig4(provider);
|
|
2264
|
+
const svg = config.iconSvg.replace(/width="\d+"/, `width="${size}"`).replace(/height="\d+"/, `height="${size}"`);
|
|
2265
|
+
return /* @__PURE__ */ jsx15(
|
|
2266
|
+
"span",
|
|
2267
|
+
{
|
|
2268
|
+
style: { display: "flex", alignItems: "center", flexShrink: 0 },
|
|
2269
|
+
dangerouslySetInnerHTML: { __html: svg }
|
|
2270
|
+
}
|
|
2271
|
+
);
|
|
267
2272
|
}
|
|
268
2273
|
export {
|
|
269
2274
|
AuthonProvider,
|
|
2275
|
+
Button,
|
|
2276
|
+
Divider,
|
|
2277
|
+
Input,
|
|
270
2278
|
Protect,
|
|
2279
|
+
ProviderIcon,
|
|
271
2280
|
SignIn,
|
|
272
2281
|
SignUp,
|
|
273
2282
|
SignedIn,
|
|
274
2283
|
SignedOut,
|
|
2284
|
+
SocialButton,
|
|
2285
|
+
SocialButtons,
|
|
2286
|
+
ThemeProvider,
|
|
275
2287
|
UserButton,
|
|
2288
|
+
UserProfile,
|
|
276
2289
|
useAuthon,
|
|
2290
|
+
useAuthonMfa,
|
|
2291
|
+
useAuthonPasskeys,
|
|
2292
|
+
useAuthonPasswordless,
|
|
2293
|
+
useAuthonSessions,
|
|
2294
|
+
useAuthonWeb3,
|
|
2295
|
+
useBranding,
|
|
2296
|
+
useOrganization,
|
|
2297
|
+
useOrganizationList,
|
|
277
2298
|
useUser
|
|
278
2299
|
};
|
|
279
2300
|
//# sourceMappingURL=index.js.map
|