@nocios/crudify-ui 3.0.4 → 3.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ __export(index_exports, {
45
45
  SessionStatus: () => SessionStatus,
46
46
  TokenStorage: () => TokenStorage,
47
47
  UserProfileDisplay: () => UserProfileDisplay_default,
48
- crudify: () => import_crudify_browser7.default,
48
+ crudify: () => import_crudify_browser6.default,
49
49
  decodeJwtSafely: () => decodeJwtSafely,
50
50
  getCookie: () => getCookie,
51
51
  getCurrentUserEmail: () => getCurrentUserEmail,
@@ -65,7 +65,7 @@ __export(index_exports, {
65
65
  useUserProfile: () => useUserProfile
66
66
  });
67
67
  module.exports = __toCommonJS(index_exports);
68
- var import_crudify_browser7 = __toESM(require("@nocios/crudify-browser"));
68
+ var import_crudify_browser6 = __toESM(require("@nocios/crudify-browser"));
69
69
  __reExport(index_exports, require("@nocios/crudify-browser"), module.exports);
70
70
 
71
71
  // src/components/CrudifyLogin/index.tsx
@@ -733,7 +733,7 @@ function handleCrudifyError(error) {
733
733
  // src/components/CrudifyLogin/Forms/LoginForm.tsx
734
734
  var import_jsx_runtime4 = require("react/jsx-runtime");
735
735
  var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
736
- const { crudify: crudify7 } = useCrudify();
736
+ const { crudify: crudify6 } = useCrudify();
737
737
  const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
738
738
  const { t } = useTranslation();
739
739
  const usernameInputRef = (0, import_react5.useRef)(null);
@@ -784,10 +784,10 @@ var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError,
784
784
  clearErrors();
785
785
  setLoading(true);
786
786
  try {
787
- if (!crudify7) {
787
+ if (!crudify6) {
788
788
  throw new Error("Crudify not initialized");
789
789
  }
790
- const response = await crudify7.login(state.formData.username, state.formData.password);
790
+ const response = await crudify6.login(state.formData.username, state.formData.password);
791
791
  setLoading(false);
792
792
  if (response.success) {
793
793
  console.log("\u{1F510} LoginForm - Login successful, calling onLoginSuccess");
@@ -942,7 +942,7 @@ var import_react6 = require("react");
942
942
  var import_material2 = require("@mui/material");
943
943
  var import_jsx_runtime5 = require("react/jsx-runtime");
944
944
  var ForgotPasswordForm = ({ onScreenChange, onError }) => {
945
- const { crudify: crudify7 } = useCrudify();
945
+ const { crudify: crudify6 } = useCrudify();
946
946
  const [email, setEmail] = (0, import_react6.useState)("");
947
947
  const [loading, setLoading] = (0, import_react6.useState)(false);
948
948
  const [errors, setErrors] = (0, import_react6.useState)([]);
@@ -971,7 +971,7 @@ var ForgotPasswordForm = ({ onScreenChange, onError }) => {
971
971
  return emailRegex.test(email2);
972
972
  };
973
973
  const handleSubmit = async () => {
974
- if (loading || !crudify7) return;
974
+ if (loading || !crudify6) return;
975
975
  setErrors([]);
976
976
  setHelperTextEmail(null);
977
977
  if (!email) {
@@ -985,7 +985,7 @@ var ForgotPasswordForm = ({ onScreenChange, onError }) => {
985
985
  setLoading(true);
986
986
  try {
987
987
  const data = [{ operation: "requestPasswordReset", data: { email } }];
988
- const response = await crudify7.transaction(data);
988
+ const response = await crudify6.transaction(data);
989
989
  if (response.success) {
990
990
  if (response.data && response.data.existingCodeValid) {
991
991
  setCodeAlreadyExists(true);
@@ -1088,7 +1088,7 @@ var import_react7 = require("react");
1088
1088
  var import_material3 = require("@mui/material");
1089
1089
  var import_jsx_runtime6 = require("react/jsx-runtime");
1090
1090
  var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess }) => {
1091
- const { crudify: crudify7 } = useCrudify();
1091
+ const { crudify: crudify6 } = useCrudify();
1092
1092
  const [newPassword, setNewPassword] = (0, import_react7.useState)("");
1093
1093
  const [confirmPassword, setConfirmPassword] = (0, import_react7.useState)("");
1094
1094
  const [loading, setLoading] = (0, import_react7.useState)(false);
@@ -1168,9 +1168,9 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1168
1168
  setErrors([t("resetPassword.invalidCode")]);
1169
1169
  setValidatingCode(false);
1170
1170
  setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
1171
- }, [searchParams, crudify7, t, onScreenChange]);
1171
+ }, [searchParams, crudify6, t, onScreenChange]);
1172
1172
  (0, import_react7.useEffect)(() => {
1173
- if (crudify7 && pendingValidation && !isValidating) {
1173
+ if (crudify6 && pendingValidation && !isValidating) {
1174
1174
  setIsValidating(true);
1175
1175
  const validateCode = async (emailToValidate, codeToValidate) => {
1176
1176
  try {
@@ -1180,7 +1180,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1180
1180
  data: { email: emailToValidate, codePassword: codeToValidate }
1181
1181
  }
1182
1182
  ];
1183
- const response = await crudify7.transaction(data);
1183
+ const response = await crudify6.transaction(data);
1184
1184
  if (response.data && Array.isArray(response.data)) {
1185
1185
  const validationResult = response.data[0];
1186
1186
  if (validationResult && validationResult.response && validationResult.response.status === "OK") {
@@ -1209,7 +1209,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1209
1209
  };
1210
1210
  validateCode(pendingValidation.email, pendingValidation.code);
1211
1211
  }
1212
- }, [crudify7, pendingValidation, t, onScreenChange]);
1212
+ }, [crudify6, pendingValidation, t, onScreenChange]);
1213
1213
  const validatePassword = (password) => {
1214
1214
  if (password.length < 8) {
1215
1215
  return t("resetPassword.passwordTooShort");
@@ -1217,7 +1217,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1217
1217
  return null;
1218
1218
  };
1219
1219
  const handleSubmit = async () => {
1220
- if (loading || !crudify7) return;
1220
+ if (loading || !crudify6) return;
1221
1221
  setErrors([]);
1222
1222
  setHelperTextNewPassword(null);
1223
1223
  setHelperTextConfirmPassword(null);
@@ -1248,7 +1248,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1248
1248
  data: { email, codePassword: code, newPassword }
1249
1249
  }
1250
1250
  ];
1251
- const response = await crudify7.transaction(data);
1251
+ const response = await crudify6.transaction(data);
1252
1252
  if (response.success) {
1253
1253
  setErrors([]);
1254
1254
  setTimeout(() => {
@@ -1359,7 +1359,7 @@ var import_react8 = require("react");
1359
1359
  var import_material4 = require("@mui/material");
1360
1360
  var import_jsx_runtime7 = require("react/jsx-runtime");
1361
1361
  var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
1362
- const { crudify: crudify7 } = useCrudify();
1362
+ const { crudify: crudify6 } = useCrudify();
1363
1363
  const [code, setCode] = (0, import_react8.useState)("");
1364
1364
  const [loading, setLoading] = (0, import_react8.useState)(false);
1365
1365
  const [errors, setErrors] = (0, import_react8.useState)([]);
@@ -1398,7 +1398,7 @@ var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
1398
1398
  }
1399
1399
  }, [searchParams, onScreenChange]);
1400
1400
  const handleSubmit = async () => {
1401
- if (loading || !crudify7) return;
1401
+ if (loading || !crudify6) return;
1402
1402
  setErrors([]);
1403
1403
  setHelperTextCode(null);
1404
1404
  if (!code) {
@@ -1417,7 +1417,7 @@ var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
1417
1417
  data: { email, codePassword: code }
1418
1418
  }
1419
1419
  ];
1420
- const response = await crudify7.transaction(data);
1420
+ const response = await crudify6.transaction(data);
1421
1421
  if (response.success) {
1422
1422
  onScreenChange?.("resetPassword", { email, code, fromCodeVerification: "true" });
1423
1423
  } else {
@@ -1540,1805 +1540,1831 @@ var CrudifyInitializer = ({ children, fallback }) => {
1540
1540
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children });
1541
1541
  };
1542
1542
 
1543
- // src/components/CrudifyLogin/hooks/useCrudifyLogin.ts
1543
+ // src/providers/SessionProvider.tsx
1544
+ var import_react10 = require("react");
1545
+
1546
+ // src/hooks/useSession.ts
1544
1547
  var import_react9 = require("react");
1548
+
1549
+ // src/core/SessionManager.ts
1545
1550
  var import_crudify_browser2 = __toESM(require("@nocios/crudify-browser"));
1546
- var useCrudifyLogin = (config, _options = {}) => {
1547
- console.log("\u{1F50D} useCrudifyLogin - Input config:", config);
1548
- const finalConfig = (0, import_react9.useMemo)(() => {
1549
- const publicApiKey = config.publicApiKey || import_crudify_browser2.default?.publicApiKey || import_crudify_browser2.default?.apiKey || getCookie("publicApiKey") || null;
1550
- const rawEnv = config.env || getCookie("environment") || "prod";
1551
- const env = ["dev", "stg", "prod"].includes(rawEnv) ? rawEnv : "prod";
1552
- const appName = config.appName || getCookie("appName") || "Crudia";
1553
- const loginActions = config.loginActions || (() => {
1554
- try {
1555
- const cookieValue = getCookie("loginActions");
1556
- return cookieValue ? cookieValue.split(",").map((action) => action.trim()).filter(Boolean) : [];
1557
- } catch {
1558
- return [];
1559
- }
1560
- })();
1561
- console.log("\u{1F50D} useCrudifyLogin - Resolved publicApiKey:", publicApiKey);
1562
- console.log(
1563
- "\u{1F50D} useCrudifyLogin - Sources - props:",
1564
- config.publicApiKey,
1565
- "crudify:",
1566
- import_crudify_browser2.default?.publicApiKey,
1567
- "cookie:",
1568
- getCookie("publicApiKey")
1569
- );
1570
- console.log("\u{1F50D} useCrudifyLogin - Final env:", env, "from rawEnv:", rawEnv);
1571
- const finalResult = {
1572
- publicApiKey,
1573
- env,
1574
- appName,
1575
- loginActions
1576
- };
1577
- console.log("\u{1F50D} useCrudifyLogin - Final config result:", finalResult);
1578
- return finalResult;
1579
- }, [config]);
1580
- return { config: finalConfig };
1581
- };
1582
1551
 
1583
- // src/components/CrudifyLogin/index.tsx
1584
- var import_jsx_runtime9 = require("react/jsx-runtime");
1585
- var CrudifyLoginInternal = ({
1586
- onScreenChange,
1587
- onExternalNavigate,
1588
- onLoginSuccess,
1589
- onError,
1590
- redirectUrl = "/"
1591
- }) => {
1592
- const { t } = useTranslation();
1593
- const { state, setScreen } = useLoginState();
1594
- const handleScreenChange = (screen2, params) => {
1595
- let finalParams = params;
1596
- if (screen2 === "login") {
1597
- finalParams = {};
1598
- } else if (screen2 === "forgotPassword" && !params) {
1599
- finalParams = {};
1600
- }
1601
- setScreen(screen2, finalParams);
1602
- onScreenChange?.(screen2, finalParams);
1603
- };
1604
- const renderCurrentForm = () => {
1605
- const commonProps = {
1606
- onScreenChange: handleScreenChange,
1607
- onExternalNavigate,
1608
- onError,
1609
- redirectUrl
1610
- };
1611
- switch (state.currentScreen) {
1612
- case "forgotPassword":
1613
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ForgotPasswordForm_default, { ...commonProps });
1614
- case "checkCode":
1615
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CheckCodeForm_default, { ...commonProps, searchParams: state.searchParams });
1616
- case "resetPassword":
1617
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1618
- ResetPasswordForm_default,
1619
- {
1620
- ...commonProps,
1621
- searchParams: state.searchParams,
1622
- onResetSuccess: () => {
1623
- handleScreenChange("login");
1624
- }
1625
- }
1626
- );
1627
- default:
1628
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LoginForm_default, { ...commonProps, onLoginSuccess });
1552
+ // src/utils/tokenStorage.ts
1553
+ var import_crypto_js = __toESM(require("crypto-js"));
1554
+ var _TokenStorage = class _TokenStorage {
1555
+ /**
1556
+ * Configurar tipo de almacenamiento
1557
+ */
1558
+ static setStorageType(type) {
1559
+ _TokenStorage.storageType = type;
1560
+ }
1561
+ /**
1562
+ * Verificar si el storage está disponible
1563
+ */
1564
+ static isStorageAvailable(type) {
1565
+ try {
1566
+ const storage = window[type];
1567
+ const testKey = "__storage_test__";
1568
+ storage.setItem(testKey, "test");
1569
+ storage.removeItem(testKey);
1570
+ return true;
1571
+ } catch {
1572
+ return false;
1629
1573
  }
1630
- };
1631
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(CrudifyInitializer, { children: [
1632
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_material6.Box, { sx: { display: "flex", justifyContent: "center", mb: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1633
- "img",
1634
- {
1635
- src: state.config.logo || "/nocios-default.png",
1636
- alt: t("login.logoAlt"),
1637
- style: {
1638
- width: "100%",
1639
- maxWidth: "150px",
1640
- height: "auto"
1641
- },
1642
- onError: (e) => {
1643
- const target = e.target;
1644
- target.src = "/nocios-default.png";
1645
- }
1646
- }
1647
- ) }),
1648
- state.config.appName && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1649
- import_material6.Typography,
1650
- {
1651
- variant: "h6",
1652
- component: "h1",
1653
- sx: {
1654
- textAlign: "center",
1655
- mb: 2,
1656
- color: state.config.colors?.primaryColor || "#1066BA"
1657
- },
1658
- children: state.config.appName
1659
- }
1660
- ),
1661
- renderCurrentForm()
1662
- ] });
1663
- };
1664
- var CrudifyLogin = ({
1665
- translations,
1666
- translationsUrl,
1667
- language = "en",
1668
- config = {},
1669
- initialScreen = "login",
1670
- autoReadFromCookies = true,
1671
- ...props
1672
- }) => {
1673
- const { config: finalConfig } = useCrudifyLogin(config);
1674
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(I18nProvider, { translations, translationsUrl, language, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CrudifyProvider, { config: finalConfig, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LoginStateProvider, { config, initialScreen, autoReadFromCookies, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CrudifyLoginInternal, { config, ...props }) }) }) });
1675
- };
1676
- var CrudifyLogin_default = CrudifyLogin;
1677
-
1678
- // src/components/UserProfile/UserProfileDisplay.tsx
1679
- var import_material7 = require("@mui/material");
1680
- var import_icons_material = require("@mui/icons-material");
1681
-
1682
- // src/hooks/useUserProfile.ts
1683
- var import_react10 = require("react");
1684
- var import_crudify_browser3 = __toESM(require("@nocios/crudify-browser"));
1685
-
1686
- // src/utils/jwtUtils.ts
1687
- var decodeJwtSafely = (token) => {
1688
- try {
1689
- const parts = token.split(".");
1690
- if (parts.length !== 3) {
1691
- console.warn("Invalid JWT format: token must have 3 parts");
1574
+ }
1575
+ /**
1576
+ * Obtener instancia de storage
1577
+ */
1578
+ static getStorage() {
1579
+ if (_TokenStorage.storageType === "none") return null;
1580
+ if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
1581
+ console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
1692
1582
  return null;
1693
1583
  }
1694
- const payload = parts[1];
1695
- const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
1696
- const decodedPayload = JSON.parse(atob(paddedPayload));
1697
- return decodedPayload;
1698
- } catch (error) {
1699
- console.warn("Failed to decode JWT token:", error);
1700
- return null;
1584
+ return window[_TokenStorage.storageType];
1701
1585
  }
1702
- };
1703
- var getCurrentUserEmail = () => {
1704
- try {
1705
- let token = null;
1706
- token = sessionStorage.getItem("authToken");
1707
- console.log("\u{1F50D} getCurrentUserEmail - authToken:", token ? `${token.substring(0, 20)}...` : null);
1708
- if (!token) {
1709
- token = sessionStorage.getItem("token");
1710
- console.log("\u{1F50D} getCurrentUserEmail - token:", token ? `${token.substring(0, 20)}...` : null);
1586
+ /**
1587
+ * Encriptar datos sensibles
1588
+ */
1589
+ static encrypt(data) {
1590
+ try {
1591
+ return import_crypto_js.default.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
1592
+ } catch (error) {
1593
+ console.error("Crudify: Encryption failed", error);
1594
+ return data;
1711
1595
  }
1712
- if (!token) {
1713
- token = localStorage.getItem("authToken") || localStorage.getItem("token");
1714
- console.log("\u{1F50D} getCurrentUserEmail - localStorage:", token ? `${token.substring(0, 20)}...` : null);
1596
+ }
1597
+ /**
1598
+ * Desencriptar datos
1599
+ */
1600
+ static decrypt(encryptedData) {
1601
+ try {
1602
+ const bytes = import_crypto_js.default.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
1603
+ const decrypted = bytes.toString(import_crypto_js.default.enc.Utf8);
1604
+ return decrypted || encryptedData;
1605
+ } catch (error) {
1606
+ console.error("Crudify: Decryption failed", error);
1607
+ return encryptedData;
1715
1608
  }
1716
- if (!token) {
1717
- console.warn("\u{1F50D} getCurrentUserEmail - No token found in any storage");
1718
- return null;
1609
+ }
1610
+ /**
1611
+ * Guardar tokens de forma segura
1612
+ */
1613
+ static saveTokens(tokens) {
1614
+ const storage = _TokenStorage.getStorage();
1615
+ if (!storage) return;
1616
+ try {
1617
+ const tokenData = {
1618
+ accessToken: tokens.accessToken,
1619
+ refreshToken: tokens.refreshToken,
1620
+ expiresAt: tokens.expiresAt,
1621
+ refreshExpiresAt: tokens.refreshExpiresAt,
1622
+ savedAt: Date.now()
1623
+ };
1624
+ const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
1625
+ storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
1626
+ console.debug("Crudify: Tokens saved successfully");
1627
+ } catch (error) {
1628
+ console.error("Crudify: Failed to save tokens", error);
1719
1629
  }
1720
- const payload = decodeJwtSafely(token);
1721
- if (!payload) {
1722
- console.warn("\u{1F50D} getCurrentUserEmail - Failed to decode token");
1630
+ }
1631
+ /**
1632
+ * Obtener tokens guardados
1633
+ */
1634
+ static getTokens() {
1635
+ const storage = _TokenStorage.getStorage();
1636
+ if (!storage) return null;
1637
+ try {
1638
+ const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
1639
+ if (!encrypted) return null;
1640
+ const decrypted = _TokenStorage.decrypt(encrypted);
1641
+ const tokenData = JSON.parse(decrypted);
1642
+ if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
1643
+ console.warn("Crudify: Incomplete token data found, clearing storage");
1644
+ _TokenStorage.clearTokens();
1645
+ return null;
1646
+ }
1647
+ if (Date.now() >= tokenData.refreshExpiresAt) {
1648
+ console.info("Crudify: Refresh token expired, clearing storage");
1649
+ _TokenStorage.clearTokens();
1650
+ return null;
1651
+ }
1652
+ return {
1653
+ accessToken: tokenData.accessToken,
1654
+ refreshToken: tokenData.refreshToken,
1655
+ expiresAt: tokenData.expiresAt,
1656
+ refreshExpiresAt: tokenData.refreshExpiresAt
1657
+ };
1658
+ } catch (error) {
1659
+ console.error("Crudify: Failed to retrieve tokens", error);
1660
+ _TokenStorage.clearTokens();
1723
1661
  return null;
1724
1662
  }
1725
- const email = payload.email || payload["cognito:username"] || null;
1726
- console.log("\u{1F50D} getCurrentUserEmail - Extracted email:", email);
1727
- return email;
1728
- } catch (error) {
1729
- console.warn("Failed to get current user email:", error);
1730
- return null;
1731
- }
1732
- };
1733
- var isTokenExpired = (token) => {
1734
- try {
1735
- const payload = decodeJwtSafely(token);
1736
- if (!payload || !payload.exp) return true;
1737
- const currentTime = Math.floor(Date.now() / 1e3);
1738
- return payload.exp < currentTime;
1739
- } catch {
1740
- return true;
1741
1663
  }
1742
- };
1743
-
1744
- // src/hooks/useUserProfile.ts
1745
- var useUserProfile = (options = {}) => {
1746
- const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
1747
- const [userProfile, setUserProfile] = (0, import_react10.useState)(null);
1748
- const [loading, setLoading] = (0, import_react10.useState)(false);
1749
- const [error, setError] = (0, import_react10.useState)(null);
1750
- const [extendedData, setExtendedData] = (0, import_react10.useState)({});
1751
- const abortControllerRef = (0, import_react10.useRef)(null);
1752
- const mountedRef = (0, import_react10.useRef)(true);
1753
- const requestIdRef = (0, import_react10.useRef)(0);
1754
- const retryCountRef = (0, import_react10.useRef)(0);
1755
- const clearProfile = (0, import_react10.useCallback)(() => {
1756
- setUserProfile(null);
1757
- setError(null);
1758
- setLoading(false);
1759
- setExtendedData({});
1760
- }, []);
1761
- const refreshProfile = (0, import_react10.useCallback)(async () => {
1762
- const userEmail = getCurrentUserEmail();
1763
- if (!userEmail) {
1764
- if (mountedRef.current) {
1765
- setError("No user email available");
1766
- setLoading(false);
1767
- }
1768
- return;
1769
- }
1770
- if (abortControllerRef.current) {
1771
- abortControllerRef.current.abort();
1772
- }
1773
- const abortController = new AbortController();
1774
- abortControllerRef.current = abortController;
1775
- const currentRequestId = ++requestIdRef.current;
1664
+ /**
1665
+ * Limpiar tokens almacenados
1666
+ */
1667
+ static clearTokens() {
1668
+ const storage = _TokenStorage.getStorage();
1669
+ if (!storage) return;
1776
1670
  try {
1777
- if (mountedRef.current) {
1778
- setLoading(true);
1779
- setError(null);
1780
- }
1781
- const response = await import_crudify_browser3.default.readItems("users", {
1782
- filter: { email: userEmail },
1783
- pagination: { limit: 1 }
1784
- });
1785
- if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
1786
- if (response.success && response.data && response.data.length > 0) {
1787
- const userData = response.data[0];
1788
- setUserProfile(userData);
1789
- const additionalData = {
1790
- fullProfile: userData,
1791
- totalFields: Object.keys(userData).length,
1792
- displayData: {
1793
- id: userData.id,
1794
- email: userData.email,
1795
- username: userData.username,
1796
- firstName: userData.firstName,
1797
- lastName: userData.lastName,
1798
- fullName: userData.fullName || `${userData.firstName || ""} ${userData.lastName || ""}`.trim(),
1799
- role: userData.role,
1800
- permissions: userData.permissions || [],
1801
- isActive: userData.isActive,
1802
- lastLogin: userData.lastLogin,
1803
- createdAt: userData.createdAt,
1804
- updatedAt: userData.updatedAt,
1805
- // Include any custom fields
1806
- ...Object.keys(userData).filter(
1807
- (key) => ![
1808
- "id",
1809
- "email",
1810
- "username",
1811
- "firstName",
1812
- "lastName",
1813
- "fullName",
1814
- "role",
1815
- "permissions",
1816
- "isActive",
1817
- "lastLogin",
1818
- "createdAt",
1819
- "updatedAt"
1820
- ].includes(key)
1821
- ).reduce((acc, key) => ({ ...acc, [key]: userData[key] }), {})
1822
- }
1823
- };
1824
- setExtendedData(additionalData);
1825
- setError(null);
1826
- retryCountRef.current = 0;
1827
- } else {
1828
- setError("User profile not found");
1829
- setUserProfile(null);
1830
- setExtendedData({});
1831
- }
1832
- }
1833
- } catch (err) {
1834
- if (currentRequestId === requestIdRef.current && mountedRef.current) {
1835
- const error2 = err;
1836
- if (error2.name === "AbortError") {
1837
- return;
1838
- }
1839
- const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
1840
- if (shouldRetry) {
1841
- retryCountRef.current++;
1842
- setTimeout(() => {
1843
- if (mountedRef.current) {
1844
- refreshProfile();
1845
- }
1846
- }, 1e3 * retryCountRef.current);
1847
- } else {
1848
- setError("Failed to load user profile");
1849
- setUserProfile(null);
1850
- setExtendedData({});
1851
- }
1852
- }
1853
- } finally {
1854
- if (currentRequestId === requestIdRef.current && mountedRef.current) {
1855
- setLoading(false);
1856
- }
1857
- if (abortControllerRef.current === abortController) {
1858
- abortControllerRef.current = null;
1859
- }
1860
- }
1861
- }, [retryOnError, maxRetries]);
1862
- (0, import_react10.useEffect)(() => {
1863
- if (autoFetch) {
1864
- refreshProfile();
1671
+ storage.removeItem(_TokenStorage.TOKEN_KEY);
1672
+ console.debug("Crudify: Tokens cleared from storage");
1673
+ } catch (error) {
1674
+ console.error("Crudify: Failed to clear tokens", error);
1865
1675
  }
1866
- }, [autoFetch, refreshProfile]);
1867
- (0, import_react10.useEffect)(() => {
1868
- mountedRef.current = true;
1869
- return () => {
1870
- mountedRef.current = false;
1871
- if (abortControllerRef.current) {
1872
- abortControllerRef.current.abort();
1873
- abortControllerRef.current = null;
1874
- }
1676
+ }
1677
+ /**
1678
+ * Verificar si hay tokens válidos guardados
1679
+ */
1680
+ static hasValidTokens() {
1681
+ const tokens = _TokenStorage.getTokens();
1682
+ return tokens !== null;
1683
+ }
1684
+ /**
1685
+ * Obtener información de expiración
1686
+ */
1687
+ static getExpirationInfo() {
1688
+ const tokens = _TokenStorage.getTokens();
1689
+ if (!tokens) return null;
1690
+ const now = Date.now();
1691
+ return {
1692
+ accessExpired: now >= tokens.expiresAt,
1693
+ refreshExpired: now >= tokens.refreshExpiresAt,
1694
+ accessExpiresIn: Math.max(0, tokens.expiresAt - now),
1695
+ refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
1875
1696
  };
1876
- }, []);
1877
- return {
1878
- userProfile,
1879
- loading,
1880
- error,
1881
- extendedData,
1882
- refreshProfile,
1883
- clearProfile
1884
- };
1697
+ }
1698
+ /**
1699
+ * Actualizar solo el access token (después de refresh)
1700
+ */
1701
+ static updateAccessToken(newAccessToken, newExpiresAt) {
1702
+ const existingTokens = _TokenStorage.getTokens();
1703
+ if (!existingTokens) {
1704
+ console.warn("Crudify: Cannot update access token, no existing tokens found");
1705
+ return;
1706
+ }
1707
+ _TokenStorage.saveTokens({
1708
+ ...existingTokens,
1709
+ accessToken: newAccessToken,
1710
+ expiresAt: newExpiresAt
1711
+ });
1712
+ }
1885
1713
  };
1714
+ _TokenStorage.TOKEN_KEY = "crudify_tokens";
1715
+ _TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
1716
+ _TokenStorage.storageType = "localStorage";
1717
+ var TokenStorage = _TokenStorage;
1886
1718
 
1887
- // src/components/UserProfile/UserProfileDisplay.tsx
1888
- var import_react11 = require("react");
1889
- var import_jsx_runtime10 = require("react/jsx-runtime");
1890
- var UserProfileDisplay = ({
1891
- showExtendedData = true,
1892
- showProfileCard = true,
1893
- autoRefresh = true
1894
- }) => {
1895
- const { userProfile, loading, error, extendedData, refreshProfile } = useUserProfile({
1896
- autoFetch: autoRefresh,
1897
- retryOnError: true,
1898
- maxRetries: 3
1899
- });
1900
- const [showAllFields, setShowAllFields] = (0, import_react11.useState)(false);
1901
- if (loading) {
1902
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { display: "flex", justifyContent: "center", alignItems: "center", p: 3, children: [
1903
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.CircularProgress, {}),
1904
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Typography, { variant: "body2", sx: { ml: 2 }, children: "Cargando perfil de usuario..." })
1905
- ] });
1719
+ // src/core/SessionManager.ts
1720
+ var SessionManager = class _SessionManager {
1721
+ constructor() {
1722
+ this.config = {};
1723
+ this.initialized = false;
1906
1724
  }
1907
- if (error) {
1908
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1909
- import_material7.Alert,
1910
- {
1911
- severity: "error",
1912
- action: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.IconButton, { color: "inherit", size: "small", onClick: refreshProfile, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Typography, { variant: "caption", children: "Reintentar" }) }),
1913
- children: [
1914
- "Error al cargar el perfil: ",
1915
- error
1916
- ]
1917
- }
1918
- );
1725
+ static getInstance() {
1726
+ if (!_SessionManager.instance) {
1727
+ _SessionManager.instance = new _SessionManager();
1728
+ }
1729
+ return _SessionManager.instance;
1919
1730
  }
1920
- if (!userProfile) {
1921
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Alert, { severity: "warning", children: "No se encontr\xF3 informaci\xF3n del usuario" });
1731
+ /**
1732
+ * Inicializar el SessionManager
1733
+ */
1734
+ async initialize(config = {}) {
1735
+ if (this.initialized) {
1736
+ console.warn("SessionManager: Already initialized");
1737
+ return;
1738
+ }
1739
+ this.config = {
1740
+ storageType: "localStorage",
1741
+ autoRestore: true,
1742
+ enableLogging: false,
1743
+ ...config
1744
+ };
1745
+ TokenStorage.setStorageType(this.config.storageType || "localStorage");
1746
+ if (this.config.enableLogging) {
1747
+ }
1748
+ if (this.config.autoRestore) {
1749
+ await this.restoreSession();
1750
+ }
1751
+ this.initialized = true;
1752
+ this.log("SessionManager initialized successfully");
1922
1753
  }
1923
- const displayData = extendedData?.displayData || {};
1924
- const totalFields = extendedData?.totalFields || 0;
1925
- const formatDate = (dateString) => {
1926
- if (!dateString) return "No disponible";
1754
+ /**
1755
+ * Login con persistencia automática
1756
+ */
1757
+ async login(email, password) {
1927
1758
  try {
1928
- return new Date(dateString).toLocaleString("es-ES", {
1929
- year: "numeric",
1930
- month: "short",
1931
- day: "numeric",
1932
- hour: "2-digit",
1933
- minute: "2-digit"
1934
- });
1935
- } catch {
1936
- return dateString;
1937
- }
1938
- };
1939
- const renderFieldValue = (key, value) => {
1940
- if (value === null || value === void 0) return "No disponible";
1941
- if (typeof value === "boolean") return value ? "S\xED" : "No";
1942
- if (Array.isArray(value)) return value.length > 0 ? value.join(", ") : "Ninguno";
1943
- if (typeof value === "object") return JSON.stringify(value, null, 2);
1944
- return String(value);
1945
- };
1946
- const basicFields = [
1947
- { key: "id", label: "ID", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Badge, {}) },
1948
- { key: "email", label: "Email", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Email, {}) },
1949
- { key: "username", label: "Usuario", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Person, {}) },
1950
- { key: "fullName", label: "Nombre completo", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.AccountCircle, {}) },
1951
- { key: "role", label: "Rol", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Security, {}) }
1952
- ];
1953
- const extendedFields = [
1954
- { key: "firstName", label: "Nombre" },
1955
- { key: "lastName", label: "Apellido" },
1956
- { key: "isActive", label: "Activo" },
1957
- { key: "lastLogin", label: "\xDAltimo login" },
1958
- { key: "createdAt", label: "Creado" },
1959
- { key: "updatedAt", label: "Actualizado" }
1960
- ];
1961
- const knownFields = [...basicFields.map((f) => f.key), ...extendedFields.map((f) => f.key), "permissions"];
1962
- const customFields = Object.keys(displayData).filter((key) => !knownFields.includes(key)).map((key) => ({ key, label: key }));
1963
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { children: [
1964
- showProfileCard && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Card, { sx: { mb: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.CardContent, { children: [
1965
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { display: "flex", alignItems: "center", mb: 2, children: [
1966
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1967
- import_material7.Avatar,
1968
- {
1969
- src: displayData.avatar,
1970
- sx: { width: 56, height: 56, mr: 2 },
1971
- children: displayData.fullName?.[0] || displayData.username?.[0] || displayData.email?.[0]
1759
+ this.log("Attempting login...");
1760
+ const response = await import_crudify_browser2.default.login(email, password);
1761
+ if (!response.success) {
1762
+ this.log("Login failed:", response.errors);
1763
+ return {
1764
+ success: false,
1765
+ error: this.formatError(response.errors)
1766
+ };
1767
+ }
1768
+ const tokens = {
1769
+ accessToken: response.data.token,
1770
+ refreshToken: response.data.refreshToken,
1771
+ expiresAt: response.data.expiresAt,
1772
+ refreshExpiresAt: response.data.refreshExpiresAt
1773
+ };
1774
+ TokenStorage.saveTokens(tokens);
1775
+ this.log("Login successful, tokens saved");
1776
+ this.config.onLoginSuccess?.(tokens);
1777
+ return {
1778
+ success: true,
1779
+ tokens
1780
+ };
1781
+ } catch (error) {
1782
+ this.log("Login error:", error);
1783
+ return {
1784
+ success: false,
1785
+ error: error instanceof Error ? error.message : "Unknown error"
1786
+ };
1787
+ }
1788
+ }
1789
+ /**
1790
+ * Logout con limpieza de tokens
1791
+ */
1792
+ async logout() {
1793
+ try {
1794
+ this.log("Logging out...");
1795
+ await import_crudify_browser2.default.logout();
1796
+ TokenStorage.clearTokens();
1797
+ this.log("Logout successful");
1798
+ this.config.onLogout?.();
1799
+ } catch (error) {
1800
+ this.log("Logout error:", error);
1801
+ TokenStorage.clearTokens();
1802
+ }
1803
+ }
1804
+ /**
1805
+ * Restaurar sesión desde storage
1806
+ */
1807
+ async restoreSession() {
1808
+ try {
1809
+ this.log("Attempting to restore session...");
1810
+ const savedTokens = TokenStorage.getTokens();
1811
+ if (!savedTokens) {
1812
+ this.log("No valid tokens found in storage");
1813
+ return false;
1814
+ }
1815
+ import_crudify_browser2.default.setTokens({
1816
+ accessToken: savedTokens.accessToken,
1817
+ refreshToken: savedTokens.refreshToken,
1818
+ expiresAt: savedTokens.expiresAt,
1819
+ refreshExpiresAt: savedTokens.refreshExpiresAt
1820
+ });
1821
+ this.log("Session restored successfully");
1822
+ this.config.onSessionRestored?.(savedTokens);
1823
+ return true;
1824
+ } catch (error) {
1825
+ this.log("Session restore error:", error);
1826
+ TokenStorage.clearTokens();
1827
+ return false;
1828
+ }
1829
+ }
1830
+ /**
1831
+ * Verificar si el usuario está autenticado
1832
+ */
1833
+ isAuthenticated() {
1834
+ return import_crudify_browser2.default.isLogin() || TokenStorage.hasValidTokens();
1835
+ }
1836
+ /**
1837
+ * Obtener información de tokens actuales
1838
+ */
1839
+ getTokenInfo() {
1840
+ const crudifyTokens = import_crudify_browser2.default.getTokenData();
1841
+ const storageInfo = TokenStorage.getExpirationInfo();
1842
+ return {
1843
+ isLoggedIn: this.isAuthenticated(),
1844
+ crudifyTokens,
1845
+ storageInfo,
1846
+ hasValidTokens: TokenStorage.hasValidTokens()
1847
+ };
1848
+ }
1849
+ /**
1850
+ * Refrescar tokens manualmente
1851
+ */
1852
+ async refreshTokens() {
1853
+ try {
1854
+ this.log("Manually refreshing tokens...");
1855
+ const response = await import_crudify_browser2.default.refreshAccessToken();
1856
+ if (!response.success) {
1857
+ this.log("Token refresh failed:", response.errors);
1858
+ TokenStorage.clearTokens();
1859
+ this.config.onSessionExpired?.();
1860
+ return false;
1861
+ }
1862
+ const newTokens = {
1863
+ accessToken: response.data.token,
1864
+ refreshToken: response.data.refreshToken,
1865
+ expiresAt: response.data.expiresAt,
1866
+ refreshExpiresAt: response.data.refreshExpiresAt
1867
+ };
1868
+ TokenStorage.saveTokens(newTokens);
1869
+ this.log("Tokens refreshed and saved successfully");
1870
+ return true;
1871
+ } catch (error) {
1872
+ this.log("Token refresh error:", error);
1873
+ TokenStorage.clearTokens();
1874
+ this.config.onSessionExpired?.();
1875
+ return false;
1876
+ }
1877
+ }
1878
+ /**
1879
+ * Configurar interceptor de respuesta para manejo automático de errores
1880
+ */
1881
+ setupResponseInterceptor() {
1882
+ import_crudify_browser2.default.setResponseInterceptor(async (response) => {
1883
+ if (response.errors) {
1884
+ const hasAuthError = response.errors.some(
1885
+ (error) => error.message?.includes("Unauthorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED"
1886
+ );
1887
+ if (hasAuthError && TokenStorage.hasValidTokens()) {
1888
+ this.log("Auth error detected, attempting token refresh...");
1889
+ const refreshSuccess = await this.refreshTokens();
1890
+ if (!refreshSuccess) {
1891
+ this.log("Session expired, triggering callback");
1892
+ this.config.onSessionExpired?.();
1972
1893
  }
1973
- ),
1974
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { children: [
1975
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Typography, { variant: "h6", children: displayData.fullName || displayData.username || displayData.email }),
1976
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Typography, { variant: "body2", color: "text.secondary", children: displayData.role || "Usuario" }),
1977
- displayData.isActive !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1978
- import_material7.Chip,
1979
- {
1980
- label: displayData.isActive ? "Activo" : "Inactivo",
1981
- color: displayData.isActive ? "success" : "error",
1982
- size: "small",
1983
- sx: { mt: 0.5 }
1984
- }
1985
- )
1986
- ] })
1987
- ] }),
1988
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Box, { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: 2, children: basicFields.map(
1989
- ({ key, label, icon }) => displayData[key] ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { display: "flex", alignItems: "center", children: [
1990
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Box, { sx: { mr: 1, color: "text.secondary" }, children: icon }),
1991
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { children: [
1992
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Typography, { variant: "caption", color: "text.secondary", children: label }),
1993
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Typography, { variant: "body2", children: renderFieldValue(key, displayData[key]) })
1994
- ] })
1995
- ] }, key) : null
1996
- ) }),
1997
- displayData.permissions && Array.isArray(displayData.permissions) && displayData.permissions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { mt: 2, children: [
1998
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Typography, { variant: "caption", color: "text.secondary", display: "block", children: "Permisos" }),
1999
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { display: "flex", flexWrap: "wrap", gap: 0.5, mt: 0.5, children: [
2000
- displayData.permissions.slice(0, 5).map((permission, index) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Chip, { label: permission, size: "small", variant: "outlined" }, index)),
2001
- displayData.permissions.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Chip, { label: `+${displayData.permissions.length - 5} m\xE1s`, size: "small" })
2002
- ] })
2003
- ] })
2004
- ] }) }),
2005
- showExtendedData && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.CardContent, { children: [
2006
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, children: [
2007
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Typography, { variant: "h6", display: "flex", alignItems: "center", children: [
2008
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Info, { sx: { mr: 1 } }),
2009
- "Informaci\xF3n Detallada"
2010
- ] }),
2011
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Chip, { label: `${totalFields} campos totales`, size: "small" })
2012
- ] }),
2013
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.List, { dense: true, children: [
2014
- extendedFields.map(({ key, label }) => displayData[key] !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.ListItem, { divider: true, children: [
2015
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.ListItemIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Schedule, { fontSize: "small" }) }),
2016
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2017
- import_material7.ListItemText,
2018
- {
2019
- primary: label,
2020
- secondary: key.includes("At") || key.includes("Login") ? formatDate(displayData[key]) : renderFieldValue(key, displayData[key])
2021
- }
2022
- )
2023
- ] }, key)),
2024
- customFields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2025
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Divider, { sx: { my: 1 } }),
2026
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.ListItem, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2027
- import_material7.ListItemText,
2028
- {
2029
- primary: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", children: [
2030
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Typography, { variant: "subtitle2", children: [
2031
- "Campos Personalizados (",
2032
- customFields.length,
2033
- ")"
2034
- ] }),
2035
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.IconButton, { size: "small", onClick: () => setShowAllFields(!showAllFields), children: showAllFields ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.ExpandLess, {}) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.ExpandMore, {}) })
2036
- ] })
2037
- }
2038
- ) }),
2039
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Collapse, { in: showAllFields, children: customFields.map(({ key, label }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.ListItem, { sx: { pl: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2040
- import_material7.ListItemText,
2041
- {
2042
- primary: label,
2043
- secondary: renderFieldValue(key, displayData[key])
2044
- }
2045
- ) }, key)) })
2046
- ] })
2047
- ] }),
2048
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Box, { mt: 2, display: "flex", justifyContent: "space-between", alignItems: "center", children: [
2049
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_material7.Typography, { variant: "caption", color: "text.secondary", children: [
2050
- "\xDAltima actualizaci\xF3n: ",
2051
- formatDate(displayData.updatedAt)
2052
- ] }),
2053
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.IconButton, { size: "small", onClick: refreshProfile, disabled: loading, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material7.Typography, { variant: "caption", children: "Actualizar" }) })
2054
- ] })
2055
- ] }) })
2056
- ] });
2057
- };
2058
- var UserProfileDisplay_default = UserProfileDisplay;
2059
-
2060
- // src/components/PublicPolicies/Policies.tsx
2061
- var import_react14 = require("react");
2062
- var import_react_i18next3 = require("react-i18next");
2063
- var import_material10 = require("@mui/material");
2064
- var import_icons_material4 = require("@mui/icons-material");
2065
-
2066
- // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
2067
- var import_react13 = require("react");
2068
- var import_react_i18next2 = require("react-i18next");
2069
- var import_material9 = require("@mui/material");
2070
- var import_icons_material3 = require("@mui/icons-material");
2071
-
2072
- // src/components/PublicPolicies/FieldSelector/FieldSelector.tsx
2073
- var import_react12 = require("react");
2074
- var import_react_i18next = require("react-i18next");
2075
- var import_material8 = require("@mui/material");
2076
- var import_icons_material2 = require("@mui/icons-material");
2077
- var import_jsx_runtime11 = require("react/jsx-runtime");
2078
- var FieldSelector = ({
2079
- value,
2080
- onChange,
2081
- availableFields,
2082
- error,
2083
- disabled = false
2084
- }) => {
2085
- const { t } = (0, import_react_i18next.useTranslation)();
2086
- const [mode, setMode] = (0, import_react12.useState)("custom");
2087
- const isUpdatingRef = (0, import_react12.useRef)(false);
2088
- (0, import_react12.useEffect)(() => {
2089
- const current = value || { allow: [], owner_allow: [], deny: [] };
2090
- const all = new Set(availableFields);
2091
- const allow = (current.allow || []).filter((f) => all.has(f));
2092
- const owner = (current.owner_allow || []).filter((f) => all.has(f));
2093
- const deny = (current.deny || []).filter((f) => all.has(f));
2094
- availableFields.forEach((f) => {
2095
- if (!allow.includes(f) && !owner.includes(f) && !deny.includes(f)) deny.push(f);
1894
+ }
1895
+ }
1896
+ return response;
2096
1897
  });
2097
- const normalized = { allow, owner_allow: owner, deny };
2098
- if (JSON.stringify(normalized) !== JSON.stringify(current)) {
2099
- onChange(normalized);
1898
+ this.log("Response interceptor configured");
1899
+ }
1900
+ /**
1901
+ * Limpiar sesión completamente
1902
+ */
1903
+ clearSession() {
1904
+ TokenStorage.clearTokens();
1905
+ import_crudify_browser2.default.logout();
1906
+ this.log("Session cleared completely");
1907
+ }
1908
+ // Métodos privados
1909
+ log(message, ...args) {
1910
+ if (this.config.enableLogging) {
1911
+ console.log(`[SessionManager] ${message}`, ...args);
2100
1912
  }
2101
- if (allow.length === availableFields.length) setMode("all");
2102
- else if (deny.length === availableFields.length) setMode("none");
2103
- else setMode("custom");
2104
- }, [availableFields, value]);
2105
- const setAllAllow = () => {
2106
- isUpdatingRef.current = true;
2107
- onChange({ allow: [...availableFields], owner_allow: [], deny: [] });
2108
- setMode("all");
2109
- setTimeout(() => {
2110
- isUpdatingRef.current = false;
2111
- }, 0);
2112
- };
2113
- const setAllDeny = () => {
2114
- isUpdatingRef.current = true;
2115
- onChange({ allow: [], owner_allow: [], deny: [...availableFields] });
2116
- setMode("none");
2117
- setTimeout(() => {
2118
- isUpdatingRef.current = false;
2119
- }, 0);
2120
- };
2121
- const getFieldState = (fieldName) => {
2122
- if (value?.allow?.includes(fieldName)) return "allow";
2123
- if (value?.owner_allow?.includes(fieldName)) return "owner_allow";
2124
- return "deny";
2125
- };
2126
- const setFieldState = (fieldName, state) => {
2127
- isUpdatingRef.current = true;
2128
- const allow = new Set(value?.allow || []);
2129
- const owner = new Set(value?.owner_allow || []);
2130
- const deny = new Set(value?.deny || []);
2131
- allow.delete(fieldName);
2132
- owner.delete(fieldName);
2133
- deny.delete(fieldName);
2134
- if (state === "allow") allow.add(fieldName);
2135
- if (state === "owner_allow") owner.add(fieldName);
2136
- if (state === "deny") deny.add(fieldName);
2137
- onChange({ allow: Array.from(allow), owner_allow: Array.from(owner), deny: Array.from(deny) });
2138
- setMode("custom");
2139
- setTimeout(() => {
2140
- isUpdatingRef.current = false;
2141
- }, 0);
2142
- };
2143
- if (availableFields.length === 0) {
2144
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material8.Box, { children: [
2145
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
2146
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: t("modules.form.publicPolicies.fields.conditions.noFieldsAvailable") }),
2147
- error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material8.FormHelperText, { error: true, sx: { mt: 1 }, children: error })
2148
- ] });
2149
1913
  }
