@nocios/crudify-components 2.0.42 → 2.0.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/.github/workflows/ci.yml +70 -0
  2. package/.husky/pre-commit +26 -0
  3. package/.husky/pre-push +30 -0
  4. package/.prettierignore +18 -0
  5. package/.prettierrc +9 -0
  6. package/.scannerwork/.sonar_lock +0 -0
  7. package/.scannerwork/report-task.txt +6 -0
  8. package/README.md +44 -36
  9. package/README_DEPTH.md +148 -141
  10. package/coverage/coverage-final.json +83 -83
  11. package/coverage/index.html +175 -175
  12. package/coverage/lcov-report/base.css +224 -0
  13. package/coverage/lcov-report/block-navigation.js +87 -0
  14. package/coverage/lcov-report/favicon.png +0 -0
  15. package/coverage/lcov-report/index.html +686 -0
  16. package/coverage/lcov-report/prettify.css +1 -0
  17. package/coverage/lcov-report/prettify.js +2 -0
  18. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  19. package/coverage/lcov-report/sorter.js +210 -0
  20. package/coverage/lcov.info +22388 -0
  21. package/dist/{CrudiaMarkdownField-CXAuu-v2.d.ts → CrudiaMarkdownField-CkiBwG-U.d.ts} +8 -8
  22. package/dist/{CrudiaMarkdownField-TNyMLb-h.d.mts → CrudiaMarkdownField-D-DqiXMQ.d.mts} +8 -8
  23. package/dist/{GlobalNotificationProvider-Zq18OkpI.d.ts → GlobalNotificationProvider-CdwdNv_8.d.mts} +4 -4
  24. package/dist/{GlobalNotificationProvider-Zq18OkpI.d.mts → GlobalNotificationProvider-CdwdNv_8.d.ts} +4 -4
  25. package/dist/chunk-2WAUZ6KI.js +1 -0
  26. package/dist/chunk-3IGZNZCT.mjs +1 -0
  27. package/dist/chunk-43L2PP77.mjs +1 -0
  28. package/dist/chunk-6VS5OT3A.mjs +1 -0
  29. package/dist/chunk-BWJTTMKS.js +1 -0
  30. package/dist/chunk-EMPPCCVU.js +1 -0
  31. package/dist/chunk-J43UPGBE.js +1 -0
  32. package/dist/chunk-K6ZRXOJ7.mjs +1 -0
  33. package/dist/chunk-RYQQZTEP.js +1 -0
  34. package/dist/chunk-VTMSOK4V.mjs +1 -0
  35. package/dist/components.d.mts +3 -3
  36. package/dist/components.d.ts +3 -3
  37. package/dist/components.js +1 -1
  38. package/dist/components.mjs +1 -1
  39. package/dist/{errorTranslation-D-Y7uNN_.d.mts → errorTranslation-BcX8AaK7.d.mts} +5 -5
  40. package/dist/{errorTranslation-DDlAXpMl.d.ts → errorTranslation-CF-5JClP.d.ts} +5 -5
  41. package/dist/hooks.d.mts +3 -3
  42. package/dist/hooks.d.ts +3 -3
  43. package/dist/hooks.js +1 -1
  44. package/dist/hooks.mjs +1 -1
  45. package/dist/{index-U--xRr8A.d.mts → index-D06kTP0C.d.mts} +18 -18
  46. package/dist/{index-dXVRVcEB.d.ts → index-DEDnmsdO.d.ts} +18 -18
  47. package/dist/index.d.mts +362 -362
  48. package/dist/index.d.ts +362 -362
  49. package/dist/index.js +2 -2
  50. package/dist/index.mjs +2 -2
  51. package/dist/{tenantConfig-DqJqQkoR.d.mts → tenantConfig-CYnS9TPV.d.mts} +14 -8
  52. package/dist/{tenantConfig-DqJqQkoR.d.ts → tenantConfig-CYnS9TPV.d.ts} +14 -8
  53. package/dist/utils.d.mts +3 -3
  54. package/dist/utils.d.ts +3 -3
  55. package/dist/utils.js +1 -1
  56. package/dist/utils.mjs +1 -1
  57. package/eslint.config.mjs +140 -0
  58. package/package.json +32 -3
  59. package/scripts/bump-version.cjs +23 -0
  60. package/sonar-project.properties +23 -0
  61. package/tests/helpers/testUtils.tsx +89 -0
  62. package/tests/hooks/useSession/testUtils.tsx +212 -0
  63. package/tests/setup.ts +139 -0
  64. package/tests/vitest.d.ts +1 -0
  65. package/vitest.config.ts +20 -9
  66. package/.github/workflows/test.yml +0 -59
  67. package/dist/chunk-2IOB6HHT.js +0 -1
  68. package/dist/chunk-2WHCDP7V.mjs +0 -1
  69. package/dist/chunk-37WOWEJG.js +0 -1
  70. package/dist/chunk-44VU4TSP.mjs +0 -1
  71. package/dist/chunk-4LMFQECS.js +0 -1
  72. package/dist/chunk-BXFEQ6KP.js +0 -1
  73. package/dist/chunk-MFQKGZI4.js +0 -1
  74. package/dist/chunk-NJERBWND.mjs +0 -1
  75. package/dist/chunk-WMLIOPUC.mjs +0 -1
  76. package/dist/chunk-XMEZUDF6.mjs +0 -1
