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