2150
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material8.Box, { children: [
2151
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
2152
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material8.Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
2153
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2154
- import_material8.Button,
2155
- {
2156
- variant: mode === "all" ? "contained" : "outlined",
2157
- startIcon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material2.SelectAll, {}),
2158
- onClick: setAllAllow,
2159
- disabled,
2160
- size: "small",
2161
- sx: {
2162
- minWidth: 120,
2163
- ...mode === "all" && {
2164
- backgroundColor: "#16a34a",
2165
- "&:hover": { backgroundColor: "#15803d" }
2166
- }
2167
- },
2168
- children: t("modules.form.publicPolicies.fields.conditions.allFields")
2169
- }
2170
- ),
2171
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2172
- import_material8.Button,
2173
- {
2174
- variant: mode === "none" ? "contained" : "outlined",
2175
- startIcon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material2.ClearAll, {}),
2176
- onClick: setAllDeny,
2177
- disabled,
2178
- size: "small",
2179
- sx: {
2180
- minWidth: 120,
2181
- ...mode === "none" && {
2182
- backgroundColor: "#cf222e",
2183
- "&:hover": { backgroundColor: "#bc1f2c" }
2184
- }
2185
- },
2186
- children: t("modules.form.publicPolicies.fields.conditions.noFields")
2187
- }
2188
- )
2189
- ] }),
2190
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material8.Box, { sx: { p: 2, border: "1px solid #d1d9e0", borderRadius: 1, backgroundColor: "#f6f8fa" }, children: [
2191
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.help") }),
2192
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material8.Stack, { spacing: 1, children: availableFields.map((fieldName) => {
2193
- const fieldState = getFieldState(fieldName);
2194
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material8.Stack, { direction: "row", spacing: 1, alignItems: "center", children: [
2195
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material8.Typography, { variant: "body2", sx: { minWidth: 100, fontFamily: "monospace" }, children: fieldName }),
2196
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material8.ToggleButtonGroup, { value: fieldState, exclusive: true, size: "small", children: [
2197
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2198
- import_material8.ToggleButton,
2199
- {
2200
- value: "allow",
2201
- onClick: () => setFieldState(fieldName, "allow"),
2202
- disabled,
2203
- sx: {
2204
- px: 2,
2205
- color: fieldState === "allow" ? "#ffffff" : "#6b7280",
2206
- backgroundColor: fieldState === "allow" ? "#16a34a" : "#f3f4f6",
2207
- borderColor: fieldState === "allow" ? "#16a34a" : "#d1d5db",
2208
- "&:hover": {
2209
- backgroundColor: fieldState === "allow" ? "#15803d" : "#e5e7eb",
2210
- borderColor: fieldState === "allow" ? "#15803d" : "#9ca3af"
2211
- },
2212
- "&.Mui-selected": {
2213
- backgroundColor: "#16a34a",
2214
- color: "#ffffff",
2215
- "&:hover": {
2216
- backgroundColor: "#15803d"
2217
- }
2218
- }
2219
- },
2220
- children: [
2221
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material2.CheckCircle, { sx: { fontSize: 16, mr: 0.5 } }),
2222
- t("modules.form.publicPolicies.fields.conditions.states.allow")
2223
- ]
2224
- }
2225
- ),
2226
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2227
- import_material8.ToggleButton,
2228
- {
2229
- value: "owner_allow",
2230
- onClick: () => setFieldState(fieldName, "owner_allow"),
2231
- disabled,
2232
- sx: {
2233
- px: 2,
2234
- color: fieldState === "owner_allow" ? "#ffffff" : "#6b7280",
2235
- backgroundColor: fieldState === "owner_allow" ? "#0ea5e9" : "#f3f4f6",
2236
- borderColor: fieldState === "owner_allow" ? "#0ea5e9" : "#d1d5db",
2237
- "&:hover": {
2238
- backgroundColor: fieldState === "owner_allow" ? "#0284c7" : "#e5e7eb",
2239
- borderColor: fieldState === "owner_allow" ? "#0284c7" : "#9ca3af"
2240
- },
2241
- "&.Mui-selected": {
2242
- backgroundColor: "#0ea5e9",
2243
- color: "#ffffff",
2244
- "&:hover": {
2245
- backgroundColor: "#0284c7"
2246
- }
2247
- }
2248
- },
2249
- children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
2250
- }
2251
- ),
2252
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2253
- import_material8.ToggleButton,
2254
- {
2255
- value: "deny",
2256
- onClick: () => setFieldState(fieldName, "deny"),
2257
- disabled,
2258
- sx: {
2259
- px: 2,
2260
- color: fieldState === "deny" ? "#ffffff" : "#6b7280",
2261
- backgroundColor: fieldState === "deny" ? "#dc2626" : "#f3f4f6",
2262
- borderColor: fieldState === "deny" ? "#dc2626" : "#d1d5db",
2263
- "&:hover": {
2264
- backgroundColor: fieldState === "deny" ? "#b91c1c" : "#e5e7eb",
2265
- borderColor: fieldState === "deny" ? "#b91c1c" : "#9ca3af"
2266
- },
2267
- "&.Mui-selected": {
2268
- backgroundColor: "#dc2626",
2269
- color: "#ffffff",
2270
- "&:hover": {
2271
- backgroundColor: "#b91c1c"
2272
- }
2273
- }
2274
- },
2275
- children: [
2276
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material2.Cancel, { sx: { fontSize: 16, mr: 0.5 } }),
2277
- t("modules.form.publicPolicies.fields.conditions.states.deny")
2278
- ]
2279
- }
2280
- )
2281
- ] })
2282
- ] }, fieldName);
2283
- }) })
2284
- ] }),
2285
- error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material8.FormHelperText, { error: true, sx: { mt: 1 }, children: error })
2286
- ] });
1914
+ formatError(errors) {
1915
+ if (!errors) return "Unknown error";
1916
+ if (typeof errors === "string") return errors;
1917
+ if (typeof errors === "object") {
1918
+ const errorMessages = Object.values(errors).flat();
1919
+ return errorMessages.join(", ");
1920
+ }
1921
+ return "Authentication failed";
1922
+ }
2287
1923
  };
