@rqdhw3n/react-auth-flow 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +459 -631
- package/dist/index.cjs.js +1178 -537
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +132 -71
- package/dist/index.es.js +1144 -503
- package/dist/index.es.js.map +1 -1
- package/dist/style.css +415 -0
- package/package.json +26 -21
package/dist/index.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { createContext, useReducer, useMemo, useCallback, useEffect, useContext, useState, useRef, useId } from "react";
|
|
3
3
|
import { Navigate } from "react-router-dom";
|
|
4
4
|
const AuthContext = createContext(
|
|
5
5
|
void 0
|
|
@@ -62,150 +62,304 @@ const DEFAULT_ENDPOINTS = {
|
|
|
62
62
|
refresh: "/auth/refresh",
|
|
63
63
|
forgotPassword: "/auth/forgot-password",
|
|
64
64
|
resetPassword: "/auth/reset-password",
|
|
65
|
-
verifyEmail: "/auth/verify-email"
|
|
66
|
-
|
|
67
|
-
const defaultAdapter = async (url, options) => {
|
|
68
|
-
const response = await fetch(url, options);
|
|
69
|
-
return response;
|
|
65
|
+
verifyEmail: "/auth/verify-email",
|
|
66
|
+
twoFactorVerify: "/auth/2fa/verify"
|
|
70
67
|
};
|
|
68
|
+
async function parseResponse(response) {
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
let errorData = null;
|
|
71
|
+
try {
|
|
72
|
+
errorData = await response.json();
|
|
73
|
+
} catch {
|
|
74
|
+
errorData = {
|
|
75
|
+
error: {
|
|
76
|
+
message: response.statusText || "Request failed",
|
|
77
|
+
statusCode: response.status
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
throw errorData;
|
|
82
|
+
}
|
|
83
|
+
if (response.status === 204) {
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
87
|
+
if (contentType.includes("application/json")) {
|
|
88
|
+
return response.json();
|
|
89
|
+
}
|
|
90
|
+
const text = await response.text();
|
|
91
|
+
return text ? { message: text } : {};
|
|
92
|
+
}
|
|
93
|
+
function isResponseLike(value) {
|
|
94
|
+
return typeof value === "object" && value !== null && "ok" in value && "status" in value && "headers" in value;
|
|
95
|
+
}
|
|
96
|
+
function createDefaultRequestAdapter(config) {
|
|
97
|
+
const { baseURL, credentials, headers } = config;
|
|
98
|
+
return async ({ endpoint, method, data, headers: requestHeaders }) => {
|
|
99
|
+
const response = await fetch(`${baseURL}${endpoint}`, {
|
|
100
|
+
method,
|
|
101
|
+
credentials,
|
|
102
|
+
headers: {
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
...headers,
|
|
105
|
+
...requestHeaders
|
|
106
|
+
},
|
|
107
|
+
body: data === void 0 ? void 0 : JSON.stringify(data)
|
|
108
|
+
});
|
|
109
|
+
return parseResponse(response);
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function createLegacyAdapter(config) {
|
|
113
|
+
const { adapter, baseURL, credentials, headers } = config;
|
|
114
|
+
return async ({ endpoint, method, data, headers: requestHeaders }) => {
|
|
115
|
+
const response = await adapter(`${baseURL}${endpoint}`, {
|
|
116
|
+
method,
|
|
117
|
+
credentials,
|
|
118
|
+
headers: {
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
...headers,
|
|
121
|
+
...requestHeaders
|
|
122
|
+
},
|
|
123
|
+
body: data === void 0 ? void 0 : JSON.stringify(data)
|
|
124
|
+
});
|
|
125
|
+
return parseResponse(response);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
71
128
|
function createAuthClient(config = {}) {
|
|
72
129
|
const {
|
|
73
|
-
baseURL
|
|
130
|
+
baseURL,
|
|
131
|
+
baseUrl,
|
|
74
132
|
endpoints = {},
|
|
75
|
-
headers = {},
|
|
133
|
+
headers: initialHeaders = {},
|
|
76
134
|
credentials = "include",
|
|
77
|
-
|
|
135
|
+
requestAdapter,
|
|
136
|
+
adapter
|
|
78
137
|
} = config;
|
|
79
|
-
const
|
|
138
|
+
const resolvedBaseURL = baseURL ?? baseUrl ?? "";
|
|
139
|
+
const finalEndpoints = {
|
|
140
|
+
...DEFAULT_ENDPOINTS,
|
|
141
|
+
...endpoints
|
|
142
|
+
};
|
|
143
|
+
const headers = { ...initialHeaders };
|
|
144
|
+
const resolvedAdapter = requestAdapter ?? (adapter ? createLegacyAdapter({
|
|
145
|
+
adapter,
|
|
146
|
+
baseURL: resolvedBaseURL,
|
|
147
|
+
credentials,
|
|
148
|
+
headers
|
|
149
|
+
}) : createDefaultRequestAdapter({
|
|
150
|
+
baseURL: resolvedBaseURL,
|
|
151
|
+
credentials,
|
|
152
|
+
headers
|
|
153
|
+
}));
|
|
80
154
|
async function request(method, endpoint, data) {
|
|
81
|
-
const url = `${baseURL}${endpoint}`;
|
|
82
|
-
const options = {
|
|
83
|
-
method,
|
|
84
|
-
credentials,
|
|
85
|
-
headers: {
|
|
86
|
-
"Content-Type": "application/json",
|
|
87
|
-
...headers
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
if (data) {
|
|
91
|
-
options.body = JSON.stringify(data);
|
|
92
|
-
}
|
|
93
155
|
try {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
message: response.statusText,
|
|
103
|
-
statusCode: response.status
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
throw errorData;
|
|
108
|
-
}
|
|
109
|
-
const contentType = response.headers.get("content-type");
|
|
110
|
-
if (contentType && contentType.includes("application/json")) {
|
|
111
|
-
return await response.json();
|
|
156
|
+
const result = await resolvedAdapter({
|
|
157
|
+
endpoint,
|
|
158
|
+
method,
|
|
159
|
+
data,
|
|
160
|
+
headers
|
|
161
|
+
});
|
|
162
|
+
if (isResponseLike(result)) {
|
|
163
|
+
return await parseResponse(result);
|
|
112
164
|
}
|
|
113
|
-
return {};
|
|
165
|
+
return result ?? {};
|
|
114
166
|
} catch (error) {
|
|
115
167
|
throw normalizeError(error);
|
|
116
168
|
}
|
|
117
169
|
}
|
|
118
170
|
return {
|
|
119
|
-
/**
|
|
120
|
-
* Login with email and password
|
|
121
|
-
*/
|
|
122
171
|
async login(email, password, rememberMe) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
);
|
|
128
|
-
return response;
|
|
172
|
+
return request("POST", finalEndpoints.login, {
|
|
173
|
+
email,
|
|
174
|
+
password,
|
|
175
|
+
rememberMe
|
|
176
|
+
});
|
|
129
177
|
},
|
|
130
|
-
/**
|
|
131
|
-
* Register a new account
|
|
132
|
-
*/
|
|
133
178
|
async register(payload) {
|
|
134
|
-
|
|
135
|
-
"POST",
|
|
136
|
-
finalEndpoints.register,
|
|
137
|
-
payload
|
|
138
|
-
);
|
|
139
|
-
return response;
|
|
179
|
+
return request("POST", finalEndpoints.register, payload);
|
|
140
180
|
},
|
|
141
|
-
/**
|
|
142
|
-
* Logout the user
|
|
143
|
-
*/
|
|
144
181
|
async logout() {
|
|
145
182
|
await request("POST", finalEndpoints.logout);
|
|
146
183
|
},
|
|
147
|
-
/**
|
|
148
|
-
* Get current user
|
|
149
|
-
*/
|
|
150
184
|
async me() {
|
|
151
|
-
|
|
152
|
-
"GET",
|
|
153
|
-
finalEndpoints.me
|
|
154
|
-
);
|
|
155
|
-
return response;
|
|
185
|
+
return request("GET", finalEndpoints.me);
|
|
156
186
|
},
|
|
157
|
-
/**
|
|
158
|
-
* Refresh the authentication session
|
|
159
|
-
*/
|
|
160
187
|
async refresh() {
|
|
161
|
-
|
|
162
|
-
"POST",
|
|
163
|
-
finalEndpoints.refresh
|
|
164
|
-
);
|
|
165
|
-
return response;
|
|
188
|
+
return request("POST", finalEndpoints.refresh);
|
|
166
189
|
},
|
|
167
|
-
/**
|
|
168
|
-
* Request a password reset
|
|
169
|
-
*/
|
|
170
190
|
async forgotPassword(email) {
|
|
171
|
-
|
|
191
|
+
return request("POST", finalEndpoints.forgotPassword, {
|
|
192
|
+
email
|
|
193
|
+
});
|
|
172
194
|
},
|
|
173
|
-
/**
|
|
174
|
-
* Reset password with token
|
|
175
|
-
*/
|
|
176
195
|
async resetPassword(token, password, confirmPassword) {
|
|
177
|
-
|
|
196
|
+
return request("POST", finalEndpoints.resetPassword, {
|
|
178
197
|
token,
|
|
179
198
|
password,
|
|
180
199
|
confirmPassword
|
|
181
200
|
});
|
|
182
201
|
},
|
|
183
|
-
/**
|
|
184
|
-
* Verify email with token
|
|
185
|
-
*/
|
|
186
202
|
async verifyEmail(token, email) {
|
|
187
|
-
|
|
203
|
+
return request("POST", finalEndpoints.verifyEmail, {
|
|
204
|
+
token,
|
|
205
|
+
email
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
async verifyTwoFactor(code) {
|
|
209
|
+
return request("POST", finalEndpoints.twoFactorVerify, {
|
|
210
|
+
code
|
|
211
|
+
});
|
|
188
212
|
},
|
|
189
|
-
/**
|
|
190
|
-
* Set custom headers for subsequent requests
|
|
191
|
-
*/
|
|
192
213
|
setHeaders(newHeaders) {
|
|
193
214
|
Object.assign(headers, newHeaders);
|
|
194
215
|
},
|
|
195
|
-
/**
|
|
196
|
-
* Get current endpoints configuration
|
|
197
|
-
*/
|
|
198
216
|
getEndpoints() {
|
|
199
217
|
return finalEndpoints;
|
|
200
218
|
}
|
|
201
219
|
};
|
|
202
220
|
}
|
|
221
|
+
const DEFAULT_STORAGE_KEY = "rq-auth-flow-user";
|
|
222
|
+
const DEFAULT_OTP_LENGTH = 6;
|
|
223
|
+
let memoryUser = null;
|
|
224
|
+
function getStorage() {
|
|
225
|
+
if (typeof window === "undefined") {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
return window.localStorage;
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function readStoredUser(storageKey) {
|
|
235
|
+
const storage = getStorage();
|
|
236
|
+
if (!storage) {
|
|
237
|
+
return memoryUser;
|
|
238
|
+
}
|
|
239
|
+
const rawValue = storage.getItem(storageKey);
|
|
240
|
+
if (!rawValue) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
return JSON.parse(rawValue);
|
|
245
|
+
} catch {
|
|
246
|
+
storage.removeItem(storageKey);
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function writeStoredUser(storageKey, user) {
|
|
251
|
+
const storage = getStorage();
|
|
252
|
+
memoryUser = user;
|
|
253
|
+
if (!storage) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (user === null) {
|
|
257
|
+
storage.removeItem(storageKey);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
storage.setItem(storageKey, JSON.stringify(user));
|
|
261
|
+
}
|
|
262
|
+
function createAdminUser(email, mockUser) {
|
|
263
|
+
return {
|
|
264
|
+
id: 1,
|
|
265
|
+
name: "Admin Test",
|
|
266
|
+
email,
|
|
267
|
+
roles: ["admin"],
|
|
268
|
+
permissions: ["users.manage", "billing.edit"],
|
|
269
|
+
...mockUser
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function createRegisteredUser(name, email, mockUser) {
|
|
273
|
+
return {
|
|
274
|
+
id: 2,
|
|
275
|
+
name: name || "New User",
|
|
276
|
+
email,
|
|
277
|
+
roles: ["user"],
|
|
278
|
+
permissions: ["users.create"],
|
|
279
|
+
...mockUser
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function toSuccessResponse(message, user) {
|
|
283
|
+
return user ? { success: true, message, user } : { success: true, message };
|
|
284
|
+
}
|
|
285
|
+
function createMockAuthAdapter(options = {}) {
|
|
286
|
+
const {
|
|
287
|
+
endpoints = {},
|
|
288
|
+
mockStorageKey = DEFAULT_STORAGE_KEY,
|
|
289
|
+
mockUser
|
|
290
|
+
} = options;
|
|
291
|
+
const resolvedEndpoints = {
|
|
292
|
+
login: "/auth/login",
|
|
293
|
+
register: "/auth/register",
|
|
294
|
+
logout: "/auth/logout",
|
|
295
|
+
me: "/auth/me",
|
|
296
|
+
refresh: "/auth/refresh",
|
|
297
|
+
forgotPassword: "/auth/forgot-password",
|
|
298
|
+
resetPassword: "/auth/reset-password",
|
|
299
|
+
verifyEmail: "/auth/verify-email",
|
|
300
|
+
twoFactorVerify: "/auth/2fa/verify",
|
|
301
|
+
...endpoints
|
|
302
|
+
};
|
|
303
|
+
return async ({ endpoint, data }) => {
|
|
304
|
+
if (endpoint === resolvedEndpoints.login) {
|
|
305
|
+
const payload = data ?? {};
|
|
306
|
+
const user = createAdminUser(payload.email ?? "admin@example.com", mockUser);
|
|
307
|
+
writeStoredUser(mockStorageKey, user);
|
|
308
|
+
return toSuccessResponse("Mock login successful.", user);
|
|
309
|
+
}
|
|
310
|
+
if (endpoint === resolvedEndpoints.register) {
|
|
311
|
+
const payload = data ?? {};
|
|
312
|
+
const user = createRegisteredUser(
|
|
313
|
+
payload.name,
|
|
314
|
+
payload.email ?? "user@example.com",
|
|
315
|
+
mockUser
|
|
316
|
+
);
|
|
317
|
+
writeStoredUser(mockStorageKey, user);
|
|
318
|
+
return toSuccessResponse("Mock registration successful.", user);
|
|
319
|
+
}
|
|
320
|
+
if (endpoint === resolvedEndpoints.logout) {
|
|
321
|
+
writeStoredUser(mockStorageKey, null);
|
|
322
|
+
return toSuccessResponse("Mock logout successful.");
|
|
323
|
+
}
|
|
324
|
+
if (endpoint === resolvedEndpoints.me || endpoint === resolvedEndpoints.refresh) {
|
|
325
|
+
const user = readStoredUser(mockStorageKey);
|
|
326
|
+
return user ? { success: true, user } : { success: true, user: null };
|
|
327
|
+
}
|
|
328
|
+
if (endpoint === resolvedEndpoints.forgotPassword) {
|
|
329
|
+
return toSuccessResponse("Mock password reset request accepted.");
|
|
330
|
+
}
|
|
331
|
+
if (endpoint === resolvedEndpoints.resetPassword) {
|
|
332
|
+
return toSuccessResponse("Mock password reset successful.");
|
|
333
|
+
}
|
|
334
|
+
if (endpoint === resolvedEndpoints.verifyEmail) {
|
|
335
|
+
return toSuccessResponse("Mock email verification successful.");
|
|
336
|
+
}
|
|
337
|
+
if (endpoint === resolvedEndpoints.twoFactorVerify) {
|
|
338
|
+
const payload = data ?? {};
|
|
339
|
+
const code = payload.code?.trim() ?? "";
|
|
340
|
+
if (code.length !== DEFAULT_OTP_LENGTH) {
|
|
341
|
+
throw {
|
|
342
|
+
code: "INVALID_2FA_CODE",
|
|
343
|
+
message: `Mock 2FA code must be exactly ${DEFAULT_OTP_LENGTH} characters.`
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return toSuccessResponse("Mock 2FA verification successful.");
|
|
347
|
+
}
|
|
348
|
+
return toSuccessResponse("Mock request successful.");
|
|
349
|
+
};
|
|
350
|
+
}
|
|
203
351
|
const initialState = {
|
|
204
352
|
user: null,
|
|
205
353
|
isAuthenticated: false,
|
|
206
354
|
isLoading: false,
|
|
207
355
|
error: null
|
|
208
356
|
};
|
|
357
|
+
const defaultTheme = {
|
|
358
|
+
primaryColor: "#2563eb",
|
|
359
|
+
primaryHoverColor: "#1d4ed8",
|
|
360
|
+
radius: "14px",
|
|
361
|
+
fontFamily: "inherit"
|
|
362
|
+
};
|
|
209
363
|
function authReducer(state, action) {
|
|
210
364
|
switch (action.type) {
|
|
211
365
|
case "SET_LOADING":
|
|
@@ -221,29 +375,63 @@ function authReducer(state, action) {
|
|
|
221
375
|
isLoading: false
|
|
222
376
|
};
|
|
223
377
|
case "LOGOUT":
|
|
224
|
-
return initialState;
|
|
378
|
+
return { ...initialState };
|
|
225
379
|
default:
|
|
226
380
|
return state;
|
|
227
381
|
}
|
|
228
382
|
}
|
|
383
|
+
function toAuthError(error) {
|
|
384
|
+
if (!error) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
return typeof error === "string" ? {
|
|
388
|
+
code: "AUTH_ERROR",
|
|
389
|
+
message: error
|
|
390
|
+
} : error;
|
|
391
|
+
}
|
|
229
392
|
const AuthProvider = ({
|
|
230
393
|
children,
|
|
231
|
-
baseURL
|
|
394
|
+
baseURL,
|
|
395
|
+
baseUrl,
|
|
232
396
|
endpoints = {},
|
|
397
|
+
headers = {},
|
|
398
|
+
credentials = "include",
|
|
233
399
|
onAuthError,
|
|
234
400
|
autoRefresh = true,
|
|
235
|
-
|
|
236
|
-
|
|
401
|
+
autoRestore = true,
|
|
402
|
+
refreshInterval = 5 * 60 * 1e3,
|
|
403
|
+
requestAdapter,
|
|
404
|
+
theme,
|
|
405
|
+
mock = false,
|
|
406
|
+
mockStorageKey = "rq-auth-flow-user",
|
|
407
|
+
mockUser
|
|
237
408
|
}) => {
|
|
238
409
|
const [state, dispatch] = useReducer(authReducer, initialState);
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
410
|
+
const resolvedTheme = { ...defaultTheme, ...theme };
|
|
411
|
+
const resolvedBaseURL = baseURL ?? baseUrl ?? "";
|
|
412
|
+
const client = useMemo(() => {
|
|
413
|
+
const resolvedRequestAdapter = mock ? createMockAuthAdapter({
|
|
242
414
|
endpoints,
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
415
|
+
mockStorageKey,
|
|
416
|
+
mockUser
|
|
417
|
+
}) : requestAdapter;
|
|
418
|
+
return createAuthClient({
|
|
419
|
+
baseURL: resolvedBaseURL,
|
|
420
|
+
endpoints,
|
|
421
|
+
headers,
|
|
422
|
+
credentials,
|
|
423
|
+
requestAdapter: resolvedRequestAdapter
|
|
424
|
+
});
|
|
425
|
+
}, [
|
|
426
|
+
credentials,
|
|
427
|
+
endpoints,
|
|
428
|
+
headers,
|
|
429
|
+
mock,
|
|
430
|
+
mockStorageKey,
|
|
431
|
+
mockUser,
|
|
432
|
+
requestAdapter,
|
|
433
|
+
resolvedBaseURL
|
|
434
|
+
]);
|
|
247
435
|
const handleError = useCallback(
|
|
248
436
|
(error) => {
|
|
249
437
|
dispatch({ type: "SET_ERROR", payload: error });
|
|
@@ -251,35 +439,65 @@ const AuthProvider = ({
|
|
|
251
439
|
},
|
|
252
440
|
[onAuthError]
|
|
253
441
|
);
|
|
442
|
+
const clearError = useCallback(() => {
|
|
443
|
+
dispatch({ type: "SET_ERROR", payload: null });
|
|
444
|
+
}, []);
|
|
445
|
+
const setError = useCallback(
|
|
446
|
+
(error) => {
|
|
447
|
+
const nextError = toAuthError(error);
|
|
448
|
+
dispatch({ type: "SET_ERROR", payload: nextError });
|
|
449
|
+
if (nextError) {
|
|
450
|
+
onAuthError?.(nextError);
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
[onAuthError]
|
|
454
|
+
);
|
|
455
|
+
const setUser = useCallback((user) => {
|
|
456
|
+
dispatch({ type: "SET_USER", payload: user });
|
|
457
|
+
}, []);
|
|
458
|
+
const resolveAction = useCallback((response) => {
|
|
459
|
+
if (response?.user) {
|
|
460
|
+
dispatch({ type: "SET_USER", payload: response.user });
|
|
461
|
+
return response.user;
|
|
462
|
+
}
|
|
463
|
+
dispatch({ type: "SET_ERROR", payload: null });
|
|
464
|
+
return null;
|
|
465
|
+
}, []);
|
|
254
466
|
const restoreSession = useCallback(async () => {
|
|
255
467
|
dispatch({ type: "SET_LOADING", payload: true });
|
|
256
468
|
try {
|
|
257
469
|
const response = await client.me();
|
|
258
470
|
if (response.user) {
|
|
259
471
|
dispatch({ type: "SET_USER", payload: response.user });
|
|
260
|
-
|
|
261
|
-
dispatch({ type: "LOGOUT" });
|
|
472
|
+
return response.user;
|
|
262
473
|
}
|
|
263
|
-
|
|
264
|
-
|
|
474
|
+
dispatch({ type: "LOGOUT" });
|
|
475
|
+
return null;
|
|
476
|
+
} catch {
|
|
477
|
+
dispatch({ type: "LOGOUT" });
|
|
478
|
+
return null;
|
|
265
479
|
}
|
|
266
480
|
}, [client]);
|
|
267
481
|
const refreshSession = useCallback(async () => {
|
|
482
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
268
483
|
try {
|
|
269
484
|
const response = await client.refresh();
|
|
270
|
-
|
|
271
|
-
dispatch({ type: "SET_USER", payload: response.user });
|
|
272
|
-
}
|
|
485
|
+
return resolveAction(response);
|
|
273
486
|
} catch (error) {
|
|
274
487
|
const authError = normalizeError(error);
|
|
275
488
|
handleError(authError);
|
|
489
|
+
return null;
|
|
276
490
|
}
|
|
277
|
-
}, [client, handleError]);
|
|
491
|
+
}, [client, handleError, resolveAction]);
|
|
278
492
|
const login = useCallback(
|
|
279
493
|
async (payload) => {
|
|
280
494
|
dispatch({ type: "SET_LOADING", payload: true });
|
|
281
495
|
try {
|
|
282
|
-
const response = await client.login(
|
|
496
|
+
const response = await client.login(
|
|
497
|
+
payload.email,
|
|
498
|
+
payload.password,
|
|
499
|
+
payload.rememberMe
|
|
500
|
+
);
|
|
283
501
|
if (!response.user) {
|
|
284
502
|
throw new Error("No user data in response");
|
|
285
503
|
}
|
|
@@ -315,8 +533,7 @@ const AuthProvider = ({
|
|
|
315
533
|
dispatch({ type: "SET_LOADING", payload: true });
|
|
316
534
|
try {
|
|
317
535
|
await client.logout();
|
|
318
|
-
} catch
|
|
319
|
-
console.error("Logout error:", error);
|
|
536
|
+
} catch {
|
|
320
537
|
} finally {
|
|
321
538
|
dispatch({ type: "LOGOUT" });
|
|
322
539
|
}
|
|
@@ -325,76 +542,158 @@ const AuthProvider = ({
|
|
|
325
542
|
async (payload) => {
|
|
326
543
|
dispatch({ type: "SET_LOADING", payload: true });
|
|
327
544
|
try {
|
|
328
|
-
await client.forgotPassword(payload.email);
|
|
329
|
-
|
|
545
|
+
const response = await client.forgotPassword(payload.email);
|
|
546
|
+
resolveAction(response);
|
|
547
|
+
return response;
|
|
330
548
|
} catch (error) {
|
|
331
549
|
const authError = normalizeError(error);
|
|
332
550
|
handleError(authError);
|
|
333
551
|
throw authError;
|
|
334
552
|
}
|
|
335
553
|
},
|
|
336
|
-
[client, handleError]
|
|
554
|
+
[client, handleError, resolveAction]
|
|
337
555
|
);
|
|
338
556
|
const resetPassword = useCallback(
|
|
339
557
|
async (payload) => {
|
|
340
558
|
dispatch({ type: "SET_LOADING", payload: true });
|
|
341
559
|
try {
|
|
342
|
-
await client.resetPassword(
|
|
560
|
+
const response = await client.resetPassword(
|
|
343
561
|
payload.token,
|
|
344
562
|
payload.password,
|
|
345
563
|
payload.confirmPassword
|
|
346
564
|
);
|
|
347
|
-
|
|
565
|
+
resolveAction(response);
|
|
566
|
+
return response;
|
|
348
567
|
} catch (error) {
|
|
349
568
|
const authError = normalizeError(error);
|
|
350
569
|
handleError(authError);
|
|
351
570
|
throw authError;
|
|
352
571
|
}
|
|
353
572
|
},
|
|
354
|
-
[client, handleError]
|
|
573
|
+
[client, handleError, resolveAction]
|
|
355
574
|
);
|
|
356
575
|
const verifyEmail = useCallback(
|
|
357
576
|
async (payload) => {
|
|
358
577
|
dispatch({ type: "SET_LOADING", payload: true });
|
|
359
578
|
try {
|
|
360
|
-
await client.verifyEmail(payload.token, payload.email);
|
|
361
|
-
|
|
579
|
+
const response = await client.verifyEmail(payload.token, payload.email);
|
|
580
|
+
resolveAction(response);
|
|
581
|
+
return response;
|
|
362
582
|
} catch (error) {
|
|
363
583
|
const authError = normalizeError(error);
|
|
364
584
|
handleError(authError);
|
|
365
585
|
throw authError;
|
|
366
586
|
}
|
|
367
587
|
},
|
|
368
|
-
[client, handleError]
|
|
588
|
+
[client, handleError, resolveAction]
|
|
589
|
+
);
|
|
590
|
+
const verifyTwoFactor = useCallback(
|
|
591
|
+
async (payload) => {
|
|
592
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
593
|
+
try {
|
|
594
|
+
const response = await client.verifyTwoFactor(payload.code);
|
|
595
|
+
resolveAction(response);
|
|
596
|
+
return response;
|
|
597
|
+
} catch (error) {
|
|
598
|
+
const authError = normalizeError(error);
|
|
599
|
+
handleError(authError);
|
|
600
|
+
throw authError;
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
[client, handleError, resolveAction]
|
|
604
|
+
);
|
|
605
|
+
const hasRole = useCallback(
|
|
606
|
+
(role) => state.user?.roles?.includes(role) ?? false,
|
|
607
|
+
[state.user]
|
|
608
|
+
);
|
|
609
|
+
const hasAnyRole = useCallback(
|
|
610
|
+
(roles) => roles.some((role) => state.user?.roles?.includes(role)),
|
|
611
|
+
[state.user]
|
|
612
|
+
);
|
|
613
|
+
const hasPermission = useCallback(
|
|
614
|
+
(permission) => state.user?.permissions?.includes(permission) ?? false,
|
|
615
|
+
[state.user]
|
|
616
|
+
);
|
|
617
|
+
const hasAnyPermission = useCallback(
|
|
618
|
+
(permissions) => permissions.some(
|
|
619
|
+
(permission) => state.user?.permissions?.includes(permission)
|
|
620
|
+
),
|
|
621
|
+
[state.user]
|
|
622
|
+
);
|
|
623
|
+
const hasAllPermissions = useCallback(
|
|
624
|
+
(permissions) => permissions.every(
|
|
625
|
+
(permission) => state.user?.permissions?.includes(permission)
|
|
626
|
+
),
|
|
627
|
+
[state.user]
|
|
369
628
|
);
|
|
370
|
-
const setUser = useCallback((user) => {
|
|
371
|
-
dispatch({ type: "SET_USER", payload: user });
|
|
372
|
-
}, []);
|
|
373
629
|
useEffect(() => {
|
|
374
|
-
|
|
375
|
-
|
|
630
|
+
if (!autoRestore) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
void restoreSession();
|
|
634
|
+
}, [autoRestore, restoreSession]);
|
|
376
635
|
useEffect(() => {
|
|
377
636
|
if (!autoRefresh || !state.isAuthenticated) {
|
|
378
637
|
return;
|
|
379
638
|
}
|
|
380
|
-
const
|
|
381
|
-
refreshSession();
|
|
639
|
+
const intervalId = window.setInterval(() => {
|
|
640
|
+
void refreshSession();
|
|
382
641
|
}, refreshInterval);
|
|
383
|
-
return () => clearInterval(
|
|
384
|
-
}, [autoRefresh,
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
642
|
+
return () => window.clearInterval(intervalId);
|
|
643
|
+
}, [autoRefresh, refreshInterval, refreshSession, state.isAuthenticated]);
|
|
644
|
+
const themeStyle = useMemo(
|
|
645
|
+
() => ({
|
|
646
|
+
"--rq-auth-primary": resolvedTheme.primaryColor,
|
|
647
|
+
"--rq-auth-primary-hover": resolvedTheme.primaryHoverColor,
|
|
648
|
+
"--rq-auth-radius": resolvedTheme.radius,
|
|
649
|
+
"--rq-auth-font-family": resolvedTheme.fontFamily,
|
|
650
|
+
fontFamily: resolvedTheme.fontFamily
|
|
651
|
+
}),
|
|
652
|
+
[resolvedTheme.fontFamily, resolvedTheme.primaryColor, resolvedTheme.primaryHoverColor, resolvedTheme.radius]
|
|
653
|
+
);
|
|
654
|
+
const value = useMemo(
|
|
655
|
+
() => ({
|
|
656
|
+
...state,
|
|
657
|
+
login,
|
|
658
|
+
register,
|
|
659
|
+
logout,
|
|
660
|
+
forgotPassword,
|
|
661
|
+
resetPassword,
|
|
662
|
+
verifyEmail,
|
|
663
|
+
verifyTwoFactor,
|
|
664
|
+
refreshSession,
|
|
665
|
+
restoreSession,
|
|
666
|
+
clearError,
|
|
667
|
+
setError,
|
|
668
|
+
setUser,
|
|
669
|
+
hasRole,
|
|
670
|
+
hasAnyRole,
|
|
671
|
+
hasPermission,
|
|
672
|
+
hasAnyPermission,
|
|
673
|
+
hasAllPermissions
|
|
674
|
+
}),
|
|
675
|
+
[
|
|
676
|
+
clearError,
|
|
677
|
+
forgotPassword,
|
|
678
|
+
hasAllPermissions,
|
|
679
|
+
hasAnyPermission,
|
|
680
|
+
hasAnyRole,
|
|
681
|
+
hasPermission,
|
|
682
|
+
hasRole,
|
|
683
|
+
login,
|
|
684
|
+
logout,
|
|
685
|
+
refreshSession,
|
|
686
|
+
register,
|
|
687
|
+
resetPassword,
|
|
688
|
+
restoreSession,
|
|
689
|
+
setError,
|
|
690
|
+
setUser,
|
|
691
|
+
state,
|
|
692
|
+
verifyEmail,
|
|
693
|
+
verifyTwoFactor
|
|
694
|
+
]
|
|
695
|
+
);
|
|
696
|
+
return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { className: "rq-auth-theme", style: themeStyle, children }) });
|
|
398
697
|
};
|
|
399
698
|
function useAuth() {
|
|
400
699
|
const context = useContext(AuthContext);
|
|
@@ -406,16 +705,53 @@ function useAuth() {
|
|
|
406
705
|
function cn(...classes) {
|
|
407
706
|
return classes.filter(Boolean).join(" ");
|
|
408
707
|
}
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
708
|
+
const AUTH_FORM_CLASSES = {
|
|
709
|
+
container: "rq-auth-container",
|
|
710
|
+
card: "rq-auth-card",
|
|
711
|
+
shell: "rq-auth-shell",
|
|
712
|
+
brand: "rq-auth-brand",
|
|
713
|
+
footer: "rq-auth-footer",
|
|
714
|
+
header: "rq-auth-header",
|
|
715
|
+
form: "rq-auth-form",
|
|
716
|
+
field: "rq-auth-field",
|
|
717
|
+
label: "rq-auth-label",
|
|
718
|
+
input: "rq-auth-input",
|
|
719
|
+
button: "rq-auth-button",
|
|
720
|
+
buttonContent: "rq-auth-button-content",
|
|
721
|
+
buttonSpinner: "rq-auth-button-spinner",
|
|
722
|
+
error: "rq-auth-error",
|
|
723
|
+
success: "rq-auth-success",
|
|
724
|
+
title: "rq-auth-title",
|
|
725
|
+
subtitle: "rq-auth-subtitle",
|
|
726
|
+
checkbox: "rq-auth-checkbox",
|
|
727
|
+
checkboxInput: "rq-auth-checkbox-input",
|
|
728
|
+
checkboxLabel: "rq-auth-checkbox-label",
|
|
729
|
+
otpGroup: "rq-auth-otp-group",
|
|
730
|
+
otpInput: "rq-auth-otp-input",
|
|
731
|
+
socialButton: "rq-auth-social-button",
|
|
732
|
+
socialIcon: "rq-auth-social-icon",
|
|
733
|
+
socialLabel: "rq-auth-social-label"
|
|
418
734
|
};
|
|
735
|
+
const AuthLayout = ({
|
|
736
|
+
children,
|
|
737
|
+
title,
|
|
738
|
+
subtitle,
|
|
739
|
+
className,
|
|
740
|
+
cardClassName,
|
|
741
|
+
brand,
|
|
742
|
+
footer
|
|
743
|
+
}) => {
|
|
744
|
+
return /* @__PURE__ */ jsx("section", { className: cn(AUTH_FORM_CLASSES.shell, className), children: /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.card, cardClassName), children: [
|
|
745
|
+
brand ? /* @__PURE__ */ jsx("div", { className: AUTH_FORM_CLASSES.brand, children: brand }) : null,
|
|
746
|
+
(title || subtitle) && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
747
|
+
title ? /* @__PURE__ */ jsx("h1", { className: AUTH_FORM_CLASSES.title, children: title }) : null,
|
|
748
|
+
subtitle ? /* @__PURE__ */ jsx("p", { className: AUTH_FORM_CLASSES.subtitle, children: subtitle }) : null
|
|
749
|
+
] }),
|
|
750
|
+
children,
|
|
751
|
+
footer ? /* @__PURE__ */ jsx("div", { className: AUTH_FORM_CLASSES.footer, children: footer }) : null
|
|
752
|
+
] }) });
|
|
753
|
+
};
|
|
754
|
+
AuthLayout.displayName = "AuthLayout";
|
|
419
755
|
const LoginForm = ({
|
|
420
756
|
className,
|
|
421
757
|
formClassName,
|
|
@@ -436,7 +772,12 @@ const LoginForm = ({
|
|
|
436
772
|
onSuccess,
|
|
437
773
|
onError
|
|
438
774
|
}) => {
|
|
439
|
-
const {
|
|
775
|
+
const {
|
|
776
|
+
login,
|
|
777
|
+
clearError,
|
|
778
|
+
isLoading: authLoading,
|
|
779
|
+
error: authError
|
|
780
|
+
} = useAuth();
|
|
440
781
|
const [formError, setFormError] = useState("");
|
|
441
782
|
const [email, setEmail] = useState("");
|
|
442
783
|
const [password, setPassword] = useState("");
|
|
@@ -445,6 +786,7 @@ const LoginForm = ({
|
|
|
445
786
|
const error = formError || authError?.message;
|
|
446
787
|
const handleSubmit = async (e) => {
|
|
447
788
|
e.preventDefault();
|
|
789
|
+
clearError();
|
|
448
790
|
setFormError("");
|
|
449
791
|
if (!email || !password) {
|
|
450
792
|
setFormError("Email and password are required");
|
|
@@ -462,89 +804,93 @@ const LoginForm = ({
|
|
|
462
804
|
});
|
|
463
805
|
}
|
|
464
806
|
};
|
|
465
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
466
|
-
showTitle && title && /* @__PURE__ */ jsxs("div", { className:
|
|
467
|
-
/* @__PURE__ */ jsx("h1", { className: cn(
|
|
468
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: cn(
|
|
807
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
808
|
+
showTitle && title && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
809
|
+
/* @__PURE__ */ jsx("h1", { className: cn(AUTH_FORM_CLASSES.title, titleClassName), children: title }),
|
|
810
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn(AUTH_FORM_CLASSES.subtitle, subtitleClassName), children: subtitle })
|
|
469
811
|
] }),
|
|
470
|
-
/* @__PURE__ */ jsxs(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
812
|
+
/* @__PURE__ */ jsxs(
|
|
813
|
+
"form",
|
|
814
|
+
{
|
|
815
|
+
onSubmit: handleSubmit,
|
|
816
|
+
className: cn(AUTH_FORM_CLASSES.form, formClassName),
|
|
817
|
+
"aria-busy": isLoading,
|
|
818
|
+
children: [
|
|
819
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
820
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "email", className: cn(AUTH_FORM_CLASSES.label, labelClassName), children: labels.email || "Email" }),
|
|
821
|
+
/* @__PURE__ */ jsx(
|
|
822
|
+
"input",
|
|
823
|
+
{
|
|
824
|
+
id: "email",
|
|
825
|
+
type: "email",
|
|
826
|
+
value: email,
|
|
827
|
+
onChange: (e) => setEmail(e.target.value),
|
|
828
|
+
placeholder: placeholders.email || "your@email.com",
|
|
829
|
+
disabled: isLoading,
|
|
830
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
831
|
+
autoComplete: "username",
|
|
832
|
+
required: true
|
|
833
|
+
}
|
|
834
|
+
)
|
|
835
|
+
] }),
|
|
836
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
837
|
+
/* @__PURE__ */ jsx(
|
|
838
|
+
"label",
|
|
839
|
+
{
|
|
840
|
+
htmlFor: "password",
|
|
841
|
+
className: cn(AUTH_FORM_CLASSES.label, labelClassName),
|
|
842
|
+
children: labels.password || "Password"
|
|
843
|
+
}
|
|
844
|
+
),
|
|
845
|
+
/* @__PURE__ */ jsx(
|
|
846
|
+
"input",
|
|
847
|
+
{
|
|
848
|
+
id: "password",
|
|
849
|
+
type: "password",
|
|
850
|
+
value: password,
|
|
851
|
+
onChange: (e) => setPassword(e.target.value),
|
|
852
|
+
placeholder: placeholders.password || "Enter your password",
|
|
853
|
+
disabled: isLoading,
|
|
854
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
855
|
+
autoComplete: "current-password",
|
|
856
|
+
required: true
|
|
857
|
+
}
|
|
858
|
+
)
|
|
859
|
+
] }),
|
|
860
|
+
/* @__PURE__ */ jsxs("label", { htmlFor: "rememberMe", className: AUTH_FORM_CLASSES.checkbox, children: [
|
|
861
|
+
/* @__PURE__ */ jsx(
|
|
862
|
+
"input",
|
|
863
|
+
{
|
|
864
|
+
id: "rememberMe",
|
|
865
|
+
type: "checkbox",
|
|
866
|
+
checked: rememberMe,
|
|
867
|
+
onChange: (e) => setRememberMe(e.target.checked),
|
|
868
|
+
disabled: isLoading,
|
|
869
|
+
className: AUTH_FORM_CLASSES.checkboxInput
|
|
870
|
+
}
|
|
871
|
+
),
|
|
872
|
+
/* @__PURE__ */ jsx("span", { className: AUTH_FORM_CLASSES.checkboxLabel, children: labels.rememberMe || "Remember me" })
|
|
873
|
+
] }),
|
|
874
|
+
error && /* @__PURE__ */ jsx("div", { className: cn(AUTH_FORM_CLASSES.error, errorClassName), role: "alert", children: error }),
|
|
875
|
+
/* @__PURE__ */ jsx(
|
|
876
|
+
"button",
|
|
877
|
+
{
|
|
878
|
+
type: "submit",
|
|
879
|
+
disabled: isLoading,
|
|
880
|
+
className: cn(AUTH_FORM_CLASSES.button, buttonClassName),
|
|
881
|
+
"aria-busy": isLoading,
|
|
882
|
+
children: isLoading ? /* @__PURE__ */ jsxs("span", { className: AUTH_FORM_CLASSES.buttonContent, children: [
|
|
883
|
+
/* @__PURE__ */ jsx("span", { className: AUTH_FORM_CLASSES.buttonSpinner, "aria-hidden": "true" }),
|
|
884
|
+
loadingText
|
|
885
|
+
] }) : submitButtonText
|
|
886
|
+
}
|
|
887
|
+
)
|
|
888
|
+
]
|
|
889
|
+
}
|
|
890
|
+
)
|
|
535
891
|
] });
|
|
536
892
|
};
|
|
537
893
|
LoginForm.displayName = "LoginForm";
|
|
538
|
-
const DEFAULT_CLASSES$3 = {
|
|
539
|
-
form: "space-y-5",
|
|
540
|
-
field: "space-y-2",
|
|
541
|
-
label: "block text-sm font-medium text-slate-700",
|
|
542
|
-
input: "w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition placeholder:text-slate-400 focus:border-blue-500 focus:ring-4 focus:ring-blue-100 disabled:cursor-not-allowed disabled:bg-slate-100",
|
|
543
|
-
button: "w-full rounded-xl bg-blue-600 px-4 py-3 text-sm font-semibold text-white transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60",
|
|
544
|
-
error: "rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-600",
|
|
545
|
-
title: "text-2xl font-bold text-slate-900",
|
|
546
|
-
subtitle: "mt-1 text-sm text-slate-500"
|
|
547
|
-
};
|
|
548
894
|
const RegisterForm = ({
|
|
549
895
|
className,
|
|
550
896
|
formClassName,
|
|
@@ -565,7 +911,12 @@ const RegisterForm = ({
|
|
|
565
911
|
onSuccess,
|
|
566
912
|
onError
|
|
567
913
|
}) => {
|
|
568
|
-
const {
|
|
914
|
+
const {
|
|
915
|
+
register,
|
|
916
|
+
clearError,
|
|
917
|
+
isLoading: authLoading,
|
|
918
|
+
error: authError
|
|
919
|
+
} = useAuth();
|
|
569
920
|
const [formError, setFormError] = useState("");
|
|
570
921
|
const [name, setName] = useState("");
|
|
571
922
|
const [email, setEmail] = useState("");
|
|
@@ -575,6 +926,7 @@ const RegisterForm = ({
|
|
|
575
926
|
const error = formError || authError?.message;
|
|
576
927
|
const handleSubmit = async (e) => {
|
|
577
928
|
e.preventDefault();
|
|
929
|
+
clearError();
|
|
578
930
|
setFormError("");
|
|
579
931
|
if (!name || !email || !password || !confirmPassword) {
|
|
580
932
|
setFormError("All fields are required");
|
|
@@ -605,101 +957,120 @@ const RegisterForm = ({
|
|
|
605
957
|
});
|
|
606
958
|
}
|
|
607
959
|
};
|
|
608
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
609
|
-
showTitle && title && /* @__PURE__ */ jsxs("div", { className:
|
|
610
|
-
/* @__PURE__ */ jsx("h1", { className: cn(
|
|
611
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: cn(
|
|
960
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
961
|
+
showTitle && title && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
962
|
+
/* @__PURE__ */ jsx("h1", { className: cn(AUTH_FORM_CLASSES.title, titleClassName), children: title }),
|
|
963
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn(AUTH_FORM_CLASSES.subtitle, subtitleClassName), children: subtitle })
|
|
612
964
|
] }),
|
|
613
|
-
/* @__PURE__ */ jsxs(
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
965
|
+
/* @__PURE__ */ jsxs(
|
|
966
|
+
"form",
|
|
967
|
+
{
|
|
968
|
+
onSubmit: handleSubmit,
|
|
969
|
+
className: cn(AUTH_FORM_CLASSES.form, formClassName),
|
|
970
|
+
"aria-busy": isLoading,
|
|
971
|
+
children: [
|
|
972
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
973
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "name", className: cn(AUTH_FORM_CLASSES.label, labelClassName), children: labels.name || "Full Name" }),
|
|
974
|
+
/* @__PURE__ */ jsx(
|
|
975
|
+
"input",
|
|
976
|
+
{
|
|
977
|
+
id: "name",
|
|
978
|
+
type: "text",
|
|
979
|
+
value: name,
|
|
980
|
+
onChange: (e) => setName(e.target.value),
|
|
981
|
+
placeholder: placeholders.name || "John Doe",
|
|
982
|
+
disabled: isLoading,
|
|
983
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
984
|
+
autoComplete: "name",
|
|
985
|
+
required: true
|
|
986
|
+
}
|
|
987
|
+
)
|
|
988
|
+
] }),
|
|
989
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
990
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "email", className: cn(AUTH_FORM_CLASSES.label, labelClassName), children: labels.email || "Email" }),
|
|
991
|
+
/* @__PURE__ */ jsx(
|
|
992
|
+
"input",
|
|
993
|
+
{
|
|
994
|
+
id: "email",
|
|
995
|
+
type: "email",
|
|
996
|
+
value: email,
|
|
997
|
+
onChange: (e) => setEmail(e.target.value),
|
|
998
|
+
placeholder: placeholders.email || "your@email.com",
|
|
999
|
+
disabled: isLoading,
|
|
1000
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
1001
|
+
autoComplete: "email",
|
|
1002
|
+
required: true
|
|
1003
|
+
}
|
|
1004
|
+
)
|
|
1005
|
+
] }),
|
|
1006
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
1007
|
+
/* @__PURE__ */ jsx(
|
|
1008
|
+
"label",
|
|
1009
|
+
{
|
|
1010
|
+
htmlFor: "password",
|
|
1011
|
+
className: cn(AUTH_FORM_CLASSES.label, labelClassName),
|
|
1012
|
+
children: labels.password || "Password"
|
|
1013
|
+
}
|
|
1014
|
+
),
|
|
1015
|
+
/* @__PURE__ */ jsx(
|
|
1016
|
+
"input",
|
|
1017
|
+
{
|
|
1018
|
+
id: "password",
|
|
1019
|
+
type: "password",
|
|
1020
|
+
value: password,
|
|
1021
|
+
onChange: (e) => setPassword(e.target.value),
|
|
1022
|
+
placeholder: placeholders.password || "Create a password",
|
|
1023
|
+
disabled: isLoading,
|
|
1024
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
1025
|
+
autoComplete: "new-password",
|
|
1026
|
+
required: true
|
|
1027
|
+
}
|
|
1028
|
+
)
|
|
1029
|
+
] }),
|
|
1030
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
1031
|
+
/* @__PURE__ */ jsx(
|
|
1032
|
+
"label",
|
|
1033
|
+
{
|
|
1034
|
+
htmlFor: "confirmPassword",
|
|
1035
|
+
className: cn(AUTH_FORM_CLASSES.label, labelClassName),
|
|
1036
|
+
children: labels.confirmPassword || "Confirm Password"
|
|
1037
|
+
}
|
|
1038
|
+
),
|
|
1039
|
+
/* @__PURE__ */ jsx(
|
|
1040
|
+
"input",
|
|
1041
|
+
{
|
|
1042
|
+
id: "confirmPassword",
|
|
1043
|
+
type: "password",
|
|
1044
|
+
value: confirmPassword,
|
|
1045
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
1046
|
+
placeholder: placeholders.confirmPassword || "Confirm your password",
|
|
1047
|
+
disabled: isLoading,
|
|
1048
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
1049
|
+
autoComplete: "new-password",
|
|
1050
|
+
required: true
|
|
1051
|
+
}
|
|
1052
|
+
)
|
|
1053
|
+
] }),
|
|
1054
|
+
error && /* @__PURE__ */ jsx("div", { className: cn(AUTH_FORM_CLASSES.error, errorClassName), role: "alert", children: error }),
|
|
1055
|
+
/* @__PURE__ */ jsx(
|
|
1056
|
+
"button",
|
|
1057
|
+
{
|
|
1058
|
+
type: "submit",
|
|
1059
|
+
disabled: isLoading,
|
|
1060
|
+
className: cn(AUTH_FORM_CLASSES.button, buttonClassName),
|
|
1061
|
+
"aria-busy": isLoading,
|
|
1062
|
+
children: isLoading ? /* @__PURE__ */ jsxs("span", { className: AUTH_FORM_CLASSES.buttonContent, children: [
|
|
1063
|
+
/* @__PURE__ */ jsx("span", { className: AUTH_FORM_CLASSES.buttonSpinner, "aria-hidden": "true" }),
|
|
1064
|
+
loadingText
|
|
1065
|
+
] }) : submitButtonText
|
|
1066
|
+
}
|
|
1067
|
+
)
|
|
1068
|
+
]
|
|
1069
|
+
}
|
|
1070
|
+
)
|
|
689
1071
|
] });
|
|
690
1072
|
};
|
|
691
1073
|
RegisterForm.displayName = "RegisterForm";
|
|
692
|
-
const DEFAULT_CLASSES$2 = {
|
|
693
|
-
form: "space-y-5",
|
|
694
|
-
field: "space-y-2",
|
|
695
|
-
label: "block text-sm font-medium text-slate-700",
|
|
696
|
-
input: "w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition placeholder:text-slate-400 focus:border-blue-500 focus:ring-4 focus:ring-blue-100 disabled:cursor-not-allowed disabled:bg-slate-100",
|
|
697
|
-
button: "w-full rounded-xl bg-blue-600 px-4 py-3 text-sm font-semibold text-white transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60",
|
|
698
|
-
error: "rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-600",
|
|
699
|
-
success: "rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-700",
|
|
700
|
-
title: "text-2xl font-bold text-slate-900",
|
|
701
|
-
subtitle: "mt-1 text-sm text-slate-500"
|
|
702
|
-
};
|
|
703
1074
|
const ForgotPasswordForm = ({
|
|
704
1075
|
className,
|
|
705
1076
|
formClassName,
|
|
@@ -723,6 +1094,7 @@ const ForgotPasswordForm = ({
|
|
|
723
1094
|
}) => {
|
|
724
1095
|
const {
|
|
725
1096
|
forgotPassword,
|
|
1097
|
+
clearError,
|
|
726
1098
|
isLoading: authLoading,
|
|
727
1099
|
error: authError
|
|
728
1100
|
} = useAuth();
|
|
@@ -733,6 +1105,7 @@ const ForgotPasswordForm = ({
|
|
|
733
1105
|
const error = formError || authError?.message;
|
|
734
1106
|
const handleSubmit = async (e) => {
|
|
735
1107
|
e.preventDefault();
|
|
1108
|
+
clearError();
|
|
736
1109
|
setFormError("");
|
|
737
1110
|
setSuccessMessage("");
|
|
738
1111
|
if (!email) {
|
|
@@ -754,61 +1127,63 @@ const ForgotPasswordForm = ({
|
|
|
754
1127
|
}
|
|
755
1128
|
};
|
|
756
1129
|
if (successMessage) {
|
|
757
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
758
|
-
showTitle && title && /* @__PURE__ */ jsxs("div", { className:
|
|
759
|
-
/* @__PURE__ */ jsx("h1", { className: cn(
|
|
760
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: cn(
|
|
1130
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
1131
|
+
showTitle && title && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
1132
|
+
/* @__PURE__ */ jsx("h1", { className: cn(AUTH_FORM_CLASSES.title, titleClassName), children: title }),
|
|
1133
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn(AUTH_FORM_CLASSES.subtitle, subtitleClassName), children: subtitle })
|
|
761
1134
|
] }),
|
|
762
|
-
/* @__PURE__ */ jsx("div", { className: cn(
|
|
1135
|
+
/* @__PURE__ */ jsx("div", { className: cn(AUTH_FORM_CLASSES.success, successClassName), role: "status", children: /* @__PURE__ */ jsx("p", { children: successMessage }) })
|
|
763
1136
|
] });
|
|
764
1137
|
}
|
|
765
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
766
|
-
showTitle && title && /* @__PURE__ */ jsxs("div", { className:
|
|
767
|
-
/* @__PURE__ */ jsx("h1", { className: cn(
|
|
768
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: cn(
|
|
1138
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
1139
|
+
showTitle && title && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
1140
|
+
/* @__PURE__ */ jsx("h1", { className: cn(AUTH_FORM_CLASSES.title, titleClassName), children: title }),
|
|
1141
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn(AUTH_FORM_CLASSES.subtitle, subtitleClassName), children: subtitle })
|
|
769
1142
|
] }),
|
|
770
|
-
/* @__PURE__ */ jsxs(
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1143
|
+
/* @__PURE__ */ jsxs(
|
|
1144
|
+
"form",
|
|
1145
|
+
{
|
|
1146
|
+
onSubmit: handleSubmit,
|
|
1147
|
+
className: cn(AUTH_FORM_CLASSES.form, formClassName),
|
|
1148
|
+
"aria-busy": isLoading,
|
|
1149
|
+
children: [
|
|
1150
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
1151
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "email", className: cn(AUTH_FORM_CLASSES.label, labelClassName), children: labels.email || "Email" }),
|
|
1152
|
+
/* @__PURE__ */ jsx(
|
|
1153
|
+
"input",
|
|
1154
|
+
{
|
|
1155
|
+
id: "email",
|
|
1156
|
+
type: "email",
|
|
1157
|
+
value: email,
|
|
1158
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1159
|
+
placeholder: placeholders.email || "your@email.com",
|
|
1160
|
+
disabled: isLoading,
|
|
1161
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
1162
|
+
autoComplete: "email",
|
|
1163
|
+
required: true
|
|
1164
|
+
}
|
|
1165
|
+
)
|
|
1166
|
+
] }),
|
|
1167
|
+
error && /* @__PURE__ */ jsx("div", { className: cn(AUTH_FORM_CLASSES.error, errorClassName), role: "alert", children: error }),
|
|
1168
|
+
/* @__PURE__ */ jsx(
|
|
1169
|
+
"button",
|
|
1170
|
+
{
|
|
1171
|
+
type: "submit",
|
|
1172
|
+
disabled: isLoading,
|
|
1173
|
+
className: cn(AUTH_FORM_CLASSES.button, buttonClassName),
|
|
1174
|
+
"aria-busy": isLoading,
|
|
1175
|
+
children: isLoading ? /* @__PURE__ */ jsxs("span", { className: AUTH_FORM_CLASSES.buttonContent, children: [
|
|
1176
|
+
/* @__PURE__ */ jsx("span", { className: AUTH_FORM_CLASSES.buttonSpinner, "aria-hidden": "true" }),
|
|
1177
|
+
loadingText
|
|
1178
|
+
] }) : submitButtonText
|
|
1179
|
+
}
|
|
1180
|
+
)
|
|
1181
|
+
]
|
|
1182
|
+
}
|
|
1183
|
+
)
|
|
798
1184
|
] });
|
|
799
1185
|
};
|
|
800
1186
|
ForgotPasswordForm.displayName = "ForgotPasswordForm";
|
|
801
|
-
const DEFAULT_CLASSES$1 = {
|
|
802
|
-
form: "space-y-5",
|
|
803
|
-
field: "space-y-2",
|
|
804
|
-
label: "block text-sm font-medium text-slate-700",
|
|
805
|
-
input: "w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition placeholder:text-slate-400 focus:border-blue-500 focus:ring-4 focus:ring-blue-100 disabled:cursor-not-allowed disabled:bg-slate-100",
|
|
806
|
-
button: "w-full rounded-xl bg-blue-600 px-4 py-3 text-sm font-semibold text-white transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60",
|
|
807
|
-
error: "rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-600",
|
|
808
|
-
success: "rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-700",
|
|
809
|
-
title: "text-2xl font-bold text-slate-900",
|
|
810
|
-
subtitle: "mt-1 text-sm text-slate-500"
|
|
811
|
-
};
|
|
812
1187
|
const ResetPasswordForm = ({
|
|
813
1188
|
token,
|
|
814
1189
|
className,
|
|
@@ -833,6 +1208,7 @@ const ResetPasswordForm = ({
|
|
|
833
1208
|
}) => {
|
|
834
1209
|
const {
|
|
835
1210
|
resetPassword,
|
|
1211
|
+
clearError,
|
|
836
1212
|
isLoading: authLoading,
|
|
837
1213
|
error: authError
|
|
838
1214
|
} = useAuth();
|
|
@@ -844,6 +1220,7 @@ const ResetPasswordForm = ({
|
|
|
844
1220
|
const error = formError || authError?.message;
|
|
845
1221
|
const handleSubmit = async (e) => {
|
|
846
1222
|
e.preventDefault();
|
|
1223
|
+
clearError();
|
|
847
1224
|
setFormError("");
|
|
848
1225
|
setSuccessMessage("");
|
|
849
1226
|
if (!password || !confirmPassword) {
|
|
@@ -874,77 +1251,94 @@ const ResetPasswordForm = ({
|
|
|
874
1251
|
}
|
|
875
1252
|
};
|
|
876
1253
|
if (successMessage) {
|
|
877
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
878
|
-
showTitle && title && /* @__PURE__ */ jsxs("div", { className:
|
|
879
|
-
/* @__PURE__ */ jsx("h1", { className: cn(
|
|
880
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: cn(
|
|
1254
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
1255
|
+
showTitle && title && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
1256
|
+
/* @__PURE__ */ jsx("h1", { className: cn(AUTH_FORM_CLASSES.title, titleClassName), children: title }),
|
|
1257
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn(AUTH_FORM_CLASSES.subtitle, subtitleClassName), children: subtitle })
|
|
881
1258
|
] }),
|
|
882
|
-
/* @__PURE__ */ jsx("div", { className: cn(
|
|
1259
|
+
/* @__PURE__ */ jsx("div", { className: cn(AUTH_FORM_CLASSES.success, successClassName), role: "status", children: /* @__PURE__ */ jsx("p", { children: successMessage }) })
|
|
883
1260
|
] });
|
|
884
1261
|
}
|
|
885
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
886
|
-
showTitle && title && /* @__PURE__ */ jsxs("div", { className:
|
|
887
|
-
/* @__PURE__ */ jsx("h1", { className: cn(
|
|
888
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: cn(
|
|
1262
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
1263
|
+
showTitle && title && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
1264
|
+
/* @__PURE__ */ jsx("h1", { className: cn(AUTH_FORM_CLASSES.title, titleClassName), children: title }),
|
|
1265
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn(AUTH_FORM_CLASSES.subtitle, subtitleClassName), children: subtitle })
|
|
889
1266
|
] }),
|
|
890
|
-
/* @__PURE__ */ jsxs(
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1267
|
+
/* @__PURE__ */ jsxs(
|
|
1268
|
+
"form",
|
|
1269
|
+
{
|
|
1270
|
+
onSubmit: handleSubmit,
|
|
1271
|
+
className: cn(AUTH_FORM_CLASSES.form, formClassName),
|
|
1272
|
+
"aria-busy": isLoading,
|
|
1273
|
+
children: [
|
|
1274
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
1275
|
+
/* @__PURE__ */ jsx(
|
|
1276
|
+
"label",
|
|
1277
|
+
{
|
|
1278
|
+
htmlFor: "password",
|
|
1279
|
+
className: cn(AUTH_FORM_CLASSES.label, labelClassName),
|
|
1280
|
+
children: labels.password || "New Password"
|
|
1281
|
+
}
|
|
1282
|
+
),
|
|
1283
|
+
/* @__PURE__ */ jsx(
|
|
1284
|
+
"input",
|
|
1285
|
+
{
|
|
1286
|
+
id: "password",
|
|
1287
|
+
type: "password",
|
|
1288
|
+
value: password,
|
|
1289
|
+
onChange: (e) => setPassword(e.target.value),
|
|
1290
|
+
placeholder: placeholders.password || "Enter a new password",
|
|
1291
|
+
disabled: isLoading,
|
|
1292
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
1293
|
+
autoComplete: "new-password",
|
|
1294
|
+
required: true
|
|
1295
|
+
}
|
|
1296
|
+
)
|
|
1297
|
+
] }),
|
|
1298
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
1299
|
+
/* @__PURE__ */ jsx(
|
|
1300
|
+
"label",
|
|
1301
|
+
{
|
|
1302
|
+
htmlFor: "confirmPassword",
|
|
1303
|
+
className: cn(AUTH_FORM_CLASSES.label, labelClassName),
|
|
1304
|
+
children: labels.confirmPassword || "Confirm Password"
|
|
1305
|
+
}
|
|
1306
|
+
),
|
|
1307
|
+
/* @__PURE__ */ jsx(
|
|
1308
|
+
"input",
|
|
1309
|
+
{
|
|
1310
|
+
id: "confirmPassword",
|
|
1311
|
+
type: "password",
|
|
1312
|
+
value: confirmPassword,
|
|
1313
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
1314
|
+
placeholder: placeholders.confirmPassword || "Confirm your new password",
|
|
1315
|
+
disabled: isLoading,
|
|
1316
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
1317
|
+
autoComplete: "new-password",
|
|
1318
|
+
required: true
|
|
1319
|
+
}
|
|
1320
|
+
)
|
|
1321
|
+
] }),
|
|
1322
|
+
error && /* @__PURE__ */ jsx("div", { className: cn(AUTH_FORM_CLASSES.error, errorClassName), role: "alert", children: error }),
|
|
1323
|
+
/* @__PURE__ */ jsx(
|
|
1324
|
+
"button",
|
|
1325
|
+
{
|
|
1326
|
+
type: "submit",
|
|
1327
|
+
disabled: isLoading,
|
|
1328
|
+
className: cn(AUTH_FORM_CLASSES.button, buttonClassName),
|
|
1329
|
+
"aria-busy": isLoading,
|
|
1330
|
+
children: isLoading ? /* @__PURE__ */ jsxs("span", { className: AUTH_FORM_CLASSES.buttonContent, children: [
|
|
1331
|
+
/* @__PURE__ */ jsx("span", { className: AUTH_FORM_CLASSES.buttonSpinner, "aria-hidden": "true" }),
|
|
1332
|
+
loadingText
|
|
1333
|
+
] }) : submitButtonText
|
|
1334
|
+
}
|
|
1335
|
+
)
|
|
1336
|
+
]
|
|
1337
|
+
}
|
|
1338
|
+
)
|
|
934
1339
|
] });
|
|
935
1340
|
};
|
|
936
1341
|
ResetPasswordForm.displayName = "ResetPasswordForm";
|
|
937
|
-
const DEFAULT_CLASSES = {
|
|
938
|
-
form: "space-y-5",
|
|
939
|
-
field: "space-y-2",
|
|
940
|
-
label: "block text-sm font-medium text-slate-700",
|
|
941
|
-
input: "w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition placeholder:text-slate-400 focus:border-blue-500 focus:ring-4 focus:ring-blue-100 disabled:cursor-not-allowed disabled:bg-slate-100",
|
|
942
|
-
button: "w-full rounded-xl bg-blue-600 px-4 py-3 text-sm font-semibold text-white transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60",
|
|
943
|
-
error: "rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-600",
|
|
944
|
-
success: "rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-700",
|
|
945
|
-
title: "text-2xl font-bold text-slate-900",
|
|
946
|
-
subtitle: "mt-1 text-sm text-slate-500"
|
|
947
|
-
};
|
|
948
1342
|
const VerifyEmailForm = ({
|
|
949
1343
|
token: initialToken,
|
|
950
1344
|
email: initialEmail,
|
|
@@ -970,6 +1364,7 @@ const VerifyEmailForm = ({
|
|
|
970
1364
|
}) => {
|
|
971
1365
|
const {
|
|
972
1366
|
verifyEmail,
|
|
1367
|
+
clearError,
|
|
973
1368
|
isLoading: authLoading,
|
|
974
1369
|
error: authError
|
|
975
1370
|
} = useAuth();
|
|
@@ -979,14 +1374,9 @@ const VerifyEmailForm = ({
|
|
|
979
1374
|
const [successMessage, setSuccessMessage] = useState("");
|
|
980
1375
|
const isLoading = authLoading;
|
|
981
1376
|
const error = formError || authError?.message;
|
|
982
|
-
useEffect(() => {
|
|
983
|
-
if (token && email && !successMessage && !error) {
|
|
984
|
-
handleSubmit({ preventDefault: () => {
|
|
985
|
-
} });
|
|
986
|
-
}
|
|
987
|
-
}, [token, email]);
|
|
988
1377
|
const handleSubmit = async (e) => {
|
|
989
1378
|
e.preventDefault();
|
|
1379
|
+
clearError();
|
|
990
1380
|
setFormError("");
|
|
991
1381
|
setSuccessMessage("");
|
|
992
1382
|
if (!token) {
|
|
@@ -1010,106 +1400,357 @@ const VerifyEmailForm = ({
|
|
|
1010
1400
|
});
|
|
1011
1401
|
}
|
|
1012
1402
|
};
|
|
1403
|
+
useEffect(() => {
|
|
1404
|
+
if (token && email && !successMessage && !error) {
|
|
1405
|
+
void handleSubmit({ preventDefault: () => {
|
|
1406
|
+
} });
|
|
1407
|
+
}
|
|
1408
|
+
}, [token, email]);
|
|
1013
1409
|
if (successMessage) {
|
|
1014
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
1015
|
-
showTitle && title && /* @__PURE__ */ jsxs("div", { className:
|
|
1016
|
-
/* @__PURE__ */ jsx("h1", { className: cn(
|
|
1017
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: cn(
|
|
1410
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
1411
|
+
showTitle && title && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
1412
|
+
/* @__PURE__ */ jsx("h1", { className: cn(AUTH_FORM_CLASSES.title, titleClassName), children: title }),
|
|
1413
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn(AUTH_FORM_CLASSES.subtitle, subtitleClassName), children: subtitle })
|
|
1018
1414
|
] }),
|
|
1019
|
-
/* @__PURE__ */ jsx("div", { className: cn(
|
|
1415
|
+
/* @__PURE__ */ jsx("div", { className: cn(AUTH_FORM_CLASSES.success, successClassName), role: "status", children: /* @__PURE__ */ jsx("p", { children: successMessage }) })
|
|
1020
1416
|
] });
|
|
1021
1417
|
}
|
|
1022
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
1023
|
-
showTitle && title && /* @__PURE__ */ jsxs("div", { className:
|
|
1024
|
-
/* @__PURE__ */ jsx("h1", { className: cn(
|
|
1025
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: cn(
|
|
1418
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
1419
|
+
showTitle && title && /* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
1420
|
+
/* @__PURE__ */ jsx("h1", { className: cn(AUTH_FORM_CLASSES.title, titleClassName), children: title }),
|
|
1421
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn(AUTH_FORM_CLASSES.subtitle, subtitleClassName), children: subtitle })
|
|
1026
1422
|
] }),
|
|
1027
|
-
/* @__PURE__ */ jsxs(
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1423
|
+
/* @__PURE__ */ jsxs(
|
|
1424
|
+
"form",
|
|
1425
|
+
{
|
|
1426
|
+
onSubmit: handleSubmit,
|
|
1427
|
+
className: cn(AUTH_FORM_CLASSES.form, formClassName),
|
|
1428
|
+
"aria-busy": isLoading,
|
|
1429
|
+
children: [
|
|
1430
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
1431
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "email", className: cn(AUTH_FORM_CLASSES.label, labelClassName), children: labels.email || "Email" }),
|
|
1432
|
+
/* @__PURE__ */ jsx(
|
|
1433
|
+
"input",
|
|
1434
|
+
{
|
|
1435
|
+
id: "email",
|
|
1436
|
+
type: "email",
|
|
1437
|
+
value: email,
|
|
1438
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1439
|
+
placeholder: placeholders.email || "your@email.com",
|
|
1440
|
+
disabled: isLoading,
|
|
1441
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
1442
|
+
autoComplete: "email",
|
|
1443
|
+
required: true
|
|
1444
|
+
}
|
|
1445
|
+
)
|
|
1446
|
+
] }),
|
|
1447
|
+
/* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.field, fieldClassName), children: [
|
|
1448
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "token", className: cn(AUTH_FORM_CLASSES.label, labelClassName), children: labels.token || "Verification Code" }),
|
|
1449
|
+
/* @__PURE__ */ jsx(
|
|
1450
|
+
"input",
|
|
1451
|
+
{
|
|
1452
|
+
id: "token",
|
|
1453
|
+
type: "text",
|
|
1454
|
+
value: token,
|
|
1455
|
+
onChange: (e) => setToken(e.target.value),
|
|
1456
|
+
placeholder: placeholders.token || "Enter verification code",
|
|
1457
|
+
disabled: isLoading,
|
|
1458
|
+
className: cn(AUTH_FORM_CLASSES.input, inputClassName),
|
|
1459
|
+
autoComplete: "one-time-code",
|
|
1460
|
+
required: true
|
|
1461
|
+
}
|
|
1462
|
+
)
|
|
1463
|
+
] }),
|
|
1464
|
+
error && /* @__PURE__ */ jsx("div", { className: cn(AUTH_FORM_CLASSES.error, errorClassName), role: "alert", children: error }),
|
|
1465
|
+
/* @__PURE__ */ jsx(
|
|
1466
|
+
"button",
|
|
1467
|
+
{
|
|
1468
|
+
type: "submit",
|
|
1469
|
+
disabled: isLoading,
|
|
1470
|
+
className: cn(AUTH_FORM_CLASSES.button, buttonClassName),
|
|
1471
|
+
"aria-busy": isLoading,
|
|
1472
|
+
children: isLoading ? /* @__PURE__ */ jsxs("span", { className: AUTH_FORM_CLASSES.buttonContent, children: [
|
|
1473
|
+
/* @__PURE__ */ jsx("span", { className: AUTH_FORM_CLASSES.buttonSpinner, "aria-hidden": "true" }),
|
|
1474
|
+
loadingText
|
|
1475
|
+
] }) : submitButtonText
|
|
1476
|
+
}
|
|
1477
|
+
)
|
|
1478
|
+
]
|
|
1479
|
+
}
|
|
1480
|
+
)
|
|
1481
|
+
] });
|
|
1482
|
+
};
|
|
1483
|
+
VerifyEmailForm.displayName = "VerifyEmailForm";
|
|
1484
|
+
function toDigits(value, length) {
|
|
1485
|
+
return Array.from({ length }, (_, index) => value[index] ?? "");
|
|
1486
|
+
}
|
|
1487
|
+
const OtpInput = ({
|
|
1488
|
+
length = 6,
|
|
1489
|
+
value,
|
|
1490
|
+
onChange,
|
|
1491
|
+
onComplete,
|
|
1492
|
+
disabled = false,
|
|
1493
|
+
className,
|
|
1494
|
+
inputClassName
|
|
1495
|
+
}) => {
|
|
1496
|
+
const [internalValue, setInternalValue] = useState("");
|
|
1497
|
+
const inputRefs = useRef([]);
|
|
1498
|
+
const baseId = useId();
|
|
1499
|
+
const currentValue = value ?? internalValue;
|
|
1500
|
+
const digits = useMemo(
|
|
1501
|
+
() => toDigits(currentValue.slice(0, length), length),
|
|
1502
|
+
[currentValue, length]
|
|
1503
|
+
);
|
|
1504
|
+
const commitValue = (nextDigits) => {
|
|
1505
|
+
const nextValue = nextDigits.join("").slice(0, length);
|
|
1506
|
+
if (value === void 0) {
|
|
1507
|
+
setInternalValue(nextValue);
|
|
1508
|
+
}
|
|
1509
|
+
onChange?.(nextValue);
|
|
1510
|
+
if (nextDigits.every(Boolean) && nextValue.length === length) {
|
|
1511
|
+
onComplete?.(nextValue);
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
const focusInput = (index) => {
|
|
1515
|
+
inputRefs.current[index]?.focus();
|
|
1516
|
+
inputRefs.current[index]?.select();
|
|
1517
|
+
};
|
|
1518
|
+
const handleChange = (index, nextChunk) => {
|
|
1519
|
+
const sanitized = nextChunk.replace(/\s+/g, "");
|
|
1520
|
+
const nextDigits = [...digits];
|
|
1521
|
+
if (!sanitized) {
|
|
1522
|
+
nextDigits[index] = "";
|
|
1523
|
+
commitValue(nextDigits);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
if (sanitized.length > 1) {
|
|
1527
|
+
sanitized.slice(0, length - index).split("").forEach((character, offset) => {
|
|
1528
|
+
nextDigits[index + offset] = character;
|
|
1529
|
+
});
|
|
1530
|
+
commitValue(nextDigits);
|
|
1531
|
+
focusInput(Math.min(index + sanitized.length, length - 1));
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
nextDigits[index] = sanitized;
|
|
1535
|
+
commitValue(nextDigits);
|
|
1536
|
+
if (index < length - 1) {
|
|
1537
|
+
focusInput(index + 1);
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
const handleKeyDown = (index, event) => {
|
|
1541
|
+
if (event.key === "Backspace" && !digits[index] && index > 0) {
|
|
1542
|
+
focusInput(index - 1);
|
|
1543
|
+
}
|
|
1544
|
+
if (event.key === "ArrowLeft" && index > 0) {
|
|
1545
|
+
event.preventDefault();
|
|
1546
|
+
focusInput(index - 1);
|
|
1547
|
+
}
|
|
1548
|
+
if (event.key === "ArrowRight" && index < length - 1) {
|
|
1549
|
+
event.preventDefault();
|
|
1550
|
+
focusInput(index + 1);
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1553
|
+
const handlePaste = (index, event) => {
|
|
1554
|
+
event.preventDefault();
|
|
1555
|
+
handleChange(index, event.clipboardData.getData("text"));
|
|
1556
|
+
};
|
|
1557
|
+
return /* @__PURE__ */ jsx(
|
|
1558
|
+
"div",
|
|
1559
|
+
{
|
|
1560
|
+
className: cn(AUTH_FORM_CLASSES.otpGroup, className),
|
|
1561
|
+
style: {
|
|
1562
|
+
"--rq-auth-otp-columns": String(length)
|
|
1563
|
+
},
|
|
1564
|
+
children: digits.map((digit, index) => /* @__PURE__ */ jsx(
|
|
1565
|
+
"input",
|
|
1566
|
+
{
|
|
1567
|
+
ref: (element) => {
|
|
1568
|
+
inputRefs.current[index] = element;
|
|
1569
|
+
},
|
|
1570
|
+
id: `${baseId}-${index}`,
|
|
1571
|
+
type: "text",
|
|
1572
|
+
inputMode: "text",
|
|
1573
|
+
autoComplete: index === 0 ? "one-time-code" : "off",
|
|
1574
|
+
maxLength: 1,
|
|
1575
|
+
value: digit,
|
|
1576
|
+
disabled,
|
|
1577
|
+
className: cn(AUTH_FORM_CLASSES.otpInput, inputClassName),
|
|
1578
|
+
onChange: (event) => handleChange(index, event.target.value),
|
|
1579
|
+
onKeyDown: (event) => handleKeyDown(index, event),
|
|
1580
|
+
onPaste: (event) => handlePaste(index, event),
|
|
1581
|
+
"aria-label": `OTP digit ${index + 1}`
|
|
1582
|
+
},
|
|
1583
|
+
`otp-${index}`
|
|
1584
|
+
))
|
|
1585
|
+
}
|
|
1586
|
+
);
|
|
1587
|
+
};
|
|
1588
|
+
OtpInput.displayName = "OtpInput";
|
|
1589
|
+
const TwoFactorForm = ({
|
|
1590
|
+
onSuccess,
|
|
1591
|
+
onError,
|
|
1592
|
+
className,
|
|
1593
|
+
submitButtonText = "Verify Code",
|
|
1594
|
+
loadingText = "Verifying...",
|
|
1595
|
+
title = "Two-factor authentication",
|
|
1596
|
+
subtitle = "Enter the verification code to continue.",
|
|
1597
|
+
length = 6
|
|
1598
|
+
}) => {
|
|
1599
|
+
const {
|
|
1600
|
+
verifyTwoFactor,
|
|
1601
|
+
clearError,
|
|
1602
|
+
isLoading,
|
|
1603
|
+
error: authError
|
|
1604
|
+
} = useAuth();
|
|
1605
|
+
const [code, setCode] = useState("");
|
|
1606
|
+
const [formError, setFormError] = useState("");
|
|
1607
|
+
const error = formError || authError?.message;
|
|
1608
|
+
const handleSubmit = async (event) => {
|
|
1609
|
+
event.preventDefault();
|
|
1610
|
+
clearError();
|
|
1611
|
+
setFormError("");
|
|
1612
|
+
if (code.length !== length) {
|
|
1613
|
+
setFormError(`Enter the full ${length}-digit code.`);
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
try {
|
|
1617
|
+
const response = await verifyTwoFactor({ code });
|
|
1618
|
+
onSuccess?.(response);
|
|
1619
|
+
} catch (caughtError) {
|
|
1620
|
+
const message = caughtError instanceof Error ? caughtError.message : "Two-factor verification failed";
|
|
1621
|
+
setFormError(message);
|
|
1622
|
+
onError?.({
|
|
1623
|
+
code: "TWO_FACTOR_ERROR",
|
|
1624
|
+
message
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
};
|
|
1628
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(AUTH_FORM_CLASSES.container, className), children: [
|
|
1629
|
+
/* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.header, children: [
|
|
1630
|
+
/* @__PURE__ */ jsx("h1", { className: AUTH_FORM_CLASSES.title, children: title }),
|
|
1631
|
+
/* @__PURE__ */ jsx("p", { className: AUTH_FORM_CLASSES.subtitle, children: subtitle })
|
|
1632
|
+
] }),
|
|
1633
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: AUTH_FORM_CLASSES.form, children: [
|
|
1634
|
+
/* @__PURE__ */ jsxs("div", { className: AUTH_FORM_CLASSES.field, children: [
|
|
1635
|
+
/* @__PURE__ */ jsx("label", { className: AUTH_FORM_CLASSES.label, children: "Verification code" }),
|
|
1046
1636
|
/* @__PURE__ */ jsx(
|
|
1047
|
-
|
|
1637
|
+
OtpInput,
|
|
1048
1638
|
{
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
placeholder: placeholders.token || "Enter verification code",
|
|
1054
|
-
disabled: isLoading,
|
|
1055
|
-
className: cn(DEFAULT_CLASSES.input, inputClassName),
|
|
1056
|
-
required: true
|
|
1639
|
+
length,
|
|
1640
|
+
value: code,
|
|
1641
|
+
onChange: setCode,
|
|
1642
|
+
disabled: isLoading
|
|
1057
1643
|
}
|
|
1058
1644
|
)
|
|
1059
1645
|
] }),
|
|
1060
|
-
error
|
|
1646
|
+
error ? /* @__PURE__ */ jsx("div", { className: AUTH_FORM_CLASSES.error, role: "alert", children: error }) : null,
|
|
1061
1647
|
/* @__PURE__ */ jsx(
|
|
1062
1648
|
"button",
|
|
1063
1649
|
{
|
|
1064
1650
|
type: "submit",
|
|
1065
1651
|
disabled: isLoading,
|
|
1066
|
-
className:
|
|
1652
|
+
className: AUTH_FORM_CLASSES.button,
|
|
1067
1653
|
children: isLoading ? loadingText : submitButtonText
|
|
1068
1654
|
}
|
|
1069
1655
|
)
|
|
1070
1656
|
] })
|
|
1071
1657
|
] });
|
|
1072
1658
|
};
|
|
1073
|
-
|
|
1659
|
+
TwoFactorForm.displayName = "TwoFactorForm";
|
|
1660
|
+
const defaultLabels = {
|
|
1661
|
+
google: "Continue with Google",
|
|
1662
|
+
github: "Continue with GitHub",
|
|
1663
|
+
facebook: "Continue with Facebook",
|
|
1664
|
+
custom: "Continue"
|
|
1665
|
+
};
|
|
1666
|
+
const defaultIcons = {
|
|
1667
|
+
google: "G",
|
|
1668
|
+
github: "GH",
|
|
1669
|
+
facebook: "f",
|
|
1670
|
+
custom: "+"
|
|
1671
|
+
};
|
|
1672
|
+
const SocialLoginButton = ({
|
|
1673
|
+
provider,
|
|
1674
|
+
label,
|
|
1675
|
+
icon,
|
|
1676
|
+
onClick,
|
|
1677
|
+
className,
|
|
1678
|
+
disabled = false
|
|
1679
|
+
}) => {
|
|
1680
|
+
return /* @__PURE__ */ jsxs(
|
|
1681
|
+
"button",
|
|
1682
|
+
{
|
|
1683
|
+
type: "button",
|
|
1684
|
+
className: cn(AUTH_FORM_CLASSES.socialButton, className),
|
|
1685
|
+
"data-provider": provider,
|
|
1686
|
+
onClick,
|
|
1687
|
+
disabled,
|
|
1688
|
+
children: [
|
|
1689
|
+
/* @__PURE__ */ jsx("span", { className: AUTH_FORM_CLASSES.socialIcon, "aria-hidden": "true", children: icon ?? defaultIcons[provider] }),
|
|
1690
|
+
/* @__PURE__ */ jsx("span", { className: AUTH_FORM_CLASSES.socialLabel, children: label ?? defaultLabels[provider] })
|
|
1691
|
+
]
|
|
1692
|
+
}
|
|
1693
|
+
);
|
|
1694
|
+
};
|
|
1695
|
+
SocialLoginButton.displayName = "SocialLoginButton";
|
|
1074
1696
|
const ProtectedRoute = ({
|
|
1075
1697
|
children,
|
|
1076
1698
|
redirectTo = "/login",
|
|
1077
1699
|
fallback,
|
|
1078
1700
|
roles,
|
|
1079
|
-
permissions
|
|
1701
|
+
permissions,
|
|
1702
|
+
unauthorizedTo,
|
|
1703
|
+
requireAllPermissions = true
|
|
1080
1704
|
}) => {
|
|
1081
|
-
const {
|
|
1705
|
+
const {
|
|
1706
|
+
isAuthenticated,
|
|
1707
|
+
isLoading,
|
|
1708
|
+
hasAllPermissions,
|
|
1709
|
+
hasAnyPermission,
|
|
1710
|
+
hasAnyRole
|
|
1711
|
+
} = useAuth();
|
|
1082
1712
|
if (isLoading) {
|
|
1083
1713
|
return fallback || /* @__PURE__ */ jsx("div", { className: "auth-loading", children: "Loading..." });
|
|
1084
1714
|
}
|
|
1085
1715
|
if (!isAuthenticated) {
|
|
1086
1716
|
return /* @__PURE__ */ jsx(Navigate, { to: redirectTo, replace: true });
|
|
1087
1717
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
if (permissions && permissions.length > 0) {
|
|
1095
|
-
const hasPermission = user?.permissions?.some(
|
|
1096
|
-
(permission) => permissions.includes(permission)
|
|
1097
|
-
);
|
|
1098
|
-
if (!hasPermission) {
|
|
1099
|
-
return /* @__PURE__ */ jsx("div", { className: "auth-forbidden", children: /* @__PURE__ */ jsx("p", { children: "You do not have permission to access this page." }) });
|
|
1100
|
-
}
|
|
1718
|
+
const hasRequiredRole = !roles?.length || hasAnyRole(roles);
|
|
1719
|
+
const hasRequiredPermission = !permissions?.length || (requireAllPermissions ? hasAllPermissions(permissions) : hasAnyPermission(permissions));
|
|
1720
|
+
if (!hasRequiredRole || !hasRequiredPermission) {
|
|
1721
|
+
return /* @__PURE__ */ jsx(Navigate, { to: unauthorizedTo ?? redirectTo, replace: true });
|
|
1101
1722
|
}
|
|
1102
1723
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
1103
1724
|
};
|
|
1104
1725
|
ProtectedRoute.displayName = "ProtectedRoute";
|
|
1726
|
+
const GuestRoute = ({
|
|
1727
|
+
children,
|
|
1728
|
+
redirectTo = "/",
|
|
1729
|
+
fallback
|
|
1730
|
+
}) => {
|
|
1731
|
+
const { isAuthenticated, isLoading } = useAuth();
|
|
1732
|
+
if (isLoading) {
|
|
1733
|
+
return fallback || /* @__PURE__ */ jsx("div", { className: "auth-loading", children: "Loading..." });
|
|
1734
|
+
}
|
|
1735
|
+
if (isAuthenticated) {
|
|
1736
|
+
return /* @__PURE__ */ jsx(Navigate, { to: redirectTo, replace: true });
|
|
1737
|
+
}
|
|
1738
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
1739
|
+
};
|
|
1740
|
+
GuestRoute.displayName = "GuestRoute";
|
|
1105
1741
|
export {
|
|
1106
1742
|
AuthContext,
|
|
1743
|
+
AuthLayout,
|
|
1107
1744
|
AuthProvider,
|
|
1108
1745
|
ForgotPasswordForm,
|
|
1746
|
+
GuestRoute,
|
|
1109
1747
|
LoginForm,
|
|
1748
|
+
OtpInput,
|
|
1110
1749
|
ProtectedRoute,
|
|
1111
1750
|
RegisterForm,
|
|
1112
1751
|
ResetPasswordForm,
|
|
1752
|
+
SocialLoginButton,
|
|
1753
|
+
TwoFactorForm,
|
|
1113
1754
|
VerifyEmailForm,
|
|
1114
1755
|
cn,
|
|
1115
1756
|
createAuthClient,
|