@nocios/crudify-ui 1.1.0 → 1.2.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 +665 -98
- package/dist/CrudifyDataProvider-F6UCGYUF.mjs +14 -0
- package/dist/chunk-ZV5J7FRE.mjs +1166 -0
- package/dist/index.d.mts +12 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +1419 -1313
- package/dist/index.mjs +280 -1388
- package/mejoras_npm_lib.md +790 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nocios/crudify-ui - Sistema Completo de Autenticación y CRUD
|
|
2
2
|
|
|
3
|
-
>
|
|
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.
|
|
4
4
|
|
|
5
|
-
## 🚀
|
|
5
|
+
## 🚀 Instalación
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @nocios/crudify-ui
|
|
@@ -10,23 +10,343 @@ npm install @nocios/crudify-ui
|
|
|
10
10
|
|
|
11
11
|
## 📋 Dependencias Requeridas
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Asegúrate de tener estas dependencias en tu proyecto:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npm install react react-dom @mui/material @mui/icons-material
|
|
16
|
+
npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## ⭐ **NUEVO: Sistema Unificado (Recomendado)**
|
|
20
20
|
|
|
21
|
-
###
|
|
21
|
+
### 🏗️ Implementación con CrudifyDataProvider
|
|
22
|
+
|
|
23
|
+
El nuevo sistema unificado proporciona inicialización robusta, gestión automática de tokens y operaciones CRUD sin configuración compleja:
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
// App.tsx - Configuración principal
|
|
27
|
+
import React from 'react';
|
|
28
|
+
import { CrudifyDataProvider } from '@nocios/crudify-ui';
|
|
29
|
+
import AppContent from './AppContent';
|
|
30
|
+
|
|
31
|
+
function App() {
|
|
32
|
+
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>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default App;
|
|
44
|
+
```
|
|
45
|
+
|
|
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';
|
|
51
|
+
|
|
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
|
+
```
|
|
22
77
|
|
|
23
78
|
```tsx
|
|
24
|
-
|
|
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
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 🔧 **Hooks Disponibles**
|
|
122
|
+
|
|
123
|
+
### 🎯 **useCrudifyAuth** - Gestión de Autenticación
|
|
124
|
+
|
|
125
|
+
Hook principal para manejar el estado de autenticación:
|
|
126
|
+
|
|
127
|
+
```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
|
+
```
|
|
156
|
+
|
|
157
|
+
### 📊 **useCrudifyData** - Operaciones CRUD
|
|
158
|
+
|
|
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
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<div>
|
|
193
|
+
<button onClick={handleLoadProducts} disabled={!isReady}>
|
|
194
|
+
Cargar Productos
|
|
195
|
+
</button>
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 👤 **useCrudifyUser** - Gestión de Usuario
|
|
202
|
+
|
|
203
|
+
Hook especializado para datos del usuario:
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
import { useCrudifyUser } from '@nocios/crudify-ui';
|
|
207
|
+
|
|
208
|
+
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';
|
|
238
|
+
|
|
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();
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<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>
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## ⚙️ **Configuración del CrudifyDataProvider**
|
|
259
|
+
|
|
260
|
+
### Props del Provider
|
|
261
|
+
|
|
262
|
+
```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
|
+
};
|
|
278
|
+
|
|
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)
|
|
283
|
+
|
|
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
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import { CrudifyDataProvider } from '@nocios/crudify-ui';
|
|
298
|
+
|
|
299
|
+
function App() {
|
|
300
|
+
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>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## 🎨 **Componente CrudifyLogin**
|
|
334
|
+
|
|
335
|
+
### Uso Standalone (Sistema Legacy)
|
|
336
|
+
|
|
337
|
+
Para uso directo del componente de login sin el provider unificado:
|
|
338
|
+
|
|
339
|
+
```tsx
|
|
340
|
+
import { CrudifyLogin } from '@nocios/crudify-ui';
|
|
25
341
|
|
|
26
342
|
function LoginPage() {
|
|
27
343
|
const handleLoginSuccess = (token: string, redirectUrl?: string) => {
|
|
28
|
-
//
|
|
344
|
+
// ⚠️ Importante: Sincronizar con el sistema global
|
|
29
345
|
sessionStorage.setItem("authToken", token);
|
|
346
|
+
|
|
347
|
+
// Si usas CrudifyDataProvider, también sincronizar ahí
|
|
348
|
+
// setToken(token); // usando useCrudifyAuth
|
|
349
|
+
|
|
30
350
|
window.location.href = redirectUrl || "/dashboard";
|
|
31
351
|
};
|
|
32
352
|
|
|
@@ -35,60 +355,63 @@ function LoginPage() {
|
|
|
35
355
|
config={{
|
|
36
356
|
publicApiKey: "tu-api-key",
|
|
37
357
|
env: "prod",
|
|
38
|
-
appName: "Mi
|
|
358
|
+
appName: "Mi Aplicación",
|
|
359
|
+
logo: "/logo.png",
|
|
360
|
+
colors: {
|
|
361
|
+
primaryColor: "#1066BA",
|
|
362
|
+
bgColor: "#f5f5f5"
|
|
363
|
+
},
|
|
364
|
+
loginActions: ["forgotPassword", "createUser"]
|
|
39
365
|
}}
|
|
40
366
|
onLoginSuccess={handleLoginSuccess}
|
|
367
|
+
onError={(error) => console.error('Login error:', error)}
|
|
41
368
|
redirectUrl="/dashboard"
|
|
369
|
+
language="es"
|
|
42
370
|
/>
|
|
43
371
|
);
|
|
44
372
|
}
|
|
45
373
|
```
|
|
46
374
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### Props del Componente
|
|
50
|
-
|
|
51
|
-
| Prop | Tipo | Default | Descripcion |
|
|
52
|
-
| --------------------- | ----------------------------------------------- | --------- | --------------------------------- |
|
|
53
|
-
| `config` | `CrudifyLoginConfig` | `{}` | Configuracion del componente |
|
|
54
|
-
| `onLoginSuccess` | `(token: string, redirectUrl?: string) => void` | - | Callback cuando login es exitoso |
|
|
55
|
-
| `onError` | `(error: string) => void` | - | Callback para manejo de errores |
|
|
56
|
-
| `onScreenChange` | `(screen: string, params?: object) => void` | - | Callback para cambios de pantalla |
|
|
57
|
-
| `onExternalNavigate` | `(path: string) => void` | - | Callback para navegacion externa |
|
|
58
|
-
| `initialScreen` | `login,forgotPassword,resetPassword,checkCode` | `"login"` | Pantalla inicial |
|
|
59
|
-
| `redirectUrl` | `string` | `"/"` | URL de redireccion post-login |
|
|
60
|
-
| `autoReadFromCookies` | `boolean` | `true` | Leer configuracion desde cookies |
|
|
61
|
-
| `language` | `string` | `"en"` | Idioma de la interfaz |
|
|
62
|
-
| `translations` | `CrudifyLoginTranslations` | - | Traducciones customizadas |
|
|
63
|
-
| `translationsUrl` | `string` | - | URL para cargar traducciones |
|
|
64
|
-
|
|
65
|
-
### Configuracion (CrudifyLoginConfig)
|
|
375
|
+
### Props del CrudifyLogin
|
|
66
376
|
|
|
67
377
|
```tsx
|
|
68
|
-
interface
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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;
|
|
79
399
|
}
|
|
80
400
|
```
|
|
81
401
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
### Colores y Branding
|
|
402
|
+
### Personalización Visual
|
|
85
403
|
|
|
86
404
|
```tsx
|
|
87
405
|
<CrudifyLogin
|
|
88
406
|
config={{
|
|
89
407
|
appName: "Mi App",
|
|
90
408
|
logo: "/mi-logo.png",
|
|
91
|
-
colors: {
|
|
409
|
+
colors: {
|
|
410
|
+
primaryColor: "#1066BA",
|
|
411
|
+
bgColor: "#f5f5f5",
|
|
412
|
+
textColor: "#333333",
|
|
413
|
+
buttonTextColor: "#ffffff"
|
|
414
|
+
},
|
|
92
415
|
}}
|
|
93
416
|
/>
|
|
94
417
|
```
|
|
@@ -97,65 +420,63 @@ interface CrudifyLoginConfig {
|
|
|
97
420
|
|
|
98
421
|
```tsx
|
|
99
422
|
const misTraduciones = {
|
|
100
|
-
"login.title": "Iniciar
|
|
423
|
+
"login.title": "Iniciar Sesión",
|
|
101
424
|
"login.usernameLabel": "Usuario o Email",
|
|
102
|
-
"login.passwordLabel": "
|
|
425
|
+
"login.passwordLabel": "Contraseña",
|
|
103
426
|
"login.loginButton": "Entrar",
|
|
104
|
-
"login.forgotPasswordLink": "¿Olvidaste tu
|
|
427
|
+
"login.forgotPasswordLink": "¿Olvidaste tu contraseña?",
|
|
428
|
+
"forgotPassword.title": "Recuperar Contraseña",
|
|
429
|
+
"forgotPassword.emailLabel": "Email",
|
|
430
|
+
"forgotPassword.sendButton": "Enviar Código"
|
|
105
431
|
};
|
|
106
432
|
|
|
107
|
-
<CrudifyLogin
|
|
433
|
+
<CrudifyLogin
|
|
434
|
+
translations={misTraduciones}
|
|
435
|
+
language="es"
|
|
436
|
+
/>;
|
|
108
437
|
```
|
|
109
438
|
|
|
110
|
-
##
|
|
439
|
+
## 🛠️ **Utilidades y Funciones de Alto Nivel**
|
|
111
440
|
|
|
112
|
-
###
|
|
441
|
+
### 🔑 **Acceso Directo a la Instancia**
|
|
113
442
|
|
|
114
|
-
|
|
443
|
+
Para casos avanzados donde necesitas acceso directo a la instancia de crudify:
|
|
115
444
|
|
|
116
445
|
```tsx
|
|
117
|
-
import {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
<p>Email: {userProfile?.email}</p>
|
|
129
|
-
<button onClick={refreshProfile}>Actualizar Perfil</button>
|
|
130
|
-
</div>
|
|
131
|
-
);
|
|
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
|
+
}
|
|
132
457
|
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### useCrudifyLogin
|
|
136
|
-
|
|
137
|
-
Hook interno para configuracion avanzada:
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
import { useCrudifyLogin } from "@nocios/crudify-ui";
|
|
141
|
-
|
|
142
|
-
function CustomLogin() {
|
|
143
|
-
const { config } = useCrudifyLogin({
|
|
144
|
-
publicApiKey: "mi-api-key",
|
|
145
|
-
env: "prod",
|
|
146
|
-
});
|
|
147
458
|
|
|
148
|
-
|
|
149
|
-
|
|
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
|
+
}
|
|
150
468
|
}
|
|
151
469
|
```
|
|
152
470
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
### JWT Utils
|
|
471
|
+
### 🔐 **Utilidades de Tokens JWT**
|
|
156
472
|
|
|
157
473
|
```tsx
|
|
158
|
-
import {
|
|
474
|
+
import {
|
|
475
|
+
getCurrentUserEmail,
|
|
476
|
+
decodeJwtSafely,
|
|
477
|
+
isTokenExpired,
|
|
478
|
+
getTokenPayload
|
|
479
|
+
} from '@nocios/crudify-ui';
|
|
159
480
|
|
|
160
481
|
// Obtener email del usuario actual
|
|
161
482
|
const userEmail = getCurrentUserEmail();
|
|
@@ -165,36 +486,125 @@ const payload = decodeJwtSafely(token);
|
|
|
165
486
|
|
|
166
487
|
// Verificar si token ha expirado
|
|
167
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));
|
|
168
494
|
```
|
|
169
495
|
|
|
170
|
-
### Storage Seguro
|
|
496
|
+
### 🔒 **Storage Seguro**
|
|
171
497
|
|
|
172
498
|
```tsx
|
|
173
|
-
import {
|
|
499
|
+
import {
|
|
500
|
+
secureSessionStorage,
|
|
501
|
+
secureLocalStorage,
|
|
502
|
+
TokenManager
|
|
503
|
+
} from '@nocios/crudify-ui';
|
|
174
504
|
|
|
175
|
-
//
|
|
505
|
+
// Uso básico del storage seguro
|
|
176
506
|
secureSessionStorage.setToken(token);
|
|
507
|
+
const currentToken = secureSessionStorage.getToken();
|
|
508
|
+
secureSessionStorage.clear();
|
|
177
509
|
|
|
178
|
-
//
|
|
179
|
-
const
|
|
510
|
+
// Uso avanzado con TokenManager
|
|
511
|
+
const tokenManager = TokenManager.getInstance();
|
|
180
512
|
|
|
181
|
-
//
|
|
182
|
-
|
|
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();
|
|
183
524
|
```
|
|
184
525
|
|
|
185
|
-
### Manejo de Errores
|
|
526
|
+
### 🚨 **Manejo de Errores**
|
|
186
527
|
|
|
187
528
|
```tsx
|
|
188
|
-
import {
|
|
189
|
-
|
|
529
|
+
import {
|
|
530
|
+
handleCrudifyError,
|
|
531
|
+
ERROR_CODES,
|
|
532
|
+
getErrorMessage,
|
|
533
|
+
CrudifyErrorHandler
|
|
534
|
+
} from '@nocios/crudify-ui';
|
|
535
|
+
|
|
536
|
+
// Manejo básico de errores
|
|
190
537
|
try {
|
|
191
|
-
|
|
538
|
+
await readItems('products', {});
|
|
192
539
|
} catch (error) {
|
|
193
540
|
const parsedErrors = handleCrudifyError(error);
|
|
194
541
|
parsedErrors.forEach((err) => {
|
|
195
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
|
+
}
|
|
196
556
|
});
|
|
197
557
|
}
|
|
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
|
+
```
|
|
571
|
+
|
|
572
|
+
### 🔍 **Hooks Legacy (Compatibilidad)**
|
|
573
|
+
|
|
574
|
+
Para migrar desde versiones anteriores:
|
|
575
|
+
|
|
576
|
+
```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();
|
|
586
|
+
|
|
587
|
+
if (loading) return <div>Cargando...</div>;
|
|
588
|
+
if (error) return <div>Error: {error}</div>;
|
|
589
|
+
|
|
590
|
+
return (
|
|
591
|
+
<div>
|
|
592
|
+
<h1>Bienvenido, {userProfile?.name}</h1>
|
|
593
|
+
<p>Email: {userProfile?.email}</p>
|
|
594
|
+
<button onClick={refreshProfile}>Actualizar Perfil</button>
|
|
595
|
+
</div>
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
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
|
+
}
|
|
198
608
|
```
|
|
199
609
|
|
|
200
610
|
## 📱 Flujos de Autenticacion
|
|
@@ -673,5 +1083,162 @@ VITE_TEST_ENV=prod
|
|
|
673
1083
|
|
|
674
1084
|
---
|
|
675
1085
|
|
|
676
|
-
**
|
|
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
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// ✅ Usar hooks especializados
|
|
1114
|
+
const { isAuthenticated } = useCrudifyAuth();
|
|
1115
|
+
const { readItems } = useCrudifyData();
|
|
1116
|
+
const { user } = useCrudifyUser();
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
### ❌ **DON'T - Evitar**
|
|
1120
|
+
|
|
1121
|
+
```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
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
### 🔄 **Migración desde Versiones Anteriores**
|
|
1143
|
+
|
|
1144
|
+
Si tienes código existente, puedes migrar gradualmente:
|
|
1145
|
+
|
|
1146
|
+
```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
|
|
1185
|
+
|
|
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>
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
### 📊 **Debugging y Desarrollo**
|
|
1203
|
+
|
|
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
|
+
```
|
|
1217
|
+
|
|
1218
|
+
---
|
|
1219
|
+
|
|
1220
|
+
## 📋 **Changelog y Migración**
|
|
1221
|
+
|
|
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
|
|
1231
|
+
|
|
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
|
|
1237
|
+
|
|
1238
|
+
---
|
|
1239
|
+
|
|
1240
|
+
**Version:** 1.2.1
|
|
677
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)
|