package/README_DEPTH.md CHANGED
@@ -43,8 +43,8 @@ npm install @mui/material @mui/icons-material react react-dom
43
43
  El `SessionProvider` es el punto de entrada principal para manejar la autenticación y sesiones en tu aplicación:
44
44
 
45
45
  ```tsx
46
- import React from "react";
47
- import { SessionProvider } from "@nocios/crudify-components";
46
+ import React from 'react';
47
+ import { SessionProvider } from '@nocios/crudify-components';
48
48
 
49
49
  function App() {
50
50
  return (
@@ -52,12 +52,12 @@ function App() {
52
52
  options={{
53
53
  // Configuración automática basada en variables de entorno
54
54
  // o configuración manual si es necesario
55
- storageType: "localStorage", // 'localStorage' | 'sessionStorage'
55
+ storageType: 'localStorage', // 'localStorage' | 'sessionStorage'
56
56
  onSessionExpired: () => {
57
- console.log("Sesión expirada");
57
+ console.log('Sesión expirada');
58
58
  },
59
59
  onSessionRestored: (tokens) => {
60
- console.log("Sesión restaurada:", tokens);
60
+ console.log('Sesión restaurada:', tokens);
61
61
  },
62
62
  }}
63
63
  >
@@ -84,17 +84,17 @@ REACT_APP_CRUDIFY_ENV=dev # dev | stg | api | prod
84
84
  Hook principal para manejo completo de autenticación:
85
85
 
86
86
  ```tsx
87
- import { useAuth } from "@nocios/crudify-components";
87
+ import { useAuth } from '@nocios/crudify-components';
88
88
 
89
89
  function LoginPage() {
90
90
  const { login, logout, isAuthenticated, isLoading, error, clearError } = useAuth();
91
91
 
92
92
  const handleLogin = async () => {
93
93
  try {
94
- const result = await login("user@example.com", "password");
95
- console.log("Login exitoso:", result);
94
+ const result = await login('user@example.com', 'password');
95
+ console.log('Login exitoso:', result);
96
96
  } catch (error) {
97
- console.error("Error de login:", error);
97
+ console.error('Error de login:', error);
98
98
  }
99
99
  };
100
100
 
@@ -108,7 +108,7 @@ function LoginPage() {
108
108
  <button onClick={handleLogout}>Cerrar Sesión</button>
109
109
  ) : (
110
110
  <button onClick={handleLogin} disabled={isLoading}>
111
- {isLoading ? "Iniciando..." : "Iniciar Sesión"}
111
+ {isLoading ? 'Iniciando...' : 'Iniciar Sesión'}
112
112
  </button>
113
113
  )}
114
114
  {error && <div>Error: {error}</div>}
