@nocios/crudify-ui 1.0.44 → 1.0.46

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.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _nocios_crudify_browser from '@nocios/crudify-browser';
1
2
  export * from '@nocios/crudify-browser';
2
3
  export { default as crudify } from '@nocios/crudify-browser';
3
4
  import React from 'react';
@@ -27,6 +28,47 @@ interface CrudifyLoginProps {
27
28
 
28
29
  declare const CrudifyLogin: React.FC<CrudifyLoginProps>;
29
30
 
31
+ interface UseUserProfileOptions {
32
+ autoFetch?: boolean;
33
+ retryOnError?: boolean;
34
+ maxRetries?: number;
35
+ }
36
+ interface UseUserProfileReturn {
37
+ userProfile: any | null;
38
+ loading: boolean;
39
+ error: string | null;
40
+ refreshProfile: () => Promise<void>;
41
+ clearProfile: () => void;
42
+ }
43
+ declare const useUserProfile: (options?: UseUserProfileOptions) => UseUserProfileReturn;
44
+
45
+ interface UseCrudifyLoginOptions {
46
+ showErrorNotifications?: boolean;
47
+ showSuccessNotifications?: boolean;
48
+ }
49
+ declare const useCrudifyLogin: (config: CrudifyLoginConfig, _options?: UseCrudifyLoginOptions) => {
50
+ crudify: {
51
+ login: (identifier: string, password: string) => Promise<_nocios_crudify_browser.CrudifyResponse>;
52
+ transaction: (data: any, options?: {
53
+ signal?: AbortSignal;
54
+ }) => Promise<_nocios_crudify_browser.CrudifyResponse>;
55
+ } | null;
56
+ };
57
+
58
+ interface JWTPayload {
59
+ email?: string;
60
+ "cognito:username"?: string;
61
+ sub?: string;
62
+ exp?: number;
63
+ iat?: number;
64
+ iss?: string;
65
+ aud?: string;
66
+ [key: string]: any;
67
+ }
68
+ declare const decodeJwtSafely: (token: string) => JWTPayload | null;
69
+ declare const getCurrentUserEmail: () => string | null;
70
+ declare const isTokenExpired: (token: string) => boolean;
71
+
30
72
  declare const getCookie: (name: string) => string | null;
31
73
 
32
74
  declare class SecureStorage {
@@ -43,4 +85,4 @@ declare class SecureStorage {
43
85
  declare const secureSessionStorage: SecureStorage;
44
86
  declare const secureLocalStorage: SecureStorage;
45
87
 
46
- export { type BoxScreenType, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, getCookie, secureLocalStorage, secureSessionStorage };
88
+ export { type BoxScreenType, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, decodeJwtSafely, getCookie, getCurrentUserEmail, isTokenExpired, secureLocalStorage, secureSessionStorage, useCrudifyLogin, useUserProfile };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _nocios_crudify_browser from '@nocios/crudify-browser';
1
2
  export * from '@nocios/crudify-browser';
2
3
  export { default as crudify } from '@nocios/crudify-browser';
3
4
  import React from 'react';
@@ -27,6 +28,47 @@ interface CrudifyLoginProps {
27
28
 
28
29
  declare const CrudifyLogin: React.FC<CrudifyLoginProps>;
29
30
 
31
+ interface UseUserProfileOptions {
32
+ autoFetch?: boolean;
33
+ retryOnError?: boolean;
34
+ maxRetries?: number;
35
+ }
36
+ interface UseUserProfileReturn {
37
+ userProfile: any | null;
38
+ loading: boolean;
39
+ error: string | null;
40
+ refreshProfile: () => Promise<void>;
41
+ clearProfile: () => void;
42
+ }
43
+ declare const useUserProfile: (options?: UseUserProfileOptions) => UseUserProfileReturn;
44
+
45
+ interface UseCrudifyLoginOptions {
46
+ showErrorNotifications?: boolean;
47
+ showSuccessNotifications?: boolean;
48
+ }
49
+ declare const useCrudifyLogin: (config: CrudifyLoginConfig, _options?: UseCrudifyLoginOptions) => {
50
+ crudify: {
51
+ login: (identifier: string, password: string) => Promise<_nocios_crudify_browser.CrudifyResponse>;
52
+ transaction: (data: any, options?: {
53
+ signal?: AbortSignal;
54
+ }) => Promise<_nocios_crudify_browser.CrudifyResponse>;
55
+ } | null;
56
+ };
57
+
58
+ interface JWTPayload {
59
+ email?: string;
60
+ "cognito:username"?: string;
61
+ sub?: string;
62
+ exp?: number;
63
+ iat?: number;
64
+ iss?: string;
65
+ aud?: string;
66
+ [key: string]: any;
67
+ }
68
+ declare const decodeJwtSafely: (token: string) => JWTPayload | null;
69
+ declare const getCurrentUserEmail: () => string | null;
70
+ declare const isTokenExpired: (token: string) => boolean;
71
+
30
72
  declare const getCookie: (name: string) => string | null;
31
73
 
32
74
  declare class SecureStorage {
@@ -43,4 +85,4 @@ declare class SecureStorage {
43
85
  declare const secureSessionStorage: SecureStorage;
44
86
  declare const secureLocalStorage: SecureStorage;
45
87
 
46
- export { type BoxScreenType, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, getCookie, secureLocalStorage, secureSessionStorage };
88
+ export { type BoxScreenType, CrudifyLogin, type CrudifyLoginConfig, type CrudifyLoginProps, decodeJwtSafely, getCookie, getCurrentUserEmail, isTokenExpired, secureLocalStorage, secureSessionStorage, useCrudifyLogin, useUserProfile };
package/dist/index.js CHANGED
@@ -32,13 +32,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
34
  CrudifyLogin: () => CrudifyLogin_default,
35
- crudify: () => import_crudify_browser2.default,
35
+ crudify: () => import_crudify_browser3.default,
36
+ decodeJwtSafely: () => decodeJwtSafely,
36
37
  getCookie: () => getCookie,
38
+ getCurrentUserEmail: () => getCurrentUserEmail,
39
+ isTokenExpired: () => isTokenExpired,
37
40
  secureLocalStorage: () => secureLocalStorage,
38
- secureSessionStorage: () => secureSessionStorage
41
+ secureSessionStorage: () => secureSessionStorage,
42
+ useCrudifyLogin: () => useCrudifyLogin,
43
+ useUserProfile: () => useUserProfile
39
44
  });
40
45
  module.exports = __toCommonJS(index_exports);
41
- var import_crudify_browser2 = __toESM(require("@nocios/crudify-browser"));
46
+ var import_crudify_browser3 = __toESM(require("@nocios/crudify-browser"));
42
47
  __reExport(index_exports, require("@nocios/crudify-browser"), module.exports);
43
48
 
44
49
  // src/components/CrudifyLogin/index.tsx
@@ -64,10 +69,18 @@ var getCookie = (name) => {
64
69
  // src/components/CrudifyLogin/hooks/useCrudifyLogin.ts
65
70
  var useCrudifyLogin = (config, _options = {}) => {
66
71
  const finalConfig = (0, import_react.useMemo)(() => {
67
- const publicApiKey = config.publicApiKey || getCookie("publicApiKey");
68
- const env = config.env || getCookie("environment") || "prod";
72
+ const publicApiKey = config.publicApiKey || getCookie("publicApiKey") || null;
73
+ const rawEnv = config.env || getCookie("environment") || "prod";
74
+ const env = ["dev", "stg", "prod"].includes(rawEnv) ? rawEnv : "prod";
69
75
  const appName = config.appName || getCookie("appName") || "Crudia";
70
- const loginActions = config.loginActions || (getCookie("loginActions") ?? "").split(",").map((action) => action.trim()).filter(Boolean) || [];
76
+ const loginActions = config.loginActions || (() => {
77
+ try {
78
+ const cookieValue = getCookie("loginActions");
79
+ return cookieValue ? cookieValue.split(",").map((action) => action.trim()).filter(Boolean) : [];
80
+ } catch {
81
+ return [];
82
+ }
83
+ })();
71
84
  return {
72
85
  publicApiKey,
73
86
  env,
@@ -76,15 +89,14 @@ var useCrudifyLogin = (config, _options = {}) => {
76
89
  };
77
90
  }, [config]);
78
91
  (0, import_react.useEffect)(() => {
79
- if (finalConfig.publicApiKey) {
80
- try {
81
- import_crudify_browser.default.config(finalConfig.env);
82
- import_crudify_browser.default.init(finalConfig.publicApiKey, "none");
83
- } catch (error) {
84
- console.error("Error initializing crudify:", error);
85
- }
92
+ if (!finalConfig.publicApiKey) return;
93
+ try {
94
+ import_crudify_browser.default.config(finalConfig.env);
95
+ import_crudify_browser.default.init(finalConfig.publicApiKey, "none");
96
+ } catch (error) {
97
+ console.error("Error initializing crudify:", error);
86
98
  }
87
- }, [finalConfig]);
99
+ }, [finalConfig.publicApiKey, finalConfig.env]);
88
100
  const crudifyMethods = (0, import_react.useMemo)(() => {
89
101
  if (!finalConfig.publicApiKey) {
90
102
  return null;
@@ -430,13 +442,13 @@ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
430
442
  const [emailSent, setEmailSent] = (0, import_react3.useState)(false);
431
443
  const [codeAlreadyExists, setCodeAlreadyExists] = (0, import_react3.useState)(false);
432
444
  const { t } = (0, import_react_i18next2.useTranslation)();
433
- const { crudify: crudify2 } = useCrudifyLogin(config);
445
+ const { crudify: crudify3 } = useCrudifyLogin(config);
434
446
  const validateEmail = (email2) => {
435
447
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
436
448
  return emailRegex.test(email2);
437
449
  };
438
450
  const handleSubmit = async () => {
439
- if (loading || !crudify2) return;
451
+ if (loading || !crudify3) return;
440
452
  setErrors([]);
441
453
  setHelperTextEmail(null);
442
454
  if (!email) {
@@ -450,7 +462,7 @@ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
450
462
  setLoading(true);
451
463
  try {
452
464
  const data = [{ operation: "requestPasswordReset", data: { email } }];
453
- const response = await crudify2.transaction(data);
465
+ const response = await crudify3.transaction(data);
454
466
  if (response.success) {
455
467
  if (response.data && response.data.existingCodeValid) {
456
468
  setCodeAlreadyExists(true);
@@ -558,10 +570,10 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
558
570
  const [codeValidated, setCodeValidated] = (0, import_react4.useState)(false);
559
571
  const [resetSuccess, setResetSuccess] = (0, import_react4.useState)(false);
560
572
  const { t } = (0, import_react_i18next3.useTranslation)();
561
- const { crudify: crudify2 } = useCrudifyLogin(config);
573
+ const { crudify: crudify3 } = useCrudifyLogin(config);
562
574
  (0, import_react4.useEffect)(() => {
563
575
  const validateCode = async (emailToValidate, codeToValidate) => {
564
- if (!crudify2) return;
576
+ if (!crudify3) return;
565
577
  try {
566
578
  const data = [
567
579
  {
@@ -569,7 +581,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
569
581
  data: { email: emailToValidate, codePassword: codeToValidate }
570
582
  }
571
583
  ];
572
- const response = await crudify2.transaction(data);
584
+ const response = await crudify3.transaction(data);
573
585
  if (response.success) {
574
586
  setCodeValidated(true);
575
587
  } else {
@@ -597,7 +609,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
597
609
  setValidatingCode(false);
598
610
  setErrors([t("resetPassword.missingParameters")]);
599
611
  }
600
- }, [searchParams, crudify2, t]);
612
+ }, [searchParams, crudify3, t]);
601
613
  const validatePasswords = () => {
602
614
  setHelperTextNewPassword(null);
603
615
  setHelperTextConfirmPassword(null);
@@ -620,7 +632,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
620
632
  return true;
621
633
  };
622
634
  const handleSubmit = async () => {
623
- if (loading || !crudify2) return;
635
+ if (loading || !crudify3) return;
624
636
  setErrors([]);
625
637
  if (!validatePasswords()) return;
626
638
  setLoading(true);
@@ -631,7 +643,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
631
643
  data: { email, codePassword: code, newPassword }
632
644
  }
633
645
  ];
634
- const response = await crudify2.transaction(data);
646
+ const response = await crudify3.transaction(data);
635
647
  if (response.success) {
636
648
  setResetSuccess(true);
637
649
  } else {
@@ -775,7 +787,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
775
787
  const [helperTextEmail, setHelperTextEmail] = (0, import_react5.useState)(null);
776
788
  const [helperTextCode, setHelperTextCode] = (0, import_react5.useState)(null);
777
789
  const { t } = (0, import_react_i18next4.useTranslation)();
778
- const { crudify: crudify2 } = useCrudifyLogin(config);
790
+ const { crudify: crudify3 } = useCrudifyLogin(config);
779
791
  (0, import_react5.useEffect)(() => {
780
792
  const timer = setTimeout(() => {
781
793
  const emailInput = document.getElementById("email");
@@ -788,7 +800,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
788
800
  return emailRegex.test(email2);
789
801
  };
790
802
  const handleSubmit = async () => {
791
- if (loading || !crudify2) return;
803
+ if (loading || !crudify3) return;
792
804
  setErrors([]);
793
805
  setHelperTextEmail(null);
794
806
  setHelperTextCode(null);
@@ -812,7 +824,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
812
824
  data: { email, codePassword: code }
813
825
  }
814
826
  ];
815
- const response = await crudify2.transaction(data);
827
+ const response = await crudify3.transaction(data);
816
828
  if (response.success) {
817
829
  const params = new URLSearchParams({ email, code }).toString();
818
830
  onNavigate?.(`/login/resetPassword?${params}`);
@@ -1074,12 +1086,162 @@ var CrudifyLogin = ({
1074
1086
  );
1075
1087
  };
1076
1088
  var CrudifyLogin_default = CrudifyLogin;
1089
+
1090
+ // src/hooks/useUserProfile.ts
1091
+ var import_react7 = require("react");
1092
+ var import_crudify_browser2 = __toESM(require("@nocios/crudify-browser"));
1093
+
1094
+ // src/utils/jwtUtils.ts
1095
+ var decodeJwtSafely = (token) => {
1096
+ try {
1097
+ const parts = token.split(".");
1098
+ if (parts.length !== 3) {
1099
+ console.warn("Invalid JWT format: token must have 3 parts");
1100
+ return null;
1101
+ }
1102
+ const payload = parts[1];
1103
+ const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
1104
+ const decodedPayload = JSON.parse(atob(paddedPayload));
1105
+ return decodedPayload;
1106
+ } catch (error) {
1107
+ console.warn("Failed to decode JWT token:", error);
1108
+ return null;
1109
+ }
1110
+ };
1111
+ var getCurrentUserEmail = () => {
1112
+ try {
1113
+ const token = sessionStorage.getItem("token");
1114
+ if (!token) return null;
1115
+ const payload = decodeJwtSafely(token);
1116
+ if (!payload) return null;
1117
+ return payload.email || payload["cognito:username"] || null;
1118
+ } catch (error) {
1119
+ console.warn("Failed to get current user email:", error);
1120
+ return null;
1121
+ }
1122
+ };
1123
+ var isTokenExpired = (token) => {
1124
+ try {
1125
+ const payload = decodeJwtSafely(token);
1126
+ if (!payload || !payload.exp) return true;
1127
+ const currentTime = Math.floor(Date.now() / 1e3);
1128
+ return payload.exp < currentTime;
1129
+ } catch {
1130
+ return true;
1131
+ }
1132
+ };
1133
+
1134
+ // src/hooks/useUserProfile.ts
1135
+ var useUserProfile = (options = {}) => {
1136
+ const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
1137
+ const [userProfile, setUserProfile] = (0, import_react7.useState)(null);
1138
+ const [loading, setLoading] = (0, import_react7.useState)(false);
1139
+ const [error, setError] = (0, import_react7.useState)(null);
1140
+ const abortControllerRef = (0, import_react7.useRef)(null);
1141
+ const mountedRef = (0, import_react7.useRef)(true);
1142
+ const requestIdRef = (0, import_react7.useRef)(0);
1143
+ const retryCountRef = (0, import_react7.useRef)(0);
1144
+ const clearProfile = (0, import_react7.useCallback)(() => {
1145
+ setUserProfile(null);
1146
+ setError(null);
1147
+ setLoading(false);
1148
+ }, []);
1149
+ const refreshProfile = (0, import_react7.useCallback)(async () => {
1150
+ const userEmail = getCurrentUserEmail();
1151
+ if (!userEmail) {
1152
+ if (mountedRef.current) {
1153
+ setError("No user email available");
1154
+ setLoading(false);
1155
+ }
1156
+ return;
1157
+ }
1158
+ if (abortControllerRef.current) {
1159
+ abortControllerRef.current.abort();
1160
+ }
1161
+ const abortController = new AbortController();
1162
+ abortControllerRef.current = abortController;
1163
+ const currentRequestId = ++requestIdRef.current;
1164
+ try {
1165
+ if (mountedRef.current) {
1166
+ setLoading(true);
1167
+ setError(null);
1168
+ }
1169
+ const response = await import_crudify_browser2.default.readItems("users", {
1170
+ where: { email: userEmail },
1171
+ limit: 1
1172
+ });
1173
+ if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
1174
+ if (response.success && response.data && response.data.length > 0) {
1175
+ setUserProfile(response.data[0]);
1176
+ setError(null);
1177
+ retryCountRef.current = 0;
1178
+ } else {
1179
+ setError("User profile not found");
1180
+ setUserProfile(null);
1181
+ }
1182
+ }
1183
+ } catch (err) {
1184
+ if (currentRequestId === requestIdRef.current && mountedRef.current) {
1185
+ if (err.name === "AbortError") {
1186
+ return;
1187
+ }
1188
+ console.error("Error loading user profile:", err);
1189
+ const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (err.message?.includes("Network Error") || err.message?.includes("Failed to fetch"));
1190
+ if (shouldRetry) {
1191
+ retryCountRef.current++;
1192
+ setTimeout(() => {
1193
+ if (mountedRef.current) {
1194
+ refreshProfile();
1195
+ }
1196
+ }, 1e3 * retryCountRef.current);
1197
+ } else {
1198
+ setError("Failed to load user profile");
1199
+ setUserProfile(null);
1200
+ }
1201
+ }
1202
+ } finally {
1203
+ if (currentRequestId === requestIdRef.current && mountedRef.current) {
1204
+ setLoading(false);
1205
+ }
1206
+ if (abortControllerRef.current === abortController) {
1207
+ abortControllerRef.current = null;
1208
+ }
1209
+ }
1210
+ }, [retryOnError, maxRetries]);
1211
+ (0, import_react7.useEffect)(() => {
1212
+ if (autoFetch) {
1213
+ refreshProfile();
1214
+ }
1215
+ }, [autoFetch, refreshProfile]);
1216
+ (0, import_react7.useEffect)(() => {
1217
+ mountedRef.current = true;
1218
+ return () => {
1219
+ mountedRef.current = false;
1220
+ if (abortControllerRef.current) {
1221
+ abortControllerRef.current.abort();
1222
+ abortControllerRef.current = null;
1223
+ }
1224
+ };
1225
+ }, []);
1226
+ return {
1227
+ userProfile,
1228
+ loading,
1229
+ error,
1230
+ refreshProfile,
1231
+ clearProfile
1232
+ };
1233
+ };
1077
1234
  // Annotate the CommonJS export names for ESM import in node:
1078
1235
  0 && (module.exports = {
1079
1236
  CrudifyLogin,
1080
1237
  crudify,
1238
+ decodeJwtSafely,
1081
1239
  getCookie,
1240
+ getCurrentUserEmail,
1241
+ isTokenExpired,
1082
1242
  secureLocalStorage,
1083
1243
  secureSessionStorage,
1244
+ useCrudifyLogin,
1245
+ useUserProfile,
1084
1246
  ...require("@nocios/crudify-browser")
1085
1247
  });
package/dist/index.mjs CHANGED
@@ -25,10 +25,18 @@ var getCookie = (name) => {
25
25
  // src/components/CrudifyLogin/hooks/useCrudifyLogin.ts
26
26
  var useCrudifyLogin = (config, _options = {}) => {
27
27
  const finalConfig = useMemo(() => {
28
- const publicApiKey = config.publicApiKey || getCookie("publicApiKey");
29
- const env = config.env || getCookie("environment") || "prod";
28
+ const publicApiKey = config.publicApiKey || getCookie("publicApiKey") || null;
29
+ const rawEnv = config.env || getCookie("environment") || "prod";
30
+ const env = ["dev", "stg", "prod"].includes(rawEnv) ? rawEnv : "prod";
30
31
  const appName = config.appName || getCookie("appName") || "Crudia";
31
- const loginActions = config.loginActions || (getCookie("loginActions") ?? "").split(",").map((action) => action.trim()).filter(Boolean) || [];
32
+ const loginActions = config.loginActions || (() => {
33
+ try {
34
+ const cookieValue = getCookie("loginActions");
35
+ return cookieValue ? cookieValue.split(",").map((action) => action.trim()).filter(Boolean) : [];
36
+ } catch {
37
+ return [];
38
+ }
39
+ })();
32
40
  return {
33
41
  publicApiKey,
34
42
  env,
@@ -37,15 +45,14 @@ var useCrudifyLogin = (config, _options = {}) => {
37
45
  };
38
46
  }, [config]);
39
47
  useEffect(() => {
40
- if (finalConfig.publicApiKey) {
41
- try {
42
- crudify.config(finalConfig.env);
43
- crudify.init(finalConfig.publicApiKey, "none");
44
- } catch (error) {
45
- console.error("Error initializing crudify:", error);
46
- }
48
+ if (!finalConfig.publicApiKey) return;
49
+ try {
50
+ crudify.config(finalConfig.env);
51
+ crudify.init(finalConfig.publicApiKey, "none");
52
+ } catch (error) {
53
+ console.error("Error initializing crudify:", error);
47
54
  }
48
- }, [finalConfig]);
55
+ }, [finalConfig.publicApiKey, finalConfig.env]);
49
56
  const crudifyMethods = useMemo(() => {
50
57
  if (!finalConfig.publicApiKey) {
51
58
  return null;
@@ -391,13 +398,13 @@ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
391
398
  const [emailSent, setEmailSent] = useState2(false);
392
399
  const [codeAlreadyExists, setCodeAlreadyExists] = useState2(false);
393
400
  const { t } = useTranslation2();
394
- const { crudify: crudify2 } = useCrudifyLogin(config);
401
+ const { crudify: crudify3 } = useCrudifyLogin(config);
395
402
  const validateEmail = (email2) => {
396
403
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
397
404
  return emailRegex.test(email2);
398
405
  };
399
406
  const handleSubmit = async () => {
400
- if (loading || !crudify2) return;
407
+ if (loading || !crudify3) return;
401
408
  setErrors([]);
402
409
  setHelperTextEmail(null);
403
410
  if (!email) {
@@ -411,7 +418,7 @@ var ForgotPasswordForm = ({ config, onNavigate, onError }) => {
411
418
  setLoading(true);
412
419
  try {
413
420
  const data = [{ operation: "requestPasswordReset", data: { email } }];
414
- const response = await crudify2.transaction(data);
421
+ const response = await crudify3.transaction(data);
415
422
  if (response.success) {
416
423
  if (response.data && response.data.existingCodeValid) {
417
424
  setCodeAlreadyExists(true);
@@ -519,10 +526,10 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
519
526
  const [codeValidated, setCodeValidated] = useState3(false);
520
527
  const [resetSuccess, setResetSuccess] = useState3(false);
521
528
  const { t } = useTranslation3();
522
- const { crudify: crudify2 } = useCrudifyLogin(config);
529
+ const { crudify: crudify3 } = useCrudifyLogin(config);
523
530
  useEffect3(() => {
524
531
  const validateCode = async (emailToValidate, codeToValidate) => {
525
- if (!crudify2) return;
532
+ if (!crudify3) return;
526
533
  try {
527
534
  const data = [
528
535
  {
@@ -530,7 +537,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
530
537
  data: { email: emailToValidate, codePassword: codeToValidate }
531
538
  }
532
539
  ];
533
- const response = await crudify2.transaction(data);
540
+ const response = await crudify3.transaction(data);
534
541
  if (response.success) {
535
542
  setCodeValidated(true);
536
543
  } else {
@@ -558,7 +565,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
558
565
  setValidatingCode(false);
559
566
  setErrors([t("resetPassword.missingParameters")]);
560
567
  }
561
- }, [searchParams, crudify2, t]);
568
+ }, [searchParams, crudify3, t]);
562
569
  const validatePasswords = () => {
563
570
  setHelperTextNewPassword(null);
564
571
  setHelperTextConfirmPassword(null);
@@ -581,7 +588,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
581
588
  return true;
582
589
  };
583
590
  const handleSubmit = async () => {
584
- if (loading || !crudify2) return;
591
+ if (loading || !crudify3) return;
585
592
  setErrors([]);
586
593
  if (!validatePasswords()) return;
587
594
  setLoading(true);
@@ -592,7 +599,7 @@ var ResetPasswordForm = ({ config, onNavigate, onError, searchParams }) => {
592
599
  data: { email, codePassword: code, newPassword }
593
600
  }
594
601
  ];
595
- const response = await crudify2.transaction(data);
602
+ const response = await crudify3.transaction(data);
596
603
  if (response.success) {
597
604
  setResetSuccess(true);
598
605
  } else {
@@ -736,7 +743,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
736
743
  const [helperTextEmail, setHelperTextEmail] = useState4(null);
737
744
  const [helperTextCode, setHelperTextCode] = useState4(null);
738
745
  const { t } = useTranslation4();
739
- const { crudify: crudify2 } = useCrudifyLogin(config);
746
+ const { crudify: crudify3 } = useCrudifyLogin(config);
740
747
  useEffect4(() => {
741
748
  const timer = setTimeout(() => {
742
749
  const emailInput = document.getElementById("email");
@@ -749,7 +756,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
749
756
  return emailRegex.test(email2);
750
757
  };
751
758
  const handleSubmit = async () => {
752
- if (loading || !crudify2) return;
759
+ if (loading || !crudify3) return;
753
760
  setErrors([]);
754
761
  setHelperTextEmail(null);
755
762
  setHelperTextCode(null);
@@ -773,7 +780,7 @@ var CheckCodeForm = ({ config, onNavigate, onError }) => {
773
780
  data: { email, codePassword: code }
774
781
  }
775
782
  ];
776
- const response = await crudify2.transaction(data);
783
+ const response = await crudify3.transaction(data);
777
784
  if (response.success) {
778
785
  const params = new URLSearchParams({ email, code }).toString();
779
786
  onNavigate?.(`/login/resetPassword?${params}`);
@@ -1035,10 +1042,160 @@ var CrudifyLogin = ({
1035
1042
  );
1036
1043
  };
1037
1044
  var CrudifyLogin_default = CrudifyLogin;
1045
+
1046
+ // src/hooks/useUserProfile.ts
1047
+ import { useState as useState6, useEffect as useEffect5, useCallback, useRef as useRef2 } from "react";
1048
+ import crudify2 from "@nocios/crudify-browser";
1049
+
1050
+ // src/utils/jwtUtils.ts
1051
+ var decodeJwtSafely = (token) => {
1052
+ try {
1053
+ const parts = token.split(".");
1054
+ if (parts.length !== 3) {
1055
+ console.warn("Invalid JWT format: token must have 3 parts");
1056
+ return null;
1057
+ }
1058
+ const payload = parts[1];
1059
+ const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
1060
+ const decodedPayload = JSON.parse(atob(paddedPayload));
1061
+ return decodedPayload;
1062
+ } catch (error) {
1063
+ console.warn("Failed to decode JWT token:", error);
1064
+ return null;
1065
+ }
1066
+ };
1067
+ var getCurrentUserEmail = () => {
1068
+ try {
1069
+ const token = sessionStorage.getItem("token");
1070
+ if (!token) return null;
1071
+ const payload = decodeJwtSafely(token);
1072
+ if (!payload) return null;
1073
+ return payload.email || payload["cognito:username"] || null;
1074
+ } catch (error) {
1075
+ console.warn("Failed to get current user email:", error);
1076
+ return null;
1077
+ }
1078
+ };
1079
+ var isTokenExpired = (token) => {
1080
+ try {
1081
+ const payload = decodeJwtSafely(token);
1082
+ if (!payload || !payload.exp) return true;
1083
+ const currentTime = Math.floor(Date.now() / 1e3);
1084
+ return payload.exp < currentTime;
1085
+ } catch {
1086
+ return true;
1087
+ }
1088
+ };
1089
+
1090
+ // src/hooks/useUserProfile.ts
1091
+ var useUserProfile = (options = {}) => {
1092
+ const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
1093
+ const [userProfile, setUserProfile] = useState6(null);
1094
+ const [loading, setLoading] = useState6(false);
1095
+ const [error, setError] = useState6(null);
1096
+ const abortControllerRef = useRef2(null);
1097
+ const mountedRef = useRef2(true);
1098
+ const requestIdRef = useRef2(0);
1099
+ const retryCountRef = useRef2(0);
1100
+ const clearProfile = useCallback(() => {
1101
+ setUserProfile(null);
1102
+ setError(null);
1103
+ setLoading(false);
1104
+ }, []);
1105
+ const refreshProfile = useCallback(async () => {
1106
+ const userEmail = getCurrentUserEmail();
1107
+ if (!userEmail) {
1108
+ if (mountedRef.current) {
1109
+ setError("No user email available");
1110
+ setLoading(false);
1111
+ }
1112
+ return;
1113
+ }
1114
+ if (abortControllerRef.current) {
1115
+ abortControllerRef.current.abort();
1116
+ }
1117
+ const abortController = new AbortController();
1118
+ abortControllerRef.current = abortController;
1119
+ const currentRequestId = ++requestIdRef.current;
1120
+ try {
1121
+ if (mountedRef.current) {
1122
+ setLoading(true);
1123
+ setError(null);
1124
+ }
1125
+ const response = await crudify2.readItems("users", {
1126
+ where: { email: userEmail },
1127
+ limit: 1
1128
+ });
1129
+ if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
1130
+ if (response.success && response.data && response.data.length > 0) {
1131
+ setUserProfile(response.data[0]);
1132
+ setError(null);
1133
+ retryCountRef.current = 0;
1134
+ } else {
1135
+ setError("User profile not found");
1136
+ setUserProfile(null);
1137
+ }
1138
+ }
1139
+ } catch (err) {
1140
+ if (currentRequestId === requestIdRef.current && mountedRef.current) {
1141
+ if (err.name === "AbortError") {
1142
+ return;
1143
+ }
1144
+ console.error("Error loading user profile:", err);
1145
+ const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (err.message?.includes("Network Error") || err.message?.includes("Failed to fetch"));
1146
+ if (shouldRetry) {
1147
+ retryCountRef.current++;
1148
+ setTimeout(() => {
1149
+ if (mountedRef.current) {
1150
+ refreshProfile();
1151
+ }
1152
+ }, 1e3 * retryCountRef.current);
1153
+ } else {
1154
+ setError("Failed to load user profile");
1155
+ setUserProfile(null);
1156
+ }
1157
+ }
1158
+ } finally {
1159
+ if (currentRequestId === requestIdRef.current && mountedRef.current) {
1160
+ setLoading(false);
1161
+ }
1162
+ if (abortControllerRef.current === abortController) {
1163
+ abortControllerRef.current = null;
1164
+ }
1165
+ }
1166
+ }, [retryOnError, maxRetries]);
1167
+ useEffect5(() => {
1168
+ if (autoFetch) {
1169
+ refreshProfile();
1170
+ }
1171
+ }, [autoFetch, refreshProfile]);
1172
+ useEffect5(() => {
1173
+ mountedRef.current = true;
1174
+ return () => {
1175
+ mountedRef.current = false;
1176
+ if (abortControllerRef.current) {
1177
+ abortControllerRef.current.abort();
1178
+ abortControllerRef.current = null;
1179
+ }
1180
+ };
1181
+ }, []);
1182
+ return {
1183
+ userProfile,
1184
+ loading,
1185
+ error,
1186
+ refreshProfile,
1187
+ clearProfile
1188
+ };
1189
+ };
1038
1190
  export {
1039
1191
  CrudifyLogin_default as CrudifyLogin,
1040
1192
  default2 as crudify,
1193
+ decodeJwtSafely,
1041
1194
  getCookie,
1195
+ getCurrentUserEmail,
1196
+ isTokenExpired,
1042
1197
  secureLocalStorage,
1043
- secureSessionStorage
1198
+ secureSessionStorage,
1199
+ useCrudifyLogin,
1200
+ useUserProfile
1044
1201
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocios/crudify-ui",
3
- "version": "1.0.44",
3
+ "version": "1.0.46",
4
4
  "description": "Biblioteca de componentes UI para Crudify",
5
5
  "author": "Nocios",
6
6
  "license": "MIT",
@@ -25,7 +25,7 @@
25
25
  "@mui/icons-material": "^7.1.0",
26
26
  "@mui/material": "^7.1.0",
27
27
  "@mui/x-data-grid": "^8.5.1",
28
- "@nocios/crudify-browser": "^1.0.84",
28
+ "@nocios/crudify-browser": "^1.0.85",
29
29
  "crypto-js": "^4.2.0",
30
30
  "i18next-browser-languagedetector": "^8.1.0",
31
31
  "i18next-http-backend": "^3.0.2",