@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.
Files changed (3) hide show
  1. package/dist/index.js +1784 -1774
  2. package/dist/index.mjs +1784 -1774
  3. 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/components/CrudifyLogin/Forms/LoginForm.tsx
394
- import { useEffect as useEffect4, useRef } from "react";
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/utils/errorHandler.ts
398
- var ERROR_CODES = {
399
- // Authentication Errors
400
- INVALID_CREDENTIALS: "INVALID_CREDENTIALS",
401
- UNAUTHORIZED: "UNAUTHORIZED",
402
- INVALID_API_KEY: "INVALID_API_KEY",
403
- USER_NOT_FOUND: "USER_NOT_FOUND",
404
- USER_NOT_ACTIVE: "USER_NOT_ACTIVE",
405
- NO_PERMISSION: "NO_PERMISSION",
406
- // Data Errors
407
- ITEM_NOT_FOUND: "ITEM_NOT_FOUND",
408
- NOT_FOUND: "NOT_FOUND",
409
- IN_USE: "IN_USE",
410
- // Validation Errors
411
- FIELD_ERROR: "FIELD_ERROR",
412
- BAD_REQUEST: "BAD_REQUEST",
413
- INVALID_EMAIL: "INVALID_EMAIL",
414
- INVALID_CODE: "INVALID_CODE",
415
- // System Errors
416
- INTERNAL_SERVER_ERROR: "INTERNAL_SERVER_ERROR",
417
- DATABASE_CONNECTION_ERROR: "DATABASE_CONNECTION_ERROR",
418
- INVALID_CONFIGURATION: "INVALID_CONFIGURATION",
419
- UNKNOWN_OPERATION: "UNKNOWN_OPERATION",
420
- // Rate Limiting
421
- TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS",
422
- // Network Errors
423
- NETWORK_ERROR: "NETWORK_ERROR",
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
- if (errors.length === 0 && apiResponse.success === false) {
530
- errors.push({
531
- code: ERROR_CODES.BAD_REQUEST,
532
- message: "Request failed",
533
- severity: "warning"
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
- } catch (error) {
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
- return errors.length > 0 ? errors : [
545
- {
546
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
547
- message: "Unknown error occurred",
548
- severity: "error"
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
- function parseTransactionError(response) {
553
- try {
554
- const transactionResponse = response;
555
- if (transactionResponse.data && Array.isArray(transactionResponse.data)) {
556
- const errors = [];
557
- transactionResponse.data.forEach((item, index) => {
558
- if (item.response?.status === "TOO_MANY_REQUESTS") {
559
- errors.push({
560
- code: ERROR_CODES.TOO_MANY_REQUESTS,
561
- message: getErrorMessage(ERROR_CODES.TOO_MANY_REQUESTS),
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
- function isValidErrorCode(code) {
589
- return Object.values(ERROR_CODES).includes(code);
590
- }
591
- function getErrorMessage(code) {
592
- const messages = {
593
- [ERROR_CODES.INVALID_CREDENTIALS]: "Invalid email or password",
594
- [ERROR_CODES.UNAUTHORIZED]: "You are not authorized to perform this action",
595
- [ERROR_CODES.INVALID_API_KEY]: "Invalid API key",
596
- [ERROR_CODES.USER_NOT_FOUND]: "User not found",
597
- [ERROR_CODES.USER_NOT_ACTIVE]: "User account is not active",
598
- [ERROR_CODES.NO_PERMISSION]: "You do not have permission to perform this action",
599
- [ERROR_CODES.ITEM_NOT_FOUND]: "Item not found",
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
- if (error.message.includes("NetworkError") || error.message.includes("Failed to fetch")) {
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
- code: ERROR_CODES.NETWORK_ERROR,
628
- message: getErrorMessage(ERROR_CODES.NETWORK_ERROR),
629
- severity: "error"
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
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
634
- message: error.message || "An unexpected error occurred",
635
- severity: "error",
636
- details: { originalError: error }
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
- return {
640
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
641
- message: "An unknown error occurred",
642
- severity: "error",
643
- details: { originalError: error }
644
- };
645
- }
646
- function handleCrudifyError(error) {
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
- return parseApiError(error);
557
+ _TokenStorage.saveTokens({
558
+ ...existingTokens,
559
+ accessToken: newAccessToken,
560
+ expiresAt: newExpiresAt
561
+ });
656
562
  }
657
- return [
658
- {
659
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
660
- message: "An unknown error occurred",
661
- severity: "error",
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/components/CrudifyLogin/Forms/LoginForm.tsx
668
- import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
669
- var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
670
- const { crudify: crudify6 } = useCrudify();
671
- const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
672
- const { t } = useTranslation();
673
- const usernameInputRef = useRef(null);
674
- const getRedirectUrl = () => {
675
- if (state.searchParams.redirect) {
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 parsedError.message || t("error.unknown");
707
- };
708
- const handleLogin = async () => {
709
- if (state.loading) return;
710
- if (!state.formData.username.trim()) {
711
- setFieldError("username", t("login.usernameRequired"));
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
- if (!state.formData.password.trim()) {
715
- setFieldError("password", t("login.passwordRequired"));
716
- return;
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
- clearErrors();
719
- setLoading(true);
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
- if (!crudify6) {
722
- throw new Error("Crudify not initialized");
723
- }
724
- const response = await crudify6.login(state.formData.username, state.formData.password);
725
- setLoading(false);
726
- if (response.success) {
727
- console.log("\u{1F510} LoginForm - Login successful, calling onLoginSuccess");
728
- const finalRedirectUrl = getRedirectUrl();
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
- setLoading(false);
737
- const parsedErrors = handleCrudifyError(error);
738
- const translatedErrors = parsedErrors.map(translateError);
739
- setFieldError("global", translatedErrors);
740
- if (onError) {
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
- if (!validateEmail(email)) {
916
- setHelperTextEmail(t("forgotPassword.invalidEmail"));
917
- return;
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
- setLoading(true);
653
+ }
654
+ /**
655
+ * Restaurar sesión desde storage
656
+ */
657
+ async restoreSession() {
920
658
  try {
921
- const data = [{ operation: "requestPasswordReset", data: { email } }];
922
- const response = await crudify6.transaction(data);
923
- if (response.success) {
924
- if (response.data && response.data.existingCodeValid) {
925
- setCodeAlreadyExists(true);
926
- } else {
927
- setEmailSent(true);
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
- } else {
930
- const parsedErrors = handleCrudifyError(response);
931
- const translatedErrors = parsedErrors.map(translateError);
932
- setErrors(translatedErrors);
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
- const parsedErrors = handleCrudifyError(error);
936
- const translatedErrors = parsedErrors.map(translateError);
937
- setErrors(translatedErrors);
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
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
974
- /* @__PURE__ */ jsxs2(Box2, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
975
- /* @__PURE__ */ jsxs2(Box2, { sx: { mb: 2 }, children: [
976
- /* @__PURE__ */ jsx5(Typography2, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("forgotPassword.title") }),
977
- /* @__PURE__ */ jsx5(Typography2, { variant: "body2", sx: { color: "grey.600" }, children: t("forgotPassword.instructions") })
978
- ] }),
979
- /* @__PURE__ */ jsxs2(Box2, { sx: { mb: 1 }, children: [
980
- /* @__PURE__ */ jsx5(
981
- Typography2,
982
- {
983
- variant: "body2",
984
- component: "label",
985
- htmlFor: "email",
986
- sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
987
- children: t("forgotPassword.emailLabel")
988
- }
989
- ),
990
- /* @__PURE__ */ jsx5(
991
- TextField2,
992
- {
993
- fullWidth: true,
994
- id: "email",
995
- name: "email",
996
- type: "email",
997
- value: email,
998
- disabled: loading,
999
- onChange: (e) => setEmail(e.target.value),
1000
- error: !!helperTextEmail,
1001
- helperText: helperTextEmail,
1002
- autoComplete: "email",
1003
- placeholder: t("forgotPassword.emailPlaceholder"),
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
- return parsedError.message || t("error.unknown");
1055
- };
1056
- const getParam = (key) => {
1057
- if (!searchParams) return null;
1058
- if (searchParams instanceof URLSearchParams) {
1059
- return searchParams.get(key);
1060
- }
1061
- return searchParams[key] || null;
1062
- };
1063
- useEffect5(() => {
1064
- if (!searchParams) {
1065
- return;
1066
- }
1067
- if (searchParams) {
1068
- const fromCodeVerificationParam = getParam("fromCodeVerification");
1069
- const emailParam = getParam("email");
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
- if (emailParam && codeParam) {
1095
- setEmail(emailParam);
1096
- setCode(codeParam);
1097
- setFromCodeVerification(false);
1098
- setPendingValidation({ email: emailParam, code: codeParam });
1099
- return;
1100
- }
1101
- }
1102
- setErrors([t("resetPassword.invalidCode")]);
1103
- setValidatingCode(false);
1104
- setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
1105
- }, [searchParams, crudify6, t, onScreenChange]);
1106
- useEffect5(() => {
1107
- if (crudify6 && pendingValidation && !isValidating) {
1108
- setIsValidating(true);
1109
- const validateCode = async (emailToValidate, codeToValidate) => {
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
- return null;
1152
- };
1153
- const handleSubmit = async () => {
1154
- if (loading || !crudify6) return;
1155
- setErrors([]);
1156
- setHelperTextNewPassword(null);
1157
- setHelperTextConfirmPassword(null);
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
- if (!confirmPassword) {
1170
- setHelperTextConfirmPassword(t("resetPassword.confirmPasswordRequired"));
1171
- hasErrors = true;
1172
- } else if (newPassword !== confirmPassword) {
1173
- setHelperTextConfirmPassword(t("resetPassword.passwordsDoNotMatch"));
1174
- hasErrors = true;
1175
- }
1176
- if (hasErrors) return;
1177
- setLoading(true);
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
- const data = [
1180
- {
1181
- operation: "validateAndResetPassword",
1182
- data: { email, codePassword: code, newPassword }
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
- const response = await crudify6.transaction(data);
1186
- if (response.success) {
1187
- setErrors([]);
1188
- setTimeout(() => {
1189
- onResetSuccess?.();
1190
- }, 1e3);
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
- const parsedErrors = handleCrudifyError(response);
1193
- const translatedErrors = parsedErrors.map(translateError);
1194
- setErrors(translatedErrors);
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 parsedErrors = handleCrudifyError(error);
1198
- const translatedErrors = parsedErrors.map(translateError);
1199
- setErrors(translatedErrors);
1200
- if (onError) {
1201
- onError(translatedErrors.join(", "));
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
- setLoading(false);
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
- const handleBack = () => {
1207
- if (fromCodeVerification) {
1208
- onScreenChange?.("checkCode", { email });
1209
- } else {
1210
- onScreenChange?.("forgotPassword");
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
- if (validatingCode) {
1214
- return /* @__PURE__ */ jsx6(Box3, { sx: { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "300px" }, children: /* @__PURE__ */ jsx6(CircularProgress3, {}) });
1215
- }
1216
- if (!codeValidated) {
1217
- return /* @__PURE__ */ jsx6(Box3, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx6(Alert3, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) });
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/components/CrudifyLogin/Forms/CheckCodeForm.tsx
1292
- import { useState as useState5, useEffect as useEffect6 } from "react";
1293
- 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";
1294
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1295
- var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
1296
- const { crudify: crudify6 } = useCrudify();
1297
- const [code, setCode] = useState5("");
1298
- const [loading, setLoading] = useState5(false);
1299
- const [errors, setErrors] = useState5([]);
1300
- const [helperTextCode, setHelperTextCode] = useState5(null);
1301
- const [email, setEmail] = useState5("");
1302
- const { t } = useTranslation();
1303
- const getParam = (key) => {
1304
- if (!searchParams) return null;
1305
- if (searchParams instanceof URLSearchParams) {
1306
- return searchParams.get(key);
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
- return searchParams[key] || null;
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
- return parsedError.message || t("error.unknown");
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
- }, [searchParams, onScreenChange]);
1334
- const handleSubmit = async () => {
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 (code.length !== 6) {
1343
- setHelperTextCode(t("checkCode.codeRequired"));
1344
- return;
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 data = [
1349
- {
1350
- operation: "validatePasswordResetCode",
1351
- data: { email, codePassword: code }
1352
- }
1353
- ];
1354
- const response = await crudify6.transaction(data);
1355
- if (response.success) {
1356
- onScreenChange?.("resetPassword", { email, code, fromCodeVerification: "true" });
1357
- } else {
1358
- const parsedErrors = handleCrudifyError(response);
1359
- const translatedErrors = parsedErrors.map(translateError);
1360
- setErrors(translatedErrors);
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
- const parsedErrors = handleCrudifyError(error);
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
- const handleBack = () => {
1374
- onScreenChange?.("forgotPassword");
1375
- };
1376
- const handleCodeChange = (event) => {
1377
- const value = event.target.value.replace(/\D/g, "").slice(0, 6);
1378
- setCode(value);
1379
- };
1380
- return /* @__PURE__ */ jsxs4(Fragment4, { children: [
1381
- /* @__PURE__ */ jsxs4(Box4, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
1382
- /* @__PURE__ */ jsxs4(Box4, { sx: { mb: 2 }, children: [
1383
- /* @__PURE__ */ jsx7(Typography4, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("checkCode.title") }),
1384
- /* @__PURE__ */ jsx7(Typography4, { variant: "body2", sx: { color: "grey.600" }, children: t("checkCode.instructions") })
1385
- ] }),
1386
- /* @__PURE__ */ jsxs4(Box4, { sx: { mb: 1 }, children: [
1387
- /* @__PURE__ */ jsx7(
1388
- Typography4,
1389
- {
1390
- variant: "body2",
1391
- component: "label",
1392
- htmlFor: "code",
1393
- sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
1394
- children: t("checkCode.codeLabel")
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
- /* @__PURE__ */ jsx7(
1398
- TextField4,
1399
- {
1400
- fullWidth: true,
1401
- id: "code",
1402
- name: "code",
1403
- type: "text",
1404
- value: code,
1405
- disabled: loading,
1406
- onChange: handleCodeChange,
1407
- error: !!helperTextCode,
1408
- helperText: helperTextCode,
1409
- placeholder: t("checkCode.codePlaceholder"),
1410
- inputProps: {
1411
- maxLength: 6,
1412
- style: { textAlign: "center", fontSize: "1.5rem", letterSpacing: "0.4rem" }
1413
- },
1414
- required: true
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
- /* @__PURE__ */ jsx7(
1419
- Button4,
1420
- {
1421
- disabled: loading || code.length !== 6,
1422
- type: "button",
1423
- onClick: handleSubmit,
1424
- fullWidth: true,
1425
- variant: "contained",
1426
- color: "primary",
1427
- sx: { mt: 2, mb: 2 },
1428
- children: loading ? /* @__PURE__ */ jsx7(CircularProgress4, { size: 20 }) : t("checkCode.verifyButton")
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
- /* @__PURE__ */ jsx7(Box4, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx7(Link4, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
1432
- ] }),
1433
- /* @__PURE__ */ jsx7(Box4, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx7(Alert4, { sx: { mt: 2 }, severity: "error", children: error }, index)) })
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
- sx: {
1449
- display: "flex",
1450
- flexDirection: "column",
1451
- alignItems: "center",
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
- * Verificar si el storage está disponible
1497
- */
1498
- static isStorageAvailable(type) {
1499
- try {
1500
- const storage = window[type];
1501
- const testKey = "__storage_test__";
1502
- storage.setItem(testKey, "test");
1503
- storage.removeItem(testKey);
1504
- return true;
1505
- } catch {
1506
- return false;
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
- * Obtener instancia de storage
1511
- */
1512
- static getStorage() {
1513
- if (_TokenStorage.storageType === "none") return null;
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 window[_TokenStorage.storageType];
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
- * Encriptar datos sensibles
1522
- */
1523
- static encrypt(data) {
1524
- try {
1525
- return CryptoJS.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
1526
- } catch (error) {
1527
- console.error("Crudify: Encryption failed", error);
1528
- return data;
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
- * Desencriptar datos
1533
- */
1534
- static decrypt(encryptedData) {
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
- * Guardar tokens de forma segura
1546
- */
1547
- static saveTokens(tokens) {
1548
- const storage = _TokenStorage.getStorage();
1549
- if (!storage) return;
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
- * Obtener tokens guardados
1567
- */
1568
- static getTokens() {
1569
- const storage = _TokenStorage.getStorage();
1570
- if (!storage) return null;
1571
- try {
1572
- const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
1573
- if (!encrypted) return null;
1574
- const decrypted = _TokenStorage.decrypt(encrypted);
1575
- const tokenData = JSON.parse(decrypted);
1576
- if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
1577
- console.warn("Crudify: Incomplete token data found, clearing storage");
1578
- _TokenStorage.clearTokens();
1579
- return null;
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
- if (Date.now() >= tokenData.refreshExpiresAt) {
1582
- console.info("Crudify: Refresh token expired, clearing storage");
1583
- _TokenStorage.clearTokens();
1584
- return null;
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
- * Limpiar tokens almacenados
1600
- */
1601
- static clearTokens() {
1602
- const storage = _TokenStorage.getStorage();
1603
- if (!storage) return;
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
- _TokenStorage.saveTokens({
1642
- ...existingTokens,
1643
- accessToken: newAccessToken,
1644
- expiresAt: newExpiresAt
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
- _TokenStorage.TOKEN_KEY = "crudify_tokens";
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/core/SessionManager.ts
1654
- var SessionManager = class _SessionManager {
1655
- constructor() {
1656
- this.config = {};
1657
- this.initialized = false;
1658
- }
1659
- static getInstance() {
1660
- if (!_SessionManager.instance) {
1661
- _SessionManager.instance = new _SessionManager();
1662
- }
1663
- return _SessionManager.instance;
1664
- }
1665
- /**
1666
- * Inicializar el SessionManager
1667
- */
1668
- async initialize(config = {}) {
1669
- if (this.initialized) {
1670
- console.warn("SessionManager: Already initialized");
1671
- return;
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
- this.config = {
1674
- storageType: "localStorage",
1675
- autoRestore: true,
1676
- enableLogging: false,
1677
- ...config
1678
- };
1679
- TokenStorage.setStorageType(this.config.storageType || "localStorage");
1680
- if (this.config.enableLogging) {
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 (this.config.autoRestore) {
1683
- await this.restoreSession();
1746
+ if (!validateEmail(email)) {
1747
+ setHelperTextEmail(t("forgotPassword.invalidEmail"));
1748
+ return;
1684
1749
  }
1685
- this.initialized = true;
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
- this.log("Attempting login...");
1694
- const response = await crudify2.login(email, password);
1695
- if (!response.success) {
1696
- this.log("Login failed:", response.errors);
1697
- return {
1698
- success: false,
1699
- error: this.formatError(response.errors)
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
- this.log("Login error:", error);
1717
- return {
1718
- success: false,
1719
- error: error instanceof Error ? error.message : "Unknown error"
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
- * Logout con limpieza de tokens
1725
- */
1726
- async logout() {
1727
- try {
1728
- this.log("Logging out...");
1729
- await crudify2.logout();
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
- * Restaurar sesión desde storage
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
- * Verificar si el usuario está autenticado
1766
- */
1767
- isAuthenticated() {
1768
- return crudify2.isLogin() || TokenStorage.hasValidTokens();
1769
- }
1770
- /**
1771
- * Obtener información de tokens actuales
1772
- */
1773
- getTokenInfo() {
1774
- const crudifyTokens = crudify2.getTokenData();
1775
- const storageInfo = TokenStorage.getExpirationInfo();
1776
- return {
1777
- isLoggedIn: this.isAuthenticated(),
1778
- crudifyTokens,
1779
- storageInfo,
1780
- hasValidTokens: TokenStorage.hasValidTokens()
1781
- };
1782
- }
1783
- /**
1784
- * Refrescar tokens manualmente
1785
- */
1786
- async refreshTokens() {
1787
- try {
1788
- this.log("Manually refreshing tokens...");
1789
- const response = await crudify2.refreshAccessToken();
1790
- if (!response.success) {
1791
- this.log("Token refresh failed:", response.errors);
1792
- TokenStorage.clearTokens();
1793
- this.config.onSessionExpired?.();
1794
- return false;
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
- * Configurar interceptor de respuesta para manejo automático de errores
1814
- */
1815
- setupResponseInterceptor() {
1816
- crudify2.setResponseInterceptor(async (response) => {
1817
- if (response.errors) {
1818
- const hasAuthError = response.errors.some(
1819
- (error) => error.message?.includes("Unauthorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED"
1820
- );
1821
- if (hasAuthError && TokenStorage.hasValidTokens()) {
1822
- this.log("Auth error detected, attempting token refresh...");
1823
- const refreshSuccess = await this.refreshTokens();
1824
- if (!refreshSuccess) {
1825
- this.log("Session expired, triggering callback");
1826
- this.config.onSessionExpired?.();
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
- return response;
1831
- });
1832
- this.log("Response interceptor configured");
1833
- }
1834
- /**
1835
- * Limpiar sesión completamente
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
- return "Authentication failed";
1856
- }
1857
- };
1858
-
1859
- // src/hooks/useSession.ts
1860
- function useSession(options = {}) {
1861
- const [state, setState] = useState6({
1862
- isAuthenticated: false,
1863
- isLoading: true,
1864
- isInitialized: false,
1865
- tokens: null,
1866
- error: null
1867
- });
1868
- const sessionManager = SessionManager.getInstance();
1869
- const initialize = useCallback(async () => {
1870
- try {
1871
- setState((prev) => ({ ...prev, isLoading: true, error: null }));
1872
- const config = {
1873
- autoRestore: options.autoRestore ?? true,
1874
- enableLogging: options.enableLogging ?? false,
1875
- onSessionExpired: () => {
1876
- setState((prev) => ({
1877
- ...prev,
1878
- isAuthenticated: false,
1879
- tokens: null,
1880
- error: "Session expired"
1881
- }));
1882
- options.onSessionExpired?.();
1883
- },
1884
- onSessionRestored: (tokens) => {
1885
- setState((prev) => ({
1886
- ...prev,
1887
- isAuthenticated: true,
1888
- tokens,
1889
- error: null
1890
- }));
1891
- options.onSessionRestored?.(tokens);
1892
- },
1893
- onLoginSuccess: (tokens) => {
1894
- setState((prev) => ({
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
- await sessionManager.initialize(config);
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
- }, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
1935
- const login = useCallback(async (email, password) => {
1936
- setState((prev) => ({ ...prev, isLoading: true, error: null }));
1937
- try {
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
- }, [sessionManager]);
1972
- const logout = useCallback(async () => {
1973
- setState((prev) => ({ ...prev, isLoading: true }));
1974
- try {
1975
- await sessionManager.logout();
1976
- setState((prev) => ({
1977
- ...prev,
1978
- isAuthenticated: false,
1979
- tokens: null,
1980
- isLoading: false,
1981
- error: null
1982
- }));
1983
- } catch (error) {
1984
- setState((prev) => ({
1985
- ...prev,
1986
- isAuthenticated: false,
1987
- tokens: null,
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
- }, [sessionManager]);
1993
- const refreshTokens = useCallback(async () => {
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 success = await sessionManager.refreshTokens();
1996
- if (success) {
1997
- const tokenInfo = sessionManager.getTokenInfo();
1998
- setState((prev) => ({
1999
- ...prev,
2000
- tokens: tokenInfo.crudifyTokens.accessToken ? {
2001
- accessToken: tokenInfo.crudifyTokens.accessToken,
2002
- refreshToken: tokenInfo.crudifyTokens.refreshToken,
2003
- expiresAt: tokenInfo.crudifyTokens.expiresAt,
2004
- refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
2005
- } : null,
2006
- error: null
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
- setState((prev) => ({
2010
- ...prev,
2011
- isAuthenticated: false,
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
- setState((prev) => ({
2019
- ...prev,
2020
- isAuthenticated: false,
2021
- tokens: null,
2022
- error: error instanceof Error ? error.message : "Token refresh failed"
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
- }, [sessionManager]);
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
- // src/utils/jwtUtils.ts
2054
- var decodeJwtSafely = (token) => {
2055
- try {
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
- const email = payload.email || payload["cognito:username"] || null;
2093
- console.log("\u{1F50D} getCurrentUserEmail - Extracted email:", email);
2094
- return email;
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
- var isTokenExpired = (token) => {
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/providers/SessionProvider.tsx
2112
- import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2113
- var SessionContext = createContext4(void 0);
2114
- function SessionProvider({ children, options = {}, config: propConfig }) {
2115
- const sessionHook = useSession(options);
2116
- const resolvedConfig = useMemo2(() => {
2117
- let publicApiKey;
2118
- let env;
2119
- let appName;
2120
- let loginActions;
2121
- let logo;
2122
- let colors;
2123
- let configSource = "unknown";
2124
- if (propConfig?.publicApiKey) {
2125
- publicApiKey = propConfig.publicApiKey;
2126
- configSource = "props";
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
- if (propConfig?.logo) {
2138
- logo = propConfig.logo;
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
- if (propConfig?.colors) {
2141
- colors = propConfig.colors;
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
- if (!publicApiKey) {
2144
- const cookieApiKey = getCookie("publicApiKey");
2145
- const cookieEnv = getCookie("environment");
2146
- const cookieAppName = getCookie("appName");
2147
- const cookieLoginActions = getCookie("loginActions");
2148
- if (cookieApiKey) {
2149
- publicApiKey = cookieApiKey;
2150
- configSource = "cookies";
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
- console.log("\u{1F50D} SessionProvider - Configuration Detection:");
2163
- console.log(" - Config source:", configSource);
2164
- console.log(" - PublicApiKey:", publicApiKey ? publicApiKey.substring(0, 15) + "..." : "undefined");
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 decoded = decodeJwtSafely(sessionHook.tokens.accessToken);
2183
- if (decoded && decoded.sub && decoded.email && decoded.subscriber) {
2184
- const result = {
2185
- _id: decoded.sub,
2186
- email: decoded.email,
2187
- subscriberKey: decoded.subscriber
2188
- };
2189
- Object.keys(decoded).forEach((key) => {
2190
- if (!["sub", "email", "subscriber"].includes(key)) {
2191
- result[key] = decoded[key];
2192
- }
2193
- });
2194
- return result;
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
- console.error("Error decoding JWT token for sessionData:", error);
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
- return /* @__PURE__ */ jsx9(SessionContext.Provider, { value: contextValue, children });
2207
- }
2208
- function useSessionContext() {
2209
- const context = useContext4(SessionContext);
2210
- if (context === void 0) {
2211
- throw new Error("useSessionContext must be used within a SessionProvider");
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
- return context;
2214
- }
2215
- function ProtectedRoute({ children, fallback = /* @__PURE__ */ jsx9("div", { children: "Please log in to access this content" }), redirectTo }) {
2216
- const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
2217
- if (!isInitialized || isLoading) {
2218
- return /* @__PURE__ */ jsx9("div", { children: "Loading..." });
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 (!isAuthenticated) {
2221
- if (redirectTo) {
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";