@nocios/crudify-ui 1.3.1 → 1.3.2

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/README.md CHANGED
@@ -1,1244 +1,291 @@
1
- # @nocios/crudify-ui - Sistema Completo de Autenticación y CRUD
1
+ # @nocios/crudify-ui
2
2
 
3
- > Biblioteca completa de componentes React para autenticación y operaciones CRUD, basada en la API de Crudify con sistema de inicialización robusto y gestión unificada de estado.
3
+ [![npm version](https://badge.fury.io/js/%40nocios%2Fcrudify-ui.svg)](https://badge.fury.io/js/%40nocios%2Fcrudify-ui)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
5
 
5
- ## 🚀 Instalación
6
+ Biblioteca completa de componentes UI y hooks de React para aplicaciones Crudify. Proporciona autenticación moderna, manejo de sesiones y una API unificada.
6
7
 
7
- ```bash
8
- npm install @nocios/crudify-ui
9
- ```
8
+ ## 🚀 Características
10
9
 
11
- ## 📋 Dependencias Requeridas
10
+ - **🔐 Autenticación Moderna**: Sistema completo con Refresh Token Pattern
11
+ - **⚡ Manejo de Sesiones**: Persistencia automática y sincronización cross-tab
12
+ - **🎨 Componentes UI**: Componentes React listos con Material-UI
13
+ - **🪝 React Hooks**: Hooks especializados para CRUD y autenticación
14
+ - **🔄 API Unificada**: Re-exporta completamente @nocios/crudify-browser
15
+ - **🌍 Internacionalización**: Soporte para múltiples idiomas
16
+ - **📱 TypeScript**: Completamente tipado
12
17
 
13
- Asegúrate de tener estas dependencias en tu proyecto:
18
+ ## 📦 Instalación
14
19
 
15
20
  ```bash
16
- npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled
17
- ```
21
+ npm install @nocios/crudify-ui
18
22
 
19
- ## **NUEVO: Sistema Unificado (Recomendado)**
23
+ # Peer dependencies
24
+ npm install @mui/material @mui/icons-material react react-dom
25
+ ```
20
26
 
21
- ### 🏗️ Implementación con CrudifyDataProvider
27
+ ## 🏗️ Configuración Rápida
22
28
 
23
- El nuevo sistema unificado proporciona inicialización robusta, gestión automática de tokens y operaciones CRUD sin configuración compleja:
29
+ ### 1. Configurar SessionProvider
24
30
 
25
31
  ```tsx
26
- // App.tsx - Configuración principal
27
- import React from 'react';
28
- import { CrudifyDataProvider } from '@nocios/crudify-ui';
29
- import AppContent from './AppContent';
32
+ import { SessionProvider } from '@nocios/crudify-ui';
30
33
 
31
34
  function App() {
32
35
  return (
33
- <CrudifyDataProvider
34
- publicApiKey={process.env.REACT_APP_CRUDIFY_API_KEY}
35
- env={process.env.NODE_ENV === 'development' ? 'dev' : 'prod'}
36
- appName="Mi Aplicación"
37
- >
38
- <AppContent />
39
- </CrudifyDataProvider>
36
+ <SessionProvider>
37
+ <YourApp />
38
+ </SessionProvider>
40
39
  );
41
40
  }
42
-
43
- export default App;
44
41
  ```
45
42
 
46
- ```tsx
47
- // AppContent.tsx - Lógica de autenticación
48
- import React from 'react';
49
- import { useCrudifyAuth, CrudifyLogin } from '@nocios/crudify-ui';
50
- import Dashboard from './Dashboard';
43
+ ### 2. Variables de Entorno
51
44
 
52
- function AppContent() {
53
- const { isAuthenticated, token, setToken, user, loading } = useCrudifyAuth();
54
-
55
- if (loading) {
56
- return <div>Cargando...</div>;
57
- }
58
-
59
- if (!isAuthenticated) {
60
- return (
61
- <CrudifyLogin
62
- onLoginSuccess={(newToken) => {
63
- setToken(newToken); // ✅ Sincronización automática
64
- // No necesitas manejar redirección manual
65
- }}
66
- config={{
67
- colors: { primaryColor: "#1066BA" },
68
- loginActions: ["forgotPassword"]
69
- }}
70
- />
71
- );
72
- }
73
-
74
- return <Dashboard user={user} />;
75
- }
76
- ```
77
-
78
- ```tsx
79
- // Dashboard.tsx - Uso de operaciones CRUD
80
- import React, { useEffect, useState } from 'react';
81
- import { useCrudifyData } from '@nocios/crudify-ui';
82
-
83
- function Dashboard({ user }) {
84
- const { readItems, createItem, isReady, loading } = useCrudifyData();
85
- const [items, setItems] = useState([]);
86
-
87
- useEffect(() => {
88
- if (isReady) {
89
- loadItems();
90
- }
91
- }, [isReady]);
92
-
93
- const loadItems = async () => {
94
- try {
95
- const response = await readItems('products', { active: true });
96
- if (response.success) {
97
- setItems(response.data);
98
- }
99
- } catch (error) {
100
- console.error('Error loading items:', error);
101
- }
102
- };
103
-
104
- if (!isReady) {
105
- return <div>Inicializando sistema...</div>;
106
- }
107
-
108
- return (
109
- <div>
110
- <h1>Dashboard - {user?.name}</h1>
111
- <div>
112
- {items.map(item => (
113
- <div key={item.id}>{item.name}</div>
114
- ))}
115
- </div>
116
- </div>
117
- );
118
- }
45
+ ```bash
46
+ # .env
47
+ REACT_APP_CRUDIFY_PUBLIC_API_KEY=tu_api_key
48
+ REACT_APP_CRUDIFY_ENV=dev
119
49
  ```
120
50
 
121
- ## 🔧 **Hooks Disponibles**
122
-
123
- ### 🎯 **useCrudifyAuth** - Gestión de Autenticación
51
+ ## 🪝 Hooks Principales
124
52
 
125
- Hook principal para manejar el estado de autenticación:
53
+ ### useAuth - Autenticación
126
54
 
127
55
  ```tsx
128
- import { useCrudifyAuth } from '@nocios/crudify-ui';
129
-
130
- function AuthComponent() {
131
- const {
132
- isAuthenticated, // boolean - Si el usuario está autenticado
133
- token, // string | null - Token JWT actual
134
- setToken, // (token: string | null) => void - Función para establecer token
135
- user, // object | null - Datos del usuario
136
- loading, // boolean - Estado de carga
137
- login, // (email: string, password: string) => Promise<CrudifyApiResponse>
138
- logout, // () => void - Función para logout
139
- refreshUserData // () => Promise<void> - Actualizar datos del usuario
140
- } = useCrudifyAuth();
141
-
142
- return (
143
- <div>
144
- {isAuthenticated ? (
145
- <div>
146
- <h1>Bienvenido, {user?.name}</h1>
147
- <button onClick={logout}>Cerrar Sesión</button>
148
- </div>
149
- ) : (
150
- <div>No autenticado</div>
151
- )}
152
- </div>
153
- );
154
- }
155
- ```
56
+ import { useAuth } from '@nocios/crudify-ui';
156
57
 
157
- ### 📊 **useCrudifyData** - Operaciones CRUD
58
+ function LoginComponent() {
59
+ const { login, logout, isAuthenticated, isLoading } = useAuth();
158
60
 
159
- Hook para realizar operaciones CRUD con inicialización automática:
160
-
161
- ```tsx
162
- import { useCrudifyData } from '@nocios/crudify-ui';
163
-
164
- function DataComponent() {
165
- const {
166
- readItems, // (moduleKey: string, filter?: object, options?: any) => Promise<CrudifyApiResponse>
167
- readItem, // (moduleKey: string, filter: object, options?: any) => Promise<CrudifyApiResponse>
168
- createItem, // (moduleKey: string, data: object, options?: any) => Promise<CrudifyApiResponse>
169
- updateItem, // (moduleKey: string, data: object, options?: any) => Promise<CrudifyApiResponse>
170
- deleteItem, // (moduleKey: string, id: string, options?: any) => Promise<CrudifyApiResponse>
171
- transaction, // (operations: any[], options?: any) => Promise<CrudifyApiResponse>
172
- getStructure, // (options?: any) => Promise<CrudifyApiResponse>
173
- getStructurePublic,// (options?: any) => Promise<CrudifyApiResponse>
174
- isReady, // boolean - Si el sistema está listo para operaciones
175
- loading // boolean - Estado de carga
176
- } = useCrudifyData();
177
-
178
- const handleLoadProducts = async () => {
179
- if (!isReady) return;
180
-
181
- try {
182
- const response = await readItems('products', { active: true });
183
- if (response.success) {
184
- console.log('Productos:', response.data);
185
- }
186
- } catch (error) {
187
- console.error('Error:', error);
188
- }
61
+ const handleLogin = async () => {
62
+ const result = await login('user@example.com', 'password');
189
63
  };
190
64
 
191
- return (
192
- <div>
193
- <button onClick={handleLoadProducts} disabled={!isReady}>
194
- Cargar Productos
195
- </button>
196
- </div>
65
+ return isAuthenticated ? (
66
+ <button onClick={logout}>Cerrar Sesión</button>
67
+ ) : (
68
+ <button onClick={handleLogin} disabled={isLoading}>
69
+ Iniciar Sesión
70
+ </button>
197
71
  );
198
72
  }
199
73
  ```
200
74
 
201
- ### 👤 **useCrudifyUser** - Gestión de Usuario
202
-
203
- Hook especializado para datos del usuario:
75
+ ### useUserData - Datos del Usuario
204
76
 
205
77
  ```tsx
206
- import { useCrudifyUser } from '@nocios/crudify-ui';
78
+ import { useUserData } from '@nocios/crudify-ui';
207
79
 
208
80
  function UserProfile() {
209
- const {
210
- user, // object | null - Datos del usuario
211
- loading, // boolean - Estado de carga
212
- error, // string | null - Error si existe
213
- refreshUserData, // () => Promise<void> - Actualizar datos
214
- updateUserProfile, // (data: object) => Promise<CrudifyApiResponse>
215
- isReady // boolean - Si está listo
216
- } = useCrudifyUser();
217
-
218
- if (loading) return <div>Cargando perfil...</div>;
219
- if (error) return <div>Error: {error}</div>;
220
-
221
- return (
222
- <div>
223
- <h2>Perfil de Usuario</h2>
224
- <p>Nombre: {user?.name}</p>
225
- <p>Email: {user?.email}</p>
226
- <button onClick={refreshUserData}>Actualizar Datos</button>
227
- </div>
228
- );
229
- }
230
- ```
231
-
232
- ### ⚙️ **useCrudifyConfig** - Acceso a Configuración
233
-
234
- Hook para acceder a la configuración actual:
235
-
236
- ```tsx
237
- import { useCrudifyConfig } from '@nocios/crudify-ui';
81
+ const { userData, sessionData, isLoading } = useUserData();
238
82
 
239
- function ConfigDisplay() {
240
- const {
241
- config, // CrudifyConfig - Configuración actual
242
- updateConfig, // (newConfig: Partial<CrudifyConfig>) => void
243
- isConfigured, // boolean - Si está configurado
244
- environment // string - Entorno actual (dev, prod, etc.)
245
- } = useCrudifyConfig();
83
+ if (isLoading) return <div>Cargando...</div>;
246
84
 
247
85
  return (
248
86
  <div>
249
- <h3>Configuración Actual</h3>
250
- <p>App: {config.appName}</p>
251
- <p>Entorno: {environment}</p>
252
- <p>Configurado: {isConfigured ? 'Sí' : 'No'}</p>
87
+ <h2>{userData?.fullName}</h2>
88
+ <p>Email: {sessionData?.email}</p>
89
+ <img src={userData?.avatar} alt="Avatar" />
253
90
  </div>
254
91
  );
255
92
  }
256
93
  ```
257
94
 
258
- ## ⚙️ **Configuración del CrudifyDataProvider**
259
-
260
- ### Props del Provider
95
+ ### useData - Operaciones CRUD
261
96
 
262
97
  ```tsx
263
- interface CrudifyDataProviderProps {
264
- children: React.ReactNode;
265
-
266
- // Configuración básica
267
- publicApiKey?: string; // Clave de API de Crudify
268
- env?: 'dev' | 'stg' | 'api' | 'prod'; // Entorno
269
- appName?: string; // Nombre de la aplicación
270
-
271
- // Configuración visual
272
- logo?: string; // URL del logo
273
- colors?: {
274
- primaryColor?: string; // Color principal
275
- bgColor?: string; // Color de fondo
276
- [key: string]: string | undefined;
277
- };
98
+ import { useData } from '@nocios/crudify-ui';
278
99
 
279
- // Configuración avanzada
280
- autoReadFromCookies?: boolean; // Leer configuración desde cookies (default: true)
281
- autoInitialize?: boolean; // Inicializar automáticamente (default: true)
282
- persistToken?: boolean; // Persistir token entre sesiones (default: true)
100
+ function ProductManager() {
101
+ const { create, read, update, remove } = useData();
283
102
 
284
- // Configuración de login
285
- loginActions?: string[]; // Acciones disponibles: ["forgotPassword", "createUser"]
286
-
287
- // Callbacks opcionales
288
- onInitialized?: () => void; // Callback cuando se inicializa
289
- onError?: (error: string) => void; // Callback para errores
290
- onTokenChange?: (token: string | null) => void; // Callback cuando cambia el token
291
- }
292
- ```
293
-
294
- ### Ejemplo de Configuración Completa
103
+ const createProduct = async () => {
104
+ await create('products', { name: 'Nuevo Producto', price: 99.99 });
105
+ };
295
106
 
296
- ```tsx
297
- import { CrudifyDataProvider } from '@nocios/crudify-ui';
107
+ const getProducts = async () => {
108
+ const products = await read('products', { limit: 10 });
109
+ };
298
110
 
299
- function App() {
300
111
  return (
301
- <CrudifyDataProvider
302
- // Configuración básica
303
- publicApiKey={process.env.REACT_APP_CRUDIFY_API_KEY}
304
- env={process.env.NODE_ENV === 'development' ? 'dev' : 'prod'}
305
- appName="Mi Aplicación"
306
-
307
- // Configuración visual
308
- logo="/logo.png"
309
- colors={{
310
- primaryColor: "#1066BA",
311
- bgColor: "#f5f5f5"
312
- }}
313
-
314
- // Configuración avanzada
315
- autoReadFromCookies={true}
316
- autoInitialize={true}
317
- persistToken={true}
318
-
319
- // Acciones de login disponibles
320
- loginActions={["forgotPassword", "createUser"]}
321
-
322
- // Callbacks
323
- onInitialized={() => console.log('Sistema inicializado')}
324
- onError={(error) => console.error('Error del sistema:', error)}
325
- onTokenChange={(token) => console.log('Token actualizado:', !!token)}
326
- >
327
- <AppContent />
328
- </CrudifyDataProvider>
112
+ <button onClick={createProduct}>Crear Producto</button>
329
113
  );
330
114
  }
331
115
  ```
332
116
 
333
- ## 🎨 **Componente CrudifyLogin**
334
-
335
- ### Uso Standalone (Sistema Legacy)
117
+ ## 🎨 Componentes UI
336
118
 
337
- Para uso directo del componente de login sin el provider unificado:
119
+ ### CrudifyLogin - Login Completo
338
120
 
339
121
  ```tsx
340
122
  import { CrudifyLogin } from '@nocios/crudify-ui';
341
123
 
342
- function LoginPage() {
343
- const handleLoginSuccess = (token: string, redirectUrl?: string) => {
344
- // ⚠️ Importante: Sincronizar con el sistema global
345
- sessionStorage.setItem("authToken", token);
346
-
347
- // Si usas CrudifyDataProvider, también sincronizar ahí
348
- // setToken(token); // usando useCrudifyAuth
349
-
350
- window.location.href = redirectUrl || "/dashboard";
351
- };
352
-
124
+ function AuthPage() {
353
125
  return (
354
126
  <CrudifyLogin
355
127
  config={{
356
- publicApiKey: "tu-api-key",
357
- env: "prod",
358
- appName: "Mi Aplicación",
128
+ appName: "Mi App",
359
129
  logo: "/logo.png",
360
- colors: {
361
- primaryColor: "#1066BA",
362
- bgColor: "#f5f5f5"
363
- },
364
- loginActions: ["forgotPassword", "createUser"]
130
+ colors: { primaryColor: "#1976d2" }
131
+ }}
132
+ onLoginSuccess={(userData) => {
133
+ console.log('Login exitoso:', userData);
365
134
  }}
366
- onLoginSuccess={handleLoginSuccess}
367
- onError={(error) => console.error('Login error:', error)}
368
- redirectUrl="/dashboard"
369
135
  language="es"
370
136
  />
371
137
  );
372
138
  }
373
139
  ```
374
140
 
375
- ### Props del CrudifyLogin
141
+ ### ProtectedRoute - Protección de Rutas
376
142
 
377
143
  ```tsx
378
- interface CrudifyLoginProps {
379
- // Configuración
380
- config?: CrudifyLoginConfig;
381
-
382
- // Callbacks
383
- onLoginSuccess?: (token: string, redirectUrl?: string) => void;
384
- onError?: (error: string) => void;
385
- onScreenChange?: (screen: string, params?: object) => void;
386
- onExternalNavigate?: (path: string) => void;
387
-
388
- // Navegación
389
- initialScreen?: 'login' | 'forgotPassword' | 'resetPassword' | 'checkCode';
390
- redirectUrl?: string;
391
-
392
- // Personalización
393
- language?: string;
394
- translations?: CrudifyLoginTranslations;
395
- translationsUrl?: string;
396
-
397
- // Opciones
398
- autoReadFromCookies?: boolean;
144
+ import { ProtectedRoute } from '@nocios/crudify-ui';
145
+
146
+ function App() {
147
+ return (
148
+ <ProtectedRoute fallback={<LoginPage />}>
149
+ <Dashboard />
150
+ </ProtectedRoute>
151
+ );
399
152
  }
400
153
  ```
401
154
 
402
- ### Personalización Visual
403
-
404
- ```tsx
405
- <CrudifyLogin
406
- config={{
407
- appName: "Mi App",
408
- logo: "/mi-logo.png",
409
- colors: {
410
- primaryColor: "#1066BA",
411
- bgColor: "#f5f5f5",
412
- textColor: "#333333",
413
- buttonTextColor: "#ffffff"
414
- },
415
- }}
416
- />
417
- ```
155
+ ## 🔄 API de Crudify Browser
418
156
 
419
- ### Traducciones Personalizadas
157
+ Re-exporta completamente `@nocios/crudify-browser`:
420
158
 
421
159
  ```tsx
422
- const misTraduciones = {
423
- "login.title": "Iniciar Sesión",
424
- "login.usernameLabel": "Usuario o Email",
425
- "login.passwordLabel": "Contraseña",
426
- "login.loginButton": "Entrar",
427
- "login.forgotPasswordLink": "¿Olvidaste tu contraseña?",
428
- "forgotPassword.title": "Recuperar Contraseña",
429
- "forgotPassword.emailLabel": "Email",
430
- "forgotPassword.sendButton": "Enviar Código"
431
- };
160
+ import { crudify } from '@nocios/crudify-ui';
432
161
 
433
- <CrudifyLogin
434
- translations={misTraduciones}
435
- language="es"
436
- />;
162
+ // Usar directamente la API de crudify
163
+ const result = await crudify.create('users', userData);
164
+ const users = await crudify.read('users');
437
165
  ```
438
166
 
439
- ## 🛠️ **Utilidades y Funciones de Alto Nivel**
440
-
441
- ### 🔑 **Acceso Directo a la Instancia**
442
-
443
- Para casos avanzados donde necesitas acceso directo a la instancia de crudify:
444
-
445
- ```tsx
446
- import { getCrudifyInstanceAsync, getCrudifyInstanceSync } from '@nocios/crudify-ui';
447
-
448
- // Método asíncrono (recomendado) - espera a que se inicialice
449
- async function loadData() {
450
- try {
451
- const crudifyInstance = await getCrudifyInstanceAsync();
452
- const response = await crudifyInstance.readItems('products', {});
453
- return response;
454
- } catch (error) {
455
- console.error('Error:', error);
456
- }
457
- }
458
-
459
- // Método síncrono - lanza error si no está listo
460
- function quickOperation() {
461
- try {
462
- const crudifyInstance = await getCrudifyInstanceSync();
463
- // Usar solo si estás seguro de que ya está inicializado
464
- return crudifyInstance.readItems('products', {});
465
- } catch (error) {
466
- console.error('Crudify not ready:', error);
467
- }
468
- }
469
- ```
167
+ ## 🛠️ Utilidades
470
168
 
471
- ### 🔐 **Utilidades de Tokens JWT**
169
+ ### JWT Utils
472
170
 
473
171
  ```tsx
474
- import {
475
- getCurrentUserEmail,
476
- decodeJwtSafely,
477
- isTokenExpired,
478
- getTokenPayload
479
- } from '@nocios/crudify-ui';
172
+ import { decodeJwtSafely, getCurrentUserEmail } from '@nocios/crudify-ui';
480
173
 
481
- // Obtener email del usuario actual
482
- const userEmail = getCurrentUserEmail();
483
-
484
- // Decodificar token JWT de forma segura
485
174
  const payload = decodeJwtSafely(token);
486
-
487
- // Verificar si token ha expirado
488
- const expired = isTokenExpired(token);
489
-
490
- // Obtener payload completo con validación
491
- const tokenData = getTokenPayload(token);
492
- console.log('Usuario:', tokenData?.email);
493
- console.log('Expira:', new Date(tokenData?.exp * 1000));
494
- ```
495
-
496
- ### 🔒 **Storage Seguro**
497
-
498
- ```tsx
499
- import {
500
- secureSessionStorage,
501
- secureLocalStorage,
502
- TokenManager
503
- } from '@nocios/crudify-ui';
504
-
505
- // Uso básico del storage seguro
506
- secureSessionStorage.setToken(token);
507
- const currentToken = secureSessionStorage.getToken();
508
- secureSessionStorage.clear();
509
-
510
- // Uso avanzado con TokenManager
511
- const tokenManager = TokenManager.getInstance();
512
-
513
- // Establecer token con sincronización automática
514
- await tokenManager.setToken(token);
515
-
516
- // Obtener token válido (verifica expiración)
517
- const validToken = tokenManager.getValidToken();
518
-
519
- // Limpiar todo
520
- tokenManager.clearToken();
521
-
522
- // Verificar si hay token válido
523
- const hasValidToken = tokenManager.hasValidToken();
175
+ const email = getCurrentUserEmail();
524
176
  ```
525
177
 
526
- ### 🚨 **Manejo de Errores**
178
+ ### Error Handler
527
179
 
528
180
  ```tsx
529
- import {
530
- handleCrudifyError,
531
- ERROR_CODES,
532
- getErrorMessage,
533
- CrudifyErrorHandler
534
- } from '@nocios/crudify-ui';
181
+ import { handleCrudifyError, getErrorMessage } from '@nocios/crudify-ui';
535
182
 
536
- // Manejo básico de errores
537
183
  try {
538
- await readItems('products', {});
184
+ // operación
539
185
  } catch (error) {
540
- const parsedErrors = handleCrudifyError(error);
541
- parsedErrors.forEach((err) => {
542
- console.log(`Error ${err.code}: ${err.message} (Severidad: ${err.severity})`);
543
-
544
- // Diferentes acciones según el código de error
545
- switch (err.code) {
546
- case ERROR_CODES.INVALID_CREDENTIALS:
547
- // Redirigir a login
548
- break;
549
- case ERROR_CODES.NETWORK_ERROR:
550
- // Mostrar mensaje de conectividad
551
- break;
552
- case ERROR_CODES.TOO_MANY_REQUESTS:
553
- // Mostrar mensaje de espera
554
- break;
555
- }
556
- });
186
+ const parsedError = handleCrudifyError(error);
187
+ const userMessage = getErrorMessage(parsedError.code);
557
188
  }
558
-
559
- // Manejo avanzado con categorías
560
- const errorHandler = new CrudifyErrorHandler();
561
- errorHandler.setErrorCallback('authentication', (error) => {
562
- // Manejar errores de autenticación
563
- console.log('Auth error:', error);
564
- });
565
-
566
- errorHandler.setErrorCallback('validation', (error) => {
567
- // Manejar errores de validación
568
- console.log('Validation error:', error);
569
- });
570
189
  ```
571
190
 
572
- ### 🔍 **Hooks Legacy (Compatibilidad)**
573
-
574
- Para migrar desde versiones anteriores:
191
+ ## 🚀 Ejemplo Completo
575
192
 
576
193
  ```tsx
577
- // Hooks nuevos (recomendado)
578
- import { useCrudifyAuth, useCrudifyData, useCrudifyUser } from '@nocios/crudify-ui';
579
-
580
- // ⚠️ Hooks legacy (mantienen compatibilidad)
581
- import { useUserProfile, useCrudifyLogin } from '@nocios/crudify-ui';
582
-
583
- function LegacyComponent() {
584
- // Hook legacy para perfil de usuario
585
- const { userProfile, loading, error, refreshProfile } = useUserProfile();
194
+ import React from 'react';
195
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
196
+ import {
197
+ SessionProvider,
198
+ ProtectedRoute,
199
+ CrudifyLogin,
200
+ useSessionContext
201
+ } from '@nocios/crudify-ui';
586
202
 
587
- if (loading) return <div>Cargando...</div>;
588
- if (error) return <div>Error: {error}</div>;
203
+ function Dashboard() {
204
+ const { sessionData, logout } = useSessionContext();
589
205
 
590
206
  return (
591
207
  <div>
592
- <h1>Bienvenido, {userProfile?.name}</h1>
593
- <p>Email: {userProfile?.email}</p>
594
- <button onClick={refreshProfile}>Actualizar Perfil</button>
208
+ <h1>Dashboard</h1>
209
+ <p>Bienvenido, {sessionData?.email}</p>
210
+ <button onClick={logout}>Cerrar Sesión</button>
595
211
  </div>
596
212
  );
597
213
  }
598
214
 
599
- function LegacyLoginConfig() {
600
- // Hook legacy para configuración
601
- const { config } = useCrudifyLogin({
602
- publicApiKey: "mi-api-key",
603
- env: "prod",
604
- });
605
-
606
- return <div>App: {config.appName}</div>;
607
- }
608
- ```
609
-
610
- ## 📱 Flujos de Autenticacion
611
-
612
- ### 1. Login Estandar
613
-
614
- ```
615
- Usuario ingresa credenciales → Validacion → Token JWT → Redireccion
616
- ```
617
-
618
- ### 2. Recuperacion de Contrasena
619
-
620
- ```
621
- Email → Codigo por email → Validacion → Nueva contrasena → Login automatico
622
- ```
623
-
624
- ### 3. Navegacion entre Pantallas
625
-
626
- ```tsx
627
- const handleScreenChange = (screen, params) => {
628
- console.log(`Cambiando a: ${screen}`, params);
629
- // Logica personalizada de navegacion
630
- };
631
-
632
- <CrudifyLogin onScreenChange={handleScreenChange} />;
633
- ```
634
-
635
- ## 🔐 Integracion con Cookies
636
-
637
- El componente puede leer configuracion automaticamente desde cookies:
638
-
639
- ```javascript
640
- // Configurar desde JavaScript
641
- document.cookie = "publicApiKey=tu-api-key; path=/";
642
- document.cookie = "environment=prod; path=/";
643
- document.cookie = "appName=Mi App; path=/";
644
- document.cookie = 'loginActions=["forgotPassword","createUser"]; path=/';
645
- ```
646
-
647
- ## 🌍 Internacionalizacion
648
-
649
- ### Idiomas Soportados
650
-
651
- - **English** (`en`) - Default
652
- - **Espanol** (`es`)
653
- - **Personalizado** (via props)
654
-
655
- ### Cargar traducciones desde URL
656
-
657
- ```tsx
658
- <CrudifyLogin translationsUrl="/api/translations/es.json" language="es" />
659
- ```
660
-
661
- ## 📊 Integracion con Crudify API
662
-
663
- El componente incluye acceso completo a la API de Crudify:
664
-
665
- ```tsx
666
- import { crudify } from "@nocios/crudify-ui";
667
-
668
- // Usar directamente en tu app
669
- const usuarios = await crudify.users.list();
670
- const perfil = await crudify.users.profile();
671
- ```
672
-
673
- ## 🚨 Manejo de Errores
674
-
675
- ### Codigos de Error Estandar
676
-
677
- | Codigo | Descripcion | Severidad |
678
- | ----------------------- | ------------------------ | --------- |
679
- | `INVALID_CREDENTIALS` | Credenciales incorrectas | warning |
680
- | `TOO_MANY_REQUESTS` | Demasiados intentos | warning |
681
- | `NETWORK_ERROR` | Error de conexion | error |
682
- | `INTERNAL_SERVER_ERROR` | Error del servidor | error |
683
-
684
- ### Callback de Errores
685
-
686
- ```tsx
687
- const handleError = (error: string) => {
688
- // Notificar al usuario
689
- toast.error(error);
690
-
691
- // Log para debugging
692
- console.error("Login error:", error);
693
-
694
- // Analytics
695
- analytics.track("login_error", { error });
696
- };
697
-
698
- <CrudifyLogin onError={handleError} />;
699
- ```
700
-
701
- ## 🔄 Estados del Componente
702
-
703
- ### Estados de Carga
704
-
705
- - Login en progreso
706
- - Validacion de codigo
707
- - Envio de email de recuperacion
708
- - Reseteo de contrasena
709
-
710
- ### Estados de Error
711
-
712
- - Errores por campo especifico
713
- - Errores globales
714
- - Errores de red/servidor
715
-
716
- ## 📱 Ejemplo Avanzado
717
-
718
- ```tsx
719
- import React from "react";
720
- import { CrudifyLogin, useUserProfile, secureSessionStorage, handleCrudifyError } from "@nocios/crudify-ui";
721
-
722
- function AuthenticatedApp() {
723
- const { userProfile, loading } = useUserProfile();
724
-
725
- // Si hay usuario, mostrar app
726
- if (userProfile) {
727
- return <MainApp user={userProfile} />;
728
- }
729
-
730
- // Si esta cargando, mostrar spinner
731
- if (loading) {
732
- return <div>Verificando autenticacion...</div>;
733
- }
734
-
735
- // Mostrar login
215
+ function LoginPage() {
736
216
  return (
737
- <div
738
- style={{
739
- display: "flex",
740
- justifyContent: "center",
741
- alignItems: "center",
742
- minHeight: "100vh",
743
- backgroundColor: "#f5f5f5",
744
- }}
745
- >
746
- <div style={{ width: "400px", padding: "2rem" }}>
747
- <CrudifyLogin
748
- config={{
749
- publicApiKey: process.env.REACT_APP_CRUDIFY_API_KEY,
750
- env: process.env.NODE_ENV || "dev",
751
- appName: "Mi Aplicacion",
752
- logo: "/logo.svg",
753
- colors: {
754
- primaryColor: "#1066BA",
755
- },
756
- loginActions: ["forgotPassword", "createUser"],
757
- }}
758
- onLoginSuccess={(token, redirectUrl) => {
759
- secureSessionStorage.setToken(token);
760
- // Force re-render para activar useUserProfile
761
- window.location.reload();
762
- }}
763
- onError={(error) => {
764
- console.error("Authentication error:", error);
765
- // Mostrar notificacion de error
766
- }}
767
- onExternalNavigate={(path) => {
768
- window.location.href = path;
769
- }}
770
- language="es"
771
- redirectUrl="/dashboard"
772
- autoReadFromCookies={true}
773
- />
774
- </div>
217
+ <div style={{ maxWidth: '400px', margin: '0 auto' }}>
218
+ <CrudifyLogin
219
+ config={{ appName: "Mi App" }}
220
+ onLoginSuccess={(userData) => console.log('Login:', userData)}
221
+ />
775
222
  </div>
776
223
  );
777
224
  }
778
225
 
779
- export default AuthenticatedApp;
780
- ```
781
-
782
- ## 🏗️ Implementación con UnifiedProvider (Recomendado)
783
-
784
- ### Patrón Unificado para Apps Completas
785
-
786
- Para aplicaciones que necesitan gestión completa de estado y autenticación, recomendamos usar el patrón UnifiedProvider que combina la gestión de configuración y autenticación en un solo componente:
787
-
788
- ```tsx
789
- // src/contexts/UnifiedProvider.tsx
790
- import React, { createContext, useContext, useMemo, useState, useCallback, useEffect, type ReactNode } from "react"
791
- import { createTheme } from "@mui/material"
792
- import { crudify, type CrudifyEnvType } from "@nocios/crudify-ui"
793
-
794
- interface AppContextValue {
795
- logo: string | null
796
- setLogo: (logo: string) => void
797
- publicApiKey: string | null
798
- setPublicApiKey: (key: string | null) => void
799
- env: CrudifyEnvType
800
- setEnv: (env: CrudifyEnvType) => void
801
- appName: string | null
802
- setAppName: (name: string | null) => void
803
- colors: { primaryColor: string; [key: string]: string }
804
- setColors: (colors: any) => void
805
- theme: any
806
- setTheme: (theme: any) => void
807
- configLoading: boolean
808
- configError: string | null
809
- loginActions: string[]
810
- setLoginActions: (actions: string[]) => void
811
- }
812
-
813
- interface DataContextValue {
814
- token: string | null
815
- setToken: (token: string | null) => void
816
- user: Record<string, any> | null
817
- isInitialized: boolean
818
- initializationError: string | null
819
- }
820
-
821
- const AppContext = createContext<AppContextValue | null>(null)
822
- const DataContext = createContext<DataContextValue | null>(null)
823
-
824
- const UnifiedProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
825
- // Estado para token y usuario
826
- const [token, setTokenState] = useState<string | null>(null)
827
- const [user, setUser] = useState<Record<string, any> | null>(null)
828
- const [isFullyInitialized, setIsFullyInitialized] = useState(false)
829
-
830
- // Configuración desde variables de entorno
831
- const config = useMemo(() => ({
832
- publicApiKey: import.meta.env.VITE_TEST_PUBLIC_API_KEY || 'default-key',
833
- env: import.meta.env.VITE_TEST_ENV || 'prod',
834
- appName: 'Mi Aplicación'
835
- }), [])
836
-
837
- const handleSetToken = useCallback((newToken: string | null) => {
838
- setTokenState(newToken)
839
- if (newToken) {
840
- sessionStorage.setItem('crud_token', newToken)
841
- } else {
842
- sessionStorage.removeItem('crud_token')
843
- }
844
- }, [])
845
-
846
- // Inicializar token desde sessionStorage
847
- useEffect(() => {
848
- const savedToken = sessionStorage.getItem('crud_token')
849
- if (savedToken) {
850
- setTokenState(savedToken)
851
- }
852
- }, [])
853
-
854
- // Inicialización de Crudify cuando hay token
855
- useEffect(() => {
856
- if (!token) {
857
- setIsFullyInitialized(false)
858
- return
859
- }
860
-
861
- const initializeCrudify = async () => {
862
- if (isFullyInitialized) return
863
-
864
- try {
865
- await crudify.config(config.env)
866
- await crudify.init(config.publicApiKey, "none")
867
- crudify.setToken(token)
868
- setIsFullyInitialized(true)
869
- } catch (error) {
870
- console.error('Error initializing Crudify:', error)
871
- setIsFullyInitialized(true) // Evitar bucles
872
- }
873
- }
874
-
875
- initializeCrudify()
876
- }, [token, config.env, config.publicApiKey, isFullyInitialized])
877
-
878
- // Contexto de App
879
- const appContextValue: AppContextValue = {
880
- logo: null,
881
- setLogo: () => {},
882
- publicApiKey: config.publicApiKey,
883
- setPublicApiKey: () => {},
884
- env: config.env as CrudifyEnvType,
885
- setEnv: () => {},
886
- appName: config.appName,
887
- setAppName: () => {},
888
- colors: { primaryColor: "#1066BA" },
889
- setColors: () => {},
890
- theme: createTheme({ palette: { primary: { main: "#1066BA" } } }),
891
- setTheme: () => {},
892
- configLoading: false,
893
- configError: null,
894
- loginActions: [],
895
- setLoginActions: () => {}
896
- }
897
-
898
- // Contexto de Data
899
- const dataContextValue: DataContextValue = {
900
- token,
901
- setToken: handleSetToken,
902
- user,
903
- isInitialized: !!token && isFullyInitialized,
904
- initializationError: null
905
- }
906
-
907
- return (
908
- <AppContext.Provider value={appContextValue}>
909
- <DataContext.Provider value={dataContextValue}>
910
- {children}
911
- </DataContext.Provider>
912
- </AppContext.Provider>
913
- )
914
- }
915
-
916
- export const useApp = (): AppContextValue => {
917
- const context = useContext(AppContext)
918
- if (!context) throw new Error("useApp should be used within a UnifiedProvider")
919
- return context
920
- }
921
-
922
- export const useData = (): DataContextValue => {
923
- const context = useContext(DataContext)
924
- if (!context) throw new Error("useData should be used within a UnifiedProvider")
925
- return context
926
- }
927
-
928
- export { UnifiedProvider }
929
- ```
930
-
931
- ### Implementación del AppStatusHandler
932
-
933
- ```tsx
934
- // src/components/AppStatusHandler/index.tsx
935
- import { type ReactNode } from "react"
936
- import { useLocation } from "react-router-dom"
937
- import { useApp, useData } from "@contexts/UnifiedProvider"
938
- import LoadingApp from "@components/LoadingApp"
939
- import { CssBaseline, ThemeProvider } from "@mui/material"
940
- import InitError from "@components/InitError"
941
-
942
- interface AppStatusHandlerProps {
943
- children: ReactNode
944
- }
945
-
946
- const AppStatusHandler = ({ children }: AppStatusHandlerProps) => {
947
- const location = useLocation()
948
- const { theme, configLoading, configError } = useApp()
949
- const { isInitialized, initializationError } = useData()
950
-
951
- // Rutas que no requieren inicialización completa
952
- const authRoutes = ['/login', '/login/forgotPassword', '/login/checkCode', '/login/resetPassword']
953
- const isAuthRoute = authRoutes.some(route => location.pathname.startsWith(route))
954
-
955
- if (configLoading) {
956
- return <LoadingApp stage="config" />
957
- }
958
-
959
- if (!theme) {
960
- return <LoadingApp stage="theme" />
961
- }
962
-
963
- // Solo requerir inicialización para rutas protegidas
964
- if (!isInitialized && !isAuthRoute) {
965
- return <LoadingApp stage="initializing" />
966
- }
967
-
968
- if (configError || initializationError) {
969
- return <InitError configError={configError} initializationError={Boolean(initializationError)} />
970
- }
971
-
972
- return (
973
- <ThemeProvider theme={theme}>
974
- <CssBaseline />
975
- {children}
976
- </ThemeProvider>
977
- )
978
- }
979
-
980
- export default AppStatusHandler
981
- ```
982
-
983
- ### Implementación de ProtectedRoute
984
-
985
- ```tsx
986
- // src/components/Auth/ProtectedRoute/index.tsx
987
- import { type ReactNode } from "react"
988
- import { Navigate, useLocation } from "react-router-dom"
989
- import { useData } from "@contexts/UnifiedProvider"
990
-
991
- interface ProtectedRouteProps {
992
- children: ReactNode
993
- }
994
-
995
- const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
996
- const { token } = useData()
997
- const location = useLocation()
998
-
999
- if (!token) {
1000
- const fullPath = location.pathname + location.search
1001
- const redirectPath = encodeURIComponent(fullPath)
1002
- return <Navigate to={`/login?redirect=${redirectPath}`} replace />
1003
- }
1004
-
1005
- return children
1006
- }
1007
-
1008
- export default ProtectedRoute
1009
- ```
1010
-
1011
- ### Configuración de la App Principal
1012
-
1013
- ```tsx
1014
- // src/main.tsx
1015
- import React from 'react'
1016
- import ReactDOM from 'react-dom/client'
1017
- import { BrowserRouter } from 'react-router-dom'
1018
- import { UnifiedProvider } from '@contexts/UnifiedProvider'
1019
- import { GlobalNotificationProvider } from '@contexts/GlobalNotificationProvider'
1020
- import AppStatusHandler from '@components/AppStatusHandler'
1021
- import App from './App'
1022
-
1023
- ReactDOM.createRoot(document.getElementById('root')!).render(
1024
- <React.StrictMode>
1025
- <BrowserRouter>
1026
- <UnifiedProvider>
1027
- <GlobalNotificationProvider>
1028
- <AppStatusHandler>
1029
- <App />
1030
- </AppStatusHandler>
1031
- </GlobalNotificationProvider>
1032
- </UnifiedProvider>
1033
- </BrowserRouter>
1034
- </React.StrictMode>,
1035
- )
1036
- ```
1037
-
1038
- ### Uso en Componentes
1039
-
1040
- ```tsx
1041
- // Cualquier componente de la app
1042
- import { useApp, useData } from '@contexts/UnifiedProvider'
1043
-
1044
- function Dashboard() {
1045
- const { appName, theme } = useApp()
1046
- const { user, token, isInitialized } = useData()
1047
-
1048
- if (!isInitialized) {
1049
- return <div>Inicializando...</div>
1050
- }
1051
-
226
+ function App() {
1052
227
  return (
1053
- <div>
1054
- <h1>Bienvenido a {appName}</h1>
1055
- <p>Usuario: {user?.email}</p>
1056
- <p>Token válido: {token ? 'Sí' : 'No'}</p>
1057
- </div>
1058
- )
228
+ <SessionProvider>
229
+ <BrowserRouter>
230
+ <Routes>
231
+ <Route path="/login" element={<LoginPage />} />
232
+ <Route
233
+ path="/dashboard"
234
+ element={
235
+ <ProtectedRoute fallback={<LoginPage />}>
236
+ <Dashboard />
237
+ </ProtectedRoute>
238
+ }
239
+ />
240
+ </Routes>
241
+ </BrowserRouter>
242
+ </SessionProvider>
243
+ );
1059
244
  }
1060
245
  ```
1061
246
 
1062
- ### Variables de Entorno Requeridas
1063
-
1064
- ```env
1065
- # .env.development
1066
- VITE_TEST_PUBLIC_API_KEY=tu-clave-de-api-de-desarrollo
1067
- VITE_TEST_ENV=dev
1068
-
1069
- # .env.production
1070
- VITE_TEST_PUBLIC_API_KEY=tu-clave-de-api-de-produccion
1071
- VITE_TEST_ENV=prod
1072
- ```
1073
-
1074
- ### Ventajas de este Patrón
1075
-
1076
- ✅ **Inicialización automática**: Crudify se inicializa automáticamente cuando hay token
1077
- ✅ **Gestión unificada**: Un solo provider para configuración y autenticación
1078
- ✅ **Rutas protegidas**: Manejo automático de redirecciones según autenticación
1079
- ✅ **Estados de carga**: Diferentes estados para config, theme e inicialización
1080
- ✅ **Variables de entorno**: Configuración centralizada desde .env
1081
- ✅ **Compatibilidad completa**: Mantiene la API de providers anteriores
1082
- ✅ **Sin recursión**: Evita problemas de bucles infinitos en la inicialización
1083
-
1084
- ---
1085
-
1086
- ## 🚀 **Mejores Prácticas**
1087
-
1088
- ### ✅ **DO - Recomendaciones**
1089
-
1090
- ```tsx
1091
- // ✅ Usar el sistema unificado
1092
- <CrudifyDataProvider publicApiKey="..." env="prod">
1093
- <App />
1094
- </CrudifyDataProvider>
1095
-
1096
- // ✅ Verificar estado antes de operaciones
1097
- const { isReady } = useCrudifyData();
1098
- if (isReady) {
1099
- await readItems('products', {});
1100
- }
1101
-
1102
- // ✅ Manejar errores apropiadamente
1103
- try {
1104
- const response = await createItem('products', data);
1105
- if (response.success) {
1106
- // Manejar éxito
1107
- }
1108
- } catch (error) {
1109
- const errors = handleCrudifyError(error);
1110
- // Mostrar errores al usuario
1111
- }
247
+ ## 📱 TypeScript
1112
248
 
1113
- // Usar hooks especializados
1114
- const { isAuthenticated } = useCrudifyAuth();
1115
- const { readItems } = useCrudifyData();
1116
- const { user } = useCrudifyUser();
1117
- ```
1118
-
1119
- ### ❌ **DON'T - Evitar**
249
+ Tipos completos incluidos:
1120
250
 
1121
251
  ```tsx
1122
- // No inicializar crudify manualmente
1123
- await crudify.init(); // La librería maneja esto
1124
-
1125
- // ❌ No usar múltiples providers
1126
- <CrudifyProvider>
1127
- <AnotherProvider>
1128
- <CrudifyDataProvider> // Conflicto
1129
- <App />
1130
- </CrudifyDataProvider>
1131
- </AnotherProvider>
1132
- </CrudifyProvider>
1133
-
1134
- // ❌ No ignorar el estado isReady
1135
- const { readItems } = useCrudifyData();
1136
- await readItems('products', {}); // Puede fallar si no está listo
1137
-
1138
- // ❌ No manejar tokens manualmente cuando usas el provider
1139
- sessionStorage.setItem('token', token); // El provider lo maneja
252
+ import type {
253
+ CrudifyLoginProps,
254
+ UseAuthReturn,
255
+ UserProfile,
256
+ TokenData
257
+ } from '@nocios/crudify-ui';
1140
258
  ```
1141
259
 
1142
- ### 🔄 **Migración desde Versiones Anteriores**
1143
-
1144
- Si tienes código existente, puedes migrar gradualmente:
260
+ ## 🌐 Internacionalización
1145
261
 
1146
262
  ```tsx
1147
- // Antes (código legacy)
1148
- import { CrudifyLogin, useUserProfile } from '@nocios/crudify-ui';
1149
-
1150
- function OldApp() {
1151
- const { userProfile } = useUserProfile();
1152
- return userProfile ? <Dashboard /> : <CrudifyLogin />;
1153
- }
1154
-
1155
- // Después (sistema unificado)
1156
- import { CrudifyDataProvider, useCrudifyAuth, CrudifyLogin } from '@nocios/crudify-ui';
1157
-
1158
- function NewApp() {
1159
- return (
1160
- <CrudifyDataProvider publicApiKey="..." env="prod">
1161
- <AppContent />
1162
- </CrudifyDataProvider>
1163
- );
1164
- }
1165
-
1166
- function AppContent() {
1167
- const { isAuthenticated, user, setToken } = useCrudifyAuth();
1168
-
1169
- return isAuthenticated ? (
1170
- <Dashboard user={user} />
1171
- ) : (
1172
- <CrudifyLogin onLoginSuccess={setToken} />
1173
- );
1174
- }
1175
- ```
1176
-
1177
- ### 🌍 **Variables de Entorno**
1178
-
1179
- Configura estas variables en tu proyecto:
1180
-
1181
- ```bash
1182
- # .env.development
1183
- REACT_APP_CRUDIFY_API_KEY=tu-clave-de-desarrollo
1184
- REACT_APP_CRUDIFY_ENV=dev
263
+ // Idiomas soportados: 'en' | 'es'
264
+ <CrudifyLogin language="es" />
1185
265
 
1186
- # .env.production
1187
- REACT_APP_CRUDIFY_API_KEY=tu-clave-de-produccion
1188
- REACT_APP_CRUDIFY_ENV=prod
1189
- ```
1190
-
1191
- ```tsx
1192
- // Uso en la aplicación
1193
- <CrudifyDataProvider
1194
- publicApiKey={process.env.REACT_APP_CRUDIFY_API_KEY}
1195
- env={process.env.REACT_APP_CRUDIFY_ENV || 'prod'}
1196
- appName="Mi Aplicación"
1197
- >
1198
- <App />
1199
- </CrudifyDataProvider>
266
+ // Traducciones personalizadas
267
+ <CrudifyLogin
268
+ translations={{
269
+ login: { title: "Iniciar Sesión" }
270
+ }}
271
+ />
1200
272
  ```
1201
273
 
1202
- ### 📊 **Debugging y Desarrollo**
274
+ ## 🔒 Seguridad
1203
275
 
1204
- ```tsx
1205
- // Habilitar logs detallados en desarrollo
1206
- const isDevelopment = process.env.NODE_ENV === 'development';
1207
-
1208
- <CrudifyDataProvider
1209
- publicApiKey="..."
1210
- onInitialized={() => isDevelopment && console.log('✅ Crudify inicializado')}
1211
- onError={(error) => isDevelopment && console.error('❌ Error:', error)}
1212
- onTokenChange={(token) => isDevelopment && console.log('🔑 Token:', !!token)}
1213
- >
1214
- <App />
1215
- </CrudifyDataProvider>
1216
- ```
276
+ - ✅ Encriptación de tokens
277
+ - Refresh Token Pattern
278
+ - Validación automática
279
+ - ✅ Limpieza automática de tokens expirados
1217
280
 
1218
- ---
281
+ ## 📚 Documentación Completa
1219
282
 
1220
- ## 📋 **Changelog y Migración**
283
+ Para documentación detallada, ejemplos avanzados y guías completas, consulta [README_DEPTH.md](./README_DEPTH.md).
1221
284
 
1222
- ### Versión 1.2.x (Actual)
1223
- - ✅ **Nuevo**: Sistema unificado `CrudifyDataProvider`
1224
- - ✅ **Nuevo**: Hooks especializados (`useCrudifyAuth`, `useCrudifyData`, `useCrudifyUser`)
1225
- - ✅ **Nuevo**: Inicialización robusta con singleton thread-safe
1226
- - ✅ **Nuevo**: Gestión automática de tokens con sincronización cross-tab
1227
- - ✅ **Nuevo**: Acceso directo a instancia con `getCrudifyInstanceAsync`
1228
- - 🔄 **Mejorado**: Sistema de manejo de errores
1229
- - 🔄 **Mejorado**: TypeScript con tipos más precisos
1230
- - ⚠️ **Legacy**: Hooks anteriores mantienen compatibilidad
285
+ ## 📄 Licencia
1231
286
 
1232
- ### Migración desde 1.1.x
1233
- 1. Envolver la aplicación con `CrudifyDataProvider`
1234
- 2. Reemplazar hooks individuales con el sistema unificado
1235
- 3. Actualizar manejo de tokens para usar `setToken` del hook
1236
- 4. (Opcional) Migrar configuración a props del provider
287
+ MIT © [Nocios](https://github.com/nocios)
1237
288
 
1238
289
  ---
1239
290
 
1240
- **Version:** 1.2.1
1241
- **Mantenido por:** Equipo Crudify
1242
- **Repositorio:** [GitHub](https://github.com/nocios/crudify-ui)
1243
- **Documentación:** [Docs](https://docs.crudify.com)
1244
- **Soporte:** [Issues](https://github.com/nocios/crudify-ui/issues)
291
+ **¿Necesitas ayuda?** Consulta [README_DEPTH.md](./README_DEPTH.md) para documentación completa.