@@ -130,7 +130,7 @@ function LoginPage() {
130
130
  Hook para obtener y manejar los datos completos del usuario:
131
131
 
132
132
  ```tsx
133
- import { useUserData } from "@nocios/crudify-components";
133
+ import { useUserData } from '@nocios/crudify-components';
134
134
 
135
135
  function UserProfile() {
136
136
  const { userData, sessionData, isLoading, error, refetch } = useUserData({
@@ -176,54 +176,54 @@ function UserProfile() {
176
176
  Hook para realizar operaciones CRUD type-safe:
177
177
 
178
178
  ```tsx
179
- import { useData } from "@nocios/crudify-components";
179
+ import { useData } from '@nocios/crudify-components';
180
180
 
181
181
  function ProductManager() {
182
182
  const { create, read, update, remove, isInitialized } = useData();
183
183
 
184
184
  const createProduct = async () => {
185
185
  try {
186
- const result = await create("products", {
187
- name: "Nuevo Producto",
186
+ const result = await create('products', {
187
+ name: 'Nuevo Producto',
188
188
  price: 99.99,
189
- category: "electronics",
189
+ category: 'electronics',
190
190
  });
191
- console.log("Producto creado:", result);
191
+ console.log('Producto creado:', result);
192
192
  } catch (error) {
193
- console.error("Error creando producto:", error);
193
+ console.error('Error creando producto:', error);
194
194
  }
195
195
  };
196
196
 
197
197
  const getProducts = async () => {
198
198
  try {
199
- const products = await read("products", {
200
- filters: { category: "electronics" },
199
+ const products = await read('products', {
200
+ filters: { category: 'electronics' },
201
201
  limit: 10,
202
202
  offset: 0,
203
203
  });
204
- console.log("Productos:", products);
204
+ console.log('Productos:', products);
205
205
  } catch (error) {
206
- console.error("Error obteniendo productos:", error);
206
+ console.error('Error obteniendo productos:', error);
207
207
  }
208
208
  };
209
209
 
210
210
  const updateProduct = async (productId: string) => {
211
211
  try {
212
- const result = await update("products", productId, {
212
+ const result = await update('products', productId, {
213
213
  price: 89.99,
214
214
  });
215
- console.log("Producto actualizado:", result);
215
+ console.log('Producto actualizado:', result);
216
216
  } catch (error) {
217
- console.error("Error actualizando producto:", error);
217
+ console.error('Error actualizando producto:', error);
218
218
  }
219
219
  };
220
220
 
221
221
  const deleteProduct = async (productId: string) => {
222
222
  try {
223
- await remove("products", productId);
224
- console.log("Producto eliminado");
223
+ await remove('products', productId);
224
+ console.log('Producto eliminado');
225
225
  } catch (error) {
226
- console.error("Error eliminando producto:", error);
226
+ console.error('Error eliminando producto:', error);
227
227
  }
228
228
  };
229
229
 
@@ -233,8 +233,8 @@ function ProductManager() {
233
233
  <div>
234
234
  <button onClick={createProduct}>Crear Producto</button>
235
235
  <button onClick={getProducts}>Obtener Productos</button>
236
- <button onClick={() => updateProduct("123")}>Actualizar</button>
237
- <button onClick={() => deleteProduct("123")}>Eliminar</button>
236
+ <button onClick={() => updateProduct('123')}>Actualizar</button>
237
+ <button onClick={() => deleteProduct('123')}>Eliminar</button>
238
238
  </div>
239
239
  );
240
240
  }
@@ -252,22 +252,23 @@ function ProductManager() {
252
252
  Hook de bajo nivel para control directo de sesiones:
253
253
 
254
254
  ```tsx
255
- import { useSession } from "@nocios/crudify-components";
255
+ import { useSession } from '@nocios/crudify-components';
256
256
 
257
257
  function SessionManager() {
258
- const { isAuthenticated, isLoading, tokens, error, login, logout, refreshTokens, isExpiringSoon, expiresIn } = useSession({
259
- onSessionExpired: () => console.log("Sesión expirada"),
260
- onSessionRestored: (tokens) => console.log("Sesión restaurada", tokens),
261
- onTokensRefreshed: (tokens) => console.log("Tokens renovados", tokens),
262
- });
258
+ const { isAuthenticated, isLoading, tokens, error, login, logout, refreshTokens, isExpiringSoon, expiresIn } =
259
+ useSession({
260
+ onSessionExpired: () => console.log('Sesión expirada'),
261
+ onSessionRestored: (tokens) => console.log('Sesión restaurada', tokens),
262
+ onTokensRefreshed: (tokens) => console.log('Tokens renovados', tokens),
263
+ });
263
264
 
264
265
  return (
265
266
  <div>
266
267
  <h3>Estado de Sesión</h3>
267
- <p>Autenticado: {isAuthenticated ? "" : "No"}</p>
268
- <p>Cargando: {isLoading ? "" : "No"}</p>
268
+ <p>Autenticado: {isAuthenticated ? '' : 'No'}</p>
269
+ <p>Cargando: {isLoading ? '' : 'No'}</p>
269
270
  <p>Expira en: {Math.floor(expiresIn / 60)} minutos</p>
270
- <p>Expira pronto: {isExpiringSoon ? "" : "No"}</p>
271
+ <p>Expira pronto: {isExpiringSoon ? '' : 'No'}</p>
271
272
 
272
273
  {isExpiringSoon && <button onClick={() => refreshTokens()}>Renovar Tokens</button>}
273
274
 
@@ -287,7 +288,7 @@ function SessionManager() {
287
288
  Hook para acceder al contexto global de sesión:
288
289
 
289
290
  ```tsx
290
- import { useSessionContext } from "@nocios/crudify-components";
291
+ import { useSessionContext } from '@nocios/crudify-components';
291
292
 
292
293
  function NavigationBar() {
293
294
  const { isAuthenticated, sessionData, logout, getTokenInfo } = useSessionContext();
@@ -311,26 +312,26 @@ function NavigationBar() {
311
312
  Componente completo de autenticación con múltiples pantallas:
312
313
 
313
314
  ```tsx
314
- import { CrudifyLogin } from "@nocios/crudify-components";
315
- import type { CrudifyLoginConfig } from "@nocios/crudify-components";
315
+ import { CrudifyLogin } from '@nocios/crudify-components';
316
+ import type { CrudifyLoginConfig } from '@nocios/crudify-components';
316
317
 
317
318
  function AuthPage() {
318
319
  const config: CrudifyLoginConfig = {
319
- appName: "Mi Aplicación",
320
- logo: "/logo.png",
320
+ appName: 'Mi Aplicación',
321
+ logo: '/logo.png',
321
322
  colors: {
322
- primaryColor: "#1066BA",
323
- bgColor: "#f5f5f5",
323
+ primaryColor: '#1066BA',
324
+ bgColor: '#f5f5f5',
324
325
  },
325
326
  };
326
327
 
327
328
  const handleLoginSuccess = (userData, redirectUrl) => {
328
- console.log("Login exitoso:", userData);
329
+ console.log('Login exitoso:', userData);
329
330
  // Redireccionar o actualizar estado
330
331
  };
331
332
 
332
333
  const handleError = (error: string) => {
333
- console.error("Error de autenticación:", error);
334
+ console.error('Error de autenticación:', error);
334
335
  };
335
336
 
336
337
  return (
@@ -367,7 +368,7 @@ function AuthPage() {
367
368
  Componente para mostrar información del perfil del usuario:
368
369
 
369
370
  ```tsx
370
- import { UserProfileDisplay } from "@nocios/crudify-components";
371
+ import { UserProfileDisplay } from '@nocios/crudify-components';
371
372
 
372
373
  function ProfilePage() {
373
374
  return (
@@ -384,7 +385,7 @@ function ProfilePage() {
384
385
  Componente para proteger rutas que requieren autenticación:
385
386
 
386
387
  ```tsx
387
- import { ProtectedRoute } from "@nocios/crudify-components";
388
+ import { ProtectedRoute } from '@nocios/crudify-components';
388
389
 
389
390
  function App() {
390
391
  return (
@@ -410,7 +411,7 @@ function App() {
410
411
  Componente útil para desarrollo y debug:
411
412
 
412
413
  ```tsx
413
- import { SessionDebugInfo } from "@nocios/crudify-components";
414
+ import { SessionDebugInfo } from '@nocios/crudify-components';
414
415
 
415
416
  function DevPage() {
416
417
  return (
@@ -427,32 +428,32 @@ function DevPage() {
427
428
  Componente completo para configurar políticas de acceso a datos públicos:
428
429
 
429
430
  ```tsx
430
- import { Policies, POLICY_ACTIONS, PREFERRED_POLICY_ORDER, PolicyAction } from "@nocios/crudify-components";
431
- import { useState } from "react";
431
+ import { Policies, POLICY_ACTIONS, PREFERRED_POLICY_ORDER, PolicyAction } from '@nocios/crudify-components';
432
+ import { useState } from 'react';
432
433
 
433
434
  function ModuleConfiguration() {
434
435
  const [policies, setPolicies] = useState([
435
436
  {
436
- id: "1",
437
- action: "read",
437
+ id: '1',
438
+ action: 'read',
438
439
  fields: {
439
- allow: ["name", "email"],
440
- owner_allow: ["phone"],
441
- deny: ["password", "internal_notes"],
440
+ allow: ['name', 'email'],
441
+ owner_allow: ['phone'],
442
+ deny: ['password', 'internal_notes'],
442
443
  },
443
444
  },
444
445
  {
445
- id: "2",
446
- action: "create",
446
+ id: '2',
447
+ action: 'create',
447
448
  fields: {
448
- allow: ["name", "email"],
449
+ allow: ['name', 'email'],
449
450
  owner_allow: [],
450
- deny: ["status", "verified"],
451
+ deny: ['status', 'verified'],
451
452
  },
452
453
  },
453
454
  ]);
454
455
 
455
- const availableFields = ["name", "email", "phone", "address", "status", "verified", "password", "internal_notes"];
456
+ const availableFields = ['name', 'email', 'phone', 'address', 'status', 'verified', 'password', 'internal_notes'];
456
457
 
457
458
  const handlePolicyChange = (newPolicies) => {
458
459
  setPolicies(newPolicies);
@@ -472,7 +473,7 @@ function ModuleConfiguration() {
472
473
  />
473
474
 
474
475
  {/* Mostrar acciones disponibles */}
475
- <div style={{ marginTop: "20px" }}>
476
+ <div style={{ marginTop: '20px' }}>
476
477
  <h3>Acciones Disponibles:</h3>
477
478
  <ul>
478
479
  {POLICY_ACTIONS.map((action) => (
@@ -518,11 +519,11 @@ type Policy = {
518
519
 
519
520
  ```tsx
520
521
  // Acciones de política disponibles
521
- export const POLICY_ACTIONS = ["create", "read", "update", "delete"] as const;
522
+ export const POLICY_ACTIONS = ['create', 'read', 'update', 'delete'] as const;
522
523
  export type PolicyAction = (typeof POLICY_ACTIONS)[number];
523
524
 
524
525
  // Orden preferido para mostrar las políticas
525
- export const PREFERRED_POLICY_ORDER: PolicyAction[] = ["create", "read", "update", "delete"];
526
+ export const PREFERRED_POLICY_ORDER: PolicyAction[] = ['create', 'read', 'update', 'delete'];
526
527
  ```
527
528
 
528
529
  #### Casos de Uso Comunes
@@ -532,12 +533,12 @@ export const PREFERRED_POLICY_ORDER: PolicyAction[] = ["create", "read", "update
532
533
  ```tsx
533
534
  // Permitir lectura pública de ciertos campos
534
535
  const publicReadPolicy = {
535
- id: "public-read",
536
- action: "read",
536
+ id: 'public-read',
537
+ action: 'read',
537
538
  fields: {
538
- allow: ["name", "description", "published_at"],
539
- owner_allow: ["views", "stats"],
540
- deny: ["internal_notes", "admin_flags"],
539
+ allow: ['name', 'description', 'published_at'],
540
+ owner_allow: ['views', 'stats'],
541
+ deny: ['internal_notes', 'admin_flags'],
541
542
  },
542
543
  };
543
544
  ```
@@ -547,12 +548,12 @@ const publicReadPolicy = {
547
548
  ```tsx
548
549
  // Permitir creación pero restringir ciertos campos
549
550
  const createPolicy = {
550
- id: "public-create",
551
- action: "create",
551
+ id: 'public-create',
552
+ action: 'create',
552
553
  fields: {
553
- allow: ["title", "content", "category"],
554
+ allow: ['title', 'content', 'category'],
554
555
  owner_allow: [],
555
- deny: ["status", "featured", "admin_verified"],
556
+ deny: ['status', 'featured', 'admin_verified'],
556
557
  },
557
558
  };
558
559
  ```
@@ -562,9 +563,9 @@ const createPolicy = {
562
563
  ```tsx
563
564
  // Solo el propietario puede eliminar
564
565
  const deletePolicy = {
565
- id: "owner-delete",
566
- action: "delete",
567
- permission: "owner", // '*' = todos, 'deny' = nadie, 'owner' = solo propietario
566
+ id: 'owner-delete',
567
+ action: 'delete',
568
+ permission: 'owner', // '*' = todos, 'deny' = nadie, 'owner' = solo propietario
568
569
  };
569
570
  ```
570
571
 
@@ -582,12 +583,12 @@ function PolicyManager() {
582
583
  // Error de campo específico
583
584
  if (error.fieldErrors) {
584
585
  setErrors({
585
- _error: "Hay errores en la configuración",
586
+ _error: 'Hay errores en la configuración',
586
587
  ...error.fieldErrors,
587
588
  });
588
589
  } else {
589
590
  // Error general
590
- setErrors("Error al guardar las políticas");
591
+ setErrors('Error al guardar las políticas');
591
592
  }
592
593
  }
593
594
  };
@@ -646,19 +647,19 @@ El componente usa react-i18next para internacionalización. Las claves de traduc
646
647
  Utilidades para trabajar con tokens JWT:
647
648
 
648
649
  ```tsx
649
- import { decodeJwtSafely, getCurrentUserEmail, isTokenExpired } from "@nocios/crudify-components";
650
+ import { decodeJwtSafely, getCurrentUserEmail, isTokenExpired } from '@nocios/crudify-components';
650
651
 
651
652
  // Decodificar JWT de forma segura
652
653
  const payload = decodeJwtSafely(token);
653
- console.log("Usuario ID:", payload?.sub);
654
+ console.log('Usuario ID:', payload?.sub);
654
655
 
655
656
  // Obtener email del usuario actual
656
657
  const email = getCurrentUserEmail();
657
- console.log("Email:", email);
658
+ console.log('Email:', email);
658
659
 
659
660
  // Verificar si un token está expirado
660
661
  const expired = isTokenExpired(token);
661
- console.log("Token expirado:", expired);
662
+ console.log('Token expirado:', expired);
662
663
  ```
663
664
 
664
665
  ### Token Storage
@@ -666,19 +667,19 @@ console.log("Token expirado:", expired);
666
667
  Sistema de almacenamiento seguro para tokens:
667
668
 
668
669
  ```tsx
669
- import { TokenStorage } from "@nocios/crudify-components";
670
- import type { TokenData, StorageType } from "@nocios/crudify-components";
670
+ import { TokenStorage } from '@nocios/crudify-components';
671
+ import type { TokenData, StorageType } from '@nocios/crudify-components';
671
672
 
672
673
  // Crear instancia de storage
673
674
  const storage = new TokenStorage({
674
- type: "localStorage", // 'localStorage' | 'sessionStorage'
675
- encryptionKey: "mi-clave-secreta",
675
+ type: 'localStorage', // 'localStorage' | 'sessionStorage'
676
+ encryptionKey: 'mi-clave-secreta',
676
677
  });
677
678
 
678
679
  // Guardar tokens
679
680
  const tokens: TokenData = {
680
- accessToken: "access_token_here",
681
- refreshToken: "refresh_token_here",
681
+ accessToken: 'access_token_here',
682
+ refreshToken: 'refresh_token_here',
682
683
  expiresIn: 3600,
683
684
  refreshExpiresIn: 86400,
684
685
  };
@@ -687,14 +688,14 @@ await storage.setTokens(tokens);
687
688
 
688
689
  // Obtener tokens
689
690
  const storedTokens = await storage.getTokens();
690
- console.log("Tokens almacenados:", storedTokens);
691
+ console.log('Tokens almacenados:', storedTokens);
691
692
 
692
693
  // Limpiar tokens
693
694
  await storage.clearTokens();
694
695
 
695
696
  // Verificar validez
696
697
  const isValid = await storage.isValid();
697
- console.log("Tokens válidos:", isValid);
698
+ console.log('Tokens válidos:', isValid);
698
699
  ```
699
700
 
700
701
  ### Error Handler
@@ -702,8 +703,14 @@ console.log("Tokens válidos:", isValid);
702
703
  Sistema completo de manejo de errores:
703
704
 
704
705
  ```tsx
705
- import { handleCrudifyError, parseApiError, parseTransactionError, getErrorMessage, ERROR_CODES } from "@nocios/crudify-components";
706
- import type { ParsedError, ErrorCode } from "@nocios/crudify-components";
706
+ import {
707
+ handleCrudifyError,
708
+ parseApiError,
709
+ parseTransactionError,
710
+ getErrorMessage,
711
+ ERROR_CODES,
712
+ } from '@nocios/crudify-components';
713
+ import type { ParsedError, ErrorCode } from '@nocios/crudify-components';
707
714
 
708
715
  // Manejo universal de errores
709
716
  try {
@@ -711,10 +718,10 @@ try {
711
718
  } catch (error) {
712
719
  const parsedError: ParsedError = handleCrudifyError(error);
713
720
 
714
- console.log("Tipo:", parsedError.type);
715
- console.log("Código:", parsedError.code);
716
- console.log("Mensaje:", parsedError.message);
717
- console.log("Severidad:", parsedError.severity);
721
+ console.log('Tipo:', parsedError.type);
722
+ console.log('Código:', parsedError.code);
723
+ console.log('Mensaje:', parsedError.message);
724
+ console.log('Severidad:', parsedError.severity);
718
725
 
719
726
  // Mostrar mensaje apropiado al usuario
720
727
  const userMessage = getErrorMessage(parsedError.code as ErrorCode);
@@ -728,7 +735,7 @@ const apiError = parseApiError(response);
728
735
  const transactionError = parseTransactionError(response);
729
736
 
730
737
  // Códigos de error disponibles
731
- console.log("Códigos disponibles:", ERROR_CODES);
738
+ console.log('Códigos disponibles:', ERROR_CODES);
732
739
  ```
733
740
 
734
741
  ## 🔄 API de Crudify Browser
@@ -736,12 +743,12 @@ console.log("Códigos disponibles:", ERROR_CODES);
736
743
  Esta librería re-exporta completamente `@nocios/crudify-sdk`, proporcionando una API unificada:
737
744
 
738
745
  ```tsx
739
- import { crudify } from "@nocios/crudify-components";
746
+ import { crudify } from '@nocios/crudify-components';
740
747
  // Equivale a: import crudify from '@nocios/crudify-sdk';
741
748
 
742
749
  // Todas las funcionalidades de crudify-browser están disponibles
743
- const result = await crudify.create("users", userData);
744
- const users = await crudify.read("users");
750
+ const result = await crudify.create('users', userData);
751
+ const users = await crudify.read('users');
745
752
  ```
746
753
 
747
754
  Esto significa que puedes:
@@ -778,7 +785,7 @@ import type {
778
785
  TokenData,
779
786
  ParsedError,
780
787
  ErrorCode,
781
- } from "@nocios/crudify-components";
788
+ } from '@nocios/crudify-components';
782
789
 
783
790
  // Ejemplo con tipos
784
791
  const handleLogin = async (credentials: LoginRequest): Promise<CrudifyApiResponse<LoginResponse>> => {
@@ -848,33 +855,33 @@ import { CrudifyLogin } from '@nocios/crudify-components';
848
855
  ### SessionProvider con opciones completas
849
856
 
850
857
  ```tsx
851
- import { SessionProvider } from "@nocios/crudify-components";
852
- import type { UseSessionOptions } from "@nocios/crudify-components";
858
+ import { SessionProvider } from '@nocios/crudify-components';
859
+ import type { UseSessionOptions } from '@nocios/crudify-components';
853
860
 
854
861
  const sessionOptions: UseSessionOptions = {
855
862
  // Tipo de almacenamiento
856
- storageType: "localStorage", // 'localStorage' | 'sessionStorage'
863
+ storageType: 'localStorage', // 'localStorage' | 'sessionStorage'
857
864
 
858
865
  // Callbacks del ciclo de vida
859
866
  onSessionExpired: () => {
860
- console.log("Sesión expirada - redirigir a login");
861
- window.location.href = "/login";
867
+ console.log('Sesión expirada - redirigir a login');
868
+ window.location.href = '/login';
862
869
  },
863
870
 
864
871
  onSessionRestored: (tokens) => {
865
- console.log("Sesión restaurada exitosamente", tokens);
872
+ console.log('Sesión restaurada exitosamente', tokens);
866
873
  },
867
874
 
868
875
  onTokensRefreshed: (tokens) => {
869
- console.log("Tokens renovados", tokens);
876
+ console.log('Tokens renovados', tokens);
870
877
  },
871
878
 
872
879
  onLogout: () => {
873
- console.log("Usuario cerró sesión");
880
+ console.log('Usuario cerró sesión');
874
881
  },
875
882
 
876
883
  onError: (error) => {
877
- console.error("Error de sesión:", error);
884
+ console.error('Error de sesión:', error);
878
885
  },
879
886
  };
880
887
 
@@ -894,13 +901,13 @@ function App() {
894
901
  Si necesitas configuración manual en lugar de variables de entorno:
895
902
 
896
903
  ```tsx
897
- import { crudify } from "@nocios/crudify-components";
904
+ import { crudify } from '@nocios/crudify-components';
898
905
 
899
906
  // Configurar manualmente antes de usar
900
907
  crudify.configure({
901
- publicApiKey: "tu-api-key",
902
- environment: "production", // 'dev' | 'stg' | 'api' | 'prod'
903
- baseUrl: "https://api.tudominio.com", // opcional
908
+ publicApiKey: 'tu-api-key',
909
+ environment: 'production', // 'dev' | 'stg' | 'api' | 'prod'
910
+ baseUrl: 'https://api.tudominio.com', // opcional
904
911
  });
905
912
  ```
906
913
 
@@ -909,9 +916,9 @@ crudify.configure({
909
916
  ### Aplicación básica con autenticación
910
917
 
911
918
  ```tsx
912
- import React from "react";
913
- import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
914
- import { SessionProvider, ProtectedRoute, CrudifyLogin, useSessionContext } from "@nocios/crudify-components";
919
+ import React from 'react';
920
+ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
921
+ import { SessionProvider, ProtectedRoute, CrudifyLogin, useSessionContext } from '@nocios/crudify-components';
915
922
 
916
923
  // Componente Dashboard
917
924
  function Dashboard() {
@@ -929,14 +936,14 @@ function Dashboard() {
929
936
  // Componente Login
930
937
  function LoginPage() {
931
938
  return (
932
- <div style={{ maxWidth: "400px", margin: "0 auto", padding: "2rem" }}>
939
+ <div style={{ maxWidth: '400px', margin: '0 auto', padding: '2rem' }}>
933
940
  <CrudifyLogin
934
941
  config={{
935
- appName: "Mi App",
936
- colors: { primaryColor: "#1976d2" },
942
+ appName: 'Mi App',
943
+ colors: { primaryColor: '#1976d2' },
937
944
  }}
938
945
  onLoginSuccess={(userData) => {
939
- console.log("Login exitoso:", userData);
946
+ console.log('Login exitoso:', userData);
940
947
  // La navegación se maneja automáticamente
941
948
  }}
942
949
  />
@@ -972,8 +979,8 @@ export default App;
972
979
  ### Aplicación con operaciones CRUD
973
980
 
974
981
  ```tsx
975
- import React, { useState, useEffect } from "react";
976
- import { useData, useUserData } from "@nocios/crudify-components";
982
+ import React, { useState, useEffect } from 'react';
983
+ import { useData, useUserData } from '@nocios/crudify-components';
977
984
 
978
985
  function ProductManager() {
979
986
  const [products, setProducts] = useState([]);
@@ -990,13 +997,13 @@ function ProductManager() {
990
997
  const loadProducts = async () => {
991
998
  setLoading(true);
992
999
  try {
993
- const result = await read("products", {
1000
+ const result = await read('products', {
994
1001
  filters: { userId: userData?.id },
995
1002
  limit: 20,
996
1003
  });
997
1004
  setProducts(result.data || []);
998
1005
  } catch (error) {
999
- console.error("Error cargando productos:", error);
1006
+ console.error('Error cargando productos:', error);
1000
1007
  } finally {
1001
1008
  setLoading(false);
1002
1009
  }
@@ -1004,31 +1011,31 @@ function ProductManager() {
1004
1011
 
1005
1012
  const createProduct = async (productData) => {
1006
1013
  try {
1007
- await create("products", {
1014
+ await create('products', {
1008
1015
  ...productData,
1009
1016
  userId: userData?.id,
1010
1017
  });
1011
1018
  await loadProducts(); // Recargar lista
1012
1019
  } catch (error) {
1013
- console.error("Error creando producto:", error);
1020
+ console.error('Error creando producto:', error);
1014
1021
  }
1015
1022
  };
1016
1023
 
1017
1024
  const updateProduct = async (productId, updates) => {
1018
1025
  try {
1019
- await update("products", productId, updates);
1026
+ await update('products', productId, updates);
1020
1027
  await loadProducts(); // Recargar lista
1021
1028
  } catch (error) {
1022
- console.error("Error actualizando producto:", error);
1029
+ console.error('Error actualizando producto:', error);
1023
1030
  }
1024
1031
  };
1025
1032
 
1026
1033
  const deleteProduct = async (productId) => {
1027
1034
  try {
1028
- await remove("products", productId);
1035
+ await remove('products', productId);
1029
1036
  await loadProducts(); // Recargar lista
1030
1037
  } catch (error) {
1031
- console.error("Error eliminando producto:", error);
1038
+ console.error('Error eliminando producto:', error);
1032
1039
  }
1033
1040
  };
1034
1041
 
@@ -1041,7 +1048,7 @@ function ProductManager() {
1041
1048
  <button
1042
1049
  onClick={() =>
1043
1050
  createProduct({
1044
- name: "Nuevo Producto",
1051
+ name: 'Nuevo Producto',
1045
1052
  price: 99.99,
1046
1053
  })
1047
1054
  }
@@ -1091,7 +1098,7 @@ function App() {
1091
1098
  return (
1092
1099
  <SessionProvider
1093
1100
  options={{
1094
- storageType: "localStorage", // Más persistente
1101
+ storageType: 'localStorage', // Más persistente
1095
1102
  onSessionExpired: handleSessionExpired,
1096
1103
  }}
1097
1104
  >
@@ -1109,7 +1116,7 @@ function App() {
1109
1116
  const { error, clearError } = useAuth();
1110
1117
  useEffect(() => {
1111
1118
  if (error) {
1112
- console.error("Error de auth:", error);
1119
+ console.error('Error de auth:', error);
1113
1120
  // Mostrar notificación al usuario
1114
1121
  clearError();
1115
1122
  }
@@ -1121,8 +1128,8 @@ useEffect(() => {
1121
1128
  // Opcional: limpiar datos sensibles
1122
1129
  };
1123
1130
 
1124
- window.addEventListener("beforeunload", handleBeforeUnload);
1125
- return () => window.removeEventListener("beforeunload", handleBeforeUnload);
1131
+ window.addEventListener('beforeunload', handleBeforeUnload);
1132
+ return () => window.removeEventListener('beforeunload', handleBeforeUnload);
1126
1133
  }, []);
1127
1134
  ```
1128
1135
 
@@ -1156,7 +1163,7 @@ useEffect(() => {
1156
1163
  ```tsx
1157
1164
  // Verificar configuración de environment
1158
1165
  crudify.configure({
1159
- environment: "dev", // Asegurar environment correcto
1166
+ environment: 'dev', // Asegurar environment correcto
1160
1167
  publicApiKey: process.env.REACT_APP_CRUDIFY_PUBLIC_API_KEY,
1161
1168
  });
1162
1169
  ```
@@ -1165,11 +1172,11 @@ crudify.configure({
1165
1172
 
1166
1173
  ```tsx
1167
1174
  // Usar SessionDebugInfo para desarrollo
1168
- import { SessionDebugInfo } from "@nocios/crudify-components";
1175
+ import { SessionDebugInfo } from '@nocios/crudify-components';
1169
1176
 
1170
1177
  function DevPanel() {
1171
- return process.env.NODE_ENV === "development" ? (
1172
- <div style={{ position: "fixed", bottom: 0, right: 0, background: "white", padding: "1rem" }}>
1178
+ return process.env.NODE_ENV === 'development' ? (
1179
+ <div style={{ position: 'fixed', bottom: 0, right: 0, background: 'white', padding: '1rem' }}>
1173
1180
  <SessionDebugInfo />
1174
1181
  </div>
1175
1182
  ) : null;