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