2288
- var FieldSelector_default = FieldSelector;
2289
-
2290
- // src/components/PublicPolicies/constants.ts
2291
- var POLICY_ACTIONS = ["create", "read", "update", "delete"];
2292
- var PREFERRED_POLICY_ORDER = [
2293
- "create",
2294
- "read",
2295
- "update",
2296
- "delete"
2297
- ];
2298
1924
 
2299
- // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
2300
- var import_jsx_runtime12 = require("react/jsx-runtime");
2301
- var PolicyItem = (0, import_react13.forwardRef)(({
2302
- policy,
2303
- onChange,
2304
- onRemove,
2305
- availableFields,
2306
- isSubmitting = false,
2307
- usedActions,
2308
- error
2309
- }, ref) => {
2310
- const { t } = (0, import_react_i18next2.useTranslation)();
2311
- const takenActions = new Set(Array.from(usedActions || []));
2312
- takenActions.delete(policy.action);
2313
- const actionOptions = POLICY_ACTIONS.map((a) => ({
2314
- value: a,
2315
- label: t(`modules.form.publicPolicies.fields.action.options.${a}`)
2316
- }));
2317
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2318
- import_material9.Paper,
2319
- {
2320
- ref,
2321
- sx: {
2322
- p: 3,
2323
- border: "1px solid #d1d9e0",
2324
- borderRadius: 2,
2325
- position: "relative",
2326
- backgroundColor: "#ffffff"
2327
- },
2328
- children: [
2329
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 3 }, children: [
2330
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2331
- import_material9.Typography,
2332
- {
2333
- variant: "subtitle1",
2334
- sx: {
2335
- fontWeight: 600,
2336
- color: "#111418",
2337
- fontSize: "1rem"
2338
- },
2339
- children: t("modules.form.publicPolicies.policyTitle")
2340
- }
2341
- ),
2342
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2343
- import_material9.IconButton,
2344
- {
2345
- onClick: onRemove,
2346
- size: "small",
2347
- disabled: isSubmitting,
2348
- "aria-label": t("modules.form.publicPolicies.removePolicy"),
2349
- sx: {
2350
- color: "#656d76",
2351
- "&:hover": {
2352
- color: "#cf222e",
2353
- backgroundColor: "rgba(207, 34, 46, 0.1)"
2354
- }
2355
- },
2356
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons_material3.Delete, {})
2357
- }
2358
- )
2359
- ] }),
2360
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Stack, { spacing: 3, children: [
2361
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material9.Stack, { direction: { xs: "column", md: "row" }, spacing: 2, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material9.Box, { sx: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.FormControl, { fullWidth: true, children: [
2362
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material9.InputLabel, { children: t("modules.form.publicPolicies.fields.action.label") }),
2363
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2364
- import_material9.Select,
2365
- {
2366
- value: policy.action,
2367
- label: t("modules.form.publicPolicies.fields.action.label"),
2368
- disabled: isSubmitting,
2369
- onChange: (e) => {
2370
- const newAction = e.target.value;
2371
- const next = { ...policy, action: newAction };
2372
- if (newAction === "delete") {
2373
- next.permission = "deny";
2374
- delete next.fields;
2375
- } else {
2376
- next.fields = { allow: [], owner_allow: [], deny: availableFields };
2377
- delete next.permission;
2378
- }
2379
- onChange(next);
2380
- },
2381
- sx: {
2382
- backgroundColor: "#ffffff",
2383
- "&:hover .MuiOutlinedInput-notchedOutline": {
2384
- borderColor: "#8c959f"
2385
- },
2386
- "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
2387
- borderColor: "#0969da",
2388
- borderWidth: 2
2389
- }
2390
- },
2391
- children: actionOptions.map((option) => {
2392
- const disabledOption = takenActions.has(option.value);
2393
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material9.MenuItem, { value: option.value, disabled: disabledOption, children: option.label }, option.value);
2394
- })
2395
- }
2396
- ),
2397
- error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material9.FormHelperText, { error: true, children: error })
2398
- ] }) }) }),
2399
- policy.action === "delete" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { children: [
2400
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material9.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
2401
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
2402
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2403
- import_material9.Button,
2404
- {
2405
- variant: policy.permission === "*" ? "contained" : "outlined",
2406
- startIcon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons_material3.SelectAll, {}),
2407
- onClick: () => onChange({ ...policy, permission: "*" }),
2408
- disabled: isSubmitting,
2409
- size: "small",
2410
- sx: {
2411
- minWidth: 140,
2412
- whiteSpace: "nowrap",
2413
- ...policy.permission === "*" && {
2414
- backgroundColor: "#16a34a",
2415
- "&:hover": { backgroundColor: "#15803d" }
2416
- }
2417
- },
2418
- children: t("modules.form.publicPolicies.fields.conditions.allFields")
2419
- }
2420
- ),
2421
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2422
- import_material9.Button,
2423
- {
2424
- variant: policy.permission === "owner" ? "contained" : "outlined",
2425
- onClick: () => onChange({ ...policy, permission: "owner" }),
2426
- disabled: isSubmitting,
2427
- size: "small",
2428
- sx: {
2429
- minWidth: 140,
2430
- whiteSpace: "nowrap",
2431
- ...policy.permission === "owner" && {
2432
- backgroundColor: "#0ea5e9",
2433
- "&:hover": { backgroundColor: "#0284c7" }
2434
- }
2435
- },
2436
- children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
2437
- }
2438
- ),
2439
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2440
- import_material9.Button,
2441
- {
2442
- variant: policy.permission === "deny" ? "contained" : "outlined",
2443
- startIcon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons_material3.ClearAll, {}),
2444
- onClick: () => onChange({ ...policy, permission: "deny" }),
2445
- disabled: isSubmitting,
2446
- size: "small",
2447
- sx: {
2448
- minWidth: 140,
2449
- whiteSpace: "nowrap",
2450
- ...policy.permission === "deny" && {
2451
- backgroundColor: "#cf222e",
2452
- "&:hover": { backgroundColor: "#bc1f2c" }
2453
- }
2454
- },
2455
- children: t("modules.form.publicPolicies.fields.conditions.noFields")
2456
- }
2457
- )
2458
- ] })
2459
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2460
- FieldSelector_default,
2461
- {
2462
- value: policy.fields || { allow: [], owner_allow: [], deny: [] },
2463
- onChange: (nextFields) => onChange({ ...policy, fields: nextFields }),
2464
- availableFields,
2465
- disabled: isSubmitting
2466
- }
2467
- ),
2468
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material9.Paper, { variant: "outlined", sx: { p: 2, backgroundColor: "#f9fafb" }, children: policy.action === "delete" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
2469
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { component: "span", sx: {
2470
- color: policy.permission === "*" ? "#16a34a" : policy.permission === "owner" ? "#0ea5e9" : "#dc2626"
2471
- }, children: [
2472
- t("modules.form.publicPolicies.fields.conditions.states.allow"),
2473
- ":"
2474
- ] }),
2475
- " ",
2476
- policy.permission || "-"
2477
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Stack, { spacing: 0.5, divider: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material9.Divider, { sx: { borderColor: "#e5e7eb" } }), children: [
2478
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
2479
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { component: "span", sx: { color: "#16a34a" }, children: [
2480
- t("modules.form.publicPolicies.fields.conditions.states.allow"),
2481
- ":"
2482
- ] }),
2483
- " ",
2484
- (policy?.fields?.allow || []).join(", ") || "-"
2485
- ] }),
2486
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
2487
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { component: "span", sx: { color: "#0ea5e9" }, children: [
2488
- t("modules.form.publicPolicies.fields.conditions.states.ownerAllow"),
2489
- ":"
2490
- ] }),
2491
- " ",
2492
- (policy?.fields?.owner_allow || []).join(", ") || "-"
2493
- ] }),
2494
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
2495
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material9.Box, { component: "span", sx: { color: "#dc2626" }, children: [
2496
- t("modules.form.publicPolicies.fields.conditions.states.deny"),
2497
- ":"
2498
- ] }),
2499
- " ",
2500
- (policy?.fields?.deny || []).join(", ") || "-"
2501
- ] })
2502
- ] }) })
2503
- ] })
2504
- ]
2505
- }
2506
- );
2507
- });
2508
- var PolicyItem_default = PolicyItem;
2509
-
2510
- // src/components/PublicPolicies/Policies.tsx
2511
- var import_jsx_runtime13 = require("react/jsx-runtime");
2512
- var generateId = () => {
2513
- const c = globalThis?.crypto;
2514
- if (c && typeof c.randomUUID === "function") return c.randomUUID();
2515
- return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
2516
- };
2517
- var Policies = ({
2518
- policies,
2519
- onChange,
2520
- availableFields,
2521
- errors,
2522
- isSubmitting = false
2523
- }) => {
2524
- const { t } = (0, import_react_i18next3.useTranslation)();
2525
- const policyRefs = (0, import_react14.useRef)({});
2526
- const takenActions = new Set((policies || []).map((p) => p.action).filter(Boolean));
2527
- const remainingActions = PREFERRED_POLICY_ORDER.filter((a) => !takenActions.has(a));
2528
- const canAddPolicy = remainingActions.length > 0;
2529
- const addPolicy = () => {
2530
- const defaultAction = remainingActions[0] || "create";
2531
- const newPolicy = {
2532
- id: generateId(),
2533
- action: defaultAction
2534
- };
2535
- if (defaultAction === "delete") {
2536
- newPolicy.permission = "deny";
2537
- } else {
2538
- newPolicy.fields = {
2539
- allow: [],
2540
- owner_allow: [],
2541
- deny: availableFields
1925
+ // src/hooks/useSession.ts
1926
+ function useSession(options = {}) {
1927
+ const [state, setState] = (0, import_react9.useState)({
1928
+ isAuthenticated: false,
1929
+ isLoading: true,
1930
+ isInitialized: false,
1931
+ tokens: null,
1932
+ error: null
1933
+ });
1934
+ const sessionManager = SessionManager.getInstance();
1935
+ const initialize = (0, import_react9.useCallback)(async () => {
1936
+ try {
1937
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
1938
+ const config = {
1939
+ autoRestore: options.autoRestore ?? true,
1940
+ enableLogging: options.enableLogging ?? false,
1941
+ onSessionExpired: () => {
1942
+ setState((prev) => ({
1943
+ ...prev,
1944
+ isAuthenticated: false,
1945
+ tokens: null,
1946
+ error: "Session expired"
1947
+ }));
1948
+ options.onSessionExpired?.();
1949
+ },
1950
+ onSessionRestored: (tokens) => {
1951
+ setState((prev) => ({
1952
+ ...prev,
1953
+ isAuthenticated: true,
1954
+ tokens,
1955
+ error: null
1956
+ }));
1957
+ options.onSessionRestored?.(tokens);
1958
+ },
1959
+ onLoginSuccess: (tokens) => {
1960
+ setState((prev) => ({
1961
+ ...prev,
1962
+ isAuthenticated: true,
1963
+ tokens,
1964
+ error: null
1965
+ }));
1966
+ },
1967
+ onLogout: () => {
1968
+ setState((prev) => ({
1969
+ ...prev,
1970
+ isAuthenticated: false,
1971
+ tokens: null,
1972
+ error: null
1973
+ }));
1974
+ }
2542
1975
  };
1976
+ await sessionManager.initialize(config);
1977
+ sessionManager.setupResponseInterceptor();
1978
+ const isAuth = sessionManager.isAuthenticated();
1979
+ const tokenInfo = sessionManager.getTokenInfo();
1980
+ setState((prev) => ({
1981
+ ...prev,
1982
+ isAuthenticated: isAuth,
1983
+ isInitialized: true,
1984
+ isLoading: false,
1985
+ tokens: tokenInfo.crudifyTokens.accessToken ? {
1986
+ accessToken: tokenInfo.crudifyTokens.accessToken,
1987
+ refreshToken: tokenInfo.crudifyTokens.refreshToken,
1988
+ expiresAt: tokenInfo.crudifyTokens.expiresAt,
1989
+ refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
1990
+ } : null
1991
+ }));
1992
+ } catch (error) {
1993
+ setState((prev) => ({
1994
+ ...prev,
1995
+ isLoading: false,
1996
+ isInitialized: true,
1997
+ error: error instanceof Error ? error.message : "Initialization failed"
1998
+ }));
2543
1999
  }
2544
- const next = [...policies || [], newPolicy];
2545
- onChange(next);
2546
- setTimeout(() => {
2547
- const newIndex = next.length - 1;
2548
- const el = policyRefs.current[newIndex];
2549
- if (el) {
2550
- el.scrollIntoView({ behavior: "smooth", block: "center" });
2000
+ }, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
2001
+ const login = (0, import_react9.useCallback)(async (email, password) => {
2002
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
2003
+ try {
2004
+ const result = await sessionManager.login(email, password);
2005
+ if (result.success && result.tokens) {
2006
+ setState((prev) => ({
2007
+ ...prev,
2008
+ isAuthenticated: true,
2009
+ tokens: result.tokens,
2010
+ isLoading: false,
2011
+ error: null
2012
+ }));
2013
+ } else {
2014
+ setState((prev) => ({
2015
+ ...prev,
2016
+ isAuthenticated: false,
2017
+ tokens: null,
2018
+ isLoading: false,
2019
+ error: result.error || "Login failed"
2020
+ }));
2551
2021
  }
2552
- }, 100);
2553
- };
2554
- const removePolicy = (index) => {
2555
- const next = [...policies];
2556
- next.splice(index, 1);
2557
- onChange(next);
2558
- };
2559
- const arrayError = (() => {
2560
- if (!errors) return null;
2561
- if (typeof errors === "string") return errors;
2562
- const msg = errors._error;
2563
- return typeof msg === "string" ? msg : null;
2564
- })();
2565
- const usedActions = new Set((policies || []).map((p) => p.action));
2566
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2567
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material10.Divider, { sx: { borderColor: "#e0e4e7" } }),
2568
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material10.Box, { children: [
2569
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material10.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material10.Box, { children: [
2570
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2571
- import_material10.Typography,
2572
- {
2573
- variant: "h6",
2574
- sx: {
2575
- fontWeight: 600,
2576
- color: "#111418",
2577
- mb: 1
2578
- },
2579
- children: t("modules.form.publicPolicies.title")
2580
- }
2581
- ),
2582
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2583
- import_material10.Typography,
2584
- {
2585
- variant: "body2",
2586
- color: "text.secondary",
2587
- sx: { fontSize: "0.875rem" },
2588
- children: t("modules.form.publicPolicies.description")
2589
- }
2590
- )
2591
- ] }) }),
2592
- arrayError && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material10.Alert, { severity: "error", sx: { mb: 3 }, children: arrayError }),
2593
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material10.Stack, { spacing: 3, children: [
2594
- (policies || []).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material10.Alert, { severity: "info", children: t("modules.form.publicPolicies.noPolicies") }) : policies.map((policy, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2595
- PolicyItem_default,
2596
- {
2597
- ref: (el) => {
2598
- policyRefs.current[index] = el;
2599
- },
2600
- policy,
2601
- onChange: (nextPolicy) => {
2602
- const next = [...policies];
2603
- next[index] = nextPolicy;
2604
- onChange(next);
2605
- },
2606
- onRemove: () => removePolicy(index),
2607
- availableFields,
2608
- isSubmitting,
2609
- usedActions,
2610
- error: typeof errors === "object" && errors && policy.id in errors ? errors[policy.id] : void 0
2611
- },
2612
- policy.id
2613
- )),
2614
- canAddPolicy && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material10.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2615
- import_material10.Button,
2616
- {
2617
- type: "button",
2618
- variant: "outlined",
2619
- startIcon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_icons_material4.Add, {}),
2620
- onClick: addPolicy,
2621
- disabled: isSubmitting,
2622
- sx: {
2623
- borderColor: "#d0d7de",
2624
- color: "#656d76",
2625
- "&:hover": {
2626
- borderColor: "#8c959f",
2627
- backgroundColor: "transparent"
2628
- }
2629
- },
2630
- children: t("modules.form.publicPolicies.addPolicy")
2631
- }
2632
- ) })
2633
- ] })
2634
- ] })
2635
- ] });
2636
- };
2637
- var Policies_default = Policies;
2638
-
2639
- // src/core/SessionManager.ts
2640
- var import_crudify_browser4 = __toESM(require("@nocios/crudify-browser"));
2641
-
2642
- // src/utils/tokenStorage.ts
2643
- var import_crypto_js = __toESM(require("crypto-js"));
2644
- var _TokenStorage = class _TokenStorage {
2645
- /**
2646
- * Configurar tipo de almacenamiento
2647
- */
2648
- static setStorageType(type) {
2649
- _TokenStorage.storageType = type;
2650
- }
2651
- /**
2652
- * Verificar si el storage está disponible
2653
- */
2654
- static isStorageAvailable(type) {
2655
- try {
2656
- const storage = window[type];
2657
- const testKey = "__storage_test__";
2658
- storage.setItem(testKey, "test");
2659
- storage.removeItem(testKey);
2660
- return true;
2661
- } catch {
2662
- return false;
2663
- }
2664
- }
2665
- /**
2666
- * Obtener instancia de storage
2667
- */
2668
- static getStorage() {
2669
- if (_TokenStorage.storageType === "none") return null;
2670
- if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
2671
- console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
2672
- return null;
2673
- }
2674
- return window[_TokenStorage.storageType];
2675
- }
2676
- /**
2677
- * Encriptar datos sensibles
2678
- */
2679
- static encrypt(data) {
2680
- try {
2681
- return import_crypto_js.default.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
2682
- } catch (error) {
2683
- console.error("Crudify: Encryption failed", error);
2684
- return data;
2685
- }
2686
- }
2687
- /**
2688
- * Desencriptar datos
2689
- */
2690
- static decrypt(encryptedData) {
2691
- try {
2692
- const bytes = import_crypto_js.default.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
2693
- const decrypted = bytes.toString(import_crypto_js.default.enc.Utf8);
2694
- return decrypted || encryptedData;
2022
+ return result;
2695
2023
  } catch (error) {
2696
- console.error("Crudify: Decryption failed", error);
2697
- return encryptedData;
2024
+ const errorMsg = error instanceof Error ? error.message : "Login failed";
2025
+ setState((prev) => ({
2026
+ ...prev,
2027
+ isAuthenticated: false,
2028
+ tokens: null,
2029
+ isLoading: false,
2030
+ error: errorMsg
2031
+ }));
2032
+ return {
2033
+ success: false,
2034
+ error: errorMsg
2035
+ };
2698
2036
  }
2699
- }
2700
- /**
2701
- * Guardar tokens de forma segura
2702
- */
2703
- static saveTokens(tokens) {
2704
- const storage = _TokenStorage.getStorage();
2705
- if (!storage) return;
2037
+ }, [sessionManager]);
2038
+ const logout = (0, import_react9.useCallback)(async () => {
2039
+ setState((prev) => ({ ...prev, isLoading: true }));
2706
2040
  try {
2707
- const tokenData = {
2708
- accessToken: tokens.accessToken,
2709
- refreshToken: tokens.refreshToken,
2710
- expiresAt: tokens.expiresAt,
2711
- refreshExpiresAt: tokens.refreshExpiresAt,
2712
- savedAt: Date.now()
2713
- };
2714
- const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
2715
- storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
2716
- console.debug("Crudify: Tokens saved successfully");
2041
+ await sessionManager.logout();
2042
+ setState((prev) => ({
2043
+ ...prev,
2044
+ isAuthenticated: false,
2045
+ tokens: null,
2046
+ isLoading: false,
2047
+ error: null
2048
+ }));
2717
2049
  } catch (error) {
2718
- console.error("Crudify: Failed to save tokens", error);
2050
+ setState((prev) => ({
2051
+ ...prev,
2052
+ isAuthenticated: false,
2053
+ tokens: null,
2054
+ isLoading: false,
2055
+ error: error instanceof Error ? error.message : "Logout error"
2056
+ }));
2719
2057
  }
2720
- }
2721
- /**
2722
- * Obtener tokens guardados
2723
- */
2724
- static getTokens() {
2725
- const storage = _TokenStorage.getStorage();
2726
- if (!storage) return null;
2058
+ }, [sessionManager]);
2059
+ const refreshTokens = (0, import_react9.useCallback)(async () => {
2727
2060
  try {
2728
- const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
2729
- if (!encrypted) return null;
2730
- const decrypted = _TokenStorage.decrypt(encrypted);
2731
- const tokenData = JSON.parse(decrypted);
2732
- if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
2733
- console.warn("Crudify: Incomplete token data found, clearing storage");
2734
- _TokenStorage.clearTokens();
2735
- return null;
2736
- }
2737
- if (Date.now() >= tokenData.refreshExpiresAt) {
2738
- console.info("Crudify: Refresh token expired, clearing storage");
2739
- _TokenStorage.clearTokens();
2740
- return null;
2061
+ const success = await sessionManager.refreshTokens();
2062
+ if (success) {
2063
+ const tokenInfo = sessionManager.getTokenInfo();
2064
+ setState((prev) => ({
2065
+ ...prev,
2066
+ tokens: tokenInfo.crudifyTokens.accessToken ? {
2067
+ accessToken: tokenInfo.crudifyTokens.accessToken,
2068
+ refreshToken: tokenInfo.crudifyTokens.refreshToken,
2069
+ expiresAt: tokenInfo.crudifyTokens.expiresAt,
2070
+ refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
2071
+ } : null,
2072
+ error: null
2073
+ }));
2074
+ } else {
2075
+ setState((prev) => ({
2076
+ ...prev,
2077
+ isAuthenticated: false,
2078
+ tokens: null,
2079
+ error: "Token refresh failed"
2080
+ }));
2741
2081
  }
2742
- return {
2743
- accessToken: tokenData.accessToken,
2744
- refreshToken: tokenData.refreshToken,
2745
- expiresAt: tokenData.expiresAt,
2746
- refreshExpiresAt: tokenData.refreshExpiresAt
2747
- };
2082
+ return success;
2748
2083
  } catch (error) {
2749
- console.error("Crudify: Failed to retrieve tokens", error);
2750
- _TokenStorage.clearTokens();
2084
+ setState((prev) => ({
2085
+ ...prev,
2086
+ isAuthenticated: false,
2087
+ tokens: null,
2088
+ error: error instanceof Error ? error.message : "Token refresh failed"
2089
+ }));
2090
+ return false;
2091
+ }
2092
+ }, [sessionManager]);
2093
+ const clearError = (0, import_react9.useCallback)(() => {
2094
+ setState((prev) => ({ ...prev, error: null }));
2095
+ }, []);
2096
+ const getTokenInfo = (0, import_react9.useCallback)(() => {
2097
+ return sessionManager.getTokenInfo();
2098
+ }, [sessionManager]);
2099
+ (0, import_react9.useEffect)(() => {
2100
+ initialize();
2101
+ }, [initialize]);
2102
+ return {
2103
+ // Estado
2104
+ ...state,
2105
+ // Acciones
2106
+ login,
2107
+ logout,
2108
+ refreshTokens,
2109
+ clearError,
2110
+ getTokenInfo,
2111
+ // Utilidades
2112
+ isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
2113
+ // 5 minutos
2114
+ expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
2115
+ refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
2116
+ };
2117
+ }
2118
+
2119
+ // src/utils/jwtUtils.ts
2120
+ var decodeJwtSafely = (token) => {
2121
+ try {
2122
+ const parts = token.split(".");
2123
+ if (parts.length !== 3) {
2124
+ console.warn("Invalid JWT format: token must have 3 parts");
2751
2125
  return null;
2752
2126
  }
2127
+ const payload = parts[1];
2128
+ const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
2129
+ const decodedPayload = JSON.parse(atob(paddedPayload));
2130
+ return decodedPayload;
2131
+ } catch (error) {
2132
+ console.warn("Failed to decode JWT token:", error);
2133
+ return null;
2753
2134
  }
2754
- /**
2755
- * Limpiar tokens almacenados
2756
- */
2757
- static clearTokens() {
2758
- const storage = _TokenStorage.getStorage();
2759
- if (!storage) return;
2760
- try {
2761
- storage.removeItem(_TokenStorage.TOKEN_KEY);
2762
- console.debug("Crudify: Tokens cleared from storage");
2763
- } catch (error) {
2764
- console.error("Crudify: Failed to clear tokens", error);
2135
+ };
2136
+ var getCurrentUserEmail = () => {
2137
+ try {
2138
+ let token = null;
2139
+ token = sessionStorage.getItem("authToken");
2140
+ console.log("\u{1F50D} getCurrentUserEmail - authToken:", token ? `${token.substring(0, 20)}...` : null);
2141
+ if (!token) {
2142
+ token = sessionStorage.getItem("token");
2143
+ console.log("\u{1F50D} getCurrentUserEmail - token:", token ? `${token.substring(0, 20)}...` : null);
2765
2144
  }
2766
- }
2767
- /**
2768
- * Verificar si hay tokens válidos guardados
2769
- */
2770
- static hasValidTokens() {
2771
- const tokens = _TokenStorage.getTokens();
2772
- return tokens !== null;
2773
- }
2774
- /**
2775
- * Obtener información de expiración
2776
- */
2777
- static getExpirationInfo() {
2778
- const tokens = _TokenStorage.getTokens();
2779
- if (!tokens) return null;
2780
- const now = Date.now();
2781
- return {
2782
- accessExpired: now >= tokens.expiresAt,
2783
- refreshExpired: now >= tokens.refreshExpiresAt,
2784
- accessExpiresIn: Math.max(0, tokens.expiresAt - now),
2785
- refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
2786
- };
2787
- }
2788
- /**
2789
- * Actualizar solo el access token (después de refresh)
2790
- */
2791
- static updateAccessToken(newAccessToken, newExpiresAt) {
2792
- const existingTokens = _TokenStorage.getTokens();
2793
- if (!existingTokens) {
2794
- console.warn("Crudify: Cannot update access token, no existing tokens found");
2795
- return;
2145
+ if (!token) {
2146
+ token = localStorage.getItem("authToken") || localStorage.getItem("token");
2147
+ console.log("\u{1F50D} getCurrentUserEmail - localStorage:", token ? `${token.substring(0, 20)}...` : null);
2796
2148
  }
2797
- _TokenStorage.saveTokens({
2798
- ...existingTokens,
2799
- accessToken: newAccessToken,
2800
- expiresAt: newExpiresAt
2801
- });
2149
+ if (!token) {
2150
+ console.warn("\u{1F50D} getCurrentUserEmail - No token found in any storage");
2151
+ return null;
2152
+ }
2153
+ const payload = decodeJwtSafely(token);
2154
+ if (!payload) {
2155
+ console.warn("\u{1F50D} getCurrentUserEmail - Failed to decode token");
2156
+ return null;
2157
+ }
2158
+ const email = payload.email || payload["cognito:username"] || null;
2159
+ console.log("\u{1F50D} getCurrentUserEmail - Extracted email:", email);
2160
+ return email;
2161
+ } catch (error) {
2162
+ console.warn("Failed to get current user email:", error);
2163
+ return null;
2802
2164
  }
2803
2165
  };
2804
- _TokenStorage.TOKEN_KEY = "crudify_tokens";
2805
- _TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
2806
- _TokenStorage.storageType = "localStorage";
2807
- var TokenStorage = _TokenStorage;
2808
-
2809
- // src/core/SessionManager.ts
2810
- var SessionManager = class _SessionManager {
2811
- constructor() {
2812
- this.config = {};
2813
- this.initialized = false;
2814
- }
2815
- static getInstance() {
2816
- if (!_SessionManager.instance) {
2817
- _SessionManager.instance = new _SessionManager();
2818
- }
2819
- return _SessionManager.instance;
2166
+ var isTokenExpired = (token) => {
2167
+ try {
2168
+ const payload = decodeJwtSafely(token);
2169
+ if (!payload || !payload.exp) return true;
2170
+ const currentTime = Math.floor(Date.now() / 1e3);
2171
+ return payload.exp < currentTime;
2172
+ } catch {
2173
+ return true;
2820
2174
  }
2821
- /**
2822
- * Inicializar el SessionManager
2823
- */
2824
- async initialize(config = {}) {
2825
- if (this.initialized) {
2826
- console.warn("SessionManager: Already initialized");
2827
- return;
2828
- }
2829
- this.config = {
2830
- storageType: "localStorage",
2831
- autoRestore: true,
2832
- enableLogging: false,
2833
- ...config
2175
+ };
2176
+
2177
+ // src/providers/SessionProvider.tsx
2178
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2179
+ var SessionContext = (0, import_react10.createContext)(void 0);
2180
+ function SessionProvider({ children, options = {}, config: propConfig }) {
2181
+ const sessionHook = useSession(options);
2182
+ const resolvedConfig = (0, import_react10.useMemo)(() => {
2183
+ let publicApiKey;
2184
+ let env;
2185
+ let appName;
2186
+ let loginActions;
2187
+ let logo;
2188
+ let colors;
2189
+ let configSource = "unknown";
2190
+ if (propConfig?.publicApiKey) {
2191
+ publicApiKey = propConfig.publicApiKey;
2192
+ configSource = "props";
2193
+ }
2194
+ if (propConfig?.env) {
2195
+ env = propConfig.env;
2196
+ }
2197
+ if (propConfig?.appName) {
2198
+ appName = propConfig.appName;
2199
+ }
2200
+ if (propConfig?.loginActions) {
2201
+ loginActions = propConfig.loginActions;
2202
+ }
2203
+ if (propConfig?.logo) {
2204
+ logo = propConfig.logo;
2205
+ }
2206
+ if (propConfig?.colors) {
2207
+ colors = propConfig.colors;
2208
+ }
2209
+ if (!publicApiKey) {
2210
+ const cookieApiKey = getCookie("publicApiKey");
2211
+ const cookieEnv = getCookie("environment");
2212
+ const cookieAppName = getCookie("appName");
2213
+ const cookieLoginActions = getCookie("loginActions");
2214
+ if (cookieApiKey) {
2215
+ publicApiKey = cookieApiKey;
2216
+ configSource = "cookies";
2217
+ }
2218
+ if (cookieEnv && ["dev", "stg", "prod"].includes(cookieEnv)) {
2219
+ env = cookieEnv;
2220
+ }
2221
+ if (cookieAppName) {
2222
+ appName = cookieAppName;
2223
+ }
2224
+ if (cookieLoginActions) {
2225
+ loginActions = cookieLoginActions.split(",").map((s) => s.trim()).filter(Boolean);
2226
+ }
2227
+ }
2228
+ console.log("\u{1F50D} SessionProvider - Configuration Detection:");
2229
+ console.log(" - Config source:", configSource);
2230
+ console.log(" - PublicApiKey:", publicApiKey ? publicApiKey.substring(0, 15) + "..." : "undefined");
2231
+ console.log(" - Environment:", env);
2232
+ console.log(" - App name:", appName);
2233
+ console.log(" - Login actions:", loginActions);
2234
+ return {
2235
+ publicApiKey,
2236
+ env,
2237
+ appName,
2238
+ loginActions,
2239
+ logo,
2240
+ colors
2834
2241
  };
2835
- TokenStorage.setStorageType(this.config.storageType || "localStorage");
2836
- if (this.config.enableLogging) {
2837
- }
2838
- if (this.config.autoRestore) {
2839
- await this.restoreSession();
2242
+ }, [propConfig]);
2243
+ const sessionData = (0, import_react10.useMemo)(() => {
2244
+ if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
2245
+ return null;
2840
2246
  }
2841
- this.initialized = true;
2842
- this.log("SessionManager initialized successfully");
2843
- }
2844
- /**
2845
- * Login con persistencia automática
2846
- */
2847
- async login(email, password) {
2848
2247
  try {
2849
- this.log("Attempting login...");
2850
- const response = await import_crudify_browser4.default.login(email, password);
2851
- if (!response.success) {
2852
- this.log("Login failed:", response.errors);
2853
- return {
2854
- success: false,
2855
- error: this.formatError(response.errors)
2248
+ const decoded = decodeJwtSafely(sessionHook.tokens.accessToken);
2249
+ if (decoded && decoded.sub && decoded.email && decoded.subscriber) {
2250
+ const result = {
2251
+ _id: decoded.sub,
2252
+ email: decoded.email,
2253
+ subscriberKey: decoded.subscriber
2856
2254
  };
2255
+ Object.keys(decoded).forEach((key) => {
2256
+ if (!["sub", "email", "subscriber"].includes(key)) {
2257
+ result[key] = decoded[key];
2258
+ }
2259
+ });
2260
+ return result;
2857
2261
  }
2858
- const tokens = {
2859
- accessToken: response.data.token,
2860
- refreshToken: response.data.refreshToken,
2861
- expiresAt: response.data.expiresAt,
2862
- refreshExpiresAt: response.data.refreshExpiresAt
2863
- };
2864
- TokenStorage.saveTokens(tokens);
2865
- this.log("Login successful, tokens saved");
2866
- this.config.onLoginSuccess?.(tokens);
2867
- return {
2868
- success: true,
2869
- tokens
2870
- };
2871
2262
  } catch (error) {
2872
- this.log("Login error:", error);
2873
- return {
2874
- success: false,
2875
- error: error instanceof Error ? error.message : "Unknown error"
2876
- };
2263
+ console.error("Error decoding JWT token for sessionData:", error);
2877
2264
  }
2265
+ return null;
2266
+ }, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
2267
+ const contextValue = {
2268
+ ...sessionHook,
2269
+ sessionData,
2270
+ config: resolvedConfig
2271
+ };
2272
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SessionContext.Provider, { value: contextValue, children });
2273
+ }
2274
+ function useSessionContext() {
2275
+ const context = (0, import_react10.useContext)(SessionContext);
2276
+ if (context === void 0) {
2277
+ throw new Error("useSessionContext must be used within a SessionProvider");
2878
2278
  }
2879
- /**
2880
- * Logout con limpieza de tokens
2881
- */
2882
- async logout() {
2883
- try {
2884
- this.log("Logging out...");
2885
- await import_crudify_browser4.default.logout();
2886
- TokenStorage.clearTokens();
2887
- this.log("Logout successful");
2888
- this.config.onLogout?.();
2889
- } catch (error) {
2890
- this.log("Logout error:", error);
2891
- TokenStorage.clearTokens();
2892
- }
2279
+ return context;
2280
+ }
2281
+ function ProtectedRoute({ children, fallback = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { children: "Please log in to access this content" }), redirectTo }) {
2282
+ const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
2283
+ if (!isInitialized || isLoading) {
2284
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { children: "Loading..." });
2893
2285
  }
2894
- /**
2895
- * Restaurar sesión desde storage
2896
- */
2897
- async restoreSession() {
2898
- try {
2899
- this.log("Attempting to restore session...");
2900
- const savedTokens = TokenStorage.getTokens();
2901
- if (!savedTokens) {
2902
- this.log("No valid tokens found in storage");
2903
- return false;
2904
- }
2905
- import_crudify_browser4.default.setTokens({
2906
- accessToken: savedTokens.accessToken,
2907
- refreshToken: savedTokens.refreshToken,
2908
- expiresAt: savedTokens.expiresAt,
2909
- refreshExpiresAt: savedTokens.refreshExpiresAt
2910
- });
2911
- this.log("Session restored successfully");
2912
- this.config.onSessionRestored?.(savedTokens);
2913
- return true;
2914
- } catch (error) {
2915
- this.log("Session restore error:", error);
2916
- TokenStorage.clearTokens();
2917
- return false;
2286
+ if (!isAuthenticated) {
2287
+ if (redirectTo) {
2288
+ redirectTo();
2289
+ return null;
2918
2290
  }
2291
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: fallback });
2919
2292
  }
2920
- /**
2921
- * Verificar si el usuario está autenticado
2922
- */
2923
- isAuthenticated() {
2924
- return import_crudify_browser4.default.isLogin() || TokenStorage.hasValidTokens();
2925
- }
2926
- /**
2927
- * Obtener información de tokens actuales
2928
- */
2929
- getTokenInfo() {
2930
- const crudifyTokens = import_crudify_browser4.default.getTokenData();
2931
- const storageInfo = TokenStorage.getExpirationInfo();
2932
- return {
2933
- isLoggedIn: this.isAuthenticated(),
2934
- crudifyTokens,
2935
- storageInfo,
2936
- hasValidTokens: TokenStorage.hasValidTokens()
2937
- };
2293
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children });
2294
+ }
2295
+ function SessionDebugInfo() {
2296
+ const session = useSessionContext();
2297
+ if (!session.isInitialized) {
2298
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { children: "Session not initialized" });
2938
2299
  }
2939
- /**
2940
- * Refrescar tokens manualmente
2941
- */
2942
- async refreshTokens() {
2943
- try {
2944
- this.log("Manually refreshing tokens...");
2945
- const response = await import_crudify_browser4.default.refreshAccessToken();
2946
- if (!response.success) {
2947
- this.log("Token refresh failed:", response.errors);
2948
- TokenStorage.clearTokens();
2949
- this.config.onSessionExpired?.();
2950
- return false;
2951
- }
2952
- const newTokens = {
2953
- accessToken: response.data.token,
2954
- refreshToken: response.data.refreshToken,
2955
- expiresAt: response.data.expiresAt,
2956
- refreshExpiresAt: response.data.refreshExpiresAt
2957
- };
2958
- TokenStorage.saveTokens(newTokens);
2959
- this.log("Tokens refreshed and saved successfully");
2960
- return true;
2961
- } catch (error) {
2962
- this.log("Token refresh error:", error);
2963
- TokenStorage.clearTokens();
2964
- this.config.onSessionExpired?.();
2965
- return false;
2300
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2301
+ "div",
2302
+ {
2303
+ style: {
2304
+ padding: "10px",
2305
+ margin: "10px",
2306
+ border: "1px solid #ccc",
2307
+ borderRadius: "4px",
2308
+ fontSize: "12px",
2309
+ fontFamily: "monospace"
2310
+ },
2311
+ children: [
2312
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { children: "Session Debug Info" }),
2313
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2314
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Authenticated:" }),
2315
+ " ",
2316
+ session.isAuthenticated ? "Yes" : "No"
2317
+ ] }),
2318
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2319
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Loading:" }),
2320
+ " ",
2321
+ session.isLoading ? "Yes" : "No"
2322
+ ] }),
2323
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2324
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Error:" }),
2325
+ " ",
2326
+ session.error || "None"
2327
+ ] }),
2328
+ session.tokens && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2329
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2330
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Access Token:" }),
2331
+ " ",
2332
+ session.tokens.accessToken.substring(0, 20),
2333
+ "..."
2334
+ ] }),
2335
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2336
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Refresh Token:" }),
2337
+ " ",
2338
+ session.tokens.refreshToken.substring(0, 20),
2339
+ "..."
2340
+ ] }),
2341
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2342
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Access Expires In:" }),
2343
+ " ",
2344
+ Math.round(session.expiresIn / 1e3 / 60),
2345
+ " minutes"
2346
+ ] }),
2347
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2348
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Refresh Expires In:" }),
2349
+ " ",
2350
+ Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
2351
+ " hours"
2352
+ ] }),
2353
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2354
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Expiring Soon:" }),
2355
+ " ",
2356
+ session.isExpiringSoon ? "Yes" : "No"
2357
+ ] })
2358
+ ] })
2359
+ ]
2966
2360
  }
