@nocios/crudify-components 1.0.0
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/.github/workflows/test.yml +59 -0
- package/.nvmrc +1 -0
- package/README.md +398 -0
- package/README_DEPTH.md +1230 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/coverage-final.json +85 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +506 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/CrudiaMarkdownField-C54-A_J3.d.mts +328 -0
- package/dist/CrudiaMarkdownField-C8HQh7s5.d.ts +328 -0
- package/dist/GlobalNotificationProvider-Zq18OkpI.d.mts +96 -0
- package/dist/GlobalNotificationProvider-Zq18OkpI.d.ts +96 -0
- package/dist/api-B4uXiHF0.d.mts +118 -0
- package/dist/api-B4uXiHF0.d.ts +118 -0
- package/dist/chunk-2XOTIEKS.js +1 -0
- package/dist/chunk-5HFI5CZ5.js +1 -0
- package/dist/chunk-CHDM7KGH.js +1 -0
- package/dist/chunk-HVTRRU4W.mjs +1 -0
- package/dist/chunk-JAPL7EZJ.mjs +1 -0
- package/dist/chunk-JNEWPO2J.mjs +1 -0
- package/dist/chunk-MFYHD6S5.js +1 -0
- package/dist/chunk-MGJZTOEM.mjs +1 -0
- package/dist/chunk-NBQH6QOU.mjs +1 -0
- package/dist/chunk-NSV6ECYO.js +1 -0
- package/dist/chunk-PNI3ZBZV.js +1 -0
- package/dist/chunk-U4RS66TB.mjs +1 -0
- package/dist/components.d.mts +24 -0
- package/dist/components.d.ts +24 -0
- package/dist/components.js +1 -0
- package/dist/components.mjs +1 -0
- package/dist/errorTranslation-DGdrMidg.d.ts +143 -0
- package/dist/errorTranslation-qwwQTvCO.d.mts +143 -0
- package/dist/hooks.d.mts +6 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.js +1 -0
- package/dist/hooks.mjs +1 -0
- package/dist/index-BUKX3duW.d.ts +854 -0
- package/dist/index-Y9tTsinC.d.mts +854 -0
- package/dist/index.d.mts +1274 -0
- package/dist/index.d.ts +1274 -0
- package/dist/index.js +6 -0
- package/dist/index.mjs +6 -0
- package/dist/utils.d.mts +175 -0
- package/dist/utils.d.ts +175 -0
- package/dist/utils.js +1 -0
- package/dist/utils.mjs +1 -0
- package/package.json +88 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
import { A as AutoGenerateConfig } from './GlobalNotificationProvider-Zq18OkpI.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Password validation types and constants
|
|
7
|
+
* Used for password requirements validation in forms
|
|
8
|
+
*/
|
|
9
|
+
type PasswordRuleType = "minLength" | "alphanumeric" | "uppercase" | "lowercase" | "number" | "special";
|
|
10
|
+
interface PasswordRule {
|
|
11
|
+
type: PasswordRuleType;
|
|
12
|
+
value?: number | string;
|
|
13
|
+
message: string;
|
|
14
|
+
enforce?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface EvaluatedPasswordRule {
|
|
17
|
+
message: string;
|
|
18
|
+
valid: boolean;
|
|
19
|
+
enforce: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Default password rules used as fallback when no custom rules are provided.
|
|
23
|
+
* Messages are translation keys that should be translated at runtime.
|
|
24
|
+
*/
|
|
25
|
+
declare const DEFAULT_PASSWORD_RULES: PasswordRule[];
|
|
26
|
+
|
|
27
|
+
type BoxScreenType = "login" | "signUp" | "forgotPassword" | "resetPassword" | "checkCode";
|
|
28
|
+
interface CrudifyLoginConfig {
|
|
29
|
+
publicApiKey?: string | null;
|
|
30
|
+
env?: "dev" | "stg" | "api" | "prod";
|
|
31
|
+
appName?: string;
|
|
32
|
+
logo?: string;
|
|
33
|
+
loginActions?: string[];
|
|
34
|
+
}
|
|
35
|
+
interface CrudifyLoginTranslations {
|
|
36
|
+
[key: string]: string | CrudifyLoginTranslations;
|
|
37
|
+
}
|
|
38
|
+
interface UserLoginData {
|
|
39
|
+
token: string;
|
|
40
|
+
username?: string;
|
|
41
|
+
email?: string;
|
|
42
|
+
userId?: string;
|
|
43
|
+
profile?: Record<string, unknown>;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
interface CrudifyLoginProps {
|
|
47
|
+
onScreenChange?: (screen: BoxScreenType, params?: Record<string, string>) => void;
|
|
48
|
+
onExternalNavigate?: (path: string) => void;
|
|
49
|
+
onLoginSuccess?: (userData: UserLoginData, redirectUrl?: string) => void;
|
|
50
|
+
onError?: (error: string) => void;
|
|
51
|
+
initialScreen?: BoxScreenType;
|
|
52
|
+
redirectUrl?: string;
|
|
53
|
+
autoReadFromCookies?: boolean;
|
|
54
|
+
translations?: CrudifyLoginTranslations;
|
|
55
|
+
translationsUrl?: string;
|
|
56
|
+
language?: string;
|
|
57
|
+
/** Custom password rules for reset password form. Falls back to default rules if not provided. */
|
|
58
|
+
passwordRules?: PasswordRule[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
declare const CrudifyLogin: React.FC<CrudifyLoginProps>;
|
|
62
|
+
|
|
63
|
+
interface UserProfileDisplayProps {
|
|
64
|
+
showExtendedData?: boolean;
|
|
65
|
+
showProfileCard?: boolean;
|
|
66
|
+
autoRefresh?: boolean;
|
|
67
|
+
}
|
|
68
|
+
declare const UserProfileDisplay: React.FC<UserProfileDisplayProps>;
|
|
69
|
+
|
|
70
|
+
declare const POLICY_ACTIONS: readonly ["create", "read", "update", "delete"];
|
|
71
|
+
type PolicyAction = typeof POLICY_ACTIONS[number];
|
|
72
|
+
declare const PREFERRED_POLICY_ORDER: PolicyAction[];
|
|
73
|
+
|
|
74
|
+
type Policy = {
|
|
75
|
+
id: string;
|
|
76
|
+
action: PolicyAction;
|
|
77
|
+
fields?: {
|
|
78
|
+
allow: string[];
|
|
79
|
+
owner_allow: string[];
|
|
80
|
+
deny: string[];
|
|
81
|
+
};
|
|
82
|
+
permission?: string;
|
|
83
|
+
};
|
|
84
|
+
type FieldErrorMap = string | ({
|
|
85
|
+
_error?: string;
|
|
86
|
+
} & Record<string, string | undefined>);
|
|
87
|
+
interface PoliciesProps {
|
|
88
|
+
policies: Policy[];
|
|
89
|
+
onChange: (next: Policy[]) => void;
|
|
90
|
+
availableFields: string[];
|
|
91
|
+
errors?: FieldErrorMap;
|
|
92
|
+
isSubmitting?: boolean;
|
|
93
|
+
}
|
|
94
|
+
declare const Policies: React.FC<PoliciesProps>;
|
|
95
|
+
|
|
96
|
+
declare function LoginComponent(): react_jsx_runtime.JSX.Element;
|
|
97
|
+
/**
|
|
98
|
+
* Componente simple de estado de sesión para mostrar en cualquier lugar
|
|
99
|
+
*/
|
|
100
|
+
declare function SessionStatus(): react_jsx_runtime.JSX.Element;
|
|
101
|
+
|
|
102
|
+
interface CrudiaAutoGenerateProps {
|
|
103
|
+
config: AutoGenerateConfig;
|
|
104
|
+
value?: string;
|
|
105
|
+
onChange?: (value: string) => void;
|
|
106
|
+
label?: string;
|
|
107
|
+
error?: boolean;
|
|
108
|
+
helperText?: string;
|
|
109
|
+
readOnly?: boolean;
|
|
110
|
+
disabled?: boolean;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Componente para campos autogenerados (códigos secuenciales)
|
|
114
|
+
*
|
|
115
|
+
* Características:
|
|
116
|
+
* - Auto-fetch del código al montar
|
|
117
|
+
* - Detección automática de errores de duplicado
|
|
118
|
+
* - Botón "Regenerar" en endAdornment cuando hay error de duplicado
|
|
119
|
+
* - Estados de loading y error
|
|
120
|
+
* - Readonly por defecto
|
|
121
|
+
* - Integración con useAutoGenerate hook
|
|
122
|
+
*
|
|
123
|
+
* Flujo de Manejo de Errores de Duplicado:
|
|
124
|
+
* 1. Usuario abre formulario → Código se genera automáticamente (ej: PROD-0013671)
|
|
125
|
+
* 2. Usuario llena formulario y hace submit
|
|
126
|
+
* 3. Backend retorna error "duplicate key" o "unique constraint"
|
|
127
|
+
* 4. Formulario padre pasa error={true} y helperText="El código ya existe"
|
|
128
|
+
* 5. Componente detecta palabras clave de duplicado y muestra botón de regenerar
|
|
129
|
+
* 6. Usuario hace click en botón de regenerar → Nuevo código se genera
|
|
130
|
+
* 7. onChange se llama con nuevo código → Formulario padre limpia el error
|
|
131
|
+
* 8. Botón de regenerar desaparece (porque ya no hay error)
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```tsx
|
|
135
|
+
* // Uso básico
|
|
136
|
+
* <CrudiaAutoGenerate
|
|
137
|
+
* config={{ prefix: "PROD-", padding: 7 }}
|
|
138
|
+
* onChange={(code) => setFormData({ ...formData, barCode: code })}
|
|
139
|
+
* label="Código de Barras"
|
|
140
|
+
* />
|
|
141
|
+
*
|
|
142
|
+
* // Con manejo de error de duplicado desde el formulario
|
|
143
|
+
* <CrudiaAutoGenerate
|
|
144
|
+
* config={{ prefix: "PROD-", padding: 7 }}
|
|
145
|
+
* onChange={(code) => {
|
|
146
|
+
* setFormData({ ...formData, barCode: code });
|
|
147
|
+
* // Limpiar error cuando cambia el código
|
|
148
|
+
* setFormErrors({ ...formErrors, barCode: null });
|
|
149
|
+
* }}
|
|
150
|
+
* label="Código de Barras"
|
|
151
|
+
* error={!!formErrors.barCode}
|
|
152
|
+
* helperText={formErrors.barCode}
|
|
153
|
+
* />
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
declare const CrudiaAutoGenerate: React.FC<CrudiaAutoGenerateProps>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* File field component with:
|
|
160
|
+
* - Drag & drop
|
|
161
|
+
* - File preview
|
|
162
|
+
* - Progress bar during upload
|
|
163
|
+
* - Delete files (soft delete)
|
|
164
|
+
* - Type and size validation
|
|
165
|
+
* - Single/multiple support
|
|
166
|
+
*/
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Deletion handlers exposed by the component
|
|
170
|
+
*/
|
|
171
|
+
interface CrudiaFileFieldDeletionHandlers {
|
|
172
|
+
/** Execute all pending deletions (call this on form success callback) */
|
|
173
|
+
commitDeletions: () => Promise<{
|
|
174
|
+
success: boolean;
|
|
175
|
+
errors: string[];
|
|
176
|
+
}>;
|
|
177
|
+
/** Cancel all pending deletions and restore files */
|
|
178
|
+
restorePendingDeletions: () => void;
|
|
179
|
+
/** Whether there are files pending deletion */
|
|
180
|
+
hasPendingDeletions: boolean;
|
|
181
|
+
/** Mark the field as submitted (triggers validation errors display) */
|
|
182
|
+
markAsSubmitted: () => void;
|
|
183
|
+
/** Whether the field is valid */
|
|
184
|
+
isValid: boolean;
|
|
185
|
+
/** Whether files are currently being uploaded */
|
|
186
|
+
isUploading: boolean;
|
|
187
|
+
/** Wait for all uploads to complete - returns completed file paths */
|
|
188
|
+
waitForUploads: () => Promise<string[]>;
|
|
189
|
+
/** Get completed file paths synchronously (may be stale if uploads just finished) */
|
|
190
|
+
getCompletedFilePaths: () => string[];
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Props del componente CrudiaFileField
|
|
194
|
+
*/
|
|
195
|
+
interface CrudiaFileFieldProps {
|
|
196
|
+
/** Label del campo */
|
|
197
|
+
label?: string;
|
|
198
|
+
/** Tipos MIME permitidos (ej: ["image/png", "image/jpeg"]) */
|
|
199
|
+
accept?: string[];
|
|
200
|
+
/** Tamaño máximo por archivo en bytes */
|
|
201
|
+
maxFileSize?: number;
|
|
202
|
+
/** Permitir múltiples archivos */
|
|
203
|
+
multiple?: boolean;
|
|
204
|
+
/** Número máximo de archivos (solo si multiple=true) */
|
|
205
|
+
maxFiles?: number;
|
|
206
|
+
/** Número mínimo de archivos requeridos */
|
|
207
|
+
minFiles?: number;
|
|
208
|
+
/** Campo requerido */
|
|
209
|
+
required?: boolean;
|
|
210
|
+
/** Campo deshabilitado */
|
|
211
|
+
disabled?: boolean;
|
|
212
|
+
/** Error externo */
|
|
213
|
+
error?: boolean;
|
|
214
|
+
/** Texto de ayuda o error */
|
|
215
|
+
helperText?: string;
|
|
216
|
+
/**
|
|
217
|
+
* Callback cuando cambian los filePaths de archivos completados
|
|
218
|
+
* Los filePaths son rutas relativas con visibility (public/... o private/...)
|
|
219
|
+
*/
|
|
220
|
+
onChange?: (filePaths: string[]) => void;
|
|
221
|
+
/** Callback cuando se valida el campo */
|
|
222
|
+
onValidation?: (isValid: boolean, error: string | null) => void;
|
|
223
|
+
/** Archivos existentes (para edición) - usar filePath (ruta relativa con visibility) */
|
|
224
|
+
initialFiles?: Array<{
|
|
225
|
+
filePath: string;
|
|
226
|
+
name: string;
|
|
227
|
+
size?: number;
|
|
228
|
+
contentType?: string;
|
|
229
|
+
}>;
|
|
230
|
+
/** Texto placeholder para drag zone */
|
|
231
|
+
placeholder?: string;
|
|
232
|
+
/** Mostrar lista de archivos */
|
|
233
|
+
showFileList?: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* Visibilidad de los archivos subidos
|
|
236
|
+
* @default "private"
|
|
237
|
+
*/
|
|
238
|
+
visibility?: "public" | "private";
|
|
239
|
+
/**
|
|
240
|
+
* Mostrar preview de imágenes
|
|
241
|
+
* @default true para imágenes
|
|
242
|
+
*/
|
|
243
|
+
showPreview?: boolean;
|
|
244
|
+
/**
|
|
245
|
+
* URL base para prefijar al path del archivo
|
|
246
|
+
* El valor guardado será baseUrl + path
|
|
247
|
+
* También se usa para previsualizar/descargar archivos públicos en modo readonly
|
|
248
|
+
*/
|
|
249
|
+
baseUrl: string;
|
|
250
|
+
/**
|
|
251
|
+
* Callback called when deletion handlers are ready
|
|
252
|
+
* Use this to get access to commitDeletions and restorePendingDeletions functions
|
|
253
|
+
*/
|
|
254
|
+
onDeletionHandlersReady?: (handlers: CrudiaFileFieldDeletionHandlers) => void;
|
|
255
|
+
/**
|
|
256
|
+
* Form mode: 'create' or 'edit' - affects delete behavior
|
|
257
|
+
* - create: delete shows confirmation and deletes immediately from server
|
|
258
|
+
* - edit: existing files use pendingDeletion, new files delete immediately
|
|
259
|
+
* @default "create"
|
|
260
|
+
*/
|
|
261
|
+
mode?: "create" | "edit";
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Main file field component
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```tsx
|
|
268
|
+
* // Basic usage - single file
|
|
269
|
+
* <CrudiaFileField
|
|
270
|
+
* label="Document"
|
|
271
|
+
* accept={["application/pdf"]}
|
|
272
|
+
* onChange={(filePaths) => setFormData({ ...formData, document: filePaths[0] })}
|
|
273
|
+
* />
|
|
274
|
+
*
|
|
275
|
+
* // Multiple files with limits
|
|
276
|
+
* <CrudiaFileField
|
|
277
|
+
* label="Images"
|
|
278
|
+
* accept={["image/png", "image/jpeg"]}
|
|
279
|
+
* multiple
|
|
280
|
+
* maxFiles={5}
|
|
281
|
+
* minFiles={1}
|
|
282
|
+
* maxFileSize={5 * 1024 * 1024}
|
|
283
|
+
* onChange={(filePaths) => setFormData({ ...formData, images: filePaths })}
|
|
284
|
+
* />
|
|
285
|
+
*
|
|
286
|
+
* // With initial files (edit mode)
|
|
287
|
+
* <CrudiaFileField
|
|
288
|
+
* label="Attachments"
|
|
289
|
+
* multiple
|
|
290
|
+
* initialFiles={existingFiles}
|
|
291
|
+
* onChange={handleFilesChange}
|
|
292
|
+
* />
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
declare const CrudiaFileField: React.FC<CrudiaFileFieldProps>;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Markdown field component with rich text editing using MDXEditor
|
|
299
|
+
* - WYSIWYG markdown editing
|
|
300
|
+
* - Toolbar with formatting options
|
|
301
|
+
* - Saves content as markdown string
|
|
302
|
+
*/
|
|
303
|
+
|
|
304
|
+
interface CrudiaMarkdownFieldProps {
|
|
305
|
+
/** Field label */
|
|
306
|
+
label?: string;
|
|
307
|
+
/** Current value (markdown string) */
|
|
308
|
+
value?: string;
|
|
309
|
+
/** Callback when content changes */
|
|
310
|
+
onChange?: (value: string) => void;
|
|
311
|
+
/** Required field */
|
|
312
|
+
required?: boolean;
|
|
313
|
+
/** Disabled/readonly field */
|
|
314
|
+
disabled?: boolean;
|
|
315
|
+
/** External error state */
|
|
316
|
+
error?: boolean;
|
|
317
|
+
/** Helper or error text */
|
|
318
|
+
helperText?: string;
|
|
319
|
+
/** Placeholder text */
|
|
320
|
+
placeholder?: string;
|
|
321
|
+
/** Minimum editor height in pixels */
|
|
322
|
+
minHeight?: number;
|
|
323
|
+
/** Maximum editor height in pixels (scrolls after) */
|
|
324
|
+
maxHeight?: number;
|
|
325
|
+
}
|
|
326
|
+
declare const CrudiaMarkdownField: React.FC<CrudiaMarkdownFieldProps>;
|
|
327
|
+
|
|
328
|
+
export { type BoxScreenType as B, CrudifyLogin as C, DEFAULT_PASSWORD_RULES as D, type EvaluatedPasswordRule as E, LoginComponent as L, Policies as P, SessionStatus as S, UserProfileDisplay as U, CrudiaAutoGenerate as a, CrudiaFileField as b, CrudiaMarkdownField as c, type CrudifyLoginConfig as d, type CrudifyLoginProps as e, type CrudifyLoginTranslations as f, type UserLoginData as g, type PolicyAction as h, type CrudiaAutoGenerateProps as i, type CrudiaFileFieldProps as j, type CrudiaMarkdownFieldProps as k, type PasswordRule as l, POLICY_ACTIONS as m, PREFERRED_POLICY_ORDER as n, type CrudiaFileFieldDeletionHandlers as o, type PasswordRuleType as p };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface AutoGenerateConfig {
|
|
4
|
+
prefix: string;
|
|
5
|
+
padding?: number;
|
|
6
|
+
separator?: string;
|
|
7
|
+
}
|
|
8
|
+
interface UseAutoGenerateReturn {
|
|
9
|
+
value: string;
|
|
10
|
+
loading: boolean;
|
|
11
|
+
error: string | null;
|
|
12
|
+
regenerate: () => Promise<void>;
|
|
13
|
+
clearError: () => void;
|
|
14
|
+
}
|
|
15
|
+
interface UseAutoGenerateOptions {
|
|
16
|
+
autoFetch?: boolean;
|
|
17
|
+
onSuccess?: (value: string) => void;
|
|
18
|
+
onError?: (error: string) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Hook para generar códigos autogenerados desde el servidor
|
|
22
|
+
*
|
|
23
|
+
* ⚠️ IMPORTANTE: Genera y RESERVA el código inmediatamente al montar
|
|
24
|
+
* El servidor incrementa counter en cada llamada, código queda reservado
|
|
25
|
+
*
|
|
26
|
+
* Flujo Normal:
|
|
27
|
+
* 1. Componente monta → fetchNextSequence() ejecuta
|
|
28
|
+
* 2. Servidor incrementa counter y retorna valor
|
|
29
|
+
* 3. Usuario ve código → completa form → guarda exitosamente
|
|
30
|
+
*
|
|
31
|
+
* Flujo de Error (raro):
|
|
32
|
+
* 1. Dos usuarios abren form al mismo tiempo
|
|
33
|
+
* 2. Ambos reciben diferentes códigos (Counter atómico)
|
|
34
|
+
* 3. Usuario A guarda primero → OK
|
|
35
|
+
* 4. Usuario B intenta guardar → Error "Duplicate key" (validación única)
|
|
36
|
+
* 5. Usuario B ve botón "Regenerar código" → Click
|
|
37
|
+
* 6. regenerate() obtiene nuevo código → Usuario B guarda → OK
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* function ProductForm() {
|
|
42
|
+
* const { value, loading, error, regenerate, clearError } = useAutoGenerate({
|
|
43
|
+
* prefix: "PROD-",
|
|
44
|
+
* padding: 7
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* return (
|
|
48
|
+
* <div>
|
|
49
|
+
* <input value={value} readOnly />
|
|
50
|
+
* {loading && <CircularProgress />}
|
|
51
|
+
* {error && (
|
|
52
|
+
* <div>
|
|
53
|
+
* <span>Error: {error}</span>
|
|
54
|
+
* <button onClick={regenerate}>Regenerar código</button>
|
|
55
|
+
* </div>
|
|
56
|
+
* )}
|
|
57
|
+
* </div>
|
|
58
|
+
* );
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
declare const useAutoGenerate: (config: AutoGenerateConfig, options?: UseAutoGenerateOptions) => UseAutoGenerateReturn;
|
|
63
|
+
|
|
64
|
+
type NotificationSeverity = "error" | "info" | "success" | "warning";
|
|
65
|
+
interface Notification {
|
|
66
|
+
id: string;
|
|
67
|
+
message: string;
|
|
68
|
+
severity: NotificationSeverity;
|
|
69
|
+
autoHideDuration?: number;
|
|
70
|
+
persistent?: boolean;
|
|
71
|
+
allowHtml?: boolean;
|
|
72
|
+
}
|
|
73
|
+
interface GlobalNotificationContextValue {
|
|
74
|
+
showNotification: (message: string, severity?: NotificationSeverity, options?: {
|
|
75
|
+
autoHideDuration?: number;
|
|
76
|
+
persistent?: boolean;
|
|
77
|
+
allowHtml?: boolean;
|
|
78
|
+
}) => string;
|
|
79
|
+
hideNotification: (id: string) => void;
|
|
80
|
+
clearAllNotifications: () => void;
|
|
81
|
+
}
|
|
82
|
+
interface GlobalNotificationProviderProps {
|
|
83
|
+
children: React.ReactNode;
|
|
84
|
+
maxNotifications?: number;
|
|
85
|
+
defaultAutoHideDuration?: number;
|
|
86
|
+
position?: {
|
|
87
|
+
vertical: "top" | "bottom";
|
|
88
|
+
horizontal: "left" | "center" | "right";
|
|
89
|
+
};
|
|
90
|
+
enabled?: boolean;
|
|
91
|
+
allowHtml?: boolean;
|
|
92
|
+
}
|
|
93
|
+
declare const GlobalNotificationProvider: React.FC<GlobalNotificationProviderProps>;
|
|
94
|
+
declare const useGlobalNotification: () => GlobalNotificationContextValue;
|
|
95
|
+
|
|
96
|
+
export { type AutoGenerateConfig as A, GlobalNotificationProvider as G, type Notification as N, type UseAutoGenerateReturn as U, type GlobalNotificationProviderProps as a, type NotificationSeverity as b, useAutoGenerate as c, type UseAutoGenerateOptions as d, useGlobalNotification as u };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface AutoGenerateConfig {
|
|
4
|
+
prefix: string;
|
|
5
|
+
padding?: number;
|
|
6
|
+
separator?: string;
|
|
7
|
+
}
|
|
8
|
+
interface UseAutoGenerateReturn {
|
|
9
|
+
value: string;
|
|
10
|
+
loading: boolean;
|
|
11
|
+
error: string | null;
|
|
12
|
+
regenerate: () => Promise<void>;
|
|
13
|
+
clearError: () => void;
|
|
14
|
+
}
|
|
15
|
+
interface UseAutoGenerateOptions {
|
|
16
|
+
autoFetch?: boolean;
|
|
17
|
+
onSuccess?: (value: string) => void;
|
|
18
|
+
onError?: (error: string) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Hook para generar códigos autogenerados desde el servidor
|
|
22
|
+
*
|
|
23
|
+
* ⚠️ IMPORTANTE: Genera y RESERVA el código inmediatamente al montar
|
|
24
|
+
* El servidor incrementa counter en cada llamada, código queda reservado
|
|
25
|
+
*
|
|
26
|
+
* Flujo Normal:
|
|
27
|
+
* 1. Componente monta → fetchNextSequence() ejecuta
|
|
28
|
+
* 2. Servidor incrementa counter y retorna valor
|
|
29
|
+
* 3. Usuario ve código → completa form → guarda exitosamente
|
|
30
|
+
*
|
|
31
|
+
* Flujo de Error (raro):
|
|
32
|
+
* 1. Dos usuarios abren form al mismo tiempo
|
|
33
|
+
* 2. Ambos reciben diferentes códigos (Counter atómico)
|
|
34
|
+
* 3. Usuario A guarda primero → OK
|
|
35
|
+
* 4. Usuario B intenta guardar → Error "Duplicate key" (validación única)
|
|
36
|
+
* 5. Usuario B ve botón "Regenerar código" → Click
|
|
37
|
+
* 6. regenerate() obtiene nuevo código → Usuario B guarda → OK
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* function ProductForm() {
|
|
42
|
+
* const { value, loading, error, regenerate, clearError } = useAutoGenerate({
|
|
43
|
+
* prefix: "PROD-",
|
|
44
|
+
* padding: 7
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* return (
|
|
48
|
+
* <div>
|
|
49
|
+
* <input value={value} readOnly />
|
|
50
|
+
* {loading && <CircularProgress />}
|
|
51
|
+
* {error && (
|
|
52
|
+
* <div>
|
|
53
|
+
* <span>Error: {error}</span>
|
|
54
|
+
* <button onClick={regenerate}>Regenerar código</button>
|
|
55
|
+
* </div>
|
|
56
|
+
* )}
|
|
57
|
+
* </div>
|
|
58
|
+
* );
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
declare const useAutoGenerate: (config: AutoGenerateConfig, options?: UseAutoGenerateOptions) => UseAutoGenerateReturn;
|
|
63
|
+
|
|
64
|
+
type NotificationSeverity = "error" | "info" | "success" | "warning";
|
|
65
|
+
interface Notification {
|
|
66
|
+
id: string;
|
|
67
|
+
message: string;
|
|
68
|
+
severity: NotificationSeverity;
|
|
69
|
+
autoHideDuration?: number;
|
|
70
|
+
persistent?: boolean;
|
|
71
|
+
allowHtml?: boolean;
|
|
72
|
+
}
|
|
73
|
+
interface GlobalNotificationContextValue {
|
|
74
|
+
showNotification: (message: string, severity?: NotificationSeverity, options?: {
|
|
75
|
+
autoHideDuration?: number;
|
|
76
|
+
persistent?: boolean;
|
|
77
|
+
allowHtml?: boolean;
|
|
78
|
+
}) => string;
|
|
79
|
+
hideNotification: (id: string) => void;
|
|
80
|
+
clearAllNotifications: () => void;
|
|
81
|
+
}
|
|
82
|
+
interface GlobalNotificationProviderProps {
|
|
83
|
+
children: React.ReactNode;
|
|
84
|
+
maxNotifications?: number;
|
|
85
|
+
defaultAutoHideDuration?: number;
|
|
86
|
+
position?: {
|
|
87
|
+
vertical: "top" | "bottom";
|
|
88
|
+
horizontal: "left" | "center" | "right";
|
|
89
|
+
};
|
|
90
|
+
enabled?: boolean;
|
|
91
|
+
allowHtml?: boolean;
|
|
92
|
+
}
|
|
93
|
+
declare const GlobalNotificationProvider: React.FC<GlobalNotificationProviderProps>;
|
|
94
|
+
declare const useGlobalNotification: () => GlobalNotificationContextValue;
|
|
95
|
+
|
|
96
|
+
export { type AutoGenerateConfig as A, GlobalNotificationProvider as G, type Notification as N, type UseAutoGenerateReturn as U, type GlobalNotificationProviderProps as a, type NotificationSeverity as b, useAutoGenerate as c, type UseAutoGenerateOptions as d, useGlobalNotification as u };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
interface CrudifyApiResponse<T = unknown> {
|
|
2
|
+
success: boolean;
|
|
3
|
+
data?: T;
|
|
4
|
+
errors?: string | Record<string, string[]> | string[];
|
|
5
|
+
errorCode?: string;
|
|
6
|
+
fieldsWarning?: Record<string, string[]> | null;
|
|
7
|
+
}
|
|
8
|
+
interface CrudifyTransactionResponse {
|
|
9
|
+
success: boolean;
|
|
10
|
+
data?: TransactionResponseData[];
|
|
11
|
+
errors?: string | Record<string, string[]>;
|
|
12
|
+
errorCode?: string;
|
|
13
|
+
}
|
|
14
|
+
interface TransactionResponseData {
|
|
15
|
+
response?: {
|
|
16
|
+
status: string;
|
|
17
|
+
data?: unknown;
|
|
18
|
+
message?: string;
|
|
19
|
+
};
|
|
20
|
+
errors?: string | Record<string, string[]>;
|
|
21
|
+
}
|
|
22
|
+
interface UserProfile {
|
|
23
|
+
id: string | number;
|
|
24
|
+
email: string;
|
|
25
|
+
username?: string;
|
|
26
|
+
firstName?: string;
|
|
27
|
+
lastName?: string;
|
|
28
|
+
fullName?: string;
|
|
29
|
+
avatar?: string;
|
|
30
|
+
role?: string;
|
|
31
|
+
permissions?: string[];
|
|
32
|
+
lastLogin?: string;
|
|
33
|
+
isActive?: boolean;
|
|
34
|
+
createdAt?: string;
|
|
35
|
+
updatedAt?: string;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
interface LoginResponse {
|
|
39
|
+
token: string;
|
|
40
|
+
user?: UserProfile;
|
|
41
|
+
expiresIn?: number;
|
|
42
|
+
refreshToken?: string;
|
|
43
|
+
}
|
|
44
|
+
interface LoginRequest {
|
|
45
|
+
identifier: string;
|
|
46
|
+
password: string;
|
|
47
|
+
}
|
|
48
|
+
interface ForgotPasswordRequest {
|
|
49
|
+
email: string;
|
|
50
|
+
}
|
|
51
|
+
interface ResetPasswordRequest {
|
|
52
|
+
email: string;
|
|
53
|
+
code: string;
|
|
54
|
+
newPassword: string;
|
|
55
|
+
}
|
|
56
|
+
interface ValidateCodeRequest {
|
|
57
|
+
email: string;
|
|
58
|
+
codePassword: string;
|
|
59
|
+
}
|
|
60
|
+
interface JwtPayload {
|
|
61
|
+
sub?: string;
|
|
62
|
+
email?: string;
|
|
63
|
+
username?: string;
|
|
64
|
+
iat?: number;
|
|
65
|
+
exp?: number;
|
|
66
|
+
iss?: string;
|
|
67
|
+
aud?: string;
|
|
68
|
+
[key: string]: unknown;
|
|
69
|
+
}
|
|
70
|
+
interface ApiError {
|
|
71
|
+
code: string;
|
|
72
|
+
message: string;
|
|
73
|
+
field?: string;
|
|
74
|
+
details?: Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
interface ValidationError {
|
|
77
|
+
field: string;
|
|
78
|
+
message: string;
|
|
79
|
+
code: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Options for Crudify CRUD operations
|
|
83
|
+
*/
|
|
84
|
+
interface CrudifyOperationOptions {
|
|
85
|
+
signal?: AbortSignal;
|
|
86
|
+
/** Action metadata for success notifications */
|
|
87
|
+
actionConfig?: {
|
|
88
|
+
key?: string;
|
|
89
|
+
moduleKey?: string;
|
|
90
|
+
[key: string]: unknown;
|
|
91
|
+
};
|
|
92
|
+
/** Skip showing notifications for this operation */
|
|
93
|
+
skipNotifications?: boolean;
|
|
94
|
+
/** Allow additional properties */
|
|
95
|
+
[key: string]: unknown;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Options for Crudify request operations
|
|
99
|
+
*/
|
|
100
|
+
interface CrudifyRequestOptions {
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Represents a single operation within a transaction.
|
|
105
|
+
*/
|
|
106
|
+
interface TransactionOperation {
|
|
107
|
+
operation: "create" | "update" | "delete" | string;
|
|
108
|
+
moduleKey?: string;
|
|
109
|
+
data?: Record<string, unknown>;
|
|
110
|
+
_id?: string;
|
|
111
|
+
[key: string]: unknown;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Input for transaction operations. Can be a single operation or an array of operations.
|
|
115
|
+
*/
|
|
116
|
+
type TransactionInput = TransactionOperation | TransactionOperation[] | Record<string, unknown>;
|
|
117
|
+
|
|
118
|
+
export type { ApiError as A, CrudifyApiResponse as C, ForgotPasswordRequest as F, JwtPayload as J, LoginResponse as L, ResetPasswordRequest as R, TransactionResponseData as T, UserProfile as U, ValidateCodeRequest as V, CrudifyTransactionResponse as a, LoginRequest as b, ValidationError as c, CrudifyOperationOptions as d, CrudifyRequestOptions as e, TransactionOperation as f, TransactionInput as g };
|