@ciromaciel/auth-react 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -1
- package/dist/index.esm.js +1140 -660
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1137 -657
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.esm.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
|
-
import { useState, useCallback, useEffect, useMemo, createContext, useRef } from 'react';
|
|
2
|
+
import { useState, useCallback, useEffect, useMemo, createContext, useRef, useId } from 'react';
|
|
3
3
|
import { useShallow } from 'zustand/react/shallow';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import { Navigate, Outlet, useNavigate } from 'react-router-dom';
|
|
6
|
-
import { Modal, Stack, Text, Group, Image, Title, Paper, Anchor, NavLink, ActionIcon, Loader, Avatar, Button, Divider, TextInput, Alert, Center, Box,
|
|
6
|
+
import { Modal, Stack, Text, Group, Image, Title, Paper, Anchor, NavLink, ActionIcon, Loader, Avatar, Button, Divider, TextInput, Alert, Center, Box, UnstyledButton, FileButton } from '@mantine/core';
|
|
7
7
|
import { useForm } from '@mantine/form';
|
|
8
|
-
import { IconX, IconArrowRight, IconBrandGoogle, IconArrowLeft, IconRefresh, IconAlertCircle,
|
|
8
|
+
import { IconX, IconArrowRight, IconBrandGoogle, IconArrowLeft, IconRefresh, IconAlertCircle, IconDeviceTablet, IconDeviceMobile, IconDeviceLaptop, IconDeviceDesktop, IconMail, IconBrandGithub, IconLink, IconChevronDown, IconLogout, IconBuilding, IconUser, IconCreditCard } from '@tabler/icons-react';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The accounts that already signed in on this browser.
|
|
@@ -3235,47 +3235,579 @@ function SignIn({
|
|
|
3235
3235
|
});
|
|
3236
3236
|
}
|
|
3237
3237
|
|
|
3238
|
+
/*
|
|
3239
|
+
* How a session reads on the account screen: which device, since when, and
|
|
3240
|
+
* how many there are.
|
|
3241
|
+
*
|
|
3242
|
+
* Pure functions, kept out of the component so they can be tested against real
|
|
3243
|
+
* user agents. The order of the checks is the point of this file:
|
|
3244
|
+
* - iPhone and iPad announce themselves as "like Mac OS X", so they must be
|
|
3245
|
+
* recognised BEFORE macOS — the old parser showed every phone as a Mac;
|
|
3246
|
+
* - Edge, Opera and Samsung Internet all carry "Chrome" in the string, and
|
|
3247
|
+
* Chrome on iOS carries "Safari", so each is checked before the engine it
|
|
3248
|
+
* imitates — the old parser never reached its Opera branch.
|
|
3249
|
+
*/
|
|
3250
|
+
|
|
3251
|
+
const BROWSERS = [['Edge', /\bEdg(?:e|A|iOS)?\//], ['Opera', /\b(?:OPR|Opera|OPT)\//], ['Samsung Internet', /\bSamsungBrowser\//], ['Firefox', /\b(?:Firefox|FxiOS)\//], ['Chrome', /\b(?:Chrome|CriOS)\//], ['Safari', /\bSafari\//]];
|
|
3252
|
+
|
|
3253
|
+
/**
|
|
3254
|
+
* @param {string} [userAgent]
|
|
3255
|
+
* @returns {{ browser: string|null, os: string|null, kind: 'desktop'|'phone'|'tablet' }}
|
|
3256
|
+
*/
|
|
3257
|
+
function describeDevice(userAgent) {
|
|
3258
|
+
const ua = userAgent || '';
|
|
3259
|
+
const browser = BROWSERS.find(([, pattern]) => pattern.test(ua))?.[0] || null;
|
|
3260
|
+
if (/\biPad\b/.test(ua)) return {
|
|
3261
|
+
browser,
|
|
3262
|
+
os: 'iPadOS',
|
|
3263
|
+
kind: 'tablet'
|
|
3264
|
+
};
|
|
3265
|
+
if (/\biPhone\b|\biPod\b/.test(ua)) return {
|
|
3266
|
+
browser,
|
|
3267
|
+
os: 'iOS',
|
|
3268
|
+
kind: 'phone'
|
|
3269
|
+
};
|
|
3270
|
+
if (/\bAndroid\b/.test(ua)) return {
|
|
3271
|
+
browser,
|
|
3272
|
+
os: 'Android',
|
|
3273
|
+
kind: /\bMobile\b/.test(ua) ? 'phone' : 'tablet'
|
|
3274
|
+
};
|
|
3275
|
+
if (/\bWindows\b/.test(ua)) return {
|
|
3276
|
+
browser,
|
|
3277
|
+
os: 'Windows',
|
|
3278
|
+
kind: 'desktop'
|
|
3279
|
+
};
|
|
3280
|
+
if (/\bCrOS\b/.test(ua)) return {
|
|
3281
|
+
browser,
|
|
3282
|
+
os: 'ChromeOS',
|
|
3283
|
+
kind: 'desktop'
|
|
3284
|
+
};
|
|
3285
|
+
if (/\bMac OS X\b|\bMacintosh\b/.test(ua)) return {
|
|
3286
|
+
browser,
|
|
3287
|
+
os: 'macOS',
|
|
3288
|
+
kind: 'desktop'
|
|
3289
|
+
};
|
|
3290
|
+
if (/\bLinux\b/.test(ua)) return {
|
|
3291
|
+
browser,
|
|
3292
|
+
os: 'Linux',
|
|
3293
|
+
kind: 'desktop'
|
|
3294
|
+
};
|
|
3295
|
+
return {
|
|
3296
|
+
browser,
|
|
3297
|
+
os: null,
|
|
3298
|
+
kind: 'desktop'
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
/** "Chrome no macOS", "Safari", "iOS", or "Aparelho desconhecido". */
|
|
3303
|
+
function deviceLabel({
|
|
3304
|
+
browser,
|
|
3305
|
+
os
|
|
3306
|
+
}) {
|
|
3307
|
+
if (browser && os) return `${browser} no ${os}`;
|
|
3308
|
+
return browser || os || 'Aparelho desconhecido';
|
|
3309
|
+
}
|
|
3310
|
+
const pad = n => String(n).padStart(2, '0');
|
|
3311
|
+
|
|
3312
|
+
/**
|
|
3313
|
+
* "hoje, 09:12", "ontem, 21:40", "23/09, 14:05" or "23/09/2025, 14:05".
|
|
3314
|
+
*
|
|
3315
|
+
* Calendar days in the viewer's own time zone: a session opened at 23:50 is
|
|
3316
|
+
* "ontem" ten minutes later, which is what the person remembers.
|
|
3317
|
+
*/
|
|
3318
|
+
function formatSessionStart(value, now = new Date()) {
|
|
3319
|
+
const date = new Date(value);
|
|
3320
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
3321
|
+
const time = `${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
3322
|
+
const startOfDay = d => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
3323
|
+
const days = Math.round((startOfDay(now) - startOfDay(date)) / 86_400_000);
|
|
3324
|
+
if (days === 0) return `hoje, ${time}`;
|
|
3325
|
+
if (days === 1) return `ontem, ${time}`;
|
|
3326
|
+
const day = `${pad(date.getDate())}/${pad(date.getMonth() + 1)}`;
|
|
3327
|
+
return date.getFullYear() === now.getFullYear() ? `${day}, ${time}` : `${day}/${date.getFullYear()}, ${time}`;
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
/** "1 sessão aberta", "3 sessões abertas". */
|
|
3331
|
+
function countSessions(count) {
|
|
3332
|
+
return count === 1 ? '1 sessão aberta' : `${count} sessões abertas`;
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3335
|
+
/**
|
|
3336
|
+
* The current session first, then the others from the newest.
|
|
3337
|
+
*
|
|
3338
|
+
* The API orders by creation only, so the device the person is holding could
|
|
3339
|
+
* land anywhere in the list; it is the one they look for first.
|
|
3340
|
+
*/
|
|
3341
|
+
function orderSessions(sessions, currentId) {
|
|
3342
|
+
return [...(sessions || [])].sort((a, b) => {
|
|
3343
|
+
if (a.id === currentId) return -1;
|
|
3344
|
+
if (b.id === currentId) return 1;
|
|
3345
|
+
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
3346
|
+
});
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
/*
|
|
3350
|
+
* Who the signed-in person is, as the account card and the account screen
|
|
3351
|
+
* show it. One rule for both, so the two never disagree about the same person.
|
|
3352
|
+
*
|
|
3353
|
+
* A name equal to the email's local part is not a name: it is what an account
|
|
3354
|
+
* born from a code gets by default, and showing it on top of the email repeats
|
|
3355
|
+
* the same fact. It counts as "no name", and the email becomes the title.
|
|
3356
|
+
*/
|
|
3357
|
+
function describeUser(user) {
|
|
3358
|
+
const name = (user?.fullName || user?.name || '').trim();
|
|
3359
|
+
const email = (user?.primaryEmailAddress || user?.email || '').trim();
|
|
3360
|
+
const hasRealName = Boolean(name) && name.toLowerCase() !== email.split('@')[0].toLowerCase() && name.toLowerCase() !== email.toLowerCase();
|
|
3361
|
+
const title = hasRealName ? name : email || name;
|
|
3362
|
+
const initials = hasRealName ? name.split(/\s+/).map(part => part[0]).join('').slice(0, 2).toUpperCase() : (email || name).charAt(0).toUpperCase();
|
|
3363
|
+
return {
|
|
3364
|
+
name,
|
|
3365
|
+
email,
|
|
3366
|
+
hasRealName,
|
|
3367
|
+
title,
|
|
3368
|
+
initials,
|
|
3369
|
+
image: user?.imageUrl || user?.image || null
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
const LABELS = {
|
|
3374
|
+
title: 'Conta',
|
|
3375
|
+
subtitle: 'Quem você é, como você entra e onde a sua sessão está aberta.',
|
|
3376
|
+
close: 'Fechar',
|
|
3377
|
+
profileSection: 'Perfil',
|
|
3378
|
+
avatar: 'Foto',
|
|
3379
|
+
name: 'Nome',
|
|
3380
|
+
email: 'E-mail',
|
|
3381
|
+
edit: 'Alterar',
|
|
3382
|
+
save: 'Salvar',
|
|
3383
|
+
cancel: 'Cancelar',
|
|
3384
|
+
remove: 'Remover',
|
|
3385
|
+
notDefined: 'Não definido',
|
|
3386
|
+
namePlaceholder: 'Seu nome',
|
|
3387
|
+
nameHint: 'Aparece no cartão da conta e para quem divide uma organização com você. Enter salva, Esc cancela.',
|
|
3388
|
+
nameRequired: 'Digite um nome.',
|
|
3389
|
+
emailHint: 'É para onde vai o código de acesso, por isso não muda por aqui.',
|
|
3390
|
+
avatarPrompt: 'Arraste uma imagem ou clique para escolher',
|
|
3391
|
+
avatarHint: 'JPG, PNG, GIF ou WebP, até {size}. Ela aparece em todos os painéis.',
|
|
3392
|
+
avatarInvalidType: 'Escolha uma imagem: JPG, PNG, GIF ou WebP.',
|
|
3393
|
+
avatarTooLarge: 'Imagem grande demais. O máximo é {size}.',
|
|
3394
|
+
signInSection: 'Formas de entrar',
|
|
3395
|
+
codeMethod: 'Código',
|
|
3396
|
+
codeByEmail: 'Por e-mail',
|
|
3397
|
+
alwaysOn: 'Sempre ativo',
|
|
3398
|
+
notConnected: 'Não conectado',
|
|
3399
|
+
connect: 'Conectar',
|
|
3400
|
+
disconnect: 'Desconectar',
|
|
3401
|
+
sessionsSection: 'Sessões',
|
|
3402
|
+
devices: 'Aparelhos',
|
|
3403
|
+
showSessions: 'Ver sessões',
|
|
3404
|
+
hideSessions: 'Ocultar',
|
|
3405
|
+
thisDevice: 'Este aparelho',
|
|
3406
|
+
end: 'Encerrar',
|
|
3407
|
+
since: 'desde',
|
|
3408
|
+
unknownIP: 'IP desconhecido',
|
|
3409
|
+
loadingSessions: 'Carregando as sessões…',
|
|
3410
|
+
noSessionsFound: 'Nenhuma sessão encontrada.',
|
|
3411
|
+
confirmEndBody: 'Quem estiver nesses aparelhos volta para a tela de entrar. Este aparelho continua conectado.',
|
|
3412
|
+
genericFailure: 'Não foi possível concluir. Tente de novo.'
|
|
3413
|
+
};
|
|
3414
|
+
const PROVIDER_MARKS = {
|
|
3415
|
+
google: IconBrandGoogle,
|
|
3416
|
+
github: IconBrandGithub
|
|
3417
|
+
};
|
|
3418
|
+
const DEVICE_MARKS = {
|
|
3419
|
+
desktop: IconDeviceLaptop,
|
|
3420
|
+
phone: IconDeviceMobile,
|
|
3421
|
+
tablet: IconDeviceTablet
|
|
3422
|
+
};
|
|
3423
|
+
const color = token => token === 'transparent' ? 'transparent' : `var(--mantine-color-${token.replace('.', '-')})`;
|
|
3424
|
+
|
|
3425
|
+
/*
|
|
3426
|
+
* The action buttons, as data. Hover and keyboard focus share the same look,
|
|
3427
|
+
* like the account card's rows. A destructive action keeps its red text on
|
|
3428
|
+
* hover and only gains a light red ground: turning the text black while the
|
|
3429
|
+
* border turned red read as a different button.
|
|
3430
|
+
*/
|
|
3431
|
+
const TONES = {
|
|
3432
|
+
default: {
|
|
3433
|
+
rest: {
|
|
3434
|
+
text: 'gray.9',
|
|
3435
|
+
border: 'gray.3',
|
|
3436
|
+
ground: 'transparent'
|
|
3437
|
+
},
|
|
3438
|
+
active: {
|
|
3439
|
+
border: 'gray.9'
|
|
3440
|
+
}
|
|
3441
|
+
},
|
|
3442
|
+
dark: {
|
|
3443
|
+
rest: {
|
|
3444
|
+
text: 'white',
|
|
3445
|
+
border: 'gray.9',
|
|
3446
|
+
ground: 'gray.9'
|
|
3447
|
+
},
|
|
3448
|
+
active: {
|
|
3449
|
+
border: 'gray.7',
|
|
3450
|
+
ground: 'gray.7'
|
|
3451
|
+
}
|
|
3452
|
+
},
|
|
3453
|
+
quiet: {
|
|
3454
|
+
rest: {
|
|
3455
|
+
text: 'gray.6',
|
|
3456
|
+
border: 'transparent',
|
|
3457
|
+
ground: 'transparent'
|
|
3458
|
+
},
|
|
3459
|
+
active: {
|
|
3460
|
+
text: 'gray.9',
|
|
3461
|
+
border: 'gray.3'
|
|
3462
|
+
}
|
|
3463
|
+
},
|
|
3464
|
+
danger: {
|
|
3465
|
+
rest: {
|
|
3466
|
+
text: 'red.8',
|
|
3467
|
+
border: 'transparent',
|
|
3468
|
+
ground: 'transparent'
|
|
3469
|
+
},
|
|
3470
|
+
active: {
|
|
3471
|
+
ground: 'red.0',
|
|
3472
|
+
border: 'red.2'
|
|
3473
|
+
}
|
|
3474
|
+
},
|
|
3475
|
+
dangerOutline: {
|
|
3476
|
+
rest: {
|
|
3477
|
+
text: 'red.8',
|
|
3478
|
+
border: 'gray.3',
|
|
3479
|
+
ground: 'transparent'
|
|
3480
|
+
},
|
|
3481
|
+
active: {
|
|
3482
|
+
ground: 'red.0',
|
|
3483
|
+
border: 'red.2'
|
|
3484
|
+
}
|
|
3485
|
+
},
|
|
3486
|
+
dangerFill: {
|
|
3487
|
+
rest: {
|
|
3488
|
+
text: 'white',
|
|
3489
|
+
border: 'red.8',
|
|
3490
|
+
ground: 'red.8'
|
|
3491
|
+
},
|
|
3492
|
+
active: {
|
|
3493
|
+
border: 'red.9',
|
|
3494
|
+
ground: 'red.9'
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
};
|
|
3498
|
+
function ActionButton({
|
|
3499
|
+
tone = 'default',
|
|
3500
|
+
loading = false,
|
|
3501
|
+
disabled = false,
|
|
3502
|
+
children,
|
|
3503
|
+
style,
|
|
3504
|
+
...others
|
|
3505
|
+
}) {
|
|
3506
|
+
const [isActive, setIsActive] = useState(false);
|
|
3507
|
+
const isBlocked = disabled || loading;
|
|
3508
|
+
const look = {
|
|
3509
|
+
...TONES[tone].rest,
|
|
3510
|
+
...(isActive && !isBlocked ? TONES[tone].active : {})
|
|
3511
|
+
};
|
|
3512
|
+
return /*#__PURE__*/jsxs(UnstyledButton, {
|
|
3513
|
+
disabled: isBlocked,
|
|
3514
|
+
onMouseEnter: () => setIsActive(true),
|
|
3515
|
+
onMouseLeave: () => setIsActive(false),
|
|
3516
|
+
onFocus: () => setIsActive(true),
|
|
3517
|
+
onBlur: () => setIsActive(false),
|
|
3518
|
+
style: {
|
|
3519
|
+
display: 'inline-flex',
|
|
3520
|
+
alignItems: 'center',
|
|
3521
|
+
justifyContent: 'center',
|
|
3522
|
+
gap: 6,
|
|
3523
|
+
padding: '5px 12px',
|
|
3524
|
+
fontSize: 12,
|
|
3525
|
+
fontWeight: 700,
|
|
3526
|
+
lineHeight: 1.5,
|
|
3527
|
+
whiteSpace: 'nowrap',
|
|
3528
|
+
borderRadius: 0,
|
|
3529
|
+
color: color(look.text),
|
|
3530
|
+
background: color(look.ground),
|
|
3531
|
+
border: `1px solid ${color(look.border)}`,
|
|
3532
|
+
opacity: disabled ? 0.4 : 1,
|
|
3533
|
+
cursor: isBlocked ? 'default' : 'pointer',
|
|
3534
|
+
...style
|
|
3535
|
+
},
|
|
3536
|
+
...others,
|
|
3537
|
+
children: [loading && /*#__PURE__*/jsx(Loader, {
|
|
3538
|
+
size: 10,
|
|
3539
|
+
color: "currentColor"
|
|
3540
|
+
}), children]
|
|
3541
|
+
});
|
|
3542
|
+
}
|
|
3543
|
+
function SectionLabel({
|
|
3544
|
+
children,
|
|
3545
|
+
note
|
|
3546
|
+
}) {
|
|
3547
|
+
return /*#__PURE__*/jsxs(Group, {
|
|
3548
|
+
justify: "space-between",
|
|
3549
|
+
align: "baseline",
|
|
3550
|
+
gap: 8,
|
|
3551
|
+
mb: 4,
|
|
3552
|
+
children: [/*#__PURE__*/jsx(Text, {
|
|
3553
|
+
fz: 11,
|
|
3554
|
+
fw: 800,
|
|
3555
|
+
tt: "uppercase",
|
|
3556
|
+
lts: "1.5px",
|
|
3557
|
+
c: "gray.4",
|
|
3558
|
+
children: children
|
|
3559
|
+
}), note && /*#__PURE__*/jsx(Text, {
|
|
3560
|
+
fz: 12,
|
|
3561
|
+
fw: 500,
|
|
3562
|
+
c: "gray.5",
|
|
3563
|
+
children: note
|
|
3564
|
+
})]
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
function Section({
|
|
3568
|
+
label,
|
|
3569
|
+
note,
|
|
3570
|
+
isFirst,
|
|
3571
|
+
children
|
|
3572
|
+
}) {
|
|
3573
|
+
return /*#__PURE__*/jsxs(Box, {
|
|
3574
|
+
px: 24,
|
|
3575
|
+
pt: 16,
|
|
3576
|
+
pb: 8,
|
|
3577
|
+
style: isFirst ? undefined : {
|
|
3578
|
+
borderTop: '1px solid var(--mantine-color-gray-2)'
|
|
3579
|
+
},
|
|
3580
|
+
children: [/*#__PURE__*/jsx(SectionLabel, {
|
|
3581
|
+
note: note,
|
|
3582
|
+
children: label
|
|
3583
|
+
}), children]
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
const rowDivider = {
|
|
3587
|
+
borderTop: '1px solid var(--mantine-color-gray-2)'
|
|
3588
|
+
};
|
|
3589
|
+
|
|
3590
|
+
/** A label, a value and an optional action, on the same 88px label column. */
|
|
3591
|
+
function Row$1({
|
|
3592
|
+
label,
|
|
3593
|
+
children,
|
|
3594
|
+
action,
|
|
3595
|
+
isFirst
|
|
3596
|
+
}) {
|
|
3597
|
+
return /*#__PURE__*/jsxs(Box, {
|
|
3598
|
+
py: 10,
|
|
3599
|
+
mih: 52,
|
|
3600
|
+
style: {
|
|
3601
|
+
display: 'grid',
|
|
3602
|
+
gridTemplateColumns: label ? '88px 1fr auto' : '1fr auto',
|
|
3603
|
+
alignItems: 'center',
|
|
3604
|
+
gap: 12,
|
|
3605
|
+
...(isFirst ? {} : rowDivider)
|
|
3606
|
+
},
|
|
3607
|
+
children: [label && /*#__PURE__*/jsx(Text, {
|
|
3608
|
+
fz: 12,
|
|
3609
|
+
fw: 500,
|
|
3610
|
+
c: "gray.5",
|
|
3611
|
+
children: label
|
|
3612
|
+
}), /*#__PURE__*/jsx(Box, {
|
|
3613
|
+
fz: 13,
|
|
3614
|
+
fw: 500,
|
|
3615
|
+
c: "gray.9",
|
|
3616
|
+
style: {
|
|
3617
|
+
minWidth: 0,
|
|
3618
|
+
overflowWrap: 'anywhere'
|
|
3619
|
+
},
|
|
3620
|
+
children: children
|
|
3621
|
+
}), action || /*#__PURE__*/jsx("span", {})]
|
|
3622
|
+
});
|
|
3623
|
+
}
|
|
3624
|
+
function Hint({
|
|
3625
|
+
children,
|
|
3626
|
+
c = 'gray.5'
|
|
3627
|
+
}) {
|
|
3628
|
+
return /*#__PURE__*/jsx(Text, {
|
|
3629
|
+
fz: 12,
|
|
3630
|
+
fw: 500,
|
|
3631
|
+
c: c,
|
|
3632
|
+
mt: 2,
|
|
3633
|
+
children: children
|
|
3634
|
+
});
|
|
3635
|
+
}
|
|
3636
|
+
|
|
3637
|
+
/*
|
|
3638
|
+
* A failure is shown where it happened, on the screen itself. Five of the
|
|
3639
|
+
* seven panels passed no `onError`, and a rejected photo or a failed rename
|
|
3640
|
+
* vanished without a word.
|
|
3641
|
+
*/
|
|
3642
|
+
function FailureNote({
|
|
3643
|
+
failure,
|
|
3644
|
+
section
|
|
3645
|
+
}) {
|
|
3646
|
+
if (failure?.section !== section) return null;
|
|
3647
|
+
return /*#__PURE__*/jsx(Text, {
|
|
3648
|
+
role: "alert",
|
|
3649
|
+
fz: 12,
|
|
3650
|
+
fw: 600,
|
|
3651
|
+
c: "red.8",
|
|
3652
|
+
mt: 4,
|
|
3653
|
+
children: failure.message
|
|
3654
|
+
});
|
|
3655
|
+
}
|
|
3656
|
+
function Chip({
|
|
3657
|
+
children,
|
|
3658
|
+
tone = 'outline'
|
|
3659
|
+
}) {
|
|
3660
|
+
const looks = {
|
|
3661
|
+
outline: {
|
|
3662
|
+
c: 'gray.6',
|
|
3663
|
+
bg: 'transparent',
|
|
3664
|
+
border: 'gray.3'
|
|
3665
|
+
},
|
|
3666
|
+
dark: {
|
|
3667
|
+
c: 'white',
|
|
3668
|
+
bg: 'gray.9',
|
|
3669
|
+
border: 'gray.9'
|
|
3670
|
+
},
|
|
3671
|
+
good: {
|
|
3672
|
+
c: 'teal.9',
|
|
3673
|
+
bg: 'teal.0',
|
|
3674
|
+
border: 'teal.2'
|
|
3675
|
+
}
|
|
3676
|
+
};
|
|
3677
|
+
const look = looks[tone];
|
|
3678
|
+
return /*#__PURE__*/jsx(Text, {
|
|
3679
|
+
component: "span",
|
|
3680
|
+
fz: 10,
|
|
3681
|
+
fw: 800,
|
|
3682
|
+
tt: "uppercase",
|
|
3683
|
+
lts: "1.2px",
|
|
3684
|
+
lh: 1.6,
|
|
3685
|
+
px: 6,
|
|
3686
|
+
c: look.c,
|
|
3687
|
+
bg: look.bg,
|
|
3688
|
+
style: {
|
|
3689
|
+
border: `1px solid ${color(look.border)}`,
|
|
3690
|
+
whiteSpace: 'nowrap',
|
|
3691
|
+
display: 'inline-block'
|
|
3692
|
+
},
|
|
3693
|
+
children: children
|
|
3694
|
+
});
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
/** An icon and a label, side by side, with an optional line under the label. */
|
|
3698
|
+
function WithIcon({
|
|
3699
|
+
icon: Icon,
|
|
3700
|
+
children,
|
|
3701
|
+
detail
|
|
3702
|
+
}) {
|
|
3703
|
+
return /*#__PURE__*/jsxs(Group, {
|
|
3704
|
+
gap: 10,
|
|
3705
|
+
wrap: "nowrap",
|
|
3706
|
+
align: detail ? 'flex-start' : 'center',
|
|
3707
|
+
children: [/*#__PURE__*/jsx(Icon, {
|
|
3708
|
+
size: 18,
|
|
3709
|
+
stroke: 1.5,
|
|
3710
|
+
style: {
|
|
3711
|
+
flex: 'none',
|
|
3712
|
+
color: 'var(--mantine-color-gray-5)',
|
|
3713
|
+
marginTop: detail ? 1 : 0
|
|
3714
|
+
}
|
|
3715
|
+
}), /*#__PURE__*/jsxs(Box, {
|
|
3716
|
+
style: {
|
|
3717
|
+
minWidth: 0
|
|
3718
|
+
},
|
|
3719
|
+
children: [children, detail && /*#__PURE__*/jsx(Hint, {
|
|
3720
|
+
children: detail
|
|
3721
|
+
})]
|
|
3722
|
+
})]
|
|
3723
|
+
});
|
|
3724
|
+
}
|
|
3725
|
+
function SquareAvatar({
|
|
3726
|
+
src,
|
|
3727
|
+
initials,
|
|
3728
|
+
size
|
|
3729
|
+
}) {
|
|
3730
|
+
return /*#__PURE__*/jsx(Avatar, {
|
|
3731
|
+
src: src || null,
|
|
3732
|
+
alt: "",
|
|
3733
|
+
size: size,
|
|
3734
|
+
radius: 0,
|
|
3735
|
+
color: "gray.9",
|
|
3736
|
+
variant: "filled",
|
|
3737
|
+
styles: {
|
|
3738
|
+
root: {
|
|
3739
|
+
borderRadius: 0,
|
|
3740
|
+
flex: 'none'
|
|
3741
|
+
},
|
|
3742
|
+
placeholder: {
|
|
3743
|
+
fontSize: Math.round(size / 3),
|
|
3744
|
+
fontWeight: 800
|
|
3745
|
+
}
|
|
3746
|
+
},
|
|
3747
|
+
children: initials
|
|
3748
|
+
});
|
|
3749
|
+
}
|
|
3750
|
+
const formatSize = bytes => `${Math.round(bytes / 1024)} KB`;
|
|
3751
|
+
|
|
3752
|
+
/**
|
|
3753
|
+
* @param {object} props
|
|
3754
|
+
* @param {'modal'|'card'} [props.variant='modal']
|
|
3755
|
+
* @param {boolean} [props.opened] - Visibility (modal only)
|
|
3756
|
+
* @param {Function} [props.onClose] - Closing (modal only)
|
|
3757
|
+
* @param {Function} [props.onProfileUpdate] - Receives `{ name }` or `{ image }`
|
|
3758
|
+
* @param {Function} [props.onSessionRevoked] - Receives the ended session's id
|
|
3759
|
+
* @param {Function} [props.onOtherSessionsRevoked]
|
|
3760
|
+
* @param {Function} [props.onProviderUnlinked] - Receives the provider id
|
|
3761
|
+
* @param {Function} [props.onError] - Receives the Error and `{ section, isShownOnScreen: true }`:
|
|
3762
|
+
* the screen already shows the message, so a panel should not toast it again
|
|
3763
|
+
* @param {boolean} [props.showAvatar=true]
|
|
3764
|
+
* @param {boolean} [props.showName=true]
|
|
3765
|
+
* @param {boolean} [props.showEmail=true]
|
|
3766
|
+
* @param {boolean} [props.showSignInMethods=true] - Drawn only when the application enabled a provider
|
|
3767
|
+
* @param {boolean} [props.showSessions=true]
|
|
3768
|
+
* @param {Partial<typeof LABELS>} [props.labels] - To change a word; the defaults are already Portuguese
|
|
3769
|
+
* @param {string} [props.title]
|
|
3770
|
+
* @param {string} [props.subtitle]
|
|
3771
|
+
* @param {string|import('react').ReactNode} [props.logo] - Card variant only
|
|
3772
|
+
* @param {number} [props.logoHeight=28]
|
|
3773
|
+
* @param {number} [props.width=520]
|
|
3774
|
+
* @param {number} [props.maxAvatarSize=512000] - In bytes
|
|
3775
|
+
* @param {import('react').ReactNode} [props.customSections] - Rendered after the built-in sections
|
|
3776
|
+
*/
|
|
3238
3777
|
function UserProfile({
|
|
3239
|
-
// Variant
|
|
3240
3778
|
variant = 'modal',
|
|
3241
3779
|
opened,
|
|
3242
3780
|
onClose,
|
|
3243
|
-
// Callbacks
|
|
3244
3781
|
onProfileUpdate,
|
|
3245
3782
|
onSessionRevoked,
|
|
3246
3783
|
onOtherSessionsRevoked,
|
|
3784
|
+
onProviderUnlinked,
|
|
3247
3785
|
onError,
|
|
3248
|
-
// Features toggle
|
|
3249
3786
|
showAvatar = true,
|
|
3250
3787
|
showName = true,
|
|
3251
3788
|
showEmail = true,
|
|
3789
|
+
showSignInMethods = true,
|
|
3252
3790
|
showSessions = true,
|
|
3253
|
-
// Customização
|
|
3254
3791
|
labels = {},
|
|
3255
|
-
title
|
|
3256
|
-
subtitle
|
|
3792
|
+
title,
|
|
3793
|
+
subtitle,
|
|
3257
3794
|
logo,
|
|
3258
3795
|
logoHeight = 28,
|
|
3259
|
-
width =
|
|
3260
|
-
// Avatar config
|
|
3796
|
+
width = 520,
|
|
3261
3797
|
maxAvatarSize = 500 * 1024,
|
|
3262
|
-
// 500KB
|
|
3263
|
-
|
|
3264
|
-
// Custom sections (React nodes to render after built-in sections)
|
|
3265
3798
|
customSections,
|
|
3266
3799
|
...containerProps
|
|
3267
3800
|
}) {
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3801
|
+
const t = {
|
|
3802
|
+
...LABELS,
|
|
3803
|
+
...labels
|
|
3804
|
+
};
|
|
3805
|
+
const isVisible = variant === 'card' || Boolean(opened);
|
|
3272
3806
|
const {
|
|
3273
3807
|
user,
|
|
3274
3808
|
updateProfile,
|
|
3275
3809
|
loadingUpdateProfile
|
|
3276
3810
|
} = useUser();
|
|
3277
|
-
|
|
3278
|
-
// Hook para sessions
|
|
3279
3811
|
const {
|
|
3280
3812
|
currentSession,
|
|
3281
3813
|
sessions,
|
|
@@ -3286,698 +3818,650 @@ function UserProfile({
|
|
|
3286
3818
|
loadingListSessions,
|
|
3287
3819
|
loadingRevokeSession
|
|
3288
3820
|
} = useSessions();
|
|
3821
|
+
const identity = describeUser(user);
|
|
3289
3822
|
|
|
3290
|
-
//
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
const
|
|
3301
|
-
|
|
3302
|
-
browser: 'Unknown Browser',
|
|
3303
|
-
os: 'Unknown OS'
|
|
3304
|
-
};
|
|
3305
|
-
let browser = 'Unknown Browser';
|
|
3306
|
-
let os = 'Unknown OS';
|
|
3307
|
-
|
|
3308
|
-
// Detect browser
|
|
3309
|
-
if (ua.includes('Chrome') && !ua.includes('Edg')) browser = 'Chrome';else if (ua.includes('Firefox')) browser = 'Firefox';else if (ua.includes('Safari') && !ua.includes('Chrome')) browser = 'Safari';else if (ua.includes('Edg')) browser = 'Edge';else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'Opera';
|
|
3310
|
-
|
|
3311
|
-
// Detect OS
|
|
3312
|
-
if (ua.includes('Windows')) os = 'Windows';else if (ua.includes('Mac OS')) os = 'macOS';else if (ua.includes('Linux')) os = 'Linux';else if (ua.includes('Android')) os = 'Android';else if (ua.includes('iPhone') || ua.includes('iPad')) os = 'iOS';
|
|
3313
|
-
return {
|
|
3314
|
-
browser,
|
|
3315
|
-
os
|
|
3316
|
-
};
|
|
3317
|
-
};
|
|
3318
|
-
|
|
3319
|
-
// Session handlers
|
|
3320
|
-
const handleRevokeSession = async sessionId => {
|
|
3321
|
-
// Check if revoking current session
|
|
3322
|
-
const isCurrentSession = sessionId === currentSession?.id || sessions.length === 1;
|
|
3323
|
-
|
|
3324
|
-
// If revoking current session, handle logout immediately after revocation
|
|
3325
|
-
if (isCurrentSession) {
|
|
3326
|
-
try {
|
|
3327
|
-
await revokeSession(sessionId);
|
|
3328
|
-
onSessionRevoked?.(sessionId);
|
|
3329
|
-
} catch (error) {
|
|
3330
|
-
onError?.(error);
|
|
3331
|
-
// Even if it fails, we're revoking our own session, so just logout
|
|
3332
|
-
// The server already revoked our session
|
|
3333
|
-
}
|
|
3334
|
-
// Clear auth state and redirect
|
|
3335
|
-
localStorage.removeItem('auth:token');
|
|
3336
|
-
window.dispatchEvent(new CustomEvent('auth:session-revoked'));
|
|
3337
|
-
if (variant === 'modal') onClose?.();
|
|
3338
|
-
return;
|
|
3339
|
-
}
|
|
3340
|
-
|
|
3341
|
-
// Revoking another session
|
|
3342
|
-
try {
|
|
3343
|
-
await revokeSession(sessionId);
|
|
3344
|
-
onSessionRevoked?.(sessionId);
|
|
3345
|
-
} catch (error) {
|
|
3346
|
-
// Check for 401 error (our SDK uses error.res, axios uses error.response)
|
|
3347
|
-
const status = error.res?.status || error.response?.status;
|
|
3348
|
-
if (status === 401) {
|
|
3349
|
-
// This means our session was revoked, not the target one - do logout
|
|
3350
|
-
localStorage.removeItem('auth:token');
|
|
3351
|
-
window.dispatchEvent(new CustomEvent('auth:session-revoked'));
|
|
3352
|
-
if (variant === 'modal') onClose?.();
|
|
3353
|
-
return;
|
|
3354
|
-
}
|
|
3355
|
-
onError?.(error);
|
|
3356
|
-
}
|
|
3357
|
-
};
|
|
3358
|
-
const handleRevokeOtherSessions = async () => {
|
|
3359
|
-
try {
|
|
3360
|
-
await revokeOtherSessions();
|
|
3361
|
-
onOtherSessionsRevoked?.();
|
|
3362
|
-
} catch (error) {
|
|
3363
|
-
onError?.(error);
|
|
3364
|
-
}
|
|
3365
|
-
};
|
|
3366
|
-
|
|
3367
|
-
// Name form
|
|
3823
|
+
// One editor open at a time: 'name', 'avatar' or null.
|
|
3824
|
+
const [editing, setEditing] = useState(null);
|
|
3825
|
+
const [avatarPreview, setAvatarPreview] = useState(null);
|
|
3826
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
3827
|
+
const [areSessionsOpen, setAreSessionsOpen] = useState(false);
|
|
3828
|
+
const [isSessionsRowActive, setIsSessionsRowActive] = useState(false);
|
|
3829
|
+
const [isConfirmingEnd, setIsConfirmingEnd] = useState(false);
|
|
3830
|
+
const [providers, setProviders] = useState([]);
|
|
3831
|
+
const [linked, setLinked] = useState([]);
|
|
3832
|
+
const [pendingProvider, setPendingProvider] = useState(null);
|
|
3833
|
+
const [failure, setFailure] = useState(null);
|
|
3834
|
+
const sessionsListId = useId();
|
|
3368
3835
|
const nameForm = useForm({
|
|
3369
3836
|
initialValues: {
|
|
3370
3837
|
name: ''
|
|
3371
3838
|
},
|
|
3372
3839
|
validate: {
|
|
3373
|
-
name:
|
|
3840
|
+
name: value => value.trim() ? null : t.nameRequired
|
|
3374
3841
|
}
|
|
3375
3842
|
});
|
|
3376
3843
|
|
|
3377
|
-
//
|
|
3378
|
-
|
|
3379
|
-
|
|
3844
|
+
// Sessions and sign-in methods load when the screen is shown, not before.
|
|
3845
|
+
useEffect(() => {
|
|
3846
|
+
if (!isVisible) return;
|
|
3847
|
+
if (showSessions) {
|
|
3848
|
+
getSession().catch(error => console.warn('[AuthSDK] Failed to read the current session:', error.message));
|
|
3849
|
+
listSessions().catch(error => console.warn('[AuthSDK] Failed to list sessions:', error.message));
|
|
3850
|
+
}
|
|
3851
|
+
if (showSignInMethods) refreshSignInMethods();
|
|
3852
|
+
}, [isVisible, showSessions, showSignInMethods]);
|
|
3380
3853
|
|
|
3381
|
-
//
|
|
3382
|
-
|
|
3383
|
-
if (
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3854
|
+
// Closing the modal resets it: the next opening starts at rest.
|
|
3855
|
+
useEffect(() => {
|
|
3856
|
+
if (isVisible) return;
|
|
3857
|
+
closeEditor();
|
|
3858
|
+
setAreSessionsOpen(false);
|
|
3859
|
+
setIsConfirmingEnd(false);
|
|
3860
|
+
}, [isVisible]);
|
|
3861
|
+
async function refreshSignInMethods() {
|
|
3862
|
+
const available = await getSocialProviders();
|
|
3863
|
+
setProviders(available);
|
|
3864
|
+
if (available.length === 0) return;
|
|
3865
|
+
try {
|
|
3866
|
+
setLinked(await getLinkedProviders());
|
|
3867
|
+
} catch (error) {
|
|
3868
|
+
console.warn('[AuthSDK] Failed to list linked providers:', error.message);
|
|
3869
|
+
setLinked([]);
|
|
3387
3870
|
}
|
|
3871
|
+
}
|
|
3388
3872
|
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3873
|
+
/*
|
|
3874
|
+
* `onError` still fires, with `isShownOnScreen: true`: a panel that wants a
|
|
3875
|
+
* log or a metric has it, and knows not to show a second message.
|
|
3876
|
+
*/
|
|
3877
|
+
function fail(section, error) {
|
|
3878
|
+
setFailure({
|
|
3879
|
+
section,
|
|
3880
|
+
message: error?.message || t.genericFailure
|
|
3881
|
+
});
|
|
3882
|
+
onError?.(error, {
|
|
3883
|
+
section,
|
|
3884
|
+
isShownOnScreen: true
|
|
3885
|
+
});
|
|
3886
|
+
}
|
|
3887
|
+
function closeEditor() {
|
|
3888
|
+
setFailure(null);
|
|
3889
|
+
setEditing(null);
|
|
3890
|
+
setAvatarPreview(null);
|
|
3891
|
+
setIsDragging(false);
|
|
3892
|
+
nameForm.reset();
|
|
3893
|
+
}
|
|
3894
|
+
function openEditor(section) {
|
|
3895
|
+
closeEditor();
|
|
3896
|
+
setEditing(section);
|
|
3897
|
+
if (section === 'name') nameForm.setValues({
|
|
3898
|
+
name: identity.name
|
|
3899
|
+
});
|
|
3900
|
+
}
|
|
3901
|
+
async function handleSaveName(values) {
|
|
3902
|
+
const name = values.name.trim();
|
|
3903
|
+
try {
|
|
3904
|
+
await updateProfile({
|
|
3905
|
+
name
|
|
3906
|
+
});
|
|
3907
|
+
closeEditor();
|
|
3908
|
+
onProfileUpdate?.({
|
|
3909
|
+
name
|
|
3910
|
+
});
|
|
3911
|
+
} catch (error) {
|
|
3912
|
+
fail('name', error);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
function handleAvatarFile(file) {
|
|
3916
|
+
if (!file) return;
|
|
3917
|
+
setFailure(null);
|
|
3918
|
+
if (!file.type?.startsWith('image/')) {
|
|
3919
|
+
fail('avatar', new Error(t.avatarInvalidType));
|
|
3392
3920
|
return;
|
|
3393
3921
|
}
|
|
3394
|
-
|
|
3395
|
-
// Validate file size
|
|
3396
3922
|
if (file.size > maxAvatarSize) {
|
|
3397
|
-
|
|
3923
|
+
fail('avatar', new Error(t.avatarTooLarge.replace('{size}', formatSize(maxAvatarSize))));
|
|
3398
3924
|
return;
|
|
3399
3925
|
}
|
|
3400
|
-
setAvatarFile(file);
|
|
3401
|
-
|
|
3402
|
-
// Convert to base64
|
|
3403
3926
|
const reader = new FileReader();
|
|
3404
|
-
reader.onloadend = () =>
|
|
3405
|
-
setAvatarPreview(reader.result);
|
|
3406
|
-
};
|
|
3927
|
+
reader.onloadend = () => setAvatarPreview(reader.result);
|
|
3407
3928
|
reader.readAsDataURL(file);
|
|
3408
|
-
}
|
|
3409
|
-
|
|
3410
|
-
// Populate forms when user data is available or section opens
|
|
3411
|
-
useEffect(() => {
|
|
3412
|
-
if (editingSection === 'name' && user?.name) {
|
|
3413
|
-
nameForm.setValues({
|
|
3414
|
-
name: user.name
|
|
3415
|
-
});
|
|
3416
|
-
}
|
|
3417
|
-
if (editingSection === 'avatar' && user?.image) {
|
|
3418
|
-
setAvatarPreview(user.image);
|
|
3419
|
-
}
|
|
3420
|
-
}, [editingSection, user]);
|
|
3421
|
-
const handleToggleSection = section => {
|
|
3422
|
-
if (editingSection === section) {
|
|
3423
|
-
setEditingSection(null);
|
|
3424
|
-
nameForm.reset();
|
|
3425
|
-
setAvatarPreview(null);
|
|
3426
|
-
setAvatarFile(null);
|
|
3427
|
-
} else {
|
|
3428
|
-
setEditingSection(section);
|
|
3429
|
-
}
|
|
3430
|
-
};
|
|
3431
|
-
const handleChangeName = async values => {
|
|
3929
|
+
}
|
|
3930
|
+
async function saveAvatar(image) {
|
|
3432
3931
|
try {
|
|
3433
3932
|
await updateProfile({
|
|
3434
|
-
|
|
3933
|
+
image
|
|
3435
3934
|
});
|
|
3436
|
-
|
|
3437
|
-
setEditingSection(null);
|
|
3935
|
+
closeEditor();
|
|
3438
3936
|
onProfileUpdate?.({
|
|
3439
|
-
|
|
3937
|
+
image
|
|
3440
3938
|
});
|
|
3441
3939
|
} catch (error) {
|
|
3442
|
-
|
|
3940
|
+
fail('avatar', error);
|
|
3443
3941
|
}
|
|
3444
|
-
}
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3942
|
+
}
|
|
3943
|
+
async function handleUnlink(provider) {
|
|
3944
|
+
setPendingProvider(provider);
|
|
3945
|
+
setFailure(null);
|
|
3946
|
+
try {
|
|
3947
|
+
await unlinkSocialProvider(provider);
|
|
3948
|
+
setLinked(current => current.filter(item => item.provider !== provider));
|
|
3949
|
+
onProviderUnlinked?.(provider);
|
|
3950
|
+
} catch (error) {
|
|
3951
|
+
fail('signIn', error);
|
|
3952
|
+
} finally {
|
|
3953
|
+
setPendingProvider(null);
|
|
3454
3954
|
}
|
|
3955
|
+
}
|
|
3956
|
+
async function handleLink(provider) {
|
|
3957
|
+
setPendingProvider(provider);
|
|
3958
|
+
setFailure(null);
|
|
3455
3959
|
try {
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
});
|
|
3459
|
-
setAvatarPreview(null);
|
|
3460
|
-
setAvatarFile(null);
|
|
3461
|
-
setEditingSection(null);
|
|
3462
|
-
onProfileUpdate?.({
|
|
3463
|
-
image: avatarPreview
|
|
3464
|
-
});
|
|
3960
|
+
// Leaves for the provider's consent screen; the page navigates away.
|
|
3961
|
+
await startSocialLink(provider);
|
|
3465
3962
|
} catch (error) {
|
|
3466
|
-
|
|
3963
|
+
setPendingProvider(null);
|
|
3964
|
+
fail('signIn', error);
|
|
3467
3965
|
}
|
|
3468
|
-
}
|
|
3469
|
-
|
|
3966
|
+
}
|
|
3967
|
+
async function handleEndSession(sessionId) {
|
|
3968
|
+
setFailure(null);
|
|
3470
3969
|
try {
|
|
3471
|
-
await
|
|
3472
|
-
|
|
3473
|
-
});
|
|
3474
|
-
setAvatarPreview(null);
|
|
3475
|
-
setAvatarFile(null);
|
|
3476
|
-
setEditingSection(null);
|
|
3477
|
-
onProfileUpdate?.({
|
|
3478
|
-
image: ''
|
|
3479
|
-
});
|
|
3970
|
+
await revokeSession(sessionId);
|
|
3971
|
+
onSessionRevoked?.(sessionId);
|
|
3480
3972
|
} catch (error) {
|
|
3481
|
-
|
|
3973
|
+
fail('sessions', error);
|
|
3482
3974
|
}
|
|
3483
|
-
}
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3975
|
+
}
|
|
3976
|
+
async function handleEndOthers() {
|
|
3977
|
+
setFailure(null);
|
|
3978
|
+
try {
|
|
3979
|
+
await revokeOtherSessions();
|
|
3980
|
+
setIsConfirmingEnd(false);
|
|
3981
|
+
onOtherSessionsRevoked?.();
|
|
3982
|
+
} catch (error) {
|
|
3983
|
+
fail('sessions', error);
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
if (!user) return null;
|
|
3987
|
+
const ordered = orderSessions(sessions, currentSession?.id);
|
|
3988
|
+
const current = ordered.find(item => item.id === currentSession?.id);
|
|
3989
|
+
const othersCount = ordered.filter(item => item.id !== currentSession?.id).length;
|
|
3990
|
+
const hasProfile = showAvatar || showName || showEmail;
|
|
3991
|
+
const hasSignInMethods = showSignInMethods && providers.length > 0;
|
|
3992
|
+
const sessionsSummary = current ? `${deviceLabel(describeDevice(current.userAgent))} (${t.thisDevice.toLowerCase()})${othersCount ? ` e mais ${othersCount}` : ''}` : null;
|
|
3993
|
+
const header = /*#__PURE__*/jsxs(Group, {
|
|
3994
|
+
align: "flex-start",
|
|
3995
|
+
wrap: "nowrap",
|
|
3996
|
+
gap: 12,
|
|
3997
|
+
px: 24,
|
|
3998
|
+
pt: 20,
|
|
3999
|
+
pb: 16,
|
|
4000
|
+
style: {
|
|
4001
|
+
borderBottom: '1px solid var(--mantine-color-gray-2)'
|
|
4002
|
+
},
|
|
4003
|
+
children: [variant === 'card' && logo && (typeof logo === 'string' ? /*#__PURE__*/jsx(Image, {
|
|
4004
|
+
src: logo,
|
|
4005
|
+
alt: "",
|
|
4006
|
+
h: logoHeight,
|
|
4007
|
+
w: "auto",
|
|
4008
|
+
fit: "contain"
|
|
4009
|
+
}) : logo), /*#__PURE__*/jsxs(Box, {
|
|
4010
|
+
style: {
|
|
4011
|
+
flex: 1,
|
|
4012
|
+
minWidth: 0
|
|
4013
|
+
},
|
|
4014
|
+
children: [/*#__PURE__*/jsx(Text, {
|
|
4015
|
+
component: variant === 'modal' ? Modal.Title : 'h2',
|
|
4016
|
+
m: 0,
|
|
4017
|
+
fz: 20,
|
|
4018
|
+
fw: 900,
|
|
4019
|
+
lts: "-0.03em",
|
|
4020
|
+
lh: 1.2,
|
|
4021
|
+
c: "gray.9",
|
|
4022
|
+
children: title || t.title
|
|
4023
|
+
}), /*#__PURE__*/jsx(Hint, {
|
|
4024
|
+
children: subtitle || t.subtitle
|
|
4025
|
+
})]
|
|
4026
|
+
}), variant === 'modal' && /*#__PURE__*/jsx(ActionButton, {
|
|
4027
|
+
"aria-label": t.close,
|
|
4028
|
+
onClick: onClose,
|
|
4029
|
+
style: {
|
|
4030
|
+
width: 32,
|
|
4031
|
+
height: 32,
|
|
4032
|
+
padding: 0,
|
|
4033
|
+
flex: 'none'
|
|
4034
|
+
},
|
|
4035
|
+
children: /*#__PURE__*/jsx(IconX, {
|
|
4036
|
+
size: 16,
|
|
3499
4037
|
stroke: 1.5
|
|
3500
4038
|
})
|
|
3501
|
-
})
|
|
3502
|
-
|
|
4039
|
+
})]
|
|
4040
|
+
});
|
|
4041
|
+
const identityStrip = /*#__PURE__*/jsxs(Group, {
|
|
4042
|
+
gap: 14,
|
|
4043
|
+
wrap: "nowrap",
|
|
4044
|
+
px: 24,
|
|
4045
|
+
py: 18,
|
|
4046
|
+
bg: "gray.1",
|
|
4047
|
+
style: {
|
|
4048
|
+
borderBottom: '1px solid var(--mantine-color-gray-2)'
|
|
4049
|
+
},
|
|
4050
|
+
children: [/*#__PURE__*/jsx(SquareAvatar, {
|
|
4051
|
+
src: identity.image,
|
|
4052
|
+
initials: identity.initials,
|
|
4053
|
+
size: 48
|
|
4054
|
+
}), /*#__PURE__*/jsxs(Box, {
|
|
4055
|
+
style: {
|
|
4056
|
+
minWidth: 0
|
|
4057
|
+
},
|
|
3503
4058
|
children: [/*#__PURE__*/jsx(Text, {
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
4059
|
+
fz: 15,
|
|
4060
|
+
fw: 800,
|
|
4061
|
+
c: "gray.9",
|
|
4062
|
+
lh: 1.3,
|
|
4063
|
+
truncate: "end",
|
|
4064
|
+
children: identity.title
|
|
4065
|
+
}), identity.hasRealName && identity.email && /*#__PURE__*/jsx(Text, {
|
|
4066
|
+
fz: 12,
|
|
4067
|
+
fw: 500,
|
|
4068
|
+
c: "gray.5",
|
|
4069
|
+
truncate: "end",
|
|
4070
|
+
children: identity.email
|
|
3511
4071
|
})]
|
|
3512
4072
|
})]
|
|
3513
4073
|
});
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
4074
|
+
const avatarEditor = /*#__PURE__*/jsxs(Stack, {
|
|
4075
|
+
gap: 10,
|
|
4076
|
+
py: 12,
|
|
4077
|
+
children: [/*#__PURE__*/jsx(Text, {
|
|
4078
|
+
fz: 12,
|
|
4079
|
+
fw: 700,
|
|
4080
|
+
c: "gray.9",
|
|
4081
|
+
children: t.avatar
|
|
4082
|
+
}), /*#__PURE__*/jsx(FileButton, {
|
|
4083
|
+
onChange: handleAvatarFile,
|
|
4084
|
+
accept: "image/png,image/jpeg,image/gif,image/webp",
|
|
4085
|
+
children: props => /*#__PURE__*/jsxs(UnstyledButton, {
|
|
4086
|
+
...props,
|
|
4087
|
+
onDragOver: event => {
|
|
4088
|
+
event.preventDefault();
|
|
4089
|
+
setIsDragging(true);
|
|
4090
|
+
},
|
|
4091
|
+
onDragLeave: () => setIsDragging(false),
|
|
4092
|
+
onDrop: event => {
|
|
4093
|
+
event.preventDefault();
|
|
4094
|
+
setIsDragging(false);
|
|
4095
|
+
handleAvatarFile(event.dataTransfer.files?.[0]);
|
|
4096
|
+
},
|
|
4097
|
+
style: {
|
|
4098
|
+
display: 'flex',
|
|
4099
|
+
alignItems: 'center',
|
|
4100
|
+
gap: 14,
|
|
4101
|
+
padding: 14,
|
|
4102
|
+
borderRadius: 0,
|
|
4103
|
+
background: isDragging ? 'var(--mantine-color-gray-2)' : 'var(--mantine-color-gray-1)',
|
|
4104
|
+
border: `1px dashed var(--mantine-color-${isDragging ? 'gray-9' : 'gray-4'})`
|
|
4105
|
+
},
|
|
4106
|
+
children: [/*#__PURE__*/jsx(SquareAvatar, {
|
|
4107
|
+
src: avatarPreview || identity.image,
|
|
4108
|
+
initials: identity.initials,
|
|
4109
|
+
size: 64
|
|
4110
|
+
}), /*#__PURE__*/jsxs(Box, {
|
|
4111
|
+
children: [/*#__PURE__*/jsx(Text, {
|
|
4112
|
+
fz: 13,
|
|
4113
|
+
fw: 700,
|
|
4114
|
+
c: "gray.9",
|
|
4115
|
+
children: t.avatarPrompt
|
|
4116
|
+
}), /*#__PURE__*/jsx(Hint, {
|
|
4117
|
+
children: t.avatarHint.replace('{size}', formatSize(maxAvatarSize))
|
|
4118
|
+
})]
|
|
4119
|
+
})]
|
|
4120
|
+
})
|
|
4121
|
+
}), /*#__PURE__*/jsx(FailureNote, {
|
|
4122
|
+
failure: failure,
|
|
4123
|
+
section: "avatar"
|
|
4124
|
+
}), /*#__PURE__*/jsxs(Group, {
|
|
4125
|
+
justify: "flex-end",
|
|
4126
|
+
gap: 8,
|
|
4127
|
+
children: [identity.image && !avatarPreview && /*#__PURE__*/jsx(ActionButton, {
|
|
4128
|
+
tone: "danger",
|
|
4129
|
+
loading: loadingUpdateProfile,
|
|
4130
|
+
onClick: () => saveAvatar(''),
|
|
4131
|
+
style: {
|
|
4132
|
+
marginRight: 'auto'
|
|
4133
|
+
},
|
|
4134
|
+
children: t.remove
|
|
4135
|
+
}), /*#__PURE__*/jsx(ActionButton, {
|
|
4136
|
+
tone: "quiet",
|
|
4137
|
+
onClick: closeEditor,
|
|
4138
|
+
children: t.cancel
|
|
4139
|
+
}), /*#__PURE__*/jsx(ActionButton, {
|
|
4140
|
+
tone: "dark",
|
|
4141
|
+
loading: loadingUpdateProfile,
|
|
4142
|
+
disabled: !avatarPreview,
|
|
4143
|
+
onClick: () => saveAvatar(avatarPreview),
|
|
4144
|
+
children: t.save
|
|
4145
|
+
})]
|
|
4146
|
+
})]
|
|
4147
|
+
});
|
|
4148
|
+
const nameEditor = /*#__PURE__*/jsx("form", {
|
|
4149
|
+
onSubmit: nameForm.onSubmit(handleSaveName),
|
|
4150
|
+
style: {
|
|
4151
|
+
...rowDivider,
|
|
4152
|
+
padding: '12px 0 14px'
|
|
4153
|
+
},
|
|
4154
|
+
children: /*#__PURE__*/jsxs(Stack, {
|
|
4155
|
+
gap: 10,
|
|
4156
|
+
children: [/*#__PURE__*/jsx(TextInput, {
|
|
4157
|
+
label: t.name,
|
|
4158
|
+
placeholder: t.namePlaceholder,
|
|
4159
|
+
autoComplete: "name",
|
|
4160
|
+
"data-autofocus": true,
|
|
4161
|
+
autoFocus: true,
|
|
4162
|
+
radius: 0,
|
|
4163
|
+
size: "sm",
|
|
4164
|
+
styles: {
|
|
4165
|
+
label: {
|
|
4166
|
+
fontSize: 12,
|
|
4167
|
+
fontWeight: 700,
|
|
4168
|
+
color: 'var(--mantine-color-gray-9)',
|
|
4169
|
+
marginBottom: 6
|
|
4170
|
+
}
|
|
4171
|
+
},
|
|
4172
|
+
onKeyDown: event => {
|
|
4173
|
+
if (event.key !== 'Escape') return;
|
|
4174
|
+
event.stopPropagation();
|
|
4175
|
+
closeEditor();
|
|
4176
|
+
},
|
|
4177
|
+
...nameForm.getInputProps('name')
|
|
4178
|
+
}), /*#__PURE__*/jsx(Hint, {
|
|
4179
|
+
children: t.nameHint
|
|
4180
|
+
}), /*#__PURE__*/jsx(FailureNote, {
|
|
4181
|
+
failure: failure,
|
|
4182
|
+
section: "name"
|
|
4183
|
+
}), /*#__PURE__*/jsxs(Group, {
|
|
4184
|
+
justify: "flex-end",
|
|
4185
|
+
gap: 8,
|
|
4186
|
+
children: [/*#__PURE__*/jsx(ActionButton, {
|
|
4187
|
+
tone: "quiet",
|
|
4188
|
+
onClick: closeEditor,
|
|
4189
|
+
children: t.cancel
|
|
4190
|
+
}), /*#__PURE__*/jsx(ActionButton, {
|
|
4191
|
+
tone: "dark",
|
|
4192
|
+
type: "submit",
|
|
4193
|
+
loading: loadingUpdateProfile,
|
|
4194
|
+
children: t.save
|
|
3541
4195
|
})]
|
|
3542
|
-
}), action && /*#__PURE__*/jsx(Tooltip, {
|
|
3543
|
-
label: actionLabel || action,
|
|
3544
|
-
position: "left",
|
|
3545
|
-
children: /*#__PURE__*/jsx(Anchor, {
|
|
3546
|
-
size: "xs",
|
|
3547
|
-
fw: 300,
|
|
3548
|
-
onClick: onClick,
|
|
3549
|
-
c: "gray",
|
|
3550
|
-
underline: "none",
|
|
3551
|
-
children: expanded ? labels.cancel || 'Cancel' : action
|
|
3552
|
-
})
|
|
3553
4196
|
})]
|
|
3554
4197
|
})
|
|
3555
4198
|
});
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
children: [
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
size: 48,
|
|
3580
|
-
radius: "xl"
|
|
3581
|
-
// color="initials"
|
|
3582
|
-
})
|
|
3583
|
-
})
|
|
3584
|
-
}), /*#__PURE__*/jsx(Collapse, {
|
|
3585
|
-
in: editingSection === 'avatar',
|
|
3586
|
-
children: /*#__PURE__*/jsx(Paper, {
|
|
3587
|
-
p: "sm",
|
|
3588
|
-
withBorder: true,
|
|
3589
|
-
radius: "sm",
|
|
3590
|
-
children: /*#__PURE__*/jsxs(Stack, {
|
|
3591
|
-
gap: "md",
|
|
3592
|
-
align: "center",
|
|
3593
|
-
children: [/*#__PURE__*/jsx(FileButton, {
|
|
3594
|
-
onChange: handleAvatarFileChange,
|
|
3595
|
-
accept: "image/*",
|
|
3596
|
-
children: props => /*#__PURE__*/jsx(Tooltip, {
|
|
3597
|
-
label: labels.clickToChange || 'Clique para alterar',
|
|
3598
|
-
position: "bottom",
|
|
3599
|
-
children: /*#__PURE__*/jsxs(Box, {
|
|
3600
|
-
...props,
|
|
3601
|
-
pos: "relative",
|
|
3602
|
-
style: {
|
|
3603
|
-
cursor: 'pointer'
|
|
3604
|
-
},
|
|
3605
|
-
children: [/*#__PURE__*/jsx(Avatar, {
|
|
3606
|
-
src: avatarPreview || user?.image,
|
|
3607
|
-
name: user?.name || user?.email,
|
|
3608
|
-
size: 80,
|
|
3609
|
-
radius: 80,
|
|
3610
|
-
color: "gray"
|
|
3611
|
-
}), /*#__PURE__*/jsx(ThemeIcon, {
|
|
3612
|
-
size: 26,
|
|
3613
|
-
radius: "xl",
|
|
3614
|
-
color: "gray",
|
|
3615
|
-
pos: "absolute",
|
|
3616
|
-
bottom: 0,
|
|
3617
|
-
right: 0,
|
|
3618
|
-
bd: "2px solid body",
|
|
3619
|
-
children: /*#__PURE__*/jsx(IconPhoto, {
|
|
3620
|
-
size: 14,
|
|
3621
|
-
stroke: 1.5
|
|
3622
|
-
})
|
|
3623
|
-
})]
|
|
3624
|
-
})
|
|
3625
|
-
})
|
|
3626
|
-
}), /*#__PURE__*/jsx(Text, {
|
|
3627
|
-
size: "xs",
|
|
3628
|
-
c: "dimmed",
|
|
3629
|
-
ta: "center",
|
|
3630
|
-
children: labels.avatarHint || `Máximo ${Math.round(maxAvatarSize / 1024)}KB • JPG, PNG, GIF, WebP`
|
|
3631
|
-
}), /*#__PURE__*/jsxs(Group, {
|
|
3632
|
-
justify: "center",
|
|
3633
|
-
gap: "xs",
|
|
3634
|
-
children: [user?.image && /*#__PURE__*/jsx(Button, {
|
|
3635
|
-
variant: "subtle",
|
|
3636
|
-
color: "gray",
|
|
3637
|
-
size: "xs",
|
|
3638
|
-
onClick: handleRemoveAvatar,
|
|
3639
|
-
loading: loadingUpdateProfile,
|
|
3640
|
-
loaderProps: {
|
|
3641
|
-
size: 12
|
|
3642
|
-
},
|
|
3643
|
-
leftSection: /*#__PURE__*/jsx(IconTrash, {
|
|
3644
|
-
size: 14,
|
|
3645
|
-
stroke: 1.5
|
|
3646
|
-
}),
|
|
3647
|
-
children: labels.remove || 'Remover'
|
|
3648
|
-
}), /*#__PURE__*/jsx(Button, {
|
|
3649
|
-
variant: "default",
|
|
3650
|
-
size: "xs",
|
|
3651
|
-
onClick: () => handleToggleSection('avatar'),
|
|
3652
|
-
children: labels.cancel || 'Cancelar'
|
|
3653
|
-
}), /*#__PURE__*/jsx(Button, {
|
|
3654
|
-
size: "xs",
|
|
3655
|
-
loading: loadingUpdateProfile,
|
|
3656
|
-
loaderProps: {
|
|
3657
|
-
size: 12
|
|
3658
|
-
},
|
|
3659
|
-
leftSection: /*#__PURE__*/jsx(IconCheck, {
|
|
3660
|
-
size: 14,
|
|
3661
|
-
stroke: 1.5
|
|
3662
|
-
}),
|
|
3663
|
-
onClick: handleChangeAvatar,
|
|
3664
|
-
disabled: !avatarPreview || avatarPreview === user?.image,
|
|
3665
|
-
children: labels.save || 'Salvar'
|
|
3666
|
-
})]
|
|
3667
|
-
})]
|
|
3668
|
-
})
|
|
3669
|
-
})
|
|
3670
|
-
})]
|
|
3671
|
-
}), showName && /*#__PURE__*/jsxs(Fragment, {
|
|
3672
|
-
children: [/*#__PURE__*/jsx(SettingRow, {
|
|
3673
|
-
label: labels.name || 'Nome',
|
|
3674
|
-
action: labels.change || 'Change',
|
|
3675
|
-
actionLabel: labels.changeName || 'Change your display name',
|
|
3676
|
-
onClick: () => handleToggleSection('name'),
|
|
3677
|
-
expanded: editingSection === 'name',
|
|
3678
|
-
children: /*#__PURE__*/jsxs(Group, {
|
|
3679
|
-
gap: "xs",
|
|
3680
|
-
children: [/*#__PURE__*/jsx(IconPencil, {
|
|
3681
|
-
size: 18,
|
|
3682
|
-
stroke: 1.5,
|
|
3683
|
-
color: "var(--mantine-color-dimmed)"
|
|
3684
|
-
}), /*#__PURE__*/jsx(Text, {
|
|
3685
|
-
size: "sm",
|
|
3686
|
-
children: user?.name || labels.notDefined || 'Não definido'
|
|
3687
|
-
})]
|
|
3688
|
-
})
|
|
3689
|
-
}), /*#__PURE__*/jsx(Collapse, {
|
|
3690
|
-
in: editingSection === 'name',
|
|
3691
|
-
children: /*#__PURE__*/jsx(Paper, {
|
|
3692
|
-
p: "sm",
|
|
3693
|
-
withBorder: true,
|
|
3694
|
-
radius: "sm",
|
|
3695
|
-
children: /*#__PURE__*/jsx("form", {
|
|
3696
|
-
onSubmit: nameForm.onSubmit(handleChangeName),
|
|
3697
|
-
children: /*#__PURE__*/jsxs(Stack, {
|
|
3698
|
-
gap: "sm",
|
|
3699
|
-
children: [/*#__PURE__*/jsx(TextInput, {
|
|
3700
|
-
label: labels.name || 'Nome',
|
|
3701
|
-
placeholder: labels.namePlaceholder || 'Digite seu nome',
|
|
3702
|
-
leftSection: /*#__PURE__*/jsx(IconUser, {
|
|
3703
|
-
size: 16,
|
|
3704
|
-
stroke: 1.5
|
|
3705
|
-
}),
|
|
3706
|
-
...nameForm.getInputProps('name')
|
|
3707
|
-
}), /*#__PURE__*/jsxs(Group, {
|
|
3708
|
-
justify: "flex-end",
|
|
3709
|
-
gap: "xs",
|
|
3710
|
-
children: [/*#__PURE__*/jsx(Button, {
|
|
3711
|
-
variant: "default",
|
|
3712
|
-
size: "xs",
|
|
3713
|
-
onClick: () => handleToggleSection('name'),
|
|
3714
|
-
children: labels.cancel || 'Cancelar'
|
|
3715
|
-
}), /*#__PURE__*/jsx(Button, {
|
|
3716
|
-
type: "submit",
|
|
3717
|
-
size: "xs",
|
|
3718
|
-
loading: loadingUpdateProfile,
|
|
3719
|
-
loaderProps: {
|
|
3720
|
-
size: 12
|
|
3721
|
-
},
|
|
3722
|
-
leftSection: /*#__PURE__*/jsx(IconCheck, {
|
|
3723
|
-
size: 14,
|
|
3724
|
-
stroke: 1.5
|
|
3725
|
-
}),
|
|
3726
|
-
children: labels.save || 'Salvar'
|
|
3727
|
-
})]
|
|
3728
|
-
})]
|
|
3729
|
-
})
|
|
3730
|
-
})
|
|
3731
|
-
})
|
|
3732
|
-
})]
|
|
3733
|
-
}), showEmail && /*#__PURE__*/jsx(SettingRow, {
|
|
3734
|
-
label: labels.email || 'Email',
|
|
4199
|
+
const sessionsList = /*#__PURE__*/jsxs(Box, {
|
|
4200
|
+
id: sessionsListId,
|
|
4201
|
+
mb: 10,
|
|
4202
|
+
children: [loadingListSessions && ordered.length === 0 ? /*#__PURE__*/jsx(Hint, {
|
|
4203
|
+
children: t.loadingSessions
|
|
4204
|
+
}) : ordered.length === 0 ? /*#__PURE__*/jsx(Hint, {
|
|
4205
|
+
children: t.noSessionsFound
|
|
4206
|
+
}) : ordered.map((item, index) => {
|
|
4207
|
+
const device = describeDevice(item.userAgent);
|
|
4208
|
+
const isCurrent = item.id === currentSession?.id;
|
|
4209
|
+
const since = formatSessionStart(item.createdAt);
|
|
4210
|
+
return /*#__PURE__*/jsx(Row$1, {
|
|
4211
|
+
isFirst: index === 0,
|
|
4212
|
+
action: isCurrent ? null : /*#__PURE__*/jsx(ActionButton, {
|
|
4213
|
+
tone: "danger",
|
|
4214
|
+
loading: loadingRevokeSession === item.id,
|
|
4215
|
+
onClick: () => handleEndSession(item.id),
|
|
4216
|
+
"aria-label": `${t.end} ${deviceLabel(device)}`,
|
|
4217
|
+
children: t.end
|
|
4218
|
+
}),
|
|
4219
|
+
children: /*#__PURE__*/jsx(WithIcon, {
|
|
4220
|
+
icon: DEVICE_MARKS[device.kind] || IconDeviceDesktop,
|
|
4221
|
+
detail: `${item.ipAddress ? `IP ${item.ipAddress}` : t.unknownIP}${since ? ` · ${t.since} ${since}` : ''}`,
|
|
3735
4222
|
children: /*#__PURE__*/jsxs(Group, {
|
|
3736
|
-
gap:
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
children: user?.email || 'email@exemplo.com'
|
|
4223
|
+
gap: 6,
|
|
4224
|
+
wrap: "wrap",
|
|
4225
|
+
children: [/*#__PURE__*/jsx("span", {
|
|
4226
|
+
children: deviceLabel(device)
|
|
4227
|
+
}), isCurrent && /*#__PURE__*/jsx(Chip, {
|
|
4228
|
+
tone: "dark",
|
|
4229
|
+
children: t.thisDevice
|
|
3744
4230
|
})]
|
|
3745
4231
|
})
|
|
4232
|
+
})
|
|
4233
|
+
}, item.id);
|
|
4234
|
+
}), /*#__PURE__*/jsx(FailureNote, {
|
|
4235
|
+
failure: failure,
|
|
4236
|
+
section: "sessions"
|
|
4237
|
+
}), othersCount > 0 && current && (isConfirmingEnd ? /*#__PURE__*/jsxs(Stack, {
|
|
4238
|
+
role: "alertdialog",
|
|
4239
|
+
"aria-label": endOthersQuestion(othersCount),
|
|
4240
|
+
gap: 10,
|
|
4241
|
+
p: 12,
|
|
4242
|
+
mt: 4,
|
|
4243
|
+
bg: "red.0",
|
|
4244
|
+
style: {
|
|
4245
|
+
border: '1px solid var(--mantine-color-red-2)'
|
|
4246
|
+
},
|
|
4247
|
+
children: [/*#__PURE__*/jsxs(Text, {
|
|
4248
|
+
fz: 12,
|
|
4249
|
+
fw: 500,
|
|
4250
|
+
c: "gray.7",
|
|
4251
|
+
children: [/*#__PURE__*/jsx(Text, {
|
|
4252
|
+
span: true,
|
|
4253
|
+
inherit: true,
|
|
4254
|
+
fw: 800,
|
|
4255
|
+
c: "gray.9",
|
|
4256
|
+
children: endOthersQuestion(othersCount)
|
|
4257
|
+
}), ' ', t.confirmEndBody]
|
|
4258
|
+
}), /*#__PURE__*/jsxs(Group, {
|
|
4259
|
+
justify: "flex-end",
|
|
4260
|
+
gap: 8,
|
|
4261
|
+
children: [/*#__PURE__*/jsx(ActionButton, {
|
|
4262
|
+
tone: "quiet",
|
|
4263
|
+
onClick: () => setIsConfirmingEnd(false),
|
|
4264
|
+
children: t.cancel
|
|
4265
|
+
}), /*#__PURE__*/jsx(ActionButton, {
|
|
4266
|
+
tone: "dangerFill",
|
|
4267
|
+
loading: loadingRevokeSession === 'all',
|
|
4268
|
+
onClick: handleEndOthers,
|
|
4269
|
+
children: endOthersConfirm(othersCount)
|
|
3746
4270
|
})]
|
|
3747
4271
|
})]
|
|
3748
|
-
})
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
})
|
|
3775
|
-
}), /*#__PURE__*/jsx(Collapse, {
|
|
3776
|
-
in: editingSection === 'sessions',
|
|
3777
|
-
children: /*#__PURE__*/jsx(Paper, {
|
|
3778
|
-
p: "sm",
|
|
3779
|
-
withBorder: true,
|
|
3780
|
-
radius: "sm",
|
|
3781
|
-
children: /*#__PURE__*/jsx(Stack, {
|
|
3782
|
-
gap: "xs",
|
|
3783
|
-
children: loadingListSessions ? /*#__PURE__*/jsx(Text, {
|
|
3784
|
-
size: "xs",
|
|
3785
|
-
c: "dimmed",
|
|
3786
|
-
ta: "center",
|
|
3787
|
-
py: "md",
|
|
3788
|
-
children: labels.loadingSessions || 'Carregando sessões...'
|
|
3789
|
-
}) : sessions.length === 0 ? /*#__PURE__*/jsx(Text, {
|
|
3790
|
-
size: "xs",
|
|
3791
|
-
c: "dimmed",
|
|
3792
|
-
ta: "center",
|
|
3793
|
-
py: "md",
|
|
3794
|
-
children: labels.noSessionsFound || 'Nenhuma sessão encontrada'
|
|
3795
|
-
}) : /*#__PURE__*/jsxs(Fragment, {
|
|
3796
|
-
children: [sessions.map(sessionItem => {
|
|
3797
|
-
const isCurrentSession = sessionItem.id === currentSession?.id;
|
|
3798
|
-
const deviceInfo = parseUserAgent(sessionItem.userAgent);
|
|
3799
|
-
const createdDate = new Date(sessionItem.createdAt);
|
|
3800
|
-
return /*#__PURE__*/jsx(Paper, {
|
|
3801
|
-
p: "xs",
|
|
3802
|
-
withBorder: isCurrentSession,
|
|
3803
|
-
bd: isCurrentSession ? '1px solid gray' : undefined,
|
|
3804
|
-
radius: "sm",
|
|
3805
|
-
children: /*#__PURE__*/jsxs(Group, {
|
|
3806
|
-
justify: "space-between",
|
|
3807
|
-
wrap: "nowrap",
|
|
3808
|
-
align: "center",
|
|
3809
|
-
children: [/*#__PURE__*/jsxs(Group, {
|
|
3810
|
-
gap: "sm",
|
|
3811
|
-
wrap: "nowrap",
|
|
3812
|
-
flex: 1,
|
|
3813
|
-
children: [/*#__PURE__*/jsx(ThemeIcon, {
|
|
3814
|
-
size: 32,
|
|
3815
|
-
variant: "subtle",
|
|
3816
|
-
color: "gray",
|
|
3817
|
-
children: /*#__PURE__*/jsx(IconDeviceMobile, {
|
|
3818
|
-
size: 18,
|
|
3819
|
-
stroke: 1.5
|
|
3820
|
-
})
|
|
3821
|
-
}), /*#__PURE__*/jsxs(Box, {
|
|
3822
|
-
flex: 1,
|
|
3823
|
-
children: [/*#__PURE__*/jsxs(Group, {
|
|
3824
|
-
gap: "xs",
|
|
3825
|
-
children: [/*#__PURE__*/jsx(Text, {
|
|
3826
|
-
size: "xs",
|
|
3827
|
-
fw: 600,
|
|
3828
|
-
children: deviceInfo.browser
|
|
3829
|
-
}), isCurrentSession && /*#__PURE__*/jsx(Badge, {
|
|
3830
|
-
size: "xs",
|
|
3831
|
-
variant: "light",
|
|
3832
|
-
color: "gray",
|
|
3833
|
-
children: labels.thisDevice || 'Este dispositivo'
|
|
3834
|
-
})]
|
|
3835
|
-
}), /*#__PURE__*/jsxs(Text, {
|
|
3836
|
-
size: "xs",
|
|
3837
|
-
c: "dimmed",
|
|
3838
|
-
children: [deviceInfo.os, " \u2022 ", sessionItem.ipAddress || labels.unknownIP || 'IP desconhecido']
|
|
3839
|
-
}), /*#__PURE__*/jsxs(Text, {
|
|
3840
|
-
size: "xs",
|
|
3841
|
-
c: "dimmed",
|
|
3842
|
-
children: [labels.createdAt || 'Criada em', " ", createdDate.toLocaleDateString('pt-BR'), " ", labels.at || 'às', ' ', createdDate.toLocaleTimeString('pt-BR', {
|
|
3843
|
-
hour: '2-digit',
|
|
3844
|
-
minute: '2-digit'
|
|
3845
|
-
})]
|
|
3846
|
-
})]
|
|
3847
|
-
})]
|
|
3848
|
-
}), /*#__PURE__*/jsx(Tooltip, {
|
|
3849
|
-
label: isCurrentSession ? labels.signOutAndEnd || 'Encerrar e sair' : labels.endSession || 'Encerrar sessão',
|
|
3850
|
-
children: /*#__PURE__*/jsx(Button, {
|
|
3851
|
-
variant: "subtle",
|
|
3852
|
-
color: "gray",
|
|
3853
|
-
size: "xs",
|
|
3854
|
-
onClick: () => handleRevokeSession(sessionItem.id),
|
|
3855
|
-
loading: loadingRevokeSession === sessionItem.id,
|
|
3856
|
-
loaderProps: {
|
|
3857
|
-
size: 12
|
|
3858
|
-
},
|
|
3859
|
-
leftSection: /*#__PURE__*/jsx(IconLogout, {
|
|
3860
|
-
size: 14,
|
|
3861
|
-
stroke: 1.5
|
|
3862
|
-
}),
|
|
3863
|
-
children: labels.end || 'Encerrar'
|
|
3864
|
-
})
|
|
3865
|
-
})]
|
|
3866
|
-
})
|
|
3867
|
-
}, sessionItem.id);
|
|
3868
|
-
}), sessions.length > 1 && /*#__PURE__*/jsx(Group, {
|
|
3869
|
-
justify: "flex-end",
|
|
3870
|
-
mt: "xs",
|
|
3871
|
-
children: /*#__PURE__*/jsx(Button, {
|
|
3872
|
-
variant: "subtle",
|
|
3873
|
-
color: "gray",
|
|
3874
|
-
size: "xs",
|
|
3875
|
-
onClick: handleRevokeOtherSessions,
|
|
3876
|
-
loading: loadingRevokeSession === 'all',
|
|
3877
|
-
loaderProps: {
|
|
3878
|
-
size: 12
|
|
3879
|
-
},
|
|
3880
|
-
leftSection: /*#__PURE__*/jsx(IconLogout, {
|
|
3881
|
-
size: 14,
|
|
3882
|
-
stroke: 1.5
|
|
3883
|
-
}),
|
|
3884
|
-
children: labels.endOtherSessions || 'Encerrar todas as outras sessões'
|
|
3885
|
-
})
|
|
3886
|
-
})]
|
|
3887
|
-
})
|
|
3888
|
-
})
|
|
3889
|
-
})
|
|
3890
|
-
})]
|
|
4272
|
+
}) : /*#__PURE__*/jsx(Group, {
|
|
4273
|
+
justify: "flex-end",
|
|
4274
|
+
pt: 4,
|
|
4275
|
+
pb: 4,
|
|
4276
|
+
children: /*#__PURE__*/jsx(ActionButton, {
|
|
4277
|
+
tone: "dangerOutline",
|
|
4278
|
+
onClick: () => setIsConfirmingEnd(true),
|
|
4279
|
+
children: endOthersAction(othersCount)
|
|
4280
|
+
})
|
|
4281
|
+
}))]
|
|
4282
|
+
});
|
|
4283
|
+
const content = /*#__PURE__*/jsxs(Fragment, {
|
|
4284
|
+
children: [header, identityStrip, hasProfile && /*#__PURE__*/jsxs(Section, {
|
|
4285
|
+
label: t.profileSection,
|
|
4286
|
+
isFirst: true,
|
|
4287
|
+
children: [showAvatar && (editing === 'avatar' ? avatarEditor : /*#__PURE__*/jsx(Row$1, {
|
|
4288
|
+
label: t.avatar,
|
|
4289
|
+
isFirst: true,
|
|
4290
|
+
action: /*#__PURE__*/jsx(ActionButton, {
|
|
4291
|
+
onClick: () => openEditor('avatar'),
|
|
4292
|
+
children: t.edit
|
|
4293
|
+
}),
|
|
4294
|
+
children: /*#__PURE__*/jsx(SquareAvatar, {
|
|
4295
|
+
src: identity.image,
|
|
4296
|
+
initials: identity.initials,
|
|
4297
|
+
size: 32
|
|
3891
4298
|
})
|
|
4299
|
+
})), showName && (editing === 'name' ? nameEditor : /*#__PURE__*/jsx(Row$1, {
|
|
4300
|
+
label: t.name,
|
|
4301
|
+
isFirst: !showAvatar,
|
|
4302
|
+
action: /*#__PURE__*/jsx(ActionButton, {
|
|
4303
|
+
onClick: () => openEditor('name'),
|
|
4304
|
+
children: t.edit
|
|
4305
|
+
}),
|
|
4306
|
+
children: identity.name || /*#__PURE__*/jsx(Text, {
|
|
4307
|
+
span: true,
|
|
4308
|
+
inherit: true,
|
|
4309
|
+
c: "gray.5",
|
|
4310
|
+
children: t.notDefined
|
|
4311
|
+
})
|
|
4312
|
+
})), showEmail && /*#__PURE__*/jsxs(Row$1, {
|
|
4313
|
+
label: t.email,
|
|
4314
|
+
isFirst: !showAvatar && !showName,
|
|
4315
|
+
children: [identity.email, /*#__PURE__*/jsx(Hint, {
|
|
4316
|
+
children: t.emailHint
|
|
4317
|
+
})]
|
|
4318
|
+
})]
|
|
4319
|
+
}), hasSignInMethods && /*#__PURE__*/jsxs(Section, {
|
|
4320
|
+
label: t.signInSection,
|
|
4321
|
+
isFirst: !hasProfile,
|
|
4322
|
+
children: [/*#__PURE__*/jsx(Row$1, {
|
|
4323
|
+
label: t.codeMethod,
|
|
4324
|
+
isFirst: true,
|
|
4325
|
+
action: /*#__PURE__*/jsx(Chip, {
|
|
4326
|
+
tone: "good",
|
|
4327
|
+
children: t.alwaysOn
|
|
4328
|
+
}),
|
|
4329
|
+
children: /*#__PURE__*/jsx(WithIcon, {
|
|
4330
|
+
icon: IconMail,
|
|
4331
|
+
children: t.codeByEmail
|
|
4332
|
+
})
|
|
4333
|
+
}), providers.map(item => {
|
|
4334
|
+
const link = linked.find(entry => entry.provider === item.provider);
|
|
4335
|
+
return /*#__PURE__*/jsx(Row$1, {
|
|
4336
|
+
label: item.name,
|
|
4337
|
+
action: link ? /*#__PURE__*/jsx(ActionButton, {
|
|
4338
|
+
tone: "quiet",
|
|
4339
|
+
loading: pendingProvider === item.provider,
|
|
4340
|
+
onClick: () => handleUnlink(item.provider),
|
|
4341
|
+
"aria-label": `${t.disconnect} ${item.name}`,
|
|
4342
|
+
children: t.disconnect
|
|
4343
|
+
}) : /*#__PURE__*/jsx(ActionButton, {
|
|
4344
|
+
loading: pendingProvider === item.provider,
|
|
4345
|
+
onClick: () => handleLink(item.provider),
|
|
4346
|
+
"aria-label": `${t.connect} ${item.name}`,
|
|
4347
|
+
children: t.connect
|
|
4348
|
+
}),
|
|
4349
|
+
children: /*#__PURE__*/jsx(WithIcon, {
|
|
4350
|
+
icon: PROVIDER_MARKS[item.provider] || IconLink,
|
|
4351
|
+
children: link ? link.email || item.name : /*#__PURE__*/jsx(Text, {
|
|
4352
|
+
span: true,
|
|
4353
|
+
inherit: true,
|
|
4354
|
+
c: "gray.5",
|
|
4355
|
+
children: t.notConnected
|
|
4356
|
+
})
|
|
4357
|
+
})
|
|
4358
|
+
}, item.provider);
|
|
4359
|
+
}), /*#__PURE__*/jsx(FailureNote, {
|
|
4360
|
+
failure: failure,
|
|
4361
|
+
section: "signIn"
|
|
3892
4362
|
})]
|
|
4363
|
+
}), showSessions && /*#__PURE__*/jsxs(Section, {
|
|
4364
|
+
label: t.sessionsSection,
|
|
4365
|
+
isFirst: !hasProfile && !hasSignInMethods,
|
|
4366
|
+
children: [/*#__PURE__*/jsx(UnstyledButton, {
|
|
4367
|
+
"aria-expanded": areSessionsOpen,
|
|
4368
|
+
"aria-controls": sessionsListId,
|
|
4369
|
+
onClick: () => {
|
|
4370
|
+
setAreSessionsOpen(isOpen => !isOpen);
|
|
4371
|
+
setIsConfirmingEnd(false);
|
|
4372
|
+
},
|
|
4373
|
+
onMouseEnter: () => setIsSessionsRowActive(true),
|
|
4374
|
+
onMouseLeave: () => setIsSessionsRowActive(false),
|
|
4375
|
+
onFocus: () => setIsSessionsRowActive(true),
|
|
4376
|
+
onBlur: () => setIsSessionsRowActive(false),
|
|
4377
|
+
w: "100%",
|
|
4378
|
+
style: {
|
|
4379
|
+
display: 'block',
|
|
4380
|
+
borderRadius: 0
|
|
4381
|
+
},
|
|
4382
|
+
children: /*#__PURE__*/jsx(Row$1, {
|
|
4383
|
+
label: t.devices,
|
|
4384
|
+
isFirst: true,
|
|
4385
|
+
action: /*#__PURE__*/jsxs(Box, {
|
|
4386
|
+
component: "span",
|
|
4387
|
+
style: {
|
|
4388
|
+
display: 'inline-flex',
|
|
4389
|
+
alignItems: 'center',
|
|
4390
|
+
gap: 4,
|
|
4391
|
+
padding: '5px 12px',
|
|
4392
|
+
fontSize: 12,
|
|
4393
|
+
fontWeight: 700,
|
|
4394
|
+
color: 'var(--mantine-color-gray-9)',
|
|
4395
|
+
// The whole line is the button; the pill only shows it.
|
|
4396
|
+
border: `1px solid var(--mantine-color-${isSessionsRowActive ? 'gray-9' : 'gray-3'})`,
|
|
4397
|
+
whiteSpace: 'nowrap'
|
|
4398
|
+
},
|
|
4399
|
+
children: [areSessionsOpen ? t.hideSessions : t.showSessions, /*#__PURE__*/jsx(IconChevronDown, {
|
|
4400
|
+
size: 14,
|
|
4401
|
+
stroke: 1.5,
|
|
4402
|
+
style: {
|
|
4403
|
+
transform: areSessionsOpen ? 'rotate(180deg)' : 'none',
|
|
4404
|
+
transition: 'transform 150ms ease'
|
|
4405
|
+
}
|
|
4406
|
+
})]
|
|
4407
|
+
}),
|
|
4408
|
+
children: /*#__PURE__*/jsx(WithIcon, {
|
|
4409
|
+
icon: IconDeviceLaptop,
|
|
4410
|
+
detail: sessionsSummary,
|
|
4411
|
+
children: loadingListSessions && ordered.length === 0 ? t.loadingSessions : countSessions(ordered.length)
|
|
4412
|
+
})
|
|
4413
|
+
})
|
|
4414
|
+
}), areSessionsOpen && sessionsList]
|
|
3893
4415
|
}), customSections]
|
|
3894
4416
|
});
|
|
3895
|
-
|
|
3896
|
-
// Renderizar como Modal
|
|
3897
4417
|
if (variant === 'modal') {
|
|
3898
|
-
return /*#__PURE__*/jsxs(Modal, {
|
|
3899
|
-
opened: opened,
|
|
4418
|
+
return /*#__PURE__*/jsxs(Modal.Root, {
|
|
4419
|
+
opened: Boolean(opened),
|
|
3900
4420
|
onClose: onClose,
|
|
3901
|
-
size: width
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
4421
|
+
size: width
|
|
4422
|
+
// With an editor open, Esc belongs to the editor: it cancels the
|
|
4423
|
+
// edit and leaves the screen open. Mantine listens for Esc on its
|
|
4424
|
+
// own, so stopping the key in the field is not enough.
|
|
4425
|
+
,
|
|
4426
|
+
closeOnEscape: !editing && !isConfirmingEnd,
|
|
4427
|
+
...containerProps,
|
|
4428
|
+
children: [/*#__PURE__*/jsx(Modal.Overlay, {
|
|
3905
4429
|
backgroundOpacity: 0.5,
|
|
3906
4430
|
blur: 4
|
|
3907
|
-
},
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
children:
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
})
|
|
3918
|
-
}), /*#__PURE__*/jsxs(Stack, {
|
|
3919
|
-
gap: 0,
|
|
3920
|
-
children: [/*#__PURE__*/jsx(Title, {
|
|
3921
|
-
order: 5,
|
|
3922
|
-
fw: 600,
|
|
3923
|
-
children: title
|
|
3924
|
-
}), /*#__PURE__*/jsx(Text, {
|
|
3925
|
-
size: "xs",
|
|
3926
|
-
c: "dimmed",
|
|
3927
|
-
children: subtitle
|
|
3928
|
-
})]
|
|
3929
|
-
})]
|
|
3930
|
-
}),
|
|
3931
|
-
...containerProps,
|
|
3932
|
-
children: [/*#__PURE__*/jsx(Divider, {
|
|
3933
|
-
mb: "md"
|
|
3934
|
-
}), profileContent]
|
|
4431
|
+
}), /*#__PURE__*/jsx(Modal.Content, {
|
|
4432
|
+
radius: 0,
|
|
4433
|
+
style: {
|
|
4434
|
+
border: '1px solid var(--mantine-color-gray-3)'
|
|
4435
|
+
},
|
|
4436
|
+
children: /*#__PURE__*/jsx(Modal.Body, {
|
|
4437
|
+
p: 0,
|
|
4438
|
+
children: content
|
|
4439
|
+
})
|
|
4440
|
+
})]
|
|
3935
4441
|
});
|
|
3936
4442
|
}
|
|
3937
|
-
|
|
3938
|
-
// Renderizar como Card
|
|
3939
4443
|
return /*#__PURE__*/jsx(Paper, {
|
|
3940
|
-
withBorder: true
|
|
3941
|
-
|
|
3942
|
-
,
|
|
3943
|
-
p: "md",
|
|
4444
|
+
withBorder: true,
|
|
4445
|
+
radius: 0,
|
|
4446
|
+
p: 0,
|
|
3944
4447
|
w: width,
|
|
3945
|
-
|
|
4448
|
+
maw: "100%",
|
|
3946
4449
|
...containerProps,
|
|
3947
|
-
children:
|
|
3948
|
-
gap: "sm",
|
|
3949
|
-
children: [/*#__PURE__*/jsxs(Group, {
|
|
3950
|
-
gap: "sm",
|
|
3951
|
-
children: [logo ? /*#__PURE__*/jsx(Image, {
|
|
3952
|
-
src: logo,
|
|
3953
|
-
alt: "Auth",
|
|
3954
|
-
h: logoHeight,
|
|
3955
|
-
fit: "contain"
|
|
3956
|
-
}) : /*#__PURE__*/jsx(ThemeIcon, {
|
|
3957
|
-
size: 32,
|
|
3958
|
-
variant: "subtle",
|
|
3959
|
-
color: "gray",
|
|
3960
|
-
children: /*#__PURE__*/jsx(IconUserCircle, {
|
|
3961
|
-
size: 24,
|
|
3962
|
-
stroke: 1.5
|
|
3963
|
-
})
|
|
3964
|
-
}), /*#__PURE__*/jsxs(Stack, {
|
|
3965
|
-
gap: 0,
|
|
3966
|
-
children: [/*#__PURE__*/jsx(Title, {
|
|
3967
|
-
order: 5,
|
|
3968
|
-
fw: 600,
|
|
3969
|
-
children: title
|
|
3970
|
-
}), /*#__PURE__*/jsx(Text, {
|
|
3971
|
-
size: "xs",
|
|
3972
|
-
c: "dimmed",
|
|
3973
|
-
children: subtitle
|
|
3974
|
-
})]
|
|
3975
|
-
})]
|
|
3976
|
-
}), /*#__PURE__*/jsx(Divider, {}), profileContent]
|
|
3977
|
-
})
|
|
4450
|
+
children: content
|
|
3978
4451
|
});
|
|
3979
4452
|
}
|
|
3980
4453
|
|
|
4454
|
+
/* The only texts that change with the count, so they are functions, not labels. */
|
|
4455
|
+
function endOthersAction(count) {
|
|
4456
|
+
return count === 1 ? 'Encerrar a outra' : `Encerrar as outras ${count}`;
|
|
4457
|
+
}
|
|
4458
|
+
function endOthersQuestion(count) {
|
|
4459
|
+
return count === 1 ? 'Encerrar 1 sessão?' : `Encerrar ${count} sessões?`;
|
|
4460
|
+
}
|
|
4461
|
+
function endOthersConfirm(count) {
|
|
4462
|
+
return count === 1 ? 'Encerrar 1 sessão' : `Encerrar ${count} sessões`;
|
|
4463
|
+
}
|
|
4464
|
+
|
|
3981
4465
|
const ROLE_LABELS = {
|
|
3982
4466
|
owner: 'Dono',
|
|
3983
4467
|
admin: 'Administrador',
|
|
@@ -4054,17 +4538,13 @@ function UserInformation({
|
|
|
4054
4538
|
...others
|
|
4055
4539
|
}) {
|
|
4056
4540
|
if (!user) return null;
|
|
4057
|
-
const
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
*/
|
|
4065
|
-
const hasRealName = Boolean(name) && name.toLowerCase() !== email.split('@')[0].toLowerCase() && name.toLowerCase() !== email.toLowerCase();
|
|
4066
|
-
const title = hasRealName ? name : email || name;
|
|
4067
|
-
const initials = hasRealName ? name.split(/\s+/).map(part => part[0]).join('').slice(0, 2).toUpperCase() : (email || name).charAt(0).toUpperCase();
|
|
4541
|
+
const {
|
|
4542
|
+
email,
|
|
4543
|
+
hasRealName,
|
|
4544
|
+
title,
|
|
4545
|
+
initials,
|
|
4546
|
+
image
|
|
4547
|
+
} = describeUser(user);
|
|
4068
4548
|
const baseRows = [onAccountClick && {
|
|
4069
4549
|
id: 'account',
|
|
4070
4550
|
label: accountLabel,
|
|
@@ -4088,7 +4568,7 @@ function UserInformation({
|
|
|
4088
4568
|
px: padded ? 16 : 0,
|
|
4089
4569
|
py: 14,
|
|
4090
4570
|
children: [/*#__PURE__*/jsx(Avatar, {
|
|
4091
|
-
src:
|
|
4571
|
+
src: image,
|
|
4092
4572
|
alt: "",
|
|
4093
4573
|
size: 32,
|
|
4094
4574
|
radius: 0,
|