dauth-context-react 6.1.0 → 6.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/dist/index.d.mts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +1249 -164
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1249 -164
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/DauthProfileModal.tsx +1285 -246
- package/src/api/dauth.api.ts +73 -0
- package/src/api/interfaces/dauth.api.responses.ts +36 -0
- package/src/index.tsx +33 -1
- package/src/initialDauthState.ts +3 -0
- package/src/interfaces.ts +28 -0
- package/src/reducer/dauth.actions.ts +91 -0
- package/src/webauthn.ts +111 -0
package/dist/index.js
CHANGED
|
@@ -40,7 +40,10 @@ var initialDauthState = {
|
|
|
40
40
|
logout: () => {
|
|
41
41
|
},
|
|
42
42
|
updateUser: () => Promise.resolve(false),
|
|
43
|
-
deleteAccount: () => Promise.resolve(false)
|
|
43
|
+
deleteAccount: () => Promise.resolve(false),
|
|
44
|
+
getPasskeyCredentials: () => Promise.resolve([]),
|
|
45
|
+
registerPasskey: () => Promise.resolve(null),
|
|
46
|
+
deletePasskeyCredential: () => Promise.resolve(false)
|
|
44
47
|
};
|
|
45
48
|
var initialDauthState_default = initialDauthState;
|
|
46
49
|
|
|
@@ -147,6 +150,134 @@ async function deleteAccountAPI(basePath) {
|
|
|
147
150
|
const data = await response.json();
|
|
148
151
|
return { response, data };
|
|
149
152
|
}
|
|
153
|
+
async function getPasskeyCredentialsAPI(basePath) {
|
|
154
|
+
const response = await fetch(
|
|
155
|
+
`${basePath}/passkey/credentials`,
|
|
156
|
+
{
|
|
157
|
+
method: "GET",
|
|
158
|
+
headers: { "X-CSRF-Token": getCsrfToken() },
|
|
159
|
+
credentials: "include"
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
const data = await response.json();
|
|
163
|
+
return { response, data };
|
|
164
|
+
}
|
|
165
|
+
async function startPasskeyRegistrationAPI(basePath) {
|
|
166
|
+
const response = await fetch(
|
|
167
|
+
`${basePath}/passkey/register/start`,
|
|
168
|
+
{
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: {
|
|
171
|
+
"Content-Type": "application/json",
|
|
172
|
+
"X-CSRF-Token": getCsrfToken()
|
|
173
|
+
},
|
|
174
|
+
credentials: "include"
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
const data = await response.json();
|
|
178
|
+
return { response, data };
|
|
179
|
+
}
|
|
180
|
+
async function finishPasskeyRegistrationAPI(basePath, body) {
|
|
181
|
+
const response = await fetch(
|
|
182
|
+
`${basePath}/passkey/register/finish`,
|
|
183
|
+
{
|
|
184
|
+
method: "POST",
|
|
185
|
+
headers: {
|
|
186
|
+
"Content-Type": "application/json",
|
|
187
|
+
"X-CSRF-Token": getCsrfToken()
|
|
188
|
+
},
|
|
189
|
+
credentials: "include",
|
|
190
|
+
body: JSON.stringify(body)
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
const data = await response.json();
|
|
194
|
+
return { response, data };
|
|
195
|
+
}
|
|
196
|
+
async function deletePasskeyCredentialAPI(basePath, credentialId) {
|
|
197
|
+
const response = await fetch(
|
|
198
|
+
`${basePath}/passkey/credentials/${credentialId}`,
|
|
199
|
+
{
|
|
200
|
+
method: "DELETE",
|
|
201
|
+
headers: { "X-CSRF-Token": getCsrfToken() },
|
|
202
|
+
credentials: "include"
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
const data = await response.json();
|
|
206
|
+
return { response, data };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/webauthn.ts
|
|
210
|
+
function base64urlToBuffer(base64url) {
|
|
211
|
+
const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
|
|
212
|
+
const pad = base64.length % 4;
|
|
213
|
+
const padded = pad ? base64 + "=".repeat(4 - pad) : base64;
|
|
214
|
+
const binary = atob(padded);
|
|
215
|
+
const bytes = new Uint8Array(binary.length);
|
|
216
|
+
for (let i = 0; i < binary.length; i++) {
|
|
217
|
+
bytes[i] = binary.charCodeAt(i);
|
|
218
|
+
}
|
|
219
|
+
return bytes.buffer;
|
|
220
|
+
}
|
|
221
|
+
function bufferToBase64url(buffer) {
|
|
222
|
+
const bytes = new Uint8Array(buffer);
|
|
223
|
+
let binary = "";
|
|
224
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
225
|
+
binary += String.fromCharCode(bytes[i]);
|
|
226
|
+
}
|
|
227
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
228
|
+
}
|
|
229
|
+
async function createPasskeyCredential(options) {
|
|
230
|
+
const publicKey = {
|
|
231
|
+
...options,
|
|
232
|
+
challenge: base64urlToBuffer(options.challenge),
|
|
233
|
+
user: {
|
|
234
|
+
...options.user,
|
|
235
|
+
id: base64urlToBuffer(options.user.id)
|
|
236
|
+
},
|
|
237
|
+
excludeCredentials: (options.excludeCredentials ?? []).map(
|
|
238
|
+
(c) => ({
|
|
239
|
+
...c,
|
|
240
|
+
id: base64urlToBuffer(c.id)
|
|
241
|
+
})
|
|
242
|
+
)
|
|
243
|
+
};
|
|
244
|
+
const credential = await navigator.credentials.create({
|
|
245
|
+
publicKey
|
|
246
|
+
});
|
|
247
|
+
if (!credential) {
|
|
248
|
+
throw new Error("Passkey registration was cancelled");
|
|
249
|
+
}
|
|
250
|
+
const attestation = credential.response;
|
|
251
|
+
return {
|
|
252
|
+
id: credential.id,
|
|
253
|
+
rawId: bufferToBase64url(credential.rawId),
|
|
254
|
+
type: credential.type,
|
|
255
|
+
response: {
|
|
256
|
+
clientDataJSON: bufferToBase64url(
|
|
257
|
+
attestation.clientDataJSON
|
|
258
|
+
),
|
|
259
|
+
attestationObject: bufferToBase64url(
|
|
260
|
+
attestation.attestationObject
|
|
261
|
+
),
|
|
262
|
+
...attestation.getTransports ? { transports: attestation.getTransports() } : {},
|
|
263
|
+
...attestation.getPublicKeyAlgorithm ? {
|
|
264
|
+
publicKeyAlgorithm: attestation.getPublicKeyAlgorithm()
|
|
265
|
+
} : {},
|
|
266
|
+
...attestation.getPublicKey ? {
|
|
267
|
+
publicKey: bufferToBase64url(
|
|
268
|
+
attestation.getPublicKey()
|
|
269
|
+
)
|
|
270
|
+
} : {},
|
|
271
|
+
...attestation.getAuthenticatorData ? {
|
|
272
|
+
authenticatorData: bufferToBase64url(
|
|
273
|
+
attestation.getAuthenticatorData()
|
|
274
|
+
)
|
|
275
|
+
} : {}
|
|
276
|
+
},
|
|
277
|
+
clientExtensionResults: credential.getClientExtensionResults(),
|
|
278
|
+
authenticatorAttachment: credential.authenticatorAttachment ?? void 0
|
|
279
|
+
};
|
|
280
|
+
}
|
|
150
281
|
|
|
151
282
|
// src/reducer/dauth.actions.ts
|
|
152
283
|
async function exchangeCodeAction(ctx, code) {
|
|
@@ -270,6 +401,65 @@ async function deleteAccountAction(ctx) {
|
|
|
270
401
|
return false;
|
|
271
402
|
}
|
|
272
403
|
}
|
|
404
|
+
async function getPasskeyCredentialsAction(ctx) {
|
|
405
|
+
const { authProxyPath, onError } = ctx;
|
|
406
|
+
try {
|
|
407
|
+
const result = await getPasskeyCredentialsAPI(authProxyPath);
|
|
408
|
+
if (result.response.status === 200) {
|
|
409
|
+
return result.data.credentials ?? [];
|
|
410
|
+
}
|
|
411
|
+
return [];
|
|
412
|
+
} catch (error) {
|
|
413
|
+
onError(
|
|
414
|
+
error instanceof Error ? error : new Error("Get passkey credentials error")
|
|
415
|
+
);
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function registerPasskeyAction(ctx, name) {
|
|
420
|
+
const { authProxyPath, onError } = ctx;
|
|
421
|
+
try {
|
|
422
|
+
const startResult = await startPasskeyRegistrationAPI(authProxyPath);
|
|
423
|
+
if (startResult.response.status !== 200) {
|
|
424
|
+
onError(new Error("Failed to start passkey registration"));
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
const credential = await createPasskeyCredential(
|
|
428
|
+
startResult.data
|
|
429
|
+
);
|
|
430
|
+
const finishResult = await finishPasskeyRegistrationAPI(
|
|
431
|
+
authProxyPath,
|
|
432
|
+
{ credential, name }
|
|
433
|
+
);
|
|
434
|
+
if (finishResult.response.status === 200 || finishResult.response.status === 201) {
|
|
435
|
+
return finishResult.data.credential;
|
|
436
|
+
}
|
|
437
|
+
onError(
|
|
438
|
+
new Error("Failed to finish passkey registration")
|
|
439
|
+
);
|
|
440
|
+
return null;
|
|
441
|
+
} catch (error) {
|
|
442
|
+
onError(
|
|
443
|
+
error instanceof Error ? error : new Error("Passkey registration error")
|
|
444
|
+
);
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async function deletePasskeyCredentialAction(ctx, credentialId) {
|
|
449
|
+
const { authProxyPath, onError } = ctx;
|
|
450
|
+
try {
|
|
451
|
+
const result = await deletePasskeyCredentialAPI(
|
|
452
|
+
authProxyPath,
|
|
453
|
+
credentialId
|
|
454
|
+
);
|
|
455
|
+
return result.response.status === 200;
|
|
456
|
+
} catch (error) {
|
|
457
|
+
onError(
|
|
458
|
+
error instanceof Error ? error : new Error("Delete passkey error")
|
|
459
|
+
);
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
273
463
|
var resetUser = (dispatch) => {
|
|
274
464
|
return dispatch({
|
|
275
465
|
type: LOGIN,
|
|
@@ -361,6 +551,107 @@ function IconBack() {
|
|
|
361
551
|
}
|
|
362
552
|
);
|
|
363
553
|
}
|
|
554
|
+
function IconFingerprint() {
|
|
555
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
556
|
+
"svg",
|
|
557
|
+
{
|
|
558
|
+
width: "16",
|
|
559
|
+
height: "16",
|
|
560
|
+
viewBox: "0 0 24 24",
|
|
561
|
+
fill: "none",
|
|
562
|
+
stroke: "currentColor",
|
|
563
|
+
strokeWidth: "2",
|
|
564
|
+
strokeLinecap: "round",
|
|
565
|
+
strokeLinejoin: "round",
|
|
566
|
+
children: [
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4" }),
|
|
568
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 13.12c0 2.38 0 6.38-1 8.88" }),
|
|
569
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17.29 21.02c.12-.6.43-2.3.5-3.02" }),
|
|
570
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2 12a10 10 0 0 1 18-6" }),
|
|
571
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2 16h.01" }),
|
|
572
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21.8 16c.2-2 .131-5.354 0-6" }),
|
|
573
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 19.5C5.5 18 6 15 6 12a6 6 0 0 1 .34-2" }),
|
|
574
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8.65 22c.21-.66.45-1.32.57-2" }),
|
|
575
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 6.8a6 6 0 0 1 9 5.2v2" })
|
|
576
|
+
]
|
|
577
|
+
}
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
function IconShield() {
|
|
581
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
582
|
+
"svg",
|
|
583
|
+
{
|
|
584
|
+
width: "20",
|
|
585
|
+
height: "20",
|
|
586
|
+
viewBox: "0 0 24 24",
|
|
587
|
+
fill: "none",
|
|
588
|
+
stroke: "currentColor",
|
|
589
|
+
strokeWidth: "2",
|
|
590
|
+
strokeLinecap: "round",
|
|
591
|
+
strokeLinejoin: "round",
|
|
592
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" })
|
|
593
|
+
}
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
function IconTrash() {
|
|
597
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
598
|
+
"svg",
|
|
599
|
+
{
|
|
600
|
+
width: "14",
|
|
601
|
+
height: "14",
|
|
602
|
+
viewBox: "0 0 24 24",
|
|
603
|
+
fill: "none",
|
|
604
|
+
stroke: "currentColor",
|
|
605
|
+
strokeWidth: "2",
|
|
606
|
+
strokeLinecap: "round",
|
|
607
|
+
strokeLinejoin: "round",
|
|
608
|
+
children: [
|
|
609
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 6h18" }),
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
|
|
612
|
+
]
|
|
613
|
+
}
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
function IconLogOut() {
|
|
617
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
618
|
+
"svg",
|
|
619
|
+
{
|
|
620
|
+
width: "16",
|
|
621
|
+
height: "16",
|
|
622
|
+
viewBox: "0 0 24 24",
|
|
623
|
+
fill: "none",
|
|
624
|
+
stroke: "currentColor",
|
|
625
|
+
strokeWidth: "2",
|
|
626
|
+
strokeLinecap: "round",
|
|
627
|
+
strokeLinejoin: "round",
|
|
628
|
+
children: [
|
|
629
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
|
|
630
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "16 17 21 12 16 7" }),
|
|
631
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
|
|
632
|
+
]
|
|
633
|
+
}
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
function IconCamera() {
|
|
637
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
638
|
+
"svg",
|
|
639
|
+
{
|
|
640
|
+
width: "14",
|
|
641
|
+
height: "14",
|
|
642
|
+
viewBox: "0 0 24 24",
|
|
643
|
+
fill: "none",
|
|
644
|
+
stroke: "currentColor",
|
|
645
|
+
strokeWidth: "2",
|
|
646
|
+
strokeLinecap: "round",
|
|
647
|
+
strokeLinejoin: "round",
|
|
648
|
+
children: [
|
|
649
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" }),
|
|
650
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "13", r: "3" })
|
|
651
|
+
]
|
|
652
|
+
}
|
|
653
|
+
);
|
|
654
|
+
}
|
|
364
655
|
function Spinner() {
|
|
365
656
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: spinnerStyle, "aria-hidden": "true" });
|
|
366
657
|
}
|
|
@@ -388,7 +679,10 @@ function useModalAnimation(open) {
|
|
|
388
679
|
}
|
|
389
680
|
if (phase === "entered" || phase === "entering") {
|
|
390
681
|
setPhase("exiting");
|
|
391
|
-
const timer = setTimeout(
|
|
682
|
+
const timer = setTimeout(
|
|
683
|
+
() => setPhase("exited"),
|
|
684
|
+
MOBILE_TRANSITION_MS
|
|
685
|
+
);
|
|
392
686
|
return () => clearTimeout(timer);
|
|
393
687
|
}
|
|
394
688
|
return void 0;
|
|
@@ -456,27 +750,64 @@ function useScrollLock(active) {
|
|
|
456
750
|
};
|
|
457
751
|
}, [active]);
|
|
458
752
|
}
|
|
459
|
-
function DauthProfileModal({
|
|
460
|
-
|
|
753
|
+
function DauthProfileModal({
|
|
754
|
+
open,
|
|
755
|
+
onClose,
|
|
756
|
+
onAvatarUpload
|
|
757
|
+
}) {
|
|
758
|
+
const {
|
|
759
|
+
user,
|
|
760
|
+
domain,
|
|
761
|
+
updateUser,
|
|
762
|
+
deleteAccount,
|
|
763
|
+
logout,
|
|
764
|
+
getPasskeyCredentials,
|
|
765
|
+
registerPasskey,
|
|
766
|
+
deletePasskeyCredential
|
|
767
|
+
} = useDauth();
|
|
461
768
|
const isDesktop = useMediaQuery("(min-width: 641px)");
|
|
462
769
|
const phase = useModalAnimation(open);
|
|
463
770
|
const modalRef = (0, import_react.useRef)(null);
|
|
771
|
+
const avatarInputRef = (0, import_react.useRef)(null);
|
|
772
|
+
const showSecurity = domain.authMethods?.passkey === true;
|
|
773
|
+
const [activeTab, setActiveTab] = (0, import_react.useState)("profile");
|
|
464
774
|
const [name, setName] = (0, import_react.useState)("");
|
|
465
775
|
const [lastname, setLastname] = (0, import_react.useState)("");
|
|
466
776
|
const [nickname, setNickname] = (0, import_react.useState)("");
|
|
467
777
|
const [country, setCountry] = (0, import_react.useState)("");
|
|
778
|
+
const [telPrefix, setTelPrefix] = (0, import_react.useState)("");
|
|
779
|
+
const [telSuffix, setTelSuffix] = (0, import_react.useState)("");
|
|
780
|
+
const [birthDate, setBirthDate] = (0, import_react.useState)("");
|
|
781
|
+
const [customFieldValues, setCustomFieldValues] = (0, import_react.useState)({});
|
|
468
782
|
const [populated, setPopulated] = (0, import_react.useState)(false);
|
|
469
783
|
const [saving, setSaving] = (0, import_react.useState)(false);
|
|
470
784
|
const [status, setStatus] = (0, import_react.useState)(null);
|
|
471
785
|
const [showDelete, setShowDelete] = (0, import_react.useState)(false);
|
|
472
786
|
const [deleteText, setDeleteText] = (0, import_react.useState)("");
|
|
473
787
|
const [deleting, setDeleting] = (0, import_react.useState)(false);
|
|
788
|
+
const [credentials, setCredentials] = (0, import_react.useState)([]);
|
|
789
|
+
const [loadingCreds, setLoadingCreds] = (0, import_react.useState)(false);
|
|
790
|
+
const [showRegister, setShowRegister] = (0, import_react.useState)(false);
|
|
791
|
+
const [passkeyName, setPasskeyName] = (0, import_react.useState)("");
|
|
792
|
+
const [registering, setRegistering] = (0, import_react.useState)(false);
|
|
793
|
+
const [passkeyStatus, setPasskeyStatus] = (0, import_react.useState)(null);
|
|
794
|
+
const [uploadingAvatar, setUploadingAvatar] = (0, import_react.useState)(false);
|
|
474
795
|
(0, import_react.useEffect)(() => {
|
|
475
796
|
if (open && user?._id && !populated) {
|
|
476
797
|
setName(user.name || "");
|
|
477
798
|
setLastname(user.lastname || "");
|
|
478
799
|
setNickname(user.nickname || "");
|
|
479
800
|
setCountry(user.country || "");
|
|
801
|
+
setTelPrefix(user.telPrefix || "");
|
|
802
|
+
setTelSuffix(user.telSuffix || "");
|
|
803
|
+
setBirthDate(
|
|
804
|
+
user.birthDate ? new Date(user.birthDate).toISOString().split("T")[0] : ""
|
|
805
|
+
);
|
|
806
|
+
const cf = {};
|
|
807
|
+
for (const f of domain.customFields ?? []) {
|
|
808
|
+
cf[f.key] = user.customFields?.[f.key] ?? "";
|
|
809
|
+
}
|
|
810
|
+
setCustomFieldValues(cf);
|
|
480
811
|
setPopulated(true);
|
|
481
812
|
}
|
|
482
813
|
if (!open) {
|
|
@@ -484,13 +815,36 @@ function DauthProfileModal({ open, onClose }) {
|
|
|
484
815
|
setStatus(null);
|
|
485
816
|
setShowDelete(false);
|
|
486
817
|
setDeleteText("");
|
|
818
|
+
setActiveTab("profile");
|
|
819
|
+
setPasskeyStatus(null);
|
|
820
|
+
setShowRegister(false);
|
|
821
|
+
setPasskeyName("");
|
|
487
822
|
}
|
|
488
823
|
}, [open, user, populated]);
|
|
824
|
+
(0, import_react.useEffect)(() => {
|
|
825
|
+
if (activeTab !== "security" || !showSecurity) return;
|
|
826
|
+
setLoadingCreds(true);
|
|
827
|
+
getPasskeyCredentials().then((creds) => {
|
|
828
|
+
setCredentials(creds);
|
|
829
|
+
setLoadingCreds(false);
|
|
830
|
+
});
|
|
831
|
+
}, [activeTab, showSecurity, getPasskeyCredentials]);
|
|
489
832
|
(0, import_react.useEffect)(() => {
|
|
490
833
|
if (status?.type !== "success") return;
|
|
491
|
-
const timer = setTimeout(
|
|
834
|
+
const timer = setTimeout(
|
|
835
|
+
() => setStatus(null),
|
|
836
|
+
SUCCESS_TIMEOUT_MS
|
|
837
|
+
);
|
|
492
838
|
return () => clearTimeout(timer);
|
|
493
839
|
}, [status]);
|
|
840
|
+
(0, import_react.useEffect)(() => {
|
|
841
|
+
if (passkeyStatus?.type !== "success") return;
|
|
842
|
+
const timer = setTimeout(
|
|
843
|
+
() => setPasskeyStatus(null),
|
|
844
|
+
SUCCESS_TIMEOUT_MS
|
|
845
|
+
);
|
|
846
|
+
return () => clearTimeout(timer);
|
|
847
|
+
}, [passkeyStatus]);
|
|
494
848
|
useFocusTrap(modalRef, phase === "entered", onClose);
|
|
495
849
|
useScrollLock(phase !== "exited");
|
|
496
850
|
const hasField = (0, import_react.useCallback)(
|
|
@@ -503,8 +857,23 @@ function DauthProfileModal({ open, onClose }) {
|
|
|
503
857
|
);
|
|
504
858
|
const hasChanges = (0, import_react.useMemo)(() => {
|
|
505
859
|
if (!user?._id) return false;
|
|
506
|
-
|
|
507
|
-
|
|
860
|
+
const origBirthDate = user.birthDate ? new Date(user.birthDate).toISOString().split("T")[0] : "";
|
|
861
|
+
const cfChanged = (domain.customFields ?? []).some(
|
|
862
|
+
(f) => (customFieldValues[f.key] ?? "") !== (user.customFields?.[f.key] ?? "")
|
|
863
|
+
);
|
|
864
|
+
return name !== (user.name || "") || lastname !== (user.lastname || "") || nickname !== (user.nickname || "") || country !== (user.country || "") || telPrefix !== (user.telPrefix || "") || telSuffix !== (user.telSuffix || "") || birthDate !== origBirthDate || cfChanged;
|
|
865
|
+
}, [
|
|
866
|
+
name,
|
|
867
|
+
lastname,
|
|
868
|
+
nickname,
|
|
869
|
+
country,
|
|
870
|
+
telPrefix,
|
|
871
|
+
telSuffix,
|
|
872
|
+
birthDate,
|
|
873
|
+
customFieldValues,
|
|
874
|
+
user,
|
|
875
|
+
domain.customFields
|
|
876
|
+
]);
|
|
508
877
|
const canSave = name.trim().length > 0 && hasChanges && !saving;
|
|
509
878
|
const handleSave = (0, import_react.useCallback)(async () => {
|
|
510
879
|
setSaving(true);
|
|
@@ -513,6 +882,14 @@ function DauthProfileModal({ open, onClose }) {
|
|
|
513
882
|
if (hasField("lastname")) fields.lastname = lastname;
|
|
514
883
|
if (hasField("nickname")) fields.nickname = nickname;
|
|
515
884
|
if (hasField("country")) fields.country = country;
|
|
885
|
+
if (hasField("tel_prefix"))
|
|
886
|
+
fields.telPrefix = telPrefix;
|
|
887
|
+
if (hasField("tel_suffix"))
|
|
888
|
+
fields.telSuffix = telSuffix;
|
|
889
|
+
if (hasField("birth_date") && birthDate)
|
|
890
|
+
fields.birthDate = birthDate;
|
|
891
|
+
if ((domain.customFields ?? []).length > 0)
|
|
892
|
+
fields.customFields = customFieldValues;
|
|
516
893
|
const ok = await updateUser(fields);
|
|
517
894
|
setSaving(false);
|
|
518
895
|
if (ok) {
|
|
@@ -526,7 +903,19 @@ function DauthProfileModal({ open, onClose }) {
|
|
|
526
903
|
message: "Something went wrong. Please try again."
|
|
527
904
|
});
|
|
528
905
|
}
|
|
529
|
-
}, [
|
|
906
|
+
}, [
|
|
907
|
+
name,
|
|
908
|
+
lastname,
|
|
909
|
+
nickname,
|
|
910
|
+
country,
|
|
911
|
+
telPrefix,
|
|
912
|
+
telSuffix,
|
|
913
|
+
birthDate,
|
|
914
|
+
customFieldValues,
|
|
915
|
+
hasField,
|
|
916
|
+
updateUser,
|
|
917
|
+
domain.customFields
|
|
918
|
+
]);
|
|
530
919
|
const handleDelete = (0, import_react.useCallback)(async () => {
|
|
531
920
|
setDeleting(true);
|
|
532
921
|
const ok = await deleteAccount();
|
|
@@ -542,16 +931,87 @@ function DauthProfileModal({ open, onClose }) {
|
|
|
542
931
|
setDeleteText("");
|
|
543
932
|
}
|
|
544
933
|
}, [deleteAccount, onClose]);
|
|
934
|
+
const handleLanguage = (0, import_react.useCallback)(
|
|
935
|
+
async (lang) => {
|
|
936
|
+
await updateUser({ language: lang });
|
|
937
|
+
},
|
|
938
|
+
[updateUser]
|
|
939
|
+
);
|
|
940
|
+
const handleRegisterPasskey = (0, import_react.useCallback)(async () => {
|
|
941
|
+
setRegistering(true);
|
|
942
|
+
setPasskeyStatus(null);
|
|
943
|
+
const cred = await registerPasskey(
|
|
944
|
+
passkeyName || void 0
|
|
945
|
+
);
|
|
946
|
+
setRegistering(false);
|
|
947
|
+
if (cred) {
|
|
948
|
+
setCredentials((prev) => [...prev, cred]);
|
|
949
|
+
setPasskeyName("");
|
|
950
|
+
setShowRegister(false);
|
|
951
|
+
setPasskeyStatus({
|
|
952
|
+
type: "success",
|
|
953
|
+
message: "Passkey registered successfully"
|
|
954
|
+
});
|
|
955
|
+
} else {
|
|
956
|
+
setPasskeyStatus({
|
|
957
|
+
type: "error",
|
|
958
|
+
message: "Failed to register passkey"
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
}, [passkeyName, registerPasskey]);
|
|
962
|
+
const handleDeletePasskey = (0, import_react.useCallback)(
|
|
963
|
+
async (credentialId) => {
|
|
964
|
+
const ok = await deletePasskeyCredential(credentialId);
|
|
965
|
+
if (ok) {
|
|
966
|
+
setCredentials(
|
|
967
|
+
(prev) => prev.filter((c) => c._id !== credentialId)
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
},
|
|
971
|
+
[deletePasskeyCredential]
|
|
972
|
+
);
|
|
973
|
+
const handleAvatarClick = (0, import_react.useCallback)(() => {
|
|
974
|
+
if (onAvatarUpload) {
|
|
975
|
+
avatarInputRef.current?.click();
|
|
976
|
+
}
|
|
977
|
+
}, [onAvatarUpload]);
|
|
978
|
+
const handleAvatarChange = (0, import_react.useCallback)(
|
|
979
|
+
async (e) => {
|
|
980
|
+
const file = e.target.files?.[0];
|
|
981
|
+
if (!file || !onAvatarUpload) return;
|
|
982
|
+
setUploadingAvatar(true);
|
|
983
|
+
try {
|
|
984
|
+
const url = await onAvatarUpload(file);
|
|
985
|
+
if (url) {
|
|
986
|
+
await updateUser({ avatar: url });
|
|
987
|
+
}
|
|
988
|
+
} catch {
|
|
989
|
+
}
|
|
990
|
+
setUploadingAvatar(false);
|
|
991
|
+
if (avatarInputRef.current) {
|
|
992
|
+
avatarInputRef.current.value = "";
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
[onAvatarUpload, updateUser]
|
|
996
|
+
);
|
|
997
|
+
const handleSignOut = (0, import_react.useCallback)(() => {
|
|
998
|
+
logout();
|
|
999
|
+
onClose();
|
|
1000
|
+
}, [logout, onClose]);
|
|
545
1001
|
const themeVars = (0, import_react.useMemo)(() => {
|
|
546
1002
|
const t = domain.modalTheme;
|
|
547
1003
|
if (!t) return {};
|
|
548
1004
|
const vars = {};
|
|
549
1005
|
if (t.accent) vars["--dauth-accent"] = t.accent;
|
|
550
|
-
if (t.accentHover)
|
|
1006
|
+
if (t.accentHover)
|
|
1007
|
+
vars["--dauth-accent-hover"] = t.accentHover;
|
|
551
1008
|
if (t.surface) vars["--dauth-surface"] = t.surface;
|
|
552
|
-
if (t.surfaceHover)
|
|
553
|
-
|
|
554
|
-
if (t.
|
|
1009
|
+
if (t.surfaceHover)
|
|
1010
|
+
vars["--dauth-surface-hover"] = t.surfaceHover;
|
|
1011
|
+
if (t.textPrimary)
|
|
1012
|
+
vars["--dauth-text-primary"] = t.textPrimary;
|
|
1013
|
+
if (t.textSecondary)
|
|
1014
|
+
vars["--dauth-text-secondary"] = t.textSecondary;
|
|
555
1015
|
if (t.border) vars["--dauth-border"] = t.border;
|
|
556
1016
|
return vars;
|
|
557
1017
|
}, [domain.modalTheme]);
|
|
@@ -602,6 +1062,11 @@ function DauthProfileModal({ open, onClose }) {
|
|
|
602
1062
|
transition: `transform ${dur}ms ${easing}`
|
|
603
1063
|
};
|
|
604
1064
|
const avatarInitial = (user.name || user.email || "?").charAt(0).toUpperCase();
|
|
1065
|
+
const tabs = [
|
|
1066
|
+
{ key: "profile", label: "Profile" },
|
|
1067
|
+
...showSecurity ? [{ key: "security", label: "Security" }] : [],
|
|
1068
|
+
{ key: "account", label: "Account" }
|
|
1069
|
+
];
|
|
605
1070
|
return (0, import_react_dom.createPortal)(
|
|
606
1071
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
607
1072
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -645,191 +1110,663 @@ function DauthProfileModal({ open, onClose }) {
|
|
|
645
1110
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: "dauth-profile-title", style: titleStyle, children: "Your Profile" }),
|
|
646
1111
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: 36 } })
|
|
647
1112
|
] }),
|
|
1113
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: tabBar, role: "tablist", children: tabs.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1114
|
+
"button",
|
|
1115
|
+
{
|
|
1116
|
+
role: "tab",
|
|
1117
|
+
type: "button",
|
|
1118
|
+
"aria-selected": activeTab === t.key,
|
|
1119
|
+
style: {
|
|
1120
|
+
...tabBtn,
|
|
1121
|
+
color: activeTab === t.key ? "var(--dauth-accent, #6366f1)" : "var(--dauth-text-secondary, #a1a1aa)",
|
|
1122
|
+
borderBottomColor: activeTab === t.key ? "var(--dauth-accent, #6366f1)" : "transparent"
|
|
1123
|
+
},
|
|
1124
|
+
onClick: () => setActiveTab(t.key),
|
|
1125
|
+
children: t.label
|
|
1126
|
+
},
|
|
1127
|
+
t.key
|
|
1128
|
+
)) }),
|
|
648
1129
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: bodyStyle, children: [
|
|
649
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
650
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
src: user.avatar.url,
|
|
654
|
-
alt: "",
|
|
655
|
-
style: {
|
|
656
|
-
width: "100%",
|
|
657
|
-
height: "100%",
|
|
658
|
-
objectFit: "cover"
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
) : avatarInitial }),
|
|
662
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: emailText, children: user.email })
|
|
663
|
-
] }),
|
|
664
|
-
status && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
665
|
-
"div",
|
|
666
|
-
{
|
|
667
|
-
role: "status",
|
|
668
|
-
"aria-live": "polite",
|
|
669
|
-
style: statusMsg(status.type),
|
|
670
|
-
children: status.message
|
|
671
|
-
}
|
|
672
|
-
),
|
|
673
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
674
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
675
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dauth-name", style: label, children: "Name *" }),
|
|
676
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
677
|
-
"input",
|
|
1130
|
+
activeTab === "profile" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1131
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: avatarSection, children: [
|
|
1132
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1133
|
+
"div",
|
|
678
1134
|
{
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
1135
|
+
style: {
|
|
1136
|
+
...avatarCircle,
|
|
1137
|
+
cursor: onAvatarUpload ? "pointer" : "default",
|
|
1138
|
+
position: "relative"
|
|
1139
|
+
},
|
|
1140
|
+
onClick: handleAvatarClick,
|
|
1141
|
+
children: [
|
|
1142
|
+
uploadingAvatar ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}) : user.avatar?.url ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1143
|
+
"img",
|
|
1144
|
+
{
|
|
1145
|
+
src: user.avatar.url,
|
|
1146
|
+
alt: "",
|
|
1147
|
+
style: {
|
|
1148
|
+
width: "100%",
|
|
1149
|
+
height: "100%",
|
|
1150
|
+
objectFit: "cover"
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
) : avatarInitial,
|
|
1154
|
+
onAvatarUpload && !uploadingAvatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: avatarOverlay, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconCamera, {}) })
|
|
1155
|
+
]
|
|
688
1156
|
}
|
|
689
|
-
)
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: "dauth-lastname", style: label, children: [
|
|
693
|
-
"Last name",
|
|
694
|
-
isRequired("lastname") ? " *" : ""
|
|
695
|
-
] }),
|
|
696
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1157
|
+
),
|
|
1158
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: emailText, children: user.email }),
|
|
1159
|
+
onAvatarUpload && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
697
1160
|
"input",
|
|
698
1161
|
{
|
|
699
|
-
|
|
700
|
-
type: "
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
disabled: saving,
|
|
705
|
-
style: input,
|
|
706
|
-
onFocus: inputFocusHandler,
|
|
707
|
-
onBlur: inputBlurHandler
|
|
1162
|
+
ref: avatarInputRef,
|
|
1163
|
+
type: "file",
|
|
1164
|
+
accept: "image/*",
|
|
1165
|
+
style: { display: "none" },
|
|
1166
|
+
onChange: handleAvatarChange
|
|
708
1167
|
}
|
|
709
1168
|
)
|
|
710
1169
|
] }),
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
1170
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1171
|
+
"div",
|
|
1172
|
+
{
|
|
1173
|
+
role: "status",
|
|
1174
|
+
"aria-live": "polite",
|
|
1175
|
+
style: statusMsg(status.type),
|
|
1176
|
+
children: status.message
|
|
1177
|
+
}
|
|
1178
|
+
),
|
|
1179
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1180
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
1181
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1182
|
+
"label",
|
|
1183
|
+
{
|
|
1184
|
+
htmlFor: "dauth-name",
|
|
1185
|
+
style: label,
|
|
1186
|
+
children: "Name *"
|
|
1187
|
+
}
|
|
1188
|
+
),
|
|
1189
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1190
|
+
"input",
|
|
1191
|
+
{
|
|
1192
|
+
id: "dauth-name",
|
|
1193
|
+
type: "text",
|
|
1194
|
+
value: name,
|
|
1195
|
+
onChange: (e) => setName(e.target.value),
|
|
1196
|
+
placeholder: "Your name",
|
|
1197
|
+
disabled: saving,
|
|
1198
|
+
style: input,
|
|
1199
|
+
onFocus: inputFocusHandler,
|
|
1200
|
+
onBlur: inputBlurHandler
|
|
1201
|
+
}
|
|
1202
|
+
)
|
|
715
1203
|
] }),
|
|
716
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
1204
|
+
hasField("lastname") && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
1205
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1206
|
+
"label",
|
|
1207
|
+
{
|
|
1208
|
+
htmlFor: "dauth-lastname",
|
|
1209
|
+
style: label,
|
|
1210
|
+
children: [
|
|
1211
|
+
"Last name",
|
|
1212
|
+
isRequired("lastname") ? " *" : ""
|
|
1213
|
+
]
|
|
1214
|
+
}
|
|
1215
|
+
),
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1217
|
+
"input",
|
|
1218
|
+
{
|
|
1219
|
+
id: "dauth-lastname",
|
|
1220
|
+
type: "text",
|
|
1221
|
+
value: lastname,
|
|
1222
|
+
onChange: (e) => setLastname(e.target.value),
|
|
1223
|
+
placeholder: "Your last name",
|
|
1224
|
+
disabled: saving,
|
|
1225
|
+
style: input,
|
|
1226
|
+
onFocus: inputFocusHandler,
|
|
1227
|
+
onBlur: inputBlurHandler
|
|
1228
|
+
}
|
|
1229
|
+
)
|
|
735
1230
|
] }),
|
|
736
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
737
|
-
|
|
1231
|
+
hasField("nickname") && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
1232
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1233
|
+
"label",
|
|
1234
|
+
{
|
|
1235
|
+
htmlFor: "dauth-nickname",
|
|
1236
|
+
style: label,
|
|
1237
|
+
children: [
|
|
1238
|
+
"Nickname",
|
|
1239
|
+
isRequired("nickname") ? " *" : ""
|
|
1240
|
+
]
|
|
1241
|
+
}
|
|
1242
|
+
),
|
|
1243
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1244
|
+
"input",
|
|
1245
|
+
{
|
|
1246
|
+
id: "dauth-nickname",
|
|
1247
|
+
type: "text",
|
|
1248
|
+
value: nickname,
|
|
1249
|
+
onChange: (e) => setNickname(e.target.value),
|
|
1250
|
+
placeholder: "Choose a nickname",
|
|
1251
|
+
disabled: saving,
|
|
1252
|
+
style: input,
|
|
1253
|
+
onFocus: inputFocusHandler,
|
|
1254
|
+
onBlur: inputBlurHandler
|
|
1255
|
+
}
|
|
1256
|
+
)
|
|
1257
|
+
] }),
|
|
1258
|
+
hasField("country") && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
1259
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1260
|
+
"label",
|
|
1261
|
+
{
|
|
1262
|
+
htmlFor: "dauth-country",
|
|
1263
|
+
style: label,
|
|
1264
|
+
children: [
|
|
1265
|
+
"Country",
|
|
1266
|
+
isRequired("country") ? " *" : ""
|
|
1267
|
+
]
|
|
1268
|
+
}
|
|
1269
|
+
),
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1271
|
+
"input",
|
|
1272
|
+
{
|
|
1273
|
+
id: "dauth-country",
|
|
1274
|
+
type: "text",
|
|
1275
|
+
value: country,
|
|
1276
|
+
onChange: (e) => setCountry(e.target.value),
|
|
1277
|
+
placeholder: "Your country",
|
|
1278
|
+
disabled: saving,
|
|
1279
|
+
style: input,
|
|
1280
|
+
onFocus: inputFocusHandler,
|
|
1281
|
+
onBlur: inputBlurHandler
|
|
1282
|
+
}
|
|
1283
|
+
)
|
|
1284
|
+
] }),
|
|
1285
|
+
(hasField("tel_prefix") || hasField("tel_suffix")) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
1286
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: label, children: [
|
|
1287
|
+
"Phone",
|
|
1288
|
+
isRequired("tel_prefix") || isRequired("tel_suffix") ? " *" : ""
|
|
1289
|
+
] }),
|
|
1290
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1291
|
+
"div",
|
|
1292
|
+
{
|
|
1293
|
+
style: {
|
|
1294
|
+
display: "flex",
|
|
1295
|
+
gap: 8
|
|
1296
|
+
},
|
|
1297
|
+
children: [
|
|
1298
|
+
hasField("tel_prefix") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1299
|
+
"input",
|
|
1300
|
+
{
|
|
1301
|
+
id: "dauth-tel-prefix",
|
|
1302
|
+
type: "text",
|
|
1303
|
+
value: telPrefix,
|
|
1304
|
+
onChange: (e) => setTelPrefix(e.target.value),
|
|
1305
|
+
placeholder: "+34",
|
|
1306
|
+
disabled: saving,
|
|
1307
|
+
style: {
|
|
1308
|
+
...input,
|
|
1309
|
+
width: 80,
|
|
1310
|
+
flexShrink: 0
|
|
1311
|
+
},
|
|
1312
|
+
onFocus: inputFocusHandler,
|
|
1313
|
+
onBlur: inputBlurHandler,
|
|
1314
|
+
"aria-label": "Phone prefix"
|
|
1315
|
+
}
|
|
1316
|
+
),
|
|
1317
|
+
hasField("tel_suffix") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1318
|
+
"input",
|
|
1319
|
+
{
|
|
1320
|
+
id: "dauth-tel-suffix",
|
|
1321
|
+
type: "tel",
|
|
1322
|
+
value: telSuffix,
|
|
1323
|
+
onChange: (e) => setTelSuffix(e.target.value),
|
|
1324
|
+
placeholder: "612 345 678",
|
|
1325
|
+
disabled: saving,
|
|
1326
|
+
style: {
|
|
1327
|
+
...input,
|
|
1328
|
+
flex: 1
|
|
1329
|
+
},
|
|
1330
|
+
onFocus: inputFocusHandler,
|
|
1331
|
+
onBlur: inputBlurHandler,
|
|
1332
|
+
"aria-label": "Phone number"
|
|
1333
|
+
}
|
|
1334
|
+
)
|
|
1335
|
+
]
|
|
1336
|
+
}
|
|
1337
|
+
)
|
|
1338
|
+
] }),
|
|
1339
|
+
hasField("birth_date") && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
1340
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1341
|
+
"label",
|
|
1342
|
+
{
|
|
1343
|
+
htmlFor: "dauth-birthdate",
|
|
1344
|
+
style: label,
|
|
1345
|
+
children: [
|
|
1346
|
+
"Birth date",
|
|
1347
|
+
isRequired("birth_date") ? " *" : ""
|
|
1348
|
+
]
|
|
1349
|
+
}
|
|
1350
|
+
),
|
|
1351
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1352
|
+
"input",
|
|
1353
|
+
{
|
|
1354
|
+
id: "dauth-birthdate",
|
|
1355
|
+
type: "date",
|
|
1356
|
+
value: birthDate,
|
|
1357
|
+
onChange: (e) => setBirthDate(e.target.value),
|
|
1358
|
+
disabled: saving,
|
|
1359
|
+
style: input,
|
|
1360
|
+
onFocus: inputFocusHandler,
|
|
1361
|
+
onBlur: inputBlurHandler
|
|
1362
|
+
}
|
|
1363
|
+
)
|
|
1364
|
+
] }),
|
|
1365
|
+
(domain.customFields ?? []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { style: separator }),
|
|
1367
|
+
domain.customFields.map((cf) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1368
|
+
"div",
|
|
1369
|
+
{
|
|
1370
|
+
style: fieldGroup,
|
|
1371
|
+
children: [
|
|
1372
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1373
|
+
"label",
|
|
1374
|
+
{
|
|
1375
|
+
htmlFor: `dauth-cf-${cf.key}`,
|
|
1376
|
+
style: label,
|
|
1377
|
+
children: [
|
|
1378
|
+
cf.label,
|
|
1379
|
+
cf.required ? " *" : ""
|
|
1380
|
+
]
|
|
1381
|
+
}
|
|
1382
|
+
),
|
|
1383
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1384
|
+
"input",
|
|
1385
|
+
{
|
|
1386
|
+
id: `dauth-cf-${cf.key}`,
|
|
1387
|
+
type: "text",
|
|
1388
|
+
value: customFieldValues[cf.key] ?? "",
|
|
1389
|
+
onChange: (e) => setCustomFieldValues(
|
|
1390
|
+
(prev) => ({
|
|
1391
|
+
...prev,
|
|
1392
|
+
[cf.key]: e.target.value
|
|
1393
|
+
})
|
|
1394
|
+
),
|
|
1395
|
+
disabled: saving,
|
|
1396
|
+
style: input,
|
|
1397
|
+
onFocus: inputFocusHandler,
|
|
1398
|
+
onBlur: inputBlurHandler
|
|
1399
|
+
}
|
|
1400
|
+
)
|
|
1401
|
+
]
|
|
1402
|
+
},
|
|
1403
|
+
cf.key
|
|
1404
|
+
))
|
|
1405
|
+
] })
|
|
1406
|
+
] }),
|
|
1407
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { style: separator }),
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: label, children: "Language" }),
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: 8 }, children: ["es", "en"].map((lang) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1411
|
+
"button",
|
|
738
1412
|
{
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
)
|
|
1413
|
+
type: "button",
|
|
1414
|
+
style: {
|
|
1415
|
+
...langBtn,
|
|
1416
|
+
backgroundColor: user.language === lang ? "var(--dauth-accent, #6366f1)" : "var(--dauth-surface-secondary, rgba(255, 255, 255, 0.04))",
|
|
1417
|
+
color: user.language === lang ? "#ffffff" : "var(--dauth-text-secondary, #a1a1aa)"
|
|
1418
|
+
},
|
|
1419
|
+
onClick: () => handleLanguage(lang),
|
|
1420
|
+
children: lang === "es" ? "Espa\xF1ol" : "English"
|
|
1421
|
+
},
|
|
1422
|
+
lang
|
|
1423
|
+
)) })
|
|
750
1424
|
] })
|
|
751
1425
|
] }),
|
|
752
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: dangerDesc, children: "Permanently delete your account and all associated data." }),
|
|
756
|
-
!showDelete ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
757
|
-
"button",
|
|
1426
|
+
activeTab === "security" && showSecurity && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1427
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1428
|
+
"div",
|
|
758
1429
|
{
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1430
|
+
style: {
|
|
1431
|
+
display: "flex",
|
|
1432
|
+
alignItems: "center",
|
|
1433
|
+
justifyContent: "space-between",
|
|
1434
|
+
marginBottom: 16
|
|
1435
|
+
},
|
|
1436
|
+
children: [
|
|
1437
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1438
|
+
"div",
|
|
1439
|
+
{
|
|
1440
|
+
style: {
|
|
1441
|
+
...label,
|
|
1442
|
+
marginBottom: 0,
|
|
1443
|
+
fontWeight: 600
|
|
1444
|
+
},
|
|
1445
|
+
children: "Passkeys"
|
|
1446
|
+
}
|
|
1447
|
+
),
|
|
1448
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1449
|
+
"button",
|
|
1450
|
+
{
|
|
1451
|
+
type: "button",
|
|
1452
|
+
style: outlineBtn,
|
|
1453
|
+
onClick: () => setShowRegister(!showRegister),
|
|
1454
|
+
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "var(--dauth-surface-hover, #232340)",
|
|
1455
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "transparent",
|
|
1456
|
+
children: "+ Add passkey"
|
|
1457
|
+
}
|
|
1458
|
+
)
|
|
1459
|
+
]
|
|
765
1460
|
}
|
|
766
|
-
)
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1461
|
+
),
|
|
1462
|
+
showRegister && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: registerPanel, children: [
|
|
1463
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fieldGroup, children: [
|
|
1464
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1465
|
+
"label",
|
|
1466
|
+
{
|
|
1467
|
+
htmlFor: "dauth-passkey-name",
|
|
1468
|
+
style: label,
|
|
1469
|
+
children: "Passkey name (optional)"
|
|
1470
|
+
}
|
|
1471
|
+
),
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1473
|
+
"input",
|
|
1474
|
+
{
|
|
1475
|
+
id: "dauth-passkey-name",
|
|
1476
|
+
type: "text",
|
|
1477
|
+
value: passkeyName,
|
|
1478
|
+
onChange: (e) => setPasskeyName(e.target.value),
|
|
1479
|
+
placeholder: "e.g. MacBook Touch ID",
|
|
1480
|
+
disabled: registering,
|
|
1481
|
+
style: input,
|
|
1482
|
+
onFocus: inputFocusHandler,
|
|
1483
|
+
onBlur: inputBlurHandler
|
|
1484
|
+
}
|
|
1485
|
+
)
|
|
772
1486
|
] }),
|
|
773
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
774
|
-
"input",
|
|
775
|
-
{
|
|
776
|
-
type: "text",
|
|
777
|
-
value: deleteText,
|
|
778
|
-
onChange: (e) => setDeleteText(e.target.value),
|
|
779
|
-
placeholder: `Type ${CONFIRM_WORD}`,
|
|
780
|
-
style: input,
|
|
781
|
-
onFocus: inputFocusHandler,
|
|
782
|
-
onBlur: inputBlurHandler,
|
|
783
|
-
disabled: deleting
|
|
784
|
-
}
|
|
785
|
-
),
|
|
786
1487
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
787
1488
|
"div",
|
|
788
1489
|
{
|
|
789
1490
|
style: {
|
|
790
1491
|
display: "flex",
|
|
791
|
-
gap: 8
|
|
792
|
-
marginTop: 12
|
|
1492
|
+
gap: 8
|
|
793
1493
|
},
|
|
794
1494
|
children: [
|
|
795
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
1495
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
796
1496
|
"button",
|
|
797
1497
|
{
|
|
798
1498
|
type: "button",
|
|
799
|
-
style:
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
setDeleteText("");
|
|
1499
|
+
style: {
|
|
1500
|
+
...smallAccentBtn,
|
|
1501
|
+
opacity: registering ? 0.6 : 1
|
|
803
1502
|
},
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
children:
|
|
1503
|
+
disabled: registering,
|
|
1504
|
+
onClick: handleRegisterPasskey,
|
|
1505
|
+
children: [
|
|
1506
|
+
registering ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconFingerprint, {}),
|
|
1507
|
+
registering ? "Registering..." : "Register"
|
|
1508
|
+
]
|
|
807
1509
|
}
|
|
808
1510
|
),
|
|
809
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
810
1512
|
"button",
|
|
811
1513
|
{
|
|
812
1514
|
type: "button",
|
|
813
|
-
style:
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
disabled: deleteText !== CONFIRM_WORD || deleting,
|
|
819
|
-
onClick: handleDelete,
|
|
820
|
-
children: [
|
|
821
|
-
deleting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}),
|
|
822
|
-
"Delete my account"
|
|
823
|
-
]
|
|
1515
|
+
style: cancelBtn,
|
|
1516
|
+
onClick: () => setShowRegister(false),
|
|
1517
|
+
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "var(--dauth-surface-hover, #232340)",
|
|
1518
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "transparent",
|
|
1519
|
+
children: "Cancel"
|
|
824
1520
|
}
|
|
825
1521
|
)
|
|
826
1522
|
]
|
|
827
1523
|
}
|
|
828
1524
|
)
|
|
829
|
-
] })
|
|
1525
|
+
] }),
|
|
1526
|
+
passkeyStatus && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1527
|
+
"div",
|
|
1528
|
+
{
|
|
1529
|
+
role: "status",
|
|
1530
|
+
"aria-live": "polite",
|
|
1531
|
+
style: {
|
|
1532
|
+
...statusMsg(passkeyStatus.type),
|
|
1533
|
+
marginTop: 12
|
|
1534
|
+
},
|
|
1535
|
+
children: passkeyStatus.message
|
|
1536
|
+
}
|
|
1537
|
+
),
|
|
1538
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 12 }, children: loadingCreds ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1539
|
+
"div",
|
|
1540
|
+
{
|
|
1541
|
+
style: {
|
|
1542
|
+
textAlign: "center",
|
|
1543
|
+
padding: 24
|
|
1544
|
+
},
|
|
1545
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})
|
|
1546
|
+
}
|
|
1547
|
+
) : credentials.length > 0 ? credentials.map((cred) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1548
|
+
"div",
|
|
1549
|
+
{
|
|
1550
|
+
style: credentialRow,
|
|
1551
|
+
children: [
|
|
1552
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1553
|
+
"div",
|
|
1554
|
+
{
|
|
1555
|
+
style: {
|
|
1556
|
+
display: "flex",
|
|
1557
|
+
alignItems: "center",
|
|
1558
|
+
gap: 12,
|
|
1559
|
+
flex: 1,
|
|
1560
|
+
minWidth: 0
|
|
1561
|
+
},
|
|
1562
|
+
children: [
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1564
|
+
"span",
|
|
1565
|
+
{
|
|
1566
|
+
style: {
|
|
1567
|
+
color: "var(--dauth-accent, #6366f1)",
|
|
1568
|
+
flexShrink: 0
|
|
1569
|
+
},
|
|
1570
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconFingerprint, {})
|
|
1571
|
+
}
|
|
1572
|
+
),
|
|
1573
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1574
|
+
"div",
|
|
1575
|
+
{
|
|
1576
|
+
style: {
|
|
1577
|
+
minWidth: 0,
|
|
1578
|
+
flex: 1
|
|
1579
|
+
},
|
|
1580
|
+
children: [
|
|
1581
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1582
|
+
"div",
|
|
1583
|
+
{
|
|
1584
|
+
style: {
|
|
1585
|
+
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
1586
|
+
fontWeight: 500,
|
|
1587
|
+
color: "var(--dauth-text-primary, #e4e4e7)",
|
|
1588
|
+
overflow: "hidden",
|
|
1589
|
+
textOverflow: "ellipsis",
|
|
1590
|
+
whiteSpace: "nowrap"
|
|
1591
|
+
},
|
|
1592
|
+
children: cred.name || "Passkey"
|
|
1593
|
+
}
|
|
1594
|
+
),
|
|
1595
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1596
|
+
"div",
|
|
1597
|
+
{
|
|
1598
|
+
style: {
|
|
1599
|
+
fontSize: "var(--dauth-font-size-xs, 0.75rem)",
|
|
1600
|
+
color: "var(--dauth-text-muted, #71717a)"
|
|
1601
|
+
},
|
|
1602
|
+
children: [
|
|
1603
|
+
cred.deviceType === "multiDevice" ? "Synced" : "Device-bound",
|
|
1604
|
+
cred.createdAt && ` \xB7 Created ${new Date(cred.createdAt).toLocaleDateString()}`
|
|
1605
|
+
]
|
|
1606
|
+
}
|
|
1607
|
+
)
|
|
1608
|
+
]
|
|
1609
|
+
}
|
|
1610
|
+
)
|
|
1611
|
+
]
|
|
1612
|
+
}
|
|
1613
|
+
),
|
|
1614
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1615
|
+
"button",
|
|
1616
|
+
{
|
|
1617
|
+
type: "button",
|
|
1618
|
+
onClick: () => handleDeletePasskey(cred._id),
|
|
1619
|
+
style: trashBtn,
|
|
1620
|
+
onMouseEnter: (e) => e.currentTarget.style.color = "var(--dauth-error, #ef4444)",
|
|
1621
|
+
onMouseLeave: (e) => e.currentTarget.style.color = "var(--dauth-text-muted, #71717a)",
|
|
1622
|
+
"aria-label": `Delete passkey ${cred.name || ""}`,
|
|
1623
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconTrash, {})
|
|
1624
|
+
}
|
|
1625
|
+
)
|
|
1626
|
+
]
|
|
1627
|
+
},
|
|
1628
|
+
cred._id
|
|
1629
|
+
)) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: emptyState, children: [
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1631
|
+
"span",
|
|
1632
|
+
{
|
|
1633
|
+
style: {
|
|
1634
|
+
color: "var(--dauth-accent, #6366f1)"
|
|
1635
|
+
},
|
|
1636
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconShield, {})
|
|
1637
|
+
}
|
|
1638
|
+
),
|
|
1639
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1641
|
+
"div",
|
|
1642
|
+
{
|
|
1643
|
+
style: {
|
|
1644
|
+
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
1645
|
+
fontWeight: 500,
|
|
1646
|
+
color: "var(--dauth-text-primary, #e4e4e7)"
|
|
1647
|
+
},
|
|
1648
|
+
children: "No passkeys registered"
|
|
1649
|
+
}
|
|
1650
|
+
),
|
|
1651
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1652
|
+
"div",
|
|
1653
|
+
{
|
|
1654
|
+
style: {
|
|
1655
|
+
fontSize: "var(--dauth-font-size-xs, 0.75rem)",
|
|
1656
|
+
color: "var(--dauth-text-secondary, #a1a1aa)"
|
|
1657
|
+
},
|
|
1658
|
+
children: "Add a passkey for faster, more secure sign-in."
|
|
1659
|
+
}
|
|
1660
|
+
)
|
|
1661
|
+
] })
|
|
1662
|
+
] }) })
|
|
1663
|
+
] }),
|
|
1664
|
+
activeTab === "account" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1665
|
+
status && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1666
|
+
"div",
|
|
1667
|
+
{
|
|
1668
|
+
role: "status",
|
|
1669
|
+
"aria-live": "polite",
|
|
1670
|
+
style: statusMsg(status.type),
|
|
1671
|
+
children: status.message
|
|
1672
|
+
}
|
|
1673
|
+
),
|
|
1674
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1675
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: dangerTitle, children: "Delete account" }),
|
|
1676
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: dangerDesc, children: "Permanently delete your account and all associated data." }),
|
|
1677
|
+
!showDelete ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1678
|
+
"button",
|
|
1679
|
+
{
|
|
1680
|
+
type: "button",
|
|
1681
|
+
style: deleteBtn,
|
|
1682
|
+
onClick: () => setShowDelete(true),
|
|
1683
|
+
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "rgba(239, 68, 68, 0.2)",
|
|
1684
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "var(--dauth-error-bg, rgba(239, 68, 68, 0.1))",
|
|
1685
|
+
children: "Delete account"
|
|
1686
|
+
}
|
|
1687
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: deletePanel, children: [
|
|
1688
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: deletePanelText, children: [
|
|
1689
|
+
"This action is permanent and cannot be undone. Type",
|
|
1690
|
+
" ",
|
|
1691
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: CONFIRM_WORD }),
|
|
1692
|
+
" to confirm."
|
|
1693
|
+
] }),
|
|
1694
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1695
|
+
"input",
|
|
1696
|
+
{
|
|
1697
|
+
type: "text",
|
|
1698
|
+
value: deleteText,
|
|
1699
|
+
onChange: (e) => setDeleteText(e.target.value),
|
|
1700
|
+
placeholder: `Type ${CONFIRM_WORD}`,
|
|
1701
|
+
style: input,
|
|
1702
|
+
onFocus: inputFocusHandler,
|
|
1703
|
+
onBlur: inputBlurHandler,
|
|
1704
|
+
disabled: deleting
|
|
1705
|
+
}
|
|
1706
|
+
),
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1708
|
+
"div",
|
|
1709
|
+
{
|
|
1710
|
+
style: {
|
|
1711
|
+
display: "flex",
|
|
1712
|
+
gap: 8,
|
|
1713
|
+
marginTop: 12
|
|
1714
|
+
},
|
|
1715
|
+
children: [
|
|
1716
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1717
|
+
"button",
|
|
1718
|
+
{
|
|
1719
|
+
type: "button",
|
|
1720
|
+
style: cancelBtn,
|
|
1721
|
+
onClick: () => {
|
|
1722
|
+
setShowDelete(false);
|
|
1723
|
+
setDeleteText("");
|
|
1724
|
+
},
|
|
1725
|
+
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "var(--dauth-surface-hover, #232340)",
|
|
1726
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "transparent",
|
|
1727
|
+
children: "Cancel"
|
|
1728
|
+
}
|
|
1729
|
+
),
|
|
1730
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1731
|
+
"button",
|
|
1732
|
+
{
|
|
1733
|
+
type: "button",
|
|
1734
|
+
style: {
|
|
1735
|
+
...deleteConfirmBtn,
|
|
1736
|
+
opacity: deleteText !== CONFIRM_WORD || deleting ? 0.5 : 1,
|
|
1737
|
+
cursor: deleteText !== CONFIRM_WORD || deleting ? "not-allowed" : "pointer"
|
|
1738
|
+
},
|
|
1739
|
+
disabled: deleteText !== CONFIRM_WORD || deleting,
|
|
1740
|
+
onClick: handleDelete,
|
|
1741
|
+
children: [
|
|
1742
|
+
deleting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}),
|
|
1743
|
+
"Delete my account"
|
|
1744
|
+
]
|
|
1745
|
+
}
|
|
1746
|
+
)
|
|
1747
|
+
]
|
|
1748
|
+
}
|
|
1749
|
+
)
|
|
1750
|
+
] })
|
|
1751
|
+
] }),
|
|
1752
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { style: separator }),
|
|
1753
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1754
|
+
"button",
|
|
1755
|
+
{
|
|
1756
|
+
type: "button",
|
|
1757
|
+
style: signOutBtn,
|
|
1758
|
+
onClick: handleSignOut,
|
|
1759
|
+
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "rgba(239, 68, 68, 0.2)",
|
|
1760
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "var(--dauth-error-bg, rgba(239, 68, 68, 0.1))",
|
|
1761
|
+
children: [
|
|
1762
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconLogOut, {}),
|
|
1763
|
+
"Sign out"
|
|
1764
|
+
]
|
|
1765
|
+
}
|
|
1766
|
+
)
|
|
830
1767
|
] })
|
|
831
1768
|
] }),
|
|
832
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: footerStyle(isDesktop), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1769
|
+
activeTab === "profile" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: footerStyle(isDesktop), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
833
1770
|
"button",
|
|
834
1771
|
{
|
|
835
1772
|
type: "button",
|
|
@@ -867,8 +1804,7 @@ var headerStyle = (isDesktop) => ({
|
|
|
867
1804
|
display: "flex",
|
|
868
1805
|
alignItems: "center",
|
|
869
1806
|
justifyContent: "space-between",
|
|
870
|
-
padding: "16px 24px",
|
|
871
|
-
borderBottom: "1px solid var(--dauth-border, rgba(255, 255, 255, 0.08))",
|
|
1807
|
+
padding: "16px 24px 0",
|
|
872
1808
|
flexShrink: 0,
|
|
873
1809
|
...!isDesktop ? {
|
|
874
1810
|
position: "sticky",
|
|
@@ -899,6 +1835,25 @@ var closeBtn = {
|
|
|
899
1835
|
transition: "background-color 150ms, color 150ms",
|
|
900
1836
|
padding: 0
|
|
901
1837
|
};
|
|
1838
|
+
var tabBar = {
|
|
1839
|
+
display: "flex",
|
|
1840
|
+
padding: "0 24px",
|
|
1841
|
+
borderBottom: "1px solid var(--dauth-border, rgba(255, 255, 255, 0.08))",
|
|
1842
|
+
flexShrink: 0
|
|
1843
|
+
};
|
|
1844
|
+
var tabBtn = {
|
|
1845
|
+
flex: 1,
|
|
1846
|
+
padding: "12px 4px",
|
|
1847
|
+
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
1848
|
+
fontWeight: 500,
|
|
1849
|
+
border: "none",
|
|
1850
|
+
borderBottom: "2px solid transparent",
|
|
1851
|
+
backgroundColor: "transparent",
|
|
1852
|
+
cursor: "pointer",
|
|
1853
|
+
transition: "color 150ms, border-color 150ms",
|
|
1854
|
+
fontFamily: "inherit",
|
|
1855
|
+
textAlign: "center"
|
|
1856
|
+
};
|
|
902
1857
|
var bodyStyle = {
|
|
903
1858
|
flex: 1,
|
|
904
1859
|
overflowY: "auto",
|
|
@@ -925,6 +1880,18 @@ var avatarCircle = {
|
|
|
925
1880
|
fontSize: "1.5rem",
|
|
926
1881
|
fontWeight: 600
|
|
927
1882
|
};
|
|
1883
|
+
var avatarOverlay = {
|
|
1884
|
+
position: "absolute",
|
|
1885
|
+
inset: 0,
|
|
1886
|
+
backgroundColor: "rgba(0, 0, 0, 0.4)",
|
|
1887
|
+
display: "flex",
|
|
1888
|
+
alignItems: "center",
|
|
1889
|
+
justifyContent: "center",
|
|
1890
|
+
borderRadius: "50%",
|
|
1891
|
+
opacity: 0.7,
|
|
1892
|
+
transition: "opacity 150ms",
|
|
1893
|
+
color: "#ffffff"
|
|
1894
|
+
};
|
|
928
1895
|
var emailText = {
|
|
929
1896
|
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
930
1897
|
color: "var(--dauth-text-secondary, #a1a1aa)"
|
|
@@ -939,7 +1906,9 @@ var statusMsg = (type) => ({
|
|
|
939
1906
|
textAlign: "center",
|
|
940
1907
|
lineHeight: 1.5
|
|
941
1908
|
});
|
|
942
|
-
var fieldGroup = {
|
|
1909
|
+
var fieldGroup = {
|
|
1910
|
+
marginBottom: 16
|
|
1911
|
+
};
|
|
943
1912
|
var label = {
|
|
944
1913
|
display: "block",
|
|
945
1914
|
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
@@ -969,12 +1938,88 @@ var inputBlurHandler = (e) => {
|
|
|
969
1938
|
e.currentTarget.style.borderColor = "var(--dauth-border, rgba(255, 255, 255, 0.08))";
|
|
970
1939
|
e.currentTarget.style.boxShadow = "none";
|
|
971
1940
|
};
|
|
1941
|
+
var langBtn = {
|
|
1942
|
+
padding: "8px 16px",
|
|
1943
|
+
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
1944
|
+
fontWeight: 500,
|
|
1945
|
+
border: "none",
|
|
1946
|
+
borderRadius: "var(--dauth-radius-sm, 8px)",
|
|
1947
|
+
cursor: "pointer",
|
|
1948
|
+
transition: "background-color 150ms, color 150ms",
|
|
1949
|
+
fontFamily: "inherit"
|
|
1950
|
+
};
|
|
972
1951
|
var separator = {
|
|
973
1952
|
height: 1,
|
|
974
1953
|
backgroundColor: "var(--dauth-border, rgba(255, 255, 255, 0.08))",
|
|
975
1954
|
margin: "24px 0",
|
|
976
1955
|
border: "none"
|
|
977
1956
|
};
|
|
1957
|
+
var outlineBtn = {
|
|
1958
|
+
padding: "6px 12px",
|
|
1959
|
+
fontSize: "var(--dauth-font-size-xs, 0.75rem)",
|
|
1960
|
+
fontWeight: 500,
|
|
1961
|
+
color: "var(--dauth-text-secondary, #a1a1aa)",
|
|
1962
|
+
backgroundColor: "transparent",
|
|
1963
|
+
border: "1px solid var(--dauth-border, rgba(255, 255, 255, 0.08))",
|
|
1964
|
+
borderRadius: "var(--dauth-radius-sm, 8px)",
|
|
1965
|
+
cursor: "pointer",
|
|
1966
|
+
transition: "background-color 150ms",
|
|
1967
|
+
fontFamily: "inherit"
|
|
1968
|
+
};
|
|
1969
|
+
var registerPanel = {
|
|
1970
|
+
padding: 16,
|
|
1971
|
+
borderRadius: "var(--dauth-radius-sm, 8px)",
|
|
1972
|
+
border: "1px solid var(--dauth-border, rgba(255, 255, 255, 0.08))",
|
|
1973
|
+
backgroundColor: "var(--dauth-surface-secondary, rgba(255, 255, 255, 0.04))",
|
|
1974
|
+
marginBottom: 12
|
|
1975
|
+
};
|
|
1976
|
+
var smallAccentBtn = {
|
|
1977
|
+
padding: "8px 16px",
|
|
1978
|
+
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
1979
|
+
fontWeight: 500,
|
|
1980
|
+
color: "#ffffff",
|
|
1981
|
+
backgroundColor: "var(--dauth-accent, #6366f1)",
|
|
1982
|
+
border: "none",
|
|
1983
|
+
borderRadius: "var(--dauth-radius-sm, 8px)",
|
|
1984
|
+
cursor: "pointer",
|
|
1985
|
+
transition: "opacity 150ms",
|
|
1986
|
+
fontFamily: "inherit",
|
|
1987
|
+
display: "flex",
|
|
1988
|
+
alignItems: "center",
|
|
1989
|
+
gap: 6
|
|
1990
|
+
};
|
|
1991
|
+
var credentialRow = {
|
|
1992
|
+
display: "flex",
|
|
1993
|
+
alignItems: "center",
|
|
1994
|
+
justifyContent: "space-between",
|
|
1995
|
+
padding: 12,
|
|
1996
|
+
borderRadius: "var(--dauth-radius-sm, 8px)",
|
|
1997
|
+
backgroundColor: "var(--dauth-surface-secondary, rgba(255, 255, 255, 0.04))",
|
|
1998
|
+
marginBottom: 8
|
|
1999
|
+
};
|
|
2000
|
+
var trashBtn = {
|
|
2001
|
+
display: "flex",
|
|
2002
|
+
alignItems: "center",
|
|
2003
|
+
justifyContent: "center",
|
|
2004
|
+
width: 28,
|
|
2005
|
+
height: 28,
|
|
2006
|
+
border: "none",
|
|
2007
|
+
backgroundColor: "transparent",
|
|
2008
|
+
color: "var(--dauth-text-muted, #71717a)",
|
|
2009
|
+
cursor: "pointer",
|
|
2010
|
+
transition: "color 150ms",
|
|
2011
|
+
padding: 0,
|
|
2012
|
+
borderRadius: "var(--dauth-radius-sm, 8px)",
|
|
2013
|
+
flexShrink: 0
|
|
2014
|
+
};
|
|
2015
|
+
var emptyState = {
|
|
2016
|
+
display: "flex",
|
|
2017
|
+
alignItems: "center",
|
|
2018
|
+
gap: 12,
|
|
2019
|
+
padding: 16,
|
|
2020
|
+
borderRadius: "var(--dauth-radius-sm, 8px)",
|
|
2021
|
+
backgroundColor: "var(--dauth-surface-secondary, rgba(255, 255, 255, 0.04))"
|
|
2022
|
+
};
|
|
978
2023
|
var dangerTitle = {
|
|
979
2024
|
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
980
2025
|
fontWeight: 600,
|
|
@@ -1013,7 +2058,6 @@ var deletePanelText = {
|
|
|
1013
2058
|
lineHeight: 1.5
|
|
1014
2059
|
};
|
|
1015
2060
|
var cancelBtn = {
|
|
1016
|
-
flex: 1,
|
|
1017
2061
|
padding: "8px 16px",
|
|
1018
2062
|
fontSize: "var(--dauth-font-size-sm, 0.875rem)",
|
|
1019
2063
|
fontWeight: 500,
|
|
@@ -1042,6 +2086,23 @@ var deleteConfirmBtn = {
|
|
|
1042
2086
|
justifyContent: "center",
|
|
1043
2087
|
gap: 8
|
|
1044
2088
|
};
|
|
2089
|
+
var signOutBtn = {
|
|
2090
|
+
width: "100%",
|
|
2091
|
+
padding: "12px 24px",
|
|
2092
|
+
fontSize: "var(--dauth-font-size-base, 1rem)",
|
|
2093
|
+
fontWeight: 500,
|
|
2094
|
+
color: "var(--dauth-error, #ef4444)",
|
|
2095
|
+
backgroundColor: "var(--dauth-error-bg, rgba(239, 68, 68, 0.1))",
|
|
2096
|
+
border: "1px solid rgba(239, 68, 68, 0.2)",
|
|
2097
|
+
borderRadius: "var(--dauth-radius-sm, 8px)",
|
|
2098
|
+
cursor: "pointer",
|
|
2099
|
+
transition: "background-color 150ms",
|
|
2100
|
+
fontFamily: "inherit",
|
|
2101
|
+
display: "flex",
|
|
2102
|
+
alignItems: "center",
|
|
2103
|
+
justifyContent: "center",
|
|
2104
|
+
gap: 8
|
|
2105
|
+
};
|
|
1045
2106
|
var footerStyle = (isDesktop) => ({
|
|
1046
2107
|
padding: "16px 24px",
|
|
1047
2108
|
borderTop: "1px solid var(--dauth-border, rgba(255, 255, 255, 0.08))",
|
|
@@ -1153,15 +2214,39 @@ var DauthProvider = (props) => {
|
|
|
1153
2214
|
() => deleteAccountAction(ctx),
|
|
1154
2215
|
[ctx]
|
|
1155
2216
|
);
|
|
2217
|
+
const getPasskeyCredentials = (0, import_react2.useCallback)(
|
|
2218
|
+
() => getPasskeyCredentialsAction(ctx),
|
|
2219
|
+
[ctx]
|
|
2220
|
+
);
|
|
2221
|
+
const registerPasskey = (0, import_react2.useCallback)(
|
|
2222
|
+
(name) => registerPasskeyAction(ctx, name),
|
|
2223
|
+
[ctx]
|
|
2224
|
+
);
|
|
2225
|
+
const deletePasskeyCredential = (0, import_react2.useCallback)(
|
|
2226
|
+
(credentialId) => deletePasskeyCredentialAction(ctx, credentialId),
|
|
2227
|
+
[ctx]
|
|
2228
|
+
);
|
|
1156
2229
|
const memoProvider = (0, import_react2.useMemo)(
|
|
1157
2230
|
() => ({
|
|
1158
2231
|
...dauthState,
|
|
1159
2232
|
loginWithRedirect,
|
|
1160
2233
|
logout,
|
|
1161
2234
|
updateUser,
|
|
1162
|
-
deleteAccount
|
|
2235
|
+
deleteAccount,
|
|
2236
|
+
getPasskeyCredentials,
|
|
2237
|
+
registerPasskey,
|
|
2238
|
+
deletePasskeyCredential
|
|
1163
2239
|
}),
|
|
1164
|
-
[
|
|
2240
|
+
[
|
|
2241
|
+
dauthState,
|
|
2242
|
+
loginWithRedirect,
|
|
2243
|
+
logout,
|
|
2244
|
+
updateUser,
|
|
2245
|
+
deleteAccount,
|
|
2246
|
+
getPasskeyCredentials,
|
|
2247
|
+
registerPasskey,
|
|
2248
|
+
deletePasskeyCredential
|
|
2249
|
+
]
|
|
1165
2250
|
);
|
|
1166
2251
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DauthContext.Provider, { value: memoProvider, children });
|
|
1167
2252
|
};
|