2967
- }
2968
- /**
2969
- * Configurar interceptor de respuesta para manejo automático de errores
2970
- */
2971
- setupResponseInterceptor() {
2972
- import_crudify_browser4.default.setResponseInterceptor(async (response) => {
2973
- if (response.errors) {
2974
- const hasAuthError = response.errors.some(
2975
- (error) => error.message?.includes("Unauthorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED"
2976
- );
2977
- if (hasAuthError && TokenStorage.hasValidTokens()) {
2978
- this.log("Auth error detected, attempting token refresh...");
2979
- const refreshSuccess = await this.refreshTokens();
2980
- if (!refreshSuccess) {
2981
- this.log("Session expired, triggering callback");
2982
- this.config.onSessionExpired?.();
2361
+ );
2362
+ }
2363
+
2364
+ // src/components/CrudifyLogin/index.tsx
2365
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2366
+ var CrudifyLoginInternal = ({
2367
+ onScreenChange,
2368
+ onExternalNavigate,
2369
+ onLoginSuccess,
2370
+ onError,
2371
+ redirectUrl = "/"
2372
+ }) => {
2373
+ const { t } = useTranslation();
2374
+ const { state, setScreen } = useLoginState();
2375
+ const { config } = useSessionContext();
2376
+ const handleScreenChange = (screen2, params) => {
2377
+ let finalParams = params;
2378
+ if (screen2 === "login") {
2379
+ finalParams = {};
2380
+ } else if (screen2 === "forgotPassword" && !params) {
2381
+ finalParams = {};
2382
+ }
2383
+ setScreen(screen2, finalParams);
2384
+ onScreenChange?.(screen2, finalParams);
2385
+ };
2386
+ const renderCurrentForm = () => {
2387
+ const commonProps = {
2388
+ onScreenChange: handleScreenChange,
2389
+ onExternalNavigate,
2390
+ onError,
2391
+ redirectUrl
2392
+ };
2393
+ switch (state.currentScreen) {
2394
+ case "forgotPassword":
2395
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ForgotPasswordForm_default, { ...commonProps });
2396
+ case "checkCode":
2397
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CheckCodeForm_default, { ...commonProps, searchParams: state.searchParams });
2398
+ case "resetPassword":
2399
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2400
+ ResetPasswordForm_default,
2401
+ {
2402
+ ...commonProps,
2403
+ searchParams: state.searchParams,
2404
+ onResetSuccess: () => {
2405
+ handleScreenChange("login");
2406
+ }
2983
2407
  }
2408
+ );
2409
+ default:
2410
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LoginForm_default, { ...commonProps, onLoginSuccess });
2411
+ }
2412
+ };
2413
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(CrudifyInitializer, { children: [
2414
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_material6.Box, { sx: { display: "flex", justifyContent: "center", mb: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2415
+ "img",
2416
+ {
2417
+ src: config.logo || "/nocios-default.png",
2418
+ alt: t("login.logoAlt"),
2419
+ style: {
2420
+ width: "100%",
2421
+ maxWidth: "150px",
2422
+ height: "auto"
2423
+ },
2424
+ onError: (e) => {
2425
+ const target = e.target;
2426
+ target.src = "/nocios-default.png";
2984
2427
  }
2985
2428
  }
2986
- return response;
2987
- });
2988
- this.log("Response interceptor configured");
2989
- }
2990
- /**
2991
- * Limpiar sesión completamente
2992
- */
2993
- clearSession() {
2994
- TokenStorage.clearTokens();
2995
- import_crudify_browser4.default.logout();
2996
- this.log("Session cleared completely");
2997
- }
2998
- // Métodos privados
2999
- log(message, ...args) {
3000
- if (this.config.enableLogging) {
3001
- console.log(`[SessionManager] ${message}`, ...args);
3002
- }
3003
- }
3004
- formatError(errors) {
3005
- if (!errors) return "Unknown error";
3006
- if (typeof errors === "string") return errors;
3007
- if (typeof errors === "object") {
3008
- const errorMessages = Object.values(errors).flat();
3009
- return errorMessages.join(", ");
3010
- }
3011
- return "Authentication failed";
3012
- }
2429
+ ) }),
2430
+ config.appName && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2431
+ import_material6.Typography,
2432
+ {
2433
+ variant: "h6",
2434
+ component: "h1",
2435
+ sx: {
2436
+ textAlign: "center",
2437
+ mb: 2,
2438
+ color: config.colors?.primaryColor || "#1066BA"
2439
+ },
2440
+ children: config.appName
2441
+ }
2442
+ ),
2443
+ renderCurrentForm()
2444
+ ] });
2445
+ };
2446
+ var CrudifyLogin = ({
2447
+ translations,
2448
+ translationsUrl,
2449
+ language = "en",
2450
+ initialScreen = "login",
2451
+ autoReadFromCookies = true,
2452
+ ...props
2453
+ }) => {
2454
+ const { config } = useSessionContext();
2455
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(I18nProvider, { translations, translationsUrl, language, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CrudifyProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LoginStateProvider, { config, initialScreen, autoReadFromCookies, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CrudifyLoginInternal, { ...props }) }) }) });
3013
2456
  };
