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