@nocios/crudify-ui 3.0.10 → 3.0.12

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