2457
+ var CrudifyLogin_default = CrudifyLogin;
3014
2458
 
3015
- // src/hooks/useSession.ts
3016
- var import_react15 = require("react");
3017
- function useSession(options = {}) {
3018
- const [state, setState] = (0, import_react15.useState)({
3019
- isAuthenticated: false,
3020
- isLoading: true,
3021
- isInitialized: false,
3022
- tokens: null,
3023
- error: null
3024
- });
3025
- const sessionManager = SessionManager.getInstance();
3026
- const initialize = (0, import_react15.useCallback)(async () => {
3027
- try {
3028
- setState((prev) => ({ ...prev, isLoading: true, error: null }));
3029
- const config = {
3030
- autoRestore: options.autoRestore ?? true,
3031
- enableLogging: options.enableLogging ?? false,
3032
- onSessionExpired: () => {
3033
- setState((prev) => ({
3034
- ...prev,
3035
- isAuthenticated: false,
3036
- tokens: null,
3037
- error: "Session expired"
3038
- }));
3039
- options.onSessionExpired?.();
3040
- },
3041
- onSessionRestored: (tokens) => {
3042
- setState((prev) => ({
3043
- ...prev,
3044
- isAuthenticated: true,
3045
- tokens,
3046
- error: null
3047
- }));
3048
- options.onSessionRestored?.(tokens);
3049
- },
3050
- onLoginSuccess: (tokens) => {
3051
- setState((prev) => ({
3052
- ...prev,
3053
- isAuthenticated: true,
3054
- tokens,
3055
- error: null
3056
- }));
3057
- },
3058
- onLogout: () => {
3059
- setState((prev) => ({
3060
- ...prev,
3061
- isAuthenticated: false,
3062
- tokens: null,
3063
- error: null
3064
- }));
3065
- }
3066
- };
3067
- await sessionManager.initialize(config);
3068
- sessionManager.setupResponseInterceptor();
3069
- const isAuth = sessionManager.isAuthenticated();
3070
- const tokenInfo = sessionManager.getTokenInfo();
3071
- setState((prev) => ({
3072
- ...prev,
3073
- isAuthenticated: isAuth,
3074
- isInitialized: true,
3075
- isLoading: false,
3076
- tokens: tokenInfo.crudifyTokens.accessToken ? {
3077
- accessToken: tokenInfo.crudifyTokens.accessToken,
3078
- refreshToken: tokenInfo.crudifyTokens.refreshToken,
3079
- expiresAt: tokenInfo.crudifyTokens.expiresAt,
3080
- refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
3081
- } : null
3082
- }));
3083
- } catch (error) {
3084
- setState((prev) => ({
3085
- ...prev,
3086
- isLoading: false,
3087
- isInitialized: true,
3088
- error: error instanceof Error ? error.message : "Initialization failed"
3089
- }));
2459
+ // src/components/UserProfile/UserProfileDisplay.tsx
2460
+ var import_material7 = require("@mui/material");
2461
+ var import_icons_material = require("@mui/icons-material");
2462
+
2463
+ // src/hooks/useUserProfile.ts
2464
+ var import_react11 = require("react");
2465
+ var import_crudify_browser3 = __toESM(require("@nocios/crudify-browser"));
2466
+ var useUserProfile = (options = {}) => {
2467
+ const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
2468
+ const [userProfile, setUserProfile] = (0, import_react11.useState)(null);
2469
+ const [loading, setLoading] = (0, import_react11.useState)(false);
2470
+ const [error, setError] = (0, import_react11.useState)(null);
2471
+ const [extendedData, setExtendedData] = (0, import_react11.useState)({});
2472
+ const abortControllerRef = (0, import_react11.useRef)(null);
2473
+ const mountedRef = (0, import_react11.useRef)(true);
2474
+ const requestIdRef = (0, import_react11.useRef)(0);
2475
+ const retryCountRef = (0, import_react11.useRef)(0);
2476
+ const clearProfile = (0, import_react11.useCallback)(() => {
2477
+ setUserProfile(null);
2478
+ setError(null);
2479
+ setLoading(false);
2480
+ setExtendedData({});
2481
+ }, []);
2482
+ const refreshProfile = (0, import_react11.useCallback)(async () => {
2483
+ const userEmail = getCurrentUserEmail();
2484
+ if (!userEmail) {
2485
+ if (mountedRef.current) {
2486
+ setError("No user email available");
2487
+ setLoading(false);
2488
+ }
2489
+ return;
3090
2490
  }
3091
- }, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
3092
- const login = (0, import_react15.useCallback)(async (email, password) => {
3093
- setState((prev) => ({ ...prev, isLoading: true, error: null }));
2491
+ if (abortControllerRef.current) {
2492
+ abortControllerRef.current.abort();
2493
+ }
2494
+ const abortController = new AbortController();
2495
+ abortControllerRef.current = abortController;
2496
+ const currentRequestId = ++requestIdRef.current;
3094
2497
  try {
3095
- const result = await sessionManager.login(email, password);
3096
- if (result.success && result.tokens) {
3097
- setState((prev) => ({
3098
- ...prev,
3099
- isAuthenticated: true,
3100
- tokens: result.tokens,
3101
- isLoading: false,
3102
- error: null
3103
- }));
3104
- } else {
3105
- setState((prev) => ({
3106
- ...prev,
3107
- isAuthenticated: false,
3108
- tokens: null,
3109
- isLoading: false,
3110
- error: result.error || "Login failed"
3111
- }));
2498
+ if (mountedRef.current) {
2499
+ setLoading(true);
2500
+ setError(null);
2501
+ }
2502
+ const response = await import_crudify_browser3.default.readItems("users", {
2503
+ filter: { email: userEmail },
2504
+ pagination: { limit: 1 }
2505
+ });
2506
+ if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
2507
+ if (response.success && response.data && response.data.length > 0) {
2508
+ const userData = response.data[0];
2509
+ setUserProfile(userData);
2510
+ const additionalData = {
2511
+ fullProfile: userData,
2512
+ totalFields: Object.keys(userData).length,
2513
+ displayData: {
2514
+ id: userData.id,
2515
+ email: userData.email,
2516
+ username: userData.username,
2517
+ firstName: userData.firstName,
2518
+ lastName: userData.lastName,
2519
+ fullName: userData.fullName || `${userData.firstName || ""} ${userData.lastName || ""}`.trim(),
2520
+ role: userData.role,
2521
+ permissions: userData.permissions || [],
2522
+ isActive: userData.isActive,
2523
+ lastLogin: userData.lastLogin,
2524
+ createdAt: userData.createdAt,
2525
+ updatedAt: userData.updatedAt,
2526
+ // Include any custom fields
2527
+ ...Object.keys(userData).filter(
2528
+ (key) => ![
2529
+ "id",
2530
+ "email",
2531
+ "username",
2532
+ "firstName",
2533
+ "lastName",
2534
+ "fullName",
2535
+ "role",
2536
+ "permissions",
2537
+ "isActive",
2538
+ "lastLogin",
2539
+ "createdAt",
2540
+ "updatedAt"
2541
+ ].includes(key)
2542
+ ).reduce((acc, key) => ({ ...acc, [key]: userData[key] }), {})
2543
+ }
2544
+ };
2545
+ setExtendedData(additionalData);
2546
+ setError(null);
2547
+ retryCountRef.current = 0;
2548
+ } else {
2549
+ setError("User profile not found");
2550
+ setUserProfile(null);
2551
+ setExtendedData({});
2552
+ }
2553
+ }
2554
+ } catch (err) {
2555
+ if (currentRequestId === requestIdRef.current && mountedRef.current) {
2556
+ const error2 = err;
2557
+ if (error2.name === "AbortError") {
2558
+ return;
2559
+ }
2560
+ const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
2561
+ if (shouldRetry) {
2562
+ retryCountRef.current++;
2563
+ setTimeout(() => {
2564
+ if (mountedRef.current) {
2565
+ refreshProfile();
2566
+ }
2567
+ }, 1e3 * retryCountRef.current);
2568
+ } else {
2569
+ setError("Failed to load user profile");
2570
+ setUserProfile(null);
2571
+ setExtendedData({});
2572
+ }
2573
+ }
2574
+ } finally {
2575
+ if (currentRequestId === requestIdRef.current && mountedRef.current) {
2576
+ setLoading(false);
2577
+ }
2578
+ if (abortControllerRef.current === abortController) {
2579
+ abortControllerRef.current = null;
3112
2580
  }
3113
- return result;
3114
- } catch (error) {
3115
- const errorMsg = error instanceof Error ? error.message : "Login failed";
3116
- setState((prev) => ({
3117
- ...prev,
3118
- isAuthenticated: false,
3119
- tokens: null,
3120
- isLoading: false,
3121
- error: errorMsg
3122
- }));
3123
- return {
3124
- success: false,
3125
- error: errorMsg
3126
- };
3127
2581
  }
3128
- }, [sessionManager]);
3129
- const logout = (0, import_react15.useCallback)(async () => {
3130
- setState((prev) => ({ ...prev, isLoading: true }));
3131
- try {
3132
- await sessionManager.logout();
3133
- setState((prev) => ({
3134
- ...prev,
3135
- isAuthenticated: false,
3136
- tokens: null,
3137
- isLoading: false,
3138
- error: null
3139
- }));
3140
- } catch (error) {
3141
- setState((prev) => ({
3142
- ...prev,
3143
- isAuthenticated: false,
3144
- tokens: null,
3145
- isLoading: false,
3146
- error: error instanceof Error ? error.message : "Logout error"
3147
- }));
2582
+ }, [retryOnError, maxRetries]);
2583
+ (0, import_react11.useEffect)(() => {
2584
+ if (autoFetch) {
2585
+ refreshProfile();
3148
2586
  }
3149
- }, [sessionManager]);
3150
- const refreshTokens = (0, import_react15.useCallback)(async () => {
3151
- try {
3152
- const success = await sessionManager.refreshTokens();
3153
- if (success) {
3154
- const tokenInfo = sessionManager.getTokenInfo();
3155
- setState((prev) => ({
3156
- ...prev,
3157
- tokens: tokenInfo.crudifyTokens.accessToken ? {
3158
- accessToken: tokenInfo.crudifyTokens.accessToken,
3159
- refreshToken: tokenInfo.crudifyTokens.refreshToken,
3160
- expiresAt: tokenInfo.crudifyTokens.expiresAt,
3161
- refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
3162
- } : null,
3163
- error: null
3164
- }));
3165
- } else {
3166
- setState((prev) => ({
3167
- ...prev,
3168
- isAuthenticated: false,
3169
- tokens: null,
3170
- error: "Token refresh failed"
3171
- }));
2587
+ }, [autoFetch, refreshProfile]);
2588
+ (0, import_react11.useEffect)(() => {
2589
+ mountedRef.current = true;
2590
+ return () => {
2591
+ mountedRef.current = false;
2592
+ if (abortControllerRef.current) {
2593
+ abortControllerRef.current.abort();
2594
+ abortControllerRef.current = null;
3172
2595
  }
3173
- return success;
3174
- } catch (error) {
3175
- setState((prev) => ({
3176
- ...prev,
3177
- isAuthenticated: false,
3178
- tokens: null,
3179
- error: error instanceof Error ? error.message : "Token refresh failed"
3180
- }));
3181
- return false;
2596
+ };
2597
+ }, []);
2598
+ return {
2599
+ userProfile,
2600
+ loading,
2601
+ error,
2602
+ extendedData,
2603
+ refreshProfile,
2604
+ clearProfile
2605
+ };
2606
+ };
2607
+
2608
+ // src/components/UserProfile/UserProfileDisplay.tsx
2609
+ var import_react12 = require("react");
2610
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2611
+ var UserProfileDisplay = ({
2612
+ showExtendedData = true,
2613
+ showProfileCard = true,
2614
+ autoRefresh = true
2615
+ }) => {
2616
+ const { userProfile, loading, error, extendedData, refreshProfile } = useUserProfile({
2617
+ autoFetch: autoRefresh,
2618
+ retryOnError: true,
2619
+ maxRetries: 3
2620
+ });
2621
+ const [showAllFields, setShowAllFields] = (0, import_react12.useState)(false);
2622
+ if (loading) {
2623
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { display: "flex", justifyContent: "center", alignItems: "center", p: 3, children: [
2624
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.CircularProgress, {}),
2625
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Typography, { variant: "body2", sx: { ml: 2 }, children: "Cargando perfil de usuario..." })
2626
+ ] });
2627
+ }
2628
+ if (error) {
2629
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2630
+ import_material7.Alert,
2631
+ {
2632
+ severity: "error",
2633
+ action: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.IconButton, { color: "inherit", size: "small", onClick: refreshProfile, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Typography, { variant: "caption", children: "Reintentar" }) }),
2634
+ children: [
2635
+ "Error al cargar el perfil: ",
2636
+ error
2637
+ ]
2638
+ }
2639
+ );
2640
+ }
2641
+ if (!userProfile) {
2642
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Alert, { severity: "warning", children: "No se encontr\xF3 informaci\xF3n del usuario" });
2643
+ }
2644
+ const displayData = extendedData?.displayData || {};
2645
+ const totalFields = extendedData?.totalFields || 0;
2646
+ const formatDate = (dateString) => {
2647
+ if (!dateString) return "No disponible";
2648
+ try {
2649
+ return new Date(dateString).toLocaleString("es-ES", {
2650
+ year: "numeric",
2651
+ month: "short",
2652
+ day: "numeric",
2653
+ hour: "2-digit",
2654
+ minute: "2-digit"
2655
+ });
2656
+ } catch {
2657
+ return dateString;
2658
+ }
2659
+ };
2660
+ const renderFieldValue = (key, value) => {
2661
+ if (value === null || value === void 0) return "No disponible";
2662
+ if (typeof value === "boolean") return value ? "S\xED" : "No";
2663
+ if (Array.isArray(value)) return value.length > 0 ? value.join(", ") : "Ninguno";
2664
+ if (typeof value === "object") return JSON.stringify(value, null, 2);
2665
+ return String(value);
2666
+ };
2667
+ const basicFields = [
2668
+ { key: "id", label: "ID", icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.Badge, {}) },
2669
+ { key: "email", label: "Email", icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.Email, {}) },
2670
+ { key: "username", label: "Usuario", icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.Person, {}) },
2671
+ { key: "fullName", label: "Nombre completo", icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.AccountCircle, {}) },
2672
+ { key: "role", label: "Rol", icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.Security, {}) }
2673
+ ];
2674
+ const extendedFields = [
2675
+ { key: "firstName", label: "Nombre" },
2676
+ { key: "lastName", label: "Apellido" },
2677
+ { key: "isActive", label: "Activo" },
2678
+ { key: "lastLogin", label: "\xDAltimo login" },
2679
+ { key: "createdAt", label: "Creado" },
2680
+ { key: "updatedAt", label: "Actualizado" }
2681
+ ];
2682
+ const knownFields = [...basicFields.map((f) => f.key), ...extendedFields.map((f) => f.key), "permissions"];
2683
+ const customFields = Object.keys(displayData).filter((key) => !knownFields.includes(key)).map((key) => ({ key, label: key }));
2684
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { children: [
2685
+ showProfileCard && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Card, { sx: { mb: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.CardContent, { children: [
2686
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { display: "flex", alignItems: "center", mb: 2, children: [
2687
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2688
+ import_material7.Avatar,
2689
+ {
2690
+ src: displayData.avatar,
2691
+ sx: { width: 56, height: 56, mr: 2 },
2692
+ children: displayData.fullName?.[0] || displayData.username?.[0] || displayData.email?.[0]
2693
+ }
2694
+ ),
2695
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { children: [
2696
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Typography, { variant: "h6", children: displayData.fullName || displayData.username || displayData.email }),
2697
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Typography, { variant: "body2", color: "text.secondary", children: displayData.role || "Usuario" }),
2698
+ displayData.isActive !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2699
+ import_material7.Chip,
2700
+ {
2701
+ label: displayData.isActive ? "Activo" : "Inactivo",
2702
+ color: displayData.isActive ? "success" : "error",
2703
+ size: "small",
2704
+ sx: { mt: 0.5 }
2705
+ }
2706
+ )
2707
+ ] })
2708
+ ] }),
2709
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Box, { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: 2, children: basicFields.map(
2710
+ ({ key, label, icon }) => displayData[key] ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { display: "flex", alignItems: "center", children: [
2711
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Box, { sx: { mr: 1, color: "text.secondary" }, children: icon }),
2712
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { children: [
2713
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Typography, { variant: "caption", color: "text.secondary", children: label }),
2714
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Typography, { variant: "body2", children: renderFieldValue(key, displayData[key]) })
2715
+ ] })
2716
+ ] }, key) : null
2717
+ ) }),
2718
+ displayData.permissions && Array.isArray(displayData.permissions) && displayData.permissions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { mt: 2, children: [
2719
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Typography, { variant: "caption", color: "text.secondary", display: "block", children: "Permisos" }),
2720
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { display: "flex", flexWrap: "wrap", gap: 0.5, mt: 0.5, children: [
2721
+ displayData.permissions.slice(0, 5).map((permission, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Chip, { label: permission, size: "small", variant: "outlined" }, index)),
2722
+ displayData.permissions.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Chip, { label: `+${displayData.permissions.length - 5} m\xE1s`, size: "small" })
2723
+ ] })
2724
+ ] })
2725
+ ] }) }),
2726
+ showExtendedData && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.CardContent, { children: [
2727
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, children: [
2728
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Typography, { variant: "h6", display: "flex", alignItems: "center", children: [
2729
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.Info, { sx: { mr: 1 } }),
2730
+ "Informaci\xF3n Detallada"
2731
+ ] }),
2732
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Chip, { label: `${totalFields} campos totales`, size: "small" })
2733
+ ] }),
2734
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.List, { dense: true, children: [
2735
+ extendedFields.map(({ key, label }) => displayData[key] !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.ListItem, { divider: true, children: [
2736
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.ListItemIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.Schedule, { fontSize: "small" }) }),
2737
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2738
+ import_material7.ListItemText,
2739
+ {
2740
+ primary: label,
2741
+ secondary: key.includes("At") || key.includes("Login") ? formatDate(displayData[key]) : renderFieldValue(key, displayData[key])
2742
+ }
2743
+ )
2744
+ ] }, key)),
2745
+ customFields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2746
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Divider, { sx: { my: 1 } }),
2747
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.ListItem, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2748
+ import_material7.ListItemText,
2749
+ {
2750
+ primary: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", children: [
2751
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Typography, { variant: "subtitle2", children: [
2752
+ "Campos Personalizados (",
2753
+ customFields.length,
2754
+ ")"
2755
+ ] }),
2756
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.IconButton, { size: "small", onClick: () => setShowAllFields(!showAllFields), children: showAllFields ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.ExpandLess, {}) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material.ExpandMore, {}) })
2757
+ ] })
2758
+ }
2759
+ ) }),
2760
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Collapse, { in: showAllFields, children: customFields.map(({ key, label }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.ListItem, { sx: { pl: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2761
+ import_material7.ListItemText,
2762
+ {
2763
+ primary: label,
2764
+ secondary: renderFieldValue(key, displayData[key])
2765
+ }
2766
+ ) }, key)) })
2767
+ ] })
2768
+ ] }),
2769
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Box, { mt: 2, display: "flex", justifyContent: "space-between", alignItems: "center", children: [
2770
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material7.Typography, { variant: "caption", color: "text.secondary", children: [
2771
+ "\xDAltima actualizaci\xF3n: ",
2772
+ formatDate(displayData.updatedAt)
2773
+ ] }),
2774
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.IconButton, { size: "small", onClick: refreshProfile, disabled: loading, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_material7.Typography, { variant: "caption", children: "Actualizar" }) })
2775
+ ] })
2776
+ ] }) })
2777
+ ] });
2778
+ };
2779
+ var UserProfileDisplay_default = UserProfileDisplay;
2780
+
2781
+ // src/components/PublicPolicies/Policies.tsx
2782
+ var import_react15 = require("react");
2783
+ var import_react_i18next3 = require("react-i18next");
2784
+ var import_material10 = require("@mui/material");
2785
+ var import_icons_material4 = require("@mui/icons-material");
2786
+
2787
+ // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
2788
+ var import_react14 = require("react");
2789
+ var import_react_i18next2 = require("react-i18next");
2790
+ var import_material9 = require("@mui/material");
2791
+ var import_icons_material3 = require("@mui/icons-material");
2792
+
2793
+ // src/components/PublicPolicies/FieldSelector/FieldSelector.tsx
2794
+ var import_react13 = require("react");
2795
+ var import_react_i18next = require("react-i18next");
2796
+ var import_material8 = require("@mui/material");
2797
+ var import_icons_material2 = require("@mui/icons-material");
2798
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2799
+ var FieldSelector = ({
2800
+ value,
2801
+ onChange,
2802
+ availableFields,
2803
+ error,
2804
+ disabled = false
2805
+ }) => {
2806
+ const { t } = (0, import_react_i18next.useTranslation)();
2807
+ const [mode, setMode] = (0, import_react13.useState)("custom");
2808
+ const isUpdatingRef = (0, import_react13.useRef)(false);
2809
+ (0, import_react13.useEffect)(() => {
2810
+ const current = value || { allow: [], owner_allow: [], deny: [] };
2811
+ const all = new Set(availableFields);
2812
+ const allow = (current.allow || []).filter((f) => all.has(f));
2813
+ const owner = (current.owner_allow || []).filter((f) => all.has(f));
2814
+ const deny = (current.deny || []).filter((f) => all.has(f));
2815
+ availableFields.forEach((f) => {
2816
+ if (!allow.includes(f) && !owner.includes(f) && !deny.includes(f)) deny.push(f);
2817
+ });
2818
+ const normalized = { allow, owner_allow: owner, deny };
2819
+ if (JSON.stringify(normalized) !== JSON.stringify(current)) {
2820
+ onChange(normalized);
2821
+ }
2822
+ if (allow.length === availableFields.length) setMode("all");
2823
+ else if (deny.length === availableFields.length) setMode("none");
2824
+ else setMode("custom");
2825
+ }, [availableFields, value]);
2826
+ const setAllAllow = () => {
2827
+ isUpdatingRef.current = true;
2828
+ onChange({ allow: [...availableFields], owner_allow: [], deny: [] });
2829
+ setMode("all");
2830
+ setTimeout(() => {
2831
+ isUpdatingRef.current = false;
2832
+ }, 0);
2833
+ };
2834
+ const setAllDeny = () => {
2835
+ isUpdatingRef.current = true;
2836
+ onChange({ allow: [], owner_allow: [], deny: [...availableFields] });
2837
+ setMode("none");
2838
+ setTimeout(() => {
2839
+ isUpdatingRef.current = false;
2840
+ }, 0);
2841
+ };
2842
+ const getFieldState = (fieldName) => {
2843
+ if (value?.allow?.includes(fieldName)) return "allow";
2844
+ if (value?.owner_allow?.includes(fieldName)) return "owner_allow";
2845
+ return "deny";
2846
+ };
2847
+ const setFieldState = (fieldName, state) => {
2848
+ isUpdatingRef.current = true;
2849
+ const allow = new Set(value?.allow || []);
2850
+ const owner = new Set(value?.owner_allow || []);
2851
+ const deny = new Set(value?.deny || []);
2852
+ allow.delete(fieldName);
2853
+ owner.delete(fieldName);
2854
+ deny.delete(fieldName);
2855
+ if (state === "allow") allow.add(fieldName);
2856
+ if (state === "owner_allow") owner.add(fieldName);
2857
+ if (state === "deny") deny.add(fieldName);
2858
+ onChange({ allow: Array.from(allow), owner_allow: Array.from(owner), deny: Array.from(deny) });
2859
+ setMode("custom");
2860
+ setTimeout(() => {
2861
+ isUpdatingRef.current = false;
2862
+ }, 0);
2863
+ };
2864
+ if (availableFields.length === 0) {
2865
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material8.Box, { children: [
2866
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
2867
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: t("modules.form.publicPolicies.fields.conditions.noFieldsAvailable") }),
2868
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material8.FormHelperText, { error: true, sx: { mt: 1 }, children: error })
2869
+ ] });
2870
+ }
2871
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material8.Box, { children: [
2872
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
2873
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material8.Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
2874
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2875
+ import_material8.Button,
2876
+ {
2877
+ variant: mode === "all" ? "contained" : "outlined",
2878
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons_material2.SelectAll, {}),
2879
+ onClick: setAllAllow,
2880
+ disabled,
2881
+ size: "small",
2882
+ sx: {
2883
+ minWidth: 120,
2884
+ ...mode === "all" && {
2885
+ backgroundColor: "#16a34a",
2886
+ "&:hover": { backgroundColor: "#15803d" }
2887
+ }
2888
+ },
2889
+ children: t("modules.form.publicPolicies.fields.conditions.allFields")
2890
+ }
2891
+ ),
2892
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2893
+ import_material8.Button,
2894
+ {
2895
+ variant: mode === "none" ? "contained" : "outlined",
2896
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons_material2.ClearAll, {}),
2897
+ onClick: setAllDeny,
2898
+ disabled,
2899
+ size: "small",
2900
+ sx: {
2901
+ minWidth: 120,
2902
+ ...mode === "none" && {
2903
+ backgroundColor: "#cf222e",
2904
+ "&:hover": { backgroundColor: "#bc1f2c" }
2905
+ }
2906
+ },
2907
+ children: t("modules.form.publicPolicies.fields.conditions.noFields")
2908
+ }
2909
+ )
2910
+ ] }),
2911
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material8.Box, { sx: { p: 2, border: "1px solid #d1d9e0", borderRadius: 1, backgroundColor: "#f6f8fa" }, children: [
2912
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.help") }),
2913
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material8.Stack, { spacing: 1, children: availableFields.map((fieldName) => {
2914
+ const fieldState = getFieldState(fieldName);
2915
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material8.Stack, { direction: "row", spacing: 1, alignItems: "center", children: [
2916
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material8.Typography, { variant: "body2", sx: { minWidth: 100, fontFamily: "monospace" }, children: fieldName }),
2917
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_material8.ToggleButtonGroup, { value: fieldState, exclusive: true, size: "small", children: [
2918
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2919
+ import_material8.ToggleButton,
2920
+ {
2921
+ value: "allow",
2922
+ onClick: () => setFieldState(fieldName, "allow"),
2923
+ disabled,
2924
+ sx: {
2925
+ px: 2,
2926
+ color: fieldState === "allow" ? "#ffffff" : "#6b7280",
2927
+ backgroundColor: fieldState === "allow" ? "#16a34a" : "#f3f4f6",
2928
+ borderColor: fieldState === "allow" ? "#16a34a" : "#d1d5db",
2929
+ "&:hover": {
2930
+ backgroundColor: fieldState === "allow" ? "#15803d" : "#e5e7eb",
2931
+ borderColor: fieldState === "allow" ? "#15803d" : "#9ca3af"
2932
+ },
2933
+ "&.Mui-selected": {
2934
+ backgroundColor: "#16a34a",
2935
+ color: "#ffffff",
2936
+ "&:hover": {
2937
+ backgroundColor: "#15803d"
2938
+ }
2939
+ }
2940
+ },
2941
+ children: [
2942
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons_material2.CheckCircle, { sx: { fontSize: 16, mr: 0.5 } }),
2943
+ t("modules.form.publicPolicies.fields.conditions.states.allow")
2944
+ ]
2945
+ }
2946
+ ),
2947
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2948
+ import_material8.ToggleButton,
2949
+ {
2950
+ value: "owner_allow",
2951
+ onClick: () => setFieldState(fieldName, "owner_allow"),
2952
+ disabled,
2953
+ sx: {
2954
+ px: 2,
2955
+ color: fieldState === "owner_allow" ? "#ffffff" : "#6b7280",
2956
+ backgroundColor: fieldState === "owner_allow" ? "#0ea5e9" : "#f3f4f6",
2957
+ borderColor: fieldState === "owner_allow" ? "#0ea5e9" : "#d1d5db",
2958
+ "&:hover": {
2959
+ backgroundColor: fieldState === "owner_allow" ? "#0284c7" : "#e5e7eb",
2960
+ borderColor: fieldState === "owner_allow" ? "#0284c7" : "#9ca3af"
2961
+ },
2962
+ "&.Mui-selected": {
2963
+ backgroundColor: "#0ea5e9",
2964
+ color: "#ffffff",
2965
+ "&:hover": {
2966
+ backgroundColor: "#0284c7"
2967
+ }
2968
+ }
2969
+ },
2970
+ children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
2971
+ }
2972
+ ),
2973
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2974
+ import_material8.ToggleButton,
2975
+ {
2976
+ value: "deny",
2977
+ onClick: () => setFieldState(fieldName, "deny"),
2978
+ disabled,
2979
+ sx: {
2980
+ px: 2,
2981
+ color: fieldState === "deny" ? "#ffffff" : "#6b7280",
2982
+ backgroundColor: fieldState === "deny" ? "#dc2626" : "#f3f4f6",
2983
+ borderColor: fieldState === "deny" ? "#dc2626" : "#d1d5db",
2984
+ "&:hover": {
2985
+ backgroundColor: fieldState === "deny" ? "#b91c1c" : "#e5e7eb",
2986
+ borderColor: fieldState === "deny" ? "#b91c1c" : "#9ca3af"
2987
+ },
2988
+ "&.Mui-selected": {
2989
+ backgroundColor: "#dc2626",
2990
+ color: "#ffffff",
2991
+ "&:hover": {
2992
+ backgroundColor: "#b91c1c"
2993
+ }
2994
+ }
2995
+ },
2996
+ children: [
2997
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons_material2.Cancel, { sx: { fontSize: 16, mr: 0.5 } }),
2998
+ t("modules.form.publicPolicies.fields.conditions.states.deny")
2999
+ ]
3000
+ }
3001
+ )
3002
+ ] })
3003
+ ] }, fieldName);
3004
+ }) })
3005
+ ] }),
3006
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_material8.FormHelperText, { error: true, sx: { mt: 1 }, children: error })
3007
+ ] });
3008
+ };
3009
+ var FieldSelector_default = FieldSelector;
3010
+
3011
+ // src/components/PublicPolicies/constants.ts
3012
+ var POLICY_ACTIONS = ["create", "read", "update", "delete"];
3013
+ var PREFERRED_POLICY_ORDER = [
3014
+ "create",
3015
+ "read",
3016
+ "update",
3017
+ "delete"
3018
+ ];
3019
+
3020
+ // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
3021
+ var import_jsx_runtime13 = require("react/jsx-runtime");
3022
+ var PolicyItem = (0, import_react14.forwardRef)(({
3023
+ policy,
3024
+ onChange,
3025
+ onRemove,
3026
+ availableFields,
3027
+ isSubmitting = false,
3028
+ usedActions,
3029
+ error
3030
+ }, ref) => {
3031
+ const { t } = (0, import_react_i18next2.useTranslation)();
3032
+ const takenActions = new Set(Array.from(usedActions || []));
3033
+ takenActions.delete(policy.action);
3034
+ const actionOptions = POLICY_ACTIONS.map((a) => ({
3035
+ value: a,
3036
+ label: t(`modules.form.publicPolicies.fields.action.options.${a}`)
3037
+ }));
3038
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3039
+ import_material9.Paper,
3040
+ {
3041
+ ref,
3042
+ sx: {
3043
+ p: 3,
3044
+ border: "1px solid #d1d9e0",
3045
+ borderRadius: 2,
3046
+ position: "relative",
3047
+ backgroundColor: "#ffffff"
3048
+ },
3049
+ children: [
3050
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 3 }, children: [
3051
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3052
+ import_material9.Typography,
3053
+ {
3054
+ variant: "subtitle1",
3055
+ sx: {
3056
+ fontWeight: 600,
3057
+ color: "#111418",
3058
+ fontSize: "1rem"
3059
+ },
3060
+ children: t("modules.form.publicPolicies.policyTitle")
3061
+ }
3062
+ ),
3063
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3064
+ import_material9.IconButton,
3065
+ {
3066
+ onClick: onRemove,
3067
+ size: "small",
3068
+ disabled: isSubmitting,
3069
+ "aria-label": t("modules.form.publicPolicies.removePolicy"),
3070
+ sx: {
3071
+ color: "#656d76",
3072
+ "&:hover": {
3073
+ color: "#cf222e",
3074
+ backgroundColor: "rgba(207, 34, 46, 0.1)"
3075
+ }
3076
+ },
3077
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_icons_material3.Delete, {})
3078
+ }
3079
+ )
3080
+ ] }),
3081
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Stack, { spacing: 3, children: [
3082
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material9.Stack, { direction: { xs: "column", md: "row" }, spacing: 2, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material9.Box, { sx: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.FormControl, { fullWidth: true, children: [
3083
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material9.InputLabel, { children: t("modules.form.publicPolicies.fields.action.label") }),
3084
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3085
+ import_material9.Select,
3086
+ {
3087
+ value: policy.action,
3088
+ label: t("modules.form.publicPolicies.fields.action.label"),
3089
+ disabled: isSubmitting,
3090
+ onChange: (e) => {
3091
+ const newAction = e.target.value;
3092
+ const next = { ...policy, action: newAction };
3093
+ if (newAction === "delete") {
3094
+ next.permission = "deny";
3095
+ delete next.fields;
3096
+ } else {
3097
+ next.fields = { allow: [], owner_allow: [], deny: availableFields };
3098
+ delete next.permission;
3099
+ }
3100
+ onChange(next);
3101
+ },
3102
+ sx: {
3103
+ backgroundColor: "#ffffff",
3104
+ "&:hover .MuiOutlinedInput-notchedOutline": {
3105
+ borderColor: "#8c959f"
3106
+ },
3107
+ "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
3108
+ borderColor: "#0969da",
3109
+ borderWidth: 2
3110
+ }
3111
+ },
3112
+ children: actionOptions.map((option) => {
3113
+ const disabledOption = takenActions.has(option.value);
3114
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material9.MenuItem, { value: option.value, disabled: disabledOption, children: option.label }, option.value);
3115
+ })
3116
+ }
3117
+ ),
3118
+ error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material9.FormHelperText, { error: true, children: error })
3119
+ ] }) }) }),
3120
+ policy.action === "delete" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Box, { children: [
3121
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material9.Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
3122
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
3123
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3124
+ import_material9.Button,
3125
+ {
3126
+ variant: policy.permission === "*" ? "contained" : "outlined",
3127
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_icons_material3.SelectAll, {}),
3128
+ onClick: () => onChange({ ...policy, permission: "*" }),
3129
+ disabled: isSubmitting,
3130
+ size: "small",
3131
+ sx: {
3132
+ minWidth: 140,
3133
+ whiteSpace: "nowrap",
3134
+ ...policy.permission === "*" && {
3135
+ backgroundColor: "#16a34a",
3136
+ "&:hover": { backgroundColor: "#15803d" }
3137
+ }
3138
+ },
3139
+ children: t("modules.form.publicPolicies.fields.conditions.allFields")
3140
+ }
3141
+ ),
3142
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3143
+ import_material9.Button,
3144
+ {
3145
+ variant: policy.permission === "owner" ? "contained" : "outlined",
3146
+ onClick: () => onChange({ ...policy, permission: "owner" }),
3147
+ disabled: isSubmitting,
3148
+ size: "small",
3149
+ sx: {
3150
+ minWidth: 140,
3151
+ whiteSpace: "nowrap",
3152
+ ...policy.permission === "owner" && {
3153
+ backgroundColor: "#0ea5e9",
3154
+ "&:hover": { backgroundColor: "#0284c7" }
3155
+ }
3156
+ },
3157
+ children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
3158
+ }
3159
+ ),
3160
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3161
+ import_material9.Button,
3162
+ {
3163
+ variant: policy.permission === "deny" ? "contained" : "outlined",
3164
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_icons_material3.ClearAll, {}),
3165
+ onClick: () => onChange({ ...policy, permission: "deny" }),
3166
+ disabled: isSubmitting,
3167
+ size: "small",
3168
+ sx: {
3169
+ minWidth: 140,
3170
+ whiteSpace: "nowrap",
3171
+ ...policy.permission === "deny" && {
3172
+ backgroundColor: "#cf222e",
3173
+ "&:hover": { backgroundColor: "#bc1f2c" }
3174
+ }
3175
+ },
3176
+ children: t("modules.form.publicPolicies.fields.conditions.noFields")
3177
+ }
3178
+ )
3179
+ ] })
3180
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3181
+ FieldSelector_default,
3182
+ {
3183
+ value: policy.fields || { allow: [], owner_allow: [], deny: [] },
3184
+ onChange: (nextFields) => onChange({ ...policy, fields: nextFields }),
3185
+ availableFields,
3186
+ disabled: isSubmitting
3187
+ }
3188
+ ),
3189
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material9.Paper, { variant: "outlined", sx: { p: 2, backgroundColor: "#f9fafb" }, children: policy.action === "delete" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
3190
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Box, { component: "span", sx: {
3191
+ color: policy.permission === "*" ? "#16a34a" : policy.permission === "owner" ? "#0ea5e9" : "#dc2626"
3192
+ }, children: [
3193
+ t("modules.form.publicPolicies.fields.conditions.states.allow"),
3194
+ ":"
3195
+ ] }),
3196
+ " ",
3197
+ policy.permission || "-"
3198
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Stack, { spacing: 0.5, divider: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material9.Divider, { sx: { borderColor: "#e5e7eb" } }), children: [
3199
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
3200
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Box, { component: "span", sx: { color: "#16a34a" }, children: [
3201
+ t("modules.form.publicPolicies.fields.conditions.states.allow"),
3202
+ ":"
3203
+ ] }),
3204
+ " ",
3205
+ (policy?.fields?.allow || []).join(", ") || "-"
3206
+ ] }),
3207
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
3208
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Box, { component: "span", sx: { color: "#0ea5e9" }, children: [
3209
+ t("modules.form.publicPolicies.fields.conditions.states.ownerAllow"),
3210
+ ":"
3211
+ ] }),
3212
+ " ",
3213
+ (policy?.fields?.owner_allow || []).join(", ") || "-"
3214
+ ] }),
3215
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Typography, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
3216
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material9.Box, { component: "span", sx: { color: "#dc2626" }, children: [
3217
+ t("modules.form.publicPolicies.fields.conditions.states.deny"),
3218
+ ":"
3219
+ ] }),
3220
+ " ",
3221
+ (policy?.fields?.deny || []).join(", ") || "-"
3222
+ ] })
3223
+ ] }) })
3224
+ ] })
3225
+ ]
3182
3226
  }
