@nocios/crudify-ui 3.0.10 → 3.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1773 -1775
- package/dist/index.mjs +1774 -1776
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -390,1910 +390,1908 @@ var useLoginState = () => {
|
|
|
390
390
|
return context;
|
|
391
391
|
};
|
|
392
392
|
|
|
393
|
-
// src/
|
|
394
|
-
import {
|
|
395
|
-
import { Typography, TextField, Button, Box, CircularProgress, Alert, Link } from "@mui/material";
|
|
393
|
+
// src/providers/SessionProvider.tsx
|
|
394
|
+
import { createContext as createContext4, useContext as useContext4, useMemo as useMemo2 } from "react";
|
|
396
395
|
|
|
397
|
-
// src/
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
TIMEOUT_ERROR: "TIMEOUT_ERROR"
|
|
425
|
-
};
|
|
426
|
-
var ERROR_SEVERITY_MAP = {
|
|
427
|
-
// Authentication - warning (user can fix)
|
|
428
|
-
[ERROR_CODES.INVALID_CREDENTIALS]: "warning",
|
|
429
|
-
[ERROR_CODES.UNAUTHORIZED]: "warning",
|
|
430
|
-
[ERROR_CODES.INVALID_API_KEY]: "error",
|
|
431
|
-
[ERROR_CODES.USER_NOT_FOUND]: "warning",
|
|
432
|
-
[ERROR_CODES.USER_NOT_ACTIVE]: "warning",
|
|
433
|
-
[ERROR_CODES.NO_PERMISSION]: "warning",
|
|
434
|
-
// Data - info (might be expected)
|
|
435
|
-
[ERROR_CODES.ITEM_NOT_FOUND]: "info",
|
|
436
|
-
[ERROR_CODES.NOT_FOUND]: "info",
|
|
437
|
-
[ERROR_CODES.IN_USE]: "warning",
|
|
438
|
-
// Validation - warning (user input issue)
|
|
439
|
-
[ERROR_CODES.FIELD_ERROR]: "warning",
|
|
440
|
-
[ERROR_CODES.BAD_REQUEST]: "warning",
|
|
441
|
-
[ERROR_CODES.INVALID_EMAIL]: "warning",
|
|
442
|
-
[ERROR_CODES.INVALID_CODE]: "warning",
|
|
443
|
-
// System - error (server issue)
|
|
444
|
-
[ERROR_CODES.INTERNAL_SERVER_ERROR]: "error",
|
|
445
|
-
[ERROR_CODES.DATABASE_CONNECTION_ERROR]: "error",
|
|
446
|
-
[ERROR_CODES.INVALID_CONFIGURATION]: "error",
|
|
447
|
-
[ERROR_CODES.UNKNOWN_OPERATION]: "error",
|
|
448
|
-
// Rate limiting - warning with higher duration
|
|
449
|
-
[ERROR_CODES.TOO_MANY_REQUESTS]: "warning",
|
|
450
|
-
// Network - error
|
|
451
|
-
[ERROR_CODES.NETWORK_ERROR]: "error",
|
|
452
|
-
[ERROR_CODES.TIMEOUT_ERROR]: "error"
|
|
453
|
-
};
|
|
454
|
-
function parseApiError(response) {
|
|
455
|
-
const errors = [];
|
|
456
|
-
try {
|
|
457
|
-
const apiResponse = response;
|
|
458
|
-
if (apiResponse.data && typeof apiResponse.data === "object") {
|
|
459
|
-
const responseData = apiResponse.data;
|
|
460
|
-
if (responseData.response) {
|
|
461
|
-
const { status, fieldsWarning } = responseData.response;
|
|
462
|
-
if (fieldsWarning && typeof fieldsWarning === "object") {
|
|
463
|
-
Object.entries(fieldsWarning).forEach(([field, messages]) => {
|
|
464
|
-
if (Array.isArray(messages) && messages.length > 0) {
|
|
465
|
-
errors.push({
|
|
466
|
-
code: ERROR_CODES.FIELD_ERROR,
|
|
467
|
-
message: messages[0],
|
|
468
|
-
severity: "warning",
|
|
469
|
-
field
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
if (status && typeof status === "string") {
|
|
475
|
-
const errorCode = status;
|
|
476
|
-
if (ERROR_SEVERITY_MAP[errorCode]) {
|
|
477
|
-
errors.push({
|
|
478
|
-
code: errorCode,
|
|
479
|
-
message: getErrorMessage(errorCode),
|
|
480
|
-
severity: ERROR_SEVERITY_MAP[errorCode]
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (apiResponse.errors) {
|
|
487
|
-
if (typeof apiResponse.errors === "string") {
|
|
488
|
-
errors.push({
|
|
489
|
-
code: ERROR_CODES.BAD_REQUEST,
|
|
490
|
-
message: apiResponse.errors,
|
|
491
|
-
severity: "warning"
|
|
492
|
-
});
|
|
493
|
-
} else if (typeof apiResponse.errors === "object") {
|
|
494
|
-
const errorObj = apiResponse.errors;
|
|
495
|
-
Object.entries(errorObj).forEach(([field, messages]) => {
|
|
496
|
-
if (Array.isArray(messages) && messages.length > 0) {
|
|
497
|
-
if (field === "_error") {
|
|
498
|
-
messages.forEach((msg) => {
|
|
499
|
-
const errorCode = typeof msg === "string" && isValidErrorCode(msg) ? msg : ERROR_CODES.BAD_REQUEST;
|
|
500
|
-
errors.push({
|
|
501
|
-
code: errorCode,
|
|
502
|
-
message: typeof msg === "string" ? msg : getErrorMessage(errorCode),
|
|
503
|
-
severity: ERROR_SEVERITY_MAP[errorCode] || "warning"
|
|
504
|
-
});
|
|
505
|
-
});
|
|
506
|
-
} else if (field === "_graphql") {
|
|
507
|
-
messages.forEach((msg) => {
|
|
508
|
-
if (typeof msg === "string") {
|
|
509
|
-
const errorCode = isValidErrorCode(msg) ? msg : ERROR_CODES.BAD_REQUEST;
|
|
510
|
-
errors.push({
|
|
511
|
-
code: errorCode,
|
|
512
|
-
message: getErrorMessage(errorCode),
|
|
513
|
-
severity: ERROR_SEVERITY_MAP[errorCode] || "warning"
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
});
|
|
517
|
-
} else {
|
|
518
|
-
errors.push({
|
|
519
|
-
code: ERROR_CODES.FIELD_ERROR,
|
|
520
|
-
message: typeof messages[0] === "string" ? messages[0] : "Validation error",
|
|
521
|
-
severity: "warning",
|
|
522
|
-
field
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
});
|
|
527
|
-
}
|
|
396
|
+
// src/hooks/useSession.ts
|
|
397
|
+
import { useState as useState3, useEffect as useEffect4, useCallback } from "react";
|
|
398
|
+
|
|
399
|
+
// src/core/SessionManager.ts
|
|
400
|
+
import crudify2 from "@nocios/crudify-browser";
|
|
401
|
+
|
|
402
|
+
// src/utils/tokenStorage.ts
|
|
403
|
+
import CryptoJS from "crypto-js";
|
|
404
|
+
var _TokenStorage = class _TokenStorage {
|
|
405
|
+
/**
|
|
406
|
+
* Configurar tipo de almacenamiento
|
|
407
|
+
*/
|
|
408
|
+
static setStorageType(type) {
|
|
409
|
+
_TokenStorage.storageType = type;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Verificar si el storage está disponible
|
|
413
|
+
*/
|
|
414
|
+
static isStorageAvailable(type) {
|
|
415
|
+
try {
|
|
416
|
+
const storage = window[type];
|
|
417
|
+
const testKey = "__storage_test__";
|
|
418
|
+
storage.setItem(testKey, "test");
|
|
419
|
+
storage.removeItem(testKey);
|
|
420
|
+
return true;
|
|
421
|
+
} catch {
|
|
422
|
+
return false;
|
|
528
423
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Obtener instancia de storage
|
|
427
|
+
*/
|
|
428
|
+
static getStorage() {
|
|
429
|
+
if (_TokenStorage.storageType === "none") return null;
|
|
430
|
+
if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
|
|
431
|
+
console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
|
|
432
|
+
return null;
|
|
535
433
|
}
|
|
536
|
-
|
|
537
|
-
errors.push({
|
|
538
|
-
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
539
|
-
message: "Failed to parse error response",
|
|
540
|
-
severity: "error",
|
|
541
|
-
details: { originalError: error }
|
|
542
|
-
});
|
|
434
|
+
return window[_TokenStorage.storageType];
|
|
543
435
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
436
|
+
/**
|
|
437
|
+
* Encriptar datos sensibles
|
|
438
|
+
*/
|
|
439
|
+
static encrypt(data) {
|
|
440
|
+
try {
|
|
441
|
+
return CryptoJS.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
|
|
442
|
+
} catch (error) {
|
|
443
|
+
console.error("Crudify: Encryption failed", error);
|
|
444
|
+
return data;
|
|
549
445
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
severity: "warning",
|
|
563
|
-
details: { transactionIndex: index }
|
|
564
|
-
});
|
|
565
|
-
} else if (!item.response || item.response.status !== "OK") {
|
|
566
|
-
errors.push({
|
|
567
|
-
code: ERROR_CODES.BAD_REQUEST,
|
|
568
|
-
message: "Transaction failed",
|
|
569
|
-
severity: "warning",
|
|
570
|
-
details: { transactionIndex: index, response: item.response }
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
});
|
|
574
|
-
return errors;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Desencriptar datos
|
|
449
|
+
*/
|
|
450
|
+
static decrypt(encryptedData) {
|
|
451
|
+
try {
|
|
452
|
+
const bytes = CryptoJS.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
|
|
453
|
+
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
|
454
|
+
return decrypted || encryptedData;
|
|
455
|
+
} catch (error) {
|
|
456
|
+
console.error("Crudify: Decryption failed", error);
|
|
457
|
+
return encryptedData;
|
|
575
458
|
}
|
|
576
|
-
return parseApiError(response);
|
|
577
|
-
} catch (error) {
|
|
578
|
-
return [
|
|
579
|
-
{
|
|
580
|
-
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
581
|
-
message: "Failed to parse transaction error",
|
|
582
|
-
severity: "error",
|
|
583
|
-
details: { originalError: error }
|
|
584
|
-
}
|
|
585
|
-
];
|
|
586
459
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
[ERROR_CODES.NOT_FOUND]: "Resource not found",
|
|
601
|
-
[ERROR_CODES.IN_USE]: "Resource is currently in use",
|
|
602
|
-
[ERROR_CODES.FIELD_ERROR]: "Validation error",
|
|
603
|
-
[ERROR_CODES.BAD_REQUEST]: "Invalid request",
|
|
604
|
-
[ERROR_CODES.INVALID_EMAIL]: "Please enter a valid email address",
|
|
605
|
-
[ERROR_CODES.INVALID_CODE]: "Invalid or expired code",
|
|
606
|
-
[ERROR_CODES.INTERNAL_SERVER_ERROR]: "Internal server error",
|
|
607
|
-
[ERROR_CODES.DATABASE_CONNECTION_ERROR]: "Database connection error",
|
|
608
|
-
[ERROR_CODES.INVALID_CONFIGURATION]: "Invalid configuration",
|
|
609
|
-
[ERROR_CODES.UNKNOWN_OPERATION]: "Unknown operation",
|
|
610
|
-
[ERROR_CODES.TOO_MANY_REQUESTS]: "Too many requests. Please try again later.",
|
|
611
|
-
[ERROR_CODES.NETWORK_ERROR]: "Network error. Please check your connection.",
|
|
612
|
-
[ERROR_CODES.TIMEOUT_ERROR]: "Request timed out. Please try again."
|
|
613
|
-
};
|
|
614
|
-
return messages[code] || "An unknown error occurred";
|
|
615
|
-
}
|
|
616
|
-
function parseJavaScriptError(error) {
|
|
617
|
-
if (error instanceof Error) {
|
|
618
|
-
if (error.name === "AbortError") {
|
|
619
|
-
return {
|
|
620
|
-
code: ERROR_CODES.TIMEOUT_ERROR,
|
|
621
|
-
message: "Request was cancelled",
|
|
622
|
-
severity: "info"
|
|
460
|
+
/**
|
|
461
|
+
* Guardar tokens de forma segura
|
|
462
|
+
*/
|
|
463
|
+
static saveTokens(tokens) {
|
|
464
|
+
const storage = _TokenStorage.getStorage();
|
|
465
|
+
if (!storage) return;
|
|
466
|
+
try {
|
|
467
|
+
const tokenData = {
|
|
468
|
+
accessToken: tokens.accessToken,
|
|
469
|
+
refreshToken: tokens.refreshToken,
|
|
470
|
+
expiresAt: tokens.expiresAt,
|
|
471
|
+
refreshExpiresAt: tokens.refreshExpiresAt,
|
|
472
|
+
savedAt: Date.now()
|
|
623
473
|
};
|
|
474
|
+
const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
|
|
475
|
+
storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
|
|
476
|
+
console.debug("Crudify: Tokens saved successfully");
|
|
477
|
+
} catch (error) {
|
|
478
|
+
console.error("Crudify: Failed to save tokens", error);
|
|
624
479
|
}
|
|
625
|
-
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Obtener tokens guardados
|
|
483
|
+
*/
|
|
484
|
+
static getTokens() {
|
|
485
|
+
const storage = _TokenStorage.getStorage();
|
|
486
|
+
if (!storage) return null;
|
|
487
|
+
try {
|
|
488
|
+
const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
|
|
489
|
+
if (!encrypted) return null;
|
|
490
|
+
const decrypted = _TokenStorage.decrypt(encrypted);
|
|
491
|
+
const tokenData = JSON.parse(decrypted);
|
|
492
|
+
if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
|
|
493
|
+
console.warn("Crudify: Incomplete token data found, clearing storage");
|
|
494
|
+
_TokenStorage.clearTokens();
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
if (Date.now() >= tokenData.refreshExpiresAt) {
|
|
498
|
+
console.info("Crudify: Refresh token expired, clearing storage");
|
|
499
|
+
_TokenStorage.clearTokens();
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
626
502
|
return {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
503
|
+
accessToken: tokenData.accessToken,
|
|
504
|
+
refreshToken: tokenData.refreshToken,
|
|
505
|
+
expiresAt: tokenData.expiresAt,
|
|
506
|
+
refreshExpiresAt: tokenData.refreshExpiresAt
|
|
630
507
|
};
|
|
508
|
+
} catch (error) {
|
|
509
|
+
console.error("Crudify: Failed to retrieve tokens", error);
|
|
510
|
+
_TokenStorage.clearTokens();
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Limpiar tokens almacenados
|
|
516
|
+
*/
|
|
517
|
+
static clearTokens() {
|
|
518
|
+
const storage = _TokenStorage.getStorage();
|
|
519
|
+
if (!storage) return;
|
|
520
|
+
try {
|
|
521
|
+
storage.removeItem(_TokenStorage.TOKEN_KEY);
|
|
522
|
+
console.debug("Crudify: Tokens cleared from storage");
|
|
523
|
+
} catch (error) {
|
|
524
|
+
console.error("Crudify: Failed to clear tokens", error);
|
|
631
525
|
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Verificar si hay tokens válidos guardados
|
|
529
|
+
*/
|
|
530
|
+
static hasValidTokens() {
|
|
531
|
+
const tokens = _TokenStorage.getTokens();
|
|
532
|
+
return tokens !== null;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Obtener información de expiración
|
|
536
|
+
*/
|
|
537
|
+
static getExpirationInfo() {
|
|
538
|
+
const tokens = _TokenStorage.getTokens();
|
|
539
|
+
if (!tokens) return null;
|
|
540
|
+
const now = Date.now();
|
|
632
541
|
return {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
542
|
+
accessExpired: now >= tokens.expiresAt,
|
|
543
|
+
refreshExpired: now >= tokens.refreshExpiresAt,
|
|
544
|
+
accessExpiresIn: Math.max(0, tokens.expiresAt - now),
|
|
545
|
+
refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
|
|
637
546
|
};
|
|
638
547
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
if (error instanceof Error) {
|
|
648
|
-
return [parseJavaScriptError(error)];
|
|
649
|
-
}
|
|
650
|
-
if (typeof error === "object" && error !== null) {
|
|
651
|
-
const response = error;
|
|
652
|
-
if (response.data && Array.isArray(response.data)) {
|
|
653
|
-
return parseTransactionError(error);
|
|
548
|
+
/**
|
|
549
|
+
* Actualizar solo el access token (después de refresh)
|
|
550
|
+
*/
|
|
551
|
+
static updateAccessToken(newAccessToken, newExpiresAt) {
|
|
552
|
+
const existingTokens = _TokenStorage.getTokens();
|
|
553
|
+
if (!existingTokens) {
|
|
554
|
+
console.warn("Crudify: Cannot update access token, no existing tokens found");
|
|
555
|
+
return;
|
|
654
556
|
}
|
|
655
|
-
|
|
557
|
+
_TokenStorage.saveTokens({
|
|
558
|
+
...existingTokens,
|
|
559
|
+
accessToken: newAccessToken,
|
|
560
|
+
expiresAt: newExpiresAt
|
|
561
|
+
});
|
|
656
562
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
details: { originalError: error }
|
|
663
|
-
}
|
|
664
|
-
];
|
|
665
|
-
}
|
|
563
|
+
};
|
|
564
|
+
_TokenStorage.TOKEN_KEY = "crudify_tokens";
|
|
565
|
+
_TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
|
|
566
|
+
_TokenStorage.storageType = "localStorage";
|
|
567
|
+
var TokenStorage = _TokenStorage;
|
|
666
568
|
|
|
667
|
-
// src/
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
try {
|
|
677
|
-
const decodedPath = decodeURIComponent(state.searchParams.redirect);
|
|
678
|
-
if (decodedPath.startsWith("/") && !decodedPath.startsWith("//")) {
|
|
679
|
-
return decodedPath;
|
|
680
|
-
}
|
|
681
|
-
} catch (error) {
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
return redirectUrl || "/";
|
|
685
|
-
};
|
|
686
|
-
useEffect4(() => {
|
|
687
|
-
const timer = setTimeout(() => {
|
|
688
|
-
if (usernameInputRef.current) usernameInputRef.current.focus();
|
|
689
|
-
}, 100);
|
|
690
|
-
return () => clearTimeout(timer);
|
|
691
|
-
}, []);
|
|
692
|
-
const translateError = (parsedError) => {
|
|
693
|
-
const possibleKeys = [
|
|
694
|
-
`errors.auth.${parsedError.code}`,
|
|
695
|
-
`errors.data.${parsedError.code}`,
|
|
696
|
-
`errors.system.${parsedError.code}`,
|
|
697
|
-
`errors.${parsedError.code}`,
|
|
698
|
-
`login.${parsedError.code.toLowerCase()}`
|
|
699
|
-
];
|
|
700
|
-
for (const key of possibleKeys) {
|
|
701
|
-
const translated = t(key);
|
|
702
|
-
if (translated !== key) {
|
|
703
|
-
return translated;
|
|
704
|
-
}
|
|
569
|
+
// src/core/SessionManager.ts
|
|
570
|
+
var SessionManager = class _SessionManager {
|
|
571
|
+
constructor() {
|
|
572
|
+
this.config = {};
|
|
573
|
+
this.initialized = false;
|
|
574
|
+
}
|
|
575
|
+
static getInstance() {
|
|
576
|
+
if (!_SessionManager.instance) {
|
|
577
|
+
_SessionManager.instance = new _SessionManager();
|
|
705
578
|
}
|
|
706
|
-
return
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
579
|
+
return _SessionManager.instance;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Inicializar el SessionManager
|
|
583
|
+
*/
|
|
584
|
+
async initialize(config = {}) {
|
|
585
|
+
if (this.initialized) {
|
|
586
|
+
console.warn("SessionManager: Already initialized");
|
|
712
587
|
return;
|
|
713
588
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
589
|
+
this.config = {
|
|
590
|
+
storageType: "localStorage",
|
|
591
|
+
autoRestore: true,
|
|
592
|
+
enableLogging: false,
|
|
593
|
+
...config
|
|
594
|
+
};
|
|
595
|
+
TokenStorage.setStorageType(this.config.storageType || "localStorage");
|
|
596
|
+
if (this.config.enableLogging) {
|
|
717
597
|
}
|
|
718
|
-
|
|
719
|
-
|
|
598
|
+
if (this.config.autoRestore) {
|
|
599
|
+
await this.restoreSession();
|
|
600
|
+
}
|
|
601
|
+
this.initialized = true;
|
|
602
|
+
this.log("SessionManager initialized successfully");
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Login con persistencia automática
|
|
606
|
+
*/
|
|
607
|
+
async login(email, password) {
|
|
720
608
|
try {
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
if (onLoginSuccess) {
|
|
730
|
-
onLoginSuccess(response.data, finalRedirectUrl);
|
|
731
|
-
}
|
|
732
|
-
} else {
|
|
733
|
-
handleLoginError(response);
|
|
609
|
+
this.log("Attempting login...");
|
|
610
|
+
const response = await crudify2.login(email, password);
|
|
611
|
+
if (!response.success) {
|
|
612
|
+
this.log("Login failed:", response.errors);
|
|
613
|
+
return {
|
|
614
|
+
success: false,
|
|
615
|
+
error: this.formatError(response.errors)
|
|
616
|
+
};
|
|
734
617
|
}
|
|
618
|
+
const tokens = {
|
|
619
|
+
accessToken: response.data.token,
|
|
620
|
+
refreshToken: response.data.refreshToken,
|
|
621
|
+
expiresAt: response.data.expiresAt,
|
|
622
|
+
refreshExpiresAt: response.data.refreshExpiresAt
|
|
623
|
+
};
|
|
624
|
+
TokenStorage.saveTokens(tokens);
|
|
625
|
+
this.log("Login successful, tokens saved");
|
|
626
|
+
this.config.onLoginSuccess?.(tokens);
|
|
627
|
+
return {
|
|
628
|
+
success: true,
|
|
629
|
+
tokens
|
|
630
|
+
};
|
|
735
631
|
} catch (error) {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
onError(translatedErrors.join(", "));
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
};
|
|
745
|
-
const handleLoginError = (response) => {
|
|
746
|
-
const parsedErrors = handleCrudifyError(response);
|
|
747
|
-
parsedErrors.forEach((error) => {
|
|
748
|
-
if (error.field) {
|
|
749
|
-
setFieldError(error.field, translateError(error));
|
|
750
|
-
} else {
|
|
751
|
-
const currentGlobalErrors = state.errors.global || [];
|
|
752
|
-
setFieldError("global", [...currentGlobalErrors, translateError(error)]);
|
|
753
|
-
}
|
|
754
|
-
});
|
|
755
|
-
};
|
|
756
|
-
const handleSubmit = (e) => {
|
|
757
|
-
e.preventDefault();
|
|
758
|
-
handleLogin();
|
|
759
|
-
};
|
|
760
|
-
const handleKeyDown = (e) => {
|
|
761
|
-
if (e.key === "Enter" && !state.loading) {
|
|
762
|
-
e.preventDefault();
|
|
763
|
-
handleLogin();
|
|
764
|
-
}
|
|
765
|
-
};
|
|
766
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
767
|
-
/* @__PURE__ */ jsxs(
|
|
768
|
-
Box,
|
|
769
|
-
{
|
|
770
|
-
component: "form",
|
|
771
|
-
noValidate: true,
|
|
772
|
-
onSubmit: handleSubmit,
|
|
773
|
-
onKeyDown: handleKeyDown,
|
|
774
|
-
sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 },
|
|
775
|
-
children: [
|
|
776
|
-
/* @__PURE__ */ jsxs(Box, { sx: { mb: 1 }, children: [
|
|
777
|
-
/* @__PURE__ */ jsx4(
|
|
778
|
-
Typography,
|
|
779
|
-
{
|
|
780
|
-
variant: "body2",
|
|
781
|
-
component: "label",
|
|
782
|
-
htmlFor: "email",
|
|
783
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
784
|
-
children: t("login.usernameOrEmailLabel")
|
|
785
|
-
}
|
|
786
|
-
),
|
|
787
|
-
/* @__PURE__ */ jsx4(
|
|
788
|
-
TextField,
|
|
789
|
-
{
|
|
790
|
-
fullWidth: true,
|
|
791
|
-
id: "email",
|
|
792
|
-
name: "email",
|
|
793
|
-
type: "email",
|
|
794
|
-
value: state.formData.username,
|
|
795
|
-
disabled: state.loading,
|
|
796
|
-
onChange: (e) => updateFormData({ username: e.target.value }),
|
|
797
|
-
error: !!state.errors.username,
|
|
798
|
-
helperText: state.errors.username,
|
|
799
|
-
autoComplete: "email",
|
|
800
|
-
placeholder: t("login.usernameOrEmailPlaceholder"),
|
|
801
|
-
inputRef: usernameInputRef,
|
|
802
|
-
required: true
|
|
803
|
-
}
|
|
804
|
-
)
|
|
805
|
-
] }),
|
|
806
|
-
/* @__PURE__ */ jsxs(Box, { sx: { mb: 1 }, children: [
|
|
807
|
-
/* @__PURE__ */ jsx4(
|
|
808
|
-
Typography,
|
|
809
|
-
{
|
|
810
|
-
variant: "body2",
|
|
811
|
-
component: "label",
|
|
812
|
-
htmlFor: "password",
|
|
813
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
814
|
-
children: t("login.passwordLabel")
|
|
815
|
-
}
|
|
816
|
-
),
|
|
817
|
-
/* @__PURE__ */ jsx4(
|
|
818
|
-
TextField,
|
|
819
|
-
{
|
|
820
|
-
fullWidth: true,
|
|
821
|
-
id: "password",
|
|
822
|
-
name: "password",
|
|
823
|
-
type: "password",
|
|
824
|
-
value: state.formData.password,
|
|
825
|
-
disabled: state.loading,
|
|
826
|
-
onChange: (e) => updateFormData({ password: e.target.value }),
|
|
827
|
-
error: !!state.errors.password,
|
|
828
|
-
helperText: state.errors.password,
|
|
829
|
-
autoComplete: "current-password",
|
|
830
|
-
placeholder: t("login.passwordPlaceholder"),
|
|
831
|
-
required: true
|
|
832
|
-
}
|
|
833
|
-
)
|
|
834
|
-
] }),
|
|
835
|
-
state.config.loginActions?.includes("forgotPassword") && /* @__PURE__ */ jsx4(Box, { sx: { display: "flex", justifyContent: "flex-end", alignItems: "center" }, children: /* @__PURE__ */ jsx4(
|
|
836
|
-
Link,
|
|
837
|
-
{
|
|
838
|
-
sx: { cursor: "pointer" },
|
|
839
|
-
onClick: () => {
|
|
840
|
-
onScreenChange?.("forgotPassword", state.searchParams);
|
|
841
|
-
},
|
|
842
|
-
variant: "body2",
|
|
843
|
-
color: "secondary",
|
|
844
|
-
children: t("login.forgotPasswordLink")
|
|
845
|
-
}
|
|
846
|
-
) }),
|
|
847
|
-
/* @__PURE__ */ jsx4(Button, { disabled: state.loading, type: "submit", fullWidth: true, variant: "contained", color: "primary", sx: { mt: 1, mb: 2 }, children: state.loading ? /* @__PURE__ */ jsx4(CircularProgress, { size: 20 }) : t("login.loginButton") })
|
|
848
|
-
]
|
|
849
|
-
}
|
|
850
|
-
),
|
|
851
|
-
/* @__PURE__ */ jsx4(Box, { children: state.errors.global && state.errors.global.length > 0 && state.errors.global.map((error, index) => /* @__PURE__ */ jsx4(Alert, { variant: "filled", sx: { mt: 2 }, severity: "error", children: /* @__PURE__ */ jsx4("div", { children: error }) }, index)) }),
|
|
852
|
-
state.config.loginActions?.includes("createUser") && /* @__PURE__ */ jsxs(Typography, { variant: "body2", align: "center", sx: { color: "text.secondary", mt: 3 }, children: [
|
|
853
|
-
t("login.noAccountPrompt"),
|
|
854
|
-
" ",
|
|
855
|
-
/* @__PURE__ */ jsx4(
|
|
856
|
-
Link,
|
|
857
|
-
{
|
|
858
|
-
sx: { cursor: "pointer" },
|
|
859
|
-
onClick: () => {
|
|
860
|
-
const searchString = Object.keys(state.searchParams).length > 0 ? `?${new URLSearchParams(state.searchParams).toString()}` : "";
|
|
861
|
-
const signupUrl = `/public/users/create${searchString}`;
|
|
862
|
-
onExternalNavigate?.(signupUrl);
|
|
863
|
-
},
|
|
864
|
-
fontWeight: "medium",
|
|
865
|
-
color: "secondary",
|
|
866
|
-
children: t("login.signUpLink")
|
|
867
|
-
}
|
|
868
|
-
)
|
|
869
|
-
] })
|
|
870
|
-
] });
|
|
871
|
-
};
|
|
872
|
-
var LoginForm_default = LoginForm;
|
|
873
|
-
|
|
874
|
-
// src/components/CrudifyLogin/Forms/ForgotPasswordForm.tsx
|
|
875
|
-
import { useState as useState3 } from "react";
|
|
876
|
-
import { Typography as Typography2, TextField as TextField2, Button as Button2, Box as Box2, CircularProgress as CircularProgress2, Alert as Alert2, Link as Link2 } from "@mui/material";
|
|
877
|
-
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
878
|
-
var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
879
|
-
const { crudify: crudify6 } = useCrudify();
|
|
880
|
-
const [email, setEmail] = useState3("");
|
|
881
|
-
const [loading, setLoading] = useState3(false);
|
|
882
|
-
const [errors, setErrors] = useState3([]);
|
|
883
|
-
const [helperTextEmail, setHelperTextEmail] = useState3(null);
|
|
884
|
-
const [emailSent, setEmailSent] = useState3(false);
|
|
885
|
-
const [codeAlreadyExists, setCodeAlreadyExists] = useState3(false);
|
|
886
|
-
const { t } = useTranslation();
|
|
887
|
-
const translateError = (parsedError) => {
|
|
888
|
-
const possibleKeys = [
|
|
889
|
-
`errors.auth.${parsedError.code}`,
|
|
890
|
-
`errors.data.${parsedError.code}`,
|
|
891
|
-
`errors.system.${parsedError.code}`,
|
|
892
|
-
`errors.${parsedError.code}`,
|
|
893
|
-
`forgotPassword.${parsedError.code.toLowerCase()}`
|
|
894
|
-
];
|
|
895
|
-
for (const key of possibleKeys) {
|
|
896
|
-
const translated = t(key);
|
|
897
|
-
if (translated !== key) {
|
|
898
|
-
return translated;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
return parsedError.message || t("error.unknown");
|
|
902
|
-
};
|
|
903
|
-
const validateEmail = (email2) => {
|
|
904
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
905
|
-
return emailRegex.test(email2);
|
|
906
|
-
};
|
|
907
|
-
const handleSubmit = async () => {
|
|
908
|
-
if (loading || !crudify6) return;
|
|
909
|
-
setErrors([]);
|
|
910
|
-
setHelperTextEmail(null);
|
|
911
|
-
if (!email) {
|
|
912
|
-
setHelperTextEmail(t("forgotPassword.emailRequired"));
|
|
913
|
-
return;
|
|
632
|
+
this.log("Login error:", error);
|
|
633
|
+
return {
|
|
634
|
+
success: false,
|
|
635
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
636
|
+
};
|
|
914
637
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Logout con limpieza de tokens
|
|
641
|
+
*/
|
|
642
|
+
async logout() {
|
|
643
|
+
try {
|
|
644
|
+
this.log("Logging out...");
|
|
645
|
+
await crudify2.logout();
|
|
646
|
+
TokenStorage.clearTokens();
|
|
647
|
+
this.log("Logout successful");
|
|
648
|
+
this.config.onLogout?.();
|
|
649
|
+
} catch (error) {
|
|
650
|
+
this.log("Logout error:", error);
|
|
651
|
+
TokenStorage.clearTokens();
|
|
918
652
|
}
|
|
919
|
-
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Restaurar sesión desde storage
|
|
656
|
+
*/
|
|
657
|
+
async restoreSession() {
|
|
920
658
|
try {
|
|
921
|
-
|
|
922
|
-
const
|
|
923
|
-
if (
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
} else {
|
|
927
|
-
setEmailSent(true);
|
|
928
|
-
}
|
|
929
|
-
} else {
|
|
930
|
-
const parsedErrors = handleCrudifyError(response);
|
|
931
|
-
const translatedErrors = parsedErrors.map(translateError);
|
|
932
|
-
setErrors(translatedErrors);
|
|
659
|
+
this.log("Attempting to restore session...");
|
|
660
|
+
const savedTokens = TokenStorage.getTokens();
|
|
661
|
+
if (!savedTokens) {
|
|
662
|
+
this.log("No valid tokens found in storage");
|
|
663
|
+
return false;
|
|
933
664
|
}
|
|
665
|
+
crudify2.setTokens({
|
|
666
|
+
accessToken: savedTokens.accessToken,
|
|
667
|
+
refreshToken: savedTokens.refreshToken,
|
|
668
|
+
expiresAt: savedTokens.expiresAt,
|
|
669
|
+
refreshExpiresAt: savedTokens.refreshExpiresAt
|
|
670
|
+
});
|
|
671
|
+
this.log("Session restored successfully");
|
|
672
|
+
this.config.onSessionRestored?.(savedTokens);
|
|
673
|
+
return true;
|
|
934
674
|
} catch (error) {
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
if (onError) {
|
|
939
|
-
onError(translatedErrors.join(", "));
|
|
940
|
-
}
|
|
941
|
-
} finally {
|
|
942
|
-
setLoading(false);
|
|
943
|
-
}
|
|
944
|
-
};
|
|
945
|
-
const handleBack = () => {
|
|
946
|
-
onScreenChange?.("login");
|
|
947
|
-
};
|
|
948
|
-
const handleGoToCheckCode = () => {
|
|
949
|
-
if (emailSent || codeAlreadyExists) {
|
|
950
|
-
onScreenChange?.("checkCode", { email });
|
|
951
|
-
return;
|
|
952
|
-
}
|
|
953
|
-
if (!email) {
|
|
954
|
-
setHelperTextEmail(t("forgotPassword.emailRequired"));
|
|
955
|
-
return;
|
|
956
|
-
}
|
|
957
|
-
if (!validateEmail(email)) {
|
|
958
|
-
setHelperTextEmail(t("forgotPassword.invalidEmail"));
|
|
959
|
-
return;
|
|
675
|
+
this.log("Session restore error:", error);
|
|
676
|
+
TokenStorage.clearTokens();
|
|
677
|
+
return false;
|
|
960
678
|
}
|
|
961
|
-
onScreenChange?.("checkCode", { email });
|
|
962
|
-
};
|
|
963
|
-
if (emailSent || codeAlreadyExists) {
|
|
964
|
-
return /* @__PURE__ */ jsx5(Fragment2, { children: /* @__PURE__ */ jsxs2(Box2, { sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2, textAlign: "center" }, children: [
|
|
965
|
-
/* @__PURE__ */ jsxs2(Box2, { sx: { mb: 2 }, children: [
|
|
966
|
-
/* @__PURE__ */ jsx5(Typography2, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: codeAlreadyExists ? t("forgotPassword.codeAlreadyExistsMessage") : t("forgotPassword.emailSentMessage") }),
|
|
967
|
-
/* @__PURE__ */ jsx5(Typography2, { variant: "body2", sx: { color: codeAlreadyExists ? "success.main" : "grey.600" }, children: codeAlreadyExists ? t("forgotPassword.checkEmailInstructions") : t("forgotPassword.checkEmailInstructions") })
|
|
968
|
-
] }),
|
|
969
|
-
/* @__PURE__ */ jsx5(Button2, { type: "button", onClick: handleGoToCheckCode, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: t("forgotPassword.enterCodeLink") }),
|
|
970
|
-
/* @__PURE__ */ jsx5(Box2, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx5(Link2, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
|
|
971
|
-
] }) });
|
|
972
679
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
required: true
|
|
1005
|
-
}
|
|
1006
|
-
)
|
|
1007
|
-
] }),
|
|
1008
|
-
/* @__PURE__ */ jsx5(Button2, { disabled: loading, type: "button", onClick: handleSubmit, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx5(CircularProgress2, { size: 20 }) : t("forgotPassword.sendCodeButton") }),
|
|
1009
|
-
/* @__PURE__ */ jsxs2(Box2, { sx: { display: "flex", justifyContent: "center", alignItems: "center", gap: 2 }, children: [
|
|
1010
|
-
/* @__PURE__ */ jsx5(Link2, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }),
|
|
1011
|
-
/* @__PURE__ */ jsx5(Typography2, { variant: "body2", sx: { color: "grey.400" }, children: "\u2022" }),
|
|
1012
|
-
/* @__PURE__ */ jsx5(Link2, { sx: { cursor: "pointer" }, onClick: handleGoToCheckCode, variant: "body2", color: "secondary", children: t("login.alreadyHaveCodeLink") })
|
|
1013
|
-
] })
|
|
1014
|
-
] }),
|
|
1015
|
-
/* @__PURE__ */ jsx5(Box2, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx5(Alert2, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) })
|
|
1016
|
-
] });
|
|
1017
|
-
};
|
|
1018
|
-
var ForgotPasswordForm_default = ForgotPasswordForm;
|
|
1019
|
-
|
|
1020
|
-
// src/components/CrudifyLogin/Forms/ResetPasswordForm.tsx
|
|
1021
|
-
import { useState as useState4, useEffect as useEffect5 } from "react";
|
|
1022
|
-
import { Typography as Typography3, TextField as TextField3, Button as Button3, Box as Box3, CircularProgress as CircularProgress3, Alert as Alert3, Link as Link3 } from "@mui/material";
|
|
1023
|
-
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1024
|
-
var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess }) => {
|
|
1025
|
-
const { crudify: crudify6 } = useCrudify();
|
|
1026
|
-
const [newPassword, setNewPassword] = useState4("");
|
|
1027
|
-
const [confirmPassword, setConfirmPassword] = useState4("");
|
|
1028
|
-
const [loading, setLoading] = useState4(false);
|
|
1029
|
-
const [errors, setErrors] = useState4([]);
|
|
1030
|
-
const [helperTextNewPassword, setHelperTextNewPassword] = useState4(null);
|
|
1031
|
-
const [helperTextConfirmPassword, setHelperTextConfirmPassword] = useState4(null);
|
|
1032
|
-
const [email, setEmail] = useState4("");
|
|
1033
|
-
const [code, setCode] = useState4("");
|
|
1034
|
-
const [fromCodeVerification, setFromCodeVerification] = useState4(false);
|
|
1035
|
-
const [validatingCode, setValidatingCode] = useState4(true);
|
|
1036
|
-
const [codeValidated, setCodeValidated] = useState4(false);
|
|
1037
|
-
const [pendingValidation, setPendingValidation] = useState4(null);
|
|
1038
|
-
const [isValidating, setIsValidating] = useState4(false);
|
|
1039
|
-
const { t } = useTranslation();
|
|
1040
|
-
const translateError = (parsedError) => {
|
|
1041
|
-
const possibleKeys = [
|
|
1042
|
-
`errors.auth.${parsedError.code}`,
|
|
1043
|
-
`errors.data.${parsedError.code}`,
|
|
1044
|
-
`errors.system.${parsedError.code}`,
|
|
1045
|
-
`errors.${parsedError.code}`,
|
|
1046
|
-
`resetPassword.${parsedError.code.toLowerCase()}`
|
|
1047
|
-
];
|
|
1048
|
-
for (const key of possibleKeys) {
|
|
1049
|
-
const translated = t(key);
|
|
1050
|
-
if (translated !== key) {
|
|
1051
|
-
return translated;
|
|
680
|
+
/**
|
|
681
|
+
* Verificar si el usuario está autenticado
|
|
682
|
+
*/
|
|
683
|
+
isAuthenticated() {
|
|
684
|
+
return crudify2.isLogin() || TokenStorage.hasValidTokens();
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Obtener información de tokens actuales
|
|
688
|
+
*/
|
|
689
|
+
getTokenInfo() {
|
|
690
|
+
const crudifyTokens = crudify2.getTokenData();
|
|
691
|
+
const storageInfo = TokenStorage.getExpirationInfo();
|
|
692
|
+
return {
|
|
693
|
+
isLoggedIn: this.isAuthenticated(),
|
|
694
|
+
crudifyTokens,
|
|
695
|
+
storageInfo,
|
|
696
|
+
hasValidTokens: TokenStorage.hasValidTokens()
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Refrescar tokens manualmente
|
|
701
|
+
*/
|
|
702
|
+
async refreshTokens() {
|
|
703
|
+
try {
|
|
704
|
+
this.log("Manually refreshing tokens...");
|
|
705
|
+
const response = await crudify2.refreshAccessToken();
|
|
706
|
+
if (!response.success) {
|
|
707
|
+
this.log("Token refresh failed:", response.errors);
|
|
708
|
+
TokenStorage.clearTokens();
|
|
709
|
+
this.config.onSessionExpired?.();
|
|
710
|
+
return false;
|
|
1052
711
|
}
|
|
712
|
+
const newTokens = {
|
|
713
|
+
accessToken: response.data.token,
|
|
714
|
+
refreshToken: response.data.refreshToken,
|
|
715
|
+
expiresAt: response.data.expiresAt,
|
|
716
|
+
refreshExpiresAt: response.data.refreshExpiresAt
|
|
717
|
+
};
|
|
718
|
+
TokenStorage.saveTokens(newTokens);
|
|
719
|
+
this.log("Tokens refreshed and saved successfully");
|
|
720
|
+
return true;
|
|
721
|
+
} catch (error) {
|
|
722
|
+
this.log("Token refresh error:", error);
|
|
723
|
+
TokenStorage.clearTokens();
|
|
724
|
+
this.config.onSessionExpired?.();
|
|
725
|
+
return false;
|
|
1053
726
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
const codeParam = getParam("code");
|
|
1071
|
-
if (fromCodeVerificationParam === "true" && emailParam && codeParam) {
|
|
1072
|
-
setEmail(emailParam);
|
|
1073
|
-
setCode(codeParam);
|
|
1074
|
-
setFromCodeVerification(true);
|
|
1075
|
-
setCodeValidated(true);
|
|
1076
|
-
setValidatingCode(false);
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
const linkParam = getParam("link");
|
|
1080
|
-
if (linkParam) {
|
|
1081
|
-
try {
|
|
1082
|
-
const decodedLink = decodeURIComponent(linkParam);
|
|
1083
|
-
const [linkCode, linkEmail] = decodedLink.split("/");
|
|
1084
|
-
if (linkCode && linkEmail && linkCode.length === 6) {
|
|
1085
|
-
setCode(linkCode);
|
|
1086
|
-
setEmail(linkEmail);
|
|
1087
|
-
setFromCodeVerification(false);
|
|
1088
|
-
setPendingValidation({ email: linkEmail, code: linkCode });
|
|
1089
|
-
return;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Configurar interceptor de respuesta para manejo automático de errores
|
|
730
|
+
*/
|
|
731
|
+
setupResponseInterceptor() {
|
|
732
|
+
crudify2.setResponseInterceptor(async (response) => {
|
|
733
|
+
if (response.errors) {
|
|
734
|
+
const hasAuthError = response.errors.some(
|
|
735
|
+
(error) => error.message?.includes("Unauthorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED"
|
|
736
|
+
);
|
|
737
|
+
if (hasAuthError && TokenStorage.hasValidTokens()) {
|
|
738
|
+
this.log("Auth error detected, attempting token refresh...");
|
|
739
|
+
const refreshSuccess = await this.refreshTokens();
|
|
740
|
+
if (!refreshSuccess) {
|
|
741
|
+
this.log("Session expired, triggering callback");
|
|
742
|
+
this.config.onSessionExpired?.();
|
|
1090
743
|
}
|
|
1091
|
-
} catch (error) {
|
|
1092
744
|
}
|
|
1093
745
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
try {
|
|
1111
|
-
const data = [
|
|
1112
|
-
{
|
|
1113
|
-
operation: "validatePasswordResetCode",
|
|
1114
|
-
data: { email: emailToValidate, codePassword: codeToValidate }
|
|
1115
|
-
}
|
|
1116
|
-
];
|
|
1117
|
-
const response = await crudify6.transaction(data);
|
|
1118
|
-
if (response.data && Array.isArray(response.data)) {
|
|
1119
|
-
const validationResult = response.data[0];
|
|
1120
|
-
if (validationResult && validationResult.response && validationResult.response.status === "OK") {
|
|
1121
|
-
setCodeValidated(true);
|
|
1122
|
-
return;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
if (response.success) {
|
|
1126
|
-
setCodeValidated(true);
|
|
1127
|
-
} else {
|
|
1128
|
-
const parsedErrors = handleCrudifyError(response);
|
|
1129
|
-
const translatedErrors = parsedErrors.map(translateError);
|
|
1130
|
-
setErrors(translatedErrors);
|
|
1131
|
-
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
1132
|
-
}
|
|
1133
|
-
} catch (error) {
|
|
1134
|
-
const parsedErrors = handleCrudifyError(error);
|
|
1135
|
-
const translatedErrors = parsedErrors.map(translateError);
|
|
1136
|
-
setErrors(translatedErrors);
|
|
1137
|
-
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
1138
|
-
} finally {
|
|
1139
|
-
setValidatingCode(false);
|
|
1140
|
-
setPendingValidation(null);
|
|
1141
|
-
setIsValidating(false);
|
|
1142
|
-
}
|
|
1143
|
-
};
|
|
1144
|
-
validateCode(pendingValidation.email, pendingValidation.code);
|
|
1145
|
-
}
|
|
1146
|
-
}, [crudify6, pendingValidation, t, onScreenChange]);
|
|
1147
|
-
const validatePassword = (password) => {
|
|
1148
|
-
if (password.length < 8) {
|
|
1149
|
-
return t("resetPassword.passwordTooShort");
|
|
746
|
+
return response;
|
|
747
|
+
});
|
|
748
|
+
this.log("Response interceptor configured");
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Limpiar sesión completamente
|
|
752
|
+
*/
|
|
753
|
+
clearSession() {
|
|
754
|
+
TokenStorage.clearTokens();
|
|
755
|
+
crudify2.logout();
|
|
756
|
+
this.log("Session cleared completely");
|
|
757
|
+
}
|
|
758
|
+
// Métodos privados
|
|
759
|
+
log(message, ...args) {
|
|
760
|
+
if (this.config.enableLogging) {
|
|
761
|
+
console.log(`[SessionManager] ${message}`, ...args);
|
|
1150
762
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
if (
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
let hasErrors = false;
|
|
1159
|
-
if (!newPassword) {
|
|
1160
|
-
setHelperTextNewPassword(t("resetPassword.newPasswordRequired"));
|
|
1161
|
-
hasErrors = true;
|
|
1162
|
-
} else {
|
|
1163
|
-
const passwordError = validatePassword(newPassword);
|
|
1164
|
-
if (passwordError) {
|
|
1165
|
-
setHelperTextNewPassword(passwordError);
|
|
1166
|
-
hasErrors = true;
|
|
1167
|
-
}
|
|
763
|
+
}
|
|
764
|
+
formatError(errors) {
|
|
765
|
+
if (!errors) return "Unknown error";
|
|
766
|
+
if (typeof errors === "string") return errors;
|
|
767
|
+
if (typeof errors === "object") {
|
|
768
|
+
const errorMessages = Object.values(errors).flat();
|
|
769
|
+
return errorMessages.join(", ");
|
|
1168
770
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
771
|
+
return "Authentication failed";
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// src/hooks/useSession.ts
|
|
776
|
+
function useSession(options = {}) {
|
|
777
|
+
const [state, setState] = useState3({
|
|
778
|
+
isAuthenticated: false,
|
|
779
|
+
isLoading: true,
|
|
780
|
+
isInitialized: false,
|
|
781
|
+
tokens: null,
|
|
782
|
+
error: null
|
|
783
|
+
});
|
|
784
|
+
const sessionManager = SessionManager.getInstance();
|
|
785
|
+
const initialize = useCallback(async () => {
|
|
1178
786
|
try {
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
787
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
788
|
+
const config = {
|
|
789
|
+
autoRestore: options.autoRestore ?? true,
|
|
790
|
+
enableLogging: options.enableLogging ?? false,
|
|
791
|
+
onSessionExpired: () => {
|
|
792
|
+
setState((prev) => ({
|
|
793
|
+
...prev,
|
|
794
|
+
isAuthenticated: false,
|
|
795
|
+
tokens: null,
|
|
796
|
+
error: "Session expired"
|
|
797
|
+
}));
|
|
798
|
+
options.onSessionExpired?.();
|
|
799
|
+
},
|
|
800
|
+
onSessionRestored: (tokens) => {
|
|
801
|
+
setState((prev) => ({
|
|
802
|
+
...prev,
|
|
803
|
+
isAuthenticated: true,
|
|
804
|
+
tokens,
|
|
805
|
+
error: null
|
|
806
|
+
}));
|
|
807
|
+
options.onSessionRestored?.(tokens);
|
|
808
|
+
},
|
|
809
|
+
onLoginSuccess: (tokens) => {
|
|
810
|
+
setState((prev) => ({
|
|
811
|
+
...prev,
|
|
812
|
+
isAuthenticated: true,
|
|
813
|
+
tokens,
|
|
814
|
+
error: null
|
|
815
|
+
}));
|
|
816
|
+
},
|
|
817
|
+
onLogout: () => {
|
|
818
|
+
setState((prev) => ({
|
|
819
|
+
...prev,
|
|
820
|
+
isAuthenticated: false,
|
|
821
|
+
tokens: null,
|
|
822
|
+
error: null
|
|
823
|
+
}));
|
|
1183
824
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
825
|
+
};
|
|
826
|
+
await sessionManager.initialize(config);
|
|
827
|
+
sessionManager.setupResponseInterceptor();
|
|
828
|
+
const isAuth = sessionManager.isAuthenticated();
|
|
829
|
+
const tokenInfo = sessionManager.getTokenInfo();
|
|
830
|
+
setState((prev) => ({
|
|
831
|
+
...prev,
|
|
832
|
+
isAuthenticated: isAuth,
|
|
833
|
+
isInitialized: true,
|
|
834
|
+
isLoading: false,
|
|
835
|
+
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
836
|
+
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
837
|
+
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
838
|
+
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
839
|
+
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
840
|
+
} : null
|
|
841
|
+
}));
|
|
842
|
+
} catch (error) {
|
|
843
|
+
setState((prev) => ({
|
|
844
|
+
...prev,
|
|
845
|
+
isLoading: false,
|
|
846
|
+
isInitialized: true,
|
|
847
|
+
error: error instanceof Error ? error.message : "Initialization failed"
|
|
848
|
+
}));
|
|
849
|
+
}
|
|
850
|
+
}, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
|
|
851
|
+
const login = useCallback(async (email, password) => {
|
|
852
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
853
|
+
try {
|
|
854
|
+
const result = await sessionManager.login(email, password);
|
|
855
|
+
if (result.success && result.tokens) {
|
|
856
|
+
setState((prev) => ({
|
|
857
|
+
...prev,
|
|
858
|
+
isAuthenticated: true,
|
|
859
|
+
tokens: result.tokens,
|
|
860
|
+
isLoading: false,
|
|
861
|
+
error: null
|
|
862
|
+
}));
|
|
1191
863
|
} else {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
864
|
+
setState((prev) => ({
|
|
865
|
+
...prev,
|
|
866
|
+
isAuthenticated: false,
|
|
867
|
+
tokens: null,
|
|
868
|
+
isLoading: false,
|
|
869
|
+
error: result.error || "Login failed"
|
|
870
|
+
}));
|
|
1195
871
|
}
|
|
872
|
+
return result;
|
|
1196
873
|
} catch (error) {
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
874
|
+
const errorMsg = error instanceof Error ? error.message : "Login failed";
|
|
875
|
+
setState((prev) => ({
|
|
876
|
+
...prev,
|
|
877
|
+
isAuthenticated: false,
|
|
878
|
+
tokens: null,
|
|
879
|
+
isLoading: false,
|
|
880
|
+
error: errorMsg
|
|
881
|
+
}));
|
|
882
|
+
return {
|
|
883
|
+
success: false,
|
|
884
|
+
error: errorMsg
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
}, [sessionManager]);
|
|
888
|
+
const logout = useCallback(async () => {
|
|
889
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
890
|
+
try {
|
|
891
|
+
await sessionManager.logout();
|
|
892
|
+
setState((prev) => ({
|
|
893
|
+
...prev,
|
|
894
|
+
isAuthenticated: false,
|
|
895
|
+
tokens: null,
|
|
896
|
+
isLoading: false,
|
|
897
|
+
error: null
|
|
898
|
+
}));
|
|
899
|
+
} catch (error) {
|
|
900
|
+
setState((prev) => ({
|
|
901
|
+
...prev,
|
|
902
|
+
isAuthenticated: false,
|
|
903
|
+
tokens: null,
|
|
904
|
+
isLoading: false,
|
|
905
|
+
error: error instanceof Error ? error.message : "Logout error"
|
|
906
|
+
}));
|
|
907
|
+
}
|
|
908
|
+
}, [sessionManager]);
|
|
909
|
+
const refreshTokens = useCallback(async () => {
|
|
910
|
+
try {
|
|
911
|
+
const success = await sessionManager.refreshTokens();
|
|
912
|
+
if (success) {
|
|
913
|
+
const tokenInfo = sessionManager.getTokenInfo();
|
|
914
|
+
setState((prev) => ({
|
|
915
|
+
...prev,
|
|
916
|
+
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
917
|
+
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
918
|
+
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
919
|
+
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
920
|
+
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
921
|
+
} : null,
|
|
922
|
+
error: null
|
|
923
|
+
}));
|
|
924
|
+
} else {
|
|
925
|
+
setState((prev) => ({
|
|
926
|
+
...prev,
|
|
927
|
+
isAuthenticated: false,
|
|
928
|
+
tokens: null,
|
|
929
|
+
error: "Token refresh failed"
|
|
930
|
+
}));
|
|
1202
931
|
}
|
|
932
|
+
return success;
|
|
933
|
+
} catch (error) {
|
|
934
|
+
setState((prev) => ({
|
|
935
|
+
...prev,
|
|
936
|
+
isAuthenticated: false,
|
|
937
|
+
tokens: null,
|
|
938
|
+
error: error instanceof Error ? error.message : "Token refresh failed"
|
|
939
|
+
}));
|
|
940
|
+
return false;
|
|
1203
941
|
}
|
|
1204
|
-
|
|
942
|
+
}, [sessionManager]);
|
|
943
|
+
const clearError = useCallback(() => {
|
|
944
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
945
|
+
}, []);
|
|
946
|
+
const getTokenInfo = useCallback(() => {
|
|
947
|
+
return sessionManager.getTokenInfo();
|
|
948
|
+
}, [sessionManager]);
|
|
949
|
+
useEffect4(() => {
|
|
950
|
+
initialize();
|
|
951
|
+
}, [initialize]);
|
|
952
|
+
return {
|
|
953
|
+
// Estado
|
|
954
|
+
...state,
|
|
955
|
+
// Acciones
|
|
956
|
+
login,
|
|
957
|
+
logout,
|
|
958
|
+
refreshTokens,
|
|
959
|
+
clearError,
|
|
960
|
+
getTokenInfo,
|
|
961
|
+
// Utilidades
|
|
962
|
+
isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
|
|
963
|
+
// 5 minutos
|
|
964
|
+
expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
|
|
965
|
+
refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
|
|
1205
966
|
};
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// src/utils/jwtUtils.ts
|
|
970
|
+
var decodeJwtSafely = (token) => {
|
|
971
|
+
try {
|
|
972
|
+
const parts = token.split(".");
|
|
973
|
+
if (parts.length !== 3) {
|
|
974
|
+
console.warn("Invalid JWT format: token must have 3 parts");
|
|
975
|
+
return null;
|
|
1211
976
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
977
|
+
const payload = parts[1];
|
|
978
|
+
const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
979
|
+
const decodedPayload = JSON.parse(atob(paddedPayload));
|
|
980
|
+
return decodedPayload;
|
|
981
|
+
} catch (error) {
|
|
982
|
+
console.warn("Failed to decode JWT token:", error);
|
|
983
|
+
return null;
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
var getCurrentUserEmail = () => {
|
|
987
|
+
try {
|
|
988
|
+
let token = null;
|
|
989
|
+
token = sessionStorage.getItem("authToken");
|
|
990
|
+
console.log("\u{1F50D} getCurrentUserEmail - authToken:", token ? `${token.substring(0, 20)}...` : null);
|
|
991
|
+
if (!token) {
|
|
992
|
+
token = sessionStorage.getItem("token");
|
|
993
|
+
console.log("\u{1F50D} getCurrentUserEmail - token:", token ? `${token.substring(0, 20)}...` : null);
|
|
994
|
+
}
|
|
995
|
+
if (!token) {
|
|
996
|
+
token = localStorage.getItem("authToken") || localStorage.getItem("token");
|
|
997
|
+
console.log("\u{1F50D} getCurrentUserEmail - localStorage:", token ? `${token.substring(0, 20)}...` : null);
|
|
998
|
+
}
|
|
999
|
+
if (!token) {
|
|
1000
|
+
console.warn("\u{1F50D} getCurrentUserEmail - No token found in any storage");
|
|
1001
|
+
return null;
|
|
1002
|
+
}
|
|
1003
|
+
const payload = decodeJwtSafely(token);
|
|
1004
|
+
if (!payload) {
|
|
1005
|
+
console.warn("\u{1F50D} getCurrentUserEmail - Failed to decode token");
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
const email = payload.email || payload["cognito:username"] || null;
|
|
1009
|
+
console.log("\u{1F50D} getCurrentUserEmail - Extracted email:", email);
|
|
1010
|
+
return email;
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
console.warn("Failed to get current user email:", error);
|
|
1013
|
+
return null;
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
var isTokenExpired = (token) => {
|
|
1017
|
+
try {
|
|
1018
|
+
const payload = decodeJwtSafely(token);
|
|
1019
|
+
if (!payload || !payload.exp) return true;
|
|
1020
|
+
const currentTime = Math.floor(Date.now() / 1e3);
|
|
1021
|
+
return payload.exp < currentTime;
|
|
1022
|
+
} catch {
|
|
1023
|
+
return true;
|
|
1218
1024
|
}
|
|
1219
|
-
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1220
|
-
/* @__PURE__ */ jsxs3(Box3, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
1221
|
-
/* @__PURE__ */ jsxs3(Box3, { sx: { mb: 2 }, children: [
|
|
1222
|
-
/* @__PURE__ */ jsx6(Typography3, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("resetPassword.title") }),
|
|
1223
|
-
/* @__PURE__ */ jsx6(Typography3, { variant: "body2", sx: { color: "grey.600" }, children: t("resetPassword.instructions") })
|
|
1224
|
-
] }),
|
|
1225
|
-
/* @__PURE__ */ jsxs3(Box3, { sx: { mb: 1 }, children: [
|
|
1226
|
-
/* @__PURE__ */ jsx6(
|
|
1227
|
-
Typography3,
|
|
1228
|
-
{
|
|
1229
|
-
variant: "body2",
|
|
1230
|
-
component: "label",
|
|
1231
|
-
htmlFor: "newPassword",
|
|
1232
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
1233
|
-
children: t("resetPassword.newPasswordLabel")
|
|
1234
|
-
}
|
|
1235
|
-
),
|
|
1236
|
-
/* @__PURE__ */ jsx6(
|
|
1237
|
-
TextField3,
|
|
1238
|
-
{
|
|
1239
|
-
fullWidth: true,
|
|
1240
|
-
id: "newPassword",
|
|
1241
|
-
name: "newPassword",
|
|
1242
|
-
type: "password",
|
|
1243
|
-
value: newPassword,
|
|
1244
|
-
disabled: loading,
|
|
1245
|
-
onChange: (e) => setNewPassword(e.target.value),
|
|
1246
|
-
error: !!helperTextNewPassword,
|
|
1247
|
-
helperText: helperTextNewPassword,
|
|
1248
|
-
autoComplete: "new-password",
|
|
1249
|
-
placeholder: t("resetPassword.newPasswordPlaceholder"),
|
|
1250
|
-
required: true
|
|
1251
|
-
}
|
|
1252
|
-
)
|
|
1253
|
-
] }),
|
|
1254
|
-
/* @__PURE__ */ jsxs3(Box3, { sx: { mb: 1 }, children: [
|
|
1255
|
-
/* @__PURE__ */ jsx6(
|
|
1256
|
-
Typography3,
|
|
1257
|
-
{
|
|
1258
|
-
variant: "body2",
|
|
1259
|
-
component: "label",
|
|
1260
|
-
htmlFor: "confirmPassword",
|
|
1261
|
-
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
1262
|
-
children: t("resetPassword.confirmPasswordLabel")
|
|
1263
|
-
}
|
|
1264
|
-
),
|
|
1265
|
-
/* @__PURE__ */ jsx6(
|
|
1266
|
-
TextField3,
|
|
1267
|
-
{
|
|
1268
|
-
fullWidth: true,
|
|
1269
|
-
id: "confirmPassword",
|
|
1270
|
-
name: "confirmPassword",
|
|
1271
|
-
type: "password",
|
|
1272
|
-
value: confirmPassword,
|
|
1273
|
-
disabled: loading,
|
|
1274
|
-
onChange: (e) => setConfirmPassword(e.target.value),
|
|
1275
|
-
error: !!helperTextConfirmPassword,
|
|
1276
|
-
helperText: helperTextConfirmPassword,
|
|
1277
|
-
autoComplete: "new-password",
|
|
1278
|
-
placeholder: t("resetPassword.confirmPasswordPlaceholder"),
|
|
1279
|
-
required: true
|
|
1280
|
-
}
|
|
1281
|
-
)
|
|
1282
|
-
] }),
|
|
1283
|
-
/* @__PURE__ */ jsx6(Button3, { disabled: loading, type: "button", onClick: handleSubmit, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx6(CircularProgress3, { size: 20 }) : t("resetPassword.resetPasswordButton") }),
|
|
1284
|
-
/* @__PURE__ */ jsx6(Box3, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx6(Link3, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
|
|
1285
|
-
] }),
|
|
1286
|
-
/* @__PURE__ */ jsx6(Box3, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx6(Alert3, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) })
|
|
1287
|
-
] });
|
|
1288
1025
|
};
|
|
1289
|
-
var ResetPasswordForm_default = ResetPasswordForm;
|
|
1290
1026
|
|
|
1291
|
-
// src/
|
|
1292
|
-
import {
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
if (
|
|
1305
|
-
|
|
1306
|
-
|
|
1027
|
+
// src/providers/SessionProvider.tsx
|
|
1028
|
+
import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
1029
|
+
var SessionContext = createContext4(void 0);
|
|
1030
|
+
function SessionProvider({ children, options = {}, config: propConfig }) {
|
|
1031
|
+
const sessionHook = useSession(options);
|
|
1032
|
+
const resolvedConfig = useMemo2(() => {
|
|
1033
|
+
let publicApiKey;
|
|
1034
|
+
let env;
|
|
1035
|
+
let appName;
|
|
1036
|
+
let loginActions;
|
|
1037
|
+
let logo;
|
|
1038
|
+
let colors;
|
|
1039
|
+
let configSource = "unknown";
|
|
1040
|
+
if (propConfig?.publicApiKey) {
|
|
1041
|
+
publicApiKey = propConfig.publicApiKey;
|
|
1042
|
+
configSource = "props";
|
|
1307
1043
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
const translateError = (parsedError) => {
|
|
1311
|
-
const possibleKeys = [
|
|
1312
|
-
`errors.auth.${parsedError.code}`,
|
|
1313
|
-
`errors.data.${parsedError.code}`,
|
|
1314
|
-
`errors.system.${parsedError.code}`,
|
|
1315
|
-
`errors.${parsedError.code}`,
|
|
1316
|
-
`checkCode.${parsedError.code.toLowerCase()}`
|
|
1317
|
-
];
|
|
1318
|
-
for (const key of possibleKeys) {
|
|
1319
|
-
const translated = t(key);
|
|
1320
|
-
if (translated !== key) {
|
|
1321
|
-
return translated;
|
|
1322
|
-
}
|
|
1044
|
+
if (propConfig?.env) {
|
|
1045
|
+
env = propConfig.env;
|
|
1323
1046
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
useEffect6(() => {
|
|
1327
|
-
const emailParam = getParam("email");
|
|
1328
|
-
if (emailParam) {
|
|
1329
|
-
setEmail(emailParam);
|
|
1330
|
-
} else {
|
|
1331
|
-
onScreenChange?.("forgotPassword");
|
|
1047
|
+
if (propConfig?.appName) {
|
|
1048
|
+
appName = propConfig.appName;
|
|
1332
1049
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
if (loading || !crudify6) return;
|
|
1336
|
-
setErrors([]);
|
|
1337
|
-
setHelperTextCode(null);
|
|
1338
|
-
if (!code) {
|
|
1339
|
-
setHelperTextCode(t("checkCode.codeRequired"));
|
|
1340
|
-
return;
|
|
1050
|
+
if (propConfig?.loginActions) {
|
|
1051
|
+
loginActions = propConfig.loginActions;
|
|
1341
1052
|
}
|
|
1342
|
-
if (
|
|
1343
|
-
|
|
1344
|
-
|
|
1053
|
+
if (propConfig?.logo) {
|
|
1054
|
+
logo = propConfig.logo;
|
|
1055
|
+
}
|
|
1056
|
+
if (propConfig?.colors) {
|
|
1057
|
+
colors = propConfig.colors;
|
|
1058
|
+
}
|
|
1059
|
+
if (!publicApiKey) {
|
|
1060
|
+
const cookieApiKey = getCookie("publicApiKey");
|
|
1061
|
+
const cookieEnv = getCookie("environment");
|
|
1062
|
+
const cookieAppName = getCookie("appName");
|
|
1063
|
+
const cookieLoginActions = getCookie("loginActions");
|
|
1064
|
+
if (cookieApiKey) {
|
|
1065
|
+
publicApiKey = cookieApiKey;
|
|
1066
|
+
configSource = "cookies";
|
|
1067
|
+
}
|
|
1068
|
+
if (cookieEnv && ["dev", "stg", "prod"].includes(cookieEnv)) {
|
|
1069
|
+
env = cookieEnv;
|
|
1070
|
+
}
|
|
1071
|
+
if (cookieAppName) {
|
|
1072
|
+
appName = cookieAppName;
|
|
1073
|
+
}
|
|
1074
|
+
if (cookieLoginActions) {
|
|
1075
|
+
loginActions = cookieLoginActions.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
console.log("\u{1F50D} SessionProvider - Configuration Detection:");
|
|
1079
|
+
console.log(" - Config source:", configSource);
|
|
1080
|
+
console.log(" - PublicApiKey:", publicApiKey ? publicApiKey.substring(0, 15) + "..." : "undefined");
|
|
1081
|
+
console.log(" - Environment:", env);
|
|
1082
|
+
console.log(" - App name:", appName);
|
|
1083
|
+
console.log(" - Login actions:", loginActions);
|
|
1084
|
+
return {
|
|
1085
|
+
publicApiKey,
|
|
1086
|
+
env,
|
|
1087
|
+
appName,
|
|
1088
|
+
loginActions,
|
|
1089
|
+
logo,
|
|
1090
|
+
colors
|
|
1091
|
+
};
|
|
1092
|
+
}, [propConfig]);
|
|
1093
|
+
const sessionData = useMemo2(() => {
|
|
1094
|
+
if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
|
|
1095
|
+
return null;
|
|
1345
1096
|
}
|
|
1346
|
-
setLoading(true);
|
|
1347
1097
|
try {
|
|
1348
|
-
const
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
setLoading(false);
|
|
1098
|
+
const decoded = decodeJwtSafely(sessionHook.tokens.accessToken);
|
|
1099
|
+
if (decoded && decoded.sub && decoded.email && decoded.subscriber) {
|
|
1100
|
+
const result = {
|
|
1101
|
+
_id: decoded.sub,
|
|
1102
|
+
email: decoded.email,
|
|
1103
|
+
subscriberKey: decoded.subscriber
|
|
1104
|
+
};
|
|
1105
|
+
Object.keys(decoded).forEach((key) => {
|
|
1106
|
+
if (!["sub", "email", "subscriber"].includes(key)) {
|
|
1107
|
+
result[key] = decoded[key];
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
return result;
|
|
1362
1111
|
}
|
|
1363
1112
|
} catch (error) {
|
|
1364
|
-
|
|
1365
|
-
const translatedErrors = parsedErrors.map(translateError);
|
|
1366
|
-
setErrors(translatedErrors);
|
|
1367
|
-
setLoading(false);
|
|
1368
|
-
if (onError) {
|
|
1369
|
-
onError(translatedErrors.join(", "));
|
|
1370
|
-
}
|
|
1113
|
+
console.error("Error decoding JWT token for sessionData:", error);
|
|
1371
1114
|
}
|
|
1115
|
+
return null;
|
|
1116
|
+
}, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
|
|
1117
|
+
const contextValue = {
|
|
1118
|
+
...sessionHook,
|
|
1119
|
+
sessionData,
|
|
1120
|
+
config: resolvedConfig
|
|
1372
1121
|
};
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
const
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
}
|
|
1380
|
-
return
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1122
|
+
return /* @__PURE__ */ jsx4(SessionContext.Provider, { value: contextValue, children });
|
|
1123
|
+
}
|
|
1124
|
+
function useSessionContext() {
|
|
1125
|
+
const context = useContext4(SessionContext);
|
|
1126
|
+
if (context === void 0) {
|
|
1127
|
+
throw new Error("useSessionContext must be used within a SessionProvider");
|
|
1128
|
+
}
|
|
1129
|
+
return context;
|
|
1130
|
+
}
|
|
1131
|
+
function ProtectedRoute({ children, fallback = /* @__PURE__ */ jsx4("div", { children: "Please log in to access this content" }), redirectTo }) {
|
|
1132
|
+
const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
|
|
1133
|
+
if (!isInitialized || isLoading) {
|
|
1134
|
+
return /* @__PURE__ */ jsx4("div", { children: "Loading..." });
|
|
1135
|
+
}
|
|
1136
|
+
if (!isAuthenticated) {
|
|
1137
|
+
if (redirectTo) {
|
|
1138
|
+
redirectTo();
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
return /* @__PURE__ */ jsx4(Fragment, { children: fallback });
|
|
1142
|
+
}
|
|
1143
|
+
return /* @__PURE__ */ jsx4(Fragment, { children });
|
|
1144
|
+
}
|
|
1145
|
+
function SessionDebugInfo() {
|
|
1146
|
+
const session = useSessionContext();
|
|
1147
|
+
if (!session.isInitialized) {
|
|
1148
|
+
return /* @__PURE__ */ jsx4("div", { children: "Session not initialized" });
|
|
1149
|
+
}
|
|
1150
|
+
return /* @__PURE__ */ jsxs(
|
|
1151
|
+
"div",
|
|
1152
|
+
{
|
|
1153
|
+
style: {
|
|
1154
|
+
padding: "10px",
|
|
1155
|
+
margin: "10px",
|
|
1156
|
+
border: "1px solid #ccc",
|
|
1157
|
+
borderRadius: "4px",
|
|
1158
|
+
fontSize: "12px",
|
|
1159
|
+
fontFamily: "monospace"
|
|
1160
|
+
},
|
|
1161
|
+
children: [
|
|
1162
|
+
/* @__PURE__ */ jsx4("h4", { children: "Session Debug Info" }),
|
|
1163
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1164
|
+
/* @__PURE__ */ jsx4("strong", { children: "Authenticated:" }),
|
|
1165
|
+
" ",
|
|
1166
|
+
session.isAuthenticated ? "Yes" : "No"
|
|
1167
|
+
] }),
|
|
1168
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1169
|
+
/* @__PURE__ */ jsx4("strong", { children: "Loading:" }),
|
|
1170
|
+
" ",
|
|
1171
|
+
session.isLoading ? "Yes" : "No"
|
|
1172
|
+
] }),
|
|
1173
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1174
|
+
/* @__PURE__ */ jsx4("strong", { children: "Error:" }),
|
|
1175
|
+
" ",
|
|
1176
|
+
session.error || "None"
|
|
1177
|
+
] }),
|
|
1178
|
+
session.tokens && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1179
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1180
|
+
/* @__PURE__ */ jsx4("strong", { children: "Access Token:" }),
|
|
1181
|
+
" ",
|
|
1182
|
+
session.tokens.accessToken.substring(0, 20),
|
|
1183
|
+
"..."
|
|
1184
|
+
] }),
|
|
1185
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1186
|
+
/* @__PURE__ */ jsx4("strong", { children: "Refresh Token:" }),
|
|
1187
|
+
" ",
|
|
1188
|
+
session.tokens.refreshToken.substring(0, 20),
|
|
1189
|
+
"..."
|
|
1190
|
+
] }),
|
|
1191
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1192
|
+
/* @__PURE__ */ jsx4("strong", { children: "Access Expires In:" }),
|
|
1193
|
+
" ",
|
|
1194
|
+
Math.round(session.expiresIn / 1e3 / 60),
|
|
1195
|
+
" minutes"
|
|
1196
|
+
] }),
|
|
1197
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1198
|
+
/* @__PURE__ */ jsx4("strong", { children: "Refresh Expires In:" }),
|
|
1199
|
+
" ",
|
|
1200
|
+
Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
|
|
1201
|
+
" hours"
|
|
1202
|
+
] }),
|
|
1203
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1204
|
+
/* @__PURE__ */ jsx4("strong", { children: "Expiring Soon:" }),
|
|
1205
|
+
" ",
|
|
1206
|
+
session.isExpiringSoon ? "Yes" : "No"
|
|
1207
|
+
] })
|
|
1208
|
+
] })
|
|
1209
|
+
]
|
|
1210
|
+
}
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// src/components/CrudifyLogin/Forms/LoginForm.tsx
|
|
1215
|
+
import { useEffect as useEffect5, useRef } from "react";
|
|
1216
|
+
import { Typography, TextField, Button, Box, CircularProgress, Alert, Link } from "@mui/material";
|
|
1217
|
+
|
|
1218
|
+
// src/utils/errorHandler.ts
|
|
1219
|
+
var ERROR_CODES = {
|
|
1220
|
+
// Authentication Errors
|
|
1221
|
+
INVALID_CREDENTIALS: "INVALID_CREDENTIALS",
|
|
1222
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
1223
|
+
INVALID_API_KEY: "INVALID_API_KEY",
|
|
1224
|
+
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
1225
|
+
USER_NOT_ACTIVE: "USER_NOT_ACTIVE",
|
|
1226
|
+
NO_PERMISSION: "NO_PERMISSION",
|
|
1227
|
+
// Data Errors
|
|
1228
|
+
ITEM_NOT_FOUND: "ITEM_NOT_FOUND",
|
|
1229
|
+
NOT_FOUND: "NOT_FOUND",
|
|
1230
|
+
IN_USE: "IN_USE",
|
|
1231
|
+
// Validation Errors
|
|
1232
|
+
FIELD_ERROR: "FIELD_ERROR",
|
|
1233
|
+
BAD_REQUEST: "BAD_REQUEST",
|
|
1234
|
+
INVALID_EMAIL: "INVALID_EMAIL",
|
|
1235
|
+
INVALID_CODE: "INVALID_CODE",
|
|
1236
|
+
// System Errors
|
|
1237
|
+
INTERNAL_SERVER_ERROR: "INTERNAL_SERVER_ERROR",
|
|
1238
|
+
DATABASE_CONNECTION_ERROR: "DATABASE_CONNECTION_ERROR",
|
|
1239
|
+
INVALID_CONFIGURATION: "INVALID_CONFIGURATION",
|
|
1240
|
+
UNKNOWN_OPERATION: "UNKNOWN_OPERATION",
|
|
1241
|
+
// Rate Limiting
|
|
1242
|
+
TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS",
|
|
1243
|
+
// Network Errors
|
|
1244
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
1245
|
+
TIMEOUT_ERROR: "TIMEOUT_ERROR"
|
|
1246
|
+
};
|
|
1247
|
+
var ERROR_SEVERITY_MAP = {
|
|
1248
|
+
// Authentication - warning (user can fix)
|
|
1249
|
+
[ERROR_CODES.INVALID_CREDENTIALS]: "warning",
|
|
1250
|
+
[ERROR_CODES.UNAUTHORIZED]: "warning",
|
|
1251
|
+
[ERROR_CODES.INVALID_API_KEY]: "error",
|
|
1252
|
+
[ERROR_CODES.USER_NOT_FOUND]: "warning",
|
|
1253
|
+
[ERROR_CODES.USER_NOT_ACTIVE]: "warning",
|
|
1254
|
+
[ERROR_CODES.NO_PERMISSION]: "warning",
|
|
1255
|
+
// Data - info (might be expected)
|
|
1256
|
+
[ERROR_CODES.ITEM_NOT_FOUND]: "info",
|
|
1257
|
+
[ERROR_CODES.NOT_FOUND]: "info",
|
|
1258
|
+
[ERROR_CODES.IN_USE]: "warning",
|
|
1259
|
+
// Validation - warning (user input issue)
|
|
1260
|
+
[ERROR_CODES.FIELD_ERROR]: "warning",
|
|
1261
|
+
[ERROR_CODES.BAD_REQUEST]: "warning",
|
|
1262
|
+
[ERROR_CODES.INVALID_EMAIL]: "warning",
|
|
1263
|
+
[ERROR_CODES.INVALID_CODE]: "warning",
|
|
1264
|
+
// System - error (server issue)
|
|
1265
|
+
[ERROR_CODES.INTERNAL_SERVER_ERROR]: "error",
|
|
1266
|
+
[ERROR_CODES.DATABASE_CONNECTION_ERROR]: "error",
|
|
1267
|
+
[ERROR_CODES.INVALID_CONFIGURATION]: "error",
|
|
1268
|
+
[ERROR_CODES.UNKNOWN_OPERATION]: "error",
|
|
1269
|
+
// Rate limiting - warning with higher duration
|
|
1270
|
+
[ERROR_CODES.TOO_MANY_REQUESTS]: "warning",
|
|
1271
|
+
// Network - error
|
|
1272
|
+
[ERROR_CODES.NETWORK_ERROR]: "error",
|
|
1273
|
+
[ERROR_CODES.TIMEOUT_ERROR]: "error"
|
|
1274
|
+
};
|
|
1275
|
+
function parseApiError(response) {
|
|
1276
|
+
const errors = [];
|
|
1277
|
+
try {
|
|
1278
|
+
const apiResponse = response;
|
|
1279
|
+
if (apiResponse.data && typeof apiResponse.data === "object") {
|
|
1280
|
+
const responseData = apiResponse.data;
|
|
1281
|
+
if (responseData.response) {
|
|
1282
|
+
const { status, fieldsWarning } = responseData.response;
|
|
1283
|
+
if (fieldsWarning && typeof fieldsWarning === "object") {
|
|
1284
|
+
Object.entries(fieldsWarning).forEach(([field, messages]) => {
|
|
1285
|
+
if (Array.isArray(messages) && messages.length > 0) {
|
|
1286
|
+
errors.push({
|
|
1287
|
+
code: ERROR_CODES.FIELD_ERROR,
|
|
1288
|
+
message: messages[0],
|
|
1289
|
+
severity: "warning",
|
|
1290
|
+
field
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
if (status && typeof status === "string") {
|
|
1296
|
+
const errorCode = status;
|
|
1297
|
+
if (ERROR_SEVERITY_MAP[errorCode]) {
|
|
1298
|
+
errors.push({
|
|
1299
|
+
code: errorCode,
|
|
1300
|
+
message: getErrorMessage(errorCode),
|
|
1301
|
+
severity: ERROR_SEVERITY_MAP[errorCode]
|
|
1302
|
+
});
|
|
1415
1303
|
}
|
|
1416
|
-
)
|
|
1417
|
-
] }),
|
|
1418
|
-
/* @__PURE__ */ jsx7(
|
|
1419
|
-
Button4,
|
|
1420
|
-
{
|
|
1421
|
-
disabled: loading || code.length !== 6,
|
|
1422
|
-
type: "button",
|
|
1423
|
-
onClick: handleSubmit,
|
|
1424
|
-
fullWidth: true,
|
|
1425
|
-
variant: "contained",
|
|
1426
|
-
color: "primary",
|
|
1427
|
-
sx: { mt: 2, mb: 2 },
|
|
1428
|
-
children: loading ? /* @__PURE__ */ jsx7(CircularProgress4, { size: 20 }) : t("checkCode.verifyButton")
|
|
1429
1304
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
if (apiResponse.errors) {
|
|
1308
|
+
if (typeof apiResponse.errors === "string") {
|
|
1309
|
+
errors.push({
|
|
1310
|
+
code: ERROR_CODES.BAD_REQUEST,
|
|
1311
|
+
message: apiResponse.errors,
|
|
1312
|
+
severity: "warning"
|
|
1313
|
+
});
|
|
1314
|
+
} else if (typeof apiResponse.errors === "object") {
|
|
1315
|
+
const errorObj = apiResponse.errors;
|
|
1316
|
+
Object.entries(errorObj).forEach(([field, messages]) => {
|
|
1317
|
+
if (Array.isArray(messages) && messages.length > 0) {
|
|
1318
|
+
if (field === "_error") {
|
|
1319
|
+
messages.forEach((msg) => {
|
|
1320
|
+
const errorCode = typeof msg === "string" && isValidErrorCode(msg) ? msg : ERROR_CODES.BAD_REQUEST;
|
|
1321
|
+
errors.push({
|
|
1322
|
+
code: errorCode,
|
|
1323
|
+
message: typeof msg === "string" ? msg : getErrorMessage(errorCode),
|
|
1324
|
+
severity: ERROR_SEVERITY_MAP[errorCode] || "warning"
|
|
1325
|
+
});
|
|
1326
|
+
});
|
|
1327
|
+
} else if (field === "_graphql") {
|
|
1328
|
+
messages.forEach((msg) => {
|
|
1329
|
+
if (typeof msg === "string") {
|
|
1330
|
+
const errorCode = isValidErrorCode(msg) ? msg : ERROR_CODES.BAD_REQUEST;
|
|
1331
|
+
errors.push({
|
|
1332
|
+
code: errorCode,
|
|
1333
|
+
message: getErrorMessage(errorCode),
|
|
1334
|
+
severity: ERROR_SEVERITY_MAP[errorCode] || "warning"
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
} else {
|
|
1339
|
+
errors.push({
|
|
1340
|
+
code: ERROR_CODES.FIELD_ERROR,
|
|
1341
|
+
message: typeof messages[0] === "string" ? messages[0] : "Validation error",
|
|
1342
|
+
severity: "warning",
|
|
1343
|
+
field
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
if (errors.length === 0 && apiResponse.success === false) {
|
|
1351
|
+
errors.push({
|
|
1352
|
+
code: ERROR_CODES.BAD_REQUEST,
|
|
1353
|
+
message: "Request failed",
|
|
1354
|
+
severity: "warning"
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
} catch (error) {
|
|
1358
|
+
errors.push({
|
|
1359
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1360
|
+
message: "Failed to parse error response",
|
|
1361
|
+
severity: "error",
|
|
1362
|
+
details: { originalError: error }
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
return errors.length > 0 ? errors : [
|
|
1366
|
+
{
|
|
1367
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1368
|
+
message: "Unknown error occurred",
|
|
1369
|
+
severity: "error"
|
|
1370
|
+
}
|
|
1371
|
+
];
|
|
1372
|
+
}
|
|
1373
|
+
function parseTransactionError(response) {
|
|
1374
|
+
try {
|
|
1375
|
+
const transactionResponse = response;
|
|
1376
|
+
if (transactionResponse.data && Array.isArray(transactionResponse.data)) {
|
|
1377
|
+
const errors = [];
|
|
1378
|
+
transactionResponse.data.forEach((item, index) => {
|
|
1379
|
+
if (item.response?.status === "TOO_MANY_REQUESTS") {
|
|
1380
|
+
errors.push({
|
|
1381
|
+
code: ERROR_CODES.TOO_MANY_REQUESTS,
|
|
1382
|
+
message: getErrorMessage(ERROR_CODES.TOO_MANY_REQUESTS),
|
|
1383
|
+
severity: "warning",
|
|
1384
|
+
details: { transactionIndex: index }
|
|
1385
|
+
});
|
|
1386
|
+
} else if (!item.response || item.response.status !== "OK") {
|
|
1387
|
+
errors.push({
|
|
1388
|
+
code: ERROR_CODES.BAD_REQUEST,
|
|
1389
|
+
message: "Transaction failed",
|
|
1390
|
+
severity: "warning",
|
|
1391
|
+
details: { transactionIndex: index, response: item.response }
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
return errors;
|
|
1396
|
+
}
|
|
1397
|
+
return parseApiError(response);
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
return [
|
|
1447
1400
|
{
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
justifyContent: "center",
|
|
1453
|
-
minHeight: "200px",
|
|
1454
|
-
gap: 2
|
|
1455
|
-
},
|
|
1456
|
-
children: [
|
|
1457
|
-
/* @__PURE__ */ jsx8(CircularProgress5, {}),
|
|
1458
|
-
/* @__PURE__ */ jsx8(Typography5, { variant: "body2", color: "text.secondary", children: t("login.initializing") !== "login.initializing" ? t("login.initializing") : "Initializing..." })
|
|
1459
|
-
]
|
|
1401
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1402
|
+
message: "Failed to parse transaction error",
|
|
1403
|
+
severity: "error",
|
|
1404
|
+
details: { originalError: error }
|
|
1460
1405
|
}
|
|
1461
|
-
|
|
1462
|
-
}
|
|
1463
|
-
if (error) {
|
|
1464
|
-
return /* @__PURE__ */ jsx8(Alert5, { severity: "error", sx: { mt: 2 }, children: /* @__PURE__ */ jsxs5(Typography5, { variant: "body2", children: [
|
|
1465
|
-
t("login.initializationError") !== "login.initializationError" ? t("login.initializationError") : "Initialization error",
|
|
1466
|
-
":",
|
|
1467
|
-
" ",
|
|
1468
|
-
error
|
|
1469
|
-
] }) });
|
|
1470
|
-
}
|
|
1471
|
-
if (!isInitialized) {
|
|
1472
|
-
return /* @__PURE__ */ jsx8(Alert5, { severity: "warning", sx: { mt: 2 }, children: /* @__PURE__ */ jsx8(Typography5, { variant: "body2", children: t("login.notInitialized") !== "login.notInitialized" ? t("login.notInitialized") : "System not initialized" }) });
|
|
1473
|
-
}
|
|
1474
|
-
return /* @__PURE__ */ jsx8(Fragment5, { children });
|
|
1475
|
-
};
|
|
1476
|
-
|
|
1477
|
-
// src/providers/SessionProvider.tsx
|
|
1478
|
-
import { createContext as createContext4, useContext as useContext4, useMemo as useMemo2 } from "react";
|
|
1479
|
-
|
|
1480
|
-
// src/hooks/useSession.ts
|
|
1481
|
-
import { useState as useState6, useEffect as useEffect7, useCallback } from "react";
|
|
1482
|
-
|
|
1483
|
-
// src/core/SessionManager.ts
|
|
1484
|
-
import crudify2 from "@nocios/crudify-browser";
|
|
1485
|
-
|
|
1486
|
-
// src/utils/tokenStorage.ts
|
|
1487
|
-
import CryptoJS from "crypto-js";
|
|
1488
|
-
var _TokenStorage = class _TokenStorage {
|
|
1489
|
-
/**
|
|
1490
|
-
* Configurar tipo de almacenamiento
|
|
1491
|
-
*/
|
|
1492
|
-
static setStorageType(type) {
|
|
1493
|
-
_TokenStorage.storageType = type;
|
|
1406
|
+
];
|
|
1494
1407
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1408
|
+
}
|
|
1409
|
+
function isValidErrorCode(code) {
|
|
1410
|
+
return Object.values(ERROR_CODES).includes(code);
|
|
1411
|
+
}
|
|
1412
|
+
function getErrorMessage(code) {
|
|
1413
|
+
const messages = {
|
|
1414
|
+
[ERROR_CODES.INVALID_CREDENTIALS]: "Invalid email or password",
|
|
1415
|
+
[ERROR_CODES.UNAUTHORIZED]: "You are not authorized to perform this action",
|
|
1416
|
+
[ERROR_CODES.INVALID_API_KEY]: "Invalid API key",
|
|
1417
|
+
[ERROR_CODES.USER_NOT_FOUND]: "User not found",
|
|
1418
|
+
[ERROR_CODES.USER_NOT_ACTIVE]: "User account is not active",
|
|
1419
|
+
[ERROR_CODES.NO_PERMISSION]: "You do not have permission to perform this action",
|
|
1420
|
+
[ERROR_CODES.ITEM_NOT_FOUND]: "Item not found",
|
|
1421
|
+
[ERROR_CODES.NOT_FOUND]: "Resource not found",
|
|
1422
|
+
[ERROR_CODES.IN_USE]: "Resource is currently in use",
|
|
1423
|
+
[ERROR_CODES.FIELD_ERROR]: "Validation error",
|
|
1424
|
+
[ERROR_CODES.BAD_REQUEST]: "Invalid request",
|
|
1425
|
+
[ERROR_CODES.INVALID_EMAIL]: "Please enter a valid email address",
|
|
1426
|
+
[ERROR_CODES.INVALID_CODE]: "Invalid or expired code",
|
|
1427
|
+
[ERROR_CODES.INTERNAL_SERVER_ERROR]: "Internal server error",
|
|
1428
|
+
[ERROR_CODES.DATABASE_CONNECTION_ERROR]: "Database connection error",
|
|
1429
|
+
[ERROR_CODES.INVALID_CONFIGURATION]: "Invalid configuration",
|
|
1430
|
+
[ERROR_CODES.UNKNOWN_OPERATION]: "Unknown operation",
|
|
1431
|
+
[ERROR_CODES.TOO_MANY_REQUESTS]: "Too many requests. Please try again later.",
|
|
1432
|
+
[ERROR_CODES.NETWORK_ERROR]: "Network error. Please check your connection.",
|
|
1433
|
+
[ERROR_CODES.TIMEOUT_ERROR]: "Request timed out. Please try again."
|
|
1434
|
+
};
|
|
1435
|
+
return messages[code] || "An unknown error occurred";
|
|
1436
|
+
}
|
|
1437
|
+
function parseJavaScriptError(error) {
|
|
1438
|
+
if (error instanceof Error) {
|
|
1439
|
+
if (error.name === "AbortError") {
|
|
1440
|
+
return {
|
|
1441
|
+
code: ERROR_CODES.TIMEOUT_ERROR,
|
|
1442
|
+
message: "Request was cancelled",
|
|
1443
|
+
severity: "info"
|
|
1444
|
+
};
|
|
1507
1445
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
|
|
1515
|
-
console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
|
|
1516
|
-
return null;
|
|
1446
|
+
if (error.message.includes("NetworkError") || error.message.includes("Failed to fetch")) {
|
|
1447
|
+
return {
|
|
1448
|
+
code: ERROR_CODES.NETWORK_ERROR,
|
|
1449
|
+
message: getErrorMessage(ERROR_CODES.NETWORK_ERROR),
|
|
1450
|
+
severity: "error"
|
|
1451
|
+
};
|
|
1517
1452
|
}
|
|
1518
|
-
return
|
|
1453
|
+
return {
|
|
1454
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1455
|
+
message: error.message || "An unexpected error occurred",
|
|
1456
|
+
severity: "error",
|
|
1457
|
+
details: { originalError: error }
|
|
1458
|
+
};
|
|
1519
1459
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1460
|
+
return {
|
|
1461
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1462
|
+
message: "An unknown error occurred",
|
|
1463
|
+
severity: "error",
|
|
1464
|
+
details: { originalError: error }
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
function handleCrudifyError(error) {
|
|
1468
|
+
if (error instanceof Error) {
|
|
1469
|
+
return [parseJavaScriptError(error)];
|
|
1530
1470
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
try {
|
|
1536
|
-
const bytes = CryptoJS.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
|
|
1537
|
-
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
|
1538
|
-
return decrypted || encryptedData;
|
|
1539
|
-
} catch (error) {
|
|
1540
|
-
console.error("Crudify: Decryption failed", error);
|
|
1541
|
-
return encryptedData;
|
|
1471
|
+
if (typeof error === "object" && error !== null) {
|
|
1472
|
+
const response = error;
|
|
1473
|
+
if (response.data && Array.isArray(response.data)) {
|
|
1474
|
+
return parseTransactionError(error);
|
|
1542
1475
|
}
|
|
1476
|
+
return parseApiError(error);
|
|
1543
1477
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
try {
|
|
1551
|
-
const tokenData = {
|
|
1552
|
-
accessToken: tokens.accessToken,
|
|
1553
|
-
refreshToken: tokens.refreshToken,
|
|
1554
|
-
expiresAt: tokens.expiresAt,
|
|
1555
|
-
refreshExpiresAt: tokens.refreshExpiresAt,
|
|
1556
|
-
savedAt: Date.now()
|
|
1557
|
-
};
|
|
1558
|
-
const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
|
|
1559
|
-
storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
|
|
1560
|
-
console.debug("Crudify: Tokens saved successfully");
|
|
1561
|
-
} catch (error) {
|
|
1562
|
-
console.error("Crudify: Failed to save tokens", error);
|
|
1478
|
+
return [
|
|
1479
|
+
{
|
|
1480
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
1481
|
+
message: "An unknown error occurred",
|
|
1482
|
+
severity: "error",
|
|
1483
|
+
details: { originalError: error }
|
|
1563
1484
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1485
|
+
];
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// src/components/CrudifyLogin/Forms/LoginForm.tsx
|
|
1489
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1490
|
+
var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
|
|
1491
|
+
const { crudify: crudify6 } = useCrudify();
|
|
1492
|
+
const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
|
|
1493
|
+
const { login: sessionLogin } = useSessionContext();
|
|
1494
|
+
const { t } = useTranslation();
|
|
1495
|
+
const usernameInputRef = useRef(null);
|
|
1496
|
+
const getRedirectUrl = () => {
|
|
1497
|
+
if (state.searchParams.redirect) {
|
|
1498
|
+
try {
|
|
1499
|
+
const decodedPath = decodeURIComponent(state.searchParams.redirect);
|
|
1500
|
+
if (decodedPath.startsWith("/") && !decodedPath.startsWith("//")) {
|
|
1501
|
+
return decodedPath;
|
|
1502
|
+
}
|
|
1503
|
+
} catch (error) {
|
|
1580
1504
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1505
|
+
}
|
|
1506
|
+
return redirectUrl || "/";
|
|
1507
|
+
};
|
|
1508
|
+
useEffect5(() => {
|
|
1509
|
+
const timer = setTimeout(() => {
|
|
1510
|
+
if (usernameInputRef.current) usernameInputRef.current.focus();
|
|
1511
|
+
}, 100);
|
|
1512
|
+
return () => clearTimeout(timer);
|
|
1513
|
+
}, []);
|
|
1514
|
+
const translateError = (parsedError) => {
|
|
1515
|
+
const possibleKeys = [
|
|
1516
|
+
`errors.auth.${parsedError.code}`,
|
|
1517
|
+
`errors.data.${parsedError.code}`,
|
|
1518
|
+
`errors.system.${parsedError.code}`,
|
|
1519
|
+
`errors.${parsedError.code}`,
|
|
1520
|
+
`login.${parsedError.code.toLowerCase()}`
|
|
1521
|
+
];
|
|
1522
|
+
for (const key of possibleKeys) {
|
|
1523
|
+
const translated = t(key);
|
|
1524
|
+
if (translated !== key) {
|
|
1525
|
+
return translated;
|
|
1585
1526
|
}
|
|
1586
|
-
return {
|
|
1587
|
-
accessToken: tokenData.accessToken,
|
|
1588
|
-
refreshToken: tokenData.refreshToken,
|
|
1589
|
-
expiresAt: tokenData.expiresAt,
|
|
1590
|
-
refreshExpiresAt: tokenData.refreshExpiresAt
|
|
1591
|
-
};
|
|
1592
|
-
} catch (error) {
|
|
1593
|
-
console.error("Crudify: Failed to retrieve tokens", error);
|
|
1594
|
-
_TokenStorage.clearTokens();
|
|
1595
|
-
return null;
|
|
1596
1527
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
try {
|
|
1605
|
-
storage.removeItem(_TokenStorage.TOKEN_KEY);
|
|
1606
|
-
console.debug("Crudify: Tokens cleared from storage");
|
|
1607
|
-
} catch (error) {
|
|
1608
|
-
console.error("Crudify: Failed to clear tokens", error);
|
|
1528
|
+
return parsedError.message || t("error.unknown");
|
|
1529
|
+
};
|
|
1530
|
+
const handleLogin = async () => {
|
|
1531
|
+
if (state.loading) return;
|
|
1532
|
+
if (!state.formData.username.trim()) {
|
|
1533
|
+
setFieldError("username", t("login.usernameRequired"));
|
|
1534
|
+
return;
|
|
1609
1535
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
* Verificar si hay tokens válidos guardados
|
|
1613
|
-
*/
|
|
1614
|
-
static hasValidTokens() {
|
|
1615
|
-
const tokens = _TokenStorage.getTokens();
|
|
1616
|
-
return tokens !== null;
|
|
1617
|
-
}
|
|
1618
|
-
/**
|
|
1619
|
-
* Obtener información de expiración
|
|
1620
|
-
*/
|
|
1621
|
-
static getExpirationInfo() {
|
|
1622
|
-
const tokens = _TokenStorage.getTokens();
|
|
1623
|
-
if (!tokens) return null;
|
|
1624
|
-
const now = Date.now();
|
|
1625
|
-
return {
|
|
1626
|
-
accessExpired: now >= tokens.expiresAt,
|
|
1627
|
-
refreshExpired: now >= tokens.refreshExpiresAt,
|
|
1628
|
-
accessExpiresIn: Math.max(0, tokens.expiresAt - now),
|
|
1629
|
-
refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
|
|
1630
|
-
};
|
|
1631
|
-
}
|
|
1632
|
-
/**
|
|
1633
|
-
* Actualizar solo el access token (después de refresh)
|
|
1634
|
-
*/
|
|
1635
|
-
static updateAccessToken(newAccessToken, newExpiresAt) {
|
|
1636
|
-
const existingTokens = _TokenStorage.getTokens();
|
|
1637
|
-
if (!existingTokens) {
|
|
1638
|
-
console.warn("Crudify: Cannot update access token, no existing tokens found");
|
|
1536
|
+
if (!state.formData.password.trim()) {
|
|
1537
|
+
setFieldError("password", t("login.passwordRequired"));
|
|
1639
1538
|
return;
|
|
1640
1539
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1540
|
+
clearErrors();
|
|
1541
|
+
setLoading(true);
|
|
1542
|
+
try {
|
|
1543
|
+
const response = await sessionLogin(state.formData.username, state.formData.password);
|
|
1544
|
+
setLoading(false);
|
|
1545
|
+
if (response.success) {
|
|
1546
|
+
console.log("\u{1F510} LoginForm - Login successful via SessionProvider, calling onLoginSuccess");
|
|
1547
|
+
const finalRedirectUrl = getRedirectUrl();
|
|
1548
|
+
if (onLoginSuccess) {
|
|
1549
|
+
onLoginSuccess(response.data, finalRedirectUrl);
|
|
1550
|
+
}
|
|
1551
|
+
} else {
|
|
1552
|
+
handleLoginError(response);
|
|
1553
|
+
}
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
setLoading(false);
|
|
1556
|
+
const parsedErrors = handleCrudifyError(error);
|
|
1557
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
1558
|
+
setFieldError("global", translatedErrors);
|
|
1559
|
+
if (onError) {
|
|
1560
|
+
onError(translatedErrors.join(", "));
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
const handleLoginError = (response) => {
|
|
1565
|
+
const parsedErrors = handleCrudifyError(response);
|
|
1566
|
+
parsedErrors.forEach((error) => {
|
|
1567
|
+
if (error.field) {
|
|
1568
|
+
setFieldError(error.field, translateError(error));
|
|
1569
|
+
} else {
|
|
1570
|
+
const currentGlobalErrors = state.errors.global || [];
|
|
1571
|
+
setFieldError("global", [...currentGlobalErrors, translateError(error)]);
|
|
1572
|
+
}
|
|
1645
1573
|
});
|
|
1646
|
-
}
|
|
1574
|
+
};
|
|
1575
|
+
const handleSubmit = (e) => {
|
|
1576
|
+
e.preventDefault();
|
|
1577
|
+
handleLogin();
|
|
1578
|
+
};
|
|
1579
|
+
const handleKeyDown = (e) => {
|
|
1580
|
+
if (e.key === "Enter" && !state.loading) {
|
|
1581
|
+
e.preventDefault();
|
|
1582
|
+
handleLogin();
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1586
|
+
/* @__PURE__ */ jsxs2(
|
|
1587
|
+
Box,
|
|
1588
|
+
{
|
|
1589
|
+
component: "form",
|
|
1590
|
+
noValidate: true,
|
|
1591
|
+
onSubmit: handleSubmit,
|
|
1592
|
+
onKeyDown: handleKeyDown,
|
|
1593
|
+
sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 },
|
|
1594
|
+
children: [
|
|
1595
|
+
/* @__PURE__ */ jsxs2(Box, { sx: { mb: 1 }, children: [
|
|
1596
|
+
/* @__PURE__ */ jsx5(
|
|
1597
|
+
Typography,
|
|
1598
|
+
{
|
|
1599
|
+
variant: "body2",
|
|
1600
|
+
component: "label",
|
|
1601
|
+
htmlFor: "email",
|
|
1602
|
+
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
1603
|
+
children: t("login.usernameOrEmailLabel")
|
|
1604
|
+
}
|
|
1605
|
+
),
|
|
1606
|
+
/* @__PURE__ */ jsx5(
|
|
1607
|
+
TextField,
|
|
1608
|
+
{
|
|
1609
|
+
fullWidth: true,
|
|
1610
|
+
id: "email",
|
|
1611
|
+
name: "email",
|
|
1612
|
+
type: "email",
|
|
1613
|
+
value: state.formData.username,
|
|
1614
|
+
disabled: state.loading,
|
|
1615
|
+
onChange: (e) => updateFormData({ username: e.target.value }),
|
|
1616
|
+
error: !!state.errors.username,
|
|
1617
|
+
helperText: state.errors.username,
|
|
1618
|
+
autoComplete: "email",
|
|
1619
|
+
placeholder: t("login.usernameOrEmailPlaceholder"),
|
|
1620
|
+
inputRef: usernameInputRef,
|
|
1621
|
+
required: true
|
|
1622
|
+
}
|
|
1623
|
+
)
|
|
1624
|
+
] }),
|
|
1625
|
+
/* @__PURE__ */ jsxs2(Box, { sx: { mb: 1 }, children: [
|
|
1626
|
+
/* @__PURE__ */ jsx5(
|
|
1627
|
+
Typography,
|
|
1628
|
+
{
|
|
1629
|
+
variant: "body2",
|
|
1630
|
+
component: "label",
|
|
1631
|
+
htmlFor: "password",
|
|
1632
|
+
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
1633
|
+
children: t("login.passwordLabel")
|
|
1634
|
+
}
|
|
1635
|
+
),
|
|
1636
|
+
/* @__PURE__ */ jsx5(
|
|
1637
|
+
TextField,
|
|
1638
|
+
{
|
|
1639
|
+
fullWidth: true,
|
|
1640
|
+
id: "password",
|
|
1641
|
+
name: "password",
|
|
1642
|
+
type: "password",
|
|
1643
|
+
value: state.formData.password,
|
|
1644
|
+
disabled: state.loading,
|
|
1645
|
+
onChange: (e) => updateFormData({ password: e.target.value }),
|
|
1646
|
+
error: !!state.errors.password,
|
|
1647
|
+
helperText: state.errors.password,
|
|
1648
|
+
autoComplete: "current-password",
|
|
1649
|
+
placeholder: t("login.passwordPlaceholder"),
|
|
1650
|
+
required: true
|
|
1651
|
+
}
|
|
1652
|
+
)
|
|
1653
|
+
] }),
|
|
1654
|
+
state.config.loginActions?.includes("forgotPassword") && /* @__PURE__ */ jsx5(Box, { sx: { display: "flex", justifyContent: "flex-end", alignItems: "center" }, children: /* @__PURE__ */ jsx5(
|
|
1655
|
+
Link,
|
|
1656
|
+
{
|
|
1657
|
+
sx: { cursor: "pointer" },
|
|
1658
|
+
onClick: () => {
|
|
1659
|
+
onScreenChange?.("forgotPassword", state.searchParams);
|
|
1660
|
+
},
|
|
1661
|
+
variant: "body2",
|
|
1662
|
+
color: "secondary",
|
|
1663
|
+
children: t("login.forgotPasswordLink")
|
|
1664
|
+
}
|
|
1665
|
+
) }),
|
|
1666
|
+
/* @__PURE__ */ jsx5(Button, { disabled: state.loading, type: "submit", fullWidth: true, variant: "contained", color: "primary", sx: { mt: 1, mb: 2 }, children: state.loading ? /* @__PURE__ */ jsx5(CircularProgress, { size: 20 }) : t("login.loginButton") })
|
|
1667
|
+
]
|
|
1668
|
+
}
|
|
1669
|
+
),
|
|
1670
|
+
/* @__PURE__ */ jsx5(Box, { children: state.errors.global && state.errors.global.length > 0 && state.errors.global.map((error, index) => /* @__PURE__ */ jsx5(Alert, { variant: "filled", sx: { mt: 2 }, severity: "error", children: /* @__PURE__ */ jsx5("div", { children: error }) }, index)) }),
|
|
1671
|
+
state.config.loginActions?.includes("createUser") && /* @__PURE__ */ jsxs2(Typography, { variant: "body2", align: "center", sx: { color: "text.secondary", mt: 3 }, children: [
|
|
1672
|
+
t("login.noAccountPrompt"),
|
|
1673
|
+
" ",
|
|
1674
|
+
/* @__PURE__ */ jsx5(
|
|
1675
|
+
Link,
|
|
1676
|
+
{
|
|
1677
|
+
sx: { cursor: "pointer" },
|
|
1678
|
+
onClick: () => {
|
|
1679
|
+
const searchString = Object.keys(state.searchParams).length > 0 ? `?${new URLSearchParams(state.searchParams).toString()}` : "";
|
|
1680
|
+
const signupUrl = `/public/users/create${searchString}`;
|
|
1681
|
+
onExternalNavigate?.(signupUrl);
|
|
1682
|
+
},
|
|
1683
|
+
fontWeight: "medium",
|
|
1684
|
+
color: "secondary",
|
|
1685
|
+
children: t("login.signUpLink")
|
|
1686
|
+
}
|
|
1687
|
+
)
|
|
1688
|
+
] })
|
|
1689
|
+
] });
|
|
1647
1690
|
};
|
|
1648
|
-
|
|
1649
|
-
_TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
|
|
1650
|
-
_TokenStorage.storageType = "localStorage";
|
|
1651
|
-
var TokenStorage = _TokenStorage;
|
|
1691
|
+
var LoginForm_default = LoginForm;
|
|
1652
1692
|
|
|
1653
|
-
// src/
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1693
|
+
// src/components/CrudifyLogin/Forms/ForgotPasswordForm.tsx
|
|
1694
|
+
import { useState as useState4 } from "react";
|
|
1695
|
+
import { Typography as Typography2, TextField as TextField2, Button as Button2, Box as Box2, CircularProgress as CircularProgress2, Alert as Alert2, Link as Link2 } from "@mui/material";
|
|
1696
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1697
|
+
var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
1698
|
+
const { crudify: crudify6 } = useCrudify();
|
|
1699
|
+
const [email, setEmail] = useState4("");
|
|
1700
|
+
const [loading, setLoading] = useState4(false);
|
|
1701
|
+
const [errors, setErrors] = useState4([]);
|
|
1702
|
+
const [helperTextEmail, setHelperTextEmail] = useState4(null);
|
|
1703
|
+
const [emailSent, setEmailSent] = useState4(false);
|
|
1704
|
+
const [codeAlreadyExists, setCodeAlreadyExists] = useState4(false);
|
|
1705
|
+
const { t } = useTranslation();
|
|
1706
|
+
const translateError = (parsedError) => {
|
|
1707
|
+
const possibleKeys = [
|
|
1708
|
+
`errors.auth.${parsedError.code}`,
|
|
1709
|
+
`errors.data.${parsedError.code}`,
|
|
1710
|
+
`errors.system.${parsedError.code}`,
|
|
1711
|
+
`errors.${parsedError.code}`,
|
|
1712
|
+
`forgotPassword.${parsedError.code.toLowerCase()}`
|
|
1713
|
+
];
|
|
1714
|
+
for (const key of possibleKeys) {
|
|
1715
|
+
const translated = t(key);
|
|
1716
|
+
if (translated !== key) {
|
|
1717
|
+
return translated;
|
|
1718
|
+
}
|
|
1672
1719
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
if (
|
|
1720
|
+
return parsedError.message || t("error.unknown");
|
|
1721
|
+
};
|
|
1722
|
+
const validateEmail = (email2) => {
|
|
1723
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1724
|
+
return emailRegex.test(email2);
|
|
1725
|
+
};
|
|
1726
|
+
const handleSubmit = async () => {
|
|
1727
|
+
if (loading || !crudify6) return;
|
|
1728
|
+
setErrors([]);
|
|
1729
|
+
setHelperTextEmail(null);
|
|
1730
|
+
if (!email) {
|
|
1731
|
+
setHelperTextEmail(t("forgotPassword.emailRequired"));
|
|
1732
|
+
return;
|
|
1681
1733
|
}
|
|
1682
|
-
if (
|
|
1683
|
-
|
|
1734
|
+
if (!validateEmail(email)) {
|
|
1735
|
+
setHelperTextEmail(t("forgotPassword.invalidEmail"));
|
|
1736
|
+
return;
|
|
1684
1737
|
}
|
|
1685
|
-
|
|
1686
|
-
this.log("SessionManager initialized successfully");
|
|
1687
|
-
}
|
|
1688
|
-
/**
|
|
1689
|
-
* Login con persistencia automática
|
|
1690
|
-
*/
|
|
1691
|
-
async login(email, password) {
|
|
1738
|
+
setLoading(true);
|
|
1692
1739
|
try {
|
|
1693
|
-
|
|
1694
|
-
const response = await
|
|
1695
|
-
if (
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
}
|
|
1740
|
+
const data = [{ operation: "requestPasswordReset", data: { email } }];
|
|
1741
|
+
const response = await crudify6.transaction(data);
|
|
1742
|
+
if (response.success) {
|
|
1743
|
+
if (response.data && response.data.existingCodeValid) {
|
|
1744
|
+
setCodeAlreadyExists(true);
|
|
1745
|
+
} else {
|
|
1746
|
+
setEmailSent(true);
|
|
1747
|
+
}
|
|
1748
|
+
} else {
|
|
1749
|
+
const parsedErrors = handleCrudifyError(response);
|
|
1750
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
1751
|
+
setErrors(translatedErrors);
|
|
1701
1752
|
}
|
|
1702
|
-
const tokens = {
|
|
1703
|
-
accessToken: response.data.token,
|
|
1704
|
-
refreshToken: response.data.refreshToken,
|
|
1705
|
-
expiresAt: response.data.expiresAt,
|
|
1706
|
-
refreshExpiresAt: response.data.refreshExpiresAt
|
|
1707
|
-
};
|
|
1708
|
-
TokenStorage.saveTokens(tokens);
|
|
1709
|
-
this.log("Login successful, tokens saved");
|
|
1710
|
-
this.config.onLoginSuccess?.(tokens);
|
|
1711
|
-
return {
|
|
1712
|
-
success: true,
|
|
1713
|
-
tokens
|
|
1714
|
-
};
|
|
1715
1753
|
} catch (error) {
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1754
|
+
const parsedErrors = handleCrudifyError(error);
|
|
1755
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
1756
|
+
setErrors(translatedErrors);
|
|
1757
|
+
if (onError) {
|
|
1758
|
+
onError(translatedErrors.join(", "));
|
|
1759
|
+
}
|
|
1760
|
+
} finally {
|
|
1761
|
+
setLoading(false);
|
|
1721
1762
|
}
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
TokenStorage.clearTokens();
|
|
1731
|
-
this.log("Logout successful");
|
|
1732
|
-
this.config.onLogout?.();
|
|
1733
|
-
} catch (error) {
|
|
1734
|
-
this.log("Logout error:", error);
|
|
1735
|
-
TokenStorage.clearTokens();
|
|
1763
|
+
};
|
|
1764
|
+
const handleBack = () => {
|
|
1765
|
+
onScreenChange?.("login");
|
|
1766
|
+
};
|
|
1767
|
+
const handleGoToCheckCode = () => {
|
|
1768
|
+
if (emailSent || codeAlreadyExists) {
|
|
1769
|
+
onScreenChange?.("checkCode", { email });
|
|
1770
|
+
return;
|
|
1736
1771
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
*/
|
|
1741
|
-
async restoreSession() {
|
|
1742
|
-
try {
|
|
1743
|
-
this.log("Attempting to restore session...");
|
|
1744
|
-
const savedTokens = TokenStorage.getTokens();
|
|
1745
|
-
if (!savedTokens) {
|
|
1746
|
-
this.log("No valid tokens found in storage");
|
|
1747
|
-
return false;
|
|
1748
|
-
}
|
|
1749
|
-
crudify2.setTokens({
|
|
1750
|
-
accessToken: savedTokens.accessToken,
|
|
1751
|
-
refreshToken: savedTokens.refreshToken,
|
|
1752
|
-
expiresAt: savedTokens.expiresAt,
|
|
1753
|
-
refreshExpiresAt: savedTokens.refreshExpiresAt
|
|
1754
|
-
});
|
|
1755
|
-
this.log("Session restored successfully");
|
|
1756
|
-
this.config.onSessionRestored?.(savedTokens);
|
|
1757
|
-
return true;
|
|
1758
|
-
} catch (error) {
|
|
1759
|
-
this.log("Session restore error:", error);
|
|
1760
|
-
TokenStorage.clearTokens();
|
|
1761
|
-
return false;
|
|
1772
|
+
if (!email) {
|
|
1773
|
+
setHelperTextEmail(t("forgotPassword.emailRequired"));
|
|
1774
|
+
return;
|
|
1762
1775
|
}
|
|
1776
|
+
if (!validateEmail(email)) {
|
|
1777
|
+
setHelperTextEmail(t("forgotPassword.invalidEmail"));
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
onScreenChange?.("checkCode", { email });
|
|
1781
|
+
};
|
|
1782
|
+
if (emailSent || codeAlreadyExists) {
|
|
1783
|
+
return /* @__PURE__ */ jsx6(Fragment3, { children: /* @__PURE__ */ jsxs3(Box2, { sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2, textAlign: "center" }, children: [
|
|
1784
|
+
/* @__PURE__ */ jsxs3(Box2, { sx: { mb: 2 }, children: [
|
|
1785
|
+
/* @__PURE__ */ jsx6(Typography2, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: codeAlreadyExists ? t("forgotPassword.codeAlreadyExistsMessage") : t("forgotPassword.emailSentMessage") }),
|
|
1786
|
+
/* @__PURE__ */ jsx6(Typography2, { variant: "body2", sx: { color: codeAlreadyExists ? "success.main" : "grey.600" }, children: codeAlreadyExists ? t("forgotPassword.checkEmailInstructions") : t("forgotPassword.checkEmailInstructions") })
|
|
1787
|
+
] }),
|
|
1788
|
+
/* @__PURE__ */ jsx6(Button2, { type: "button", onClick: handleGoToCheckCode, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: t("forgotPassword.enterCodeLink") }),
|
|
1789
|
+
/* @__PURE__ */ jsx6(Box2, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx6(Link2, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
|
|
1790
|
+
] }) });
|
|
1763
1791
|
}
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1792
|
+
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1793
|
+
/* @__PURE__ */ jsxs3(Box2, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
1794
|
+
/* @__PURE__ */ jsxs3(Box2, { sx: { mb: 2 }, children: [
|
|
1795
|
+
/* @__PURE__ */ jsx6(Typography2, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("forgotPassword.title") }),
|
|
1796
|
+
/* @__PURE__ */ jsx6(Typography2, { variant: "body2", sx: { color: "grey.600" }, children: t("forgotPassword.instructions") })
|
|
1797
|
+
] }),
|
|
1798
|
+
/* @__PURE__ */ jsxs3(Box2, { sx: { mb: 1 }, children: [
|
|
1799
|
+
/* @__PURE__ */ jsx6(
|
|
1800
|
+
Typography2,
|
|
1801
|
+
{
|
|
1802
|
+
variant: "body2",
|
|
1803
|
+
component: "label",
|
|
1804
|
+
htmlFor: "email",
|
|
1805
|
+
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
1806
|
+
children: t("forgotPassword.emailLabel")
|
|
1807
|
+
}
|
|
1808
|
+
),
|
|
1809
|
+
/* @__PURE__ */ jsx6(
|
|
1810
|
+
TextField2,
|
|
1811
|
+
{
|
|
1812
|
+
fullWidth: true,
|
|
1813
|
+
id: "email",
|
|
1814
|
+
name: "email",
|
|
1815
|
+
type: "email",
|
|
1816
|
+
value: email,
|
|
1817
|
+
disabled: loading,
|
|
1818
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1819
|
+
error: !!helperTextEmail,
|
|
1820
|
+
helperText: helperTextEmail,
|
|
1821
|
+
autoComplete: "email",
|
|
1822
|
+
placeholder: t("forgotPassword.emailPlaceholder"),
|
|
1823
|
+
required: true
|
|
1824
|
+
}
|
|
1825
|
+
)
|
|
1826
|
+
] }),
|
|
1827
|
+
/* @__PURE__ */ jsx6(Button2, { disabled: loading, type: "button", onClick: handleSubmit, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx6(CircularProgress2, { size: 20 }) : t("forgotPassword.sendCodeButton") }),
|
|
1828
|
+
/* @__PURE__ */ jsxs3(Box2, { sx: { display: "flex", justifyContent: "center", alignItems: "center", gap: 2 }, children: [
|
|
1829
|
+
/* @__PURE__ */ jsx6(Link2, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }),
|
|
1830
|
+
/* @__PURE__ */ jsx6(Typography2, { variant: "body2", sx: { color: "grey.400" }, children: "\u2022" }),
|
|
1831
|
+
/* @__PURE__ */ jsx6(Link2, { sx: { cursor: "pointer" }, onClick: handleGoToCheckCode, variant: "body2", color: "secondary", children: t("login.alreadyHaveCodeLink") })
|
|
1832
|
+
] })
|
|
1833
|
+
] }),
|
|
1834
|
+
/* @__PURE__ */ jsx6(Box2, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx6(Alert2, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) })
|
|
1835
|
+
] });
|
|
1836
|
+
};
|
|
1837
|
+
var ForgotPasswordForm_default = ForgotPasswordForm;
|
|
1838
|
+
|
|
1839
|
+
// src/components/CrudifyLogin/Forms/ResetPasswordForm.tsx
|
|
1840
|
+
import { useState as useState5, useEffect as useEffect6 } from "react";
|
|
1841
|
+
import { Typography as Typography3, TextField as TextField3, Button as Button3, Box as Box3, CircularProgress as CircularProgress3, Alert as Alert3, Link as Link3 } from "@mui/material";
|
|
1842
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1843
|
+
var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess }) => {
|
|
1844
|
+
const { crudify: crudify6 } = useCrudify();
|
|
1845
|
+
const [newPassword, setNewPassword] = useState5("");
|
|
1846
|
+
const [confirmPassword, setConfirmPassword] = useState5("");
|
|
1847
|
+
const [loading, setLoading] = useState5(false);
|
|
1848
|
+
const [errors, setErrors] = useState5([]);
|
|
1849
|
+
const [helperTextNewPassword, setHelperTextNewPassword] = useState5(null);
|
|
1850
|
+
const [helperTextConfirmPassword, setHelperTextConfirmPassword] = useState5(null);
|
|
1851
|
+
const [email, setEmail] = useState5("");
|
|
1852
|
+
const [code, setCode] = useState5("");
|
|
1853
|
+
const [fromCodeVerification, setFromCodeVerification] = useState5(false);
|
|
1854
|
+
const [validatingCode, setValidatingCode] = useState5(true);
|
|
1855
|
+
const [codeValidated, setCodeValidated] = useState5(false);
|
|
1856
|
+
const [pendingValidation, setPendingValidation] = useState5(null);
|
|
1857
|
+
const [isValidating, setIsValidating] = useState5(false);
|
|
1858
|
+
const { t } = useTranslation();
|
|
1859
|
+
const translateError = (parsedError) => {
|
|
1860
|
+
const possibleKeys = [
|
|
1861
|
+
`errors.auth.${parsedError.code}`,
|
|
1862
|
+
`errors.data.${parsedError.code}`,
|
|
1863
|
+
`errors.system.${parsedError.code}`,
|
|
1864
|
+
`errors.${parsedError.code}`,
|
|
1865
|
+
`resetPassword.${parsedError.code.toLowerCase()}`
|
|
1866
|
+
];
|
|
1867
|
+
for (const key of possibleKeys) {
|
|
1868
|
+
const translated = t(key);
|
|
1869
|
+
if (translated !== key) {
|
|
1870
|
+
return translated;
|
|
1795
1871
|
}
|
|
1796
|
-
const newTokens = {
|
|
1797
|
-
accessToken: response.data.token,
|
|
1798
|
-
refreshToken: response.data.refreshToken,
|
|
1799
|
-
expiresAt: response.data.expiresAt,
|
|
1800
|
-
refreshExpiresAt: response.data.refreshExpiresAt
|
|
1801
|
-
};
|
|
1802
|
-
TokenStorage.saveTokens(newTokens);
|
|
1803
|
-
this.log("Tokens refreshed and saved successfully");
|
|
1804
|
-
return true;
|
|
1805
|
-
} catch (error) {
|
|
1806
|
-
this.log("Token refresh error:", error);
|
|
1807
|
-
TokenStorage.clearTokens();
|
|
1808
|
-
this.config.onSessionExpired?.();
|
|
1809
|
-
return false;
|
|
1810
1872
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1873
|
+
return parsedError.message || t("error.unknown");
|
|
1874
|
+
};
|
|
1875
|
+
const getParam = (key) => {
|
|
1876
|
+
if (!searchParams) return null;
|
|
1877
|
+
if (searchParams instanceof URLSearchParams) {
|
|
1878
|
+
return searchParams.get(key);
|
|
1879
|
+
}
|
|
1880
|
+
return searchParams[key] || null;
|
|
1881
|
+
};
|
|
1882
|
+
useEffect6(() => {
|
|
1883
|
+
if (!searchParams) {
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
if (searchParams) {
|
|
1887
|
+
const fromCodeVerificationParam = getParam("fromCodeVerification");
|
|
1888
|
+
const emailParam = getParam("email");
|
|
1889
|
+
const codeParam = getParam("code");
|
|
1890
|
+
if (fromCodeVerificationParam === "true" && emailParam && codeParam) {
|
|
1891
|
+
setEmail(emailParam);
|
|
1892
|
+
setCode(codeParam);
|
|
1893
|
+
setFromCodeVerification(true);
|
|
1894
|
+
setCodeValidated(true);
|
|
1895
|
+
setValidatingCode(false);
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
const linkParam = getParam("link");
|
|
1899
|
+
if (linkParam) {
|
|
1900
|
+
try {
|
|
1901
|
+
const decodedLink = decodeURIComponent(linkParam);
|
|
1902
|
+
const [linkCode, linkEmail] = decodedLink.split("/");
|
|
1903
|
+
if (linkCode && linkEmail && linkCode.length === 6) {
|
|
1904
|
+
setCode(linkCode);
|
|
1905
|
+
setEmail(linkEmail);
|
|
1906
|
+
setFromCodeVerification(false);
|
|
1907
|
+
setPendingValidation({ email: linkEmail, code: linkCode });
|
|
1908
|
+
return;
|
|
1827
1909
|
}
|
|
1910
|
+
} catch (error) {
|
|
1828
1911
|
}
|
|
1829
1912
|
}
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
clearSession() {
|
|
1838
|
-
TokenStorage.clearTokens();
|
|
1839
|
-
crudify2.logout();
|
|
1840
|
-
this.log("Session cleared completely");
|
|
1841
|
-
}
|
|
1842
|
-
// Métodos privados
|
|
1843
|
-
log(message, ...args) {
|
|
1844
|
-
if (this.config.enableLogging) {
|
|
1845
|
-
console.log(`[SessionManager] ${message}`, ...args);
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
formatError(errors) {
|
|
1849
|
-
if (!errors) return "Unknown error";
|
|
1850
|
-
if (typeof errors === "string") return errors;
|
|
1851
|
-
if (typeof errors === "object") {
|
|
1852
|
-
const errorMessages = Object.values(errors).flat();
|
|
1853
|
-
return errorMessages.join(", ");
|
|
1913
|
+
if (emailParam && codeParam) {
|
|
1914
|
+
setEmail(emailParam);
|
|
1915
|
+
setCode(codeParam);
|
|
1916
|
+
setFromCodeVerification(false);
|
|
1917
|
+
setPendingValidation({ email: emailParam, code: codeParam });
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1854
1920
|
}
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
...prev,
|
|
1896
|
-
isAuthenticated: true,
|
|
1897
|
-
tokens,
|
|
1898
|
-
error: null
|
|
1899
|
-
}));
|
|
1900
|
-
},
|
|
1901
|
-
onLogout: () => {
|
|
1902
|
-
setState((prev) => ({
|
|
1903
|
-
...prev,
|
|
1904
|
-
isAuthenticated: false,
|
|
1905
|
-
tokens: null,
|
|
1906
|
-
error: null
|
|
1907
|
-
}));
|
|
1921
|
+
setErrors([t("resetPassword.invalidCode")]);
|
|
1922
|
+
setValidatingCode(false);
|
|
1923
|
+
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
1924
|
+
}, [searchParams, crudify6, t, onScreenChange]);
|
|
1925
|
+
useEffect6(() => {
|
|
1926
|
+
if (crudify6 && pendingValidation && !isValidating) {
|
|
1927
|
+
setIsValidating(true);
|
|
1928
|
+
const validateCode = async (emailToValidate, codeToValidate) => {
|
|
1929
|
+
try {
|
|
1930
|
+
const data = [
|
|
1931
|
+
{
|
|
1932
|
+
operation: "validatePasswordResetCode",
|
|
1933
|
+
data: { email: emailToValidate, codePassword: codeToValidate }
|
|
1934
|
+
}
|
|
1935
|
+
];
|
|
1936
|
+
const response = await crudify6.transaction(data);
|
|
1937
|
+
if (response.data && Array.isArray(response.data)) {
|
|
1938
|
+
const validationResult = response.data[0];
|
|
1939
|
+
if (validationResult && validationResult.response && validationResult.response.status === "OK") {
|
|
1940
|
+
setCodeValidated(true);
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
if (response.success) {
|
|
1945
|
+
setCodeValidated(true);
|
|
1946
|
+
} else {
|
|
1947
|
+
const parsedErrors = handleCrudifyError(response);
|
|
1948
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
1949
|
+
setErrors(translatedErrors);
|
|
1950
|
+
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
1951
|
+
}
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
const parsedErrors = handleCrudifyError(error);
|
|
1954
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
1955
|
+
setErrors(translatedErrors);
|
|
1956
|
+
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
1957
|
+
} finally {
|
|
1958
|
+
setValidatingCode(false);
|
|
1959
|
+
setPendingValidation(null);
|
|
1960
|
+
setIsValidating(false);
|
|
1908
1961
|
}
|
|
1909
1962
|
};
|
|
1910
|
-
|
|
1911
|
-
sessionManager.setupResponseInterceptor();
|
|
1912
|
-
const isAuth = sessionManager.isAuthenticated();
|
|
1913
|
-
const tokenInfo = sessionManager.getTokenInfo();
|
|
1914
|
-
setState((prev) => ({
|
|
1915
|
-
...prev,
|
|
1916
|
-
isAuthenticated: isAuth,
|
|
1917
|
-
isInitialized: true,
|
|
1918
|
-
isLoading: false,
|
|
1919
|
-
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
1920
|
-
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
1921
|
-
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
1922
|
-
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
1923
|
-
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
1924
|
-
} : null
|
|
1925
|
-
}));
|
|
1926
|
-
} catch (error) {
|
|
1927
|
-
setState((prev) => ({
|
|
1928
|
-
...prev,
|
|
1929
|
-
isLoading: false,
|
|
1930
|
-
isInitialized: true,
|
|
1931
|
-
error: error instanceof Error ? error.message : "Initialization failed"
|
|
1932
|
-
}));
|
|
1963
|
+
validateCode(pendingValidation.email, pendingValidation.code);
|
|
1933
1964
|
}
|
|
1934
|
-
}, [
|
|
1935
|
-
const
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
const result = await sessionManager.login(email, password);
|
|
1939
|
-
if (result.success && result.tokens) {
|
|
1940
|
-
setState((prev) => ({
|
|
1941
|
-
...prev,
|
|
1942
|
-
isAuthenticated: true,
|
|
1943
|
-
tokens: result.tokens,
|
|
1944
|
-
isLoading: false,
|
|
1945
|
-
error: null
|
|
1946
|
-
}));
|
|
1947
|
-
} else {
|
|
1948
|
-
setState((prev) => ({
|
|
1949
|
-
...prev,
|
|
1950
|
-
isAuthenticated: false,
|
|
1951
|
-
tokens: null,
|
|
1952
|
-
isLoading: false,
|
|
1953
|
-
error: result.error || "Login failed"
|
|
1954
|
-
}));
|
|
1955
|
-
}
|
|
1956
|
-
return result;
|
|
1957
|
-
} catch (error) {
|
|
1958
|
-
const errorMsg = error instanceof Error ? error.message : "Login failed";
|
|
1959
|
-
setState((prev) => ({
|
|
1960
|
-
...prev,
|
|
1961
|
-
isAuthenticated: false,
|
|
1962
|
-
tokens: null,
|
|
1963
|
-
isLoading: false,
|
|
1964
|
-
error: errorMsg
|
|
1965
|
-
}));
|
|
1966
|
-
return {
|
|
1967
|
-
success: false,
|
|
1968
|
-
error: errorMsg
|
|
1969
|
-
};
|
|
1965
|
+
}, [crudify6, pendingValidation, t, onScreenChange]);
|
|
1966
|
+
const validatePassword = (password) => {
|
|
1967
|
+
if (password.length < 8) {
|
|
1968
|
+
return t("resetPassword.passwordTooShort");
|
|
1970
1969
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
isLoading: false,
|
|
1989
|
-
error: error instanceof Error ? error.message : "Logout error"
|
|
1990
|
-
}));
|
|
1970
|
+
return null;
|
|
1971
|
+
};
|
|
1972
|
+
const handleSubmit = async () => {
|
|
1973
|
+
if (loading || !crudify6) return;
|
|
1974
|
+
setErrors([]);
|
|
1975
|
+
setHelperTextNewPassword(null);
|
|
1976
|
+
setHelperTextConfirmPassword(null);
|
|
1977
|
+
let hasErrors = false;
|
|
1978
|
+
if (!newPassword) {
|
|
1979
|
+
setHelperTextNewPassword(t("resetPassword.newPasswordRequired"));
|
|
1980
|
+
hasErrors = true;
|
|
1981
|
+
} else {
|
|
1982
|
+
const passwordError = validatePassword(newPassword);
|
|
1983
|
+
if (passwordError) {
|
|
1984
|
+
setHelperTextNewPassword(passwordError);
|
|
1985
|
+
hasErrors = true;
|
|
1986
|
+
}
|
|
1991
1987
|
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1988
|
+
if (!confirmPassword) {
|
|
1989
|
+
setHelperTextConfirmPassword(t("resetPassword.confirmPasswordRequired"));
|
|
1990
|
+
hasErrors = true;
|
|
1991
|
+
} else if (newPassword !== confirmPassword) {
|
|
1992
|
+
setHelperTextConfirmPassword(t("resetPassword.passwordsDoNotMatch"));
|
|
1993
|
+
hasErrors = true;
|
|
1994
|
+
}
|
|
1995
|
+
if (hasErrors) return;
|
|
1996
|
+
setLoading(true);
|
|
1994
1997
|
try {
|
|
1995
|
-
const
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
}));
|
|
1998
|
+
const data = [
|
|
1999
|
+
{
|
|
2000
|
+
operation: "validateAndResetPassword",
|
|
2001
|
+
data: { email, codePassword: code, newPassword }
|
|
2002
|
+
}
|
|
2003
|
+
];
|
|
2004
|
+
const response = await crudify6.transaction(data);
|
|
2005
|
+
if (response.success) {
|
|
2006
|
+
setErrors([]);
|
|
2007
|
+
setTimeout(() => {
|
|
2008
|
+
onResetSuccess?.();
|
|
2009
|
+
}, 1e3);
|
|
2008
2010
|
} else {
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
tokens: null,
|
|
2013
|
-
error: "Token refresh failed"
|
|
2014
|
-
}));
|
|
2011
|
+
const parsedErrors = handleCrudifyError(response);
|
|
2012
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
2013
|
+
setErrors(translatedErrors);
|
|
2015
2014
|
}
|
|
2016
|
-
return success;
|
|
2017
2015
|
} catch (error) {
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2024
|
-
return false;
|
|
2016
|
+
const parsedErrors = handleCrudifyError(error);
|
|
2017
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
2018
|
+
setErrors(translatedErrors);
|
|
2019
|
+
if (onError) {
|
|
2020
|
+
onError(translatedErrors.join(", "));
|
|
2021
|
+
}
|
|
2025
2022
|
}
|
|
2026
|
-
|
|
2027
|
-
const clearError = useCallback(() => {
|
|
2028
|
-
setState((prev) => ({ ...prev, error: null }));
|
|
2029
|
-
}, []);
|
|
2030
|
-
const getTokenInfo = useCallback(() => {
|
|
2031
|
-
return sessionManager.getTokenInfo();
|
|
2032
|
-
}, [sessionManager]);
|
|
2033
|
-
useEffect7(() => {
|
|
2034
|
-
initialize();
|
|
2035
|
-
}, [initialize]);
|
|
2036
|
-
return {
|
|
2037
|
-
// Estado
|
|
2038
|
-
...state,
|
|
2039
|
-
// Acciones
|
|
2040
|
-
login,
|
|
2041
|
-
logout,
|
|
2042
|
-
refreshTokens,
|
|
2043
|
-
clearError,
|
|
2044
|
-
getTokenInfo,
|
|
2045
|
-
// Utilidades
|
|
2046
|
-
isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
|
|
2047
|
-
// 5 minutos
|
|
2048
|
-
expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
|
|
2049
|
-
refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
|
|
2023
|
+
setLoading(false);
|
|
2050
2024
|
};
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
const parts = token.split(".");
|
|
2057
|
-
if (parts.length !== 3) {
|
|
2058
|
-
console.warn("Invalid JWT format: token must have 3 parts");
|
|
2059
|
-
return null;
|
|
2060
|
-
}
|
|
2061
|
-
const payload = parts[1];
|
|
2062
|
-
const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
2063
|
-
const decodedPayload = JSON.parse(atob(paddedPayload));
|
|
2064
|
-
return decodedPayload;
|
|
2065
|
-
} catch (error) {
|
|
2066
|
-
console.warn("Failed to decode JWT token:", error);
|
|
2067
|
-
return null;
|
|
2068
|
-
}
|
|
2069
|
-
};
|
|
2070
|
-
var getCurrentUserEmail = () => {
|
|
2071
|
-
try {
|
|
2072
|
-
let token = null;
|
|
2073
|
-
token = sessionStorage.getItem("authToken");
|
|
2074
|
-
console.log("\u{1F50D} getCurrentUserEmail - authToken:", token ? `${token.substring(0, 20)}...` : null);
|
|
2075
|
-
if (!token) {
|
|
2076
|
-
token = sessionStorage.getItem("token");
|
|
2077
|
-
console.log("\u{1F50D} getCurrentUserEmail - token:", token ? `${token.substring(0, 20)}...` : null);
|
|
2078
|
-
}
|
|
2079
|
-
if (!token) {
|
|
2080
|
-
token = localStorage.getItem("authToken") || localStorage.getItem("token");
|
|
2081
|
-
console.log("\u{1F50D} getCurrentUserEmail - localStorage:", token ? `${token.substring(0, 20)}...` : null);
|
|
2082
|
-
}
|
|
2083
|
-
if (!token) {
|
|
2084
|
-
console.warn("\u{1F50D} getCurrentUserEmail - No token found in any storage");
|
|
2085
|
-
return null;
|
|
2086
|
-
}
|
|
2087
|
-
const payload = decodeJwtSafely(token);
|
|
2088
|
-
if (!payload) {
|
|
2089
|
-
console.warn("\u{1F50D} getCurrentUserEmail - Failed to decode token");
|
|
2090
|
-
return null;
|
|
2025
|
+
const handleBack = () => {
|
|
2026
|
+
if (fromCodeVerification) {
|
|
2027
|
+
onScreenChange?.("checkCode", { email });
|
|
2028
|
+
} else {
|
|
2029
|
+
onScreenChange?.("forgotPassword");
|
|
2091
2030
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
return
|
|
2095
|
-
} catch (error) {
|
|
2096
|
-
console.warn("Failed to get current user email:", error);
|
|
2097
|
-
return null;
|
|
2031
|
+
};
|
|
2032
|
+
if (validatingCode) {
|
|
2033
|
+
return /* @__PURE__ */ jsx7(Box3, { sx: { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "300px" }, children: /* @__PURE__ */ jsx7(CircularProgress3, {}) });
|
|
2098
2034
|
}
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
try {
|
|
2102
|
-
const payload = decodeJwtSafely(token);
|
|
2103
|
-
if (!payload || !payload.exp) return true;
|
|
2104
|
-
const currentTime = Math.floor(Date.now() / 1e3);
|
|
2105
|
-
return payload.exp < currentTime;
|
|
2106
|
-
} catch {
|
|
2107
|
-
return true;
|
|
2035
|
+
if (!codeValidated) {
|
|
2036
|
+
return /* @__PURE__ */ jsx7(Box3, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx7(Alert3, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) });
|
|
2108
2037
|
}
|
|
2038
|
+
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
2039
|
+
/* @__PURE__ */ jsxs4(Box3, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
2040
|
+
/* @__PURE__ */ jsxs4(Box3, { sx: { mb: 2 }, children: [
|
|
2041
|
+
/* @__PURE__ */ jsx7(Typography3, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("resetPassword.title") }),
|
|
2042
|
+
/* @__PURE__ */ jsx7(Typography3, { variant: "body2", sx: { color: "grey.600" }, children: t("resetPassword.instructions") })
|
|
2043
|
+
] }),
|
|
2044
|
+
/* @__PURE__ */ jsxs4(Box3, { sx: { mb: 1 }, children: [
|
|
2045
|
+
/* @__PURE__ */ jsx7(
|
|
2046
|
+
Typography3,
|
|
2047
|
+
{
|
|
2048
|
+
variant: "body2",
|
|
2049
|
+
component: "label",
|
|
2050
|
+
htmlFor: "newPassword",
|
|
2051
|
+
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2052
|
+
children: t("resetPassword.newPasswordLabel")
|
|
2053
|
+
}
|
|
2054
|
+
),
|
|
2055
|
+
/* @__PURE__ */ jsx7(
|
|
2056
|
+
TextField3,
|
|
2057
|
+
{
|
|
2058
|
+
fullWidth: true,
|
|
2059
|
+
id: "newPassword",
|
|
2060
|
+
name: "newPassword",
|
|
2061
|
+
type: "password",
|
|
2062
|
+
value: newPassword,
|
|
2063
|
+
disabled: loading,
|
|
2064
|
+
onChange: (e) => setNewPassword(e.target.value),
|
|
2065
|
+
error: !!helperTextNewPassword,
|
|
2066
|
+
helperText: helperTextNewPassword,
|
|
2067
|
+
autoComplete: "new-password",
|
|
2068
|
+
placeholder: t("resetPassword.newPasswordPlaceholder"),
|
|
2069
|
+
required: true
|
|
2070
|
+
}
|
|
2071
|
+
)
|
|
2072
|
+
] }),
|
|
2073
|
+
/* @__PURE__ */ jsxs4(Box3, { sx: { mb: 1 }, children: [
|
|
2074
|
+
/* @__PURE__ */ jsx7(
|
|
2075
|
+
Typography3,
|
|
2076
|
+
{
|
|
2077
|
+
variant: "body2",
|
|
2078
|
+
component: "label",
|
|
2079
|
+
htmlFor: "confirmPassword",
|
|
2080
|
+
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2081
|
+
children: t("resetPassword.confirmPasswordLabel")
|
|
2082
|
+
}
|
|
2083
|
+
),
|
|
2084
|
+
/* @__PURE__ */ jsx7(
|
|
2085
|
+
TextField3,
|
|
2086
|
+
{
|
|
2087
|
+
fullWidth: true,
|
|
2088
|
+
id: "confirmPassword",
|
|
2089
|
+
name: "confirmPassword",
|
|
2090
|
+
type: "password",
|
|
2091
|
+
value: confirmPassword,
|
|
2092
|
+
disabled: loading,
|
|
2093
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
2094
|
+
error: !!helperTextConfirmPassword,
|
|
2095
|
+
helperText: helperTextConfirmPassword,
|
|
2096
|
+
autoComplete: "new-password",
|
|
2097
|
+
placeholder: t("resetPassword.confirmPasswordPlaceholder"),
|
|
2098
|
+
required: true
|
|
2099
|
+
}
|
|
2100
|
+
)
|
|
2101
|
+
] }),
|
|
2102
|
+
/* @__PURE__ */ jsx7(Button3, { disabled: loading, type: "button", onClick: handleSubmit, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx7(CircularProgress3, { size: 20 }) : t("resetPassword.resetPasswordButton") }),
|
|
2103
|
+
/* @__PURE__ */ jsx7(Box3, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx7(Link3, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
|
|
2104
|
+
] }),
|
|
2105
|
+
/* @__PURE__ */ jsx7(Box3, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx7(Alert3, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) })
|
|
2106
|
+
] });
|
|
2109
2107
|
};
|
|
2108
|
+
var ResetPasswordForm_default = ResetPasswordForm;
|
|
2110
2109
|
|
|
2111
|
-
// src/
|
|
2112
|
-
import {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
const
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
if (
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
}
|
|
2128
|
-
if (propConfig?.env) {
|
|
2129
|
-
env = propConfig.env;
|
|
2130
|
-
}
|
|
2131
|
-
if (propConfig?.appName) {
|
|
2132
|
-
appName = propConfig.appName;
|
|
2133
|
-
}
|
|
2134
|
-
if (propConfig?.loginActions) {
|
|
2135
|
-
loginActions = propConfig.loginActions;
|
|
2110
|
+
// src/components/CrudifyLogin/Forms/CheckCodeForm.tsx
|
|
2111
|
+
import { useState as useState6, useEffect as useEffect7 } from "react";
|
|
2112
|
+
import { Typography as Typography4, TextField as TextField4, Button as Button4, Box as Box4, CircularProgress as CircularProgress4, Alert as Alert4, Link as Link4 } from "@mui/material";
|
|
2113
|
+
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2114
|
+
var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
|
|
2115
|
+
const { crudify: crudify6 } = useCrudify();
|
|
2116
|
+
const [code, setCode] = useState6("");
|
|
2117
|
+
const [loading, setLoading] = useState6(false);
|
|
2118
|
+
const [errors, setErrors] = useState6([]);
|
|
2119
|
+
const [helperTextCode, setHelperTextCode] = useState6(null);
|
|
2120
|
+
const [email, setEmail] = useState6("");
|
|
2121
|
+
const { t } = useTranslation();
|
|
2122
|
+
const getParam = (key) => {
|
|
2123
|
+
if (!searchParams) return null;
|
|
2124
|
+
if (searchParams instanceof URLSearchParams) {
|
|
2125
|
+
return searchParams.get(key);
|
|
2136
2126
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2127
|
+
return searchParams[key] || null;
|
|
2128
|
+
};
|
|
2129
|
+
const translateError = (parsedError) => {
|
|
2130
|
+
const possibleKeys = [
|
|
2131
|
+
`errors.auth.${parsedError.code}`,
|
|
2132
|
+
`errors.data.${parsedError.code}`,
|
|
2133
|
+
`errors.system.${parsedError.code}`,
|
|
2134
|
+
`errors.${parsedError.code}`,
|
|
2135
|
+
`checkCode.${parsedError.code.toLowerCase()}`
|
|
2136
|
+
];
|
|
2137
|
+
for (const key of possibleKeys) {
|
|
2138
|
+
const translated = t(key);
|
|
2139
|
+
if (translated !== key) {
|
|
2140
|
+
return translated;
|
|
2141
|
+
}
|
|
2139
2142
|
}
|
|
2140
|
-
|
|
2141
|
-
|
|
2143
|
+
return parsedError.message || t("error.unknown");
|
|
2144
|
+
};
|
|
2145
|
+
useEffect7(() => {
|
|
2146
|
+
const emailParam = getParam("email");
|
|
2147
|
+
if (emailParam) {
|
|
2148
|
+
setEmail(emailParam);
|
|
2149
|
+
} else {
|
|
2150
|
+
onScreenChange?.("forgotPassword");
|
|
2142
2151
|
}
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
}
|
|
2152
|
-
if (cookieEnv && ["dev", "stg", "prod"].includes(cookieEnv)) {
|
|
2153
|
-
env = cookieEnv;
|
|
2154
|
-
}
|
|
2155
|
-
if (cookieAppName) {
|
|
2156
|
-
appName = cookieAppName;
|
|
2157
|
-
}
|
|
2158
|
-
if (cookieLoginActions) {
|
|
2159
|
-
loginActions = cookieLoginActions.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2160
|
-
}
|
|
2152
|
+
}, [searchParams, onScreenChange]);
|
|
2153
|
+
const handleSubmit = async () => {
|
|
2154
|
+
if (loading || !crudify6) return;
|
|
2155
|
+
setErrors([]);
|
|
2156
|
+
setHelperTextCode(null);
|
|
2157
|
+
if (!code) {
|
|
2158
|
+
setHelperTextCode(t("checkCode.codeRequired"));
|
|
2159
|
+
return;
|
|
2161
2160
|
}
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
console.log(" - Environment:", env);
|
|
2166
|
-
console.log(" - App name:", appName);
|
|
2167
|
-
console.log(" - Login actions:", loginActions);
|
|
2168
|
-
return {
|
|
2169
|
-
publicApiKey,
|
|
2170
|
-
env,
|
|
2171
|
-
appName,
|
|
2172
|
-
loginActions,
|
|
2173
|
-
logo,
|
|
2174
|
-
colors
|
|
2175
|
-
};
|
|
2176
|
-
}, [propConfig]);
|
|
2177
|
-
const sessionData = useMemo2(() => {
|
|
2178
|
-
if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
|
|
2179
|
-
return null;
|
|
2161
|
+
if (code.length !== 6) {
|
|
2162
|
+
setHelperTextCode(t("checkCode.codeRequired"));
|
|
2163
|
+
return;
|
|
2180
2164
|
}
|
|
2165
|
+
setLoading(true);
|
|
2181
2166
|
try {
|
|
2182
|
-
const
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2167
|
+
const data = [
|
|
2168
|
+
{
|
|
2169
|
+
operation: "validatePasswordResetCode",
|
|
2170
|
+
data: { email, codePassword: code }
|
|
2171
|
+
}
|
|
2172
|
+
];
|
|
2173
|
+
const response = await crudify6.transaction(data);
|
|
2174
|
+
if (response.success) {
|
|
2175
|
+
onScreenChange?.("resetPassword", { email, code, fromCodeVerification: "true" });
|
|
2176
|
+
} else {
|
|
2177
|
+
const parsedErrors = handleCrudifyError(response);
|
|
2178
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
2179
|
+
setErrors(translatedErrors);
|
|
2180
|
+
setLoading(false);
|
|
2195
2181
|
}
|
|
2196
2182
|
} catch (error) {
|
|
2197
|
-
|
|
2183
|
+
const parsedErrors = handleCrudifyError(error);
|
|
2184
|
+
const translatedErrors = parsedErrors.map(translateError);
|
|
2185
|
+
setErrors(translatedErrors);
|
|
2186
|
+
setLoading(false);
|
|
2187
|
+
if (onError) {
|
|
2188
|
+
onError(translatedErrors.join(", "));
|
|
2189
|
+
}
|
|
2198
2190
|
}
|
|
2199
|
-
return null;
|
|
2200
|
-
}, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
|
|
2201
|
-
const contextValue = {
|
|
2202
|
-
...sessionHook,
|
|
2203
|
-
sessionData,
|
|
2204
|
-
config: resolvedConfig
|
|
2205
2191
|
};
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
const
|
|
2210
|
-
|
|
2211
|
-
|
|
2192
|
+
const handleBack = () => {
|
|
2193
|
+
onScreenChange?.("forgotPassword");
|
|
2194
|
+
};
|
|
2195
|
+
const handleCodeChange = (event) => {
|
|
2196
|
+
const value = event.target.value.replace(/\D/g, "").slice(0, 6);
|
|
2197
|
+
setCode(value);
|
|
2198
|
+
};
|
|
2199
|
+
return /* @__PURE__ */ jsxs5(Fragment5, { children: [
|
|
2200
|
+
/* @__PURE__ */ jsxs5(Box4, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
2201
|
+
/* @__PURE__ */ jsxs5(Box4, { sx: { mb: 2 }, children: [
|
|
2202
|
+
/* @__PURE__ */ jsx8(Typography4, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("checkCode.title") }),
|
|
2203
|
+
/* @__PURE__ */ jsx8(Typography4, { variant: "body2", sx: { color: "grey.600" }, children: t("checkCode.instructions") })
|
|
2204
|
+
] }),
|
|
2205
|
+
/* @__PURE__ */ jsxs5(Box4, { sx: { mb: 1 }, children: [
|
|
2206
|
+
/* @__PURE__ */ jsx8(
|
|
2207
|
+
Typography4,
|
|
2208
|
+
{
|
|
2209
|
+
variant: "body2",
|
|
2210
|
+
component: "label",
|
|
2211
|
+
htmlFor: "code",
|
|
2212
|
+
sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
|
|
2213
|
+
children: t("checkCode.codeLabel")
|
|
2214
|
+
}
|
|
2215
|
+
),
|
|
2216
|
+
/* @__PURE__ */ jsx8(
|
|
2217
|
+
TextField4,
|
|
2218
|
+
{
|
|
2219
|
+
fullWidth: true,
|
|
2220
|
+
id: "code",
|
|
2221
|
+
name: "code",
|
|
2222
|
+
type: "text",
|
|
2223
|
+
value: code,
|
|
2224
|
+
disabled: loading,
|
|
2225
|
+
onChange: handleCodeChange,
|
|
2226
|
+
error: !!helperTextCode,
|
|
2227
|
+
helperText: helperTextCode,
|
|
2228
|
+
placeholder: t("checkCode.codePlaceholder"),
|
|
2229
|
+
inputProps: {
|
|
2230
|
+
maxLength: 6,
|
|
2231
|
+
style: { textAlign: "center", fontSize: "1.5rem", letterSpacing: "0.4rem" }
|
|
2232
|
+
},
|
|
2233
|
+
required: true
|
|
2234
|
+
}
|
|
2235
|
+
)
|
|
2236
|
+
] }),
|
|
2237
|
+
/* @__PURE__ */ jsx8(
|
|
2238
|
+
Button4,
|
|
2239
|
+
{
|
|
2240
|
+
disabled: loading || code.length !== 6,
|
|
2241
|
+
type: "button",
|
|
2242
|
+
onClick: handleSubmit,
|
|
2243
|
+
fullWidth: true,
|
|
2244
|
+
variant: "contained",
|
|
2245
|
+
color: "primary",
|
|
2246
|
+
sx: { mt: 2, mb: 2 },
|
|
2247
|
+
children: loading ? /* @__PURE__ */ jsx8(CircularProgress4, { size: 20 }) : t("checkCode.verifyButton")
|
|
2248
|
+
}
|
|
2249
|
+
),
|
|
2250
|
+
/* @__PURE__ */ jsx8(Box4, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx8(Link4, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
|
|
2251
|
+
] }),
|
|
2252
|
+
/* @__PURE__ */ jsx8(Box4, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx8(Alert4, { sx: { mt: 2 }, severity: "error", children: error }, index)) })
|
|
2253
|
+
] });
|
|
2254
|
+
};
|
|
2255
|
+
var CheckCodeForm_default = CheckCodeForm;
|
|
2256
|
+
|
|
2257
|
+
// src/components/CrudifyLogin/components/CrudifyInitializer.tsx
|
|
2258
|
+
import { Box as Box5, CircularProgress as CircularProgress5, Alert as Alert5, Typography as Typography5 } from "@mui/material";
|
|
2259
|
+
import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2260
|
+
var CrudifyInitializer = ({ children, fallback }) => {
|
|
2261
|
+
const { isLoading, error, isInitialized } = useCrudify();
|
|
2262
|
+
const { t } = useTranslation();
|
|
2263
|
+
if (isLoading) {
|
|
2264
|
+
return fallback || /* @__PURE__ */ jsxs6(
|
|
2265
|
+
Box5,
|
|
2266
|
+
{
|
|
2267
|
+
sx: {
|
|
2268
|
+
display: "flex",
|
|
2269
|
+
flexDirection: "column",
|
|
2270
|
+
alignItems: "center",
|
|
2271
|
+
justifyContent: "center",
|
|
2272
|
+
minHeight: "200px",
|
|
2273
|
+
gap: 2
|
|
2274
|
+
},
|
|
2275
|
+
children: [
|
|
2276
|
+
/* @__PURE__ */ jsx9(CircularProgress5, {}),
|
|
2277
|
+
/* @__PURE__ */ jsx9(Typography5, { variant: "body2", color: "text.secondary", children: t("login.initializing") !== "login.initializing" ? t("login.initializing") : "Initializing..." })
|
|
2278
|
+
]
|
|
2279
|
+
}
|
|
2280
|
+
);
|
|
2212
2281
|
}
|
|
2213
|
-
|
|
2214
|
-
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2282
|
+
if (error) {
|
|
2283
|
+
return /* @__PURE__ */ jsx9(Alert5, { severity: "error", sx: { mt: 2 }, children: /* @__PURE__ */ jsxs6(Typography5, { variant: "body2", children: [
|
|
2284
|
+
t("login.initializationError") !== "login.initializationError" ? t("login.initializationError") : "Initialization error",
|
|
2285
|
+
":",
|
|
2286
|
+
" ",
|
|
2287
|
+
error
|
|
2288
|
+
] }) });
|
|
2219
2289
|
}
|
|
2220
|
-
if (!
|
|
2221
|
-
|
|
2222
|
-
redirectTo();
|
|
2223
|
-
return null;
|
|
2224
|
-
}
|
|
2225
|
-
return /* @__PURE__ */ jsx9(Fragment6, { children: fallback });
|
|
2290
|
+
if (!isInitialized) {
|
|
2291
|
+
return /* @__PURE__ */ jsx9(Alert5, { severity: "warning", sx: { mt: 2 }, children: /* @__PURE__ */ jsx9(Typography5, { variant: "body2", children: t("login.notInitialized") !== "login.notInitialized" ? t("login.notInitialized") : "System not initialized" }) });
|
|
2226
2292
|
}
|
|
2227
2293
|
return /* @__PURE__ */ jsx9(Fragment6, { children });
|
|
2228
|
-
}
|
|
2229
|
-
function SessionDebugInfo() {
|
|
2230
|
-
const session = useSessionContext();
|
|
2231
|
-
if (!session.isInitialized) {
|
|
2232
|
-
return /* @__PURE__ */ jsx9("div", { children: "Session not initialized" });
|
|
2233
|
-
}
|
|
2234
|
-
return /* @__PURE__ */ jsxs6(
|
|
2235
|
-
"div",
|
|
2236
|
-
{
|
|
2237
|
-
style: {
|
|
2238
|
-
padding: "10px",
|
|
2239
|
-
margin: "10px",
|
|
2240
|
-
border: "1px solid #ccc",
|
|
2241
|
-
borderRadius: "4px",
|
|
2242
|
-
fontSize: "12px",
|
|
2243
|
-
fontFamily: "monospace"
|
|
2244
|
-
},
|
|
2245
|
-
children: [
|
|
2246
|
-
/* @__PURE__ */ jsx9("h4", { children: "Session Debug Info" }),
|
|
2247
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2248
|
-
/* @__PURE__ */ jsx9("strong", { children: "Authenticated:" }),
|
|
2249
|
-
" ",
|
|
2250
|
-
session.isAuthenticated ? "Yes" : "No"
|
|
2251
|
-
] }),
|
|
2252
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2253
|
-
/* @__PURE__ */ jsx9("strong", { children: "Loading:" }),
|
|
2254
|
-
" ",
|
|
2255
|
-
session.isLoading ? "Yes" : "No"
|
|
2256
|
-
] }),
|
|
2257
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2258
|
-
/* @__PURE__ */ jsx9("strong", { children: "Error:" }),
|
|
2259
|
-
" ",
|
|
2260
|
-
session.error || "None"
|
|
2261
|
-
] }),
|
|
2262
|
-
session.tokens && /* @__PURE__ */ jsxs6(Fragment6, { children: [
|
|
2263
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2264
|
-
/* @__PURE__ */ jsx9("strong", { children: "Access Token:" }),
|
|
2265
|
-
" ",
|
|
2266
|
-
session.tokens.accessToken.substring(0, 20),
|
|
2267
|
-
"..."
|
|
2268
|
-
] }),
|
|
2269
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2270
|
-
/* @__PURE__ */ jsx9("strong", { children: "Refresh Token:" }),
|
|
2271
|
-
" ",
|
|
2272
|
-
session.tokens.refreshToken.substring(0, 20),
|
|
2273
|
-
"..."
|
|
2274
|
-
] }),
|
|
2275
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2276
|
-
/* @__PURE__ */ jsx9("strong", { children: "Access Expires In:" }),
|
|
2277
|
-
" ",
|
|
2278
|
-
Math.round(session.expiresIn / 1e3 / 60),
|
|
2279
|
-
" minutes"
|
|
2280
|
-
] }),
|
|
2281
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2282
|
-
/* @__PURE__ */ jsx9("strong", { children: "Refresh Expires In:" }),
|
|
2283
|
-
" ",
|
|
2284
|
-
Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
|
|
2285
|
-
" hours"
|
|
2286
|
-
] }),
|
|
2287
|
-
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2288
|
-
/* @__PURE__ */ jsx9("strong", { children: "Expiring Soon:" }),
|
|
2289
|
-
" ",
|
|
2290
|
-
session.isExpiringSoon ? "Yes" : "No"
|
|
2291
|
-
] })
|
|
2292
|
-
] })
|
|
2293
|
-
]
|
|
2294
|
-
}
|
|
2295
|
-
);
|
|
2296
|
-
}
|
|
2294
|
+
};
|
|
2297
2295
|
|
|
2298
2296
|
// src/components/CrudifyLogin/index.tsx
|
|
2299
2297
|
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|