@riligar/auth-react 1.15.0 → 1.17.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.esm.js +367 -89
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +365 -86
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -3,9 +3,9 @@ import { useMemo, useEffect, createContext, useState, useRef } 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, Image, Title, Paper, TextInput, PasswordInput, Anchor, Button, Divider, Group, Center, Loader, Box, Avatar, Collapse, Card, Tooltip, ThemeIcon } from '@mantine/core';
|
|
6
|
+
import { Modal, Stack, Text, Image, Title, Paper, TextInput, PasswordInput, Anchor, Button, Divider, Group, Center, Loader, Box, Avatar, Collapse, Card, Tooltip, ThemeIcon, Badge } from '@mantine/core';
|
|
7
7
|
import { useForm } from '@mantine/form';
|
|
8
|
-
import { IconMail, IconLock, IconArrowRight, IconBrandGoogle, IconBrandGithub, IconUser, IconSend, IconCheck, IconX, IconRefresh, IconPhoto, IconTrash, IconPencil, IconShield, IconKey, IconUserCircle } from '@tabler/icons-react';
|
|
8
|
+
import { IconMail, IconLock, IconArrowRight, IconBrandGoogle, IconBrandGithub, IconUser, IconSend, IconCheck, IconX, IconRefresh, IconPhoto, IconTrash, IconPencil, IconShield, IconKey, IconDevices, IconDeviceMobile, IconLogout, IconUserCircle } from '@tabler/icons-react';
|
|
9
9
|
import { notifications } from '@mantine/notifications';
|
|
10
10
|
|
|
11
11
|
// Config - pode ser sobrescrita pelo AuthProvider
|
|
@@ -22,14 +22,17 @@ const getEnvVar = key => {
|
|
|
22
22
|
};
|
|
23
23
|
let API_BASE = (typeof window !== 'undefined' && window?.location ? getEnvVar('VITE_API_BASE') : null) || 'http://localhost:3000';
|
|
24
24
|
let API_KEY = null;
|
|
25
|
+
let INTERNAL_MODE = false; // Modo interno: não exige API Key (para aplicações same-domain)
|
|
25
26
|
|
|
26
|
-
// Permite configurar a API base e
|
|
27
|
+
// Permite configurar a API base, key e modo interno externamente (chamado pelo AuthProvider)
|
|
27
28
|
function configure({
|
|
28
29
|
apiUrl,
|
|
29
|
-
apiKey
|
|
30
|
+
apiKey,
|
|
31
|
+
internal = false
|
|
30
32
|
}) {
|
|
31
33
|
if (apiUrl) API_BASE = apiUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
32
34
|
if (apiKey) API_KEY = apiKey;
|
|
35
|
+
INTERNAL_MODE = internal;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
// helper fetch pré-configurado
|
|
@@ -51,8 +54,8 @@ async function api(route, opts = {}) {
|
|
|
51
54
|
headers.Authorization = `Bearer ${token}`;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
// Adiciona API Key se configurada
|
|
55
|
-
if (API_KEY) {
|
|
57
|
+
// Adiciona API Key se configurada e não estiver em modo interno
|
|
58
|
+
if (API_KEY && !INTERNAL_MODE) {
|
|
56
59
|
headers['X-API-Key'] = API_KEY;
|
|
57
60
|
}
|
|
58
61
|
const res = await fetch(url, {
|
|
@@ -273,11 +276,11 @@ const getSession = async () => {
|
|
|
273
276
|
const listSessions = async () => {
|
|
274
277
|
return await api('/auth/list-sessions');
|
|
275
278
|
};
|
|
276
|
-
const revokeSession = async
|
|
277
|
-
return await api('/auth/revoke-session', {
|
|
279
|
+
const revokeSession = async id => {
|
|
280
|
+
return await api('/auth/revoke-session-by-id', {
|
|
278
281
|
method: 'POST',
|
|
279
282
|
body: JSON.stringify({
|
|
280
|
-
|
|
283
|
+
id
|
|
281
284
|
})
|
|
282
285
|
});
|
|
283
286
|
};
|
|
@@ -339,7 +342,7 @@ const useAuthStore = create((set, get) => ({
|
|
|
339
342
|
error: null,
|
|
340
343
|
// Session management
|
|
341
344
|
sessions: [],
|
|
342
|
-
|
|
345
|
+
currentSession: null,
|
|
343
346
|
// Loading states granulares
|
|
344
347
|
loadingStates: {
|
|
345
348
|
signIn: false,
|
|
@@ -397,9 +400,14 @@ const useAuthStore = create((set, get) => ({
|
|
|
397
400
|
// Precisamos buscar a sessão no servidor.
|
|
398
401
|
if (!user) {
|
|
399
402
|
try {
|
|
400
|
-
const
|
|
401
|
-
if (
|
|
402
|
-
user =
|
|
403
|
+
const sessionData = await getSession();
|
|
404
|
+
if (sessionData?.user) {
|
|
405
|
+
user = sessionData.user;
|
|
406
|
+
}
|
|
407
|
+
if (sessionData?.session) {
|
|
408
|
+
set({
|
|
409
|
+
currentSession: sessionData.session
|
|
410
|
+
});
|
|
403
411
|
}
|
|
404
412
|
} catch (sessionError) {
|
|
405
413
|
console.warn('[AuthStore] Failed to fetch session:', sessionError);
|
|
@@ -472,8 +480,13 @@ const useAuthStore = create((set, get) => ({
|
|
|
472
480
|
|
|
473
481
|
// Se não encontrou no token (sessão opaca), busca do servidor
|
|
474
482
|
if (!user) {
|
|
475
|
-
const
|
|
476
|
-
if (
|
|
483
|
+
const sessionData = await getSession();
|
|
484
|
+
if (sessionData?.user) user = sessionData.user;
|
|
485
|
+
if (sessionData?.session) {
|
|
486
|
+
set({
|
|
487
|
+
currentSession: sessionData.session
|
|
488
|
+
});
|
|
489
|
+
}
|
|
477
490
|
}
|
|
478
491
|
set({
|
|
479
492
|
user,
|
|
@@ -655,14 +668,14 @@ const useAuthStore = create((set, get) => ({
|
|
|
655
668
|
/* Session */
|
|
656
669
|
getSession: async () => {
|
|
657
670
|
try {
|
|
658
|
-
const
|
|
659
|
-
// Store current session
|
|
660
|
-
if (
|
|
671
|
+
const sessionData = await getSession();
|
|
672
|
+
// Store current session for comparison (includes id)
|
|
673
|
+
if (sessionData?.session) {
|
|
661
674
|
set({
|
|
662
|
-
|
|
675
|
+
currentSession: sessionData.session
|
|
663
676
|
});
|
|
664
677
|
}
|
|
665
|
-
return
|
|
678
|
+
return sessionData;
|
|
666
679
|
} catch (err) {
|
|
667
680
|
set({
|
|
668
681
|
error: err
|
|
@@ -874,11 +887,14 @@ function AuthProvider({
|
|
|
874
887
|
apiUrl,
|
|
875
888
|
// URL base da API (OBRIGATÓRIA)
|
|
876
889
|
apiKey,
|
|
877
|
-
// API Key para header X-API-Key (
|
|
890
|
+
// API Key para header X-API-Key (obrigatória exceto em modo internal)
|
|
891
|
+
internal = false,
|
|
892
|
+
// Modo interno: não exige API Key (para aplicações same-domain como dashboard)
|
|
878
893
|
onError // Callback de erro global
|
|
879
894
|
}) {
|
|
880
895
|
// Validação de props obrigatórias
|
|
881
|
-
|
|
896
|
+
// apiKey só é obrigatória se não estiver em modo internal
|
|
897
|
+
if (!internal && !apiKey) {
|
|
882
898
|
throw new Error('[@riligar/auth-react] apiKey é obrigatória no AuthProvider. ' + 'Obtenha sua API Key no dashboard em https://dashboard.myauth.click');
|
|
883
899
|
}
|
|
884
900
|
if (!apiUrl) {
|
|
@@ -888,15 +904,15 @@ function AuthProvider({
|
|
|
888
904
|
const startRefresh = useAuthStore(s => s.startRefresh);
|
|
889
905
|
const checkTokenValidity = useAuthStore(s => s.checkTokenValidity);
|
|
890
906
|
|
|
891
|
-
// Configura SDK com apiUrl e
|
|
892
|
-
// Configura SDK com apiUrl e apiKey
|
|
907
|
+
// Configura SDK com apiUrl, apiKey e modo interno
|
|
893
908
|
// Usamos useMemo para garantir que a configuração ocorra ANTES dos efeitos dos componentes filhos
|
|
894
909
|
useMemo(() => {
|
|
895
910
|
configure({
|
|
896
911
|
apiUrl,
|
|
897
|
-
apiKey
|
|
912
|
+
apiKey,
|
|
913
|
+
internal
|
|
898
914
|
});
|
|
899
|
-
}, [apiUrl, apiKey]);
|
|
915
|
+
}, [apiUrl, apiKey, internal]);
|
|
900
916
|
useEffect(() => {
|
|
901
917
|
init();
|
|
902
918
|
startRefresh();
|
|
@@ -914,8 +930,24 @@ function AuthProvider({
|
|
|
914
930
|
checkTokenValidity();
|
|
915
931
|
}
|
|
916
932
|
};
|
|
933
|
+
|
|
934
|
+
// Escuta evento de sessão revogada (quando o usuário revoga sua própria sessão)
|
|
935
|
+
const handleSessionRevoked = () => {
|
|
936
|
+
// Limpa o usuário do store
|
|
937
|
+
useAuthStore.setState({
|
|
938
|
+
user: null,
|
|
939
|
+
currentSession: null,
|
|
940
|
+
sessions: []
|
|
941
|
+
});
|
|
942
|
+
// Dispara evento de logout para sincronizar entre abas
|
|
943
|
+
localStorage.setItem('auth:logout', Date.now());
|
|
944
|
+
};
|
|
917
945
|
window.addEventListener('storage', handleStorageChange);
|
|
918
|
-
|
|
946
|
+
window.addEventListener('auth:session-revoked', handleSessionRevoked);
|
|
947
|
+
return () => {
|
|
948
|
+
window.removeEventListener('storage', handleStorageChange);
|
|
949
|
+
window.removeEventListener('auth:session-revoked', handleSessionRevoked);
|
|
950
|
+
};
|
|
919
951
|
}, [checkTokenValidity]);
|
|
920
952
|
|
|
921
953
|
// Verifica validade do token periodicamente
|
|
@@ -1003,6 +1035,19 @@ const useUser = () => useAuthStore(useShallow(s => ({
|
|
|
1003
1035
|
// Alias deprecado para backwards compatibility
|
|
1004
1036
|
const useProfile = useUser;
|
|
1005
1037
|
|
|
1038
|
+
// Sessions Management Hook
|
|
1039
|
+
const useSessions = () => useAuthStore(useShallow(s => ({
|
|
1040
|
+
currentSession: s.currentSession,
|
|
1041
|
+
sessions: s.sessions,
|
|
1042
|
+
getSession: s.getSession,
|
|
1043
|
+
listSessions: s.listSessions,
|
|
1044
|
+
revokeSession: s.revokeSession,
|
|
1045
|
+
revokeOtherSessions: s.revokeOtherSessions,
|
|
1046
|
+
loadingListSessions: s.loadingStates.listSessions,
|
|
1047
|
+
loadingRevokeSession: s.loadingStates.revokeSession,
|
|
1048
|
+
error: s.error
|
|
1049
|
+
})));
|
|
1050
|
+
|
|
1006
1051
|
// Application Logo Hook
|
|
1007
1052
|
const useApplicationLogo = () => {
|
|
1008
1053
|
const applicationInfo = useAuthStore(s => s.applicationInfo);
|
|
@@ -2032,6 +2077,7 @@ function UserProfile({
|
|
|
2032
2077
|
showName = true,
|
|
2033
2078
|
showEmail = true,
|
|
2034
2079
|
showPassword = true,
|
|
2080
|
+
showSessions = true,
|
|
2035
2081
|
// Customização
|
|
2036
2082
|
labels = {},
|
|
2037
2083
|
title = 'Account',
|
|
@@ -2059,6 +2105,111 @@ function UserProfile({
|
|
|
2059
2105
|
loadingChangeEmail
|
|
2060
2106
|
} = useProfile();
|
|
2061
2107
|
|
|
2108
|
+
// Hook para sessions
|
|
2109
|
+
const {
|
|
2110
|
+
currentSession,
|
|
2111
|
+
sessions,
|
|
2112
|
+
listSessions,
|
|
2113
|
+
getSession,
|
|
2114
|
+
revokeSession,
|
|
2115
|
+
revokeOtherSessions,
|
|
2116
|
+
loadingListSessions,
|
|
2117
|
+
loadingRevokeSession
|
|
2118
|
+
} = useSessions();
|
|
2119
|
+
|
|
2120
|
+
// Load sessions when opened (modal) or component mounts (card), and when sessions section is opened
|
|
2121
|
+
useEffect(() => {
|
|
2122
|
+
if (showSessions && (variant === 'card' || opened)) {
|
|
2123
|
+
// Fetch current session first to get the session ID, then list all sessions
|
|
2124
|
+
getSession().catch(err => console.warn('Failed to get current session:', err));
|
|
2125
|
+
listSessions().catch(err => console.warn('Failed to load sessions:', err));
|
|
2126
|
+
}
|
|
2127
|
+
}, [opened, showSessions, variant]);
|
|
2128
|
+
|
|
2129
|
+
// Helper to parse user agent string
|
|
2130
|
+
const parseUserAgent = ua => {
|
|
2131
|
+
if (!ua) return {
|
|
2132
|
+
browser: 'Unknown Browser',
|
|
2133
|
+
os: 'Unknown OS'
|
|
2134
|
+
};
|
|
2135
|
+
let browser = 'Unknown Browser';
|
|
2136
|
+
let os = 'Unknown OS';
|
|
2137
|
+
|
|
2138
|
+
// Detect browser
|
|
2139
|
+
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';
|
|
2140
|
+
|
|
2141
|
+
// Detect OS
|
|
2142
|
+
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';
|
|
2143
|
+
return {
|
|
2144
|
+
browser,
|
|
2145
|
+
os
|
|
2146
|
+
};
|
|
2147
|
+
};
|
|
2148
|
+
|
|
2149
|
+
// Session handlers
|
|
2150
|
+
const handleRevokeSession = async sessionId => {
|
|
2151
|
+
// Check if revoking current session
|
|
2152
|
+
const isCurrentSession = sessionId === currentSession?.id || sessions.length === 1;
|
|
2153
|
+
|
|
2154
|
+
// If revoking current session, handle logout immediately after revocation
|
|
2155
|
+
if (isCurrentSession) {
|
|
2156
|
+
try {
|
|
2157
|
+
await revokeSession(sessionId);
|
|
2158
|
+
} catch (error) {
|
|
2159
|
+
// Even if it fails, we're revoking our own session, so just logout
|
|
2160
|
+
// The server already revoked our session
|
|
2161
|
+
}
|
|
2162
|
+
// Clear auth state and redirect
|
|
2163
|
+
localStorage.removeItem('auth:token');
|
|
2164
|
+
window.dispatchEvent(new CustomEvent('auth:session-revoked'));
|
|
2165
|
+
if (variant === 'modal') onClose?.();
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// Revoking another session
|
|
2170
|
+
try {
|
|
2171
|
+
await revokeSession(sessionId);
|
|
2172
|
+
notifications.show({
|
|
2173
|
+
title: labels.successTitle || 'Sucesso',
|
|
2174
|
+
message: labels.sessionRevoked || 'Sessão encerrada com sucesso',
|
|
2175
|
+
color: 'green'
|
|
2176
|
+
});
|
|
2177
|
+
} catch (error) {
|
|
2178
|
+
// Check for 401 error (our SDK uses error.res, axios uses error.response)
|
|
2179
|
+
const status = error.res?.status || error.response?.status;
|
|
2180
|
+
if (status === 401) {
|
|
2181
|
+
// This means our session was revoked, not the target one - do logout
|
|
2182
|
+
localStorage.removeItem('auth:token');
|
|
2183
|
+
window.dispatchEvent(new CustomEvent('auth:session-revoked'));
|
|
2184
|
+
if (variant === 'modal') onClose?.();
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
notifications.show({
|
|
2188
|
+
title: labels.errorTitle || 'Erro',
|
|
2189
|
+
message: error.message || labels.sessionRevokeFailed || 'Falha ao encerrar sessão',
|
|
2190
|
+
color: 'red'
|
|
2191
|
+
});
|
|
2192
|
+
onError?.(error);
|
|
2193
|
+
}
|
|
2194
|
+
};
|
|
2195
|
+
const handleRevokeOtherSessions = async () => {
|
|
2196
|
+
try {
|
|
2197
|
+
await revokeOtherSessions();
|
|
2198
|
+
notifications.show({
|
|
2199
|
+
title: labels.successTitle || 'Sucesso',
|
|
2200
|
+
message: labels.otherSessionsRevoked || 'Todas as outras sessões foram encerradas',
|
|
2201
|
+
color: 'green'
|
|
2202
|
+
});
|
|
2203
|
+
} catch (error) {
|
|
2204
|
+
notifications.show({
|
|
2205
|
+
title: labels.errorTitle || 'Erro',
|
|
2206
|
+
message: error.message || labels.otherSessionsRevokeFailed || 'Falha ao encerrar sessões',
|
|
2207
|
+
color: 'red'
|
|
2208
|
+
});
|
|
2209
|
+
onError?.(error);
|
|
2210
|
+
}
|
|
2211
|
+
};
|
|
2212
|
+
|
|
2062
2213
|
// Password form
|
|
2063
2214
|
const passwordForm = useForm({
|
|
2064
2215
|
initialValues: {
|
|
@@ -2590,7 +2741,7 @@ function UserProfile({
|
|
|
2590
2741
|
})]
|
|
2591
2742
|
})]
|
|
2592
2743
|
})]
|
|
2593
|
-
}), showPassword && /*#__PURE__*/jsxs(Box, {
|
|
2744
|
+
}), (showPassword || showSessions) && /*#__PURE__*/jsxs(Box, {
|
|
2594
2745
|
mb: "md",
|
|
2595
2746
|
children: [/*#__PURE__*/jsx(SectionHeader, {
|
|
2596
2747
|
icon: IconShield,
|
|
@@ -2598,69 +2749,196 @@ function UserProfile({
|
|
|
2598
2749
|
description: labels.securityDescription || 'Protect your account'
|
|
2599
2750
|
}), /*#__PURE__*/jsxs(Stack, {
|
|
2600
2751
|
gap: "sm",
|
|
2601
|
-
children: [/*#__PURE__*/
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2752
|
+
children: [showPassword && /*#__PURE__*/jsxs(Fragment, {
|
|
2753
|
+
children: [/*#__PURE__*/jsx(SettingRow, {
|
|
2754
|
+
label: labels.password || 'Password',
|
|
2755
|
+
action: labels.change || 'Change',
|
|
2756
|
+
actionLabel: labels.changePassword || 'Change your password',
|
|
2757
|
+
onClick: () => handleToggleSection('password'),
|
|
2758
|
+
expanded: editingSection === 'password',
|
|
2759
|
+
children: /*#__PURE__*/jsxs(Group, {
|
|
2760
|
+
gap: "xs",
|
|
2761
|
+
children: [/*#__PURE__*/jsx(IconKey, {
|
|
2762
|
+
size: 16,
|
|
2763
|
+
color: "var(--mantine-color-dimmed)"
|
|
2764
|
+
}), /*#__PURE__*/jsx(Text, {
|
|
2765
|
+
size: "sm",
|
|
2766
|
+
c: "dimmed",
|
|
2767
|
+
children: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
|
|
2768
|
+
})]
|
|
2769
|
+
})
|
|
2770
|
+
}), /*#__PURE__*/jsx(Collapse, {
|
|
2771
|
+
in: editingSection === 'password',
|
|
2772
|
+
children: /*#__PURE__*/jsx(Card, {
|
|
2773
|
+
p: "md",
|
|
2774
|
+
withBorder: true,
|
|
2775
|
+
children: /*#__PURE__*/jsx("form", {
|
|
2776
|
+
onSubmit: passwordForm.onSubmit(handleChangePassword),
|
|
2777
|
+
children: /*#__PURE__*/jsxs(Stack, {
|
|
2778
|
+
gap: "sm",
|
|
2779
|
+
children: [/*#__PURE__*/jsx(PasswordInput, {
|
|
2780
|
+
label: labels.currentPassword || 'Senha Atual',
|
|
2781
|
+
placeholder: labels.currentPasswordPlaceholder || 'Digite sua senha atual',
|
|
2782
|
+
...passwordForm.getInputProps('currentPassword')
|
|
2783
|
+
}), /*#__PURE__*/jsx(PasswordInput, {
|
|
2784
|
+
label: labels.newPassword || 'Nova Senha',
|
|
2785
|
+
placeholder: labels.newPasswordPlaceholder || 'Digite a nova senha',
|
|
2786
|
+
description: labels.passwordDescription || 'Mínimo 8 caracteres',
|
|
2787
|
+
...passwordForm.getInputProps('newPassword')
|
|
2788
|
+
}), /*#__PURE__*/jsx(PasswordInput, {
|
|
2789
|
+
label: labels.confirmPassword || 'Confirmar Nova Senha',
|
|
2790
|
+
placeholder: labels.confirmPasswordPlaceholder || 'Confirme a nova senha',
|
|
2791
|
+
...passwordForm.getInputProps('confirmPassword')
|
|
2792
|
+
}), /*#__PURE__*/jsxs(Group, {
|
|
2793
|
+
justify: "flex-end",
|
|
2794
|
+
gap: "xs",
|
|
2795
|
+
children: [/*#__PURE__*/jsx(Button, {
|
|
2796
|
+
variant: "default",
|
|
2797
|
+
size: "xs",
|
|
2798
|
+
onClick: () => handleToggleSection('password'),
|
|
2799
|
+
leftSection: /*#__PURE__*/jsx(IconX, {
|
|
2800
|
+
size: 14
|
|
2801
|
+
}),
|
|
2802
|
+
children: labels.cancel || 'Cancelar'
|
|
2803
|
+
}), /*#__PURE__*/jsx(Button, {
|
|
2804
|
+
type: "submit",
|
|
2805
|
+
size: "xs",
|
|
2806
|
+
loading: loadingChangePassword,
|
|
2807
|
+
leftSection: /*#__PURE__*/jsx(IconCheck, {
|
|
2808
|
+
size: 14
|
|
2809
|
+
}),
|
|
2810
|
+
children: labels.save || 'Salvar'
|
|
2811
|
+
})]
|
|
2659
2812
|
})]
|
|
2660
|
-
})
|
|
2813
|
+
})
|
|
2661
2814
|
})
|
|
2662
2815
|
})
|
|
2663
|
-
})
|
|
2816
|
+
})]
|
|
2817
|
+
}), showSessions && /*#__PURE__*/jsxs(Fragment, {
|
|
2818
|
+
children: [/*#__PURE__*/jsx(SettingRow, {
|
|
2819
|
+
label: labels.sessions || 'Sessions',
|
|
2820
|
+
action: editingSection === 'sessions' ? labels.close || 'Close' : labels.manage || 'Manage',
|
|
2821
|
+
actionLabel: labels.manageSessions || 'Manage your active sessions',
|
|
2822
|
+
onClick: () => handleToggleSection('sessions'),
|
|
2823
|
+
expanded: editingSection === 'sessions',
|
|
2824
|
+
children: /*#__PURE__*/jsxs(Group, {
|
|
2825
|
+
gap: "xs",
|
|
2826
|
+
children: [/*#__PURE__*/jsx(IconDevices, {
|
|
2827
|
+
size: 16,
|
|
2828
|
+
color: "var(--mantine-color-dimmed)"
|
|
2829
|
+
}), /*#__PURE__*/jsx(Text, {
|
|
2830
|
+
size: "sm",
|
|
2831
|
+
c: "dimmed",
|
|
2832
|
+
children: sessions.length > 0 ? `${sessions.length} active session${sessions.length > 1 ? 's' : ''}` : loadingListSessions ? 'Loading...' : 'No sessions'
|
|
2833
|
+
})]
|
|
2834
|
+
})
|
|
2835
|
+
}), /*#__PURE__*/jsx(Collapse, {
|
|
2836
|
+
in: editingSection === 'sessions',
|
|
2837
|
+
children: /*#__PURE__*/jsx(Card, {
|
|
2838
|
+
p: "md",
|
|
2839
|
+
withBorder: true,
|
|
2840
|
+
children: /*#__PURE__*/jsx(Stack, {
|
|
2841
|
+
gap: "md",
|
|
2842
|
+
children: loadingListSessions ? /*#__PURE__*/jsx(Text, {
|
|
2843
|
+
size: "sm",
|
|
2844
|
+
c: "dimmed",
|
|
2845
|
+
ta: "center",
|
|
2846
|
+
children: labels.loadingSessions || 'Carregando sessões...'
|
|
2847
|
+
}) : sessions.length === 0 ? /*#__PURE__*/jsx(Text, {
|
|
2848
|
+
size: "sm",
|
|
2849
|
+
c: "dimmed",
|
|
2850
|
+
ta: "center",
|
|
2851
|
+
children: labels.noSessionsFound || 'Nenhuma sessão encontrada'
|
|
2852
|
+
}) : /*#__PURE__*/jsxs(Fragment, {
|
|
2853
|
+
children: [sessions.map(sessionItem => {
|
|
2854
|
+
const isCurrentSession = sessionItem.id === currentSession?.id;
|
|
2855
|
+
const deviceInfo = parseUserAgent(sessionItem.userAgent);
|
|
2856
|
+
const createdDate = new Date(sessionItem.createdAt);
|
|
2857
|
+
return /*#__PURE__*/jsx(Card, {
|
|
2858
|
+
p: "sm",
|
|
2859
|
+
style: {
|
|
2860
|
+
borderColor: isCurrentSession ? 'var(--mantine-color-blue-5)' : undefined,
|
|
2861
|
+
backgroundColor: isCurrentSession ? 'var(--mantine-color-blue-light)' : undefined
|
|
2862
|
+
},
|
|
2863
|
+
children: /*#__PURE__*/jsxs(Group, {
|
|
2864
|
+
justify: "space-between",
|
|
2865
|
+
wrap: "nowrap",
|
|
2866
|
+
children: [/*#__PURE__*/jsxs(Group, {
|
|
2867
|
+
gap: "sm",
|
|
2868
|
+
wrap: "nowrap",
|
|
2869
|
+
style: {
|
|
2870
|
+
flex: 1
|
|
2871
|
+
},
|
|
2872
|
+
children: [/*#__PURE__*/jsx(ThemeIcon, {
|
|
2873
|
+
size: 36,
|
|
2874
|
+
variant: "light",
|
|
2875
|
+
color: isCurrentSession ? 'blue' : 'gray',
|
|
2876
|
+
children: /*#__PURE__*/jsx(IconDeviceMobile, {
|
|
2877
|
+
size: 18
|
|
2878
|
+
})
|
|
2879
|
+
}), /*#__PURE__*/jsxs(Box, {
|
|
2880
|
+
style: {
|
|
2881
|
+
flex: 1
|
|
2882
|
+
},
|
|
2883
|
+
children: [/*#__PURE__*/jsxs(Group, {
|
|
2884
|
+
gap: "xs",
|
|
2885
|
+
children: [/*#__PURE__*/jsx(Text, {
|
|
2886
|
+
size: "sm",
|
|
2887
|
+
fw: 500,
|
|
2888
|
+
children: deviceInfo.browser
|
|
2889
|
+
}), isCurrentSession && /*#__PURE__*/jsx(Badge, {
|
|
2890
|
+
size: "xs",
|
|
2891
|
+
variant: "filled",
|
|
2892
|
+
color: "blue",
|
|
2893
|
+
children: labels.thisDevice || 'Este dispositivo'
|
|
2894
|
+
})]
|
|
2895
|
+
}), /*#__PURE__*/jsxs(Text, {
|
|
2896
|
+
size: "xs",
|
|
2897
|
+
c: "dimmed",
|
|
2898
|
+
children: [deviceInfo.os, " \u2022 ", sessionItem.ipAddress || labels.unknownIP || 'IP desconhecido']
|
|
2899
|
+
}), /*#__PURE__*/jsxs(Text, {
|
|
2900
|
+
size: "xs",
|
|
2901
|
+
c: "dimmed",
|
|
2902
|
+
children: [labels.createdAt || 'Criada em', " ", createdDate.toLocaleDateString('pt-BR'), " ", labels.at || 'às', ' ', createdDate.toLocaleTimeString('pt-BR', {
|
|
2903
|
+
hour: '2-digit',
|
|
2904
|
+
minute: '2-digit'
|
|
2905
|
+
})]
|
|
2906
|
+
})]
|
|
2907
|
+
})]
|
|
2908
|
+
}), /*#__PURE__*/jsx(Tooltip, {
|
|
2909
|
+
label: isCurrentSession ? labels.signOutAndEnd || 'Encerrar e sair' : labels.endSession || 'Encerrar sessão',
|
|
2910
|
+
children: /*#__PURE__*/jsx(Button, {
|
|
2911
|
+
variant: "light",
|
|
2912
|
+
color: "red",
|
|
2913
|
+
size: "xs",
|
|
2914
|
+
onClick: () => handleRevokeSession(sessionItem.id),
|
|
2915
|
+
loading: loadingRevokeSession,
|
|
2916
|
+
leftSection: /*#__PURE__*/jsx(IconLogout, {
|
|
2917
|
+
size: 14
|
|
2918
|
+
}),
|
|
2919
|
+
children: labels.end || 'Encerrar'
|
|
2920
|
+
})
|
|
2921
|
+
})]
|
|
2922
|
+
})
|
|
2923
|
+
}, sessionItem.id);
|
|
2924
|
+
}), sessions.length > 1 && /*#__PURE__*/jsx(Group, {
|
|
2925
|
+
justify: "flex-end",
|
|
2926
|
+
children: /*#__PURE__*/jsx(Button, {
|
|
2927
|
+
variant: "light",
|
|
2928
|
+
color: "red",
|
|
2929
|
+
size: "xs",
|
|
2930
|
+
onClick: handleRevokeOtherSessions,
|
|
2931
|
+
loading: loadingRevokeSession,
|
|
2932
|
+
leftSection: /*#__PURE__*/jsx(IconLogout, {
|
|
2933
|
+
size: 14
|
|
2934
|
+
}),
|
|
2935
|
+
children: labels.endOtherSessions || 'Encerrar todas as outras sessões'
|
|
2936
|
+
})
|
|
2937
|
+
})]
|
|
2938
|
+
})
|
|
2939
|
+
})
|
|
2940
|
+
})
|
|
2941
|
+
})]
|
|
2664
2942
|
})]
|
|
2665
2943
|
})]
|
|
2666
2944
|
})]
|
|
@@ -2844,5 +3122,5 @@ function SignOutButton({
|
|
|
2844
3122
|
});
|
|
2845
3123
|
}
|
|
2846
3124
|
|
|
2847
|
-
export { UserProfile as AccountModal, AuthCard, AuthLoaded, AuthLoading, AuthProvider, ForgotPassword, ForgotPassword as ForgotPasswordForm, MagicLink, MagicLinkCallback, MagicLink as MagicLinkForm, MagicLinkCallback as MagicLinkVerify, Protect, Protect as ProtectedRoute, ResetPassword, ResetPassword as ResetPasswordForm, SignIn, SignInButton, SignIn as SignInForm, SignOutButton, SignUp, SignUpButton, SignUp as SignUpForm, SignedIn, SignedOut, UserProfile, VerifyEmail, VerifyEmail as VerifyEmailCard, changeEmail, changePassword, configure, decodeJWT, forgotPassword, getApplicationInfo, getCurrentUser, getSession, isAuthenticated, listSessions, refreshToken, resendVerification, resetPassword, revokeOtherSessions, revokeSession, sendMagicLink, signIn, signOut, signUp, socialRedirect, updateProfile, useApplicationLogo, useAuth, useAuthLoading, useAuthStore, useCheckToken, useEmailVerification, useMagicLink, usePasswordReset, useUser as useProfile, useSession, useSignIn, useSignOut, useSignUp, useUser, verifyEmail, verifyMagicLink };
|
|
3125
|
+
export { UserProfile as AccountModal, AuthCard, AuthLoaded, AuthLoading, AuthProvider, ForgotPassword, ForgotPassword as ForgotPasswordForm, MagicLink, MagicLinkCallback, MagicLink as MagicLinkForm, MagicLinkCallback as MagicLinkVerify, Protect, Protect as ProtectedRoute, ResetPassword, ResetPassword as ResetPasswordForm, SignIn, SignInButton, SignIn as SignInForm, SignOutButton, SignUp, SignUpButton, SignUp as SignUpForm, SignedIn, SignedOut, UserProfile, VerifyEmail, VerifyEmail as VerifyEmailCard, changeEmail, changePassword, configure, decodeJWT, forgotPassword, getApplicationInfo, getCurrentUser, getSession, isAuthenticated, listSessions, refreshToken, resendVerification, resetPassword, revokeOtherSessions, revokeSession, sendMagicLink, signIn, signOut, signUp, socialRedirect, updateProfile, useApplicationLogo, useAuth, useAuthLoading, useAuthStore, useCheckToken, useEmailVerification, useMagicLink, usePasswordReset, useUser as useProfile, useSession, useSessions, useSignIn, useSignOut, useSignUp, useUser, verifyEmail, verifyMagicLink };
|
|
2848
3126
|
//# sourceMappingURL=index.esm.js.map
|