@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.
@@ -1,538 +0,0 @@
1
- # CrudifyDataProvider - Guía de Migración
2
-
3
- ## 🎯 Objetivo
4
-
5
- Esta guía muestra cómo migrar de la implementación actual fragmentada (JWTSessionProvider + hooks individuales) al nuevo sistema unificado **CrudifyDataProvider**.
6
-
7
- ## 📊 Comparación: Antes vs Después
8
-
9
- ### ❌ ANTES (Sistema Fragmentado)
10
-
11
- ```tsx
12
- // App.tsx - Múltiples providers y configuraciones
13
- import { JWTSessionProvider } from "./services/jwtSession"
14
- import { CrudifyProvider } from "@nocios/crudify-ui" // Conflicto con DataProvider
15
-
16
- function App() {
17
- return (
18
- <JWTSessionProvider>
19
- <CrudifyProvider
20
- env={process.env.VITE_TEST_ENV}
21
- publicApiKey={process.env.VITE_TEST_PUBLIC_API_KEY}
22
- >
23
- <Routes>
24
- {/* Tu aplicación */}
25
- </Routes>
26
- </CrudifyProvider>
27
- </JWTSessionProvider>
28
- )
29
- }
30
-
31
- // TestPage.tsx - Múltiples hooks y estado fragmentado
32
- import { useJWTSession } from "../services/jwtSession"
33
- import { useUserProfileWithJWT } from "../hooks/useUserProfileWithJWT"
34
- import { crudify } from "@nocios/crudify-ui"
35
-
36
- function TestPage() {
37
- const { token, user, isAuthenticated, logout } = useJWTSession()
38
- const { userProfile, loading, error, extendedData } = useUserProfileWithJWT({
39
- autoFetch: isAuthenticated
40
- })
41
-
42
- // Problemas comunes:
43
- // - "Crudify: Not initialized" errors
44
- // - Doble inicialización entre providers
45
- // - Estado desincronizado entre hooks
46
- // - Configuración duplicada y fragmentada
47
- }
48
- ```
49
-
50
- ### ✅ DESPUÉS (Sistema Unificado)
51
-
52
- ```tsx
53
- // App.tsx - Un solo provider con toda la funcionalidad
54
- import { CrudifyDataProvider } from "@nocios/crudify-ui"
55
-
56
- function App() {
57
- return (
58
- <CrudifyDataProvider
59
- env="dev" // o desde process.env.VITE_TEST_ENV
60
- publicApiKey="tu-api-key" // o desde process.env.VITE_TEST_PUBLIC_API_KEY
61
- appName="Mi App"
62
- >
63
- <Routes>
64
- {/* Tu aplicación */}
65
- </Routes>
66
- </CrudifyDataProvider>
67
- )
68
- }
69
-
70
- // TestPage.tsx - Hooks simples y estado unificado
71
- import { useCrudifyAuth, useCrudifyUser, useCrudifyData } from "@nocios/crudify-ui"
72
-
73
- function TestPage() {
74
- const { isAuthenticated, token, user, logout } = useCrudifyAuth()
75
- const { userProfile, profileLoading, extendedData } = useCrudifyUser()
76
- const { readItems, isInitialized } = useCrudifyData()
77
-
78
- // Beneficios:
79
- // ✅ Sin errores de inicialización
80
- // ✅ Estado siempre sincronizado
81
- // ✅ Configuración centralizada
82
- // ✅ Hooks más simples y robustos
83
- }
84
- ```
85
-
86
- ## 🔄 Migración Paso a Paso
87
-
88
- ### Paso 1: Actualizar npm-crudify-ui
89
-
90
- ```bash
91
- npm update @nocios/crudify-ui
92
- ```
93
-
94
- ### Paso 2: Reemplazar Providers
95
-
96
- **Antes:**
97
- ```tsx
98
- import { JWTSessionProvider } from "./services/jwtSession"
99
-
100
- function App() {
101
- return (
102
- <JWTSessionProvider>
103
- {/* tu app */}
104
- </JWTSessionProvider>
105
- )
106
- }
107
- ```
108
-
109
- **Después:**
110
- ```tsx
111
- import { CrudifyDataProvider } from "@nocios/crudify-ui"
112
-
113
- function App() {
114
- return (
115
- <CrudifyDataProvider
116
- env="dev" // Configuración directa o desde env vars
117
- publicApiKey="your-key"
118
- appName="Your App"
119
- >
120
- {/* tu app */}
121
- </CrudifyDataProvider>
122
- )
123
- }
124
- ```
125
-
126
- ### Paso 3: Migrar Hooks de Autenticación
127
-
128
- **Antes:**
129
- ```tsx
130
- import { useJWTSession } from "../services/jwtSession"
131
-
132
- function Component() {
133
- const { token, user, isAuthenticated, logout } = useJWTSession()
134
- // ...
135
- }
136
- ```
137
-
138
- **Después:**
139
- ```tsx
140
- import { useCrudifyAuth } from "@nocios/crudify-ui"
141
-
142
- function Component() {
143
- const { token, user, isAuthenticated, logout } = useCrudifyAuth()
144
- // ¡Misma API! Sin cambios en el código
145
- }
146
- ```
147
-
148
- ### Paso 4: Migrar Hooks de Usuario
149
-
150
- **Antes:**
151
- ```tsx
152
- import { useUserProfileWithJWT } from "../hooks/useUserProfileWithJWT"
153
-
154
- function Component() {
155
- const {
156
- userProfile,
157
- loading: profileLoading,
158
- error: profileError,
159
- extendedData
160
- } = useUserProfileWithJWT({ autoFetch: true })
161
- // ...
162
- }
163
- ```
164
-
165
- **Después:**
166
- ```tsx
167
- import { useCrudifyUser } from "@nocios/crudify-ui"
168
-
169
- function Component() {
170
- const {
171
- userProfile,
172
- profileLoading,
173
- profileError,
174
- extendedData
175
- } = useCrudifyUser({ autoFetch: true })
176
- // ¡Misma API! Sin cambios en el código
177
- }
178
- ```
179
-
180
- ### Paso 5: Migrar Operaciones CRUD
181
-
182
- **Antes:**
183
- ```tsx
184
- import { crudify } from "@nocios/crudify-ui"
185
-
186
- async function loadUsers() {
187
- try {
188
- // Posible error: "Crudify: Not initialized"
189
- const response = await crudify.readItems("users", { limit: 10 })
190
- return response.data
191
- } catch (error) {
192
- console.error("Error:", error)
193
- }
194
- }
195
- ```
196
-
197
- **Después:**
198
- ```tsx
199
- import { useCrudifyData } from "@nocios/crudify-ui"
200
-
201
- function Component() {
202
- const { readItems, isReady } = useCrudifyData()
203
-
204
- async function loadUsers() {
205
- if (!isReady()) {
206
- console.warn("Crudify not ready yet")
207
- return
208
- }
209
-
210
- try {
211
- // Sin errores de inicialización garantizado
212
- const response = await readItems("users", { limit: 10 })
213
- return response.data
214
- } catch (error) {
215
- console.error("Error:", error)
216
- }
217
- }
218
- }
219
- ```
220
-
221
- ## 🚀 Ejemplo Completo Migrado
222
-
223
- ### TestPage.tsx - Versión Migrada
224
-
225
- ```tsx
226
- import React from "react"
227
- import {
228
- Box, Container, Paper, Typography, Button,
229
- Divider, Alert, CircularProgress
230
- } from "@mui/material"
231
- import { useNavigate } from "react-router-dom"
232
- import {
233
- useCrudifyAuth,
234
- useCrudifyUser,
235
- useCrudifyConfig
236
- } from "@nocios/crudify-ui"
237
-
238
- const TestPage: React.FC = () => {
239
- const navigate = useNavigate()
240
-
241
- // 🔐 Autenticación unificada
242
- const {
243
- isAuthenticated,
244
- token,
245
- user,
246
- logout,
247
- loading: authLoading
248
- } = useCrudifyAuth()
249
-
250
- // 👤 Datos de usuario desde BD
251
- const {
252
- userProfile,
253
- profileLoading,
254
- profileError,
255
- extendedData,
256
- refreshProfile
257
- } = useCrudifyUser({ autoFetch: isAuthenticated })
258
-
259
- // ⚙️ Configuración actual
260
- const { config, isConfigured, getDebugInfo } = useCrudifyConfig()
261
-
262
- const handleLogout = () => {
263
- logout()
264
- navigate("/")
265
- }
266
-
267
- const handleGoToDashboard = () => {
268
- navigate("/")
269
- }
270
-
271
- // 🚫 Guard: Usuario no autenticado
272
- if (!isAuthenticated) {
273
- return (
274
- <Container maxWidth="md" sx={{ minHeight: "100vh", display: "flex", alignItems: "center" }}>
275
- <Paper elevation={3} sx={{ p: 4, width: "100%", textAlign: "center" }}>
276
- <Typography variant="h4" color="error" gutterBottom>
277
- No estás logueado
278
- </Typography>
279
- <Typography variant="body1" sx={{ mb: 3 }}>
280
- Debes iniciar sesión para ver esta página.
281
- </Typography>
282
- <Button variant="contained" onClick={() => navigate("/")} size="large">
283
- Ir al Login
284
- </Button>
285
- </Paper>
286
- </Container>
287
- )
288
- }
289
-
290
- // 📊 Datos unificados para mostrar
291
- const userData = {
292
- // Sistema de autenticación
293
- authentication: {
294
- isAuthenticated,
295
- hasToken: !!token,
296
- tokenLength: token?.length || 0,
297
- userEmail: user?.email || null,
298
- userId: user?.sub || null,
299
- expiration: user?.exp ? new Date(user.exp * 1000).toISOString() : null
300
- },
301
- // Perfil desde base de datos
302
- profile: {
303
- loading: profileLoading,
304
- error: profileError,
305
- hasProfileData: !!userProfile,
306
- totalFields: extendedData?.totalFields || 0,
307
- basicProfile: userProfile,
308
- displayData: extendedData?.displayData || null
309
- },
310
- // Configuración actual
311
- configuration: {
312
- isConfigured,
313
- env: config?.env,
314
- appName: config?.appName,
315
- configSources: config?.configSource
316
- }
317
- }
318
-
319
- return (
320
- <Container maxWidth="md" sx={{ minHeight: "100vh", py: 4 }}>
321
- <Paper elevation={3} sx={{ p: 4, width: "100%" }}>
322
-
323
- {/* 🎉 Encabezado de éxito */}
324
- <Box sx={{ mb: 4, textAlign: "center" }}>
325
- <Typography variant="h3" component="h1" gutterBottom sx={{ color: "primary.main" }}>
326
- 🎉 Usuario Logueado Exitosamente
327
- </Typography>
328
- <Typography variant="h6" color="text.secondary">
329
- Bienvenido al nuevo sistema unificado CrudifyDataProvider
330
- </Typography>
331
- </Box>
332
-
333
- <Divider sx={{ my: 3 }} />
334
-
335
- {/* 🔐 Datos de autenticación JWT */}
336
- <Box sx={{ mb: 4 }}>
337
- <Typography variant="h5" gutterBottom sx={{ color: "secondary.main" }}>
338
- 🔐 Datos de Autenticación (JWT):
339
- </Typography>
340
-
341
- <Box sx={{ mt: 2, mb: 3 }}>
342
- <Typography variant="h6" sx={{ mb: 1 }}>
343
- 👤 <strong>Usuario:</strong> {user?.email || "Sin email"}
344
- </Typography>
345
- <Typography variant="h6" sx={{ mb: 1 }}>
346
- 🆔 <strong>User ID:</strong> {user?.sub || "No definido"}
347
- </Typography>
348
- <Typography variant="h6" sx={{ mb: 1 }}>
349
- ✅ <strong>Estado:</strong> {isAuthenticated ? "Autenticado (CrudifyDataProvider)" : "No autenticado"}
350
- </Typography>
351
- <Typography variant="h6" sx={{ mb: 1 }}>
352
- 🎟️ <strong>JWT Token:</strong> {token ? "Presente" : "No disponible"}
353
- {token && <span style={{ fontSize: '0.8em', color: '#666' }}> ({token.length} caracteres)</span>}
354
- </Typography>
355
- <Typography variant="h6" sx={{ mb: 1 }}>
356
- ⏰ <strong>Token expira:</strong> {user?.exp ? new Date(user.exp * 1000).toLocaleString() : "No disponible"}
357
- </Typography>
358
- </Box>
359
- </Box>
360
-
361
- <Divider sx={{ my: 3 }} />
362
-
363
- {/* 👤 Datos del perfil de usuario */}
364
- <Box sx={{ mb: 4 }}>
365
- <Typography variant="h5" gutterBottom sx={{ color: "primary.main" }}>
366
- 👤 Perfil de Usuario (Base de Datos):
367
- </Typography>
368
-
369
- {profileLoading && (
370
- <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
371
- <CircularProgress size={20} sx={{ mr: 1 }} />
372
- <Typography>Cargando perfil automáticamente...</Typography>
373
- </Box>
374
- )}
375
-
376
- {profileError && (
377
- <Alert severity="error" sx={{ mb: 2 }}>
378
- Error al cargar perfil: {profileError}
379
- <Button onClick={refreshProfile} size="small" sx={{ ml: 1 }}>
380
- Reintentar
381
- </Button>
382
- </Alert>
383
- )}
384
-
385
- {userProfile && !profileLoading && (
386
- <Box sx={{ mb: 2 }}>
387
- <Typography variant="h6" sx={{ mb: 1 }}>
388
- 👤 <strong>ID:</strong> {extendedData?.displayData?.id || 'No disponible'}
389
- </Typography>
390
- <Typography variant="h6" sx={{ mb: 1 }}>
391
- 📧 <strong>Email:</strong> {extendedData?.displayData?.email || 'No disponible'}
392
- </Typography>
393
- <Typography variant="h6" sx={{ mb: 1 }}>
394
- 🏷️ <strong>Username:</strong> {extendedData?.displayData?.username || 'No disponible'}
395
- </Typography>
396
- <Typography variant="h6" sx={{ mb: 1 }}>
397
- 👨‍💼 <strong>Nombre Completo:</strong> {extendedData?.displayData?.fullName || 'No disponible'}
398
- </Typography>
399
- <Typography variant="h6" sx={{ mb: 1 }}>
400
- 🎭 <strong>Rol:</strong> {extendedData?.displayData?.role || 'No definido'}
401
- </Typography>
402
- <Typography variant="h6" sx={{ mb: 1 }}>
403
- 🔢 <strong>Total campos BD:</strong> {extendedData?.totalFields || 0}
404
- </Typography>
405
- </Box>
406
- )}
407
- </Box>
408
-
409
- <Divider sx={{ my: 3 }} />
410
-
411
- {/* ⚙️ Información de configuración */}
412
- <Box sx={{ mb: 4 }}>
413
- <Typography variant="h5" gutterBottom sx={{ color: "info.main" }}>
414
- ⚙️ Configuración Actual:
415
- </Typography>
416
-
417
- <Box sx={{ mt: 2, mb: 3 }}>
418
- <Typography variant="h6" sx={{ mb: 1 }}>
419
- 🌍 <strong>Entorno:</strong> {config?.env || "No definido"}
420
- </Typography>
421
- <Typography variant="h6" sx={{ mb: 1 }}>
422
- 🏢 <strong>App Name:</strong> {config?.appName || "No definido"}
423
- </Typography>
424
- <Typography variant="h6" sx={{ mb: 1 }}>
425
- ✅ <strong>Configurado:</strong> {isConfigured ? "Sí" : "No"}
426
- </Typography>
427
- <Typography variant="h6" sx={{ mb: 1 }}>
428
- 📚 <strong>Fuentes:</strong> {config?.configSource ? JSON.stringify(config.configSource) : "No disponible"}
429
- </Typography>
430
- </Box>
431
- </Box>
432
-
433
- <Divider sx={{ my: 3 }} />
434
-
435
- {/* 📋 JSON completo para debugging */}
436
- <Box sx={{ mb: 4 }}>
437
- <Typography variant="h5" gutterBottom sx={{ color: "secondary.main" }}>
438
- 📋 Datos Completos (Debug):
439
- </Typography>
440
-
441
- <Paper
442
- elevation={1}
443
- sx={{
444
- p: 3,
445
- backgroundColor: "grey.50",
446
- overflow: "auto",
447
- maxHeight: "400px"
448
- }}
449
- >
450
- <pre style={{
451
- margin: 0,
452
- fontFamily: "monospace",
453
- fontSize: "14px",
454
- whiteSpace: "pre-wrap",
455
- wordBreak: "break-word"
456
- }}>
457
- {JSON.stringify({
458
- userData,
459
- debugInfo: getDebugInfo()
460
- }, null, 2)}
461
- </pre>
462
- </Paper>
463
- </Box>
464
-
465
- <Divider sx={{ my: 3 }} />
466
-
467
- {/* 🎛️ Acciones */}
468
- <Box sx={{ display: "flex", gap: 2, justifyContent: "center", flexWrap: "wrap" }}>
469
- <Button
470
- variant="contained"
471
- color="primary"
472
- onClick={handleGoToDashboard}
473
- size="large"
474
- >
475
- Ir al Dashboard
476
- </Button>
477
-
478
- <Button
479
- variant="outlined"
480
- color="secondary"
481
- onClick={handleLogout}
482
- size="large"
483
- >
484
- Cerrar Sesión
485
- </Button>
486
-
487
- <Button
488
- variant="outlined"
489
- color="info"
490
- onClick={refreshProfile}
491
- size="large"
492
- disabled={profileLoading}
493
- >
494
- {profileLoading ? "Cargando..." : "Refresh Profile"}
495
- </Button>
496
- </Box>
497
- </Paper>
498
- </Container>
499
- )
500
- }
501
-
502
- export default TestPage
503
- ```
504
-
505
- ## 🛡️ Garantías de Compatibilidad
506
-
507
- ### ✅ Para crudia-ui (Producción)
508
- - **Sin cambios**: El sistema legacy sigue funcionando exactamente igual
509
- - **Imports preserved**: Todos los exports existentes se mantienen
510
- - **API compatibility**: 100% compatible con código existente
511
- - **Storage compatibility**: Usa la misma clave `"authToken"`
512
-
513
- ### ✅ Para crudify-ui (Desarrollo)
514
- - **API familiar**: Los nuevos hooks tienen la misma interfaz que los actuales
515
- - **Migración gradual**: Puedes migrar componente por componente
516
- - **Mejor error handling**: Sin más errores "Crudify: Not initialized"
517
- - **Estado unificado**: Un solo source of truth para toda la app
518
-
519
- ## 🚀 Beneficios Inmediatos
520
-
521
- 1. **Sin errores de inicialización**: El doble provider problem está resuelto
522
- 2. **Estado sincronizado**: Token, usuario y configuración siempre consistentes
523
- 3. **Configuración simplificada**: Un solo provider con toda la funcionalidad
524
- 4. **Mejor debugging**: Información completa disponible en `getDebugInfo()`
525
- 5. **Cross-tab sync**: Los tokens se sincronizan automáticamente entre pestañas
526
- 6. **Automatic cleanup**: Tokens expirados se limpian automáticamente
527
-
528
- ## 🔄 Migración Incremental
529
-
530
- Puedes migrar gradualmente:
531
-
532
- 1. **Fase 1**: Envolver la app con `CrudifyDataProvider`
533
- 2. **Fase 2**: Migrar componentes de autenticación usando `useCrudifyAuth`
534
- 3. **Fase 3**: Migrar componentes de usuario usando `useCrudifyUser`
535
- 4. **Fase 4**: Migrar operaciones CRUD usando `useCrudifyData`
536
- 5. **Fase 5**: Remover providers y hooks legacy
537
-
538
- ¡El nuevo sistema está listo para usar! 🎉