@nocios/crudify-ui 4.0.8 → 4.0.92

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.mjs CHANGED
@@ -1,4890 +1 @@
1
- // src/index.ts
2
- import { default as default2 } from "@nocios/crudify-browser";
3
- export * from "@nocios/crudify-browser";
4
-
5
- // src/components/CrudifyLogin/index.tsx
6
- import { Box as Box7, Typography as Typography6 } from "@mui/material";
7
-
8
- // src/components/CrudifyLogin/context/I18nProvider.tsx
9
- import { createContext, useContext, useMemo } from "react";
10
-
11
- // src/components/CrudifyLogin/hooks/useTranslationsFromUrl.ts
12
- import { useState, useEffect } from "react";
13
- var useTranslationsFromUrl = (url, providedTranslations) => {
14
- const [translations, setTranslations] = useState({});
15
- const [loading, setLoading] = useState(false);
16
- const [error, setError] = useState(null);
17
- useEffect(() => {
18
- console.log("\u{1F527} [I18nProvider] Hybrid translation loading:", {
19
- hasProvidedTranslations: !!providedTranslations && Object.keys(providedTranslations).length > 0,
20
- hasUrl: !!url,
21
- providedKeys: providedTranslations ? Object.keys(providedTranslations).length : 0
22
- });
23
- if (providedTranslations && Object.keys(providedTranslations).length > 0) {
24
- console.log("\u2705 [I18nProvider] Using provided translations (highest priority)");
25
- setTranslations(providedTranslations);
26
- setLoading(false);
27
- setError(null);
28
- return;
29
- }
30
- if (!url) {
31
- console.log("\u26A0\uFE0F [I18nProvider] No translations provided, using empty object (keys will show as-is)");
32
- setTranslations({});
33
- setLoading(false);
34
- setError(null);
35
- return;
36
- }
37
- console.log("\u{1F310} [I18nProvider] Loading translations from URL:", url);
38
- let isCancelled = false;
39
- setLoading(true);
40
- setError(null);
41
- fetch(url).then((response) => {
42
- if (!response.ok) {
43
- throw new Error(`Failed to load translations: ${response.status}`);
44
- }
45
- return response.json();
46
- }).then((data) => {
47
- if (!isCancelled) {
48
- console.log("\u2705 [I18nProvider] Translations loaded successfully from URL:", {
49
- url,
50
- keysLoaded: Object.keys(data).length
51
- });
52
- setTranslations(data);
53
- setLoading(false);
54
- }
55
- }).catch((err) => {
56
- if (!isCancelled) {
57
- console.error("\u274C [I18nProvider] Failed to load translations from URL:", url, err);
58
- setError(err.message);
59
- console.log("\u{1F504} [I18nProvider] Falling back to empty translations (keys will show as-is)");
60
- setTranslations({});
61
- setLoading(false);
62
- }
63
- });
64
- return () => {
65
- isCancelled = true;
66
- };
67
- }, [url, providedTranslations]);
68
- return {
69
- translations,
70
- loading,
71
- error
72
- };
73
- };
74
-
75
- // src/components/CrudifyLogin/context/I18nProvider.tsx
76
- import { jsx } from "react/jsx-runtime";
77
- var I18nContext = createContext(null);
78
- var getNestedValue = (obj, path) => {
79
- if (obj && obj[path]) return obj[path];
80
- return path.split(".").reduce((current, key) => {
81
- return current && typeof current === "object" ? current[key] : void 0;
82
- }, obj);
83
- };
84
- var I18nProvider = ({ children, translations, translationsUrl, language = "en" }) => {
85
- const { translations: loadedTranslations, loading } = useTranslationsFromUrl(translationsUrl, translations);
86
- const t = useMemo(() => {
87
- return (key, variables) => {
88
- let value = getNestedValue(loadedTranslations, key);
89
- if (value === void 0 || value === null) {
90
- console.log(`\u{1F50D} [I18nProvider] Translation not found for key: "${key}" - showing key as-is`);
91
- value = key;
92
- }
93
- if (variables && typeof value === "string") {
94
- Object.entries(variables).forEach(([varKey, varValue]) => {
95
- value = value.replace(new RegExp(`{{${varKey}}}`, "g"), varValue);
96
- });
97
- }
98
- return typeof value === "string" ? value : key;
99
- };
100
- }, [loadedTranslations]);
101
- const contextValue = useMemo(
102
- () => ({
103
- t,
104
- language
105
- }),
106
- [t, language]
107
- );
108
- if (loading) return /* @__PURE__ */ jsx("div", { children: "Loading translations..." });
109
- return /* @__PURE__ */ jsx(I18nContext.Provider, { value: contextValue, children });
110
- };
111
- var useTranslation = () => {
112
- const context = useContext(I18nContext);
113
- if (!context) {
114
- throw new Error("useTranslation must be used within I18nProvider");
115
- }
116
- return context;
117
- };
118
-
119
- // src/components/CrudifyLogin/context/CrudifyProvider.tsx
120
- import { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useState as useState2 } from "react";
121
- import crudify from "@nocios/crudify-browser";
122
- import { jsx as jsx2 } from "react/jsx-runtime";
123
- var CrudifyContext = createContext2(void 0);
124
- var CrudifyProvider = ({ config, children }) => {
125
- const [isLoading, setIsLoading] = useState2(true);
126
- const [error, setError] = useState2(null);
127
- const [isInitialized, setIsInitialized] = useState2(false);
128
- const [initializationKey, setInitializationKey] = useState2("");
129
- console.log("\u{1F50D} CrudifyProvider - Received config:", config);
130
- useEffect2(() => {
131
- console.log("\u{1F50D} CrudifyProvider - useEffect - config.publicApiKey:", config.publicApiKey);
132
- console.log("\u{1F50D} CrudifyProvider - useEffect - full config:", config);
133
- if (!config.publicApiKey) {
134
- console.log("\u274C CrudifyProvider - No publicApiKey provided, setting error");
135
- setError("No publicApiKey provided");
136
- setIsLoading(false);
137
- setIsInitialized(false);
138
- return;
139
- }
140
- console.log("\u2705 CrudifyProvider - publicApiKey found, proceeding with initialization");
141
- const currentKey = `${config.publicApiKey}-${config.env}`;
142
- if (currentKey === initializationKey && isInitialized) {
143
- setIsLoading(false);
144
- return;
145
- }
146
- const initializeCrudify = async () => {
147
- setIsLoading(true);
148
- setError(null);
149
- setIsInitialized(false);
150
- try {
151
- crudify.config(config.env || "prod");
152
- await crudify.init(config.publicApiKey, "none");
153
- if (typeof crudify.transaction === "function" && typeof crudify.login === "function") {
154
- setIsInitialized(true);
155
- setInitializationKey(currentKey);
156
- } else {
157
- throw new Error("Crudify methods not properly initialized");
158
- }
159
- } catch (err) {
160
- const errorMessage = err instanceof Error ? err.message : "Failed to initialize Crudify";
161
- setError(errorMessage);
162
- setIsInitialized(false);
163
- } finally {
164
- setIsLoading(false);
165
- }
166
- };
167
- initializeCrudify();
168
- }, [config.publicApiKey, config.env, initializationKey, isInitialized]);
169
- const value = {
170
- crudify: isInitialized ? crudify : null,
171
- isLoading,
172
- error,
173
- isInitialized
174
- };
175
- return /* @__PURE__ */ jsx2(CrudifyContext.Provider, { value, children });
176
- };
177
- var useCrudify = () => {
178
- const context = useContext2(CrudifyContext);
179
- if (context === void 0) {
180
- throw new Error("useCrudify must be used within a CrudifyProvider");
181
- }
182
- return context;
183
- };
184
-
185
- // src/components/CrudifyLogin/context/LoginStateProvider.tsx
186
- import { createContext as createContext3, useContext as useContext3, useReducer, useEffect as useEffect3 } from "react";
187
-
188
- // src/components/CrudifyLogin/utils/cookies.ts
189
- var getCookie = (name) => {
190
- const match = document.cookie.match(new RegExp("(^|;)\\s*" + name + "=([^;]+)"));
191
- return match ? match[2] : null;
192
- };
193
-
194
- // src/components/CrudifyLogin/context/LoginStateProvider.tsx
195
- import { jsx as jsx3 } from "react/jsx-runtime";
196
- var initialState = {
197
- currentScreen: "login",
198
- searchParams: {},
199
- formData: {
200
- username: "",
201
- password: "",
202
- email: "",
203
- code: "",
204
- newPassword: "",
205
- confirmPassword: ""
206
- },
207
- loading: false,
208
- errors: {
209
- global: []
210
- },
211
- emailSent: false,
212
- codeAlreadyExists: false,
213
- codeValidated: false,
214
- fromCodeVerification: false,
215
- config: {}
216
- };
217
- function loginStateReducer(state, action) {
218
- switch (action.type) {
219
- case "SET_SCREEN":
220
- const newState = {
221
- ...state,
222
- currentScreen: action.payload.screen,
223
- searchParams: action.payload.params || state.searchParams,
224
- // Clear form errors when changing screens
225
- errors: { global: [] }
226
- };
227
- const newUrl = new URLSearchParams(newState.searchParams);
228
- const newPath = newUrl.toString() ? `?${newUrl.toString()}` : window.location.pathname;
229
- try {
230
- window.history.replaceState({}, "", newPath);
231
- } catch (error) {
232
- }
233
- return newState;
234
- case "SET_SEARCH_PARAMS":
235
- return {
236
- ...state,
237
- searchParams: action.payload
238
- };
239
- case "UPDATE_FORM_DATA":
240
- return {
241
- ...state,
242
- formData: {
243
- ...state.formData,
244
- ...action.payload
245
- },
246
- // Clear related errors when updating form data
247
- errors: {
248
- ...state.errors,
249
- ...Object.keys(action.payload).reduce(
250
- (acc, key) => ({
251
- ...acc,
252
- [key]: void 0
253
- }),
254
- {}
255
- )
256
- }
257
- };
258
- case "SET_LOADING":
259
- return {
260
- ...state,
261
- loading: action.payload
262
- };
263
- case "SET_ERRORS":
264
- return {
265
- ...state,
266
- errors: {
267
- ...state.errors,
268
- ...action.payload
269
- }
270
- };
271
- case "CLEAR_ERRORS":
272
- return {
273
- ...state,
274
- errors: { global: [] }
275
- };
276
- case "SET_EMAIL_SENT":
277
- return {
278
- ...state,
279
- emailSent: action.payload
280
- };
281
- case "SET_CODE_ALREADY_EXISTS":
282
- return {
283
- ...state,
284
- codeAlreadyExists: action.payload
285
- };
286
- case "SET_CODE_VALIDATED":
287
- return {
288
- ...state,
289
- codeValidated: action.payload
290
- };
291
- case "SET_FROM_CODE_VERIFICATION":
292
- return {
293
- ...state,
294
- fromCodeVerification: action.payload
295
- };
296
- case "RESET_FORM":
297
- return {
298
- ...state,
299
- formData: initialState.formData,
300
- errors: { global: [] },
301
- loading: false,
302
- emailSent: false,
303
- codeAlreadyExists: false,
304
- codeValidated: false,
305
- fromCodeVerification: false
306
- };
307
- case "INIT_CONFIG":
308
- return {
309
- ...state,
310
- config: action.payload
311
- };
312
- default:
313
- return state;
314
- }
315
- }
316
- var LoginStateContext = createContext3(void 0);
317
- var LoginStateProvider = ({
318
- children,
319
- initialScreen = "login",
320
- config: providedConfig,
321
- autoReadFromCookies = true
322
- }) => {
323
- const [state, dispatch] = useReducer(loginStateReducer, {
324
- ...initialState,
325
- currentScreen: initialScreen
326
- });
327
- useEffect3(() => {
328
- const buildFinalConfig = () => {
329
- let cookieConfig = {};
330
- if (autoReadFromCookies) {
331
- try {
332
- const encodedLogo = getCookie("logo");
333
- if (encodedLogo) {
334
- const decodedLogo = decodeURIComponent(encodedLogo);
335
- if (decodedLogo.startsWith("http")) {
336
- cookieConfig.logo = decodedLogo;
337
- }
338
- }
339
- } catch (e) {
340
- console.error("Error reading configuration from cookies:", e);
341
- }
342
- }
343
- return {
344
- publicApiKey: providedConfig?.publicApiKey,
345
- env: providedConfig?.env,
346
- appName: providedConfig?.appName,
347
- logo: providedConfig?.logo || cookieConfig.logo,
348
- loginActions: providedConfig?.loginActions
349
- };
350
- };
351
- dispatch({ type: "INIT_CONFIG", payload: buildFinalConfig() });
352
- }, [providedConfig, autoReadFromCookies]);
353
- useEffect3(() => {
354
- const urlParams = new URLSearchParams(window.location.search);
355
- const paramsObject = {};
356
- urlParams.forEach((value2, key) => {
357
- paramsObject[key] = value2;
358
- });
359
- if (Object.keys(paramsObject).length > 0) {
360
- dispatch({ type: "SET_SEARCH_PARAMS", payload: paramsObject });
361
- }
362
- if (initialScreen === "checkCode" && paramsObject.email) {
363
- dispatch({
364
- type: "UPDATE_FORM_DATA",
365
- payload: { email: paramsObject.email, code: paramsObject.code || "" }
366
- });
367
- }
368
- if (initialScreen === "resetPassword" && paramsObject.link) {
369
- dispatch({ type: "SET_SEARCH_PARAMS", payload: paramsObject });
370
- }
371
- }, [initialScreen]);
372
- const setScreen = (screen2, params) => {
373
- dispatch({ type: "SET_SCREEN", payload: { screen: screen2, params } });
374
- };
375
- const updateFormData = (data) => {
376
- dispatch({ type: "UPDATE_FORM_DATA", payload: data });
377
- };
378
- const setFieldError = (field, error) => {
379
- dispatch({ type: "SET_ERRORS", payload: { [field]: error } });
380
- };
381
- const clearErrors = () => {
382
- dispatch({ type: "CLEAR_ERRORS" });
383
- };
384
- const setLoading = (loading) => {
385
- dispatch({ type: "SET_LOADING", payload: loading });
386
- };
387
- const value = {
388
- state,
389
- dispatch,
390
- setScreen,
391
- updateFormData,
392
- setFieldError,
393
- clearErrors,
394
- setLoading
395
- };
396
- return /* @__PURE__ */ jsx3(LoginStateContext.Provider, { value, children });
397
- };
398
- var useLoginState = () => {
399
- const context = useContext3(LoginStateContext);
400
- if (context === void 0) {
401
- throw new Error("useLoginState must be used within a LoginStateProvider");
402
- }
403
- return context;
404
- };
405
-
406
- // src/providers/SessionProvider.tsx
407
- import React5, { createContext as createContext5, useContext as useContext5, useMemo as useMemo2 } from "react";
408
-
409
- // src/hooks/useSession.ts
410
- import { useState as useState3, useEffect as useEffect4, useCallback } from "react";
411
-
412
- // src/core/SessionManager.ts
413
- import crudify2 from "@nocios/crudify-browser";
414
-
415
- // src/utils/tokenStorage.ts
416
- import CryptoJS from "crypto-js";
417
- var _TokenStorage = class _TokenStorage {
418
- /**
419
- * Configurar tipo de almacenamiento
420
- */
421
- static setStorageType(type) {
422
- _TokenStorage.storageType = type;
423
- }
424
- /**
425
- * Generar clave de encriptación única y persistente
426
- */
427
- static generateEncryptionKey() {
428
- const browserInfo = [
429
- navigator.userAgent,
430
- navigator.language,
431
- navigator.platform,
432
- screen.width,
433
- screen.height,
434
- Date.now().toString(),
435
- Math.random().toString(36)
436
- ].join("|");
437
- return CryptoJS.SHA256(browserInfo).toString();
438
- }
439
- /**
440
- * Obtener o generar clave de encriptación
441
- */
442
- static getEncryptionKey() {
443
- if (_TokenStorage.encryptionKey) {
444
- return _TokenStorage.encryptionKey;
445
- }
446
- const storage = window.localStorage;
447
- if (!storage) {
448
- _TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
449
- return _TokenStorage.encryptionKey;
450
- }
451
- try {
452
- let existingKey = storage.getItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
453
- if (!existingKey || existingKey.length < 32) {
454
- existingKey = _TokenStorage.generateEncryptionKey();
455
- storage.setItem(_TokenStorage.ENCRYPTION_KEY_STORAGE, existingKey);
456
- }
457
- _TokenStorage.encryptionKey = existingKey;
458
- return existingKey;
459
- } catch (error) {
460
- console.warn("Crudify: Cannot persist encryption key, using temporary key");
461
- _TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
462
- return _TokenStorage.encryptionKey;
463
- }
464
- }
465
- /**
466
- * Verificar si el storage está disponible
467
- */
468
- static isStorageAvailable(type) {
469
- try {
470
- const storage = window[type];
471
- const testKey = "__storage_test__";
472
- storage.setItem(testKey, "test");
473
- storage.removeItem(testKey);
474
- return true;
475
- } catch {
476
- return false;
477
- }
478
- }
479
- /**
480
- * Obtener instancia de storage
481
- */
482
- static getStorage() {
483
- if (_TokenStorage.storageType === "none") return null;
484
- if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
485
- console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
486
- return null;
487
- }
488
- return window[_TokenStorage.storageType];
489
- }
490
- /**
491
- * Encriptar datos sensibles
492
- */
493
- static encrypt(data) {
494
- try {
495
- const encryptionKey = _TokenStorage.getEncryptionKey();
496
- return CryptoJS.AES.encrypt(data, encryptionKey).toString();
497
- } catch (error) {
498
- console.error("Crudify: Encryption failed", error);
499
- return data;
500
- }
501
- }
502
- /**
503
- * Desencriptar datos
504
- */
505
- static decrypt(encryptedData) {
506
- try {
507
- const encryptionKey = _TokenStorage.getEncryptionKey();
508
- const bytes = CryptoJS.AES.decrypt(encryptedData, encryptionKey);
509
- const decrypted = bytes.toString(CryptoJS.enc.Utf8);
510
- return decrypted || encryptedData;
511
- } catch (error) {
512
- console.error("Crudify: Decryption failed", error);
513
- return encryptedData;
514
- }
515
- }
516
- /**
517
- * Guardar tokens de forma segura
518
- */
519
- static saveTokens(tokens) {
520
- const storage = _TokenStorage.getStorage();
521
- if (!storage) return;
522
- try {
523
- const tokenData = {
524
- accessToken: tokens.accessToken,
525
- refreshToken: tokens.refreshToken,
526
- expiresAt: tokens.expiresAt,
527
- refreshExpiresAt: tokens.refreshExpiresAt,
528
- savedAt: Date.now()
529
- };
530
- const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
531
- storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
532
- console.debug("Crudify: Tokens saved successfully");
533
- } catch (error) {
534
- console.error("Crudify: Failed to save tokens", error);
535
- }
536
- }
537
- /**
538
- * Obtener tokens guardados
539
- */
540
- static getTokens() {
541
- const storage = _TokenStorage.getStorage();
542
- if (!storage) return null;
543
- try {
544
- const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
545
- if (!encrypted) return null;
546
- const decrypted = _TokenStorage.decrypt(encrypted);
547
- const tokenData = JSON.parse(decrypted);
548
- if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
549
- console.warn("Crudify: Incomplete token data found, clearing storage");
550
- _TokenStorage.clearTokens();
551
- return null;
552
- }
553
- if (Date.now() >= tokenData.refreshExpiresAt) {
554
- console.info("Crudify: Refresh token expired, clearing storage");
555
- _TokenStorage.clearTokens();
556
- return null;
557
- }
558
- return {
559
- accessToken: tokenData.accessToken,
560
- refreshToken: tokenData.refreshToken,
561
- expiresAt: tokenData.expiresAt,
562
- refreshExpiresAt: tokenData.refreshExpiresAt
563
- };
564
- } catch (error) {
565
- console.error("Crudify: Failed to retrieve tokens", error);
566
- _TokenStorage.clearTokens();
567
- return null;
568
- }
569
- }
570
- /**
571
- * Limpiar tokens almacenados
572
- */
573
- static clearTokens() {
574
- const storage = _TokenStorage.getStorage();
575
- if (!storage) return;
576
- try {
577
- storage.removeItem(_TokenStorage.TOKEN_KEY);
578
- console.debug("Crudify: Tokens cleared from storage");
579
- } catch (error) {
580
- console.error("Crudify: Failed to clear tokens", error);
581
- }
582
- }
583
- /**
584
- * Rotar clave de encriptación (limpia tokens existentes por seguridad)
585
- */
586
- static rotateEncryptionKey() {
587
- try {
588
- _TokenStorage.clearTokens();
589
- _TokenStorage.encryptionKey = null;
590
- const storage = window.localStorage;
591
- if (storage) {
592
- storage.removeItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
593
- }
594
- console.info("Crudify: Encryption key rotated successfully");
595
- } catch (error) {
596
- console.error("Crudify: Failed to rotate encryption key", error);
597
- }
598
- }
599
- /**
600
- * Verificar si hay tokens válidos guardados
601
- */
602
- static hasValidTokens() {
603
- const tokens = _TokenStorage.getTokens();
604
- return tokens !== null;
605
- }
606
- /**
607
- * Obtener información de expiración
608
- */
609
- static getExpirationInfo() {
610
- const tokens = _TokenStorage.getTokens();
611
- if (!tokens) return null;
612
- const now = Date.now();
613
- return {
614
- accessExpired: now >= tokens.expiresAt,
615
- refreshExpired: now >= tokens.refreshExpiresAt,
616
- accessExpiresIn: Math.max(0, tokens.expiresAt - now),
617
- refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
618
- };
619
- }
620
- /**
621
- * Actualizar solo el access token (después de refresh)
622
- */
623
- static updateAccessToken(newAccessToken, newExpiresAt) {
624
- const existingTokens = _TokenStorage.getTokens();
625
- if (!existingTokens) {
626
- console.warn("Crudify: Cannot update access token, no existing tokens found");
627
- return;
628
- }
629
- _TokenStorage.saveTokens({
630
- ...existingTokens,
631
- accessToken: newAccessToken,
632
- expiresAt: newExpiresAt
633
- });
634
- }
635
- };
636
- _TokenStorage.TOKEN_KEY = "crudify_tokens";
637
- _TokenStorage.ENCRYPTION_KEY_STORAGE = "crudify_enc_key";
638
- _TokenStorage.encryptionKey = null;
639
- _TokenStorage.storageType = "localStorage";
640
- var TokenStorage = _TokenStorage;
641
-
642
- // src/utils/errorTranslation.ts
643
- var ERROR_TRANSLATION_HIERARCHY = [
644
- "errors.{category}.{code}",
645
- // errors.auth.INVALID_CREDENTIALS
646
- "errors.{code}",
647
- // errors.INVALID_CREDENTIALS
648
- "login.{code}",
649
- // login.INVALID_CREDENTIALS (legacy)
650
- "error.{code}",
651
- // error.INVALID_CREDENTIALS (singular)
652
- "messages.{code}",
653
- // messages.INVALID_CREDENTIALS
654
- "{code}"
655
- // INVALID_CREDENTIALS (direct)
656
- ];
657
- var ERROR_CATEGORY_MAP = {
658
- // Authentication errors
659
- "INVALID_CREDENTIALS": "auth",
660
- "UNAUTHORIZED": "auth",
661
- "INVALID_API_KEY": "auth",
662
- "USER_NOT_FOUND": "auth",
663
- "USER_NOT_ACTIVE": "auth",
664
- "NO_PERMISSION": "auth",
665
- "SESSION_EXPIRED": "auth",
666
- // Data errors
667
- "ITEM_NOT_FOUND": "data",
668
- "NOT_FOUND": "data",
669
- "IN_USE": "data",
670
- "DUPLICATE_ENTRY": "data",
671
- // Validation errors
672
- "FIELD_ERROR": "validation",
673
- "BAD_REQUEST": "validation",
674
- "INVALID_EMAIL": "validation",
675
- "INVALID_CODE": "validation",
676
- "REQUIRED_FIELD": "validation",
677
- // System errors
678
- "INTERNAL_SERVER_ERROR": "system",
679
- "DATABASE_CONNECTION_ERROR": "system",
680
- "INVALID_CONFIGURATION": "system",
681
- "UNKNOWN_OPERATION": "system",
682
- "TIMEOUT_ERROR": "system",
683
- "NETWORK_ERROR": "system",
684
- // Rate limiting
685
- "TOO_MANY_REQUESTS": "rate_limit"
686
- };
687
- var DEFAULT_ERROR_MESSAGES = {
688
- "INVALID_CREDENTIALS": "Invalid username or password",
689
- "UNAUTHORIZED": "You are not authorized to perform this action",
690
- "SESSION_EXPIRED": "Your session has expired. Please log in again.",
691
- "USER_NOT_FOUND": "User not found",
692
- "ITEM_NOT_FOUND": "Item not found",
693
- "FIELD_ERROR": "Invalid field value",
694
- "INTERNAL_SERVER_ERROR": "An internal error occurred",
695
- "NETWORK_ERROR": "Network connection error",
696
- "TIMEOUT_ERROR": "Request timeout",
697
- "UNKNOWN_OPERATION": "Unknown operation",
698
- "INVALID_EMAIL": "Invalid email format",
699
- "INVALID_CODE": "Invalid code",
700
- "TOO_MANY_REQUESTS": "Too many requests, please try again later"
701
- };
702
- function translateErrorCode(errorCode, config) {
703
- const { translateFn, currentLanguage, enableDebug } = config;
704
- if (enableDebug) {
705
- console.log(`\u{1F50D} [ErrorTranslation] Translating error code: ${errorCode} (lang: ${currentLanguage || "unknown"})`);
706
- }
707
- const normalizedCode = errorCode.toUpperCase();
708
- const category = ERROR_CATEGORY_MAP[normalizedCode];
709
- const translationKeys = ERROR_TRANSLATION_HIERARCHY.map((pattern) => {
710
- return pattern.replace("{category}", category || "general").replace("{code}", normalizedCode);
711
- });
712
- if (enableDebug) {
713
- console.log(`\u{1F511} [ErrorTranslation] Searching keys:`, translationKeys);
714
- }
715
- for (const key of translationKeys) {
716
- const translated = translateFn(key);
717
- if (enableDebug) {
718
- console.log(`\u{1F50D} [ErrorTranslation] Checking key: "${key}" -> result: "${translated}" (same as key: ${translated === key})`);
719
- }
720
- if (translated && translated !== key) {
721
- if (enableDebug) {
722
- console.log(`\u2705 [ErrorTranslation] Found translation at key: ${key} = "${translated}"`);
723
- }
724
- return translated;
725
- }
726
- }
727
- const defaultMessage = DEFAULT_ERROR_MESSAGES[normalizedCode];
728
- if (defaultMessage) {
729
- if (enableDebug) {
730
- console.log(`\u{1F504} [ErrorTranslation] Using default message: "${defaultMessage}"`);
731
- }
732
- return defaultMessage;
733
- }
734
- const friendlyCode = normalizedCode.replace(/_/g, " ").toLowerCase().replace(/\b\w/g, (l) => l.toUpperCase());
735
- if (enableDebug) {
736
- console.log(`\u26A0\uFE0F [ErrorTranslation] No translation found, using friendly code: "${friendlyCode}"`);
737
- }
738
- return friendlyCode;
739
- }
740
- function translateErrorCodes(errorCodes, config) {
741
- return errorCodes.map((code) => translateErrorCode(code, config));
742
- }
743
- function translateError(error, config) {
744
- const { enableDebug } = config;
745
- if (enableDebug) {
746
- console.log(`\u{1F50D} [ErrorTranslation] Translating error:`, error);
747
- }
748
- const translatedCode = translateErrorCode(error.code, config);
749
- if (translatedCode !== error.code.toUpperCase() && translatedCode !== error.code) {
750
- if (enableDebug) {
751
- console.log(`\u2705 [ErrorTranslation] Using hierarchical translation: "${translatedCode}"`);
752
- }
753
- if (error.field) {
754
- return `${error.field}: ${translatedCode}`;
755
- }
756
- return translatedCode;
757
- }
758
- if (error.message && !error.message.includes("Error:") && error.message.length > 0 && error.message !== error.code) {
759
- if (enableDebug) {
760
- console.log(`\u{1F504} [ErrorTranslation] No hierarchical translation found, using API message: "${error.message}"`);
761
- }
762
- return error.message;
763
- }
764
- if (enableDebug) {
765
- console.log(`\u26A0\uFE0F [ErrorTranslation] Using final fallback: "${translatedCode}"`);
766
- }
767
- if (error.field) {
768
- return `${error.field}: ${translatedCode}`;
769
- }
770
- return translatedCode;
771
- }
772
- function createErrorTranslator(translateFn, options = {}) {
773
- const config = {
774
- translateFn,
775
- currentLanguage: options.currentLanguage,
776
- enableDebug: options.enableDebug || false
777
- };
778
- return {
779
- translateErrorCode: (code) => translateErrorCode(code, config),
780
- translateErrorCodes: (codes) => translateErrorCodes(codes, config),
781
- translateError: (error) => translateError(error, config),
782
- // Método de conveniencia para errores de API
783
- translateApiError: (apiResponse) => {
784
- if (apiResponse?.data?.response?.status) {
785
- return translateErrorCode(apiResponse.data.response.status, config);
786
- }
787
- if (apiResponse?.status) {
788
- return translateErrorCode(apiResponse.status, config);
789
- }
790
- if (apiResponse?.code) {
791
- return translateErrorCode(apiResponse.code, config);
792
- }
793
- return "Unknown error";
794
- }
795
- };
796
- }
797
-
798
- // src/core/SessionManager.ts
799
- var SessionManager = class _SessionManager {
800
- constructor() {
801
- this.config = {};
802
- this.initialized = false;
803
- }
804
- static getInstance() {
805
- if (!_SessionManager.instance) {
806
- _SessionManager.instance = new _SessionManager();
807
- }
808
- return _SessionManager.instance;
809
- }
810
- /**
811
- * Inicializar el SessionManager
812
- */
813
- async initialize(config = {}) {
814
- if (this.initialized) {
815
- console.warn("SessionManager: Already initialized");
816
- return;
817
- }
818
- this.config = {
819
- storageType: "localStorage",
820
- autoRestore: true,
821
- enableLogging: false,
822
- ...config
823
- };
824
- TokenStorage.setStorageType(this.config.storageType || "localStorage");
825
- if (this.config.enableLogging) {
826
- }
827
- if (this.config.autoRestore) {
828
- await this.restoreSession();
829
- }
830
- this.initialized = true;
831
- this.log("SessionManager initialized successfully");
832
- }
833
- /**
834
- * Login con persistencia automática
835
- */
836
- async login(email, password) {
837
- try {
838
- this.log("Attempting login...");
839
- const response = await crudify2.login(email, password);
840
- if (!response.success) {
841
- this.log("Login failed:", response.errors);
842
- return {
843
- success: false,
844
- error: this.formatError(response.errors),
845
- rawResponse: response
846
- // Preservar respuesta original para manejo de errores específicos
847
- };
848
- }
849
- const tokens = {
850
- accessToken: response.data.token,
851
- refreshToken: response.data.refreshToken,
852
- expiresAt: response.data.expiresAt,
853
- refreshExpiresAt: response.data.refreshExpiresAt
854
- };
855
- TokenStorage.saveTokens(tokens);
856
- this.log("Login successful, tokens saved");
857
- this.config.onLoginSuccess?.(tokens);
858
- return {
859
- success: true,
860
- tokens,
861
- data: response.data
862
- // Incluir datos del usuario
863
- };
864
- } catch (error) {
865
- this.log("Login error:", error);
866
- return {
867
- success: false,
868
- error: error instanceof Error ? error.message : "Unknown error"
869
- };
870
- }
871
- }
872
- /**
873
- * Logout con limpieza de tokens
874
- */
875
- async logout() {
876
- try {
877
- this.log("Logging out...");
878
- await crudify2.logout();
879
- TokenStorage.clearTokens();
880
- this.log("Logout successful");
881
- this.config.onLogout?.();
882
- } catch (error) {
883
- this.log("Logout error:", error);
884
- TokenStorage.clearTokens();
885
- }
886
- }
887
- /**
888
- * Restaurar sesión desde storage
889
- */
890
- async restoreSession() {
891
- try {
892
- this.log("Attempting to restore session...");
893
- const savedTokens = TokenStorage.getTokens();
894
- if (!savedTokens) {
895
- this.log("No valid tokens found in storage");
896
- return false;
897
- }
898
- crudify2.setTokens({
899
- accessToken: savedTokens.accessToken,
900
- refreshToken: savedTokens.refreshToken,
901
- expiresAt: savedTokens.expiresAt,
902
- refreshExpiresAt: savedTokens.refreshExpiresAt
903
- });
904
- try {
905
- const isValid = crudify2.isLogin();
906
- if (!isValid) {
907
- this.log("Restored tokens are invalid, clearing storage");
908
- TokenStorage.clearTokens();
909
- return false;
910
- }
911
- } catch (validationError) {
912
- this.log("Token validation failed:", validationError);
913
- TokenStorage.clearTokens();
914
- return false;
915
- }
916
- this.log("Session restored successfully");
917
- this.config.onSessionRestored?.(savedTokens);
918
- return true;
919
- } catch (error) {
920
- this.log("Session restore error:", error);
921
- TokenStorage.clearTokens();
922
- return false;
923
- }
924
- }
925
- /**
926
- * Verificar si el usuario está autenticado
927
- */
928
- isAuthenticated() {
929
- return crudify2.isLogin() || TokenStorage.hasValidTokens();
930
- }
931
- /**
932
- * Obtener información de tokens actuales
933
- */
934
- getTokenInfo() {
935
- const crudifyTokens = crudify2.getTokenData();
936
- const storageInfo = TokenStorage.getExpirationInfo();
937
- return {
938
- isLoggedIn: this.isAuthenticated(),
939
- crudifyTokens,
940
- storageInfo,
941
- hasValidTokens: TokenStorage.hasValidTokens()
942
- };
943
- }
944
- /**
945
- * Refrescar tokens manualmente
946
- */
947
- async refreshTokens() {
948
- try {
949
- this.log("Manually refreshing tokens...");
950
- const response = await crudify2.refreshAccessToken();
951
- if (!response.success) {
952
- this.log("Token refresh failed:", response.errors);
953
- TokenStorage.clearTokens();
954
- this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");
955
- this.config.onSessionExpired?.();
956
- return false;
957
- }
958
- const newTokens = {
959
- accessToken: response.data.token,
960
- refreshToken: response.data.refreshToken,
961
- expiresAt: response.data.expiresAt,
962
- refreshExpiresAt: response.data.refreshExpiresAt
963
- };
964
- TokenStorage.saveTokens(newTokens);
965
- this.log("Tokens refreshed and saved successfully");
966
- return true;
967
- } catch (error) {
968
- this.log("Token refresh error:", error);
969
- TokenStorage.clearTokens();
970
- this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");
971
- this.config.onSessionExpired?.();
972
- return false;
973
- }
974
- }
975
- /**
976
- * Configurar interceptor de respuesta para manejo automático de errores
977
- */
978
- setupResponseInterceptor() {
979
- crudify2.setResponseInterceptor(async (response) => {
980
- const authError = this.detectAuthorizationError(response);
981
- if (authError.isAuthError) {
982
- console.warn("\u{1F6A8} SessionManager - Authorization error detected:", {
983
- errorType: authError.errorType,
984
- errorDetails: authError.errorDetails,
985
- fullResponse: response
986
- });
987
- if (authError.isRefreshTokenInvalid || authError.isTokenRefreshFailed) {
988
- this.log("Refresh token invalid or refresh already failed, clearing session");
989
- TokenStorage.clearTokens();
990
- this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");
991
- this.config.onSessionExpired?.();
992
- return response;
993
- }
994
- if (TokenStorage.hasValidTokens() && !authError.isIrrecoverable) {
995
- this.log("Auth error detected, attempting token refresh...");
996
- const refreshSuccess = await this.refreshTokens();
997
- if (!refreshSuccess) {
998
- this.log("Token refresh failed, triggering session expired");
999
- this.config.onSessionExpired?.();
1000
- }
1001
- } else {
1002
- this.log("Auth error with no valid tokens or irrecoverable error, triggering session expired");
1003
- TokenStorage.clearTokens();
1004
- this.config.showNotification?.(this.getSessionExpiredMessage(), "warning");
1005
- this.config.onSessionExpired?.();
1006
- }
1007
- }
1008
- return response;
1009
- });
1010
- this.log("Response interceptor configured");
1011
- }
1012
- /**
1013
- * Detectar errores de autorización en todos los formatos posibles
1014
- */
1015
- detectAuthorizationError(response) {
1016
- const result = {
1017
- isAuthError: false,
1018
- isRefreshTokenInvalid: false,
1019
- isTokenRefreshFailed: false,
1020
- isIrrecoverable: false,
1021
- errorType: "",
1022
- errorDetails: null
1023
- };
1024
- if (response.errors) {
1025
- if (Array.isArray(response.errors)) {
1026
- const hasAuthError = response.errors.some(
1027
- (error) => error.errorType === "Unauthorized" || error.message?.includes("Unauthorized") || error.message?.includes("Not Authorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED" || error.message?.includes("NOT_AUTHORIZED")
1028
- );
1029
- if (hasAuthError) {
1030
- result.isAuthError = true;
1031
- result.errorType = "GraphQL Array Format";
1032
- result.errorDetails = response.errors;
1033
- }
1034
- } else if (typeof response.errors === "object") {
1035
- const errorMessages = Object.values(response.errors).flat();
1036
- const hasAuthError = errorMessages.some(
1037
- (message) => typeof message === "string" && (message.includes("NOT_AUTHORIZED") || message.includes("TOKEN_REFRESH_FAILED") || message.includes("PLEASE_LOGIN") || message.includes("Unauthorized") || message.includes("UNAUTHENTICATED") || message.includes("Token"))
1038
- );
1039
- if (hasAuthError) {
1040
- result.isAuthError = true;
1041
- result.errorType = "GraphQL Object Format";
1042
- result.errorDetails = response.errors;
1043
- result.isTokenRefreshFailed = errorMessages.some(
1044
- (message) => typeof message === "string" && message.includes("TOKEN_REFRESH_FAILED")
1045
- );
1046
- }
1047
- }
1048
- }
1049
- if (!result.isAuthError && response.data?.response?.status === "UNAUTHORIZED") {
1050
- result.isAuthError = true;
1051
- result.errorType = "Status UNAUTHORIZED";
1052
- result.errorDetails = response.data.response;
1053
- result.isIrrecoverable = true;
1054
- }
1055
- if (!result.isAuthError && response.data?.response?.data) {
1056
- try {
1057
- const parsedData = JSON.parse(response.data.response.data);
1058
- if (parsedData.error === "REFRESH_TOKEN_INVALID" || parsedData.error === "TOKEN_EXPIRED") {
1059
- result.isAuthError = true;
1060
- result.errorType = "Parsed Data Format";
1061
- result.errorDetails = parsedData;
1062
- result.isRefreshTokenInvalid = true;
1063
- result.isIrrecoverable = true;
1064
- }
1065
- } catch {
1066
- }
1067
- }
1068
- if (!result.isAuthError && response.errorCode) {
1069
- const errorCode = response.errorCode;
1070
- const isAuthErrorCode = errorCode === "UNAUTHORIZED" || errorCode === "UNAUTHENTICATED" || errorCode === "TOKEN_EXPIRED";
1071
- if (isAuthErrorCode) {
1072
- result.isAuthError = true;
1073
- result.errorType = "Error Code Format";
1074
- result.errorDetails = { errorCode };
1075
- }
1076
- }
1077
- return result;
1078
- }
1079
- /**
1080
- * Limpiar sesión completamente
1081
- */
1082
- clearSession() {
1083
- TokenStorage.clearTokens();
1084
- crudify2.logout();
1085
- this.log("Session cleared completely");
1086
- }
1087
- /**
1088
- * Obtener mensaje de sesión expirada traducido
1089
- */
1090
- getSessionExpiredMessage() {
1091
- if (this.config.translateFn) {
1092
- return translateErrorCode("SESSION_EXPIRED", {
1093
- translateFn: this.config.translateFn,
1094
- enableDebug: this.config.enableLogging
1095
- });
1096
- }
1097
- return "Tu sesi\xF3n ha expirado. Por favor, inicia sesi\xF3n nuevamente.";
1098
- }
1099
- // Métodos privados
1100
- log(message, ...args) {
1101
- if (this.config.enableLogging) {
1102
- console.log(`[SessionManager] ${message}`, ...args);
1103
- }
1104
- }
1105
- formatError(errors) {
1106
- if (!errors) return "Unknown error";
1107
- if (typeof errors === "string") return errors;
1108
- if (typeof errors === "object") {
1109
- const errorMessages = Object.values(errors).flat();
1110
- return errorMessages.join(", ");
1111
- }
1112
- return "Authentication failed";
1113
- }
1114
- };
1115
-
1116
- // src/hooks/useSession.ts
1117
- function useSession(options = {}) {
1118
- const [state, setState] = useState3({
1119
- isAuthenticated: false,
1120
- isLoading: true,
1121
- isInitialized: false,
1122
- tokens: null,
1123
- error: null
1124
- });
1125
- const sessionManager = SessionManager.getInstance();
1126
- const initialize = useCallback(async () => {
1127
- try {
1128
- setState((prev) => ({ ...prev, isLoading: true, error: null }));
1129
- const config = {
1130
- autoRestore: options.autoRestore ?? true,
1131
- enableLogging: options.enableLogging ?? false,
1132
- showNotification: options.showNotification,
1133
- translateFn: options.translateFn,
1134
- onSessionExpired: () => {
1135
- setState((prev) => ({
1136
- ...prev,
1137
- isAuthenticated: false,
1138
- tokens: null,
1139
- error: "Session expired"
1140
- }));
1141
- options.onSessionExpired?.();
1142
- },
1143
- onSessionRestored: (tokens) => {
1144
- setState((prev) => ({
1145
- ...prev,
1146
- isAuthenticated: true,
1147
- tokens,
1148
- error: null
1149
- }));
1150
- options.onSessionRestored?.(tokens);
1151
- },
1152
- onLoginSuccess: (tokens) => {
1153
- setState((prev) => ({
1154
- ...prev,
1155
- isAuthenticated: true,
1156
- tokens,
1157
- error: null
1158
- }));
1159
- },
1160
- onLogout: () => {
1161
- setState((prev) => ({
1162
- ...prev,
1163
- isAuthenticated: false,
1164
- tokens: null,
1165
- error: null
1166
- }));
1167
- }
1168
- };
1169
- await sessionManager.initialize(config);
1170
- sessionManager.setupResponseInterceptor();
1171
- const isAuth = sessionManager.isAuthenticated();
1172
- const tokenInfo = sessionManager.getTokenInfo();
1173
- setState((prev) => ({
1174
- ...prev,
1175
- isAuthenticated: isAuth,
1176
- isInitialized: true,
1177
- isLoading: false,
1178
- tokens: tokenInfo.crudifyTokens.accessToken ? {
1179
- accessToken: tokenInfo.crudifyTokens.accessToken,
1180
- refreshToken: tokenInfo.crudifyTokens.refreshToken,
1181
- expiresAt: tokenInfo.crudifyTokens.expiresAt,
1182
- refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
1183
- } : null
1184
- }));
1185
- } catch (error) {
1186
- const errorMessage = error instanceof Error ? error.message : "Initialization failed";
1187
- setState((prev) => ({
1188
- ...prev,
1189
- isLoading: false,
1190
- isInitialized: true,
1191
- error: errorMessage
1192
- }));
1193
- }
1194
- }, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
1195
- const login = useCallback(
1196
- async (email, password) => {
1197
- setState((prev) => ({ ...prev, isLoading: true, error: null }));
1198
- try {
1199
- const result = await sessionManager.login(email, password);
1200
- if (result.success && result.tokens) {
1201
- setState((prev) => ({
1202
- ...prev,
1203
- isAuthenticated: true,
1204
- tokens: result.tokens,
1205
- isLoading: false,
1206
- error: null
1207
- }));
1208
- } else {
1209
- setState((prev) => ({
1210
- ...prev,
1211
- isAuthenticated: false,
1212
- tokens: null,
1213
- isLoading: false,
1214
- error: null
1215
- // No establecer error global para errores de login
1216
- }));
1217
- }
1218
- return result;
1219
- } catch (error) {
1220
- const errorMsg = error instanceof Error ? error.message : "Login failed";
1221
- const isCredentialError = errorMsg.includes("INVALID_CREDENTIALS") || errorMsg.includes("Invalid email") || errorMsg.includes("Invalid password") || errorMsg.includes("credentials");
1222
- setState((prev) => ({
1223
- ...prev,
1224
- isAuthenticated: false,
1225
- tokens: null,
1226
- isLoading: false,
1227
- error: isCredentialError ? null : errorMsg
1228
- // Solo errores del sistema van al estado global
1229
- }));
1230
- return {
1231
- success: false,
1232
- error: errorMsg
1233
- };
1234
- }
1235
- },
1236
- [sessionManager]
1237
- );
1238
- const logout = useCallback(async () => {
1239
- setState((prev) => ({ ...prev, isLoading: true }));
1240
- try {
1241
- await sessionManager.logout();
1242
- setState((prev) => ({
1243
- ...prev,
1244
- isAuthenticated: false,
1245
- tokens: null,
1246
- isLoading: false,
1247
- error: null
1248
- }));
1249
- } catch (error) {
1250
- setState((prev) => ({
1251
- ...prev,
1252
- isAuthenticated: false,
1253
- tokens: null,
1254
- isLoading: false,
1255
- error: error instanceof Error ? error.message : "Logout error"
1256
- }));
1257
- }
1258
- }, [sessionManager]);
1259
- const refreshTokens = useCallback(async () => {
1260
- try {
1261
- const success = await sessionManager.refreshTokens();
1262
- if (success) {
1263
- const tokenInfo = sessionManager.getTokenInfo();
1264
- setState((prev) => ({
1265
- ...prev,
1266
- tokens: tokenInfo.crudifyTokens.accessToken ? {
1267
- accessToken: tokenInfo.crudifyTokens.accessToken,
1268
- refreshToken: tokenInfo.crudifyTokens.refreshToken,
1269
- expiresAt: tokenInfo.crudifyTokens.expiresAt,
1270
- refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
1271
- } : null,
1272
- error: null
1273
- }));
1274
- } else {
1275
- setState((prev) => ({
1276
- ...prev,
1277
- isAuthenticated: false,
1278
- tokens: null,
1279
- error: "Token refresh failed"
1280
- }));
1281
- }
1282
- return success;
1283
- } catch (error) {
1284
- setState((prev) => ({
1285
- ...prev,
1286
- isAuthenticated: false,
1287
- tokens: null,
1288
- error: error instanceof Error ? error.message : "Token refresh failed"
1289
- }));
1290
- return false;
1291
- }
1292
- }, [sessionManager]);
1293
- const clearError = useCallback(() => {
1294
- setState((prev) => ({ ...prev, error: null }));
1295
- }, []);
1296
- const getTokenInfo = useCallback(() => {
1297
- return sessionManager.getTokenInfo();
1298
- }, [sessionManager]);
1299
- useEffect4(() => {
1300
- initialize();
1301
- }, [initialize]);
1302
- return {
1303
- // Estado
1304
- ...state,
1305
- // Acciones
1306
- login,
1307
- logout,
1308
- refreshTokens,
1309
- clearError,
1310
- getTokenInfo,
1311
- // Utilidades
1312
- isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
1313
- // 5 minutos
1314
- expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
1315
- refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
1316
- };
1317
- }
1318
-
1319
- // src/utils/jwtUtils.ts
1320
- var decodeJwtSafely = (token) => {
1321
- try {
1322
- const parts = token.split(".");
1323
- if (parts.length !== 3) {
1324
- console.warn("Invalid JWT format: token must have 3 parts");
1325
- return null;
1326
- }
1327
- const payload = parts[1];
1328
- const paddedPayload = payload + "=".repeat((4 - payload.length % 4) % 4);
1329
- const decodedPayload = JSON.parse(atob(paddedPayload));
1330
- return decodedPayload;
1331
- } catch (error) {
1332
- console.warn("Failed to decode JWT token:", error);
1333
- return null;
1334
- }
1335
- };
1336
- var getCurrentUserEmail = () => {
1337
- try {
1338
- let token = null;
1339
- token = sessionStorage.getItem("authToken");
1340
- console.log("\u{1F50D} getCurrentUserEmail - authToken:", token ? `${token.substring(0, 20)}...` : null);
1341
- if (!token) {
1342
- token = sessionStorage.getItem("token");
1343
- console.log("\u{1F50D} getCurrentUserEmail - token:", token ? `${token.substring(0, 20)}...` : null);
1344
- }
1345
- if (!token) {
1346
- token = localStorage.getItem("authToken") || localStorage.getItem("token");
1347
- console.log("\u{1F50D} getCurrentUserEmail - localStorage:", token ? `${token.substring(0, 20)}...` : null);
1348
- }
1349
- if (!token) {
1350
- console.warn("\u{1F50D} getCurrentUserEmail - No token found in any storage");
1351
- return null;
1352
- }
1353
- const payload = decodeJwtSafely(token);
1354
- if (!payload) {
1355
- console.warn("\u{1F50D} getCurrentUserEmail - Failed to decode token");
1356
- return null;
1357
- }
1358
- const email = payload.email || payload["cognito:username"] || null;
1359
- console.log("\u{1F50D} getCurrentUserEmail - Extracted email:", email);
1360
- return email;
1361
- } catch (error) {
1362
- console.warn("Failed to get current user email:", error);
1363
- return null;
1364
- }
1365
- };
1366
- var isTokenExpired = (token) => {
1367
- try {
1368
- const payload = decodeJwtSafely(token);
1369
- if (!payload || !payload.exp) return true;
1370
- const currentTime = Math.floor(Date.now() / 1e3);
1371
- return payload.exp < currentTime;
1372
- } catch {
1373
- return true;
1374
- }
1375
- };
1376
-
1377
- // src/components/GlobalNotificationProvider.tsx
1378
- import { useState as useState4, createContext as createContext4, useContext as useContext4, useCallback as useCallback2, useEffect as useEffect5 } from "react";
1379
- import { Snackbar, Alert, Box, Portal } from "@mui/material";
1380
- import { v4 as uuidv4 } from "uuid";
1381
- import DOMPurify from "dompurify";
1382
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
1383
- var GlobalNotificationContext = createContext4(null);
1384
- var sanitizeNotificationContent = (html) => {
1385
- return DOMPurify.sanitize(html, {
1386
- // Solo permitir tags seguros para notificaciones
1387
- ALLOWED_TAGS: ["b", "i", "em", "strong", "br", "span"],
1388
- ALLOWED_ATTR: ["class"],
1389
- // Remover scripts y eventos
1390
- FORBID_TAGS: ["script", "iframe", "object", "embed"],
1391
- FORBID_ATTR: ["onload", "onerror", "onclick", "onmouseover", "onfocus", "onblur"],
1392
- // Configuración adicional de seguridad
1393
- WHOLE_DOCUMENT: false,
1394
- RETURN_DOM: false,
1395
- RETURN_DOM_FRAGMENT: false,
1396
- RETURN_TRUSTED_TYPE: false
1397
- });
1398
- };
1399
- var GlobalNotificationProvider = ({
1400
- children,
1401
- maxNotifications = 5,
1402
- defaultAutoHideDuration = 6e3,
1403
- position = { vertical: "top", horizontal: "right" },
1404
- enabled = false,
1405
- // ✅ Por defecto DESACTIVADO
1406
- allowHtml = false
1407
- // Por defecto no permitir HTML
1408
- }) => {
1409
- const [notifications, setNotifications] = useState4([]);
1410
- const showNotification = useCallback2(
1411
- (message, severity = "info", options) => {
1412
- if (!enabled) {
1413
- return "";
1414
- }
1415
- if (!message || typeof message !== "string") {
1416
- console.warn("\u26A0\uFE0F GlobalNotificationProvider: Invalid message provided");
1417
- return "";
1418
- }
1419
- if (message.length > 1e3) {
1420
- console.warn("\u26A0\uFE0F GlobalNotificationProvider: Message too long, truncating");
1421
- message = message.substring(0, 1e3) + "...";
1422
- }
1423
- const id = uuidv4();
1424
- const newNotification = {
1425
- id,
1426
- message,
1427
- severity,
1428
- autoHideDuration: options?.autoHideDuration ?? defaultAutoHideDuration,
1429
- persistent: options?.persistent ?? false,
1430
- allowHtml: options?.allowHtml ?? allowHtml
1431
- // Usar valor del provider por defecto
1432
- };
1433
- setNotifications((prev) => {
1434
- const updatedNotifications = prev.length >= maxNotifications ? prev.slice(-(maxNotifications - 1)) : prev;
1435
- return [...updatedNotifications, newNotification];
1436
- });
1437
- return id;
1438
- },
1439
- [maxNotifications, defaultAutoHideDuration, enabled, allowHtml]
1440
- );
1441
- const hideNotification = useCallback2((id) => {
1442
- setNotifications((prev) => prev.filter((notification) => notification.id !== id));
1443
- }, []);
1444
- const clearAllNotifications = useCallback2(() => {
1445
- setNotifications([]);
1446
- }, []);
1447
- const value = {
1448
- showNotification,
1449
- hideNotification,
1450
- clearAllNotifications
1451
- };
1452
- return /* @__PURE__ */ jsxs(GlobalNotificationContext.Provider, { value, children: [
1453
- children,
1454
- enabled && /* @__PURE__ */ jsx4(Portal, { children: /* @__PURE__ */ jsx4(
1455
- Box,
1456
- {
1457
- sx: {
1458
- position: "fixed",
1459
- zIndex: 9999,
1460
- [position.vertical]: position.vertical === "top" ? 24 : 24,
1461
- [position.horizontal]: position.horizontal === "right" ? 24 : position.horizontal === "left" ? 24 : "50%",
1462
- ...position.horizontal === "center" && {
1463
- transform: "translateX(-50%)"
1464
- },
1465
- display: "flex",
1466
- flexDirection: position.vertical === "top" ? "column" : "column-reverse",
1467
- gap: 1,
1468
- maxWidth: "400px",
1469
- width: "auto"
1470
- },
1471
- children: notifications.map((notification) => /* @__PURE__ */ jsx4(NotificationItem, { notification, onClose: () => hideNotification(notification.id) }, notification.id))
1472
- }
1473
- ) })
1474
- ] });
1475
- };
1476
- var NotificationItem = ({ notification, onClose }) => {
1477
- const [open, setOpen] = useState4(true);
1478
- const handleClose = useCallback2(
1479
- (_event, reason) => {
1480
- if (reason === "clickaway") return;
1481
- setOpen(false);
1482
- setTimeout(onClose, 300);
1483
- },
1484
- [onClose]
1485
- );
1486
- useEffect5(() => {
1487
- if (!notification.persistent && notification.autoHideDuration) {
1488
- const timer = setTimeout(() => {
1489
- handleClose();
1490
- }, notification.autoHideDuration);
1491
- return () => clearTimeout(timer);
1492
- }
1493
- }, [notification.autoHideDuration, notification.persistent, handleClose]);
1494
- return /* @__PURE__ */ jsx4(
1495
- Snackbar,
1496
- {
1497
- open,
1498
- onClose: handleClose,
1499
- sx: {
1500
- position: "relative",
1501
- "& .MuiSnackbarContent-root": {
1502
- minWidth: "auto"
1503
- }
1504
- },
1505
- TransitionProps: {
1506
- enter: true,
1507
- exit: true
1508
- },
1509
- children: /* @__PURE__ */ jsx4(
1510
- Alert,
1511
- {
1512
- variant: "filled",
1513
- severity: notification.severity,
1514
- onClose: handleClose,
1515
- sx: {
1516
- width: "100%",
1517
- minWidth: "280px",
1518
- maxWidth: "400px",
1519
- wordBreak: "break-word"
1520
- },
1521
- children: notification.allowHtml ? /* @__PURE__ */ jsx4("span", { dangerouslySetInnerHTML: { __html: sanitizeNotificationContent(notification.message) } }) : /* @__PURE__ */ jsx4("span", { children: notification.message })
1522
- }
1523
- )
1524
- }
1525
- );
1526
- };
1527
- var useGlobalNotification = () => {
1528
- const context = useContext4(GlobalNotificationContext);
1529
- if (!context) {
1530
- throw new Error("useGlobalNotification debe ser usado dentro de un GlobalNotificationProvider");
1531
- }
1532
- return context;
1533
- };
1534
-
1535
- // src/providers/SessionProvider.tsx
1536
- import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
1537
- var SessionContext = createContext5(void 0);
1538
- function InnerSessionProvider({
1539
- children,
1540
- options = {},
1541
- config: propConfig,
1542
- showNotifications = false,
1543
- notificationOptions = {}
1544
- }) {
1545
- let showNotificationFn;
1546
- try {
1547
- const { showNotification } = useGlobalNotification();
1548
- showNotificationFn = showNotification;
1549
- } catch {
1550
- }
1551
- const enhancedOptions = React5.useMemo(
1552
- () => ({
1553
- ...options,
1554
- showNotification: showNotificationFn,
1555
- // TODO: Agregar translateFn cuando esté disponible
1556
- onSessionExpired: () => {
1557
- options.onSessionExpired?.();
1558
- }
1559
- }),
1560
- [options, showNotificationFn]
1561
- );
1562
- const sessionHook = useSession(enhancedOptions);
1563
- const resolvedConfig = useMemo2(() => {
1564
- let publicApiKey;
1565
- let env;
1566
- let appName;
1567
- let loginActions;
1568
- let logo;
1569
- let configSource = "unknown";
1570
- if (propConfig?.publicApiKey) {
1571
- publicApiKey = propConfig.publicApiKey;
1572
- configSource = "props";
1573
- }
1574
- if (propConfig?.env) {
1575
- env = propConfig.env;
1576
- }
1577
- if (propConfig?.appName) {
1578
- appName = propConfig.appName;
1579
- }
1580
- if (propConfig?.loginActions) {
1581
- loginActions = propConfig.loginActions;
1582
- }
1583
- if (propConfig?.logo) {
1584
- logo = propConfig.logo;
1585
- }
1586
- if (!publicApiKey) {
1587
- const cookieApiKey = getCookie("publicApiKey");
1588
- const cookieEnv = getCookie("environment");
1589
- const cookieAppName = getCookie("appName");
1590
- const cookieLoginActions = getCookie("loginActions");
1591
- const cookieLogo = getCookie("logo");
1592
- if (cookieApiKey) {
1593
- publicApiKey = cookieApiKey;
1594
- configSource = "cookies";
1595
- }
1596
- if (cookieEnv && ["dev", "stg", "prod"].includes(cookieEnv)) {
1597
- env = cookieEnv;
1598
- }
1599
- if (cookieAppName) {
1600
- appName = decodeURIComponent(cookieAppName);
1601
- }
1602
- if (cookieLoginActions) {
1603
- const decodedActions = decodeURIComponent(cookieLoginActions);
1604
- loginActions = decodedActions.split(",").map((s) => s.trim()).filter(Boolean);
1605
- }
1606
- if (cookieLogo) logo = decodeURIComponent(cookieLogo);
1607
- }
1608
- return {
1609
- publicApiKey,
1610
- env,
1611
- appName,
1612
- loginActions,
1613
- logo
1614
- };
1615
- }, [propConfig]);
1616
- const sessionData = useMemo2(() => {
1617
- if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
1618
- return null;
1619
- }
1620
- try {
1621
- const decoded = decodeJwtSafely(sessionHook.tokens.accessToken);
1622
- if (decoded && decoded.sub && decoded.email && decoded.subscriber) {
1623
- const result = {
1624
- _id: decoded.sub,
1625
- email: decoded.email,
1626
- subscriberKey: decoded.subscriber
1627
- };
1628
- Object.keys(decoded).forEach((key) => {
1629
- if (!["sub", "email", "subscriber"].includes(key)) {
1630
- result[key] = decoded[key];
1631
- }
1632
- });
1633
- return result;
1634
- }
1635
- } catch (error) {
1636
- console.error("Error decoding JWT token for sessionData:", error);
1637
- }
1638
- return null;
1639
- }, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
1640
- const contextValue = {
1641
- ...sessionHook,
1642
- sessionData,
1643
- config: resolvedConfig
1644
- };
1645
- const notificationConfig = {
1646
- enabled: showNotifications,
1647
- maxNotifications: notificationOptions.maxNotifications || 5,
1648
- defaultAutoHideDuration: notificationOptions.defaultAutoHideDuration || 6e3,
1649
- position: notificationOptions.position || { vertical: "top", horizontal: "right" }
1650
- };
1651
- return /* @__PURE__ */ jsx5(SessionContext.Provider, { value: contextValue, children });
1652
- }
1653
- function SessionProvider(props) {
1654
- const notificationConfig = {
1655
- enabled: props.showNotifications,
1656
- maxNotifications: props.notificationOptions?.maxNotifications || 5,
1657
- defaultAutoHideDuration: props.notificationOptions?.defaultAutoHideDuration || 6e3,
1658
- position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" },
1659
- allowHtml: props.notificationOptions?.allowHtml || false
1660
- // Pasar allowHtml desde notificationOptions
1661
- };
1662
- return /* @__PURE__ */ jsx5(GlobalNotificationProvider, { ...notificationConfig, children: /* @__PURE__ */ jsx5(InnerSessionProvider, { ...props }) });
1663
- }
1664
- function useSessionContext() {
1665
- const context = useContext5(SessionContext);
1666
- if (context === void 0) {
1667
- throw new Error("useSessionContext must be used within a SessionProvider");
1668
- }
1669
- return context;
1670
- }
1671
- function ProtectedRoute({ children, fallback = /* @__PURE__ */ jsx5("div", { children: "Please log in to access this content" }), redirectTo }) {
1672
- const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
1673
- if (!isInitialized || isLoading) {
1674
- return /* @__PURE__ */ jsx5("div", { children: "Loading..." });
1675
- }
1676
- if (!isAuthenticated) {
1677
- if (redirectTo) {
1678
- redirectTo();
1679
- return null;
1680
- }
1681
- return /* @__PURE__ */ jsx5(Fragment, { children: fallback });
1682
- }
1683
- return /* @__PURE__ */ jsx5(Fragment, { children });
1684
- }
1685
- function SessionDebugInfo() {
1686
- const session = useSessionContext();
1687
- if (!session.isInitialized) {
1688
- return /* @__PURE__ */ jsx5("div", { children: "Session not initialized" });
1689
- }
1690
- return /* @__PURE__ */ jsxs2(
1691
- "div",
1692
- {
1693
- style: {
1694
- padding: "10px",
1695
- margin: "10px",
1696
- border: "1px solid #ccc",
1697
- borderRadius: "4px",
1698
- fontSize: "12px",
1699
- fontFamily: "monospace"
1700
- },
1701
- children: [
1702
- /* @__PURE__ */ jsx5("h4", { children: "Session Debug Info" }),
1703
- /* @__PURE__ */ jsxs2("div", { children: [
1704
- /* @__PURE__ */ jsx5("strong", { children: "Authenticated:" }),
1705
- " ",
1706
- session.isAuthenticated ? "Yes" : "No"
1707
- ] }),
1708
- /* @__PURE__ */ jsxs2("div", { children: [
1709
- /* @__PURE__ */ jsx5("strong", { children: "Loading:" }),
1710
- " ",
1711
- session.isLoading ? "Yes" : "No"
1712
- ] }),
1713
- /* @__PURE__ */ jsxs2("div", { children: [
1714
- /* @__PURE__ */ jsx5("strong", { children: "Error:" }),
1715
- " ",
1716
- session.error || "None"
1717
- ] }),
1718
- session.tokens && /* @__PURE__ */ jsxs2(Fragment, { children: [
1719
- /* @__PURE__ */ jsxs2("div", { children: [
1720
- /* @__PURE__ */ jsx5("strong", { children: "Access Token:" }),
1721
- " ",
1722
- session.tokens.accessToken.substring(0, 20),
1723
- "..."
1724
- ] }),
1725
- /* @__PURE__ */ jsxs2("div", { children: [
1726
- /* @__PURE__ */ jsx5("strong", { children: "Refresh Token:" }),
1727
- " ",
1728
- session.tokens.refreshToken.substring(0, 20),
1729
- "..."
1730
- ] }),
1731
- /* @__PURE__ */ jsxs2("div", { children: [
1732
- /* @__PURE__ */ jsx5("strong", { children: "Access Expires In:" }),
1733
- " ",
1734
- Math.round(session.expiresIn / 1e3 / 60),
1735
- " minutes"
1736
- ] }),
1737
- /* @__PURE__ */ jsxs2("div", { children: [
1738
- /* @__PURE__ */ jsx5("strong", { children: "Refresh Expires In:" }),
1739
- " ",
1740
- Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
1741
- " hours"
1742
- ] }),
1743
- /* @__PURE__ */ jsxs2("div", { children: [
1744
- /* @__PURE__ */ jsx5("strong", { children: "Expiring Soon:" }),
1745
- " ",
1746
- session.isExpiringSoon ? "Yes" : "No"
1747
- ] })
1748
- ] })
1749
- ]
1750
- }
1751
- );
1752
- }
1753
-
1754
- // src/components/CrudifyLogin/Forms/LoginForm.tsx
1755
- import { useEffect as useEffect6, useRef } from "react";
1756
- import { Typography, TextField, Button, Box as Box2, CircularProgress, Alert as Alert2, Link } from "@mui/material";
1757
-
1758
- // src/utils/errorHandler.ts
1759
- var ERROR_CODES = {
1760
- // Authentication Errors
1761
- INVALID_CREDENTIALS: "INVALID_CREDENTIALS",
1762
- UNAUTHORIZED: "UNAUTHORIZED",
1763
- INVALID_API_KEY: "INVALID_API_KEY",
1764
- USER_NOT_FOUND: "USER_NOT_FOUND",
1765
- USER_NOT_ACTIVE: "USER_NOT_ACTIVE",
1766
- NO_PERMISSION: "NO_PERMISSION",
1767
- // Data Errors
1768
- ITEM_NOT_FOUND: "ITEM_NOT_FOUND",
1769
- NOT_FOUND: "NOT_FOUND",
1770
- IN_USE: "IN_USE",
1771
- // Validation Errors
1772
- FIELD_ERROR: "FIELD_ERROR",
1773
- BAD_REQUEST: "BAD_REQUEST",
1774
- INVALID_EMAIL: "INVALID_EMAIL",
1775
- INVALID_CODE: "INVALID_CODE",
1776
- // System Errors
1777
- INTERNAL_SERVER_ERROR: "INTERNAL_SERVER_ERROR",
1778
- DATABASE_CONNECTION_ERROR: "DATABASE_CONNECTION_ERROR",
1779
- INVALID_CONFIGURATION: "INVALID_CONFIGURATION",
1780
- UNKNOWN_OPERATION: "UNKNOWN_OPERATION",
1781
- // Rate Limiting
1782
- TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS",
1783
- // Network Errors
1784
- NETWORK_ERROR: "NETWORK_ERROR",
1785
- TIMEOUT_ERROR: "TIMEOUT_ERROR"
1786
- };
1787
- var ERROR_SEVERITY_MAP = {
1788
- // Authentication - warning (user can fix)
1789
- [ERROR_CODES.INVALID_CREDENTIALS]: "warning",
1790
- [ERROR_CODES.UNAUTHORIZED]: "warning",
1791
- [ERROR_CODES.INVALID_API_KEY]: "error",
1792
- [ERROR_CODES.USER_NOT_FOUND]: "warning",
1793
- [ERROR_CODES.USER_NOT_ACTIVE]: "warning",
1794
- [ERROR_CODES.NO_PERMISSION]: "warning",
1795
- // Data - info (might be expected)
1796
- [ERROR_CODES.ITEM_NOT_FOUND]: "info",
1797
- [ERROR_CODES.NOT_FOUND]: "info",
1798
- [ERROR_CODES.IN_USE]: "warning",
1799
- // Validation - warning (user input issue)
1800
- [ERROR_CODES.FIELD_ERROR]: "warning",
1801
- [ERROR_CODES.BAD_REQUEST]: "warning",
1802
- [ERROR_CODES.INVALID_EMAIL]: "warning",
1803
- [ERROR_CODES.INVALID_CODE]: "warning",
1804
- // System - error (server issue)
1805
- [ERROR_CODES.INTERNAL_SERVER_ERROR]: "error",
1806
- [ERROR_CODES.DATABASE_CONNECTION_ERROR]: "error",
1807
- [ERROR_CODES.INVALID_CONFIGURATION]: "error",
1808
- [ERROR_CODES.UNKNOWN_OPERATION]: "error",
1809
- // Rate limiting - warning with higher duration
1810
- [ERROR_CODES.TOO_MANY_REQUESTS]: "warning",
1811
- // Network - error
1812
- [ERROR_CODES.NETWORK_ERROR]: "error",
1813
- [ERROR_CODES.TIMEOUT_ERROR]: "error"
1814
- };
1815
- function parseApiError(response) {
1816
- const errors = [];
1817
- try {
1818
- const apiResponse = response;
1819
- if (apiResponse.data && typeof apiResponse.data === "object") {
1820
- const responseData = apiResponse.data;
1821
- if (responseData.response) {
1822
- const { status, fieldsWarning } = responseData.response;
1823
- if (fieldsWarning && typeof fieldsWarning === "object") {
1824
- Object.entries(fieldsWarning).forEach(([field, messages]) => {
1825
- if (Array.isArray(messages) && messages.length > 0) {
1826
- errors.push({
1827
- code: ERROR_CODES.FIELD_ERROR,
1828
- message: messages[0],
1829
- severity: "warning",
1830
- field
1831
- });
1832
- }
1833
- });
1834
- }
1835
- if (status && typeof status === "string") {
1836
- const errorCode = status;
1837
- if (ERROR_SEVERITY_MAP[errorCode]) {
1838
- errors.push({
1839
- code: errorCode,
1840
- message: getErrorMessage(errorCode),
1841
- severity: ERROR_SEVERITY_MAP[errorCode]
1842
- });
1843
- }
1844
- }
1845
- }
1846
- }
1847
- if (apiResponse.errors) {
1848
- if (typeof apiResponse.errors === "string") {
1849
- errors.push({
1850
- code: ERROR_CODES.BAD_REQUEST,
1851
- message: apiResponse.errors,
1852
- severity: "warning"
1853
- });
1854
- } else if (typeof apiResponse.errors === "object") {
1855
- const errorObj = apiResponse.errors;
1856
- Object.entries(errorObj).forEach(([field, messages]) => {
1857
- if (Array.isArray(messages) && messages.length > 0) {
1858
- if (field === "_error") {
1859
- messages.forEach((msg) => {
1860
- const errorCode = typeof msg === "string" && isValidErrorCode(msg) ? msg : ERROR_CODES.BAD_REQUEST;
1861
- errors.push({
1862
- code: errorCode,
1863
- message: typeof msg === "string" ? msg : getErrorMessage(errorCode),
1864
- severity: ERROR_SEVERITY_MAP[errorCode] || "warning"
1865
- });
1866
- });
1867
- } else if (field === "_graphql") {
1868
- messages.forEach((msg) => {
1869
- if (typeof msg === "string") {
1870
- const errorCode = isValidErrorCode(msg) ? msg : ERROR_CODES.BAD_REQUEST;
1871
- errors.push({
1872
- code: errorCode,
1873
- message: getErrorMessage(errorCode),
1874
- severity: ERROR_SEVERITY_MAP[errorCode] || "warning"
1875
- });
1876
- }
1877
- });
1878
- } else {
1879
- errors.push({
1880
- code: ERROR_CODES.FIELD_ERROR,
1881
- message: typeof messages[0] === "string" ? messages[0] : "Validation error",
1882
- severity: "warning",
1883
- field
1884
- });
1885
- }
1886
- }
1887
- });
1888
- }
1889
- }
1890
- if (errors.length === 0 && apiResponse.success === false) {
1891
- errors.push({
1892
- code: ERROR_CODES.BAD_REQUEST,
1893
- message: "Request failed",
1894
- severity: "warning"
1895
- });
1896
- }
1897
- } catch (error) {
1898
- errors.push({
1899
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
1900
- message: "Failed to parse error response",
1901
- severity: "error",
1902
- details: { originalError: error }
1903
- });
1904
- }
1905
- return errors.length > 0 ? errors : [
1906
- {
1907
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
1908
- message: "Unknown error occurred",
1909
- severity: "error"
1910
- }
1911
- ];
1912
- }
1913
- function parseTransactionError(response) {
1914
- try {
1915
- const transactionResponse = response;
1916
- if (transactionResponse.data && Array.isArray(transactionResponse.data)) {
1917
- const errors = [];
1918
- transactionResponse.data.forEach((item, index) => {
1919
- if (item.response?.status === "TOO_MANY_REQUESTS") {
1920
- errors.push({
1921
- code: ERROR_CODES.TOO_MANY_REQUESTS,
1922
- message: getErrorMessage(ERROR_CODES.TOO_MANY_REQUESTS),
1923
- severity: "warning",
1924
- details: { transactionIndex: index }
1925
- });
1926
- } else if (!item.response || item.response.status !== "OK") {
1927
- errors.push({
1928
- code: ERROR_CODES.BAD_REQUEST,
1929
- message: "Transaction failed",
1930
- severity: "warning",
1931
- details: { transactionIndex: index, response: item.response }
1932
- });
1933
- }
1934
- });
1935
- return errors;
1936
- }
1937
- return parseApiError(response);
1938
- } catch (error) {
1939
- return [
1940
- {
1941
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
1942
- message: "Failed to parse transaction error",
1943
- severity: "error",
1944
- details: { originalError: error }
1945
- }
1946
- ];
1947
- }
1948
- }
1949
- function isValidErrorCode(code) {
1950
- return Object.values(ERROR_CODES).includes(code);
1951
- }
1952
- function getErrorMessage(code) {
1953
- const messages = {
1954
- [ERROR_CODES.INVALID_CREDENTIALS]: "Invalid email or password",
1955
- [ERROR_CODES.UNAUTHORIZED]: "You are not authorized to perform this action",
1956
- [ERROR_CODES.INVALID_API_KEY]: "Invalid API key",
1957
- [ERROR_CODES.USER_NOT_FOUND]: "User not found",
1958
- [ERROR_CODES.USER_NOT_ACTIVE]: "User account is not active",
1959
- [ERROR_CODES.NO_PERMISSION]: "You do not have permission to perform this action",
1960
- [ERROR_CODES.ITEM_NOT_FOUND]: "Item not found",
1961
- [ERROR_CODES.NOT_FOUND]: "Resource not found",
1962
- [ERROR_CODES.IN_USE]: "Resource is currently in use",
1963
- [ERROR_CODES.FIELD_ERROR]: "Validation error",
1964
- [ERROR_CODES.BAD_REQUEST]: "Invalid request",
1965
- [ERROR_CODES.INVALID_EMAIL]: "Please enter a valid email address",
1966
- [ERROR_CODES.INVALID_CODE]: "Invalid or expired code",
1967
- [ERROR_CODES.INTERNAL_SERVER_ERROR]: "Internal server error",
1968
- [ERROR_CODES.DATABASE_CONNECTION_ERROR]: "Database connection error",
1969
- [ERROR_CODES.INVALID_CONFIGURATION]: "Invalid configuration",
1970
- [ERROR_CODES.UNKNOWN_OPERATION]: "Unknown operation",
1971
- [ERROR_CODES.TOO_MANY_REQUESTS]: "Too many requests. Please try again later.",
1972
- [ERROR_CODES.NETWORK_ERROR]: "Network error. Please check your connection.",
1973
- [ERROR_CODES.TIMEOUT_ERROR]: "Request timed out. Please try again."
1974
- };
1975
- return messages[code] || "An unknown error occurred";
1976
- }
1977
- function parseJavaScriptError(error) {
1978
- if (error instanceof Error) {
1979
- if (error.name === "AbortError") {
1980
- return {
1981
- code: ERROR_CODES.TIMEOUT_ERROR,
1982
- message: "Request was cancelled",
1983
- severity: "info"
1984
- };
1985
- }
1986
- if (error.message.includes("NetworkError") || error.message.includes("Failed to fetch")) {
1987
- return {
1988
- code: ERROR_CODES.NETWORK_ERROR,
1989
- message: getErrorMessage(ERROR_CODES.NETWORK_ERROR),
1990
- severity: "error"
1991
- };
1992
- }
1993
- return {
1994
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
1995
- message: error.message || "An unexpected error occurred",
1996
- severity: "error",
1997
- details: { originalError: error }
1998
- };
1999
- }
2000
- return {
2001
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
2002
- message: "An unknown error occurred",
2003
- severity: "error",
2004
- details: { originalError: error }
2005
- };
2006
- }
2007
- function handleCrudifyError(error) {
2008
- if (error instanceof Error) {
2009
- return [parseJavaScriptError(error)];
2010
- }
2011
- if (typeof error === "object" && error !== null) {
2012
- const response = error;
2013
- if (response.data && Array.isArray(response.data)) {
2014
- return parseTransactionError(error);
2015
- }
2016
- return parseApiError(error);
2017
- }
2018
- return [
2019
- {
2020
- code: ERROR_CODES.INTERNAL_SERVER_ERROR,
2021
- message: "An unknown error occurred",
2022
- severity: "error",
2023
- details: { originalError: error }
2024
- }
2025
- ];
2026
- }
2027
-
2028
- // src/components/CrudifyLogin/Forms/LoginForm.tsx
2029
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
2030
- var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
2031
- const { crudify: crudify7 } = useCrudify();
2032
- const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
2033
- const { login: sessionLogin } = useSessionContext();
2034
- const translationContext = useTranslation();
2035
- const { t } = translationContext;
2036
- const i18n = translationContext.i18n;
2037
- const usernameInputRef = useRef(null);
2038
- const errorTranslator = createErrorTranslator(t, {
2039
- currentLanguage: i18n?.language,
2040
- enableDebug: false
2041
- });
2042
- const getRedirectUrl = () => {
2043
- if (state.searchParams.redirect) {
2044
- try {
2045
- const decodedPath = decodeURIComponent(state.searchParams.redirect);
2046
- if (decodedPath.startsWith("/") && !decodedPath.startsWith("//")) {
2047
- return decodedPath;
2048
- }
2049
- } catch (error) {
2050
- }
2051
- }
2052
- return redirectUrl || "/";
2053
- };
2054
- useEffect6(() => {
2055
- const timer = setTimeout(() => {
2056
- if (usernameInputRef.current) usernameInputRef.current.focus();
2057
- }, 100);
2058
- return () => clearTimeout(timer);
2059
- }, []);
2060
- const translateError2 = (parsedError) => {
2061
- console.log("\u{1F50D} [LoginForm] Translating parsed error:", parsedError);
2062
- const result = errorTranslator.translateError({
2063
- code: parsedError.code,
2064
- message: parsedError.message,
2065
- field: parsedError.field
2066
- });
2067
- console.log("\u{1F50D} [LoginForm] Translation result:", result);
2068
- return result;
2069
- };
2070
- const handleLogin = async () => {
2071
- if (state.loading) return;
2072
- if (!state.formData.username.trim()) {
2073
- setFieldError("username", t("login.usernameRequired"));
2074
- return;
2075
- }
2076
- if (!state.formData.password.trim()) {
2077
- setFieldError("password", t("login.passwordRequired"));
2078
- return;
2079
- }
2080
- clearErrors();
2081
- setLoading(true);
2082
- try {
2083
- const response = await sessionLogin(state.formData.username, state.formData.password);
2084
- setLoading(false);
2085
- if (response.success) {
2086
- console.log("\u{1F510} LoginForm - Login successful via SessionProvider, calling onLoginSuccess");
2087
- const finalRedirectUrl = getRedirectUrl();
2088
- if (onLoginSuccess) {
2089
- onLoginSuccess(response.data, finalRedirectUrl);
2090
- }
2091
- } else {
2092
- const errorToHandle = response.rawResponse || response;
2093
- handleLoginError(errorToHandle);
2094
- }
2095
- } catch (error) {
2096
- setLoading(false);
2097
- const parsedErrors = handleCrudifyError(error);
2098
- const translatedErrors = parsedErrors.map(translateError2);
2099
- setFieldError("global", translatedErrors);
2100
- if (onError) {
2101
- onError(translatedErrors.join(", "));
2102
- }
2103
- }
2104
- };
2105
- const handleLoginError = (response) => {
2106
- const parsedErrors = handleCrudifyError(response);
2107
- parsedErrors.forEach((error) => {
2108
- if (error.field) {
2109
- setFieldError(error.field, translateError2(error));
2110
- } else {
2111
- const currentGlobalErrors = state.errors.global || [];
2112
- setFieldError("global", [...currentGlobalErrors, translateError2(error)]);
2113
- }
2114
- });
2115
- };
2116
- const handleSubmit = (e) => {
2117
- e.preventDefault();
2118
- handleLogin();
2119
- };
2120
- const handleKeyDown = (e) => {
2121
- if (e.key === "Enter" && !state.loading) {
2122
- e.preventDefault();
2123
- handleLogin();
2124
- }
2125
- };
2126
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
2127
- /* @__PURE__ */ jsxs3(
2128
- Box2,
2129
- {
2130
- component: "form",
2131
- noValidate: true,
2132
- onSubmit: handleSubmit,
2133
- onKeyDown: handleKeyDown,
2134
- sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 },
2135
- children: [
2136
- /* @__PURE__ */ jsxs3(Box2, { sx: { mb: 1 }, children: [
2137
- /* @__PURE__ */ jsx6(
2138
- Typography,
2139
- {
2140
- variant: "body2",
2141
- component: "label",
2142
- htmlFor: "email",
2143
- sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
2144
- children: t("login.usernameOrEmailLabel")
2145
- }
2146
- ),
2147
- /* @__PURE__ */ jsx6(
2148
- TextField,
2149
- {
2150
- fullWidth: true,
2151
- id: "email",
2152
- name: "email",
2153
- type: "email",
2154
- value: state.formData.username,
2155
- disabled: state.loading,
2156
- onChange: (e) => updateFormData({ username: e.target.value }),
2157
- error: !!state.errors.username,
2158
- helperText: state.errors.username,
2159
- autoComplete: "email",
2160
- placeholder: t("login.usernameOrEmailPlaceholder"),
2161
- inputRef: usernameInputRef,
2162
- required: true
2163
- }
2164
- )
2165
- ] }),
2166
- /* @__PURE__ */ jsxs3(Box2, { sx: { mb: 1 }, children: [
2167
- /* @__PURE__ */ jsx6(
2168
- Typography,
2169
- {
2170
- variant: "body2",
2171
- component: "label",
2172
- htmlFor: "password",
2173
- sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
2174
- children: t("login.passwordLabel")
2175
- }
2176
- ),
2177
- /* @__PURE__ */ jsx6(
2178
- TextField,
2179
- {
2180
- fullWidth: true,
2181
- id: "password",
2182
- name: "password",
2183
- type: "password",
2184
- value: state.formData.password,
2185
- disabled: state.loading,
2186
- onChange: (e) => updateFormData({ password: e.target.value }),
2187
- error: !!state.errors.password,
2188
- helperText: state.errors.password,
2189
- autoComplete: "current-password",
2190
- placeholder: t("login.passwordPlaceholder"),
2191
- required: true
2192
- }
2193
- )
2194
- ] }),
2195
- state.config.loginActions?.includes("forgotPassword") && /* @__PURE__ */ jsx6(Box2, { sx: { display: "flex", justifyContent: "flex-end", alignItems: "center" }, children: /* @__PURE__ */ jsx6(
2196
- Link,
2197
- {
2198
- sx: { cursor: "pointer" },
2199
- onClick: () => {
2200
- onScreenChange?.("forgotPassword", state.searchParams);
2201
- },
2202
- variant: "body2",
2203
- color: "secondary",
2204
- children: t("login.forgotPasswordLink")
2205
- }
2206
- ) }),
2207
- /* @__PURE__ */ jsx6(Button, { disabled: state.loading, type: "submit", fullWidth: true, variant: "contained", color: "primary", sx: { mt: 1, mb: 2 }, children: state.loading ? /* @__PURE__ */ jsx6(CircularProgress, { size: 20 }) : t("login.loginButton") })
2208
- ]
2209
- }
2210
- ),
2211
- /* @__PURE__ */ jsx6(Box2, { children: state.errors.global && state.errors.global.length > 0 && state.errors.global.map((error, index) => /* @__PURE__ */ jsx6(Alert2, { variant: "filled", sx: { mt: 2 }, severity: "error", children: /* @__PURE__ */ jsx6("div", { children: error }) }, index)) }),
2212
- state.config.loginActions?.includes("createUser") && /* @__PURE__ */ jsxs3(Typography, { variant: "body2", align: "center", sx: { color: "text.secondary", mt: 3 }, children: [
2213
- t("login.noAccountPrompt"),
2214
- " ",
2215
- /* @__PURE__ */ jsx6(
2216
- Link,
2217
- {
2218
- sx: { cursor: "pointer" },
2219
- onClick: () => {
2220
- const searchString = Object.keys(state.searchParams).length > 0 ? `?${new URLSearchParams(state.searchParams).toString()}` : "";
2221
- const signupUrl = `/public/users/create${searchString}`;
2222
- onExternalNavigate?.(signupUrl);
2223
- },
2224
- fontWeight: "medium",
2225
- color: "secondary",
2226
- children: t("login.signUpLink")
2227
- }
2228
- )
2229
- ] })
2230
- ] });
2231
- };
2232
- var LoginForm_default = LoginForm;
2233
-
2234
- // src/components/CrudifyLogin/Forms/ForgotPasswordForm.tsx
2235
- import { useState as useState5 } from "react";
2236
- import { Typography as Typography2, TextField as TextField2, Button as Button2, Box as Box3, CircularProgress as CircularProgress2, Alert as Alert3, Link as Link2 } from "@mui/material";
2237
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
2238
- var ForgotPasswordForm = ({ onScreenChange, onError }) => {
2239
- const { crudify: crudify7 } = useCrudify();
2240
- const [email, setEmail] = useState5("");
2241
- const [loading, setLoading] = useState5(false);
2242
- const [errors, setErrors] = useState5([]);
2243
- const [helperTextEmail, setHelperTextEmail] = useState5(null);
2244
- const [emailSent, setEmailSent] = useState5(false);
2245
- const [codeAlreadyExists, setCodeAlreadyExists] = useState5(false);
2246
- const { t } = useTranslation();
2247
- const translateError2 = (parsedError) => {
2248
- const possibleKeys = [
2249
- `errors.auth.${parsedError.code}`,
2250
- `errors.data.${parsedError.code}`,
2251
- `errors.system.${parsedError.code}`,
2252
- `errors.${parsedError.code}`,
2253
- `forgotPassword.${parsedError.code.toLowerCase()}`
2254
- ];
2255
- for (const key of possibleKeys) {
2256
- const translated = t(key);
2257
- if (translated !== key) {
2258
- return translated;
2259
- }
2260
- }
2261
- return parsedError.message || t("error.unknown");
2262
- };
2263
- const validateEmail = (email2) => {
2264
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2265
- return emailRegex.test(email2);
2266
- };
2267
- const handleSubmit = async () => {
2268
- if (loading || !crudify7) return;
2269
- setErrors([]);
2270
- setHelperTextEmail(null);
2271
- if (!email) {
2272
- setHelperTextEmail(t("forgotPassword.emailRequired"));
2273
- return;
2274
- }
2275
- if (!validateEmail(email)) {
2276
- setHelperTextEmail(t("forgotPassword.invalidEmail"));
2277
- return;
2278
- }
2279
- setLoading(true);
2280
- try {
2281
- const data = [{ operation: "requestPasswordReset", data: { email } }];
2282
- const response = await crudify7.transaction(data);
2283
- if (response.success) {
2284
- if (response.data && response.data.existingCodeValid) {
2285
- setCodeAlreadyExists(true);
2286
- } else {
2287
- setEmailSent(true);
2288
- }
2289
- } else {
2290
- const parsedErrors = handleCrudifyError(response);
2291
- const translatedErrors = parsedErrors.map(translateError2);
2292
- setErrors(translatedErrors);
2293
- }
2294
- } catch (error) {
2295
- const parsedErrors = handleCrudifyError(error);
2296
- const translatedErrors = parsedErrors.map(translateError2);
2297
- setErrors(translatedErrors);
2298
- if (onError) {
2299
- onError(translatedErrors.join(", "));
2300
- }
2301
- } finally {
2302
- setLoading(false);
2303
- }
2304
- };
2305
- const handleBack = () => {
2306
- onScreenChange?.("login");
2307
- };
2308
- const handleGoToCheckCode = () => {
2309
- if (emailSent || codeAlreadyExists) {
2310
- onScreenChange?.("checkCode", { email });
2311
- return;
2312
- }
2313
- if (!email) {
2314
- setHelperTextEmail(t("forgotPassword.emailRequired"));
2315
- return;
2316
- }
2317
- if (!validateEmail(email)) {
2318
- setHelperTextEmail(t("forgotPassword.invalidEmail"));
2319
- return;
2320
- }
2321
- onScreenChange?.("checkCode", { email });
2322
- };
2323
- if (emailSent || codeAlreadyExists) {
2324
- return /* @__PURE__ */ jsx7(Fragment3, { children: /* @__PURE__ */ jsxs4(Box3, { sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2, textAlign: "center" }, children: [
2325
- /* @__PURE__ */ jsxs4(Box3, { sx: { mb: 2 }, children: [
2326
- /* @__PURE__ */ jsx7(Typography2, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: codeAlreadyExists ? t("forgotPassword.codeAlreadyExistsMessage") : t("forgotPassword.emailSentMessage") }),
2327
- /* @__PURE__ */ jsx7(Typography2, { variant: "body2", sx: { color: codeAlreadyExists ? "success.main" : "grey.600" }, children: codeAlreadyExists ? t("forgotPassword.checkEmailInstructions") : t("forgotPassword.checkEmailInstructions") })
2328
- ] }),
2329
- /* @__PURE__ */ jsx7(Button2, { type: "button", onClick: handleGoToCheckCode, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: t("forgotPassword.enterCodeLink") }),
2330
- /* @__PURE__ */ jsx7(Box3, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx7(Link2, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
2331
- ] }) });
2332
- }
2333
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2334
- /* @__PURE__ */ jsxs4(Box3, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
2335
- /* @__PURE__ */ jsxs4(Box3, { sx: { mb: 2 }, children: [
2336
- /* @__PURE__ */ jsx7(Typography2, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("forgotPassword.title") }),
2337
- /* @__PURE__ */ jsx7(Typography2, { variant: "body2", sx: { color: "grey.600" }, children: t("forgotPassword.instructions") })
2338
- ] }),
2339
- /* @__PURE__ */ jsxs4(Box3, { sx: { mb: 1 }, children: [
2340
- /* @__PURE__ */ jsx7(
2341
- Typography2,
2342
- {
2343
- variant: "body2",
2344
- component: "label",
2345
- htmlFor: "email",
2346
- sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
2347
- children: t("forgotPassword.emailLabel")
2348
- }
2349
- ),
2350
- /* @__PURE__ */ jsx7(
2351
- TextField2,
2352
- {
2353
- fullWidth: true,
2354
- id: "email",
2355
- name: "email",
2356
- type: "email",
2357
- value: email,
2358
- disabled: loading,
2359
- onChange: (e) => setEmail(e.target.value),
2360
- error: !!helperTextEmail,
2361
- helperText: helperTextEmail,
2362
- autoComplete: "email",
2363
- placeholder: t("forgotPassword.emailPlaceholder"),
2364
- required: true
2365
- }
2366
- )
2367
- ] }),
2368
- /* @__PURE__ */ jsx7(Button2, { disabled: loading, type: "button", onClick: handleSubmit, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx7(CircularProgress2, { size: 20 }) : t("forgotPassword.sendCodeButton") }),
2369
- /* @__PURE__ */ jsxs4(Box3, { sx: { display: "flex", justifyContent: "center", alignItems: "center", gap: 2 }, children: [
2370
- /* @__PURE__ */ jsx7(Link2, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }),
2371
- /* @__PURE__ */ jsx7(Typography2, { variant: "body2", sx: { color: "grey.400" }, children: "\u2022" }),
2372
- /* @__PURE__ */ jsx7(Link2, { sx: { cursor: "pointer" }, onClick: handleGoToCheckCode, variant: "body2", color: "secondary", children: t("login.alreadyHaveCodeLink") })
2373
- ] })
2374
- ] }),
2375
- /* @__PURE__ */ jsx7(Box3, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx7(Alert3, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) })
2376
- ] });
2377
- };
2378
- var ForgotPasswordForm_default = ForgotPasswordForm;
2379
-
2380
- // src/components/CrudifyLogin/Forms/ResetPasswordForm.tsx
2381
- import { useState as useState6, useEffect as useEffect7 } from "react";
2382
- import { Typography as Typography3, TextField as TextField3, Button as Button3, Box as Box4, CircularProgress as CircularProgress3, Alert as Alert4, Link as Link3 } from "@mui/material";
2383
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
2384
- var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess }) => {
2385
- const { crudify: crudify7 } = useCrudify();
2386
- const [newPassword, setNewPassword] = useState6("");
2387
- const [confirmPassword, setConfirmPassword] = useState6("");
2388
- const [loading, setLoading] = useState6(false);
2389
- const [errors, setErrors] = useState6([]);
2390
- const [helperTextNewPassword, setHelperTextNewPassword] = useState6(null);
2391
- const [helperTextConfirmPassword, setHelperTextConfirmPassword] = useState6(null);
2392
- const [email, setEmail] = useState6("");
2393
- const [code, setCode] = useState6("");
2394
- const [fromCodeVerification, setFromCodeVerification] = useState6(false);
2395
- const [validatingCode, setValidatingCode] = useState6(true);
2396
- const [codeValidated, setCodeValidated] = useState6(false);
2397
- const [pendingValidation, setPendingValidation] = useState6(null);
2398
- const [isValidating, setIsValidating] = useState6(false);
2399
- const { t } = useTranslation();
2400
- const translateError2 = (parsedError) => {
2401
- const possibleKeys = [
2402
- `errors.auth.${parsedError.code}`,
2403
- `errors.data.${parsedError.code}`,
2404
- `errors.system.${parsedError.code}`,
2405
- `errors.${parsedError.code}`,
2406
- `resetPassword.${parsedError.code.toLowerCase()}`
2407
- ];
2408
- for (const key of possibleKeys) {
2409
- const translated = t(key);
2410
- if (translated !== key) {
2411
- return translated;
2412
- }
2413
- }
2414
- return parsedError.message || t("error.unknown");
2415
- };
2416
- const getParam = (key) => {
2417
- if (!searchParams) return null;
2418
- if (searchParams instanceof URLSearchParams) {
2419
- return searchParams.get(key);
2420
- }
2421
- return searchParams[key] || null;
2422
- };
2423
- useEffect7(() => {
2424
- if (!searchParams) {
2425
- return;
2426
- }
2427
- if (searchParams) {
2428
- const fromCodeVerificationParam = getParam("fromCodeVerification");
2429
- const emailParam = getParam("email");
2430
- const codeParam = getParam("code");
2431
- if (fromCodeVerificationParam === "true" && emailParam && codeParam) {
2432
- setEmail(emailParam);
2433
- setCode(codeParam);
2434
- setFromCodeVerification(true);
2435
- setCodeValidated(true);
2436
- setValidatingCode(false);
2437
- return;
2438
- }
2439
- const linkParam = getParam("link");
2440
- if (linkParam) {
2441
- try {
2442
- const decodedLink = decodeURIComponent(linkParam);
2443
- const [linkCode, linkEmail] = decodedLink.split("/");
2444
- if (linkCode && linkEmail && linkCode.length === 6) {
2445
- setCode(linkCode);
2446
- setEmail(linkEmail);
2447
- setFromCodeVerification(false);
2448
- setPendingValidation({ email: linkEmail, code: linkCode });
2449
- return;
2450
- }
2451
- } catch (error) {
2452
- }
2453
- }
2454
- if (emailParam && codeParam) {
2455
- setEmail(emailParam);
2456
- setCode(codeParam);
2457
- setFromCodeVerification(false);
2458
- setPendingValidation({ email: emailParam, code: codeParam });
2459
- return;
2460
- }
2461
- }
2462
- setErrors([t("resetPassword.invalidCode")]);
2463
- setValidatingCode(false);
2464
- setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
2465
- }, [searchParams, crudify7, t, onScreenChange]);
2466
- useEffect7(() => {
2467
- if (crudify7 && pendingValidation && !isValidating) {
2468
- setIsValidating(true);
2469
- const validateCode = async (emailToValidate, codeToValidate) => {
2470
- try {
2471
- const data = [
2472
- {
2473
- operation: "validatePasswordResetCode",
2474
- data: { email: emailToValidate, codePassword: codeToValidate }
2475
- }
2476
- ];
2477
- const response = await crudify7.transaction(data);
2478
- if (response.data && Array.isArray(response.data)) {
2479
- const validationResult = response.data[0];
2480
- if (validationResult && validationResult.response && validationResult.response.status === "OK") {
2481
- setCodeValidated(true);
2482
- return;
2483
- }
2484
- }
2485
- if (response.success) {
2486
- setCodeValidated(true);
2487
- } else {
2488
- const parsedErrors = handleCrudifyError(response);
2489
- const translatedErrors = parsedErrors.map(translateError2);
2490
- setErrors(translatedErrors);
2491
- setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
2492
- }
2493
- } catch (error) {
2494
- const parsedErrors = handleCrudifyError(error);
2495
- const translatedErrors = parsedErrors.map(translateError2);
2496
- setErrors(translatedErrors);
2497
- setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
2498
- } finally {
2499
- setValidatingCode(false);
2500
- setPendingValidation(null);
2501
- setIsValidating(false);
2502
- }
2503
- };
2504
- validateCode(pendingValidation.email, pendingValidation.code);
2505
- }
2506
- }, [crudify7, pendingValidation, t, onScreenChange]);
2507
- const validatePassword = (password) => {
2508
- if (password.length < 8) {
2509
- return t("resetPassword.passwordTooShort");
2510
- }
2511
- return null;
2512
- };
2513
- const handleSubmit = async () => {
2514
- if (loading || !crudify7) return;
2515
- setErrors([]);
2516
- setHelperTextNewPassword(null);
2517
- setHelperTextConfirmPassword(null);
2518
- let hasErrors = false;
2519
- if (!newPassword) {
2520
- setHelperTextNewPassword(t("resetPassword.newPasswordRequired"));
2521
- hasErrors = true;
2522
- } else {
2523
- const passwordError = validatePassword(newPassword);
2524
- if (passwordError) {
2525
- setHelperTextNewPassword(passwordError);
2526
- hasErrors = true;
2527
- }
2528
- }
2529
- if (!confirmPassword) {
2530
- setHelperTextConfirmPassword(t("resetPassword.confirmPasswordRequired"));
2531
- hasErrors = true;
2532
- } else if (newPassword !== confirmPassword) {
2533
- setHelperTextConfirmPassword(t("resetPassword.passwordsDoNotMatch"));
2534
- hasErrors = true;
2535
- }
2536
- if (hasErrors) return;
2537
- setLoading(true);
2538
- try {
2539
- const data = [
2540
- {
2541
- operation: "validateAndResetPassword",
2542
- data: { email, codePassword: code, newPassword }
2543
- }
2544
- ];
2545
- const response = await crudify7.transaction(data);
2546
- if (response.success) {
2547
- setErrors([]);
2548
- setTimeout(() => {
2549
- onResetSuccess?.();
2550
- }, 1e3);
2551
- } else {
2552
- const parsedErrors = handleCrudifyError(response);
2553
- const translatedErrors = parsedErrors.map(translateError2);
2554
- setErrors(translatedErrors);
2555
- }
2556
- } catch (error) {
2557
- const parsedErrors = handleCrudifyError(error);
2558
- const translatedErrors = parsedErrors.map(translateError2);
2559
- setErrors(translatedErrors);
2560
- if (onError) {
2561
- onError(translatedErrors.join(", "));
2562
- }
2563
- }
2564
- setLoading(false);
2565
- };
2566
- const handleBack = () => {
2567
- if (fromCodeVerification) {
2568
- onScreenChange?.("checkCode", { email });
2569
- } else {
2570
- onScreenChange?.("forgotPassword");
2571
- }
2572
- };
2573
- if (validatingCode) {
2574
- return /* @__PURE__ */ jsx8(Box4, { sx: { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "300px" }, children: /* @__PURE__ */ jsx8(CircularProgress3, {}) });
2575
- }
2576
- if (!codeValidated) {
2577
- return /* @__PURE__ */ jsx8(Box4, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx8(Alert4, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) });
2578
- }
2579
- return /* @__PURE__ */ jsxs5(Fragment4, { children: [
2580
- /* @__PURE__ */ jsxs5(Box4, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
2581
- /* @__PURE__ */ jsxs5(Box4, { sx: { mb: 2 }, children: [
2582
- /* @__PURE__ */ jsx8(Typography3, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("resetPassword.title") }),
2583
- /* @__PURE__ */ jsx8(Typography3, { variant: "body2", sx: { color: "grey.600" }, children: t("resetPassword.instructions") })
2584
- ] }),
2585
- /* @__PURE__ */ jsxs5(Box4, { sx: { mb: 1 }, children: [
2586
- /* @__PURE__ */ jsx8(
2587
- Typography3,
2588
- {
2589
- variant: "body2",
2590
- component: "label",
2591
- htmlFor: "newPassword",
2592
- sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
2593
- children: t("resetPassword.newPasswordLabel")
2594
- }
2595
- ),
2596
- /* @__PURE__ */ jsx8(
2597
- TextField3,
2598
- {
2599
- fullWidth: true,
2600
- id: "newPassword",
2601
- name: "newPassword",
2602
- type: "password",
2603
- value: newPassword,
2604
- disabled: loading,
2605
- onChange: (e) => setNewPassword(e.target.value),
2606
- error: !!helperTextNewPassword,
2607
- helperText: helperTextNewPassword,
2608
- autoComplete: "new-password",
2609
- placeholder: t("resetPassword.newPasswordPlaceholder"),
2610
- required: true
2611
- }
2612
- )
2613
- ] }),
2614
- /* @__PURE__ */ jsxs5(Box4, { sx: { mb: 1 }, children: [
2615
- /* @__PURE__ */ jsx8(
2616
- Typography3,
2617
- {
2618
- variant: "body2",
2619
- component: "label",
2620
- htmlFor: "confirmPassword",
2621
- sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
2622
- children: t("resetPassword.confirmPasswordLabel")
2623
- }
2624
- ),
2625
- /* @__PURE__ */ jsx8(
2626
- TextField3,
2627
- {
2628
- fullWidth: true,
2629
- id: "confirmPassword",
2630
- name: "confirmPassword",
2631
- type: "password",
2632
- value: confirmPassword,
2633
- disabled: loading,
2634
- onChange: (e) => setConfirmPassword(e.target.value),
2635
- error: !!helperTextConfirmPassword,
2636
- helperText: helperTextConfirmPassword,
2637
- autoComplete: "new-password",
2638
- placeholder: t("resetPassword.confirmPasswordPlaceholder"),
2639
- required: true
2640
- }
2641
- )
2642
- ] }),
2643
- /* @__PURE__ */ jsx8(Button3, { disabled: loading, type: "button", onClick: handleSubmit, fullWidth: true, variant: "contained", color: "primary", sx: { mt: 2, mb: 2 }, children: loading ? /* @__PURE__ */ jsx8(CircularProgress3, { size: 20 }) : t("resetPassword.resetPasswordButton") }),
2644
- /* @__PURE__ */ jsx8(Box4, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx8(Link3, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
2645
- ] }),
2646
- /* @__PURE__ */ jsx8(Box4, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx8(Alert4, { variant: "filled", sx: { mt: 2 }, severity: "error", children: error }, index)) })
2647
- ] });
2648
- };
2649
- var ResetPasswordForm_default = ResetPasswordForm;
2650
-
2651
- // src/components/CrudifyLogin/Forms/CheckCodeForm.tsx
2652
- import { useState as useState7, useEffect as useEffect8 } from "react";
2653
- import { Typography as Typography4, TextField as TextField4, Button as Button4, Box as Box5, CircularProgress as CircularProgress4, Alert as Alert5, Link as Link4 } from "@mui/material";
2654
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2655
- var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
2656
- const { crudify: crudify7 } = useCrudify();
2657
- const [code, setCode] = useState7("");
2658
- const [loading, setLoading] = useState7(false);
2659
- const [errors, setErrors] = useState7([]);
2660
- const [helperTextCode, setHelperTextCode] = useState7(null);
2661
- const [email, setEmail] = useState7("");
2662
- const { t } = useTranslation();
2663
- const getParam = (key) => {
2664
- if (!searchParams) return null;
2665
- if (searchParams instanceof URLSearchParams) {
2666
- return searchParams.get(key);
2667
- }
2668
- return searchParams[key] || null;
2669
- };
2670
- const translateError2 = (parsedError) => {
2671
- const possibleKeys = [
2672
- `errors.auth.${parsedError.code}`,
2673
- `errors.data.${parsedError.code}`,
2674
- `errors.system.${parsedError.code}`,
2675
- `errors.${parsedError.code}`,
2676
- `checkCode.${parsedError.code.toLowerCase()}`
2677
- ];
2678
- for (const key of possibleKeys) {
2679
- const translated = t(key);
2680
- if (translated !== key) {
2681
- return translated;
2682
- }
2683
- }
2684
- return parsedError.message || t("error.unknown");
2685
- };
2686
- useEffect8(() => {
2687
- const emailParam = getParam("email");
2688
- if (emailParam) {
2689
- setEmail(emailParam);
2690
- } else {
2691
- onScreenChange?.("forgotPassword");
2692
- }
2693
- }, [searchParams, onScreenChange]);
2694
- const handleSubmit = async () => {
2695
- if (loading || !crudify7) return;
2696
- setErrors([]);
2697
- setHelperTextCode(null);
2698
- if (!code) {
2699
- setHelperTextCode(t("checkCode.codeRequired"));
2700
- return;
2701
- }
2702
- if (code.length !== 6) {
2703
- setHelperTextCode(t("checkCode.codeRequired"));
2704
- return;
2705
- }
2706
- setLoading(true);
2707
- try {
2708
- const data = [
2709
- {
2710
- operation: "validatePasswordResetCode",
2711
- data: { email, codePassword: code }
2712
- }
2713
- ];
2714
- const response = await crudify7.transaction(data);
2715
- if (response.success) {
2716
- onScreenChange?.("resetPassword", { email, code, fromCodeVerification: "true" });
2717
- } else {
2718
- const parsedErrors = handleCrudifyError(response);
2719
- const translatedErrors = parsedErrors.map(translateError2);
2720
- setErrors(translatedErrors);
2721
- setLoading(false);
2722
- }
2723
- } catch (error) {
2724
- const parsedErrors = handleCrudifyError(error);
2725
- const translatedErrors = parsedErrors.map(translateError2);
2726
- setErrors(translatedErrors);
2727
- setLoading(false);
2728
- if (onError) {
2729
- onError(translatedErrors.join(", "));
2730
- }
2731
- }
2732
- };
2733
- const handleBack = () => {
2734
- onScreenChange?.("forgotPassword");
2735
- };
2736
- const handleCodeChange = (event) => {
2737
- const value = event.target.value.replace(/\D/g, "").slice(0, 6);
2738
- setCode(value);
2739
- };
2740
- return /* @__PURE__ */ jsxs6(Fragment5, { children: [
2741
- /* @__PURE__ */ jsxs6(Box5, { component: "form", noValidate: true, sx: { width: "100%", display: "flex", flexDirection: "column", gap: 2 }, children: [
2742
- /* @__PURE__ */ jsxs6(Box5, { sx: { mb: 2 }, children: [
2743
- /* @__PURE__ */ jsx9(Typography4, { variant: "h5", component: "h1", sx: { mb: 1, fontWeight: 600 }, children: t("checkCode.title") }),
2744
- /* @__PURE__ */ jsx9(Typography4, { variant: "body2", sx: { color: "grey.600" }, children: t("checkCode.instructions") })
2745
- ] }),
2746
- /* @__PURE__ */ jsxs6(Box5, { sx: { mb: 1 }, children: [
2747
- /* @__PURE__ */ jsx9(
2748
- Typography4,
2749
- {
2750
- variant: "body2",
2751
- component: "label",
2752
- htmlFor: "code",
2753
- sx: { display: "block", fontWeight: 500, color: "grey.700", mb: 0.5 },
2754
- children: t("checkCode.codeLabel")
2755
- }
2756
- ),
2757
- /* @__PURE__ */ jsx9(
2758
- TextField4,
2759
- {
2760
- fullWidth: true,
2761
- id: "code",
2762
- name: "code",
2763
- type: "text",
2764
- value: code,
2765
- disabled: loading,
2766
- onChange: handleCodeChange,
2767
- error: !!helperTextCode,
2768
- helperText: helperTextCode,
2769
- placeholder: t("checkCode.codePlaceholder"),
2770
- inputProps: {
2771
- maxLength: 6,
2772
- style: { textAlign: "center", fontSize: "1.5rem", letterSpacing: "0.4rem" }
2773
- },
2774
- required: true
2775
- }
2776
- )
2777
- ] }),
2778
- /* @__PURE__ */ jsx9(
2779
- Button4,
2780
- {
2781
- disabled: loading || code.length !== 6,
2782
- type: "button",
2783
- onClick: handleSubmit,
2784
- fullWidth: true,
2785
- variant: "contained",
2786
- color: "primary",
2787
- sx: { mt: 2, mb: 2 },
2788
- children: loading ? /* @__PURE__ */ jsx9(CircularProgress4, { size: 20 }) : t("checkCode.verifyButton")
2789
- }
2790
- ),
2791
- /* @__PURE__ */ jsx9(Box5, { sx: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ jsx9(Link4, { sx: { cursor: "pointer" }, onClick: handleBack, variant: "body2", color: "secondary", children: t("common.back") }) })
2792
- ] }),
2793
- /* @__PURE__ */ jsx9(Box5, { children: errors.length > 0 && errors.map((error, index) => /* @__PURE__ */ jsx9(Alert5, { sx: { mt: 2 }, severity: "error", children: error }, index)) })
2794
- ] });
2795
- };
2796
- var CheckCodeForm_default = CheckCodeForm;
2797
-
2798
- // src/components/CrudifyLogin/components/CrudifyInitializer.tsx
2799
- import { Box as Box6, CircularProgress as CircularProgress5, Alert as Alert6, Typography as Typography5 } from "@mui/material";
2800
- import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2801
- var CrudifyInitializer = ({ children, fallback }) => {
2802
- const { isLoading, error, isInitialized } = useCrudify();
2803
- const { t } = useTranslation();
2804
- if (isLoading) {
2805
- return fallback || /* @__PURE__ */ jsxs7(
2806
- Box6,
2807
- {
2808
- sx: {
2809
- display: "flex",
2810
- flexDirection: "column",
2811
- alignItems: "center",
2812
- justifyContent: "center",
2813
- minHeight: "200px",
2814
- gap: 2
2815
- },
2816
- children: [
2817
- /* @__PURE__ */ jsx10(CircularProgress5, {}),
2818
- /* @__PURE__ */ jsx10(Typography5, { variant: "body2", color: "text.secondary", children: t("login.initializing") !== "login.initializing" ? t("login.initializing") : "Initializing..." })
2819
- ]
2820
- }
2821
- );
2822
- }
2823
- if (error) {
2824
- return /* @__PURE__ */ jsx10(Alert6, { severity: "error", sx: { mt: 2 }, children: /* @__PURE__ */ jsxs7(Typography5, { variant: "body2", children: [
2825
- t("login.initializationError") !== "login.initializationError" ? t("login.initializationError") : "Initialization error",
2826
- ":",
2827
- " ",
2828
- error
2829
- ] }) });
2830
- }
2831
- if (!isInitialized) {
2832
- return /* @__PURE__ */ jsx10(Alert6, { severity: "warning", sx: { mt: 2 }, children: /* @__PURE__ */ jsx10(Typography5, { variant: "body2", children: t("login.notInitialized") !== "login.notInitialized" ? t("login.notInitialized") : "System not initialized" }) });
2833
- }
2834
- return /* @__PURE__ */ jsx10(Fragment6, { children });
2835
- };
2836
-
2837
- // src/components/CrudifyLogin/index.tsx
2838
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2839
- var CrudifyLoginInternal = ({
2840
- onScreenChange,
2841
- onExternalNavigate,
2842
- onLoginSuccess,
2843
- onError,
2844
- redirectUrl = "/"
2845
- }) => {
2846
- const { t } = useTranslation();
2847
- const { state, setScreen } = useLoginState();
2848
- const { config } = useSessionContext();
2849
- const { showNotification } = useGlobalNotification();
2850
- const handleScreenChange = (screen2, params) => {
2851
- let finalParams = params;
2852
- if (screen2 === "login") {
2853
- finalParams = {};
2854
- } else if (screen2 === "forgotPassword" && !params) {
2855
- finalParams = {};
2856
- }
2857
- setScreen(screen2, finalParams);
2858
- onScreenChange?.(screen2, finalParams);
2859
- };
2860
- const renderCurrentForm = () => {
2861
- const commonProps = {
2862
- onScreenChange: handleScreenChange,
2863
- onExternalNavigate,
2864
- onError,
2865
- redirectUrl
2866
- };
2867
- switch (state.currentScreen) {
2868
- case "forgotPassword":
2869
- return /* @__PURE__ */ jsx11(ForgotPasswordForm_default, { ...commonProps });
2870
- case "checkCode":
2871
- return /* @__PURE__ */ jsx11(CheckCodeForm_default, { ...commonProps, searchParams: state.searchParams });
2872
- case "resetPassword":
2873
- return /* @__PURE__ */ jsx11(
2874
- ResetPasswordForm_default,
2875
- {
2876
- ...commonProps,
2877
- searchParams: state.searchParams,
2878
- onResetSuccess: () => {
2879
- const message = t("resetPassword.successMessage");
2880
- showNotification(message, "success");
2881
- handleScreenChange("login");
2882
- }
2883
- }
2884
- );
2885
- default:
2886
- return /* @__PURE__ */ jsx11(LoginForm_default, { ...commonProps, onLoginSuccess });
2887
- }
2888
- };
2889
- return /* @__PURE__ */ jsxs8(CrudifyInitializer, { children: [
2890
- /* @__PURE__ */ jsx11(Box7, { sx: { display: "flex", justifyContent: "center", mb: 3 }, children: /* @__PURE__ */ jsx11(
2891
- "img",
2892
- {
2893
- src: config.logo || "/nocios-default.png",
2894
- alt: t("login.logoAlt"),
2895
- style: { width: "100%", maxWidth: "150px", height: "auto" },
2896
- onError: (e) => {
2897
- const target = e.target;
2898
- target.src = "/nocios-default.png";
2899
- }
2900
- }
2901
- ) }),
2902
- !config.logo && config.appName && /* @__PURE__ */ jsx11(Typography6, { variant: "h6", component: "h1", sx: { textAlign: "center", mb: 2 }, children: config.appName }),
2903
- renderCurrentForm()
2904
- ] });
2905
- };
2906
- var CrudifyLogin = ({
2907
- translations,
2908
- translationsUrl,
2909
- language = "en",
2910
- initialScreen = "login",
2911
- autoReadFromCookies = true,
2912
- ...props
2913
- }) => {
2914
- const { config } = useSessionContext();
2915
- return /* @__PURE__ */ jsx11(I18nProvider, { translations, translationsUrl, language, children: /* @__PURE__ */ jsx11(CrudifyProvider, { config, children: /* @__PURE__ */ jsx11(LoginStateProvider, { config, initialScreen, autoReadFromCookies, children: /* @__PURE__ */ jsx11(CrudifyLoginInternal, { ...props }) }) }) });
2916
- };
2917
- var CrudifyLogin_default = CrudifyLogin;
2918
-
2919
- // src/components/UserProfile/UserProfileDisplay.tsx
2920
- import {
2921
- Box as Box8,
2922
- Card,
2923
- CardContent,
2924
- Typography as Typography7,
2925
- Chip,
2926
- Avatar,
2927
- Divider,
2928
- CircularProgress as CircularProgress6,
2929
- Alert as Alert7,
2930
- List,
2931
- ListItem,
2932
- ListItemText,
2933
- ListItemIcon,
2934
- Collapse,
2935
- IconButton
2936
- } from "@mui/material";
2937
- import {
2938
- Person,
2939
- Email,
2940
- Badge,
2941
- Security,
2942
- Schedule,
2943
- AccountCircle,
2944
- ExpandMore,
2945
- ExpandLess,
2946
- Info
2947
- } from "@mui/icons-material";
2948
-
2949
- // src/hooks/useUserProfile.ts
2950
- import { useState as useState8, useEffect as useEffect9, useCallback as useCallback3, useRef as useRef2 } from "react";
2951
- import crudify3 from "@nocios/crudify-browser";
2952
- var useUserProfile = (options = {}) => {
2953
- const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
2954
- const [userProfile, setUserProfile] = useState8(null);
2955
- const [loading, setLoading] = useState8(false);
2956
- const [error, setError] = useState8(null);
2957
- const [extendedData, setExtendedData] = useState8({});
2958
- const abortControllerRef = useRef2(null);
2959
- const mountedRef = useRef2(true);
2960
- const requestIdRef = useRef2(0);
2961
- const retryCountRef = useRef2(0);
2962
- const clearProfile = useCallback3(() => {
2963
- setUserProfile(null);
2964
- setError(null);
2965
- setLoading(false);
2966
- setExtendedData({});
2967
- }, []);
2968
- const refreshProfile = useCallback3(async () => {
2969
- const userEmail = getCurrentUserEmail();
2970
- if (!userEmail) {
2971
- if (mountedRef.current) {
2972
- setError("No user email available");
2973
- setLoading(false);
2974
- }
2975
- return;
2976
- }
2977
- if (abortControllerRef.current) {
2978
- abortControllerRef.current.abort();
2979
- }
2980
- const abortController = new AbortController();
2981
- abortControllerRef.current = abortController;
2982
- const currentRequestId = ++requestIdRef.current;
2983
- try {
2984
- if (mountedRef.current) {
2985
- setLoading(true);
2986
- setError(null);
2987
- }
2988
- const response = await crudify3.readItems("users", {
2989
- filter: { email: userEmail },
2990
- pagination: { limit: 1 }
2991
- });
2992
- if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
2993
- if (response.success && response.data && response.data.length > 0) {
2994
- const userData = response.data[0];
2995
- setUserProfile(userData);
2996
- const additionalData = {
2997
- fullProfile: userData,
2998
- totalFields: Object.keys(userData).length,
2999
- displayData: {
3000
- id: userData.id,
3001
- email: userData.email,
3002
- username: userData.username,
3003
- firstName: userData.firstName,
3004
- lastName: userData.lastName,
3005
- fullName: userData.fullName || `${userData.firstName || ""} ${userData.lastName || ""}`.trim(),
3006
- role: userData.role,
3007
- permissions: userData.permissions || [],
3008
- isActive: userData.isActive,
3009
- lastLogin: userData.lastLogin,
3010
- createdAt: userData.createdAt,
3011
- updatedAt: userData.updatedAt,
3012
- // Include any custom fields
3013
- ...Object.keys(userData).filter(
3014
- (key) => ![
3015
- "id",
3016
- "email",
3017
- "username",
3018
- "firstName",
3019
- "lastName",
3020
- "fullName",
3021
- "role",
3022
- "permissions",
3023
- "isActive",
3024
- "lastLogin",
3025
- "createdAt",
3026
- "updatedAt"
3027
- ].includes(key)
3028
- ).reduce((acc, key) => ({ ...acc, [key]: userData[key] }), {})
3029
- }
3030
- };
3031
- setExtendedData(additionalData);
3032
- setError(null);
3033
- retryCountRef.current = 0;
3034
- } else {
3035
- setError("User profile not found");
3036
- setUserProfile(null);
3037
- setExtendedData({});
3038
- }
3039
- }
3040
- } catch (err) {
3041
- if (currentRequestId === requestIdRef.current && mountedRef.current) {
3042
- const error2 = err;
3043
- if (error2.name === "AbortError") {
3044
- return;
3045
- }
3046
- const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
3047
- if (shouldRetry) {
3048
- retryCountRef.current++;
3049
- setTimeout(() => {
3050
- if (mountedRef.current) {
3051
- refreshProfile();
3052
- }
3053
- }, 1e3 * retryCountRef.current);
3054
- } else {
3055
- setError("Failed to load user profile");
3056
- setUserProfile(null);
3057
- setExtendedData({});
3058
- }
3059
- }
3060
- } finally {
3061
- if (currentRequestId === requestIdRef.current && mountedRef.current) {
3062
- setLoading(false);
3063
- }
3064
- if (abortControllerRef.current === abortController) {
3065
- abortControllerRef.current = null;
3066
- }
3067
- }
3068
- }, [retryOnError, maxRetries]);
3069
- useEffect9(() => {
3070
- if (autoFetch) {
3071
- refreshProfile();
3072
- }
3073
- }, [autoFetch, refreshProfile]);
3074
- useEffect9(() => {
3075
- mountedRef.current = true;
3076
- return () => {
3077
- mountedRef.current = false;
3078
- if (abortControllerRef.current) {
3079
- abortControllerRef.current.abort();
3080
- abortControllerRef.current = null;
3081
- }
3082
- };
3083
- }, []);
3084
- return {
3085
- userProfile,
3086
- loading,
3087
- error,
3088
- extendedData,
3089
- refreshProfile,
3090
- clearProfile
3091
- };
3092
- };
3093
-
3094
- // src/components/UserProfile/UserProfileDisplay.tsx
3095
- import { useState as useState9 } from "react";
3096
- import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
3097
- var UserProfileDisplay = ({
3098
- showExtendedData = true,
3099
- showProfileCard = true,
3100
- autoRefresh = true
3101
- }) => {
3102
- const { userProfile, loading, error, extendedData, refreshProfile } = useUserProfile({
3103
- autoFetch: autoRefresh,
3104
- retryOnError: true,
3105
- maxRetries: 3
3106
- });
3107
- const [showAllFields, setShowAllFields] = useState9(false);
3108
- if (loading) {
3109
- return /* @__PURE__ */ jsxs9(Box8, { display: "flex", justifyContent: "center", alignItems: "center", p: 3, children: [
3110
- /* @__PURE__ */ jsx12(CircularProgress6, {}),
3111
- /* @__PURE__ */ jsx12(Typography7, { variant: "body2", sx: { ml: 2 }, children: "Cargando perfil de usuario..." })
3112
- ] });
3113
- }
3114
- if (error) {
3115
- return /* @__PURE__ */ jsxs9(
3116
- Alert7,
3117
- {
3118
- severity: "error",
3119
- action: /* @__PURE__ */ jsx12(IconButton, { color: "inherit", size: "small", onClick: refreshProfile, children: /* @__PURE__ */ jsx12(Typography7, { variant: "caption", children: "Reintentar" }) }),
3120
- children: [
3121
- "Error al cargar el perfil: ",
3122
- error
3123
- ]
3124
- }
3125
- );
3126
- }
3127
- if (!userProfile) {
3128
- return /* @__PURE__ */ jsx12(Alert7, { severity: "warning", children: "No se encontr\xF3 informaci\xF3n del usuario" });
3129
- }
3130
- const displayData = extendedData?.displayData || {};
3131
- const totalFields = extendedData?.totalFields || 0;
3132
- const formatDate = (dateString) => {
3133
- if (!dateString) return "No disponible";
3134
- try {
3135
- return new Date(dateString).toLocaleString("es-ES", {
3136
- year: "numeric",
3137
- month: "short",
3138
- day: "numeric",
3139
- hour: "2-digit",
3140
- minute: "2-digit"
3141
- });
3142
- } catch {
3143
- return dateString;
3144
- }
3145
- };
3146
- const renderFieldValue = (key, value) => {
3147
- if (value === null || value === void 0) return "No disponible";
3148
- if (typeof value === "boolean") return value ? "S\xED" : "No";
3149
- if (Array.isArray(value)) return value.length > 0 ? value.join(", ") : "Ninguno";
3150
- if (typeof value === "object") return JSON.stringify(value, null, 2);
3151
- return String(value);
3152
- };
3153
- const basicFields = [
3154
- { key: "id", label: "ID", icon: /* @__PURE__ */ jsx12(Badge, {}) },
3155
- { key: "email", label: "Email", icon: /* @__PURE__ */ jsx12(Email, {}) },
3156
- { key: "username", label: "Usuario", icon: /* @__PURE__ */ jsx12(Person, {}) },
3157
- { key: "fullName", label: "Nombre completo", icon: /* @__PURE__ */ jsx12(AccountCircle, {}) },
3158
- { key: "role", label: "Rol", icon: /* @__PURE__ */ jsx12(Security, {}) }
3159
- ];
3160
- const extendedFields = [
3161
- { key: "firstName", label: "Nombre" },
3162
- { key: "lastName", label: "Apellido" },
3163
- { key: "isActive", label: "Activo" },
3164
- { key: "lastLogin", label: "\xDAltimo login" },
3165
- { key: "createdAt", label: "Creado" },
3166
- { key: "updatedAt", label: "Actualizado" }
3167
- ];
3168
- const knownFields = [...basicFields.map((f) => f.key), ...extendedFields.map((f) => f.key), "permissions"];
3169
- const customFields = Object.keys(displayData).filter((key) => !knownFields.includes(key)).map((key) => ({ key, label: key }));
3170
- return /* @__PURE__ */ jsxs9(Box8, { children: [
3171
- showProfileCard && /* @__PURE__ */ jsx12(Card, { sx: { mb: 2 }, children: /* @__PURE__ */ jsxs9(CardContent, { children: [
3172
- /* @__PURE__ */ jsxs9(Box8, { display: "flex", alignItems: "center", mb: 2, children: [
3173
- /* @__PURE__ */ jsx12(
3174
- Avatar,
3175
- {
3176
- src: displayData.avatar,
3177
- sx: { width: 56, height: 56, mr: 2 },
3178
- children: displayData.fullName?.[0] || displayData.username?.[0] || displayData.email?.[0]
3179
- }
3180
- ),
3181
- /* @__PURE__ */ jsxs9(Box8, { children: [
3182
- /* @__PURE__ */ jsx12(Typography7, { variant: "h6", children: displayData.fullName || displayData.username || displayData.email }),
3183
- /* @__PURE__ */ jsx12(Typography7, { variant: "body2", color: "text.secondary", children: displayData.role || "Usuario" }),
3184
- displayData.isActive !== void 0 && /* @__PURE__ */ jsx12(
3185
- Chip,
3186
- {
3187
- label: displayData.isActive ? "Activo" : "Inactivo",
3188
- color: displayData.isActive ? "success" : "error",
3189
- size: "small",
3190
- sx: { mt: 0.5 }
3191
- }
3192
- )
3193
- ] })
3194
- ] }),
3195
- /* @__PURE__ */ jsx12(Box8, { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: 2, children: basicFields.map(
3196
- ({ key, label, icon }) => displayData[key] ? /* @__PURE__ */ jsxs9(Box8, { display: "flex", alignItems: "center", children: [
3197
- /* @__PURE__ */ jsx12(Box8, { sx: { mr: 1, color: "text.secondary" }, children: icon }),
3198
- /* @__PURE__ */ jsxs9(Box8, { children: [
3199
- /* @__PURE__ */ jsx12(Typography7, { variant: "caption", color: "text.secondary", children: label }),
3200
- /* @__PURE__ */ jsx12(Typography7, { variant: "body2", children: renderFieldValue(key, displayData[key]) })
3201
- ] })
3202
- ] }, key) : null
3203
- ) }),
3204
- displayData.permissions && Array.isArray(displayData.permissions) && displayData.permissions.length > 0 && /* @__PURE__ */ jsxs9(Box8, { mt: 2, children: [
3205
- /* @__PURE__ */ jsx12(Typography7, { variant: "caption", color: "text.secondary", display: "block", children: "Permisos" }),
3206
- /* @__PURE__ */ jsxs9(Box8, { display: "flex", flexWrap: "wrap", gap: 0.5, mt: 0.5, children: [
3207
- displayData.permissions.slice(0, 5).map((permission, index) => /* @__PURE__ */ jsx12(Chip, { label: permission, size: "small", variant: "outlined" }, index)),
3208
- displayData.permissions.length > 5 && /* @__PURE__ */ jsx12(Chip, { label: `+${displayData.permissions.length - 5} m\xE1s`, size: "small" })
3209
- ] })
3210
- ] })
3211
- ] }) }),
3212
- showExtendedData && /* @__PURE__ */ jsx12(Card, { children: /* @__PURE__ */ jsxs9(CardContent, { children: [
3213
- /* @__PURE__ */ jsxs9(Box8, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, children: [
3214
- /* @__PURE__ */ jsxs9(Typography7, { variant: "h6", display: "flex", alignItems: "center", children: [
3215
- /* @__PURE__ */ jsx12(Info, { sx: { mr: 1 } }),
3216
- "Informaci\xF3n Detallada"
3217
- ] }),
3218
- /* @__PURE__ */ jsx12(Chip, { label: `${totalFields} campos totales`, size: "small" })
3219
- ] }),
3220
- /* @__PURE__ */ jsxs9(List, { dense: true, children: [
3221
- extendedFields.map(({ key, label }) => displayData[key] !== void 0 && /* @__PURE__ */ jsxs9(ListItem, { divider: true, children: [
3222
- /* @__PURE__ */ jsx12(ListItemIcon, { children: /* @__PURE__ */ jsx12(Schedule, { fontSize: "small" }) }),
3223
- /* @__PURE__ */ jsx12(
3224
- ListItemText,
3225
- {
3226
- primary: label,
3227
- secondary: key.includes("At") || key.includes("Login") ? formatDate(displayData[key]) : renderFieldValue(key, displayData[key])
3228
- }
3229
- )
3230
- ] }, key)),
3231
- customFields.length > 0 && /* @__PURE__ */ jsxs9(Fragment7, { children: [
3232
- /* @__PURE__ */ jsx12(Divider, { sx: { my: 1 } }),
3233
- /* @__PURE__ */ jsx12(ListItem, { children: /* @__PURE__ */ jsx12(
3234
- ListItemText,
3235
- {
3236
- primary: /* @__PURE__ */ jsxs9(Box8, { display: "flex", justifyContent: "space-between", alignItems: "center", children: [
3237
- /* @__PURE__ */ jsxs9(Typography7, { variant: "subtitle2", children: [
3238
- "Campos Personalizados (",
3239
- customFields.length,
3240
- ")"
3241
- ] }),
3242
- /* @__PURE__ */ jsx12(IconButton, { size: "small", onClick: () => setShowAllFields(!showAllFields), children: showAllFields ? /* @__PURE__ */ jsx12(ExpandLess, {}) : /* @__PURE__ */ jsx12(ExpandMore, {}) })
3243
- ] })
3244
- }
3245
- ) }),
3246
- /* @__PURE__ */ jsx12(Collapse, { in: showAllFields, children: customFields.map(({ key, label }) => /* @__PURE__ */ jsx12(ListItem, { sx: { pl: 4 }, children: /* @__PURE__ */ jsx12(
3247
- ListItemText,
3248
- {
3249
- primary: label,
3250
- secondary: renderFieldValue(key, displayData[key])
3251
- }
3252
- ) }, key)) })
3253
- ] })
3254
- ] }),
3255
- /* @__PURE__ */ jsxs9(Box8, { mt: 2, display: "flex", justifyContent: "space-between", alignItems: "center", children: [
3256
- /* @__PURE__ */ jsxs9(Typography7, { variant: "caption", color: "text.secondary", children: [
3257
- "\xDAltima actualizaci\xF3n: ",
3258
- formatDate(displayData.updatedAt)
3259
- ] }),
3260
- /* @__PURE__ */ jsx12(IconButton, { size: "small", onClick: refreshProfile, disabled: loading, children: /* @__PURE__ */ jsx12(Typography7, { variant: "caption", children: "Actualizar" }) })
3261
- ] })
3262
- ] }) })
3263
- ] });
3264
- };
3265
- var UserProfileDisplay_default = UserProfileDisplay;
3266
-
3267
- // src/components/PublicPolicies/Policies.tsx
3268
- import { useRef as useRef4 } from "react";
3269
- import { useTranslation as useTranslation4 } from "react-i18next";
3270
- import {
3271
- Box as Box11,
3272
- Typography as Typography10,
3273
- Button as Button7,
3274
- Stack as Stack3,
3275
- Alert as Alert8,
3276
- Divider as Divider3
3277
- } from "@mui/material";
3278
- import { Add } from "@mui/icons-material";
3279
-
3280
- // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
3281
- import { forwardRef } from "react";
3282
- import { useTranslation as useTranslation3 } from "react-i18next";
3283
- import {
3284
- Box as Box10,
3285
- FormControl,
3286
- InputLabel,
3287
- Select,
3288
- MenuItem,
3289
- IconButton as IconButton2,
3290
- Typography as Typography9,
3291
- FormHelperText as FormHelperText2,
3292
- Stack as Stack2,
3293
- Paper,
3294
- Divider as Divider2,
3295
- Button as Button6
3296
- } from "@mui/material";
3297
- import { Delete, SelectAll as SelectAll2, ClearAll as ClearAll2 } from "@mui/icons-material";
3298
-
3299
- // src/components/PublicPolicies/FieldSelector/FieldSelector.tsx
3300
- import { useState as useState10, useEffect as useEffect10, useRef as useRef3 } from "react";
3301
- import { useTranslation as useTranslation2 } from "react-i18next";
3302
- import {
3303
- Box as Box9,
3304
- Typography as Typography8,
3305
- Button as Button5,
3306
- Stack,
3307
- FormHelperText,
3308
- ToggleButton,
3309
- ToggleButtonGroup
3310
- } from "@mui/material";
3311
- import {
3312
- CheckCircle,
3313
- Cancel,
3314
- SelectAll,
3315
- ClearAll
3316
- } from "@mui/icons-material";
3317
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
3318
- var FieldSelector = ({
3319
- value,
3320
- onChange,
3321
- availableFields,
3322
- error,
3323
- disabled = false
3324
- }) => {
3325
- const { t } = useTranslation2();
3326
- const [mode, setMode] = useState10("custom");
3327
- const isUpdatingRef = useRef3(false);
3328
- useEffect10(() => {
3329
- const current = value || { allow: [], owner_allow: [], deny: [] };
3330
- const all = new Set(availableFields);
3331
- const allow = (current.allow || []).filter((f) => all.has(f));
3332
- const owner = (current.owner_allow || []).filter((f) => all.has(f));
3333
- const deny = (current.deny || []).filter((f) => all.has(f));
3334
- availableFields.forEach((f) => {
3335
- if (!allow.includes(f) && !owner.includes(f) && !deny.includes(f)) deny.push(f);
3336
- });
3337
- const normalized = { allow, owner_allow: owner, deny };
3338
- if (JSON.stringify(normalized) !== JSON.stringify(current)) {
3339
- onChange(normalized);
3340
- }
3341
- if (allow.length === availableFields.length) setMode("all");
3342
- else if (deny.length === availableFields.length) setMode("none");
3343
- else setMode("custom");
3344
- }, [availableFields, value]);
3345
- const setAllAllow = () => {
3346
- isUpdatingRef.current = true;
3347
- onChange({ allow: [...availableFields], owner_allow: [], deny: [] });
3348
- setMode("all");
3349
- setTimeout(() => {
3350
- isUpdatingRef.current = false;
3351
- }, 0);
3352
- };
3353
- const setAllDeny = () => {
3354
- isUpdatingRef.current = true;
3355
- onChange({ allow: [], owner_allow: [], deny: [...availableFields] });
3356
- setMode("none");
3357
- setTimeout(() => {
3358
- isUpdatingRef.current = false;
3359
- }, 0);
3360
- };
3361
- const getFieldState = (fieldName) => {
3362
- if (value?.allow?.includes(fieldName)) return "allow";
3363
- if (value?.owner_allow?.includes(fieldName)) return "owner_allow";
3364
- return "deny";
3365
- };
3366
- const setFieldState = (fieldName, state) => {
3367
- isUpdatingRef.current = true;
3368
- const allow = new Set(value?.allow || []);
3369
- const owner = new Set(value?.owner_allow || []);
3370
- const deny = new Set(value?.deny || []);
3371
- allow.delete(fieldName);
3372
- owner.delete(fieldName);
3373
- deny.delete(fieldName);
3374
- if (state === "allow") allow.add(fieldName);
3375
- if (state === "owner_allow") owner.add(fieldName);
3376
- if (state === "deny") deny.add(fieldName);
3377
- onChange({ allow: Array.from(allow), owner_allow: Array.from(owner), deny: Array.from(deny) });
3378
- setMode("custom");
3379
- setTimeout(() => {
3380
- isUpdatingRef.current = false;
3381
- }, 0);
3382
- };
3383
- if (availableFields.length === 0) {
3384
- return /* @__PURE__ */ jsxs10(Box9, { children: [
3385
- /* @__PURE__ */ jsx13(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
3386
- /* @__PURE__ */ jsx13(Typography8, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: t("modules.form.publicPolicies.fields.conditions.noFieldsAvailable") }),
3387
- error && /* @__PURE__ */ jsx13(FormHelperText, { error: true, sx: { mt: 1 }, children: error })
3388
- ] });
3389
- }
3390
- return /* @__PURE__ */ jsxs10(Box9, { children: [
3391
- /* @__PURE__ */ jsx13(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
3392
- /* @__PURE__ */ jsxs10(Stack, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
3393
- /* @__PURE__ */ jsx13(
3394
- Button5,
3395
- {
3396
- variant: mode === "all" ? "contained" : "outlined",
3397
- startIcon: /* @__PURE__ */ jsx13(SelectAll, {}),
3398
- onClick: setAllAllow,
3399
- disabled,
3400
- size: "small",
3401
- sx: {
3402
- minWidth: 120,
3403
- ...mode === "all" && {
3404
- backgroundColor: "#16a34a",
3405
- "&:hover": { backgroundColor: "#15803d" }
3406
- }
3407
- },
3408
- children: t("modules.form.publicPolicies.fields.conditions.allFields")
3409
- }
3410
- ),
3411
- /* @__PURE__ */ jsx13(
3412
- Button5,
3413
- {
3414
- variant: mode === "none" ? "contained" : "outlined",
3415
- startIcon: /* @__PURE__ */ jsx13(ClearAll, {}),
3416
- onClick: setAllDeny,
3417
- disabled,
3418
- size: "small",
3419
- sx: {
3420
- minWidth: 120,
3421
- ...mode === "none" && {
3422
- backgroundColor: "#cf222e",
3423
- "&:hover": { backgroundColor: "#bc1f2c" }
3424
- }
3425
- },
3426
- children: t("modules.form.publicPolicies.fields.conditions.noFields")
3427
- }
3428
- )
3429
- ] }),
3430
- /* @__PURE__ */ jsxs10(Box9, { sx: { p: 2, border: "1px solid #d1d9e0", borderRadius: 1, backgroundColor: "#f6f8fa" }, children: [
3431
- /* @__PURE__ */ jsx13(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.help") }),
3432
- /* @__PURE__ */ jsx13(Stack, { spacing: 1, children: availableFields.map((fieldName) => {
3433
- const fieldState = getFieldState(fieldName);
3434
- return /* @__PURE__ */ jsxs10(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [
3435
- /* @__PURE__ */ jsx13(Typography8, { variant: "body2", sx: { minWidth: 100, fontFamily: "monospace" }, children: fieldName }),
3436
- /* @__PURE__ */ jsxs10(ToggleButtonGroup, { value: fieldState, exclusive: true, size: "small", children: [
3437
- /* @__PURE__ */ jsxs10(
3438
- ToggleButton,
3439
- {
3440
- value: "allow",
3441
- onClick: () => setFieldState(fieldName, "allow"),
3442
- disabled,
3443
- sx: {
3444
- px: 2,
3445
- color: fieldState === "allow" ? "#ffffff" : "#6b7280",
3446
- backgroundColor: fieldState === "allow" ? "#16a34a" : "#f3f4f6",
3447
- borderColor: fieldState === "allow" ? "#16a34a" : "#d1d5db",
3448
- "&:hover": {
3449
- backgroundColor: fieldState === "allow" ? "#15803d" : "#e5e7eb",
3450
- borderColor: fieldState === "allow" ? "#15803d" : "#9ca3af"
3451
- },
3452
- "&.Mui-selected": {
3453
- backgroundColor: "#16a34a",
3454
- color: "#ffffff",
3455
- "&:hover": {
3456
- backgroundColor: "#15803d"
3457
- }
3458
- }
3459
- },
3460
- children: [
3461
- /* @__PURE__ */ jsx13(CheckCircle, { sx: { fontSize: 16, mr: 0.5 } }),
3462
- t("modules.form.publicPolicies.fields.conditions.states.allow")
3463
- ]
3464
- }
3465
- ),
3466
- /* @__PURE__ */ jsx13(
3467
- ToggleButton,
3468
- {
3469
- value: "owner_allow",
3470
- onClick: () => setFieldState(fieldName, "owner_allow"),
3471
- disabled,
3472
- sx: {
3473
- px: 2,
3474
- color: fieldState === "owner_allow" ? "#ffffff" : "#6b7280",
3475
- backgroundColor: fieldState === "owner_allow" ? "#0ea5e9" : "#f3f4f6",
3476
- borderColor: fieldState === "owner_allow" ? "#0ea5e9" : "#d1d5db",
3477
- "&:hover": {
3478
- backgroundColor: fieldState === "owner_allow" ? "#0284c7" : "#e5e7eb",
3479
- borderColor: fieldState === "owner_allow" ? "#0284c7" : "#9ca3af"
3480
- },
3481
- "&.Mui-selected": {
3482
- backgroundColor: "#0ea5e9",
3483
- color: "#ffffff",
3484
- "&:hover": {
3485
- backgroundColor: "#0284c7"
3486
- }
3487
- }
3488
- },
3489
- children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
3490
- }
3491
- ),
3492
- /* @__PURE__ */ jsxs10(
3493
- ToggleButton,
3494
- {
3495
- value: "deny",
3496
- onClick: () => setFieldState(fieldName, "deny"),
3497
- disabled,
3498
- sx: {
3499
- px: 2,
3500
- color: fieldState === "deny" ? "#ffffff" : "#6b7280",
3501
- backgroundColor: fieldState === "deny" ? "#dc2626" : "#f3f4f6",
3502
- borderColor: fieldState === "deny" ? "#dc2626" : "#d1d5db",
3503
- "&:hover": {
3504
- backgroundColor: fieldState === "deny" ? "#b91c1c" : "#e5e7eb",
3505
- borderColor: fieldState === "deny" ? "#b91c1c" : "#9ca3af"
3506
- },
3507
- "&.Mui-selected": {
3508
- backgroundColor: "#dc2626",
3509
- color: "#ffffff",
3510
- "&:hover": {
3511
- backgroundColor: "#b91c1c"
3512
- }
3513
- }
3514
- },
3515
- children: [
3516
- /* @__PURE__ */ jsx13(Cancel, { sx: { fontSize: 16, mr: 0.5 } }),
3517
- t("modules.form.publicPolicies.fields.conditions.states.deny")
3518
- ]
3519
- }
3520
- )
3521
- ] })
3522
- ] }, fieldName);
3523
- }) })
3524
- ] }),
3525
- error && /* @__PURE__ */ jsx13(FormHelperText, { error: true, sx: { mt: 1 }, children: error })
3526
- ] });
3527
- };
3528
- var FieldSelector_default = FieldSelector;
3529
-
3530
- // src/components/PublicPolicies/constants.ts
3531
- var POLICY_ACTIONS = ["create", "read", "update", "delete"];
3532
- var PREFERRED_POLICY_ORDER = [
3533
- "create",
3534
- "read",
3535
- "update",
3536
- "delete"
3537
- ];
3538
-
3539
- // src/components/PublicPolicies/PolicyItem/PolicyItem.tsx
3540
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
3541
- var PolicyItem = forwardRef(({
3542
- policy,
3543
- onChange,
3544
- onRemove,
3545
- availableFields,
3546
- isSubmitting = false,
3547
- usedActions,
3548
- error
3549
- }, ref) => {
3550
- const { t } = useTranslation3();
3551
- const takenActions = new Set(Array.from(usedActions || []));
3552
- takenActions.delete(policy.action);
3553
- const actionOptions = POLICY_ACTIONS.map((a) => ({
3554
- value: a,
3555
- label: t(`modules.form.publicPolicies.fields.action.options.${a}`)
3556
- }));
3557
- return /* @__PURE__ */ jsxs11(
3558
- Paper,
3559
- {
3560
- ref,
3561
- sx: {
3562
- p: 3,
3563
- border: "1px solid #d1d9e0",
3564
- borderRadius: 2,
3565
- position: "relative",
3566
- backgroundColor: "#ffffff"
3567
- },
3568
- children: [
3569
- /* @__PURE__ */ jsxs11(Box10, { sx: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 3 }, children: [
3570
- /* @__PURE__ */ jsx14(
3571
- Typography9,
3572
- {
3573
- variant: "subtitle1",
3574
- sx: {
3575
- fontWeight: 600,
3576
- color: "#111418",
3577
- fontSize: "1rem"
3578
- },
3579
- children: t("modules.form.publicPolicies.policyTitle")
3580
- }
3581
- ),
3582
- /* @__PURE__ */ jsx14(
3583
- IconButton2,
3584
- {
3585
- onClick: onRemove,
3586
- size: "small",
3587
- disabled: isSubmitting,
3588
- "aria-label": t("modules.form.publicPolicies.removePolicy"),
3589
- sx: {
3590
- color: "#656d76",
3591
- "&:hover": {
3592
- color: "#cf222e",
3593
- backgroundColor: "rgba(207, 34, 46, 0.1)"
3594
- }
3595
- },
3596
- children: /* @__PURE__ */ jsx14(Delete, {})
3597
- }
3598
- )
3599
- ] }),
3600
- /* @__PURE__ */ jsxs11(Stack2, { spacing: 3, children: [
3601
- /* @__PURE__ */ jsx14(Stack2, { direction: { xs: "column", md: "row" }, spacing: 2, children: /* @__PURE__ */ jsx14(Box10, { sx: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxs11(FormControl, { fullWidth: true, children: [
3602
- /* @__PURE__ */ jsx14(InputLabel, { children: t("modules.form.publicPolicies.fields.action.label") }),
3603
- /* @__PURE__ */ jsx14(
3604
- Select,
3605
- {
3606
- value: policy.action,
3607
- label: t("modules.form.publicPolicies.fields.action.label"),
3608
- disabled: isSubmitting,
3609
- onChange: (e) => {
3610
- const newAction = e.target.value;
3611
- const next = { ...policy, action: newAction };
3612
- if (newAction === "delete") {
3613
- next.permission = "deny";
3614
- delete next.fields;
3615
- } else {
3616
- next.fields = { allow: [], owner_allow: [], deny: availableFields };
3617
- delete next.permission;
3618
- }
3619
- onChange(next);
3620
- },
3621
- sx: {
3622
- backgroundColor: "#ffffff",
3623
- "&:hover .MuiOutlinedInput-notchedOutline": {
3624
- borderColor: "#8c959f"
3625
- },
3626
- "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
3627
- borderColor: "#0969da",
3628
- borderWidth: 2
3629
- }
3630
- },
3631
- children: actionOptions.map((option) => {
3632
- const disabledOption = takenActions.has(option.value);
3633
- return /* @__PURE__ */ jsx14(MenuItem, { value: option.value, disabled: disabledOption, children: option.label }, option.value);
3634
- })
3635
- }
3636
- ),
3637
- error && /* @__PURE__ */ jsx14(FormHelperText2, { error: true, children: error })
3638
- ] }) }) }),
3639
- policy.action === "delete" ? /* @__PURE__ */ jsxs11(Box10, { children: [
3640
- /* @__PURE__ */ jsx14(Typography9, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: t("modules.form.publicPolicies.fields.conditions.label") }),
3641
- /* @__PURE__ */ jsxs11(Stack2, { direction: "row", spacing: 1, sx: { mb: 3 }, children: [
3642
- /* @__PURE__ */ jsx14(
3643
- Button6,
3644
- {
3645
- variant: policy.permission === "*" ? "contained" : "outlined",
3646
- startIcon: /* @__PURE__ */ jsx14(SelectAll2, {}),
3647
- onClick: () => onChange({ ...policy, permission: "*" }),
3648
- disabled: isSubmitting,
3649
- size: "small",
3650
- sx: {
3651
- minWidth: 140,
3652
- whiteSpace: "nowrap",
3653
- ...policy.permission === "*" && {
3654
- backgroundColor: "#16a34a",
3655
- "&:hover": { backgroundColor: "#15803d" }
3656
- }
3657
- },
3658
- children: t("modules.form.publicPolicies.fields.conditions.allFields")
3659
- }
3660
- ),
3661
- /* @__PURE__ */ jsx14(
3662
- Button6,
3663
- {
3664
- variant: policy.permission === "owner" ? "contained" : "outlined",
3665
- onClick: () => onChange({ ...policy, permission: "owner" }),
3666
- disabled: isSubmitting,
3667
- size: "small",
3668
- sx: {
3669
- minWidth: 140,
3670
- whiteSpace: "nowrap",
3671
- ...policy.permission === "owner" && {
3672
- backgroundColor: "#0ea5e9",
3673
- "&:hover": { backgroundColor: "#0284c7" }
3674
- }
3675
- },
3676
- children: t("modules.form.publicPolicies.fields.conditions.states.ownerAllow")
3677
- }
3678
- ),
3679
- /* @__PURE__ */ jsx14(
3680
- Button6,
3681
- {
3682
- variant: policy.permission === "deny" ? "contained" : "outlined",
3683
- startIcon: /* @__PURE__ */ jsx14(ClearAll2, {}),
3684
- onClick: () => onChange({ ...policy, permission: "deny" }),
3685
- disabled: isSubmitting,
3686
- size: "small",
3687
- sx: {
3688
- minWidth: 140,
3689
- whiteSpace: "nowrap",
3690
- ...policy.permission === "deny" && {
3691
- backgroundColor: "#cf222e",
3692
- "&:hover": { backgroundColor: "#bc1f2c" }
3693
- }
3694
- },
3695
- children: t("modules.form.publicPolicies.fields.conditions.noFields")
3696
- }
3697
- )
3698
- ] })
3699
- ] }) : /* @__PURE__ */ jsx14(
3700
- FieldSelector_default,
3701
- {
3702
- value: policy.fields || { allow: [], owner_allow: [], deny: [] },
3703
- onChange: (nextFields) => onChange({ ...policy, fields: nextFields }),
3704
- availableFields,
3705
- disabled: isSubmitting
3706
- }
3707
- ),
3708
- /* @__PURE__ */ jsx14(Paper, { variant: "outlined", sx: { p: 2, backgroundColor: "#f9fafb" }, children: policy.action === "delete" ? /* @__PURE__ */ jsxs11(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
3709
- /* @__PURE__ */ jsxs11(Box10, { component: "span", sx: {
3710
- color: policy.permission === "*" ? "#16a34a" : policy.permission === "owner" ? "#0ea5e9" : "#dc2626"
3711
- }, children: [
3712
- t("modules.form.publicPolicies.fields.conditions.states.allow"),
3713
- ":"
3714
- ] }),
3715
- " ",
3716
- policy.permission || "-"
3717
- ] }) : /* @__PURE__ */ jsxs11(Stack2, { spacing: 0.5, divider: /* @__PURE__ */ jsx14(Divider2, { sx: { borderColor: "#e5e7eb" } }), children: [
3718
- /* @__PURE__ */ jsxs11(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
3719
- /* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#16a34a" }, children: [
3720
- t("modules.form.publicPolicies.fields.conditions.states.allow"),
3721
- ":"
3722
- ] }),
3723
- " ",
3724
- (policy?.fields?.allow || []).join(", ") || "-"
3725
- ] }),
3726
- /* @__PURE__ */ jsxs11(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
3727
- /* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#0ea5e9" }, children: [
3728
- t("modules.form.publicPolicies.fields.conditions.states.ownerAllow"),
3729
- ":"
3730
- ] }),
3731
- " ",
3732
- (policy?.fields?.owner_allow || []).join(", ") || "-"
3733
- ] }),
3734
- /* @__PURE__ */ jsxs11(Typography9, { variant: "body2", sx: { fontFamily: "monospace", color: "text.secondary" }, children: [
3735
- /* @__PURE__ */ jsxs11(Box10, { component: "span", sx: { color: "#dc2626" }, children: [
3736
- t("modules.form.publicPolicies.fields.conditions.states.deny"),
3737
- ":"
3738
- ] }),
3739
- " ",
3740
- (policy?.fields?.deny || []).join(", ") || "-"
3741
- ] })
3742
- ] }) })
3743
- ] })
3744
- ]
3745
- }
3746
- );
3747
- });
3748
- var PolicyItem_default = PolicyItem;
3749
-
3750
- // src/components/PublicPolicies/Policies.tsx
3751
- import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
3752
- var generateId = () => {
3753
- const c = globalThis?.crypto;
3754
- if (c && typeof c.randomUUID === "function") return c.randomUUID();
3755
- return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
3756
- };
3757
- var Policies = ({
3758
- policies,
3759
- onChange,
3760
- availableFields,
3761
- errors,
3762
- isSubmitting = false
3763
- }) => {
3764
- const { t } = useTranslation4();
3765
- const policyRefs = useRef4({});
3766
- const takenActions = new Set((policies || []).map((p) => p.action).filter(Boolean));
3767
- const remainingActions = PREFERRED_POLICY_ORDER.filter((a) => !takenActions.has(a));
3768
- const canAddPolicy = remainingActions.length > 0;
3769
- const addPolicy = () => {
3770
- const defaultAction = remainingActions[0] || "create";
3771
- const newPolicy = {
3772
- id: generateId(),
3773
- action: defaultAction
3774
- };
3775
- if (defaultAction === "delete") {
3776
- newPolicy.permission = "deny";
3777
- } else {
3778
- newPolicy.fields = {
3779
- allow: [],
3780
- owner_allow: [],
3781
- deny: availableFields
3782
- };
3783
- }
3784
- const next = [...policies || [], newPolicy];
3785
- onChange(next);
3786
- setTimeout(() => {
3787
- const newIndex = next.length - 1;
3788
- const el = policyRefs.current[newIndex];
3789
- if (el) {
3790
- el.scrollIntoView({ behavior: "smooth", block: "center" });
3791
- }
3792
- }, 100);
3793
- };
3794
- const removePolicy = (index) => {
3795
- const next = [...policies];
3796
- next.splice(index, 1);
3797
- onChange(next);
3798
- };
3799
- const arrayError = (() => {
3800
- if (!errors) return null;
3801
- if (typeof errors === "string") return errors;
3802
- const msg = errors._error;
3803
- return typeof msg === "string" ? msg : null;
3804
- })();
3805
- const usedActions = new Set((policies || []).map((p) => p.action));
3806
- return /* @__PURE__ */ jsxs12(Fragment8, { children: [
3807
- /* @__PURE__ */ jsx15(Divider3, { sx: { borderColor: "#e0e4e7" } }),
3808
- /* @__PURE__ */ jsxs12(Box11, { children: [
3809
- /* @__PURE__ */ jsx15(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3, children: /* @__PURE__ */ jsxs12(Box11, { children: [
3810
- /* @__PURE__ */ jsx15(
3811
- Typography10,
3812
- {
3813
- variant: "h6",
3814
- sx: {
3815
- fontWeight: 600,
3816
- color: "#111418",
3817
- mb: 1
3818
- },
3819
- children: t("modules.form.publicPolicies.title")
3820
- }
3821
- ),
3822
- /* @__PURE__ */ jsx15(
3823
- Typography10,
3824
- {
3825
- variant: "body2",
3826
- color: "text.secondary",
3827
- sx: { fontSize: "0.875rem" },
3828
- children: t("modules.form.publicPolicies.description")
3829
- }
3830
- )
3831
- ] }) }),
3832
- arrayError && /* @__PURE__ */ jsx15(Alert8, { severity: "error", sx: { mb: 3 }, children: arrayError }),
3833
- /* @__PURE__ */ jsxs12(Stack3, { spacing: 3, children: [
3834
- (policies || []).length === 0 ? /* @__PURE__ */ jsx15(Alert8, { severity: "info", children: t("modules.form.publicPolicies.noPolicies") }) : policies.map((policy, index) => /* @__PURE__ */ jsx15(
3835
- PolicyItem_default,
3836
- {
3837
- ref: (el) => {
3838
- policyRefs.current[index] = el;
3839
- },
3840
- policy,
3841
- onChange: (nextPolicy) => {
3842
- const next = [...policies];
3843
- next[index] = nextPolicy;
3844
- onChange(next);
3845
- },
3846
- onRemove: () => removePolicy(index),
3847
- availableFields,
3848
- isSubmitting,
3849
- usedActions,
3850
- error: typeof errors === "object" && errors && policy.id in errors ? errors[policy.id] : void 0
3851
- },
3852
- policy.id
3853
- )),
3854
- canAddPolicy && /* @__PURE__ */ jsx15(Box11, { children: /* @__PURE__ */ jsx15(
3855
- Button7,
3856
- {
3857
- type: "button",
3858
- variant: "outlined",
3859
- startIcon: /* @__PURE__ */ jsx15(Add, {}),
3860
- onClick: addPolicy,
3861
- disabled: isSubmitting,
3862
- sx: {
3863
- borderColor: "#d0d7de",
3864
- color: "#656d76",
3865
- "&:hover": {
3866
- borderColor: "#8c959f",
3867
- backgroundColor: "transparent"
3868
- }
3869
- },
3870
- children: t("modules.form.publicPolicies.addPolicy")
3871
- }
3872
- ) })
3873
- ] })
3874
- ] })
3875
- ] });
3876
- };
3877
- var Policies_default = Policies;
3878
-
3879
- // src/components/LoginComponent.tsx
3880
- import { useState as useState11 } from "react";
3881
- import { Button as Button8, TextField as TextField5, Box as Box12, Alert as Alert9, Typography as Typography11, CircularProgress as CircularProgress7 } from "@mui/material";
3882
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
3883
- function LoginComponent() {
3884
- const [email, setEmail] = useState11("");
3885
- const [password, setPassword] = useState11("");
3886
- const [showForm, setShowForm] = useState11(false);
3887
- const {
3888
- isAuthenticated,
3889
- isLoading,
3890
- error,
3891
- login,
3892
- logout,
3893
- refreshTokens,
3894
- clearError,
3895
- isExpiringSoon,
3896
- expiresIn
3897
- } = useSessionContext();
3898
- const handleLogin = async (e) => {
3899
- e.preventDefault();
3900
- if (!email || !password) {
3901
- return;
3902
- }
3903
- const result = await login(email, password);
3904
- if (result.success) {
3905
- setEmail("");
3906
- setPassword("");
3907
- setShowForm(false);
3908
- }
3909
- };
3910
- const handleLogout = async () => {
3911
- await logout();
3912
- };
3913
- const handleRefreshTokens = async () => {
3914
- await refreshTokens();
3915
- };
3916
- if (isAuthenticated) {
3917
- return /* @__PURE__ */ jsxs13(Box12, { sx: { maxWidth: 600, mx: "auto", p: 3 }, children: [
3918
- /* @__PURE__ */ jsx16(Typography11, { variant: "h4", gutterBottom: true, children: "Welcome! \u{1F389}" }),
3919
- /* @__PURE__ */ jsx16(Alert9, { severity: "success", sx: { mb: 3 }, children: "You are successfully logged in with Refresh Token Pattern enabled" }),
3920
- /* @__PURE__ */ jsxs13(Box12, { sx: { mb: 3, p: 2, bgcolor: "background.paper", border: 1, borderColor: "divider", borderRadius: 1 }, children: [
3921
- /* @__PURE__ */ jsx16(Typography11, { variant: "h6", gutterBottom: true, children: "Token Status" }),
3922
- /* @__PURE__ */ jsxs13(Typography11, { variant: "body2", color: "text.secondary", children: [
3923
- "Access Token expires in: ",
3924
- Math.round(expiresIn / 1e3 / 60),
3925
- " minutes"
3926
- ] }),
3927
- isExpiringSoon && /* @__PURE__ */ jsx16(Alert9, { severity: "warning", sx: { mt: 1 }, children: "Token expires soon - automatic refresh will happen" })
3928
- ] }),
3929
- /* @__PURE__ */ jsxs13(Box12, { sx: { display: "flex", gap: 2, flexWrap: "wrap" }, children: [
3930
- /* @__PURE__ */ jsx16(
3931
- Button8,
3932
- {
3933
- variant: "contained",
3934
- onClick: handleRefreshTokens,
3935
- disabled: isLoading,
3936
- startIcon: isLoading ? /* @__PURE__ */ jsx16(CircularProgress7, { size: 16 }) : null,
3937
- children: "Refresh Tokens"
3938
- }
3939
- ),
3940
- /* @__PURE__ */ jsx16(
3941
- Button8,
3942
- {
3943
- variant: "outlined",
3944
- color: "error",
3945
- onClick: handleLogout,
3946
- disabled: isLoading,
3947
- children: "Logout"
3948
- }
3949
- )
3950
- ] }),
3951
- error && /* @__PURE__ */ jsx16(Alert9, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
3952
- ] });
3953
- }
3954
- return /* @__PURE__ */ jsxs13(Box12, { sx: { maxWidth: 400, mx: "auto", p: 3 }, children: [
3955
- /* @__PURE__ */ jsx16(Typography11, { variant: "h4", gutterBottom: true, align: "center", children: "Login with Refresh Tokens" }),
3956
- /* @__PURE__ */ jsx16(Alert9, { severity: "info", sx: { mb: 3 }, children: "This demo shows the new Refresh Token Pattern with automatic session management" }),
3957
- !showForm ? /* @__PURE__ */ jsx16(
3958
- Button8,
3959
- {
3960
- fullWidth: true,
3961
- variant: "contained",
3962
- size: "large",
3963
- onClick: () => setShowForm(true),
3964
- sx: { mt: 2 },
3965
- children: "Show Login Form"
3966
- }
3967
- ) : /* @__PURE__ */ jsxs13("form", { onSubmit: handleLogin, children: [
3968
- /* @__PURE__ */ jsx16(
3969
- TextField5,
3970
- {
3971
- fullWidth: true,
3972
- label: "Email",
3973
- type: "email",
3974
- value: email,
3975
- onChange: (e) => setEmail(e.target.value),
3976
- margin: "normal",
3977
- required: true,
3978
- autoComplete: "email"
3979
- }
3980
- ),
3981
- /* @__PURE__ */ jsx16(
3982
- TextField5,
3983
- {
3984
- fullWidth: true,
3985
- label: "Password",
3986
- type: "password",
3987
- value: password,
3988
- onChange: (e) => setPassword(e.target.value),
3989
- margin: "normal",
3990
- required: true,
3991
- autoComplete: "current-password"
3992
- }
3993
- ),
3994
- /* @__PURE__ */ jsx16(
3995
- Button8,
3996
- {
3997
- type: "submit",
3998
- fullWidth: true,
3999
- variant: "contained",
4000
- size: "large",
4001
- disabled: isLoading,
4002
- startIcon: isLoading ? /* @__PURE__ */ jsx16(CircularProgress7, { size: 16 }) : null,
4003
- sx: { mt: 3, mb: 2 },
4004
- children: isLoading ? "Logging in..." : "Login"
4005
- }
4006
- )
4007
- ] }),
4008
- error && /* @__PURE__ */ jsx16(Alert9, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
4009
- ] });
4010
- }
4011
- function SessionStatus() {
4012
- const { isAuthenticated, isLoading, isExpiringSoon, expiresIn } = useSessionContext();
4013
- if (isLoading) {
4014
- return /* @__PURE__ */ jsxs13(Box12, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
4015
- /* @__PURE__ */ jsx16(CircularProgress7, { size: 16 }),
4016
- /* @__PURE__ */ jsx16(Typography11, { variant: "caption", children: "Loading session..." })
4017
- ] });
4018
- }
4019
- if (!isAuthenticated) {
4020
- return /* @__PURE__ */ jsx16(Typography11, { variant: "caption", color: "text.secondary", children: "Not logged in" });
4021
- }
4022
- return /* @__PURE__ */ jsxs13(Box12, { children: [
4023
- /* @__PURE__ */ jsx16(Typography11, { variant: "caption", color: "success.main", children: "\u2713 Authenticated" }),
4024
- isExpiringSoon && /* @__PURE__ */ jsxs13(Typography11, { variant: "caption", color: "warning.main", display: "block", children: [
4025
- "\u26A0 Token expires in ",
4026
- Math.round(expiresIn / 1e3 / 60),
4027
- " min"
4028
- ] })
4029
- ] });
4030
- }
4031
-
4032
- // src/hooks/useUserData.ts
4033
- import { useState as useState12, useEffect as useEffect11, useCallback as useCallback4, useRef as useRef5 } from "react";
4034
- import crudify4 from "@nocios/crudify-browser";
4035
- var useUserData = (options = {}) => {
4036
- const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
4037
- const { isAuthenticated, isInitialized, sessionData, tokens } = useSessionContext();
4038
- const [userData, setUserData] = useState12(null);
4039
- const [loading, setLoading] = useState12(false);
4040
- const [error, setError] = useState12(null);
4041
- const abortControllerRef = useRef5(null);
4042
- const mountedRef = useRef5(true);
4043
- const requestIdRef = useRef5(0);
4044
- const retryCountRef = useRef5(0);
4045
- const getUserEmail = useCallback4(() => {
4046
- if (!sessionData) return null;
4047
- return sessionData.email || sessionData["cognito:username"] || null;
4048
- }, [sessionData]);
4049
- const clearProfile = useCallback4(() => {
4050
- setUserData(null);
4051
- setError(null);
4052
- setLoading(false);
4053
- retryCountRef.current = 0;
4054
- }, []);
4055
- const refreshProfile = useCallback4(async () => {
4056
- const userEmail = getUserEmail();
4057
- if (!userEmail) {
4058
- if (mountedRef.current) {
4059
- setError("No user email available from session data");
4060
- setLoading(false);
4061
- }
4062
- return;
4063
- }
4064
- if (!isInitialized) {
4065
- if (mountedRef.current) {
4066
- setError("Session not initialized");
4067
- setLoading(false);
4068
- }
4069
- return;
4070
- }
4071
- if (abortControllerRef.current) {
4072
- abortControllerRef.current.abort();
4073
- }
4074
- const abortController = new AbortController();
4075
- abortControllerRef.current = abortController;
4076
- const currentRequestId = ++requestIdRef.current;
4077
- try {
4078
- if (mountedRef.current) {
4079
- setLoading(true);
4080
- setError(null);
4081
- }
4082
- const response = await crudify4.readItems("users", {
4083
- filter: { email: userEmail },
4084
- pagination: { limit: 1 }
4085
- });
4086
- if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
4087
- let userData2 = null;
4088
- if (response.success) {
4089
- if (Array.isArray(response.data) && response.data.length > 0) {
4090
- userData2 = response.data[0];
4091
- } else if (response.data?.response?.data) {
4092
- try {
4093
- const rawData = response.data.response.data;
4094
- const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
4095
- if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
4096
- userData2 = parsedData.items[0];
4097
- } else {
4098
- }
4099
- } catch (parseError) {
4100
- }
4101
- } else if (response.data && typeof response.data === "object") {
4102
- if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
4103
- userData2 = response.data.items[0];
4104
- } else {
4105
- }
4106
- } else if (response.data?.data?.response?.data) {
4107
- try {
4108
- const rawData = response.data.data.response.data;
4109
- const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
4110
- if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
4111
- userData2 = parsedData.items[0];
4112
- }
4113
- } catch (parseError) {
4114
- }
4115
- }
4116
- }
4117
- if (userData2) {
4118
- setUserData(userData2);
4119
- setError(null);
4120
- retryCountRef.current = 0;
4121
- } else {
4122
- setError("User profile not found in database");
4123
- setUserData(null);
4124
- }
4125
- }
4126
- } catch (err) {
4127
- if (currentRequestId === requestIdRef.current && mountedRef.current) {
4128
- const error2 = err;
4129
- if (error2.name === "AbortError") {
4130
- return;
4131
- }
4132
- const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
4133
- if (shouldRetry) {
4134
- retryCountRef.current++;
4135
- setTimeout(() => {
4136
- if (mountedRef.current) {
4137
- refreshProfile();
4138
- }
4139
- }, 1e3 * retryCountRef.current);
4140
- } else {
4141
- setError("Failed to load user profile from database");
4142
- setUserData(null);
4143
- }
4144
- }
4145
- } finally {
4146
- if (currentRequestId === requestIdRef.current && mountedRef.current) {
4147
- setLoading(false);
4148
- }
4149
- if (abortControllerRef.current === abortController) {
4150
- abortControllerRef.current = null;
4151
- }
4152
- }
4153
- }, [isInitialized, getUserEmail, retryOnError, maxRetries]);
4154
- useEffect11(() => {
4155
- if (autoFetch && isAuthenticated && isInitialized) {
4156
- refreshProfile();
4157
- } else if (!isAuthenticated) {
4158
- clearProfile();
4159
- }
4160
- }, [autoFetch, isAuthenticated, isInitialized, refreshProfile, clearProfile]);
4161
- useEffect11(() => {
4162
- mountedRef.current = true;
4163
- return () => {
4164
- mountedRef.current = false;
4165
- if (abortControllerRef.current) {
4166
- abortControllerRef.current.abort();
4167
- abortControllerRef.current = null;
4168
- }
4169
- };
4170
- }, []);
4171
- const user = {
4172
- session: sessionData,
4173
- // Usar sessionData del nuevo sistema
4174
- data: userData
4175
- // Mantener userData del database igual que legacy
4176
- };
4177
- return {
4178
- user,
4179
- loading,
4180
- error,
4181
- refreshProfile,
4182
- clearProfile
4183
- };
4184
- };
4185
-
4186
- // src/hooks/useAuth.ts
4187
- import { useCallback as useCallback5 } from "react";
4188
- var useAuth = () => {
4189
- const {
4190
- isAuthenticated,
4191
- isLoading,
4192
- isInitialized,
4193
- tokens,
4194
- error,
4195
- sessionData,
4196
- login,
4197
- logout,
4198
- refreshTokens,
4199
- clearError,
4200
- getTokenInfo,
4201
- isExpiringSoon,
4202
- expiresIn,
4203
- refreshExpiresIn
4204
- } = useSessionContext();
4205
- const setToken = useCallback5((token) => {
4206
- if (token) {
4207
- console.warn("useAuth.setToken() is deprecated. Use login() method instead for better security.");
4208
- } else {
4209
- logout();
4210
- }
4211
- }, [logout]);
4212
- const tokenExpiration = tokens?.expiresAt ? new Date(tokens.expiresAt) : null;
4213
- return {
4214
- // Estado básico (compatible con legacy)
4215
- isAuthenticated,
4216
- loading: isLoading,
4217
- // En el nuevo sistema se llama isLoading
4218
- error,
4219
- // Datos del token (compatible con legacy + mejorado)
4220
- token: tokens?.accessToken || null,
4221
- user: sessionData,
4222
- // sessionData del nuevo sistema (más rico que legacy)
4223
- tokenExpiration,
4224
- // Acciones (compatible con legacy + mejorado)
4225
- setToken,
4226
- logout,
4227
- refreshToken: refreshTokens,
4228
- // Funcionalidad real en el nuevo sistema
4229
- // Nuevas funcionalidades del sistema de refresh tokens
4230
- login,
4231
- isExpiringSoon,
4232
- expiresIn,
4233
- refreshExpiresIn,
4234
- getTokenInfo,
4235
- clearError
4236
- };
4237
- };
4238
-
4239
- // src/hooks/useData.ts
4240
- import { useCallback as useCallback6 } from "react";
4241
- import crudify5 from "@nocios/crudify-browser";
4242
- var useData = () => {
4243
- const {
4244
- isInitialized,
4245
- isLoading,
4246
- error,
4247
- isAuthenticated,
4248
- login: sessionLogin
4249
- } = useSessionContext();
4250
- const isReady = useCallback6(() => {
4251
- return isInitialized && !isLoading && !error;
4252
- }, [isInitialized, isLoading, error]);
4253
- const waitForReady = useCallback6(async () => {
4254
- return new Promise((resolve, reject) => {
4255
- const checkReady = () => {
4256
- if (isReady()) {
4257
- resolve();
4258
- } else if (error) {
4259
- reject(new Error(error));
4260
- } else {
4261
- setTimeout(checkReady, 100);
4262
- }
4263
- };
4264
- checkReady();
4265
- });
4266
- }, [isReady, error]);
4267
- const ensureReady = useCallback6(async () => {
4268
- if (!isReady()) {
4269
- throw new Error("System not ready. Check isInitialized, isLoading, and error states.");
4270
- }
4271
- }, [isReady]);
4272
- const readItems = useCallback6(async (moduleKey, filter, options) => {
4273
- await ensureReady();
4274
- return await crudify5.readItems(moduleKey, filter || {}, options);
4275
- }, [ensureReady]);
4276
- const readItem = useCallback6(async (moduleKey, filter, options) => {
4277
- await ensureReady();
4278
- return await crudify5.readItem(moduleKey, filter, options);
4279
- }, [ensureReady]);
4280
- const createItem = useCallback6(async (moduleKey, data, options) => {
4281
- await ensureReady();
4282
- return await crudify5.createItem(moduleKey, data, options);
4283
- }, [ensureReady]);
4284
- const updateItem = useCallback6(async (moduleKey, data, options) => {
4285
- await ensureReady();
4286
- return await crudify5.updateItem(moduleKey, data, options);
4287
- }, [ensureReady]);
4288
- const deleteItem = useCallback6(async (moduleKey, id, options) => {
4289
- await ensureReady();
4290
- return await crudify5.deleteItem(moduleKey, id, options);
4291
- }, [ensureReady]);
4292
- const transaction = useCallback6(async (operations, options) => {
4293
- await ensureReady();
4294
- return await crudify5.transaction(operations, options);
4295
- }, [ensureReady]);
4296
- const login = useCallback6(async (email, password) => {
4297
- try {
4298
- const result = await sessionLogin(email, password);
4299
- if (result.success) {
4300
- return {
4301
- success: true,
4302
- data: result.tokens
4303
- };
4304
- } else {
4305
- return {
4306
- success: false,
4307
- errors: result.error || "Login failed"
4308
- };
4309
- }
4310
- } catch (error2) {
4311
- return {
4312
- success: false,
4313
- errors: error2 instanceof Error ? error2.message : "Login failed"
4314
- };
4315
- }
4316
- }, [sessionLogin]);
4317
- return {
4318
- // CRUD operations
4319
- readItems,
4320
- readItem,
4321
- createItem,
4322
- updateItem,
4323
- deleteItem,
4324
- transaction,
4325
- login,
4326
- // Estado
4327
- isInitialized,
4328
- isInitializing: isLoading,
4329
- // El nuevo sistema usa isLoading
4330
- initializationError: error,
4331
- // Utilidades
4332
- isReady,
4333
- waitForReady
4334
- };
4335
- };
4336
-
4337
- // src/components/CrudifyLogin/utils/secureStorage.ts
4338
- import CryptoJS2 from "crypto-js";
4339
- var SecureStorage = class {
4340
- constructor(storageType = "sessionStorage") {
4341
- this.encryptionKey = this.generateEncryptionKey();
4342
- this.storage = storageType === "localStorage" ? window.localStorage : window.sessionStorage;
4343
- }
4344
- generateEncryptionKey() {
4345
- const browserFingerprint = [
4346
- navigator.userAgent,
4347
- navigator.language,
4348
- (/* @__PURE__ */ new Date()).getTimezoneOffset(),
4349
- screen.colorDepth,
4350
- screen.width,
4351
- screen.height,
4352
- "crudify-login"
4353
- ].join("|");
4354
- return CryptoJS2.SHA256(browserFingerprint).toString();
4355
- }
4356
- setItem(key, value, expiryMinutes) {
4357
- try {
4358
- const encrypted = CryptoJS2.AES.encrypt(value, this.encryptionKey).toString();
4359
- this.storage.setItem(key, encrypted);
4360
- if (expiryMinutes) {
4361
- const expiryTime = (/* @__PURE__ */ new Date()).getTime() + expiryMinutes * 60 * 1e3;
4362
- this.storage.setItem(`${key}_expiry`, expiryTime.toString());
4363
- }
4364
- } catch (error) {
4365
- console.error("Failed to encrypt and store data:", error);
4366
- }
4367
- }
4368
- getItem(key) {
4369
- try {
4370
- const expiryKey = `${key}_expiry`;
4371
- const expiry = this.storage.getItem(expiryKey);
4372
- if (expiry) {
4373
- const expiryTime = parseInt(expiry, 10);
4374
- if ((/* @__PURE__ */ new Date()).getTime() > expiryTime) {
4375
- this.removeItem(key);
4376
- return null;
4377
- }
4378
- }
4379
- const encrypted = this.storage.getItem(key);
4380
- if (!encrypted) return null;
4381
- const decrypted = CryptoJS2.AES.decrypt(encrypted, this.encryptionKey);
4382
- const result = decrypted.toString(CryptoJS2.enc.Utf8);
4383
- if (!result) {
4384
- console.warn("Failed to decrypt stored data - may be corrupted");
4385
- this.removeItem(key);
4386
- return null;
4387
- }
4388
- return result;
4389
- } catch (error) {
4390
- console.error("Failed to decrypt data:", error);
4391
- this.removeItem(key);
4392
- return null;
4393
- }
4394
- }
4395
- removeItem(key) {
4396
- this.storage.removeItem(key);
4397
- this.storage.removeItem(`${key}_expiry`);
4398
- }
4399
- setToken(token) {
4400
- try {
4401
- const parts = token.split(".");
4402
- if (parts.length === 3) {
4403
- const payload = JSON.parse(atob(parts[1]));
4404
- if (payload.exp) {
4405
- const expiryTime = payload.exp * 1e3;
4406
- const now = (/* @__PURE__ */ new Date()).getTime();
4407
- const minutesUntilExpiry = Math.floor((expiryTime - now) / (60 * 1e3));
4408
- if (minutesUntilExpiry > 0) {
4409
- this.setItem("authToken", token, minutesUntilExpiry);
4410
- return;
4411
- }
4412
- }
4413
- }
4414
- } catch (error) {
4415
- console.warn("Failed to parse token expiry, using default expiry");
4416
- }
4417
- this.setItem("authToken", token, 24 * 60);
4418
- }
4419
- getToken() {
4420
- const token = this.getItem("authToken");
4421
- if (token) {
4422
- try {
4423
- const parts = token.split(".");
4424
- if (parts.length === 3) {
4425
- const payload = JSON.parse(atob(parts[1]));
4426
- if (payload.exp) {
4427
- const now = Math.floor(Date.now() / 1e3);
4428
- if (payload.exp < now) {
4429
- this.removeItem("authToken");
4430
- return null;
4431
- }
4432
- }
4433
- }
4434
- } catch (error) {
4435
- console.warn("Failed to validate token expiry");
4436
- this.removeItem("authToken");
4437
- return null;
4438
- }
4439
- }
4440
- return token;
4441
- }
4442
- };
4443
- var secureSessionStorage = new SecureStorage("sessionStorage");
4444
- var secureLocalStorage = new SecureStorage("localStorage");
4445
-
4446
- // src/hooks/useCrudifyWithNotifications.ts
4447
- import { useCallback as useCallback7 } from "react";
4448
- import crudify6 from "@nocios/crudify-browser";
4449
- var ERROR_SEVERITY_MAP2 = {
4450
- // Errores de autenticación - warning porque el usuario puede corregirlos
4451
- INVALID_CREDENTIALS: "warning",
4452
- UNAUTHORIZED: "warning",
4453
- INVALID_API_KEY: "error",
4454
- // Errores de usuario/permisos - warning
4455
- USER_NOT_FOUND: "warning",
4456
- USER_NOT_ACTIVE: "warning",
4457
- NO_PERMISSION: "warning",
4458
- // Errores de datos - info porque pueden ser esperados
4459
- ITEM_NOT_FOUND: "info",
4460
- NOT_FOUND: "info",
4461
- IN_USE: "warning",
4462
- // Errores de validación - warning
4463
- FIELD_ERROR: "warning",
4464
- BAD_REQUEST: "warning",
4465
- // Errores del sistema - error
4466
- INTERNAL_SERVER_ERROR: "error",
4467
- DATABASE_CONNECTION_ERROR: "error",
4468
- INVALID_CONFIGURATION: "error",
4469
- UNKNOWN_OPERATION: "error",
4470
- // Rate limiting - warning con mayor duración
4471
- TOO_MANY_REQUESTS: "warning"
4472
- };
4473
- var ERROR_TRANSLATION_MAP = {
4474
- INVALID_CREDENTIALS: "errors.auth.INVALID_CREDENTIALS",
4475
- UNAUTHORIZED: "errors.auth.UNAUTHORIZED",
4476
- INVALID_API_KEY: "errors.auth.INVALID_API_KEY",
4477
- USER_NOT_FOUND: "errors.auth.USER_NOT_FOUND",
4478
- USER_NOT_ACTIVE: "errors.auth.USER_NOT_ACTIVE",
4479
- NO_PERMISSION: "errors.auth.NO_PERMISSION",
4480
- ITEM_NOT_FOUND: "errors.data.ITEM_NOT_FOUND",
4481
- NOT_FOUND: "errors.data.NOT_FOUND",
4482
- IN_USE: "errors.data.IN_USE",
4483
- FIELD_ERROR: "errors.data.FIELD_ERROR",
4484
- BAD_REQUEST: "errors.data.BAD_REQUEST",
4485
- INTERNAL_SERVER_ERROR: "errors.system.INTERNAL_SERVER_ERROR",
4486
- DATABASE_CONNECTION_ERROR: "errors.system.DATABASE_CONNECTION_ERROR",
4487
- INVALID_CONFIGURATION: "errors.system.INVALID_CONFIGURATION",
4488
- UNKNOWN_OPERATION: "errors.system.UNKNOWN_OPERATION",
4489
- TOO_MANY_REQUESTS: "errors.system.TOO_MANY_REQUESTS"
4490
- };
4491
- var useCrudifyWithNotifications = (options = {}) => {
4492
- const { showNotification } = useGlobalNotification();
4493
- const {
4494
- showSuccessNotifications = false,
4495
- showErrorNotifications = true,
4496
- customErrorMessages = {},
4497
- defaultErrorMessage = "Ha ocurrido un error inesperado",
4498
- autoHideDuration = 6e3,
4499
- appStructure = [],
4500
- translateFn = (key) => key
4501
- // Fallback si no hay función de traducción
4502
- } = options;
4503
- const shouldShowNotification = useCallback7((response) => {
4504
- if (!response.success && response.errors) {
4505
- const hasFieldErrors = Object.keys(response.errors).some((key) => key !== "_error" && key !== "_graphql" && key !== "_transaction");
4506
- if (hasFieldErrors) return false;
4507
- if (response.errors._transaction?.includes("ONE_OR_MORE_OPERATIONS_FAILED")) return false;
4508
- if (response.errors._error?.includes("TOO_MANY_REQUESTS")) return false;
4509
- }
4510
- if (!response.success && response.data?.response?.status === "TOO_MANY_REQUESTS") return false;
4511
- return true;
4512
- }, []);
4513
- const getSafeTranslation = useCallback7(
4514
- (key, fallback) => {
4515
- const translated = translateFn(key);
4516
- if (translated === key) return fallback || translateFn("error.unknown");
4517
- return translated;
4518
- },
4519
- [translateFn]
4520
- );
4521
- const isCrudOperation = useCallback7((operation) => {
4522
- return ["create", "update", "delete"].includes(operation);
4523
- }, []);
4524
- const shouldShowSuccessNotification = useCallback7(
4525
- (operation, moduleKey) => {
4526
- if (!showSuccessNotifications) return false;
4527
- if (isCrudOperation(operation) && moduleKey) return true;
4528
- return appStructure.some((action) => action.key === operation);
4529
- },
4530
- [showSuccessNotifications, appStructure, isCrudOperation]
4531
- );
4532
- const getSuccessMessage = useCallback7(
4533
- (operation, moduleKey, actionConfig) => {
4534
- const operationKey = actionConfig?.key && typeof actionConfig.key === "string" ? actionConfig.key : operation;
4535
- const successKey = `action.onSuccess.${operationKey}`;
4536
- const successMessage = getSafeTranslation(successKey);
4537
- if (successMessage !== translateFn("error.unknown")) {
4538
- if (isCrudOperation(operationKey) && moduleKey) {
4539
- const itemNameKey = `action.${moduleKey}Singular`;
4540
- const itemName = getSafeTranslation(itemNameKey);
4541
- if (itemName !== translateFn("error.unknown")) {
4542
- return translateFn(successKey, { item: itemName });
4543
- } else {
4544
- const withoutItemKey = `action.onSuccess.${operationKey}WithoutItem`;
4545
- const withoutItemMessage = getSafeTranslation(withoutItemKey);
4546
- return withoutItemMessage !== translateFn("error.unknown") ? withoutItemMessage : successMessage;
4547
- }
4548
- }
4549
- return successMessage;
4550
- }
4551
- return translateFn("success.transaction");
4552
- },
4553
- [getSafeTranslation, translateFn, isCrudOperation]
4554
- );
4555
- const getErrorMessage2 = useCallback7(
4556
- (response) => {
4557
- if (response.errorCode && customErrorMessages[response.errorCode]) {
4558
- return customErrorMessages[response.errorCode];
4559
- }
4560
- if (response.errorCode && ERROR_TRANSLATION_MAP[response.errorCode]) {
4561
- return getSafeTranslation(ERROR_TRANSLATION_MAP[response.errorCode]);
4562
- }
4563
- if (response.errorCode) {
4564
- const possibleKeys = [
4565
- `errors.auth.${response.errorCode}`,
4566
- `errors.data.${response.errorCode}`,
4567
- `errors.system.${response.errorCode}`,
4568
- `errors.${response.errorCode}`
4569
- ];
4570
- for (const key of possibleKeys) {
4571
- const translated = getSafeTranslation(key);
4572
- if (translated !== translateFn("error.unknown")) {
4573
- return translated;
4574
- }
4575
- }
4576
- }
4577
- if (typeof response.data === "string" && response.data.startsWith("errors.")) {
4578
- const translated = getSafeTranslation(response.data);
4579
- if (translated !== translateFn("error.unknown")) {
4580
- return translated;
4581
- }
4582
- }
4583
- if (response.errors && Object.keys(response.errors).length > 0) {
4584
- const errorFields = Object.keys(response.errors);
4585
- if (errorFields.length === 1 && errorFields[0] === "_transaction") {
4586
- const transactionErrors = response.errors["_transaction"];
4587
- if (transactionErrors?.includes("ONE_OR_MORE_OPERATIONS_FAILED")) {
4588
- return "";
4589
- }
4590
- if (Array.isArray(transactionErrors) && transactionErrors.length > 0) {
4591
- const firstError = transactionErrors[0];
4592
- if (typeof firstError === "string" && firstError !== "ONE_OR_MORE_OPERATIONS_FAILED") {
4593
- try {
4594
- const parsed = JSON.parse(firstError);
4595
- if (Array.isArray(parsed) && parsed.length > 0) {
4596
- const firstTransactionError = parsed[0];
4597
- if (firstTransactionError?.response?.errorCode) {
4598
- const errorCode = firstTransactionError.response.errorCode;
4599
- if (ERROR_TRANSLATION_MAP[errorCode]) {
4600
- return getSafeTranslation(ERROR_TRANSLATION_MAP[errorCode]);
4601
- }
4602
- const possibleKeys = [
4603
- `errors.auth.${errorCode}`,
4604
- `errors.data.${errorCode}`,
4605
- `errors.system.${errorCode}`,
4606
- `errors.${errorCode}`
4607
- ];
4608
- for (const key of possibleKeys) {
4609
- const translated = getSafeTranslation(key);
4610
- if (translated !== getSafeTranslation("error.unknown")) {
4611
- return translated;
4612
- }
4613
- }
4614
- }
4615
- if (firstTransactionError?.response?.data) {
4616
- return firstTransactionError.response.data;
4617
- }
4618
- }
4619
- if (parsed?.response?.message) {
4620
- const errorMsg = parsed.response.message.toLowerCase();
4621
- if (errorMsg.includes("expired")) {
4622
- return getSafeTranslation("resetPassword.linkExpired", "El enlace ha expirado");
4623
- } else if (errorMsg.includes("invalid")) {
4624
- return getSafeTranslation("resetPassword.invalidCode", "C\xF3digo inv\xE1lido");
4625
- }
4626
- return parsed.response.message;
4627
- }
4628
- } catch {
4629
- if (firstError.toLowerCase().includes("expired")) {
4630
- return getSafeTranslation("resetPassword.linkExpired", "El enlace ha expirado");
4631
- } else if (firstError.toLowerCase().includes("invalid")) {
4632
- return getSafeTranslation("resetPassword.invalidCode", "C\xF3digo inv\xE1lido");
4633
- }
4634
- return firstError;
4635
- }
4636
- }
4637
- }
4638
- return getSafeTranslation("error.transaction", "Error en la operaci\xF3n");
4639
- }
4640
- if (errorFields.length === 1 && errorFields[0] === "_error") {
4641
- const errorMessages = response.errors["_error"];
4642
- return Array.isArray(errorMessages) ? errorMessages[0] : String(errorMessages);
4643
- }
4644
- if (errorFields.length === 1 && errorFields[0] === "_graphql") {
4645
- return getSafeTranslation("errors.system.DATABASE_CONNECTION_ERROR");
4646
- }
4647
- return `${getSafeTranslation("errors.data.FIELD_ERROR")}: ${errorFields.join(", ")}`;
4648
- }
4649
- return defaultErrorMessage || translateFn("error.unknown");
4650
- },
4651
- [customErrorMessages, defaultErrorMessage, translateFn, getSafeTranslation]
4652
- );
4653
- const getErrorSeverity = useCallback7((response) => {
4654
- if (response.errorCode && ERROR_SEVERITY_MAP2[response.errorCode]) {
4655
- return ERROR_SEVERITY_MAP2[response.errorCode];
4656
- }
4657
- return "error";
4658
- }, []);
4659
- const createItem = useCallback7(
4660
- async (moduleKey, data, options2) => {
4661
- const response = await crudify6.createItem(moduleKey, data, options2);
4662
- if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
4663
- const message = getErrorMessage2(response);
4664
- const severity = getErrorSeverity(response);
4665
- showNotification(message, severity, { autoHideDuration });
4666
- } else if (response.success) {
4667
- const actionConfig = options2?.actionConfig;
4668
- const operation = actionConfig?.key || "create";
4669
- const effectiveModuleKey = actionConfig?.moduleKey || moduleKey;
4670
- if (shouldShowSuccessNotification(operation, effectiveModuleKey)) {
4671
- const message = getSuccessMessage(operation, effectiveModuleKey, actionConfig);
4672
- showNotification(message, "success", { autoHideDuration });
4673
- }
4674
- }
4675
- return response;
4676
- },
4677
- [
4678
- showErrorNotifications,
4679
- shouldShowSuccessNotification,
4680
- showNotification,
4681
- getErrorMessage2,
4682
- getErrorSeverity,
4683
- getSuccessMessage,
4684
- autoHideDuration,
4685
- shouldShowNotification
4686
- ]
4687
- );
4688
- const updateItem = useCallback7(
4689
- async (moduleKey, data, options2) => {
4690
- const response = await crudify6.updateItem(moduleKey, data, options2);
4691
- if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
4692
- const message = getErrorMessage2(response);
4693
- const severity = getErrorSeverity(response);
4694
- showNotification(message, severity, { autoHideDuration });
4695
- } else if (response.success) {
4696
- const actionConfig = options2?.actionConfig;
4697
- const operation = actionConfig?.key || "update";
4698
- const effectiveModuleKey = actionConfig?.moduleKey || moduleKey;
4699
- if (shouldShowSuccessNotification(operation, effectiveModuleKey)) {
4700
- const message = getSuccessMessage(operation, effectiveModuleKey, actionConfig);
4701
- showNotification(message, "success", { autoHideDuration });
4702
- }
4703
- }
4704
- return response;
4705
- },
4706
- [
4707
- showErrorNotifications,
4708
- shouldShowSuccessNotification,
4709
- showNotification,
4710
- getErrorMessage2,
4711
- getErrorSeverity,
4712
- getSuccessMessage,
4713
- autoHideDuration,
4714
- shouldShowNotification
4715
- ]
4716
- );
4717
- const deleteItem = useCallback7(
4718
- async (moduleKey, id, options2) => {
4719
- const response = await crudify6.deleteItem(moduleKey, id, options2);
4720
- if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
4721
- const message = getErrorMessage2(response);
4722
- const severity = getErrorSeverity(response);
4723
- showNotification(message, severity, { autoHideDuration });
4724
- } else if (response.success) {
4725
- const actionConfig = options2?.actionConfig;
4726
- const operation = actionConfig?.key || "delete";
4727
- const effectiveModuleKey = actionConfig?.moduleKey || moduleKey;
4728
- if (shouldShowSuccessNotification(operation, effectiveModuleKey)) {
4729
- const message = getSuccessMessage(operation, effectiveModuleKey, actionConfig);
4730
- showNotification(message, "success", { autoHideDuration });
4731
- }
4732
- }
4733
- return response;
4734
- },
4735
- [
4736
- showErrorNotifications,
4737
- shouldShowSuccessNotification,
4738
- showNotification,
4739
- getErrorMessage2,
4740
- getErrorSeverity,
4741
- getSuccessMessage,
4742
- autoHideDuration,
4743
- shouldShowNotification
4744
- ]
4745
- );
4746
- const readItem = useCallback7(
4747
- async (moduleKey, filter, options2) => {
4748
- const response = await crudify6.readItem(moduleKey, filter, options2);
4749
- if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
4750
- const message = getErrorMessage2(response);
4751
- const severity = getErrorSeverity(response);
4752
- showNotification(message, severity, { autoHideDuration });
4753
- }
4754
- return response;
4755
- },
4756
- [showErrorNotifications, showNotification, getErrorMessage2, getErrorSeverity, autoHideDuration, shouldShowNotification]
4757
- );
4758
- const readItems = useCallback7(
4759
- async (moduleKey, filter, options2) => {
4760
- const response = await crudify6.readItems(moduleKey, filter, options2);
4761
- if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
4762
- const message = getErrorMessage2(response);
4763
- const severity = getErrorSeverity(response);
4764
- showNotification(message, severity, { autoHideDuration });
4765
- }
4766
- return response;
4767
- },
4768
- [showErrorNotifications, showNotification, getErrorMessage2, getErrorSeverity, autoHideDuration, shouldShowNotification]
4769
- );
4770
- const transaction = useCallback7(
4771
- async (data, options2) => {
4772
- const response = await crudify6.transaction(data, options2);
4773
- const skipNotifications = options2?.skipNotifications === true;
4774
- if (!skipNotifications && !response.success && showErrorNotifications && shouldShowNotification(response)) {
4775
- const message = getErrorMessage2(response);
4776
- const severity = getErrorSeverity(response);
4777
- showNotification(message, severity, { autoHideDuration });
4778
- } else if (!skipNotifications && response.success) {
4779
- let operation = "transaction";
4780
- let moduleKey;
4781
- let actionConfig = null;
4782
- if (options2?.actionConfig) {
4783
- actionConfig = options2.actionConfig;
4784
- operation = actionConfig.key;
4785
- moduleKey = actionConfig.moduleKey;
4786
- } else if (Array.isArray(data) && data.length > 0 && data[0].operation) {
4787
- operation = data[0].operation;
4788
- actionConfig = appStructure.find((action) => action.key === operation);
4789
- if (actionConfig) {
4790
- moduleKey = actionConfig.moduleKey;
4791
- }
4792
- }
4793
- if (shouldShowSuccessNotification(operation, moduleKey)) {
4794
- const message = getSuccessMessage(operation, moduleKey, actionConfig);
4795
- showNotification(message, "success", { autoHideDuration });
4796
- }
4797
- }
4798
- return response;
4799
- },
4800
- [
4801
- showErrorNotifications,
4802
- shouldShowSuccessNotification,
4803
- showNotification,
4804
- getErrorMessage2,
4805
- getErrorSeverity,
4806
- getSuccessMessage,
4807
- autoHideDuration,
4808
- shouldShowNotification,
4809
- appStructure
4810
- ]
4811
- );
4812
- const handleResponse = useCallback7(
4813
- (response, successMessage) => {
4814
- if (!response.success && showErrorNotifications && shouldShowNotification(response)) {
4815
- const message = getErrorMessage2(response);
4816
- const severity = getErrorSeverity(response);
4817
- showNotification(message, severity, { autoHideDuration });
4818
- } else if (response.success && showSuccessNotifications && successMessage) {
4819
- showNotification(successMessage, "success", { autoHideDuration });
4820
- }
4821
- return response;
4822
- },
4823
- [
4824
- showErrorNotifications,
4825
- showSuccessNotifications,
4826
- showNotification,
4827
- getErrorMessage2,
4828
- getErrorSeverity,
4829
- autoHideDuration,
4830
- shouldShowNotification,
4831
- translateFn
4832
- ]
4833
- );
4834
- return {
4835
- // Métodos de crudify con notificaciones automáticas
4836
- createItem,
4837
- updateItem,
4838
- deleteItem,
4839
- readItem,
4840
- readItems,
4841
- transaction,
4842
- // Método genérico para manejar respuestas
4843
- handleResponse,
4844
- // Funciones de utilidad
4845
- getErrorMessage: getErrorMessage2,
4846
- getErrorSeverity,
4847
- shouldShowNotification
4848
- };
4849
- };
4850
- export {
4851
- CrudifyLogin_default as CrudifyLogin,
4852
- ERROR_CODES,
4853
- ERROR_SEVERITY_MAP,
4854
- GlobalNotificationProvider,
4855
- LoginComponent,
4856
- POLICY_ACTIONS,
4857
- PREFERRED_POLICY_ORDER,
4858
- Policies_default as Policies,
4859
- ProtectedRoute,
4860
- SessionDebugInfo,
4861
- SessionManager,
4862
- SessionProvider,
4863
- SessionStatus,
4864
- TokenStorage,
4865
- UserProfileDisplay_default as UserProfileDisplay,
4866
- createErrorTranslator,
4867
- default2 as crudify,
4868
- decodeJwtSafely,
4869
- getCookie,
4870
- getCurrentUserEmail,
4871
- getErrorMessage,
4872
- handleCrudifyError,
4873
- isTokenExpired,
4874
- parseApiError,
4875
- parseJavaScriptError,
4876
- parseTransactionError,
4877
- secureLocalStorage,
4878
- secureSessionStorage,
4879
- translateError,
4880
- translateErrorCode,
4881
- translateErrorCodes,
4882
- useAuth,
4883
- useCrudifyWithNotifications,
4884
- useData,
4885
- useGlobalNotification,
4886
- useSession,
4887
- useSessionContext,
4888
- useUserData,
4889
- useUserProfile
4890
- };
1
+ import{a as U,b as L,c as v,d as O,e as h,f as k,g as A}from"./chunk-JFXC3E4B.mjs";import{a as N,b,c as _,d as w}from"./chunk-PVQVPSNT.mjs";import{a as r,b as a,c as n,d as m,e as l,f as x,g as d,h as y,i as c,j as T}from"./chunk-TKYA4Q4U.mjs";import{a as I,b as q}from"./chunk-T2CPA46I.mjs";import{a as S,b as P,c as R,d as g,e as E,f as C,g as D}from"./chunk-BJ6PIVZR.mjs";import{a as o,b as e,c as t,d as s,e as i,f as p,g as f,h as u}from"./chunk-YS3C7YG5.mjs";import{default as J}from"@nocios/crudify-browser";export*from"@nocios/crudify-browser";export{U as CrudifyLogin,S as ERROR_CODES,P as ERROR_SEVERITY_MAP,m as GlobalNotificationProvider,k as LoginComponent,v as POLICY_ACTIONS,O as PREFERRED_POLICY_ORDER,h as Policies,y as ProtectedRoute,c as SessionDebugInfo,a as SessionManager,x as SessionProvider,A as SessionStatus,r as TokenStorage,L as UserProfileDisplay,i as createErrorTranslator,J as crudify,p as decodeJwtSafely,o as getCookie,f as getCurrentUserEmail,E as getErrorMessage,D as handleCrudifyError,u as isTokenExpired,R as parseApiError,C as parseJavaScriptError,g as parseTransactionError,q as secureLocalStorage,I as secureSessionStorage,s as translateError,e as translateErrorCode,t as translateErrorCodes,b as useAuth,w as useCrudifyWithNotifications,_ as useData,l as useGlobalNotification,n as useSession,d as useSessionContext,N as useUserData,T as useUserProfile};