@riligar/auth-react 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +1294 -505
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1319 -508
- package/dist/index.js.map +1 -1
- package/package.json +37 -7
package/dist/index.esm.js
CHANGED
|
@@ -1,14 +1,44 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React$1, { useEffect, useMemo, createContext, useState } from 'react';
|
|
2
2
|
import { Navigate, Outlet } from 'react-router-dom';
|
|
3
|
+
import { Paper, Stack, Image, Title, Text, TextInput, PasswordInput, Anchor, Button, Divider, Group, Center, Loader } from '@mantine/core';
|
|
4
|
+
import { useForm } from '@mantine/form';
|
|
5
|
+
import { IconArrowRight, IconMail, IconBrandGoogle, IconBrandGithub, IconLock, IconUser, IconSend, IconCheck, IconX, IconRefresh } from '@tabler/icons-react';
|
|
3
6
|
|
|
4
|
-
//
|
|
5
|
-
|
|
7
|
+
// Config - pode ser sobrescrita pelo AuthProvider
|
|
8
|
+
// Tenta detectar ambiente Vite, mas falha graciosamente se não existir
|
|
9
|
+
const getEnvVar = key => {
|
|
10
|
+
try {
|
|
11
|
+
if (typeof import.meta !== 'undefined' && import.meta.env && import.meta.env[key]) {
|
|
12
|
+
return import.meta.env[key];
|
|
13
|
+
}
|
|
14
|
+
} catch (e) {
|
|
15
|
+
// ignore
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
let API_BASE = (typeof window !== 'undefined' && window?.location ? getEnvVar('VITE_API_BASE') : null) || 'http://localhost:3000';
|
|
20
|
+
let API_KEY = null;
|
|
21
|
+
|
|
22
|
+
// Permite configurar a API base e key externamente (chamado pelo AuthProvider)
|
|
23
|
+
function configure({
|
|
24
|
+
apiUrl,
|
|
25
|
+
apiKey
|
|
26
|
+
}) {
|
|
27
|
+
if (apiUrl) API_BASE = apiUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
28
|
+
if (apiKey) API_KEY = apiKey;
|
|
29
|
+
}
|
|
6
30
|
|
|
7
31
|
// helper fetch pré-configurado
|
|
8
32
|
async function api(route, opts = {}) {
|
|
33
|
+
// Garante que a rota comece com /
|
|
34
|
+
const cleanRoute = route.startsWith('/') ? route : `/${route}`;
|
|
35
|
+
|
|
36
|
+
// Constrói URL completa (API_BASE já teve trailing slash removido no configure)
|
|
37
|
+
const url = `${API_BASE}${cleanRoute}`;
|
|
9
38
|
const token = getStoredToken();
|
|
10
39
|
const headers = {
|
|
11
|
-
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
Accept: 'application/json',
|
|
12
42
|
...opts.headers
|
|
13
43
|
};
|
|
14
44
|
|
|
@@ -16,7 +46,12 @@ async function api(route, opts = {}) {
|
|
|
16
46
|
if (token) {
|
|
17
47
|
headers.Authorization = `Bearer ${token}`;
|
|
18
48
|
}
|
|
19
|
-
|
|
49
|
+
|
|
50
|
+
// Adiciona API Key se configurada
|
|
51
|
+
if (API_KEY) {
|
|
52
|
+
headers['X-API-Key'] = API_KEY;
|
|
53
|
+
}
|
|
54
|
+
const res = await fetch(url, {
|
|
20
55
|
headers,
|
|
21
56
|
...opts
|
|
22
57
|
});
|
|
@@ -43,14 +78,38 @@ function setStoredToken(token) {
|
|
|
43
78
|
window.localStorage.removeItem('auth:token');
|
|
44
79
|
}
|
|
45
80
|
}
|
|
81
|
+
// Helper para processar resposta de autenticação e salvar token
|
|
82
|
+
function handleAuthResponse(result) {
|
|
83
|
+
// Tenta encontrar o token em vários lugares possíveis
|
|
84
|
+
const token = result.token || result.session?.token || result.session?.sessionToken;
|
|
85
|
+
console.log('[AuthSDK] Handling Auth Response:', {
|
|
86
|
+
hasToken: !!result.token,
|
|
87
|
+
hasSessionToken: !!result.session?.token,
|
|
88
|
+
hasSessionSessionToken: !!result.session?.sessionToken,
|
|
89
|
+
extractedToken: token
|
|
90
|
+
});
|
|
91
|
+
if (token) {
|
|
92
|
+
console.log('[AuthSDK] Saving token to storage');
|
|
93
|
+
setStoredToken(token);
|
|
94
|
+
} else {
|
|
95
|
+
console.warn('[AuthSDK] No token found in response', result);
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
46
99
|
|
|
47
100
|
// Decodifica JWT (apenas payload, sem verificação de assinatura)
|
|
48
101
|
function decodeJWT(token) {
|
|
49
102
|
try {
|
|
50
103
|
const parts = token.split('.');
|
|
51
104
|
if (parts.length !== 3) return null;
|
|
52
|
-
|
|
53
|
-
|
|
105
|
+
|
|
106
|
+
// Safe base64 decode
|
|
107
|
+
const base64Url = parts[1];
|
|
108
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
109
|
+
|
|
110
|
+
// Verifica ambiente para decodificar
|
|
111
|
+
const jsonPayload = typeof window !== 'undefined' ? window.atob(base64) : Buffer.from(base64, 'base64').toString();
|
|
112
|
+
return JSON.parse(jsonPayload);
|
|
54
113
|
} catch {
|
|
55
114
|
return null;
|
|
56
115
|
}
|
|
@@ -59,14 +118,33 @@ function decodeJWT(token) {
|
|
|
59
118
|
// Verifica se o token está expirado
|
|
60
119
|
function isTokenExpired(token) {
|
|
61
120
|
const payload = decodeJWT(token);
|
|
62
|
-
|
|
63
|
-
|
|
121
|
+
|
|
122
|
+
// Se não for um JWT válido (payload null), assumimos que é um token opaco (session token)
|
|
123
|
+
// Nesse caso, deixamos o servidor validar via 401
|
|
124
|
+
if (!payload) return false;
|
|
125
|
+
if (!payload.exp) return false;
|
|
126
|
+
const now = Date.now();
|
|
127
|
+
const exp = payload.exp * 1000;
|
|
128
|
+
const isExpired = now >= exp;
|
|
129
|
+
if (isExpired) {
|
|
130
|
+
console.log('[AuthSDK] Token expired:', {
|
|
131
|
+
now,
|
|
132
|
+
exp,
|
|
133
|
+
diff: exp - now
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return isExpired;
|
|
64
137
|
}
|
|
65
138
|
|
|
66
139
|
// Verifica se o usuário está autenticado
|
|
67
140
|
function isAuthenticated() {
|
|
68
141
|
const token = getStoredToken();
|
|
69
|
-
|
|
142
|
+
const valid = token && !isTokenExpired(token);
|
|
143
|
+
console.log('[AuthSDK] isAuthenticated check:', {
|
|
144
|
+
hasToken: !!token,
|
|
145
|
+
valid
|
|
146
|
+
});
|
|
147
|
+
return valid;
|
|
70
148
|
}
|
|
71
149
|
|
|
72
150
|
// Obtém dados do usuário do token
|
|
@@ -82,37 +160,32 @@ function getCurrentUser() {
|
|
|
82
160
|
} : null;
|
|
83
161
|
}
|
|
84
162
|
|
|
85
|
-
/*--- métodos de autenticação-------------------*/
|
|
86
|
-
const signUp = async (email, password) => {
|
|
87
|
-
const result = await api(
|
|
88
|
-
method:
|
|
163
|
+
/*--- métodos de autenticação -------------------*/
|
|
164
|
+
const signUp = async (email, password, name = '') => {
|
|
165
|
+
const result = await api('/auth/sign-up/email', {
|
|
166
|
+
method: 'POST',
|
|
89
167
|
body: JSON.stringify({
|
|
90
168
|
email,
|
|
91
|
-
password
|
|
169
|
+
password,
|
|
170
|
+
name
|
|
92
171
|
})
|
|
93
172
|
});
|
|
94
|
-
|
|
95
|
-
setStoredToken(result.token);
|
|
96
|
-
}
|
|
97
|
-
return result;
|
|
173
|
+
return handleAuthResponse(result);
|
|
98
174
|
};
|
|
99
175
|
const signIn = async (email, password) => {
|
|
100
|
-
const result = await api(
|
|
101
|
-
method:
|
|
176
|
+
const result = await api('/auth/sign-in/email', {
|
|
177
|
+
method: 'POST',
|
|
102
178
|
body: JSON.stringify({
|
|
103
179
|
email,
|
|
104
180
|
password
|
|
105
181
|
})
|
|
106
182
|
});
|
|
107
|
-
|
|
108
|
-
setStoredToken(result.token);
|
|
109
|
-
}
|
|
110
|
-
return result;
|
|
183
|
+
return handleAuthResponse(result);
|
|
111
184
|
};
|
|
112
185
|
const signOut = async () => {
|
|
113
186
|
try {
|
|
114
|
-
await api(
|
|
115
|
-
method:
|
|
187
|
+
await api('/auth/sign-out', {
|
|
188
|
+
method: 'POST'
|
|
116
189
|
});
|
|
117
190
|
} catch {
|
|
118
191
|
// Ignora erros de logout no servidor
|
|
@@ -122,23 +195,77 @@ const signOut = async () => {
|
|
|
122
195
|
};
|
|
123
196
|
const refreshToken = async () => {
|
|
124
197
|
try {
|
|
125
|
-
const result = await api(
|
|
126
|
-
method:
|
|
198
|
+
const result = await api('/auth/refresh', {
|
|
199
|
+
method: 'POST'
|
|
127
200
|
});
|
|
128
|
-
|
|
129
|
-
setStoredToken(result.token);
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
201
|
+
return handleAuthResponse(result);
|
|
132
202
|
} catch (error) {
|
|
133
203
|
setStoredToken(null);
|
|
134
204
|
throw error;
|
|
135
205
|
}
|
|
136
206
|
};
|
|
137
207
|
|
|
138
|
-
|
|
208
|
+
/*--- Magic Link ---------------------------------*/
|
|
209
|
+
const sendMagicLink = async (email, callbackURL) => {
|
|
210
|
+
const callback = callbackURL || (typeof window !== 'undefined' ? `${window.location.origin}/auth/magic-link/verify` : '/auth/magic-link/verify');
|
|
211
|
+
return await api('/auth/sign-in/magic-link', {
|
|
212
|
+
method: 'POST',
|
|
213
|
+
body: JSON.stringify({
|
|
214
|
+
email,
|
|
215
|
+
callbackURL: callback
|
|
216
|
+
})
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
const verifyMagicLink = async token => {
|
|
220
|
+
const result = await api(`/auth/magic-link/verify?token=${encodeURIComponent(token)}`);
|
|
221
|
+
return handleAuthResponse(result);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/*--- Password Reset -----------------------------*/
|
|
225
|
+
const forgotPassword = async (email, redirectTo) => {
|
|
226
|
+
const redirect = redirectTo || (typeof window !== 'undefined' ? `${window.location.origin}/auth/reset-password` : '/auth/reset-password');
|
|
227
|
+
return await api('/auth/forget-password', {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
body: JSON.stringify({
|
|
230
|
+
email,
|
|
231
|
+
redirectTo: redirect
|
|
232
|
+
})
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
const resetPassword = async (token, newPassword) => {
|
|
236
|
+
return await api('/auth/reset-password', {
|
|
237
|
+
method: 'POST',
|
|
238
|
+
body: JSON.stringify({
|
|
239
|
+
token,
|
|
240
|
+
newPassword
|
|
241
|
+
})
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/*--- Email Verification -------------------------*/
|
|
246
|
+
const verifyEmail = async token => {
|
|
247
|
+
const result = await api(`/auth/verify-email?token=${encodeURIComponent(token)}`);
|
|
248
|
+
return handleAuthResponse(result);
|
|
249
|
+
};
|
|
250
|
+
const resendVerification = async (email, callbackURL) => {
|
|
251
|
+
const callback = callbackURL || (typeof window !== 'undefined' ? `${window.location.origin}/auth/verify-email` : '/auth/verify-email');
|
|
252
|
+
return await api('/auth/send-verification-email', {
|
|
253
|
+
method: 'POST',
|
|
254
|
+
body: JSON.stringify({
|
|
255
|
+
email,
|
|
256
|
+
callbackURL: callback
|
|
257
|
+
})
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/*--- Session ------------------------------------*/
|
|
262
|
+
const getSession = async () => {
|
|
263
|
+
return await api('/auth/session');
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/* Social login redirect (ex.: Google) -----------*/
|
|
139
267
|
function socialRedirect(provider, redirectTo = typeof window !== 'undefined' ? window.location.href : '/') {
|
|
140
268
|
if (typeof window === 'undefined') return;
|
|
141
|
-
// Melhor pegar a URL de redirect dada pelo back-end, mas isso já funciona:
|
|
142
269
|
window.location = `${API_BASE}/auth/sign-in/${provider}?redirect=${encodeURIComponent(redirectTo)}`;
|
|
143
270
|
}
|
|
144
271
|
|
|
@@ -159,479 +286,84 @@ const createStoreImpl = (createState) => {
|
|
|
159
286
|
listeners.add(listener);
|
|
160
287
|
return () => listeners.delete(listener);
|
|
161
288
|
};
|
|
162
|
-
const
|
|
163
|
-
if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") {
|
|
164
|
-
console.warn(
|
|
165
|
-
"[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
listeners.clear();
|
|
169
|
-
};
|
|
170
|
-
const api = { setState, getState, getInitialState, subscribe, destroy };
|
|
289
|
+
const api = { setState, getState, getInitialState, subscribe };
|
|
171
290
|
const initialState = state = createState(setState, getState, api);
|
|
172
291
|
return api;
|
|
173
292
|
};
|
|
174
|
-
const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
175
|
-
|
|
176
|
-
function getDefaultExportFromCjs (x) {
|
|
177
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
var withSelector = {exports: {}};
|
|
181
|
-
|
|
182
|
-
var withSelector_production = {};
|
|
183
|
-
|
|
184
|
-
var shim = {exports: {}};
|
|
185
|
-
|
|
186
|
-
var useSyncExternalStoreShim_production = {};
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* @license React
|
|
190
|
-
* use-sync-external-store-shim.production.js
|
|
191
|
-
*
|
|
192
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
193
|
-
*
|
|
194
|
-
* This source code is licensed under the MIT license found in the
|
|
195
|
-
* LICENSE file in the root directory of this source tree.
|
|
196
|
-
*/
|
|
197
|
-
|
|
198
|
-
var hasRequiredUseSyncExternalStoreShim_production;
|
|
199
|
-
|
|
200
|
-
function requireUseSyncExternalStoreShim_production () {
|
|
201
|
-
if (hasRequiredUseSyncExternalStoreShim_production) return useSyncExternalStoreShim_production;
|
|
202
|
-
hasRequiredUseSyncExternalStoreShim_production = 1;
|
|
203
|
-
var React = require$$0;
|
|
204
|
-
function is(x, y) {
|
|
205
|
-
return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);
|
|
206
|
-
}
|
|
207
|
-
var objectIs = "function" === typeof Object.is ? Object.is : is,
|
|
208
|
-
useState = React.useState,
|
|
209
|
-
useEffect = React.useEffect,
|
|
210
|
-
useLayoutEffect = React.useLayoutEffect,
|
|
211
|
-
useDebugValue = React.useDebugValue;
|
|
212
|
-
function useSyncExternalStore$2(subscribe, getSnapshot) {
|
|
213
|
-
var value = getSnapshot(),
|
|
214
|
-
_useState = useState({ inst: { value: value, getSnapshot: getSnapshot } }),
|
|
215
|
-
inst = _useState[0].inst,
|
|
216
|
-
forceUpdate = _useState[1];
|
|
217
|
-
useLayoutEffect(
|
|
218
|
-
function () {
|
|
219
|
-
inst.value = value;
|
|
220
|
-
inst.getSnapshot = getSnapshot;
|
|
221
|
-
checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
|
|
222
|
-
},
|
|
223
|
-
[subscribe, value, getSnapshot]
|
|
224
|
-
);
|
|
225
|
-
useEffect(
|
|
226
|
-
function () {
|
|
227
|
-
checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
|
|
228
|
-
return subscribe(function () {
|
|
229
|
-
checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
|
|
230
|
-
});
|
|
231
|
-
},
|
|
232
|
-
[subscribe]
|
|
233
|
-
);
|
|
234
|
-
useDebugValue(value);
|
|
235
|
-
return value;
|
|
236
|
-
}
|
|
237
|
-
function checkIfSnapshotChanged(inst) {
|
|
238
|
-
var latestGetSnapshot = inst.getSnapshot;
|
|
239
|
-
inst = inst.value;
|
|
240
|
-
try {
|
|
241
|
-
var nextValue = latestGetSnapshot();
|
|
242
|
-
return !objectIs(inst, nextValue);
|
|
243
|
-
} catch (error) {
|
|
244
|
-
return true;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
function useSyncExternalStore$1(subscribe, getSnapshot) {
|
|
248
|
-
return getSnapshot();
|
|
249
|
-
}
|
|
250
|
-
var shim =
|
|
251
|
-
"undefined" === typeof window ||
|
|
252
|
-
"undefined" === typeof window.document ||
|
|
253
|
-
"undefined" === typeof window.document.createElement
|
|
254
|
-
? useSyncExternalStore$1
|
|
255
|
-
: useSyncExternalStore$2;
|
|
256
|
-
useSyncExternalStoreShim_production.useSyncExternalStore =
|
|
257
|
-
void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;
|
|
258
|
-
return useSyncExternalStoreShim_production;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
var useSyncExternalStoreShim_development = {};
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* @license React
|
|
265
|
-
* use-sync-external-store-shim.development.js
|
|
266
|
-
*
|
|
267
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
268
|
-
*
|
|
269
|
-
* This source code is licensed under the MIT license found in the
|
|
270
|
-
* LICENSE file in the root directory of this source tree.
|
|
271
|
-
*/
|
|
272
|
-
|
|
273
|
-
var hasRequiredUseSyncExternalStoreShim_development;
|
|
274
|
-
|
|
275
|
-
function requireUseSyncExternalStoreShim_development () {
|
|
276
|
-
if (hasRequiredUseSyncExternalStoreShim_development) return useSyncExternalStoreShim_development;
|
|
277
|
-
hasRequiredUseSyncExternalStoreShim_development = 1;
|
|
278
|
-
"production" !== process.env.NODE_ENV &&
|
|
279
|
-
(function () {
|
|
280
|
-
function is(x, y) {
|
|
281
|
-
return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);
|
|
282
|
-
}
|
|
283
|
-
function useSyncExternalStore$2(subscribe, getSnapshot) {
|
|
284
|
-
didWarnOld18Alpha ||
|
|
285
|
-
void 0 === React.startTransition ||
|
|
286
|
-
((didWarnOld18Alpha = true),
|
|
287
|
-
console.error(
|
|
288
|
-
"You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."
|
|
289
|
-
));
|
|
290
|
-
var value = getSnapshot();
|
|
291
|
-
if (!didWarnUncachedGetSnapshot) {
|
|
292
|
-
var cachedValue = getSnapshot();
|
|
293
|
-
objectIs(value, cachedValue) ||
|
|
294
|
-
(console.error(
|
|
295
|
-
"The result of getSnapshot should be cached to avoid an infinite loop"
|
|
296
|
-
),
|
|
297
|
-
(didWarnUncachedGetSnapshot = true));
|
|
298
|
-
}
|
|
299
|
-
cachedValue = useState({
|
|
300
|
-
inst: { value: value, getSnapshot: getSnapshot }
|
|
301
|
-
});
|
|
302
|
-
var inst = cachedValue[0].inst,
|
|
303
|
-
forceUpdate = cachedValue[1];
|
|
304
|
-
useLayoutEffect(
|
|
305
|
-
function () {
|
|
306
|
-
inst.value = value;
|
|
307
|
-
inst.getSnapshot = getSnapshot;
|
|
308
|
-
checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
|
|
309
|
-
},
|
|
310
|
-
[subscribe, value, getSnapshot]
|
|
311
|
-
);
|
|
312
|
-
useEffect(
|
|
313
|
-
function () {
|
|
314
|
-
checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
|
|
315
|
-
return subscribe(function () {
|
|
316
|
-
checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
|
|
317
|
-
});
|
|
318
|
-
},
|
|
319
|
-
[subscribe]
|
|
320
|
-
);
|
|
321
|
-
useDebugValue(value);
|
|
322
|
-
return value;
|
|
323
|
-
}
|
|
324
|
-
function checkIfSnapshotChanged(inst) {
|
|
325
|
-
var latestGetSnapshot = inst.getSnapshot;
|
|
326
|
-
inst = inst.value;
|
|
327
|
-
try {
|
|
328
|
-
var nextValue = latestGetSnapshot();
|
|
329
|
-
return !objectIs(inst, nextValue);
|
|
330
|
-
} catch (error) {
|
|
331
|
-
return true;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
function useSyncExternalStore$1(subscribe, getSnapshot) {
|
|
335
|
-
return getSnapshot();
|
|
336
|
-
}
|
|
337
|
-
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
|
|
338
|
-
"function" ===
|
|
339
|
-
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart &&
|
|
340
|
-
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
341
|
-
var React = require$$0,
|
|
342
|
-
objectIs = "function" === typeof Object.is ? Object.is : is,
|
|
343
|
-
useState = React.useState,
|
|
344
|
-
useEffect = React.useEffect,
|
|
345
|
-
useLayoutEffect = React.useLayoutEffect,
|
|
346
|
-
useDebugValue = React.useDebugValue,
|
|
347
|
-
didWarnOld18Alpha = false,
|
|
348
|
-
didWarnUncachedGetSnapshot = false,
|
|
349
|
-
shim =
|
|
350
|
-
"undefined" === typeof window ||
|
|
351
|
-
"undefined" === typeof window.document ||
|
|
352
|
-
"undefined" === typeof window.document.createElement
|
|
353
|
-
? useSyncExternalStore$1
|
|
354
|
-
: useSyncExternalStore$2;
|
|
355
|
-
useSyncExternalStoreShim_development.useSyncExternalStore =
|
|
356
|
-
void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;
|
|
357
|
-
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
|
|
358
|
-
"function" ===
|
|
359
|
-
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
|
|
360
|
-
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
|
361
|
-
})();
|
|
362
|
-
return useSyncExternalStoreShim_development;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
var hasRequiredShim;
|
|
366
|
-
|
|
367
|
-
function requireShim () {
|
|
368
|
-
if (hasRequiredShim) return shim.exports;
|
|
369
|
-
hasRequiredShim = 1;
|
|
370
|
-
|
|
371
|
-
if (process.env.NODE_ENV === 'production') {
|
|
372
|
-
shim.exports = requireUseSyncExternalStoreShim_production();
|
|
373
|
-
} else {
|
|
374
|
-
shim.exports = requireUseSyncExternalStoreShim_development();
|
|
375
|
-
}
|
|
376
|
-
return shim.exports;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* @license React
|
|
381
|
-
* use-sync-external-store-shim/with-selector.production.js
|
|
382
|
-
*
|
|
383
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
384
|
-
*
|
|
385
|
-
* This source code is licensed under the MIT license found in the
|
|
386
|
-
* LICENSE file in the root directory of this source tree.
|
|
387
|
-
*/
|
|
388
|
-
|
|
389
|
-
var hasRequiredWithSelector_production;
|
|
390
|
-
|
|
391
|
-
function requireWithSelector_production () {
|
|
392
|
-
if (hasRequiredWithSelector_production) return withSelector_production;
|
|
393
|
-
hasRequiredWithSelector_production = 1;
|
|
394
|
-
var React = require$$0,
|
|
395
|
-
shim = requireShim();
|
|
396
|
-
function is(x, y) {
|
|
397
|
-
return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);
|
|
398
|
-
}
|
|
399
|
-
var objectIs = "function" === typeof Object.is ? Object.is : is,
|
|
400
|
-
useSyncExternalStore = shim.useSyncExternalStore,
|
|
401
|
-
useRef = React.useRef,
|
|
402
|
-
useEffect = React.useEffect,
|
|
403
|
-
useMemo = React.useMemo,
|
|
404
|
-
useDebugValue = React.useDebugValue;
|
|
405
|
-
withSelector_production.useSyncExternalStoreWithSelector = function (
|
|
406
|
-
subscribe,
|
|
407
|
-
getSnapshot,
|
|
408
|
-
getServerSnapshot,
|
|
409
|
-
selector,
|
|
410
|
-
isEqual
|
|
411
|
-
) {
|
|
412
|
-
var instRef = useRef(null);
|
|
413
|
-
if (null === instRef.current) {
|
|
414
|
-
var inst = { hasValue: false, value: null };
|
|
415
|
-
instRef.current = inst;
|
|
416
|
-
} else inst = instRef.current;
|
|
417
|
-
instRef = useMemo(
|
|
418
|
-
function () {
|
|
419
|
-
function memoizedSelector(nextSnapshot) {
|
|
420
|
-
if (!hasMemo) {
|
|
421
|
-
hasMemo = true;
|
|
422
|
-
memoizedSnapshot = nextSnapshot;
|
|
423
|
-
nextSnapshot = selector(nextSnapshot);
|
|
424
|
-
if (void 0 !== isEqual && inst.hasValue) {
|
|
425
|
-
var currentSelection = inst.value;
|
|
426
|
-
if (isEqual(currentSelection, nextSnapshot))
|
|
427
|
-
return (memoizedSelection = currentSelection);
|
|
428
|
-
}
|
|
429
|
-
return (memoizedSelection = nextSnapshot);
|
|
430
|
-
}
|
|
431
|
-
currentSelection = memoizedSelection;
|
|
432
|
-
if (objectIs(memoizedSnapshot, nextSnapshot)) return currentSelection;
|
|
433
|
-
var nextSelection = selector(nextSnapshot);
|
|
434
|
-
if (void 0 !== isEqual && isEqual(currentSelection, nextSelection))
|
|
435
|
-
return (memoizedSnapshot = nextSnapshot), currentSelection;
|
|
436
|
-
memoizedSnapshot = nextSnapshot;
|
|
437
|
-
return (memoizedSelection = nextSelection);
|
|
438
|
-
}
|
|
439
|
-
var hasMemo = false,
|
|
440
|
-
memoizedSnapshot,
|
|
441
|
-
memoizedSelection,
|
|
442
|
-
maybeGetServerSnapshot =
|
|
443
|
-
void 0 === getServerSnapshot ? null : getServerSnapshot;
|
|
444
|
-
return [
|
|
445
|
-
function () {
|
|
446
|
-
return memoizedSelector(getSnapshot());
|
|
447
|
-
},
|
|
448
|
-
null === maybeGetServerSnapshot
|
|
449
|
-
? void 0
|
|
450
|
-
: function () {
|
|
451
|
-
return memoizedSelector(maybeGetServerSnapshot());
|
|
452
|
-
}
|
|
453
|
-
];
|
|
454
|
-
},
|
|
455
|
-
[getSnapshot, getServerSnapshot, selector, isEqual]
|
|
456
|
-
);
|
|
457
|
-
var value = useSyncExternalStore(subscribe, instRef[0], instRef[1]);
|
|
458
|
-
useEffect(
|
|
459
|
-
function () {
|
|
460
|
-
inst.hasValue = true;
|
|
461
|
-
inst.value = value;
|
|
462
|
-
},
|
|
463
|
-
[value]
|
|
464
|
-
);
|
|
465
|
-
useDebugValue(value);
|
|
466
|
-
return value;
|
|
467
|
-
};
|
|
468
|
-
return withSelector_production;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
var withSelector_development = {};
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* @license React
|
|
475
|
-
* use-sync-external-store-shim/with-selector.development.js
|
|
476
|
-
*
|
|
477
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
478
|
-
*
|
|
479
|
-
* This source code is licensed under the MIT license found in the
|
|
480
|
-
* LICENSE file in the root directory of this source tree.
|
|
481
|
-
*/
|
|
293
|
+
const createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
|
|
482
294
|
|
|
483
|
-
var hasRequiredWithSelector_development;
|
|
484
|
-
|
|
485
|
-
function requireWithSelector_development () {
|
|
486
|
-
if (hasRequiredWithSelector_development) return withSelector_development;
|
|
487
|
-
hasRequiredWithSelector_development = 1;
|
|
488
|
-
"production" !== process.env.NODE_ENV &&
|
|
489
|
-
(function () {
|
|
490
|
-
function is(x, y) {
|
|
491
|
-
return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);
|
|
492
|
-
}
|
|
493
|
-
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
|
|
494
|
-
"function" ===
|
|
495
|
-
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart &&
|
|
496
|
-
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
497
|
-
var React = require$$0,
|
|
498
|
-
shim = requireShim(),
|
|
499
|
-
objectIs = "function" === typeof Object.is ? Object.is : is,
|
|
500
|
-
useSyncExternalStore = shim.useSyncExternalStore,
|
|
501
|
-
useRef = React.useRef,
|
|
502
|
-
useEffect = React.useEffect,
|
|
503
|
-
useMemo = React.useMemo,
|
|
504
|
-
useDebugValue = React.useDebugValue;
|
|
505
|
-
withSelector_development.useSyncExternalStoreWithSelector = function (
|
|
506
|
-
subscribe,
|
|
507
|
-
getSnapshot,
|
|
508
|
-
getServerSnapshot,
|
|
509
|
-
selector,
|
|
510
|
-
isEqual
|
|
511
|
-
) {
|
|
512
|
-
var instRef = useRef(null);
|
|
513
|
-
if (null === instRef.current) {
|
|
514
|
-
var inst = { hasValue: false, value: null };
|
|
515
|
-
instRef.current = inst;
|
|
516
|
-
} else inst = instRef.current;
|
|
517
|
-
instRef = useMemo(
|
|
518
|
-
function () {
|
|
519
|
-
function memoizedSelector(nextSnapshot) {
|
|
520
|
-
if (!hasMemo) {
|
|
521
|
-
hasMemo = true;
|
|
522
|
-
memoizedSnapshot = nextSnapshot;
|
|
523
|
-
nextSnapshot = selector(nextSnapshot);
|
|
524
|
-
if (void 0 !== isEqual && inst.hasValue) {
|
|
525
|
-
var currentSelection = inst.value;
|
|
526
|
-
if (isEqual(currentSelection, nextSnapshot))
|
|
527
|
-
return (memoizedSelection = currentSelection);
|
|
528
|
-
}
|
|
529
|
-
return (memoizedSelection = nextSnapshot);
|
|
530
|
-
}
|
|
531
|
-
currentSelection = memoizedSelection;
|
|
532
|
-
if (objectIs(memoizedSnapshot, nextSnapshot))
|
|
533
|
-
return currentSelection;
|
|
534
|
-
var nextSelection = selector(nextSnapshot);
|
|
535
|
-
if (void 0 !== isEqual && isEqual(currentSelection, nextSelection))
|
|
536
|
-
return (memoizedSnapshot = nextSnapshot), currentSelection;
|
|
537
|
-
memoizedSnapshot = nextSnapshot;
|
|
538
|
-
return (memoizedSelection = nextSelection);
|
|
539
|
-
}
|
|
540
|
-
var hasMemo = false,
|
|
541
|
-
memoizedSnapshot,
|
|
542
|
-
memoizedSelection,
|
|
543
|
-
maybeGetServerSnapshot =
|
|
544
|
-
void 0 === getServerSnapshot ? null : getServerSnapshot;
|
|
545
|
-
return [
|
|
546
|
-
function () {
|
|
547
|
-
return memoizedSelector(getSnapshot());
|
|
548
|
-
},
|
|
549
|
-
null === maybeGetServerSnapshot
|
|
550
|
-
? void 0
|
|
551
|
-
: function () {
|
|
552
|
-
return memoizedSelector(maybeGetServerSnapshot());
|
|
553
|
-
}
|
|
554
|
-
];
|
|
555
|
-
},
|
|
556
|
-
[getSnapshot, getServerSnapshot, selector, isEqual]
|
|
557
|
-
);
|
|
558
|
-
var value = useSyncExternalStore(subscribe, instRef[0], instRef[1]);
|
|
559
|
-
useEffect(
|
|
560
|
-
function () {
|
|
561
|
-
inst.hasValue = true;
|
|
562
|
-
inst.value = value;
|
|
563
|
-
},
|
|
564
|
-
[value]
|
|
565
|
-
);
|
|
566
|
-
useDebugValue(value);
|
|
567
|
-
return value;
|
|
568
|
-
};
|
|
569
|
-
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
|
|
570
|
-
"function" ===
|
|
571
|
-
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
|
|
572
|
-
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
|
573
|
-
})();
|
|
574
|
-
return withSelector_development;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
if (process.env.NODE_ENV === 'production') {
|
|
578
|
-
withSelector.exports = requireWithSelector_production();
|
|
579
|
-
} else {
|
|
580
|
-
withSelector.exports = requireWithSelector_development();
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
var withSelectorExports = withSelector.exports;
|
|
584
|
-
var useSyncExternalStoreExports = /*@__PURE__*/getDefaultExportFromCjs(withSelectorExports);
|
|
585
|
-
|
|
586
|
-
const { useDebugValue } = require$$0;
|
|
587
|
-
const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports;
|
|
588
|
-
let didWarnAboutEqualityFn = false;
|
|
589
295
|
const identity = (arg) => arg;
|
|
590
|
-
function useStore(api, selector = identity
|
|
591
|
-
|
|
592
|
-
console.warn(
|
|
593
|
-
"[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"
|
|
594
|
-
);
|
|
595
|
-
didWarnAboutEqualityFn = true;
|
|
596
|
-
}
|
|
597
|
-
const slice = useSyncExternalStoreWithSelector(
|
|
296
|
+
function useStore(api, selector = identity) {
|
|
297
|
+
const slice = React$1.useSyncExternalStore(
|
|
598
298
|
api.subscribe,
|
|
599
|
-
api.getState,
|
|
600
|
-
|
|
601
|
-
selector,
|
|
602
|
-
equalityFn
|
|
299
|
+
React$1.useCallback(() => selector(api.getState()), [api, selector]),
|
|
300
|
+
React$1.useCallback(() => selector(api.getInitialState()), [api, selector])
|
|
603
301
|
);
|
|
604
|
-
useDebugValue(slice);
|
|
302
|
+
React$1.useDebugValue(slice);
|
|
605
303
|
return slice;
|
|
606
304
|
}
|
|
607
305
|
const createImpl = (createState) => {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
"[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`."
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
const api = typeof createState === "function" ? createStore(createState) : createState;
|
|
614
|
-
const useBoundStore = (selector, equalityFn) => useStore(api, selector, equalityFn);
|
|
306
|
+
const api = createStore(createState);
|
|
307
|
+
const useBoundStore = (selector) => useStore(api, selector);
|
|
615
308
|
Object.assign(useBoundStore, api);
|
|
616
309
|
return useBoundStore;
|
|
617
310
|
};
|
|
618
|
-
const create = (createState) => createState ? createImpl(createState) : createImpl;
|
|
311
|
+
const create = ((createState) => createState ? createImpl(createState) : createImpl);
|
|
619
312
|
|
|
620
313
|
// Estado: { user, loading, error }
|
|
621
314
|
const useAuthStore = create((set, get) => ({
|
|
622
315
|
user: null,
|
|
623
316
|
loading: true,
|
|
624
317
|
error: null,
|
|
318
|
+
// Loading states granulares
|
|
319
|
+
loadingStates: {
|
|
320
|
+
signIn: false,
|
|
321
|
+
signUp: false,
|
|
322
|
+
signOut: false,
|
|
323
|
+
magicLink: false,
|
|
324
|
+
verifyMagicLink: false,
|
|
325
|
+
resetPassword: false,
|
|
326
|
+
verifyEmail: false,
|
|
327
|
+
resendVerification: false
|
|
328
|
+
},
|
|
329
|
+
// Helper para atualizar loading states
|
|
330
|
+
setLoading: (key, value) => set(state => ({
|
|
331
|
+
loadingStates: {
|
|
332
|
+
...state.loadingStates,
|
|
333
|
+
[key]: value
|
|
334
|
+
}
|
|
335
|
+
})),
|
|
625
336
|
/* Init ao montar o Provider */
|
|
626
337
|
init: async () => {
|
|
627
338
|
try {
|
|
628
|
-
// Verifica se há um token válido
|
|
339
|
+
// Verifica se há um token válido
|
|
629
340
|
if (isAuthenticated()) {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
341
|
+
// Tenta extrair usuário do token (JWT)
|
|
342
|
+
let user = getCurrentUser();
|
|
343
|
+
|
|
344
|
+
// Se o token for opaco (não-JWT), getCurrentUser retorna null.
|
|
345
|
+
// Precisamos buscar a sessão no servidor.
|
|
346
|
+
if (!user) {
|
|
347
|
+
try {
|
|
348
|
+
const session = await getSession();
|
|
349
|
+
if (session?.user) {
|
|
350
|
+
user = session.user;
|
|
351
|
+
}
|
|
352
|
+
} catch (sessionError) {
|
|
353
|
+
console.warn('[AuthStore] Failed to fetch session:', sessionError);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (user) {
|
|
357
|
+
set({
|
|
358
|
+
user,
|
|
359
|
+
loading: false
|
|
360
|
+
});
|
|
361
|
+
} else {
|
|
362
|
+
set({
|
|
363
|
+
user: null,
|
|
364
|
+
loading: false
|
|
365
|
+
});
|
|
366
|
+
}
|
|
635
367
|
} else {
|
|
636
368
|
set({
|
|
637
369
|
user: null,
|
|
@@ -646,57 +378,237 @@ const useAuthStore = create((set, get) => ({
|
|
|
646
378
|
});
|
|
647
379
|
}
|
|
648
380
|
},
|
|
649
|
-
/* Ações */
|
|
650
|
-
signUp: async (email, password) => {
|
|
381
|
+
/* Ações de Autenticação */
|
|
382
|
+
signUp: async (email, password, name) => {
|
|
383
|
+
const {
|
|
384
|
+
setLoading
|
|
385
|
+
} = get();
|
|
386
|
+
setLoading('signUp', true);
|
|
651
387
|
set({
|
|
652
|
-
loading: true,
|
|
653
388
|
error: null
|
|
654
389
|
});
|
|
655
390
|
try {
|
|
656
|
-
const result = await signUp(email, password);
|
|
391
|
+
const result = await signUp(email, password, name);
|
|
657
392
|
const user = getCurrentUser();
|
|
658
393
|
set({
|
|
659
394
|
user,
|
|
660
395
|
loading: false
|
|
661
396
|
});
|
|
397
|
+
setLoading('signUp', false);
|
|
662
398
|
return result;
|
|
663
399
|
} catch (err) {
|
|
664
400
|
set({
|
|
665
|
-
error: err
|
|
666
|
-
loading: false
|
|
401
|
+
error: err
|
|
667
402
|
});
|
|
403
|
+
setLoading('signUp', false);
|
|
668
404
|
throw err;
|
|
669
405
|
}
|
|
670
406
|
},
|
|
671
407
|
signIn: async (email, password) => {
|
|
408
|
+
const {
|
|
409
|
+
setLoading
|
|
410
|
+
} = get();
|
|
411
|
+
setLoading('signIn', true);
|
|
672
412
|
set({
|
|
673
|
-
loading: true,
|
|
674
413
|
error: null
|
|
675
414
|
});
|
|
676
415
|
try {
|
|
677
416
|
const result = await signIn(email, password);
|
|
678
|
-
|
|
417
|
+
|
|
418
|
+
// Tenta obter usuário do token (se JWT)
|
|
419
|
+
let user = getCurrentUser();
|
|
420
|
+
|
|
421
|
+
// Se não encontrou no token (sessão opaca), busca do servidor
|
|
422
|
+
if (!user) {
|
|
423
|
+
const session = await getSession();
|
|
424
|
+
if (session?.user) user = session.user;
|
|
425
|
+
}
|
|
679
426
|
set({
|
|
680
427
|
user,
|
|
681
428
|
loading: false
|
|
682
429
|
});
|
|
430
|
+
setLoading('signIn', false);
|
|
683
431
|
return result;
|
|
684
432
|
} catch (err) {
|
|
685
433
|
set({
|
|
686
|
-
error: err
|
|
687
|
-
loading: false
|
|
434
|
+
error: err
|
|
688
435
|
});
|
|
436
|
+
setLoading('signIn', false);
|
|
689
437
|
throw err;
|
|
690
438
|
}
|
|
691
439
|
},
|
|
692
440
|
signOut: async () => {
|
|
693
|
-
|
|
441
|
+
const {
|
|
442
|
+
setLoading
|
|
443
|
+
} = get();
|
|
444
|
+
setLoading('signOut', true);
|
|
445
|
+
try {
|
|
446
|
+
await signOut();
|
|
447
|
+
set({
|
|
448
|
+
user: null
|
|
449
|
+
});
|
|
450
|
+
// Sincronizar logout entre abas
|
|
451
|
+
if (typeof window !== 'undefined') {
|
|
452
|
+
window.localStorage.setItem('auth:logout', Date.now());
|
|
453
|
+
}
|
|
454
|
+
} finally {
|
|
455
|
+
setLoading('signOut', false);
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
/* Magic Link */
|
|
459
|
+
sendMagicLink: async (email, callbackURL) => {
|
|
460
|
+
const {
|
|
461
|
+
setLoading
|
|
462
|
+
} = get();
|
|
463
|
+
setLoading('magicLink', true);
|
|
694
464
|
set({
|
|
695
|
-
|
|
465
|
+
error: null
|
|
696
466
|
});
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
467
|
+
try {
|
|
468
|
+
const result = await sendMagicLink(email, callbackURL);
|
|
469
|
+
setLoading('magicLink', false);
|
|
470
|
+
return result;
|
|
471
|
+
} catch (err) {
|
|
472
|
+
set({
|
|
473
|
+
error: err
|
|
474
|
+
});
|
|
475
|
+
setLoading('magicLink', false);
|
|
476
|
+
throw err;
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
verifyMagicLink: async token => {
|
|
480
|
+
const {
|
|
481
|
+
setLoading
|
|
482
|
+
} = get();
|
|
483
|
+
setLoading('verifyMagicLink', true);
|
|
484
|
+
set({
|
|
485
|
+
error: null
|
|
486
|
+
});
|
|
487
|
+
try {
|
|
488
|
+
const result = await verifyMagicLink(token);
|
|
489
|
+
let user = result.user;
|
|
490
|
+
|
|
491
|
+
// Se não veio no resultado, tenta pegar do token ou do servidor
|
|
492
|
+
if (!user) {
|
|
493
|
+
user = getCurrentUser();
|
|
494
|
+
if (!user) {
|
|
495
|
+
const session = await getSession();
|
|
496
|
+
if (session?.user) user = session.user;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
if (user) {
|
|
500
|
+
set({
|
|
501
|
+
user,
|
|
502
|
+
loading: false
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
setLoading('verifyMagicLink', false);
|
|
506
|
+
return result;
|
|
507
|
+
} catch (err) {
|
|
508
|
+
set({
|
|
509
|
+
error: err
|
|
510
|
+
});
|
|
511
|
+
setLoading('verifyMagicLink', false);
|
|
512
|
+
throw err;
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
/* Password Reset */
|
|
516
|
+
forgotPassword: async (email, redirectTo) => {
|
|
517
|
+
const {
|
|
518
|
+
setLoading
|
|
519
|
+
} = get();
|
|
520
|
+
setLoading('resetPassword', true);
|
|
521
|
+
set({
|
|
522
|
+
error: null
|
|
523
|
+
});
|
|
524
|
+
try {
|
|
525
|
+
const result = await forgotPassword(email, redirectTo);
|
|
526
|
+
setLoading('resetPassword', false);
|
|
527
|
+
return result;
|
|
528
|
+
} catch (err) {
|
|
529
|
+
set({
|
|
530
|
+
error: err
|
|
531
|
+
});
|
|
532
|
+
setLoading('resetPassword', false);
|
|
533
|
+
throw err;
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
resetPassword: async (token, newPassword) => {
|
|
537
|
+
const {
|
|
538
|
+
setLoading
|
|
539
|
+
} = get();
|
|
540
|
+
setLoading('resetPassword', true);
|
|
541
|
+
set({
|
|
542
|
+
error: null
|
|
543
|
+
});
|
|
544
|
+
try {
|
|
545
|
+
const result = await resetPassword(token, newPassword);
|
|
546
|
+
setLoading('resetPassword', false);
|
|
547
|
+
return result;
|
|
548
|
+
} catch (err) {
|
|
549
|
+
set({
|
|
550
|
+
error: err
|
|
551
|
+
});
|
|
552
|
+
setLoading('resetPassword', false);
|
|
553
|
+
throw err;
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
/* Email Verification */
|
|
557
|
+
verifyEmail: async token => {
|
|
558
|
+
const {
|
|
559
|
+
setLoading
|
|
560
|
+
} = get();
|
|
561
|
+
setLoading('verifyEmail', true);
|
|
562
|
+
set({
|
|
563
|
+
error: null
|
|
564
|
+
});
|
|
565
|
+
try {
|
|
566
|
+
const result = await verifyEmail(token);
|
|
567
|
+
if (result.user) {
|
|
568
|
+
set({
|
|
569
|
+
user: result.user,
|
|
570
|
+
loading: false
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
setLoading('verifyEmail', false);
|
|
574
|
+
return result;
|
|
575
|
+
} catch (err) {
|
|
576
|
+
set({
|
|
577
|
+
error: err
|
|
578
|
+
});
|
|
579
|
+
setLoading('verifyEmail', false);
|
|
580
|
+
throw err;
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
resendVerification: async (email, callbackURL) => {
|
|
584
|
+
const {
|
|
585
|
+
setLoading
|
|
586
|
+
} = get();
|
|
587
|
+
setLoading('resendVerification', true);
|
|
588
|
+
set({
|
|
589
|
+
error: null
|
|
590
|
+
});
|
|
591
|
+
try {
|
|
592
|
+
const result = await resendVerification(email, callbackURL);
|
|
593
|
+
setLoading('resendVerification', false);
|
|
594
|
+
return result;
|
|
595
|
+
} catch (err) {
|
|
596
|
+
set({
|
|
597
|
+
error: err
|
|
598
|
+
});
|
|
599
|
+
setLoading('resendVerification', false);
|
|
600
|
+
throw err;
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
/* Session */
|
|
604
|
+
getSession: async () => {
|
|
605
|
+
try {
|
|
606
|
+
return await getSession();
|
|
607
|
+
} catch (err) {
|
|
608
|
+
set({
|
|
609
|
+
error: err
|
|
610
|
+
});
|
|
611
|
+
throw err;
|
|
700
612
|
}
|
|
701
613
|
},
|
|
702
614
|
/* Refresh do token em background */
|
|
@@ -709,7 +621,10 @@ const useAuthStore = create((set, get) => ({
|
|
|
709
621
|
const token = window.localStorage.getItem('auth:token');
|
|
710
622
|
if (token) {
|
|
711
623
|
// Decodifica o token para verificar tempo de expiração
|
|
712
|
-
const
|
|
624
|
+
const base64Url = token.split('.')[1];
|
|
625
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
626
|
+
const jsonPayload = window.atob(base64);
|
|
627
|
+
const payload = JSON.parse(jsonPayload);
|
|
713
628
|
const now = Date.now() / 1000;
|
|
714
629
|
const timeUntilExpiry = payload.exp - now;
|
|
715
630
|
|
|
@@ -750,17 +665,41 @@ const useAuthStore = create((set, get) => ({
|
|
|
750
665
|
return false;
|
|
751
666
|
}
|
|
752
667
|
return true;
|
|
753
|
-
}
|
|
668
|
+
},
|
|
669
|
+
/* Atualizar usuário manualmente */
|
|
670
|
+
setUser: user => set({
|
|
671
|
+
user
|
|
672
|
+
})
|
|
754
673
|
}));
|
|
755
674
|
|
|
756
675
|
const AuthContext = /*#__PURE__*/createContext(); // só para ter o Provider em JSX
|
|
757
676
|
|
|
758
677
|
function AuthProvider({
|
|
759
|
-
children
|
|
678
|
+
children,
|
|
679
|
+
apiUrl,
|
|
680
|
+
// URL base da API (OBRIGATÓRIA)
|
|
681
|
+
apiKey,
|
|
682
|
+
// API Key para header X-API-Key (OBRIGATÓRIA)
|
|
683
|
+
onError // Callback de erro global
|
|
760
684
|
}) {
|
|
685
|
+
// Validação de props obrigatórias
|
|
686
|
+
if (!apiKey) {
|
|
687
|
+
throw new Error('[@riligar/auth-react] apiKey é obrigatória no AuthProvider. ' + 'Obtenha sua API Key no dashboard em https://dashboard.myauth.click');
|
|
688
|
+
}
|
|
689
|
+
if (!apiUrl) {
|
|
690
|
+
throw new Error('[@riligar/auth-react] apiUrl é obrigatória no AuthProvider. ' + 'Use a URL do seu servidor de autenticação.');
|
|
691
|
+
}
|
|
761
692
|
const init = useAuthStore(s => s.init);
|
|
762
693
|
const startRefresh = useAuthStore(s => s.startRefresh);
|
|
763
694
|
const checkTokenValidity = useAuthStore(s => s.checkTokenValidity);
|
|
695
|
+
|
|
696
|
+
// Configura SDK com apiUrl e apiKey
|
|
697
|
+
useEffect(() => {
|
|
698
|
+
configure({
|
|
699
|
+
apiUrl,
|
|
700
|
+
apiKey
|
|
701
|
+
});
|
|
702
|
+
}, [apiUrl, apiKey]);
|
|
764
703
|
useEffect(() => {
|
|
765
704
|
init();
|
|
766
705
|
startRefresh();
|
|
@@ -791,8 +730,13 @@ function AuthProvider({
|
|
|
791
730
|
|
|
792
731
|
return () => clearInterval(interval);
|
|
793
732
|
}, [checkTokenValidity]);
|
|
733
|
+
|
|
734
|
+
// Contexto com onError callback
|
|
735
|
+
const contextValue = useMemo(() => ({
|
|
736
|
+
onError
|
|
737
|
+
}), [onError]);
|
|
794
738
|
return /*#__PURE__*/React.createElement(AuthContext.Provider, {
|
|
795
|
-
value:
|
|
739
|
+
value: contextValue
|
|
796
740
|
}, children);
|
|
797
741
|
}
|
|
798
742
|
|
|
@@ -803,11 +747,74 @@ const useAuth = () => useAuthStore(s => ({
|
|
|
803
747
|
error: s.error,
|
|
804
748
|
isAuthenticated: s.user !== null
|
|
805
749
|
}));
|
|
750
|
+
|
|
751
|
+
// Auth Actions
|
|
806
752
|
const useSignIn = () => useAuthStore(s => s.signIn);
|
|
807
753
|
const useSignUp = () => useAuthStore(s => s.signUp);
|
|
808
754
|
const useSignOut = () => useAuthStore(s => s.signOut);
|
|
809
755
|
const useCheckToken = () => useAuthStore(s => s.checkTokenValidity);
|
|
810
756
|
|
|
757
|
+
// Magic Link Hook
|
|
758
|
+
const useMagicLink = () => {
|
|
759
|
+
const sendMagicLink = useAuthStore(s => s.sendMagicLink);
|
|
760
|
+
const verifyMagicLink = useAuthStore(s => s.verifyMagicLink);
|
|
761
|
+
const loading = useAuthStore(s => s.loadingStates.magicLink);
|
|
762
|
+
const verifying = useAuthStore(s => s.loadingStates.verifyMagicLink);
|
|
763
|
+
const error = useAuthStore(s => s.error);
|
|
764
|
+
return {
|
|
765
|
+
sendMagicLink,
|
|
766
|
+
verifyMagicLink,
|
|
767
|
+
loading,
|
|
768
|
+
verifying,
|
|
769
|
+
error
|
|
770
|
+
};
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
// Password Reset Hook
|
|
774
|
+
const usePasswordReset = () => {
|
|
775
|
+
const forgotPassword = useAuthStore(s => s.forgotPassword);
|
|
776
|
+
const resetPassword = useAuthStore(s => s.resetPassword);
|
|
777
|
+
const loading = useAuthStore(s => s.loadingStates.resetPassword);
|
|
778
|
+
const error = useAuthStore(s => s.error);
|
|
779
|
+
return {
|
|
780
|
+
forgotPassword,
|
|
781
|
+
resetPassword,
|
|
782
|
+
loading,
|
|
783
|
+
error
|
|
784
|
+
};
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
// Email Verification Hook
|
|
788
|
+
const useEmailVerification = () => {
|
|
789
|
+
const verifyEmail = useAuthStore(s => s.verifyEmail);
|
|
790
|
+
const resendVerification = useAuthStore(s => s.resendVerification);
|
|
791
|
+
const loading = useAuthStore(s => s.loadingStates.verifyEmail);
|
|
792
|
+
const resending = useAuthStore(s => s.loadingStates.resendVerification);
|
|
793
|
+
const error = useAuthStore(s => s.error);
|
|
794
|
+
return {
|
|
795
|
+
verifyEmail,
|
|
796
|
+
resendVerification,
|
|
797
|
+
loading,
|
|
798
|
+
resending,
|
|
799
|
+
error
|
|
800
|
+
};
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// Session Hook
|
|
804
|
+
const useSession = () => {
|
|
805
|
+
const getSession = useAuthStore(s => s.getSession);
|
|
806
|
+
const user = useAuthStore(s => s.user);
|
|
807
|
+
const setUser = useAuthStore(s => s.setUser);
|
|
808
|
+
return {
|
|
809
|
+
getSession,
|
|
810
|
+
user,
|
|
811
|
+
setUser
|
|
812
|
+
};
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
// Loading States Hook
|
|
816
|
+
const useAuthLoading = () => useAuthStore(s => s.loadingStates);
|
|
817
|
+
|
|
811
818
|
function ProtectedRoute({
|
|
812
819
|
fallback = /*#__PURE__*/React.createElement("p", null, "\u231B Carregando..."),
|
|
813
820
|
redirectTo = "/login"
|
|
@@ -824,5 +831,787 @@ function ProtectedRoute({
|
|
|
824
831
|
return /*#__PURE__*/React.createElement(Outlet, null);
|
|
825
832
|
}
|
|
826
833
|
|
|
827
|
-
|
|
834
|
+
function _extends() {
|
|
835
|
+
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
836
|
+
for (var e = 1; e < arguments.length; e++) {
|
|
837
|
+
var t = arguments[e];
|
|
838
|
+
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
839
|
+
}
|
|
840
|
+
return n;
|
|
841
|
+
}, _extends.apply(null, arguments);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Container wrapper para componentes de autenticação
|
|
846
|
+
* Usa Mantine Paper com estilo consistente
|
|
847
|
+
*/
|
|
848
|
+
function AuthCard({
|
|
849
|
+
children,
|
|
850
|
+
title,
|
|
851
|
+
subtitle,
|
|
852
|
+
logo,
|
|
853
|
+
logoHeight = 32,
|
|
854
|
+
width = 350,
|
|
855
|
+
...props
|
|
856
|
+
}) {
|
|
857
|
+
return /*#__PURE__*/React.createElement(Paper, _extends({
|
|
858
|
+
withBorder: true,
|
|
859
|
+
shadow: "md",
|
|
860
|
+
p: 24,
|
|
861
|
+
w: width,
|
|
862
|
+
radius: "md"
|
|
863
|
+
}, props), /*#__PURE__*/React.createElement(Stack, {
|
|
864
|
+
gap: "sm"
|
|
865
|
+
}, (logo || title || subtitle) && /*#__PURE__*/React.createElement(Stack, {
|
|
866
|
+
gap: "xs"
|
|
867
|
+
}, logo && /*#__PURE__*/React.createElement(Image, {
|
|
868
|
+
src: logo,
|
|
869
|
+
alt: "Auth",
|
|
870
|
+
h: logoHeight,
|
|
871
|
+
fit: "contain"
|
|
872
|
+
}), title && /*#__PURE__*/React.createElement(Title, {
|
|
873
|
+
order: 3
|
|
874
|
+
}, title), subtitle && /*#__PURE__*/React.createElement(Text, {
|
|
875
|
+
size: "sm",
|
|
876
|
+
c: "dimmed"
|
|
877
|
+
}, subtitle)), children));
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
var img = "data:image/webp;base64,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";
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Formulário de Sign In completo com Mantine
|
|
884
|
+
* Suporta email/password, magic link, social login
|
|
885
|
+
*/
|
|
886
|
+
function SignInForm({
|
|
887
|
+
// Configuração
|
|
888
|
+
logo = img,
|
|
889
|
+
title = 'Entrar',
|
|
890
|
+
subtitle = 'Acesse sua conta para continuar',
|
|
891
|
+
// Features
|
|
892
|
+
showMagicLink = true,
|
|
893
|
+
showForgotPassword = true,
|
|
894
|
+
showSocialLogin = false,
|
|
895
|
+
showSignUpLink = true,
|
|
896
|
+
// Links
|
|
897
|
+
signUpUrl = '/auth/signup',
|
|
898
|
+
// Callbacks
|
|
899
|
+
onSuccess,
|
|
900
|
+
onError,
|
|
901
|
+
onMagicLinkSent,
|
|
902
|
+
onForgotPasswordSent,
|
|
903
|
+
onSocialLogin,
|
|
904
|
+
// Custom labels
|
|
905
|
+
labels = {},
|
|
906
|
+
...cardProps
|
|
907
|
+
}) {
|
|
908
|
+
const signIn = useAuthStore(s => s.signIn);
|
|
909
|
+
const sendMagicLink = useAuthStore(s => s.sendMagicLink);
|
|
910
|
+
const forgotPassword = useAuthStore(s => s.forgotPassword);
|
|
911
|
+
const loadingSignIn = useAuthStore(s => s.loadingStates.signIn);
|
|
912
|
+
const loadingMagicLink = useAuthStore(s => s.loadingStates.magicLink);
|
|
913
|
+
const loadingResetPassword = useAuthStore(s => s.loadingStates.resetPassword);
|
|
914
|
+
const form = useForm({
|
|
915
|
+
initialValues: {
|
|
916
|
+
email: '',
|
|
917
|
+
password: ''
|
|
918
|
+
},
|
|
919
|
+
validate: {
|
|
920
|
+
email: value => /^\S+@\S+$/.test(value) ? null : labels.invalidEmail || 'Email inválido',
|
|
921
|
+
password: value => value.length < 6 ? labels.passwordTooShort || 'A senha deve ter pelo menos 6 caracteres' : null
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
const handleSubmit = async values => {
|
|
925
|
+
try {
|
|
926
|
+
const result = await signIn(values.email, values.password);
|
|
927
|
+
onSuccess?.(result);
|
|
928
|
+
} catch (error) {
|
|
929
|
+
onError?.(error);
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
const handleMagicLink = async () => {
|
|
933
|
+
if (!form.values.email) {
|
|
934
|
+
form.setFieldError('email', labels.emailRequired || 'Email obrigatório');
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
try {
|
|
938
|
+
await sendMagicLink(form.values.email);
|
|
939
|
+
onMagicLinkSent?.(form.values.email);
|
|
940
|
+
} catch (error) {
|
|
941
|
+
onError?.(error);
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
const handleForgotPassword = async () => {
|
|
945
|
+
if (!form.values.email) {
|
|
946
|
+
form.setFieldError('email', labels.emailRequired || 'Email obrigatório');
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
try {
|
|
950
|
+
await forgotPassword(form.values.email);
|
|
951
|
+
onForgotPasswordSent?.(form.values.email);
|
|
952
|
+
} catch (error) {
|
|
953
|
+
onError?.(error);
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
const isLoading = loadingSignIn || loadingMagicLink || loadingResetPassword;
|
|
957
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
958
|
+
logo: logo,
|
|
959
|
+
title: title,
|
|
960
|
+
subtitle: subtitle
|
|
961
|
+
}, cardProps), /*#__PURE__*/React.createElement("form", {
|
|
962
|
+
onSubmit: form.onSubmit(handleSubmit)
|
|
963
|
+
}, /*#__PURE__*/React.createElement(Stack, {
|
|
964
|
+
gap: "sm"
|
|
965
|
+
}, /*#__PURE__*/React.createElement(TextInput, _extends({
|
|
966
|
+
label: labels.email || 'Email',
|
|
967
|
+
placeholder: labels.emailPlaceholder || 'seu@email.com',
|
|
968
|
+
leftSection: /*#__PURE__*/React.createElement(IconMail, {
|
|
969
|
+
size: 16
|
|
970
|
+
})
|
|
971
|
+
}, form.getInputProps('email'), {
|
|
972
|
+
disabled: isLoading
|
|
973
|
+
})), /*#__PURE__*/React.createElement(PasswordInput, _extends({
|
|
974
|
+
label: labels.password || 'Senha',
|
|
975
|
+
placeholder: labels.passwordPlaceholder || 'Sua senha',
|
|
976
|
+
leftSection: /*#__PURE__*/React.createElement(IconLock, {
|
|
977
|
+
size: 16
|
|
978
|
+
})
|
|
979
|
+
}, form.getInputProps('password'), {
|
|
980
|
+
disabled: isLoading
|
|
981
|
+
})), showForgotPassword && /*#__PURE__*/React.createElement(Anchor, {
|
|
982
|
+
size: "sm",
|
|
983
|
+
ta: "right",
|
|
984
|
+
onClick: handleForgotPassword,
|
|
985
|
+
style: {
|
|
986
|
+
cursor: 'pointer'
|
|
987
|
+
},
|
|
988
|
+
c: loadingResetPassword ? 'dimmed' : 'blue'
|
|
989
|
+
}, loadingResetPassword ? labels.sendingReset || 'Enviando...' : labels.forgotPassword || 'Esqueceu a senha?'), /*#__PURE__*/React.createElement(Button, {
|
|
990
|
+
type: "submit",
|
|
991
|
+
fullWidth: true,
|
|
992
|
+
loading: loadingSignIn,
|
|
993
|
+
rightSection: /*#__PURE__*/React.createElement(IconArrowRight, {
|
|
994
|
+
size: 16
|
|
995
|
+
})
|
|
996
|
+
}, labels.signInButton || 'Entrar'))), showMagicLink && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Divider, {
|
|
997
|
+
label: labels.orContinueWith || 'Ou continue com'
|
|
998
|
+
}), /*#__PURE__*/React.createElement(Button, {
|
|
999
|
+
variant: "light",
|
|
1000
|
+
fullWidth: true,
|
|
1001
|
+
loading: loadingMagicLink,
|
|
1002
|
+
leftSection: /*#__PURE__*/React.createElement(IconMail, {
|
|
1003
|
+
size: 16
|
|
1004
|
+
}),
|
|
1005
|
+
onClick: handleMagicLink,
|
|
1006
|
+
disabled: isLoading || !form.values.email
|
|
1007
|
+
}, labels.magicLinkButton || 'Enviar Link Mágico')), showSocialLogin && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Divider, null), /*#__PURE__*/React.createElement(Group, {
|
|
1008
|
+
grow: true
|
|
1009
|
+
}, /*#__PURE__*/React.createElement(Button, {
|
|
1010
|
+
variant: "light",
|
|
1011
|
+
leftSection: /*#__PURE__*/React.createElement(IconBrandGoogle, {
|
|
1012
|
+
size: 16
|
|
1013
|
+
}),
|
|
1014
|
+
onClick: () => onSocialLogin?.('google'),
|
|
1015
|
+
disabled: isLoading
|
|
1016
|
+
}, "Google"), /*#__PURE__*/React.createElement(Button, {
|
|
1017
|
+
variant: "light",
|
|
1018
|
+
leftSection: /*#__PURE__*/React.createElement(IconBrandGithub, {
|
|
1019
|
+
size: 16
|
|
1020
|
+
}),
|
|
1021
|
+
onClick: () => onSocialLogin?.('github'),
|
|
1022
|
+
disabled: isLoading
|
|
1023
|
+
}, "GitHub"))), showSignUpLink && /*#__PURE__*/React.createElement(Center, null, /*#__PURE__*/React.createElement(Text, {
|
|
1024
|
+
size: "sm",
|
|
1025
|
+
c: "dimmed"
|
|
1026
|
+
}, labels.noAccount || 'Não tem uma conta?', " ", /*#__PURE__*/React.createElement(Anchor, {
|
|
1027
|
+
href: signUpUrl
|
|
1028
|
+
}, labels.createAccount || 'Criar conta'))));
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Formulário de Sign Up com Mantine
|
|
1033
|
+
*/
|
|
1034
|
+
function SignUpForm({
|
|
1035
|
+
// Configuração
|
|
1036
|
+
logo = img,
|
|
1037
|
+
title = 'Criar Conta',
|
|
1038
|
+
subtitle = 'Preencha os dados para se cadastrar',
|
|
1039
|
+
// Features
|
|
1040
|
+
requireName = true,
|
|
1041
|
+
showSignInLink = true,
|
|
1042
|
+
showSocialLogin = false,
|
|
1043
|
+
// Links
|
|
1044
|
+
signInUrl = '/auth/signin',
|
|
1045
|
+
// Callbacks
|
|
1046
|
+
onSuccess,
|
|
1047
|
+
onError,
|
|
1048
|
+
onSocialLogin,
|
|
1049
|
+
// Custom labels
|
|
1050
|
+
labels = {},
|
|
1051
|
+
...cardProps
|
|
1052
|
+
}) {
|
|
1053
|
+
const signUp = useAuthStore(s => s.signUp);
|
|
1054
|
+
const loading = useAuthStore(s => s.loadingStates.signUp);
|
|
1055
|
+
const form = useForm({
|
|
1056
|
+
initialValues: {
|
|
1057
|
+
name: '',
|
|
1058
|
+
email: '',
|
|
1059
|
+
password: '',
|
|
1060
|
+
confirmPassword: ''
|
|
1061
|
+
},
|
|
1062
|
+
validate: {
|
|
1063
|
+
name: value => requireName && !value ? labels.nameRequired || 'Nome obrigatório' : null,
|
|
1064
|
+
email: value => /^\S+@\S+$/.test(value) ? null : labels.invalidEmail || 'Email inválido',
|
|
1065
|
+
password: value => value.length < 6 ? labels.passwordTooShort || 'A senha deve ter pelo menos 6 caracteres' : null,
|
|
1066
|
+
confirmPassword: (value, values) => value !== values.password ? labels.passwordMismatch || 'As senhas não coincidem' : null
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
const handleSubmit = async values => {
|
|
1070
|
+
try {
|
|
1071
|
+
const result = await signUp(values.email, values.password, values.name);
|
|
1072
|
+
onSuccess?.(result);
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
onError?.(error);
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1078
|
+
logo: logo,
|
|
1079
|
+
title: title,
|
|
1080
|
+
subtitle: subtitle
|
|
1081
|
+
}, cardProps), /*#__PURE__*/React.createElement("form", {
|
|
1082
|
+
onSubmit: form.onSubmit(handleSubmit)
|
|
1083
|
+
}, /*#__PURE__*/React.createElement(Stack, {
|
|
1084
|
+
gap: "sm"
|
|
1085
|
+
}, requireName && /*#__PURE__*/React.createElement(TextInput, _extends({
|
|
1086
|
+
label: labels.name || 'Nome',
|
|
1087
|
+
placeholder: labels.namePlaceholder || 'Seu nome',
|
|
1088
|
+
leftSection: /*#__PURE__*/React.createElement(IconUser, {
|
|
1089
|
+
size: 16
|
|
1090
|
+
})
|
|
1091
|
+
}, form.getInputProps('name'), {
|
|
1092
|
+
disabled: loading
|
|
1093
|
+
})), /*#__PURE__*/React.createElement(TextInput, _extends({
|
|
1094
|
+
label: labels.email || 'Email',
|
|
1095
|
+
placeholder: labels.emailPlaceholder || 'seu@email.com',
|
|
1096
|
+
leftSection: /*#__PURE__*/React.createElement(IconMail, {
|
|
1097
|
+
size: 16
|
|
1098
|
+
})
|
|
1099
|
+
}, form.getInputProps('email'), {
|
|
1100
|
+
disabled: loading
|
|
1101
|
+
})), /*#__PURE__*/React.createElement(PasswordInput, _extends({
|
|
1102
|
+
label: labels.password || 'Senha',
|
|
1103
|
+
placeholder: labels.passwordPlaceholder || 'Crie uma senha',
|
|
1104
|
+
leftSection: /*#__PURE__*/React.createElement(IconLock, {
|
|
1105
|
+
size: 16
|
|
1106
|
+
})
|
|
1107
|
+
}, form.getInputProps('password'), {
|
|
1108
|
+
disabled: loading
|
|
1109
|
+
})), /*#__PURE__*/React.createElement(PasswordInput, _extends({
|
|
1110
|
+
label: labels.confirmPassword || 'Confirmar Senha',
|
|
1111
|
+
placeholder: labels.confirmPasswordPlaceholder || 'Repita a senha',
|
|
1112
|
+
leftSection: /*#__PURE__*/React.createElement(IconLock, {
|
|
1113
|
+
size: 16
|
|
1114
|
+
})
|
|
1115
|
+
}, form.getInputProps('confirmPassword'), {
|
|
1116
|
+
disabled: loading
|
|
1117
|
+
})), /*#__PURE__*/React.createElement(Button, {
|
|
1118
|
+
type: "submit",
|
|
1119
|
+
fullWidth: true,
|
|
1120
|
+
loading: loading,
|
|
1121
|
+
rightSection: /*#__PURE__*/React.createElement(IconArrowRight, {
|
|
1122
|
+
size: 16
|
|
1123
|
+
})
|
|
1124
|
+
}, labels.signUpButton || 'Criar Conta'))), showSocialLogin && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Divider, {
|
|
1125
|
+
label: labels.orContinueWith || 'Ou continue com'
|
|
1126
|
+
}), /*#__PURE__*/React.createElement(Group, {
|
|
1127
|
+
grow: true
|
|
1128
|
+
}, /*#__PURE__*/React.createElement(Button, {
|
|
1129
|
+
variant: "light",
|
|
1130
|
+
leftSection: /*#__PURE__*/React.createElement(IconBrandGoogle, {
|
|
1131
|
+
size: 16
|
|
1132
|
+
}),
|
|
1133
|
+
onClick: () => onSocialLogin?.('google'),
|
|
1134
|
+
disabled: loading
|
|
1135
|
+
}, "Google"), /*#__PURE__*/React.createElement(Button, {
|
|
1136
|
+
variant: "light",
|
|
1137
|
+
leftSection: /*#__PURE__*/React.createElement(IconBrandGithub, {
|
|
1138
|
+
size: 16
|
|
1139
|
+
}),
|
|
1140
|
+
onClick: () => onSocialLogin?.('github'),
|
|
1141
|
+
disabled: loading
|
|
1142
|
+
}, "GitHub"))), showSignInLink && /*#__PURE__*/React.createElement(Center, null, /*#__PURE__*/React.createElement(Text, {
|
|
1143
|
+
size: "sm",
|
|
1144
|
+
c: "dimmed"
|
|
1145
|
+
}, labels.hasAccount || 'Já tem uma conta?', " ", /*#__PURE__*/React.createElement(Anchor, {
|
|
1146
|
+
href: signInUrl
|
|
1147
|
+
}, labels.signIn || 'Entrar'))));
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Formulário para envio de Magic Link
|
|
1152
|
+
*/
|
|
1153
|
+
function MagicLinkForm({
|
|
1154
|
+
// Configuração
|
|
1155
|
+
logo = img,
|
|
1156
|
+
title = 'Login sem Senha',
|
|
1157
|
+
subtitle = 'Receba um link de acesso no seu email',
|
|
1158
|
+
// Features
|
|
1159
|
+
showSignInLink = true,
|
|
1160
|
+
// Links
|
|
1161
|
+
signInUrl = '/auth/signin',
|
|
1162
|
+
// Callbacks
|
|
1163
|
+
onSent,
|
|
1164
|
+
onError,
|
|
1165
|
+
// Custom labels
|
|
1166
|
+
labels = {},
|
|
1167
|
+
...cardProps
|
|
1168
|
+
}) {
|
|
1169
|
+
const sendMagicLink = useAuthStore(s => s.sendMagicLink);
|
|
1170
|
+
const loading = useAuthStore(s => s.loadingStates.magicLink);
|
|
1171
|
+
const form = useForm({
|
|
1172
|
+
initialValues: {
|
|
1173
|
+
email: ''
|
|
1174
|
+
},
|
|
1175
|
+
validate: {
|
|
1176
|
+
email: value => /^\S+@\S+$/.test(value) ? null : labels.invalidEmail || 'Email inválido'
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
const handleSubmit = async values => {
|
|
1180
|
+
try {
|
|
1181
|
+
await sendMagicLink(values.email);
|
|
1182
|
+
onSent?.(values.email);
|
|
1183
|
+
} catch (error) {
|
|
1184
|
+
onError?.(error);
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1188
|
+
logo: logo,
|
|
1189
|
+
title: title,
|
|
1190
|
+
subtitle: subtitle
|
|
1191
|
+
}, cardProps), /*#__PURE__*/React.createElement("form", {
|
|
1192
|
+
onSubmit: form.onSubmit(handleSubmit)
|
|
1193
|
+
}, /*#__PURE__*/React.createElement(Stack, {
|
|
1194
|
+
gap: "sm"
|
|
1195
|
+
}, /*#__PURE__*/React.createElement(TextInput, _extends({
|
|
1196
|
+
label: labels.email || 'Email',
|
|
1197
|
+
placeholder: labels.emailPlaceholder || 'seu@email.com',
|
|
1198
|
+
leftSection: /*#__PURE__*/React.createElement(IconMail, {
|
|
1199
|
+
size: 16
|
|
1200
|
+
})
|
|
1201
|
+
}, form.getInputProps('email'), {
|
|
1202
|
+
disabled: loading
|
|
1203
|
+
})), /*#__PURE__*/React.createElement(Button, {
|
|
1204
|
+
type: "submit",
|
|
1205
|
+
fullWidth: true,
|
|
1206
|
+
loading: loading,
|
|
1207
|
+
leftSection: /*#__PURE__*/React.createElement(IconSend, {
|
|
1208
|
+
size: 16
|
|
1209
|
+
})
|
|
1210
|
+
}, labels.sendButton || 'Enviar Link Mágico'))), showSignInLink && /*#__PURE__*/React.createElement(Center, null, /*#__PURE__*/React.createElement(Text, {
|
|
1211
|
+
size: "sm",
|
|
1212
|
+
c: "dimmed"
|
|
1213
|
+
}, labels.backTo || 'Voltar para', " ", /*#__PURE__*/React.createElement(Anchor, {
|
|
1214
|
+
href: signInUrl
|
|
1215
|
+
}, labels.signIn || 'login com senha'))));
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/**
|
|
1219
|
+
* Componente de verificação de Magic Link
|
|
1220
|
+
* Extrai token da URL e verifica automaticamente
|
|
1221
|
+
*/
|
|
1222
|
+
function MagicLinkVerify({
|
|
1223
|
+
// Configuração
|
|
1224
|
+
logo = img,
|
|
1225
|
+
// Token pode ser passado diretamente ou extraído da URL
|
|
1226
|
+
token: propToken,
|
|
1227
|
+
// Callbacks
|
|
1228
|
+
onSuccess,
|
|
1229
|
+
onError,
|
|
1230
|
+
// Redirect após sucesso/erro
|
|
1231
|
+
redirectTo,
|
|
1232
|
+
redirectDelay = 2000,
|
|
1233
|
+
// Custom labels
|
|
1234
|
+
labels = {},
|
|
1235
|
+
...cardProps
|
|
1236
|
+
}) {
|
|
1237
|
+
const [status, setStatus] = useState('verifying'); // verifying, success, error
|
|
1238
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
1239
|
+
const verifyMagicLink = useAuthStore(s => s.verifyMagicLink);
|
|
1240
|
+
useEffect(() => {
|
|
1241
|
+
const verify = async () => {
|
|
1242
|
+
// Pega token da prop ou da URL
|
|
1243
|
+
let token = propToken;
|
|
1244
|
+
if (!token && typeof window !== 'undefined') {
|
|
1245
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
1246
|
+
token = urlParams.get('token');
|
|
1247
|
+
}
|
|
1248
|
+
if (!token) {
|
|
1249
|
+
setStatus('error');
|
|
1250
|
+
setErrorMessage(labels.noToken || 'Token não encontrado na URL');
|
|
1251
|
+
onError?.(new Error('No token'));
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
try {
|
|
1255
|
+
const result = await verifyMagicLink(token);
|
|
1256
|
+
setStatus('success');
|
|
1257
|
+
onSuccess?.(result);
|
|
1258
|
+
if (redirectTo) {
|
|
1259
|
+
setTimeout(() => {
|
|
1260
|
+
window.location.href = redirectTo;
|
|
1261
|
+
}, redirectDelay);
|
|
1262
|
+
}
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
setStatus('error');
|
|
1265
|
+
setErrorMessage(error.message || labels.verificationFailed || 'Verificação falhou');
|
|
1266
|
+
onError?.(error);
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
verify();
|
|
1270
|
+
}, [propToken, verifyMagicLink, onSuccess, onError, redirectTo, redirectDelay, labels]);
|
|
1271
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1272
|
+
logo: logo
|
|
1273
|
+
}, cardProps), status === 'verifying' && /*#__PURE__*/React.createElement(Stack, {
|
|
1274
|
+
align: "center",
|
|
1275
|
+
gap: "sm"
|
|
1276
|
+
}, /*#__PURE__*/React.createElement(Loader, {
|
|
1277
|
+
size: "lg"
|
|
1278
|
+
}), /*#__PURE__*/React.createElement(Title, {
|
|
1279
|
+
order: 4
|
|
1280
|
+
}, labels.verifying || 'Verificando Link Mágico'), /*#__PURE__*/React.createElement(Text, {
|
|
1281
|
+
size: "sm",
|
|
1282
|
+
c: "dimmed",
|
|
1283
|
+
ta: "center"
|
|
1284
|
+
}, labels.pleaseWait || 'Aguarde enquanto autenticamos você...')), status === 'success' && /*#__PURE__*/React.createElement(Stack, {
|
|
1285
|
+
align: "center",
|
|
1286
|
+
gap: "sm"
|
|
1287
|
+
}, /*#__PURE__*/React.createElement(IconCheck, {
|
|
1288
|
+
size: 48,
|
|
1289
|
+
color: "var(--mantine-color-green-6)"
|
|
1290
|
+
}), /*#__PURE__*/React.createElement(Title, {
|
|
1291
|
+
order: 4
|
|
1292
|
+
}, labels.success || 'Autenticado com Sucesso!'), /*#__PURE__*/React.createElement(Text, {
|
|
1293
|
+
size: "sm",
|
|
1294
|
+
c: "dimmed",
|
|
1295
|
+
ta: "center"
|
|
1296
|
+
}, redirectTo ? labels.redirecting || 'Redirecionando...' : labels.authenticated || 'Você está autenticado.')), status === 'error' && /*#__PURE__*/React.createElement(Stack, {
|
|
1297
|
+
align: "center",
|
|
1298
|
+
gap: "sm"
|
|
1299
|
+
}, /*#__PURE__*/React.createElement(IconX, {
|
|
1300
|
+
size: 48,
|
|
1301
|
+
color: "var(--mantine-color-red-6)"
|
|
1302
|
+
}), /*#__PURE__*/React.createElement(Title, {
|
|
1303
|
+
order: 4
|
|
1304
|
+
}, labels.failed || 'Falha na Autenticação'), /*#__PURE__*/React.createElement(Text, {
|
|
1305
|
+
size: "sm",
|
|
1306
|
+
c: "dimmed",
|
|
1307
|
+
ta: "center"
|
|
1308
|
+
}, errorMessage || labels.invalidLink || 'O link é inválido ou expirou.')));
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* Formulário para solicitar reset de senha
|
|
1313
|
+
*/
|
|
1314
|
+
function ForgotPasswordForm({
|
|
1315
|
+
// Configuração
|
|
1316
|
+
logo = img,
|
|
1317
|
+
title = 'Recuperar Senha',
|
|
1318
|
+
subtitle = 'Enviaremos um link para redefinir sua senha',
|
|
1319
|
+
// Features
|
|
1320
|
+
showSignInLink = true,
|
|
1321
|
+
// Links
|
|
1322
|
+
signInUrl = '/auth/signin',
|
|
1323
|
+
// Callbacks
|
|
1324
|
+
onSent,
|
|
1325
|
+
onError,
|
|
1326
|
+
// Custom labels
|
|
1327
|
+
labels = {},
|
|
1328
|
+
...cardProps
|
|
1329
|
+
}) {
|
|
1330
|
+
const forgotPassword = useAuthStore(s => s.forgotPassword);
|
|
1331
|
+
const loading = useAuthStore(s => s.loadingStates.resetPassword);
|
|
1332
|
+
const form = useForm({
|
|
1333
|
+
initialValues: {
|
|
1334
|
+
email: ''
|
|
1335
|
+
},
|
|
1336
|
+
validate: {
|
|
1337
|
+
email: value => /^\S+@\S+$/.test(value) ? null : labels.invalidEmail || 'Email inválido'
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
const handleSubmit = async values => {
|
|
1341
|
+
try {
|
|
1342
|
+
await forgotPassword(values.email);
|
|
1343
|
+
onSent?.(values.email);
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
onError?.(error);
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1349
|
+
logo: logo,
|
|
1350
|
+
title: title,
|
|
1351
|
+
subtitle: subtitle
|
|
1352
|
+
}, cardProps), /*#__PURE__*/React.createElement("form", {
|
|
1353
|
+
onSubmit: form.onSubmit(handleSubmit)
|
|
1354
|
+
}, /*#__PURE__*/React.createElement(Stack, {
|
|
1355
|
+
gap: "sm"
|
|
1356
|
+
}, /*#__PURE__*/React.createElement(TextInput, _extends({
|
|
1357
|
+
label: labels.email || 'Email',
|
|
1358
|
+
placeholder: labels.emailPlaceholder || 'seu@email.com',
|
|
1359
|
+
leftSection: /*#__PURE__*/React.createElement(IconMail, {
|
|
1360
|
+
size: 16
|
|
1361
|
+
})
|
|
1362
|
+
}, form.getInputProps('email'), {
|
|
1363
|
+
disabled: loading
|
|
1364
|
+
})), /*#__PURE__*/React.createElement(Button, {
|
|
1365
|
+
type: "submit",
|
|
1366
|
+
fullWidth: true,
|
|
1367
|
+
loading: loading,
|
|
1368
|
+
leftSection: /*#__PURE__*/React.createElement(IconSend, {
|
|
1369
|
+
size: 16
|
|
1370
|
+
})
|
|
1371
|
+
}, labels.sendButton || 'Enviar Link de Recuperação'))), showSignInLink && /*#__PURE__*/React.createElement(Center, null, /*#__PURE__*/React.createElement(Text, {
|
|
1372
|
+
size: "sm",
|
|
1373
|
+
c: "dimmed"
|
|
1374
|
+
}, labels.rememberedPassword || 'Lembrou a senha?', " ", /*#__PURE__*/React.createElement(Anchor, {
|
|
1375
|
+
href: signInUrl
|
|
1376
|
+
}, labels.signIn || 'Entrar'))));
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* Formulário para redefinir senha com token
|
|
1381
|
+
*/
|
|
1382
|
+
function ResetPasswordForm({
|
|
1383
|
+
// Configuração
|
|
1384
|
+
logo = img,
|
|
1385
|
+
title = 'Nova Senha',
|
|
1386
|
+
subtitle = 'Crie uma nova senha para sua conta',
|
|
1387
|
+
// Token pode ser passado diretamente ou extraído da URL
|
|
1388
|
+
token: propToken,
|
|
1389
|
+
// Callbacks
|
|
1390
|
+
onSuccess,
|
|
1391
|
+
onError,
|
|
1392
|
+
// Redirect após sucesso
|
|
1393
|
+
signInUrl = '/auth/signin',
|
|
1394
|
+
// Custom labels
|
|
1395
|
+
labels = {},
|
|
1396
|
+
...cardProps
|
|
1397
|
+
}) {
|
|
1398
|
+
const [token, setToken] = useState(propToken || '');
|
|
1399
|
+
const [success, setSuccess] = useState(false);
|
|
1400
|
+
const resetPassword = useAuthStore(s => s.resetPassword);
|
|
1401
|
+
const loading = useAuthStore(s => s.loadingStates.resetPassword);
|
|
1402
|
+
|
|
1403
|
+
// Extrai token da URL se não foi passado como prop
|
|
1404
|
+
useEffect(() => {
|
|
1405
|
+
if (!propToken && typeof window !== 'undefined') {
|
|
1406
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
1407
|
+
const urlToken = urlParams.get('token');
|
|
1408
|
+
if (urlToken) setToken(urlToken);
|
|
1409
|
+
}
|
|
1410
|
+
}, [propToken]);
|
|
1411
|
+
const form = useForm({
|
|
1412
|
+
initialValues: {
|
|
1413
|
+
password: '',
|
|
1414
|
+
confirmPassword: ''
|
|
1415
|
+
},
|
|
1416
|
+
validate: {
|
|
1417
|
+
password: value => value.length < 6 ? labels.passwordTooShort || 'A senha deve ter pelo menos 6 caracteres' : null,
|
|
1418
|
+
confirmPassword: (value, values) => value !== values.password ? labels.passwordMismatch || 'As senhas não coincidem' : null
|
|
1419
|
+
}
|
|
1420
|
+
});
|
|
1421
|
+
const handleSubmit = async values => {
|
|
1422
|
+
if (!token) {
|
|
1423
|
+
onError?.(new Error('Token não encontrado'));
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
try {
|
|
1427
|
+
await resetPassword(token, values.password);
|
|
1428
|
+
setSuccess(true);
|
|
1429
|
+
onSuccess?.();
|
|
1430
|
+
} catch (error) {
|
|
1431
|
+
onError?.(error);
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
if (success) {
|
|
1435
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1436
|
+
logo: logo
|
|
1437
|
+
}, cardProps), /*#__PURE__*/React.createElement(Stack, {
|
|
1438
|
+
align: "center",
|
|
1439
|
+
gap: "sm"
|
|
1440
|
+
}, /*#__PURE__*/React.createElement(IconCheck, {
|
|
1441
|
+
size: 48,
|
|
1442
|
+
color: "var(--mantine-color-green-6)"
|
|
1443
|
+
}), /*#__PURE__*/React.createElement(Title, {
|
|
1444
|
+
order: 4
|
|
1445
|
+
}, labels.successTitle || 'Senha Redefinida!'), /*#__PURE__*/React.createElement(Text, {
|
|
1446
|
+
size: "sm",
|
|
1447
|
+
c: "dimmed",
|
|
1448
|
+
ta: "center"
|
|
1449
|
+
}, labels.successMessage || 'Sua senha foi alterada com sucesso.'), /*#__PURE__*/React.createElement(Button, {
|
|
1450
|
+
component: "a",
|
|
1451
|
+
href: signInUrl,
|
|
1452
|
+
fullWidth: true
|
|
1453
|
+
}, labels.goToSignIn || 'Ir para Login')));
|
|
1454
|
+
}
|
|
1455
|
+
if (!token) {
|
|
1456
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1457
|
+
logo: logo
|
|
1458
|
+
}, cardProps), /*#__PURE__*/React.createElement(Stack, {
|
|
1459
|
+
align: "center",
|
|
1460
|
+
gap: "sm"
|
|
1461
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
1462
|
+
c: "red"
|
|
1463
|
+
}, labels.noToken || 'Token não encontrado na URL'), /*#__PURE__*/React.createElement(Button, {
|
|
1464
|
+
component: "a",
|
|
1465
|
+
href: signInUrl,
|
|
1466
|
+
variant: "light",
|
|
1467
|
+
fullWidth: true
|
|
1468
|
+
}, labels.goToSignIn || 'Voltar para Login')));
|
|
1469
|
+
}
|
|
1470
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1471
|
+
logo: logo,
|
|
1472
|
+
title: title,
|
|
1473
|
+
subtitle: subtitle
|
|
1474
|
+
}, cardProps), /*#__PURE__*/React.createElement("form", {
|
|
1475
|
+
onSubmit: form.onSubmit(handleSubmit)
|
|
1476
|
+
}, /*#__PURE__*/React.createElement(Stack, {
|
|
1477
|
+
gap: "sm"
|
|
1478
|
+
}, /*#__PURE__*/React.createElement(PasswordInput, _extends({
|
|
1479
|
+
label: labels.newPassword || 'Nova Senha',
|
|
1480
|
+
placeholder: labels.newPasswordPlaceholder || 'Crie uma nova senha',
|
|
1481
|
+
leftSection: /*#__PURE__*/React.createElement(IconLock, {
|
|
1482
|
+
size: 16
|
|
1483
|
+
})
|
|
1484
|
+
}, form.getInputProps('password'), {
|
|
1485
|
+
disabled: loading
|
|
1486
|
+
})), /*#__PURE__*/React.createElement(PasswordInput, _extends({
|
|
1487
|
+
label: labels.confirmPassword || 'Confirmar Senha',
|
|
1488
|
+
placeholder: labels.confirmPasswordPlaceholder || 'Repita a nova senha',
|
|
1489
|
+
leftSection: /*#__PURE__*/React.createElement(IconLock, {
|
|
1490
|
+
size: 16
|
|
1491
|
+
})
|
|
1492
|
+
}, form.getInputProps('confirmPassword'), {
|
|
1493
|
+
disabled: loading
|
|
1494
|
+
})), /*#__PURE__*/React.createElement(Button, {
|
|
1495
|
+
type: "submit",
|
|
1496
|
+
fullWidth: true,
|
|
1497
|
+
loading: loading
|
|
1498
|
+
}, labels.resetButton || 'Redefinir Senha'))));
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* Card de verificação de email
|
|
1503
|
+
* Verifica token automaticamente e permite reenvio
|
|
1504
|
+
*/
|
|
1505
|
+
function VerifyEmailCard({
|
|
1506
|
+
// Configuração
|
|
1507
|
+
logo = img,
|
|
1508
|
+
// Token pode ser passado diretamente ou extraído da URL
|
|
1509
|
+
token: propToken,
|
|
1510
|
+
// Email para reenvio (opcional)
|
|
1511
|
+
email,
|
|
1512
|
+
// Callbacks
|
|
1513
|
+
onSuccess,
|
|
1514
|
+
onError,
|
|
1515
|
+
onResent,
|
|
1516
|
+
// Redirect após sucesso
|
|
1517
|
+
redirectTo,
|
|
1518
|
+
redirectDelay = 2000,
|
|
1519
|
+
// Custom labels
|
|
1520
|
+
labels = {},
|
|
1521
|
+
...cardProps
|
|
1522
|
+
}) {
|
|
1523
|
+
const [status, setStatus] = useState('verifying'); // verifying, success, error
|
|
1524
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
1525
|
+
const verifyEmail = useAuthStore(s => s.verifyEmail);
|
|
1526
|
+
const resendVerification = useAuthStore(s => s.resendVerification);
|
|
1527
|
+
const loadingResend = useAuthStore(s => s.loadingStates.resendVerification);
|
|
1528
|
+
useEffect(() => {
|
|
1529
|
+
const verify = async () => {
|
|
1530
|
+
// Pega token da prop ou da URL
|
|
1531
|
+
let token = propToken;
|
|
1532
|
+
if (!token && typeof window !== 'undefined') {
|
|
1533
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
1534
|
+
token = urlParams.get('token');
|
|
1535
|
+
}
|
|
1536
|
+
if (!token) {
|
|
1537
|
+
setStatus('error');
|
|
1538
|
+
setErrorMessage(labels.noToken || 'Token não encontrado na URL');
|
|
1539
|
+
onError?.(new Error('No token'));
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
try {
|
|
1543
|
+
const result = await verifyEmail(token);
|
|
1544
|
+
setStatus('success');
|
|
1545
|
+
onSuccess?.(result);
|
|
1546
|
+
if (redirectTo) {
|
|
1547
|
+
setTimeout(() => {
|
|
1548
|
+
window.location.href = redirectTo;
|
|
1549
|
+
}, redirectDelay);
|
|
1550
|
+
}
|
|
1551
|
+
} catch (error) {
|
|
1552
|
+
setStatus('error');
|
|
1553
|
+
setErrorMessage(error.message || labels.verificationFailed || 'Verificação falhou');
|
|
1554
|
+
onError?.(error);
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
verify();
|
|
1558
|
+
}, [propToken, verifyEmail, onSuccess, onError, redirectTo, redirectDelay, labels]);
|
|
1559
|
+
const handleResend = async () => {
|
|
1560
|
+
if (!email) return;
|
|
1561
|
+
try {
|
|
1562
|
+
await resendVerification(email);
|
|
1563
|
+
onResent?.(email);
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
onError?.(error);
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1569
|
+
logo: logo
|
|
1570
|
+
}, cardProps), status === 'verifying' && /*#__PURE__*/React.createElement(Stack, {
|
|
1571
|
+
align: "center",
|
|
1572
|
+
gap: "sm"
|
|
1573
|
+
}, /*#__PURE__*/React.createElement(Loader, {
|
|
1574
|
+
size: "lg"
|
|
1575
|
+
}), /*#__PURE__*/React.createElement(Title, {
|
|
1576
|
+
order: 4
|
|
1577
|
+
}, labels.verifying || 'Verificando Email'), /*#__PURE__*/React.createElement(Text, {
|
|
1578
|
+
size: "sm",
|
|
1579
|
+
c: "dimmed",
|
|
1580
|
+
ta: "center"
|
|
1581
|
+
}, labels.pleaseWait || 'Aguarde enquanto verificamos seu email...')), status === 'success' && /*#__PURE__*/React.createElement(Stack, {
|
|
1582
|
+
align: "center",
|
|
1583
|
+
gap: "sm"
|
|
1584
|
+
}, /*#__PURE__*/React.createElement(IconCheck, {
|
|
1585
|
+
size: 48,
|
|
1586
|
+
color: "var(--mantine-color-green-6)"
|
|
1587
|
+
}), /*#__PURE__*/React.createElement(Title, {
|
|
1588
|
+
order: 4
|
|
1589
|
+
}, labels.success || 'Email Verificado!'), /*#__PURE__*/React.createElement(Text, {
|
|
1590
|
+
size: "sm",
|
|
1591
|
+
c: "dimmed",
|
|
1592
|
+
ta: "center"
|
|
1593
|
+
}, redirectTo ? labels.redirecting || 'Redirecionando...' : labels.verified || 'Seu email foi verificado com sucesso.')), status === 'error' && /*#__PURE__*/React.createElement(Stack, {
|
|
1594
|
+
align: "center",
|
|
1595
|
+
gap: "sm"
|
|
1596
|
+
}, /*#__PURE__*/React.createElement(IconX, {
|
|
1597
|
+
size: 48,
|
|
1598
|
+
color: "var(--mantine-color-red-6)"
|
|
1599
|
+
}), /*#__PURE__*/React.createElement(Title, {
|
|
1600
|
+
order: 4
|
|
1601
|
+
}, labels.failed || 'Verificação Falhou'), /*#__PURE__*/React.createElement(Text, {
|
|
1602
|
+
size: "sm",
|
|
1603
|
+
c: "dimmed",
|
|
1604
|
+
ta: "center"
|
|
1605
|
+
}, errorMessage || labels.invalidToken || 'O link é inválido ou expirou.'), email && /*#__PURE__*/React.createElement(Button, {
|
|
1606
|
+
variant: "light",
|
|
1607
|
+
leftSection: /*#__PURE__*/React.createElement(IconRefresh, {
|
|
1608
|
+
size: 16
|
|
1609
|
+
}),
|
|
1610
|
+
onClick: handleResend,
|
|
1611
|
+
loading: loadingResend,
|
|
1612
|
+
fullWidth: true
|
|
1613
|
+
}, labels.resend || 'Reenviar Email de Verificação')));
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
export { AuthCard, AuthProvider, ForgotPasswordForm, MagicLinkForm, MagicLinkVerify, ProtectedRoute, ResetPasswordForm, SignInForm, SignUpForm, VerifyEmailCard, configure, forgotPassword, getCurrentUser, getSession, isAuthenticated, refreshToken, resendVerification, resetPassword, sendMagicLink, signIn, signOut, signUp, socialRedirect, useAuth, useAuthLoading, useAuthStore, useCheckToken, useEmailVerification, useMagicLink, usePasswordReset, useSession, useSignIn, useSignOut, useSignUp, verifyEmail, verifyMagicLink };
|
|
828
1617
|
//# sourceMappingURL=index.esm.js.map
|