3183
- }, [sessionManager]);
3184
- const clearError = (0, import_react15.useCallback)(() => {
3185
- setState((prev) => ({ ...prev, error: null }));
3186
- }, []);
3187
- const getTokenInfo = (0, import_react15.useCallback)(() => {
3188
- return sessionManager.getTokenInfo();
3189
- }, [sessionManager]);
3190
- (0, import_react15.useEffect)(() => {
3191
- initialize();
3192
- }, [initialize]);
3193
- return {
3194
- // Estado
3195
- ...state,
3196
- // Acciones
3197
- login,
3198
- logout,
3199
- refreshTokens,
3200
- clearError,
3201
- getTokenInfo,
3202
- // Utilidades
3203
- isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
3204
- // 5 minutos
3205
- expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
3206
- refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
3207
- };
3208
- }
3227
+ );
3228
+ });
3229
+ var PolicyItem_default = PolicyItem;
3209
3230
 
3210
- // src/providers/SessionProvider.tsx
3211
- var import_react16 = require("react");
3231
+ // src/components/PublicPolicies/Policies.tsx
3212
3232
  var import_jsx_runtime14 = require("react/jsx-runtime");
3213
- var SessionContext = (0, import_react16.createContext)(void 0);
3214
- function SessionProvider({ children, options = {} }) {
3215
- const sessionHook = useSession(options);
3216
- const sessionData = (0, import_react16.useMemo)(() => {
3217
- if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
3218
- return null;
3233
+ var generateId = () => {
3234
+ const c = globalThis?.crypto;
3235
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
3236
+ return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
3237
+ };
3238
+ var Policies = ({
3239
+ policies,
3240
+ onChange,
3241
+ availableFields,
3242
+ errors,
3243
+ isSubmitting = false
3244
+ }) => {
3245
+ const { t } = (0, import_react_i18next3.useTranslation)();
3246
+ const policyRefs = (0, import_react15.useRef)({});
3247
+ const takenActions = new Set((policies || []).map((p) => p.action).filter(Boolean));
3248
+ const remainingActions = PREFERRED_POLICY_ORDER.filter((a) => !takenActions.has(a));
3249
+ const canAddPolicy = remainingActions.length > 0;
3250
+ const addPolicy = () => {
3251
+ const defaultAction = remainingActions[0] || "create";
3252
+ const newPolicy = {
3253
+ id: generateId(),
3254
+ action: defaultAction
3255
+ };
3256
+ if (defaultAction === "delete") {
3257
+ newPolicy.permission = "deny";
3258
+ } else {
3259
+ newPolicy.fields = {
3260
+ allow: [],
3261
+ owner_allow: [],
3262
+ deny: availableFields
3263
+ };
3219
3264
  }
3220
- try {
3221
- const decoded = decodeJwtSafely(sessionHook.tokens.accessToken);
3222
- if (decoded && decoded.sub && decoded.email && decoded.subscriber) {
3223
- const result = {
3224
- _id: decoded.sub,
3225
- email: decoded.email,
3226
- subscriberKey: decoded.subscriber
3227
- };
3228
- Object.keys(decoded).forEach((key) => {
3229
- if (!["sub", "email", "subscriber"].includes(key)) {
3230
- result[key] = decoded[key];
3231
- }
3232
- });
3233
- return result;
3265
+ const next = [...policies || [], newPolicy];
3266
+ onChange(next);
3267
+ setTimeout(() => {
3268
+ const newIndex = next.length - 1;
3269
+ const el = policyRefs.current[newIndex];
3270
+ if (el) {
3271
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
3234
3272
  }
3235
- } catch (error) {
3236
- console.error("Error decoding JWT token for sessionData:", error);
3237
- }
3238
- return null;
3239
- }, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
3240
- const contextValue = {
3241
- ...sessionHook,
3242
- sessionData
3273
+ }, 100);
3243
3274
  };
3244
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SessionContext.Provider, { value: contextValue, children });
3245
- }
3246
- function useSessionContext() {
3247
- const context = (0, import_react16.useContext)(SessionContext);
3248
- if (context === void 0) {
3249
- throw new Error("useSessionContext must be used within a SessionProvider");
3250
- }
3251
- return context;
3252
- }
3253
- function ProtectedRoute({
3254
- children,
3255
- fallback = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { children: "Please log in to access this content" }),
3256
- redirectTo
3257
- }) {
3258
- const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
3259
- if (!isInitialized || isLoading) {
3260
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { children: "Loading..." });
3261
- }
3262
- if (!isAuthenticated) {
3263
- if (redirectTo) {
3264
- redirectTo();
3265
- return null;
3266
- }
3267
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: fallback });
3268
- }
3269
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children });
3270
- }
3271
- function SessionDebugInfo() {
3272
- const session = useSessionContext();
3273
- if (!session.isInitialized) {
3274
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { children: "Session not initialized" });
3275
- }
3276
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: {
3277
- padding: "10px",
3278
- margin: "10px",
3279
- border: "1px solid #ccc",
3280
- borderRadius: "4px",
3281
- fontSize: "12px",
3282
- fontFamily: "monospace"
3283
- }, children: [
3284
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h4", { children: "Session Debug Info" }),
3285
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3286
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: "Authenticated:" }),
3287
- " ",
3288
- session.isAuthenticated ? "Yes" : "No"
3289
- ] }),
3290
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3291
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: "Loading:" }),
3292
- " ",
3293
- session.isLoading ? "Yes" : "No"
3294
- ] }),
3295
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3296
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: "Error:" }),
3297
- " ",
3298
- session.error || "None"
3299
- ] }),
3300
- session.tokens && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
3301
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3302
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: "Access Token:" }),
3303
- " ",
3304
- session.tokens.accessToken.substring(0, 20),
3305
- "..."
3306
- ] }),
3307
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3308
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: "Refresh Token:" }),
3309
- " ",
3310
- session.tokens.refreshToken.substring(0, 20),
3311
- "..."
3312
- ] }),
3313
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3314
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: "Access Expires In:" }),
3315
- " ",
3316
- Math.round(session.expiresIn / 1e3 / 60),
3317
- " minutes"
3318
- ] }),
3319
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3320
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: "Refresh Expires In:" }),
3321
- " ",
3322
- Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
3323
- " hours"
3324
- ] }),
3325
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3326
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: "Expiring Soon:" }),
3327
- " ",
3328
- session.isExpiringSoon ? "Yes" : "No"
3275
+ const removePolicy = (index) => {
3276
+ const next = [...policies];
3277
+ next.splice(index, 1);
3278
+ onChange(next);
3279
+ };
3280
+ const arrayError = (() => {
3281
+ if (!errors) return null;
3282
+ if (typeof errors === "string") return errors;
3283
+ const msg = errors._error;
3284
+ return typeof msg === "string" ? msg : null;
3285
+ })();
3286
+ const usedActions = new Set((policies || []).map((p) => p.action));
3287
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
3288
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material10.Divider, { sx: { borderColor: "#e0e4e7" } }),
3289
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material10.Box, { children: [
3290
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material10.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material10.Box, { children: [
3291
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3292
+ import_material10.Typography,
3293
+ {
3294
+ variant: "h6",
3295
+ sx: {
3296
+ fontWeight: 600,
3297
+ color: "#111418",
3298
+ mb: 1
3299
+ },
3300
+ children: t("modules.form.publicPolicies.title")
3301
+ }
3302
+ ),
3303
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3304
+ import_material10.Typography,
3305
+ {
3306
+ variant: "body2",
3307
+ color: "text.secondary",
3308
+ sx: { fontSize: "0.875rem" },
3309
+ children: t("modules.form.publicPolicies.description")
3310
+ }
3311
+ )
3312
+ ] }) }),
3313
+ arrayError && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material10.Alert, { severity: "error", sx: { mb: 3 }, children: arrayError }),
3314
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material10.Stack, { spacing: 3, children: [
3315
+ (policies || []).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material10.Alert, { severity: "info", children: t("modules.form.publicPolicies.noPolicies") }) : policies.map((policy, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3316
+ PolicyItem_default,
3317
+ {
3318
+ ref: (el) => {
3319
+ policyRefs.current[index] = el;
3320
+ },
3321
+ policy,
3322
+ onChange: (nextPolicy) => {
3323
+ const next = [...policies];
3324
+ next[index] = nextPolicy;
3325
+ onChange(next);
3326
+ },
3327
+ onRemove: () => removePolicy(index),
3328
+ availableFields,
3329
+ isSubmitting,
3330
+ usedActions,
3331
+ error: typeof errors === "object" && errors && policy.id in errors ? errors[policy.id] : void 0
3332
+ },
3333
+ policy.id
3334
+ )),
3335
+ canAddPolicy && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material10.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3336
+ import_material10.Button,
3337
+ {
3338
+ type: "button",
3339
+ variant: "outlined",
3340
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons_material4.Add, {}),
3341
+ onClick: addPolicy,
3342
+ disabled: isSubmitting,
3343
+ sx: {
3344
+ borderColor: "#d0d7de",
3345
+ color: "#656d76",
3346
+ "&:hover": {
3347
+ borderColor: "#8c959f",
3348
+ backgroundColor: "transparent"
3349
+ }
3350
+ },
3351
+ children: t("modules.form.publicPolicies.addPolicy")
3352
+ }
3353
+ ) })
3329
3354
  ] })
