@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.
@@ -0,0 +1,1046 @@
1
+ # @nocios/crudify-ui - Documentación Completa
2
+
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)
5
+
6
+ **@nocios/crudify-ui** es una biblioteca completa de componentes UI y hooks de React para aplicaciones que utilizan el sistema Crudify. Proporciona autenticación moderna con Refresh Token Pattern, manejo de sesiones, componentes de UI listos para usar y una API unificada.
7
+
8
+ ## 🚀 Características Principales
9
+
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 pre-construidos con Material-UI
13
+ - **🪝 React Hooks**: Hooks especializados para operaciones CRUD y autenticación
14
+ - **🔄 API Unificada**: Re-exporta completamente @nocios/crudify-browser
15
+ - **🌍 Internacionalización**: Soporte completo para múltiples idiomas
16
+ - **💾 Almacenamiento Seguro**: Encriptación de tokens con múltiples opciones de storage
17
+ - **📱 TypeScript**: Completamente tipado para mejor experiencia de desarrollo
18
+
19
+ ## 📦 Instalación
20
+
21
+ ```bash
22
+ npm install @nocios/crudify-ui
23
+
24
+ # Peer dependencies (si no las tienes instaladas)
25
+ npm install @mui/material @mui/icons-material react react-dom
26
+ ```
27
+
28
+ ### Peer Dependencies Requeridas
29
+
30
+ ```json
31
+ {
32
+ "@mui/icons-material": "^7.1.0",
33
+ "@mui/material": "^7.1.0",
34
+ "react": "^19.1.0",
35
+ "react-dom": "^19.1.0"
36
+ }
37
+ ```
38
+
39
+ ## 🏗️ Configuración Inicial
40
+
41
+ ### 1. Configurar SessionProvider
42
+
43
+ El `SessionProvider` es el punto de entrada principal para manejar la autenticación y sesiones en tu aplicación:
44
+
45
+ ```tsx
46
+ import React from 'react';
47
+ import { SessionProvider } from '@nocios/crudify-ui';
48
+
49
+ function App() {
50
+ return (
51
+ <SessionProvider
52
+ options={{
53
+ // Configuración automática basada en variables de entorno
54
+ // o configuración manual si es necesario
55
+ storageType: 'localStorage', // 'localStorage' | 'sessionStorage'
56
+ onSessionExpired: () => {
57
+ console.log('Sesión expirada');
58
+ },
59
+ onSessionRestored: (tokens) => {
60
+ console.log('Sesión restaurada:', tokens);
61
+ }
62
+ }}
63
+ >
64
+ <YourApp />
65
+ </SessionProvider>
66
+ );
67
+ }
68
+ ```
69
+
70
+ ### 2. Variables de Entorno
71
+
72
+ La biblioteca detecta automáticamente la configuración desde las variables de entorno:
73
+
74
+ ```bash
75
+ # .env
76
+ REACT_APP_CRUDIFY_PUBLIC_API_KEY=tu_api_key_aquí
77
+ REACT_APP_CRUDIFY_ENV=dev # dev | stg | api | prod
78
+ ```
79
+
80
+ ## 🪝 Hooks Principales
81
+
82
+ ### useAuth - Autenticación
83
+
84
+ Hook principal para manejo completo de autenticación:
85
+
86
+ ```tsx
87
+ import { useAuth } from '@nocios/crudify-ui';
88
+
89
+ function LoginPage() {
90
+ const {
91
+ login,
92
+ logout,
93
+ isAuthenticated,
94
+ isLoading,
95
+ error,
96
+ clearError
97
+ } = useAuth();
98
+
99
+ const handleLogin = async () => {
100
+ try {
101
+ const result = await login('user@example.com', 'password');
102
+ console.log('Login exitoso:', result);
103
+ } catch (error) {
104
+ console.error('Error de login:', error);
105
+ }
106
+ };
107
+
108
+ const handleLogout = async () => {
109
+ await logout();
110
+ };
111
+
112
+ return (
113
+ <div>
114
+ {isAuthenticated ? (
115
+ <button onClick={handleLogout}>Cerrar Sesión</button>
116
+ ) : (
117
+ <button onClick={handleLogin} disabled={isLoading}>
118
+ {isLoading ? 'Iniciando...' : 'Iniciar Sesión'}
119
+ </button>
120
+ )}
121
+ {error && <div>Error: {error}</div>}
122
+ </div>
123
+ );
124
+ }
125
+ ```
126
+
127
+ **Características del useAuth:**
128
+ - ✅ Login/logout automático con refresh tokens
129
+ - ✅ Estado de autenticación en tiempo real
130
+ - ✅ Manejo de errores integrado
131
+ - ✅ Sincronización cross-tab
132
+ - ✅ Validación automática de tokens
133
+
134
+ ### useUserData - Datos del Usuario
135
+
136
+ Hook para obtener y manejar los datos completos del usuario:
137
+
138
+ ```tsx
139
+ import { useUserData } from '@nocios/crudify-ui';
140
+
141
+ function UserProfile() {
142
+ const {
143
+ userData,
144
+ sessionData,
145
+ isLoading,
146
+ error,
147
+ refetch
148
+ } = useUserData({
149
+ autoFetch: true, // Fetch automático al montar
150
+ retryOnError: true, // Retry automático en errores
151
+ cacheTime: 300000 // Cache por 5 minutos
152
+ });
153
+
154
+ if (isLoading) return <div>Cargando usuario...</div>;
155
+ if (error) return <div>Error: {error}</div>;
156
+
157
+ return (
158
+ <div>
159
+ <h2>Perfil de Usuario</h2>
160
+
161
+ {/* Datos del JWT (sessionData) */}
162
+ <p>Email: {sessionData?.email}</p>
163
+ <p>ID: {sessionData?._id}</p>
164
+
165
+ {/* Datos de la base de datos (userData) */}
166
+ <p>Nombre completo: {userData?.fullName}</p>
167
+ <p>Avatar: <img src={userData?.avatar} alt="Avatar" /></p>
168
+ <p>Último login: {userData?.lastLogin}</p>
169
+
170
+ <button onClick={() => refetch()}>
171
+ Actualizar Datos
172
+ </button>
173
+ </div>
174
+ );
175
+ }
176
+ ```
177
+
178
+ **Características del useUserData:**
179
+ - ✅ Combina datos del JWT (sessionData) y de la BD (userData)
180
+ - ✅ Auto-fetch con deduplicación inteligente
181
+ - ✅ Retry automático en errores de red
182
+ - ✅ Cache configurable
183
+ - ✅ Refetch manual
184
+
185
+ ### useData - Operaciones CRUD
186
+
187
+ Hook para realizar operaciones CRUD type-safe:
188
+
189
+ ```tsx
190
+ import { useData } from '@nocios/crudify-ui';
191
+
192
+ function ProductManager() {
193
+ const { create, read, update, remove, isInitialized } = useData();
194
+
195
+ const createProduct = async () => {
196
+ try {
197
+ const result = await create('products', {
198
+ name: 'Nuevo Producto',
199
+ price: 99.99,
200
+ category: 'electronics'
201
+ });
202
+ console.log('Producto creado:', result);
203
+ } catch (error) {
204
+ console.error('Error creando producto:', error);
205
+ }
206
+ };
207
+
208
+ const getProducts = async () => {
209
+ try {
210
+ const products = await read('products', {
211
+ filters: { category: 'electronics' },
212
+ limit: 10,
213
+ offset: 0
214
+ });
215
+ console.log('Productos:', products);
216
+ } catch (error) {
217
+ console.error('Error obteniendo productos:', error);
218
+ }
219
+ };
220
+
221
+ const updateProduct = async (productId: string) => {
222
+ try {
223
+ const result = await update('products', productId, {
224
+ price: 89.99
225
+ });
226
+ console.log('Producto actualizado:', result);
227
+ } catch (error) {
228
+ console.error('Error actualizando producto:', error);
229
+ }
230
+ };
231
+
232
+ const deleteProduct = async (productId: string) => {
233
+ try {
234
+ await remove('products', productId);
235
+ console.log('Producto eliminado');
236
+ } catch (error) {
237
+ console.error('Error eliminando producto:', error);
238
+ }
239
+ };
240
+
241
+ if (!isInitialized) return <div>Inicializando...</div>;
242
+
243
+ return (
244
+ <div>
245
+ <button onClick={createProduct}>Crear Producto</button>
246
+ <button onClick={getProducts}>Obtener Productos</button>
247
+ <button onClick={() => updateProduct('123')}>Actualizar</button>
248
+ <button onClick={() => deleteProduct('123')}>Eliminar</button>
249
+ </div>
250
+ );
251
+ }
252
+ ```
253
+
254
+ **Características del useData:**
255
+ - ✅ Operaciones CRUD type-safe
256
+ - ✅ Verificación automática de inicialización
257
+ - ✅ Soporte para transacciones
258
+ - ✅ Manejo de errores integrado
259
+
260
+ ### useSession - Sesión de Bajo Nivel
261
+
262
+ Hook de bajo nivel para control directo de sesiones:
263
+
264
+ ```tsx
265
+ import { useSession } from '@nocios/crudify-ui';
266
+
267
+ function SessionManager() {
268
+ const {
269
+ isAuthenticated,
270
+ isLoading,
271
+ tokens,
272
+ error,
273
+ login,
274
+ logout,
275
+ refreshTokens,
276
+ isExpiringSoon,
277
+ expiresIn
278
+ } = useSession({
279
+ onSessionExpired: () => console.log('Sesión expirada'),
280
+ onSessionRestored: (tokens) => console.log('Sesión restaurada', tokens),
281
+ onTokensRefreshed: (tokens) => console.log('Tokens renovados', tokens)
282
+ });
283
+
284
+ return (
285
+ <div>
286
+ <h3>Estado de Sesión</h3>
287
+ <p>Autenticado: {isAuthenticated ? 'Sí' : 'No'}</p>
288
+ <p>Cargando: {isLoading ? 'Sí' : 'No'}</p>
289
+ <p>Expira en: {Math.floor(expiresIn / 60)} minutos</p>
290
+ <p>Expira pronto: {isExpiringSoon ? 'Sí' : 'No'}</p>
291
+
292
+ {isExpiringSoon && (
293
+ <button onClick={() => refreshTokens()}>
294
+ Renovar Tokens
295
+ </button>
296
+ )}
297
+
298
+ {tokens && (
299
+ <div>
300
+ <p>Access Token: {tokens.accessToken.substring(0, 20)}...</p>
301
+ <p>Refresh Token: {tokens.refreshToken?.substring(0, 20)}...</p>
302
+ </div>
303
+ )}
304
+ </div>
305
+ );
306
+ }
307
+ ```
308
+
309
+ ### useSessionContext - Contexto de Sesión
310
+
311
+ Hook para acceder al contexto global de sesión:
312
+
313
+ ```tsx
314
+ import { useSessionContext } from '@nocios/crudify-ui';
315
+
316
+ function NavigationBar() {
317
+ const {
318
+ isAuthenticated,
319
+ sessionData,
320
+ logout,
321
+ getTokenInfo
322
+ } = useSessionContext();
323
+
324
+ if (!isAuthenticated) return null;
325
+
326
+ return (
327
+ <nav>
328
+ <span>Bienvenido, {sessionData?.email}</span>
329
+ <button onClick={() => logout()}>Cerrar Sesión</button>
330
+ <button onClick={() => console.log(getTokenInfo())}>
331
+ Ver Token Info
332
+ </button>
333
+ </nav>
334
+ );
335
+ }
336
+ ```
337
+
338
+ ## 🎨 Componentes UI
339
+
340
+ ### CrudifyLogin - Componente de Login Completo
341
+
342
+ Componente completo de autenticación con múltiples pantallas:
343
+
344
+ ```tsx
345
+ import { CrudifyLogin } from '@nocios/crudify-ui';
346
+ import type { CrudifyLoginConfig } from '@nocios/crudify-ui';
347
+
348
+ function AuthPage() {
349
+ const config: CrudifyLoginConfig = {
350
+ appName: "Mi Aplicación",
351
+ logo: "/logo.png",
352
+ colors: {
353
+ primaryColor: "#1066BA",
354
+ bgColor: "#f5f5f5"
355
+ }
356
+ };
357
+
358
+ const handleLoginSuccess = (userData, redirectUrl) => {
359
+ console.log('Login exitoso:', userData);
360
+ // Redireccionar o actualizar estado
361
+ };
362
+
363
+ const handleError = (error: string) => {
364
+ console.error('Error de autenticación:', error);
365
+ };
366
+
367
+ return (
368
+ <CrudifyLogin
369
+ config={config}
370
+ onLoginSuccess={handleLoginSuccess}
371
+ onError={handleError}
372
+ initialScreen="login" // "login" | "forgotPassword" | "resetPassword"
373
+ language="es" // "en" | "es"
374
+ redirectUrl="/dashboard"
375
+ autoReadFromCookies={true}
376
+ />
377
+ );
378
+ }
379
+ ```
380
+
381
+ **Pantallas incluidas en CrudifyLogin:**
382
+ - 🔐 **Login**: Formulario principal de autenticación
383
+ - 🔑 **Forgot Password**: Solicitar recuperación de contraseña
384
+ - 📧 **Check Code**: Verificar código de recuperación
385
+ - 🔄 **Reset Password**: Establecer nueva contraseña
386
+
387
+ **Configuración disponible:**
388
+ - ✅ Logo y nombre de aplicación personalizable
389
+ - ✅ Colores y temas personalizables
390
+ - ✅ Traducciones completas (ES/EN)
391
+ - ✅ URLs de traducción externas
392
+ - ✅ Callbacks para todos los eventos
393
+
394
+ ### UserProfileDisplay - Perfil de Usuario
395
+
396
+ Componente para mostrar información del perfil del usuario:
397
+
398
+ ```tsx
399
+ import { UserProfileDisplay } from '@nocios/crudify-ui';
400
+
401
+ function ProfilePage() {
402
+ return (
403
+ <div>
404
+ <h1>Mi Perfil</h1>
405
+ <UserProfileDisplay />
406
+ </div>
407
+ );
408
+ }
409
+ ```
410
+
411
+ ### ProtectedRoute - Protección de Rutas
412
+
413
+ Componente para proteger rutas que requieren autenticación:
414
+
415
+ ```tsx
416
+ import { ProtectedRoute } from '@nocios/crudify-ui';
417
+
418
+ function App() {
419
+ return (
420
+ <Router>
421
+ <Routes>
422
+ <Route path="/login" element={<LoginPage />} />
423
+ <Route
424
+ path="/dashboard"
425
+ element={
426
+ <ProtectedRoute fallback={<LoginPage />}>
427
+ <Dashboard />
428
+ </ProtectedRoute>
429
+ }
430
+ />
431
+ </Routes>
432
+ </Router>
433
+ );
434
+ }
435
+ ```
436
+
437
+ ### SessionDebugInfo - Debug de Sesión
438
+
439
+ Componente útil para desarrollo y debug:
440
+
441
+ ```tsx
442
+ import { SessionDebugInfo } from '@nocios/crudify-ui';
443
+
444
+ function DevPage() {
445
+ return (
446
+ <div>
447
+ <h2>Información de Sesión (Dev)</h2>
448
+ <SessionDebugInfo />
449
+ </div>
450
+ );
451
+ }
452
+ ```
453
+
454
+ ## 🛠️ Utilidades
455
+
456
+ ### JWT Utils
457
+
458
+ Utilidades para trabajar con tokens JWT:
459
+
460
+ ```tsx
461
+ import {
462
+ decodeJwtSafely,
463
+ getCurrentUserEmail,
464
+ isTokenExpired
465
+ } from '@nocios/crudify-ui';
466
+
467
+ // Decodificar JWT de forma segura
468
+ const payload = decodeJwtSafely(token);
469
+ console.log('Usuario ID:', payload?.sub);
470
+
471
+ // Obtener email del usuario actual
472
+ const email = getCurrentUserEmail();
473
+ console.log('Email:', email);
474
+
475
+ // Verificar si un token está expirado
476
+ const expired = isTokenExpired(token);
477
+ console.log('Token expirado:', expired);
478
+ ```
479
+
480
+ ### Token Storage
481
+
482
+ Sistema de almacenamiento seguro para tokens:
483
+
484
+ ```tsx
485
+ import { TokenStorage } from '@nocios/crudify-ui';
486
+ import type { TokenData, StorageType } from '@nocios/crudify-ui';
487
+
488
+ // Crear instancia de storage
489
+ const storage = new TokenStorage({
490
+ type: 'localStorage', // 'localStorage' | 'sessionStorage'
491
+ encryptionKey: 'mi-clave-secreta'
492
+ });
493
+
494
+ // Guardar tokens
495
+ const tokens: TokenData = {
496
+ accessToken: 'access_token_here',
497
+ refreshToken: 'refresh_token_here',
498
+ expiresIn: 3600,
499
+ refreshExpiresIn: 86400
500
+ };
501
+
502
+ await storage.setTokens(tokens);
503
+
504
+ // Obtener tokens
505
+ const storedTokens = await storage.getTokens();
506
+ console.log('Tokens almacenados:', storedTokens);
507
+
508
+ // Limpiar tokens
509
+ await storage.clearTokens();
510
+
511
+ // Verificar validez
512
+ const isValid = await storage.isValid();
513
+ console.log('Tokens válidos:', isValid);
514
+ ```
515
+
516
+ ### Error Handler
517
+
518
+ Sistema completo de manejo de errores:
519
+
520
+ ```tsx
521
+ import {
522
+ handleCrudifyError,
523
+ parseApiError,
524
+ parseTransactionError,
525
+ getErrorMessage,
526
+ ERROR_CODES
527
+ } from '@nocios/crudify-ui';
528
+ import type { ParsedError, ErrorCode } from '@nocios/crudify-ui';
529
+
530
+ // Manejo universal de errores
531
+ try {
532
+ // operación que puede fallar
533
+ } catch (error) {
534
+ const parsedError: ParsedError = handleCrudifyError(error);
535
+
536
+ console.log('Tipo:', parsedError.type);
537
+ console.log('Código:', parsedError.code);
538
+ console.log('Mensaje:', parsedError.message);
539
+ console.log('Severidad:', parsedError.severity);
540
+
541
+ // Mostrar mensaje apropiado al usuario
542
+ const userMessage = getErrorMessage(parsedError.code as ErrorCode);
543
+ alert(userMessage);
544
+ }
545
+
546
+ // Parser específico para errores de API
547
+ const apiError = parseApiError(response);
548
+
549
+ // Parser específico para errores de transacción
550
+ const transactionError = parseTransactionError(response);
551
+
552
+ // Códigos de error disponibles
553
+ console.log('Códigos disponibles:', ERROR_CODES);
554
+ ```
555
+
556
+ ## 🔄 API de Crudify Browser
557
+
558
+ Esta librería re-exporta completamente `@nocios/crudify-browser`, proporcionando una API unificada:
559
+
560
+ ```tsx
561
+ import { crudify } from '@nocios/crudify-ui';
562
+ // Equivale a: import crudify from '@nocios/crudify-browser';
563
+
564
+ // Todas las funcionalidades de crudify-browser están disponibles
565
+ const result = await crudify.create('users', userData);
566
+ const users = await crudify.read('users');
567
+ ```
568
+
569
+ Esto significa que puedes:
570
+ - ✅ Importar solo desde `@nocios/crudify-ui`
571
+ - ✅ Usar todas las funcionalidades de crudify-browser
572
+ - ✅ Tener una API unificada
573
+ - ✅ Simplificar las dependencias
574
+
575
+ ## 📱 TypeScript Support
576
+
577
+ La librería incluye tipos completos para TypeScript:
578
+
579
+ ```tsx
580
+ import type {
581
+ // Tipos de API
582
+ CrudifyApiResponse,
583
+ CrudifyTransactionResponse,
584
+ UserProfile,
585
+ LoginResponse,
586
+ LoginRequest,
587
+
588
+ // Tipos de componentes
589
+ CrudifyLoginProps,
590
+ CrudifyLoginConfig,
591
+
592
+ // Tipos de hooks
593
+ UseAuthReturn,
594
+ UseUserDataReturn,
595
+ UseDataReturn,
596
+ SessionState,
597
+
598
+ // Tipos de utilidades
599
+ TokenData,
600
+ ParsedError,
601
+ ErrorCode
602
+ } from '@nocios/crudify-ui';
603
+
604
+ // Ejemplo con tipos
605
+ const handleLogin = async (
606
+ credentials: LoginRequest
607
+ ): Promise<CrudifyApiResponse<LoginResponse>> => {
608
+ // implementación con tipos seguros
609
+ };
610
+ ```
611
+
612
+ ## 🌐 Internacionalización
613
+
614
+ ### Configuración básica
615
+
616
+ ```tsx
617
+ import { CrudifyLogin } from '@nocios/crudify-ui';
618
+
619
+ // Usando traducciones incluidas
620
+ <CrudifyLogin language="es" /> // "en" | "es"
621
+
622
+ // Usando traducciones personalizadas
623
+ <CrudifyLogin
624
+ language="es"
625
+ translations={{
626
+ login: {
627
+ title: "Iniciar Sesión",
628
+ email: "Correo Electrónico",
629
+ password: "Contraseña",
630
+ submit: "Entrar"
631
+ }
632
+ }}
633
+ />
634
+
635
+ // Usando URL externa para traducciones
636
+ <CrudifyLogin
637
+ language="es"
638
+ translationsUrl="/api/translations/es.json"
639
+ />
640
+ ```
641
+
642
+ ### Estructura de traducciones
643
+
644
+ ```json
645
+ {
646
+ "login": {
647
+ "title": "Iniciar Sesión",
648
+ "email": "Correo Electrónico",
649
+ "password": "Contraseña",
650
+ "submit": "Entrar",
651
+ "forgotPassword": "¿Olvidaste tu contraseña?",
652
+ "logoAlt": "Logo de la aplicación"
653
+ },
654
+ "forgotPassword": {
655
+ "title": "Recuperar Contraseña",
656
+ "email": "Correo Electrónico",
657
+ "submit": "Enviar Código",
658
+ "backToLogin": "Volver al Login"
659
+ },
660
+ "resetPassword": {
661
+ "title": "Nueva Contraseña",
662
+ "password": "Nueva Contraseña",
663
+ "confirmPassword": "Confirmar Contraseña",
664
+ "submit": "Cambiar Contraseña"
665
+ }
666
+ }
667
+ ```
668
+
669
+ ## 🔧 Configuración Avanzada
670
+
671
+ ### SessionProvider con opciones completas
672
+
673
+ ```tsx
674
+ import { SessionProvider } from '@nocios/crudify-ui';
675
+ import type { UseSessionOptions } from '@nocios/crudify-ui';
676
+
677
+ const sessionOptions: UseSessionOptions = {
678
+ // Tipo de almacenamiento
679
+ storageType: 'localStorage', // 'localStorage' | 'sessionStorage'
680
+
681
+ // Callbacks del ciclo de vida
682
+ onSessionExpired: () => {
683
+ console.log('Sesión expirada - redirigir a login');
684
+ window.location.href = '/login';
685
+ },
686
+
687
+ onSessionRestored: (tokens) => {
688
+ console.log('Sesión restaurada exitosamente', tokens);
689
+ },
690
+
691
+ onTokensRefreshed: (tokens) => {
692
+ console.log('Tokens renovados', tokens);
693
+ },
694
+
695
+ onLogout: () => {
696
+ console.log('Usuario cerró sesión');
697
+ },
698
+
699
+ onError: (error) => {
700
+ console.error('Error de sesión:', error);
701
+ }
702
+ };
703
+
704
+ function App() {
705
+ return (
706
+ <SessionProvider options={sessionOptions}>
707
+ <Router>
708
+ <Routes>
709
+ {/* tus rutas */}
710
+ </Routes>
711
+ </Router>
712
+ </SessionProvider>
713
+ );
714
+ }
715
+ ```
716
+
717
+ ### Configuración manual de Crudify
718
+
719
+ Si necesitas configuración manual en lugar de variables de entorno:
720
+
721
+ ```tsx
722
+ import { crudify } from '@nocios/crudify-ui';
723
+
724
+ // Configurar manualmente antes de usar
725
+ crudify.configure({
726
+ publicApiKey: 'tu-api-key',
727
+ environment: 'production', // 'dev' | 'stg' | 'api' | 'prod'
728
+ baseUrl: 'https://api.tudominio.com' // opcional
729
+ });
730
+ ```
731
+
732
+ ## 🚀 Ejemplos de Uso Completos
733
+
734
+ ### Aplicación básica con autenticación
735
+
736
+ ```tsx
737
+ import React from 'react';
738
+ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
739
+ import {
740
+ SessionProvider,
741
+ ProtectedRoute,
742
+ CrudifyLogin,
743
+ useSessionContext
744
+ } from '@nocios/crudify-ui';
745
+
746
+ // Componente Dashboard
747
+ function Dashboard() {
748
+ const { sessionData, logout } = useSessionContext();
749
+
750
+ return (
751
+ <div>
752
+ <h1>Dashboard</h1>
753
+ <p>Bienvenido, {sessionData?.email}</p>
754
+ <button onClick={() => logout()}>Cerrar Sesión</button>
755
+ </div>
756
+ );
757
+ }
758
+
759
+ // Componente Login
760
+ function LoginPage() {
761
+ return (
762
+ <div style={{ maxWidth: '400px', margin: '0 auto', padding: '2rem' }}>
763
+ <CrudifyLogin
764
+ config={{
765
+ appName: "Mi App",
766
+ colors: { primaryColor: "#1976d2" }
767
+ }}
768
+ onLoginSuccess={(userData) => {
769
+ console.log('Login exitoso:', userData);
770
+ // La navegación se maneja automáticamente
771
+ }}
772
+ />
773
+ </div>
774
+ );
775
+ }
776
+
777
+ // Aplicación principal
778
+ function App() {
779
+ return (
780
+ <SessionProvider>
781
+ <BrowserRouter>
782
+ <Routes>
783
+ <Route path="/login" element={<LoginPage />} />
784
+ <Route
785
+ path="/dashboard"
786
+ element={
787
+ <ProtectedRoute fallback={<Navigate to="/login" />}>
788
+ <Dashboard />
789
+ </ProtectedRoute>
790
+ }
791
+ />
792
+ <Route path="/" element={<Navigate to="/dashboard" />} />
793
+ </Routes>
794
+ </BrowserRouter>
795
+ </SessionProvider>
796
+ );
797
+ }
798
+
799
+ export default App;
800
+ ```
801
+
802
+ ### Aplicación con operaciones CRUD
803
+
804
+ ```tsx
805
+ import React, { useState, useEffect } from 'react';
806
+ import { useData, useUserData } from '@nocios/crudify-ui';
807
+
808
+ function ProductManager() {
809
+ const [products, setProducts] = useState([]);
810
+ const [loading, setLoading] = useState(false);
811
+
812
+ const { create, read, update, remove } = useData();
813
+ const { userData } = useUserData();
814
+
815
+ // Cargar productos al montar
816
+ useEffect(() => {
817
+ loadProducts();
818
+ }, []);
819
+
820
+ const loadProducts = async () => {
821
+ setLoading(true);
822
+ try {
823
+ const result = await read('products', {
824
+ filters: { userId: userData?.id },
825
+ limit: 20
826
+ });
827
+ setProducts(result.data || []);
828
+ } catch (error) {
829
+ console.error('Error cargando productos:', error);
830
+ } finally {
831
+ setLoading(false);
832
+ }
833
+ };
834
+
835
+ const createProduct = async (productData) => {
836
+ try {
837
+ await create('products', {
838
+ ...productData,
839
+ userId: userData?.id
840
+ });
841
+ await loadProducts(); // Recargar lista
842
+ } catch (error) {
843
+ console.error('Error creando producto:', error);
844
+ }
845
+ };
846
+
847
+ const updateProduct = async (productId, updates) => {
848
+ try {
849
+ await update('products', productId, updates);
850
+ await loadProducts(); // Recargar lista
851
+ } catch (error) {
852
+ console.error('Error actualizando producto:', error);
853
+ }
854
+ };
855
+
856
+ const deleteProduct = async (productId) => {
857
+ try {
858
+ await remove('products', productId);
859
+ await loadProducts(); // Recargar lista
860
+ } catch (error) {
861
+ console.error('Error eliminando producto:', error);
862
+ }
863
+ };
864
+
865
+ if (loading) return <div>Cargando productos...</div>;
866
+
867
+ return (
868
+ <div>
869
+ <h2>Mis Productos</h2>
870
+
871
+ <button onClick={() => createProduct({
872
+ name: 'Nuevo Producto',
873
+ price: 99.99
874
+ })}>
875
+ Crear Producto
876
+ </button>
877
+
878
+ <ul>
879
+ {products.map(product => (
880
+ <li key={product.id}>
881
+ <span>{product.name} - ${product.price}</span>
882
+ <button onClick={() => updateProduct(product.id, {
883
+ price: product.price * 1.1
884
+ })}>
885
+ Aumentar Precio 10%
886
+ </button>
887
+ <button onClick={() => deleteProduct(product.id)}>
888
+ Eliminar
889
+ </button>
890
+ </li>
891
+ ))}
892
+ </ul>
893
+ </div>
894
+ );
895
+ }
896
+ ```
897
+
898
+ ## 🔒 Seguridad
899
+
900
+ ### Características de Seguridad
901
+
902
+ - ✅ **Encriptación de tokens**: Los tokens se almacenan encriptados
903
+ - ✅ **Refresh Token Pattern**: Tokens de acceso de corta duración
904
+ - ✅ **Validación automática**: Verificación de expiración y renovación
905
+ - ✅ **Almacenamiento seguro**: Soporte para localStorage y sessionStorage
906
+ - ✅ **Limpieza automática**: Tokens expirados se limpian automáticamente
907
+
908
+ ### Mejores Prácticas
909
+
910
+ ```tsx
911
+ // 1. Usar SessionProvider en el nivel más alto
912
+ function App() {
913
+ return (
914
+ <SessionProvider options={{
915
+ storageType: 'localStorage', // Más persistente
916
+ onSessionExpired: handleSessionExpired
917
+ }}>
918
+ <YourApp />
919
+ </SessionProvider>
920
+ );
921
+ }
922
+
923
+ // 2. Proteger rutas sensibles
924
+ <ProtectedRoute fallback={<LoginRedirect />}>
925
+ <SensitiveComponent />
926
+ </ProtectedRoute>
927
+
928
+ // 3. Manejar errores de autenticación
929
+ const { error, clearError } = useAuth();
930
+ useEffect(() => {
931
+ if (error) {
932
+ console.error('Error de auth:', error);
933
+ // Mostrar notificación al usuario
934
+ clearError();
935
+ }
936
+ }, [error]);
937
+
938
+ // 4. Limpiar sesión al salir
939
+ useEffect(() => {
940
+ const handleBeforeUnload = () => {
941
+ // Opcional: limpiar datos sensibles
942
+ };
943
+
944
+ window.addEventListener('beforeunload', handleBeforeUnload);
945
+ return () => window.removeEventListener('beforeunload', handleBeforeUnload);
946
+ }, []);
947
+ ```
948
+
949
+ ## 🐛 Troubleshooting
950
+
951
+ ### Problemas Comunes
952
+
953
+ **1. Token expirado constantemente**
954
+ ```tsx
955
+ // Verificar configuración de refresh tokens
956
+ const { isExpiringSoon, refreshTokens } = useSession();
957
+
958
+ useEffect(() => {
959
+ if (isExpiringSoon) {
960
+ refreshTokens();
961
+ }
962
+ }, [isExpiringSoon]);
963
+ ```
964
+
965
+ **2. Sesión no se restaura al recargar**
966
+ ```tsx
967
+ // Asegurar que SessionProvider esté en el nivel correcto
968
+ // y verificar storageType
969
+ <SessionProvider options={{ storageType: 'localStorage' }}>
970
+ ```
971
+
972
+ **3. Errores de CORS**
973
+ ```tsx
974
+ // Verificar configuración de environment
975
+ crudify.configure({
976
+ environment: 'dev', // Asegurar environment correcto
977
+ publicApiKey: process.env.REACT_APP_CRUDIFY_PUBLIC_API_KEY
978
+ });
979
+ ```
980
+
981
+ ### Debug
982
+
983
+ ```tsx
984
+ // Usar SessionDebugInfo para desarrollo
985
+ import { SessionDebugInfo } from '@nocios/crudify-ui';
986
+
987
+ function DevPanel() {
988
+ return process.env.NODE_ENV === 'development' ? (
989
+ <div style={{ position: 'fixed', bottom: 0, right: 0, background: 'white', padding: '1rem' }}>
990
+ <SessionDebugInfo />
991
+ </div>
992
+ ) : null;
993
+ }
994
+ ```
995
+
996
+ ## 📚 Referencias de API
997
+
998
+ ### Hooks API Reference
999
+
1000
+ | Hook | Propósito | Returns |
1001
+ |------|-----------|---------|
1002
+ | `useAuth` | Autenticación principal | `{ login, logout, isAuthenticated, isLoading, error }` |
1003
+ | `useUserData` | Datos del usuario | `{ userData, sessionData, isLoading, error, refetch }` |
1004
+ | `useData` | Operaciones CRUD | `{ create, read, update, remove, isInitialized }` |
1005
+ | `useSession` | Control de sesión | `{ tokens, isAuthenticated, refreshTokens, expiresIn }` |
1006
+ | `useSessionContext` | Contexto global | `{ sessionData, isAuthenticated, logout, getTokenInfo }` |
1007
+
1008
+ ### Componentes API Reference
1009
+
1010
+ | Componente | Props Principales | Propósito |
1011
+ |------------|-------------------|-----------|
1012
+ | `SessionProvider` | `options: UseSessionOptions` | Provider global de sesión |
1013
+ | `CrudifyLogin` | `config, onLoginSuccess, language` | Componente de login completo |
1014
+ | `ProtectedRoute` | `fallback, children` | Protección de rutas |
1015
+ | `UserProfileDisplay` | `showAvatar, showDetails` | Display de perfil |
1016
+
1017
+ ### Utilidades API Reference
1018
+
1019
+ | Utilidad | Funciones | Propósito |
1020
+ |----------|-----------|-----------|
1021
+ | `JWT Utils` | `decodeJwtSafely, getCurrentUserEmail, isTokenExpired` | Manejo de JWT |
1022
+ | `Token Storage` | `setTokens, getTokens, clearTokens, isValid` | Almacenamiento seguro |
1023
+ | `Error Handler` | `handleCrudifyError, parseApiError, getErrorMessage` | Manejo de errores |
1024
+
1025
+ ## 🔄 Changelog
1026
+
1027
+ ### v1.3.1
1028
+ - ✅ Sistema completo de Refresh Token Pattern
1029
+ - ✅ Hooks modernos (useAuth, useUserData, useData)
1030
+ - ✅ SessionProvider con contexto global
1031
+ - ✅ Componentes ProtectedRoute y SessionDebugInfo
1032
+ - ✅ Almacenamiento encriptado de tokens
1033
+ - ✅ Sistema completo de manejo de errores
1034
+ - ✅ Re-exportación completa de @nocios/crudify-browser
1035
+
1036
+ ## 📄 Licencia
1037
+
1038
+ MIT © [Nocios](https://github.com/nocios)
1039
+
1040
+ ## 🤝 Contribuir
1041
+
1042
+ Si encuentras bugs o tienes sugerencias, por favor crea un issue en el repositorio oficial.
1043
+
1044
+ ---
1045
+
1046
+ **¿Necesitas ayuda?** Consulta la documentación completa o contacta al equipo de desarrollo.