3330
3355
  ] })
3331
3356
  ] });
3332
- }
3357
+ };
3358
+ var Policies_default = Policies;
3333
3359
 
3334
3360
  // src/components/LoginComponent.tsx
3335
- var import_react17 = require("react");
3361
+ var import_react16 = require("react");
3336
3362
  var import_material11 = require("@mui/material");
3337
3363
  var import_jsx_runtime15 = require("react/jsx-runtime");
3338
3364
  function LoginComponent() {
3339
- const [email, setEmail] = (0, import_react17.useState)("");
3340
- const [password, setPassword] = (0, import_react17.useState)("");
3341
- const [showForm, setShowForm] = (0, import_react17.useState)(false);
3365
+ const [email, setEmail] = (0, import_react16.useState)("");
3366
+ const [password, setPassword] = (0, import_react16.useState)("");
3367
+ const [showForm, setShowForm] = (0, import_react16.useState)(false);
3342
3368
  const {
3343
3369
  isAuthenticated,
3344
3370
  isLoading,
@@ -3485,29 +3511,29 @@ function SessionStatus() {
3485
3511
  }
3486
3512
 
3487
3513
  // src/hooks/useUserData.ts
3488
- var import_react18 = require("react");
3489
- var import_crudify_browser5 = __toESM(require("@nocios/crudify-browser"));
3514
+ var import_react17 = require("react");
3515
+ var import_crudify_browser4 = __toESM(require("@nocios/crudify-browser"));
3490
3516
  var useUserData = (options = {}) => {
3491
3517
  const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
3492
3518
  const { isAuthenticated, isInitialized, sessionData, tokens } = useSessionContext();
3493
- const [userData, setUserData] = (0, import_react18.useState)(null);
3494
- const [loading, setLoading] = (0, import_react18.useState)(false);
3495
- const [error, setError] = (0, import_react18.useState)(null);
3496
- const abortControllerRef = (0, import_react18.useRef)(null);
3497
- const mountedRef = (0, import_react18.useRef)(true);
3498
- const requestIdRef = (0, import_react18.useRef)(0);
3499
- const retryCountRef = (0, import_react18.useRef)(0);
3500
- const getUserEmail = (0, import_react18.useCallback)(() => {
3519
+ const [userData, setUserData] = (0, import_react17.useState)(null);
3520
+ const [loading, setLoading] = (0, import_react17.useState)(false);
3521
+ const [error, setError] = (0, import_react17.useState)(null);
3522
+ const abortControllerRef = (0, import_react17.useRef)(null);
3523
+ const mountedRef = (0, import_react17.useRef)(true);
3524
+ const requestIdRef = (0, import_react17.useRef)(0);
3525
+ const retryCountRef = (0, import_react17.useRef)(0);
3526
+ const getUserEmail = (0, import_react17.useCallback)(() => {
3501
3527
  if (!sessionData) return null;
3502
3528
  return sessionData.email || sessionData["cognito:username"] || null;
3503
3529
  }, [sessionData]);
3504
- const clearProfile = (0, import_react18.useCallback)(() => {
3530
+ const clearProfile = (0, import_react17.useCallback)(() => {
3505
3531
  setUserData(null);
3506
3532
  setError(null);
3507
3533
  setLoading(false);
3508
3534
  retryCountRef.current = 0;
3509
3535
  }, []);
3510
- const refreshProfile = (0, import_react18.useCallback)(async () => {
3536
+ const refreshProfile = (0, import_react17.useCallback)(async () => {
3511
3537
  const userEmail = getUserEmail();
3512
3538
  console.log("\u{1F464} useUserData - Refreshing profile for:", userEmail);
3513
3539
  if (!userEmail) {
@@ -3536,7 +3562,7 @@ var useUserData = (options = {}) => {
3536
3562
  setError(null);
3537
3563
  }
3538
3564
  console.log("\u{1F464} useUserData - Fetching profile data from database");
3539
- const response = await import_crudify_browser5.default.readItems("users", {
3565
+ const response = await import_crudify_browser4.default.readItems("users", {
3540
3566
  filter: { email: userEmail },
3541
3567
  pagination: { limit: 1 }
3542
3568
  });
@@ -3639,14 +3665,14 @@ var useUserData = (options = {}) => {
3639
3665
  }
3640
3666
  }
3641
3667
  }, [isInitialized, getUserEmail, retryOnError, maxRetries]);
3642
- (0, import_react18.useEffect)(() => {
3668
+ (0, import_react17.useEffect)(() => {
3643
3669
  if (autoFetch && isAuthenticated && isInitialized) {
3644
3670
  refreshProfile();
3645
3671
  } else if (!isAuthenticated) {
3646
3672
  clearProfile();
3647
3673
  }
3648
3674
  }, [autoFetch, isAuthenticated, isInitialized, refreshProfile, clearProfile]);
3649
- (0, import_react18.useEffect)(() => {
3675
+ (0, import_react17.useEffect)(() => {
3650
3676
  mountedRef.current = true;
3651
3677
  return () => {
3652
3678
  mountedRef.current = false;
@@ -3672,7 +3698,7 @@ var useUserData = (options = {}) => {
3672
3698
  };
3673
3699
 
3674
3700
  // src/hooks/useAuth.ts
3675
- var import_react19 = require("react");
3701
+ var import_react18 = require("react");
3676
3702
  var useAuth = () => {
3677
3703
  const {
3678
3704
  isAuthenticated,
@@ -3690,7 +3716,7 @@ var useAuth = () => {
3690
3716
  expiresIn,
3691
3717
  refreshExpiresIn
3692
3718
  } = useSessionContext();
3693
- const setToken = (0, import_react19.useCallback)((token) => {
3719
+ const setToken = (0, import_react18.useCallback)((token) => {
3694
3720
  if (token) {
3695
3721
  console.warn("useAuth.setToken() is deprecated. Use login() method instead for better security.");
3696
3722
  } else {
@@ -3725,8 +3751,8 @@ var useAuth = () => {
3725
3751
  };
3726
3752
 
3727
3753
  // src/hooks/useData.ts
3728
- var import_react20 = require("react");
3729
- var import_crudify_browser6 = __toESM(require("@nocios/crudify-browser"));
3754
+ var import_react19 = require("react");
3755
+ var import_crudify_browser5 = __toESM(require("@nocios/crudify-browser"));
3730
3756
  var useData = () => {
3731
3757
  const {
3732
3758
  isInitialized,
@@ -3735,10 +3761,10 @@ var useData = () => {
3735
3761
  isAuthenticated,
3736
3762
  login: sessionLogin
3737
3763
  } = useSessionContext();
3738
- const isReady = (0, import_react20.useCallback)(() => {
3764
+ const isReady = (0, import_react19.useCallback)(() => {
3739
3765
  return isInitialized && !isLoading && !error;
3740
3766
  }, [isInitialized, isLoading, error]);
3741
- const waitForReady = (0, import_react20.useCallback)(async () => {
3767
+ const waitForReady = (0, import_react19.useCallback)(async () => {
3742
3768
  return new Promise((resolve, reject) => {
3743
3769
  const checkReady = () => {
3744
3770
  if (isReady()) {
@@ -3752,36 +3778,36 @@ var useData = () => {
3752
3778
  checkReady();
3753
3779
  });
3754
3780
  }, [isReady, error]);
3755
- const ensureReady = (0, import_react20.useCallback)(async () => {
3781
+ const ensureReady = (0, import_react19.useCallback)(async () => {
3756
3782
  if (!isReady()) {
3757
3783
  throw new Error("System not ready. Check isInitialized, isLoading, and error states.");
3758
3784
  }
3759
3785
  }, [isReady]);
3760
- const readItems = (0, import_react20.useCallback)(async (moduleKey, filter, options) => {
3786
+ const readItems = (0, import_react19.useCallback)(async (moduleKey, filter, options) => {
3761
3787
  await ensureReady();
3762
- return await import_crudify_browser6.default.readItems(moduleKey, filter || {}, options);
3788
+ return await import_crudify_browser5.default.readItems(moduleKey, filter || {}, options);
3763
3789
  }, [ensureReady]);
3764
- const readItem = (0, import_react20.useCallback)(async (moduleKey, filter, options) => {
3790
+ const readItem = (0, import_react19.useCallback)(async (moduleKey, filter, options) => {
3765
3791
  await ensureReady();
3766
- return await import_crudify_browser6.default.readItem(moduleKey, filter, options);
3792
+ return await import_crudify_browser5.default.readItem(moduleKey, filter, options);
3767
3793
  }, [ensureReady]);
3768
- const createItem = (0, import_react20.useCallback)(async (moduleKey, data, options) => {
3794
+ const createItem = (0, import_react19.useCallback)(async (moduleKey, data, options) => {
3769
3795
  await ensureReady();
3770
- return await import_crudify_browser6.default.createItem(moduleKey, data, options);
3796
+ return await import_crudify_browser5.default.createItem(moduleKey, data, options);
3771
3797
  }, [ensureReady]);
3772
- const updateItem = (0, import_react20.useCallback)(async (moduleKey, data, options) => {
3798
+ const updateItem = (0, import_react19.useCallback)(async (moduleKey, data, options) => {
3773
3799
  await ensureReady();
3774
- return await import_crudify_browser6.default.updateItem(moduleKey, data, options);
3800
+ return await import_crudify_browser5.default.updateItem(moduleKey, data, options);
3775
3801
  }, [ensureReady]);
3776
- const deleteItem = (0, import_react20.useCallback)(async (moduleKey, id, options) => {
3802
+ const deleteItem = (0, import_react19.useCallback)(async (moduleKey, id, options) => {
3777
3803
  await ensureReady();
3778
- return await import_crudify_browser6.default.deleteItem(moduleKey, id, options);
3804
+ return await import_crudify_browser5.default.deleteItem(moduleKey, id, options);
3779
3805
  }, [ensureReady]);
3780
- const transaction = (0, import_react20.useCallback)(async (operations, options) => {
3806
+ const transaction = (0, import_react19.useCallback)(async (operations, options) => {
3781
3807
  await ensureReady();
3782
- return await import_crudify_browser6.default.transaction(operations, options);
3808
+ return await import_crudify_browser5.default.transaction(operations, options);
3783
3809
  }, [ensureReady]);
3784
- const login = (0, import_react20.useCallback)(async (email, password) => {
3810
+ const login = (0, import_react19.useCallback)(async (email, password) => {
3785
3811
  try {
3786
3812
  const result = await sessionLogin(email, password);
3787
3813
  if (result.success) {