import type { ReactNode } from 'react'; import type { Fetcher } from '../LookupField'; import type { MatchResult } from '../MatchModal'; import type { AuditHistoryProps } from '../../audit/types/audit.types'; import type { GalleryResource } from '../FileUploaderGallery/FileUploaderGallery.types'; export type { MatchResult }; /** * Configuración del componente de Gestión de Archivos (FileUploaderGallery) * integrado en MasterCrud. Se usa cuando featCodeFileManager apunta a un * Feature cuyo hasFileManagement es true. */ export interface FileManagerConfig { /** Recursos (archivos/imágenes) actuales del registro */ resources?: GalleryResource[]; /** Nombre de la entidad para el gallery (si no se provee, se usa entityName de MasterCrud) */ entity?: string; /** Habilita la opción de eliminar recursos */ allowDeletion?: boolean; /** Habilita los checkboxes de selección */ allowSelection?: boolean; /** Muestra el componente de subida de archivos */ showFileUploader?: boolean; /** Estado de carga del gallery */ isLoading?: boolean; /** Mensaje de error a mostrar en el gallery */ error?: { title: string; message: string; } | null; /** * Callback para subir un archivo. * @param file Archivo a subir * @param recordId ID del registro asociado */ onUpload?: (file: File, recordId: string | number) => Promise; /** * Callback para eliminar un recurso. * @param uuid UUID del recurso a eliminar * @param recordId ID del registro asociado */ onDelete?: (uuid: string, recordId: string | number) => Promise; /** * Callback ejecutado cuando cambia la selección de recursos. */ onResourceCheck?: (uuid: string, checked: boolean, selectedResources: GalleryResource[]) => void; /** * Callback ejecutado al guardar el registro en MasterCrud. * Recibe los recursos seleccionados y el ID del registro ya persistido. * @param selected Recursos seleccionados en la galería * @param recordId ID del registro recién creado/actualizado */ onAccept?: (selected: GalleryResource[], recordId: string | number) => Promise; /** * Extensiones permitidas separadas por coma. Ej: '.pdf,.docx,.jpg' * @default extensiones corporativas estándar */ acceptedExtensions?: string; /** * Tamaño máximo por archivo individual, en MB. * @default 10 */ maxFileSizeMB?: number; /** * Tamaño máximo acumulado de todos los archivos del registro, en MB. * @default 50 */ maxTotalSizeMB?: number; /** * Permite subir varios archivos a la vez. En false, solo uno por operación. * @default true */ multipleFiles?: boolean; /** * Si se muestra el nombre del archivo debajo de la miniatura. * Por defecto: false para imágenes, true para el resto. */ showFileName?: boolean; /** * Texto del botón de aceptar cuando el gallery no está en modo embebido. * @default 'Aceptar' */ acceptButtonText?: string; /** * Clases CSS adicionales para el contenedor del gallery. */ className?: string; } /** * Tipos de campo soportados por MasterCrud * Define cómo se renderiza cada campo en el formulario: * - 'text': Input de texto estándar * - 'number': Input numérico con controles de incremento * - 'email': Input con validación de formato de correo * - 'date': Selector de fecha (Calendario) * - 'boolean': Switch o Checkbox de activación * - 'select': Dropdown con opciones estáticas (requiere 'options') * - 'lookup': LookupField para búsquedas asíncronas (requiere 'lookupConfig') */ export type FieldType = 'text' | 'number' | 'email' | 'date' | 'boolean' | 'select' | 'lookup'; /** * Tipos de navegación para formularios de creación/edición */ export type MasterCrudNavigationType = 'modal' | 'sidebar' | 'page'; /** * Estado de la vista actual del MasterCrud */ export type ViewState = 'LIST' | 'CREATE' | 'EDIT'; /** * Tipo para propiedades que pueden ser estáticas (boolean) o dinámicas (función) */ export type DynamicProp = boolean | ((formData: T) => boolean); /** * Opción para campos de tipo 'select' */ export interface SelectFieldOption { /** * Valor real del campo * @example 'ACT', 1, 'sur' */ value: string | number; /** Etiqueta visible para el usuario */ label: string; /** Icono opcional al lado de la etiqueta */ icon?: ReactNode; /** Si la opción está deshabilitada */ disabled?: boolean; } /** * Configuración extendida para cada campo */ export interface FieldConfig { /** * Si el campo es obligatorio. * Puede ser estático (true/false) o dinámico basado en el estado del formulario. */ required?: DynamicProp; /** * Si el campo está deshabilitado (solo lectura). * Puede ser estático (true/false) o dinámico basado en el estado del formulario. */ disabled?: DynamicProp; /** * Si el campo debe deshabilitarse en modo edición. * @default false */ disabledOnEdit?: boolean; /** * Longitud máxima de caracteres permitida */ maxLength?: number; /** * Longitud mínima de caracteres requerida */ minLength?: number; /** * Clave de la pestaña (formTab) a la que pertenece el campo cuando se usa el * renderizado en pestañas (prop `formTabs` de MasterCrud). Alternativa a listar * el campo en `formTabs[].fields`. Si no se asigna, el campo va a la primera pestaña. */ tab?: string; /** * @deprecated Use `showInList` en su lugar * Si se muestra en la tabla/grilla */ visibleInTable?: boolean; /** * @deprecated Use `showInCreate` y `showInEdit` en su lugar * Si se muestra en el formulario */ visibleInForm?: boolean; /** * Visible en la vista de tabla (List View) * @default true */ showInList?: boolean; /** * Visible en la vista de tarjetas (Grid View) * @default true */ showInGrid?: boolean; /** * Visible en el formulario de creación * @default true */ showInCreate?: boolean; /** * Visible en el formulario de edición * @default true */ showInEdit?: boolean; /** Si se puede buscar por este campo */ searchable?: boolean; /** Si se puede ordenar por este campo */ sortable?: boolean; /** Ancho de la columna en la tabla (CSS width) */ columnWidth?: string; /** Alineación del contenido en la tabla */ align?: 'left' | 'center' | 'right'; /** Placeholder para el input */ placeholder?: string; /** Texto de ayuda debajo del input */ helperText?: string; /** Mensaje de error de validación */ errorMessage?: string; /** Función de validación personalizada */ validate?: (value: any, formData: Record) => string | undefined; /** Valor mínimo (para number) */ min?: number; /** Valor máximo (para number) */ max?: number; /** Paso (para number) */ step?: number; /** Formato de fecha (para date) */ dateFormat?: string; /** Posición en la tarjeta (Grid View) */ cardPosition?: 'title' | 'subtitle' | 'body' | 'footer' | 'status'; /** * Texto a mostrar cuando el valor es true (para campos boolean) * @default 'Activo' */ trueLabel?: string; /** * Texto a mostrar cuando el valor es false (para campos boolean) * @default 'Inactivo' */ falseLabel?: string; /** * Cuántas columnas del grid debe ocupar este campo en el formulario. * Útil para diseños complejos (ej: ocupar todo el ancho en un grid de 2 columnas). * @example 2 */ colSpan?: number; /** * Permite la sobreescritura multicompañía para este campo. * Solo funciona si activeByCompany es true. */ allowMultiCompanyOverride?: boolean; /** * Función para obtener el valor global de este campo. * Se usa para mostrar el texto "Global: [valor]" debajo del campo. */ getGlobalValue?: (data: any) => React.ReactNode; /** * Si el valor de este campo debe aparecer en el título del modal de vinculación. */ showInLinkModalTitle?: boolean; /** * Si el campo debe buscar coincidencias al disparar el evento OnBlur. * Solo aplica en modo creación y si activeByCompany es true bajo contexto Global. */ checkMatchesOnBlur?: boolean; /** * Callback ejecutado cuando el campo pierde el foco (blur). * Útil para validaciones, cálculos o efectos secundarios. */ onBlur?: (value: any, name: string, formData: Record) => void; /** * Callback ejecutado cuando el campo se monta en el formulario. * Útil para inicialización dinámica o carga de datos. * @param setValue Función para actualizar el valor de este campo * @param formData Datos actuales del formulario */ onMount?: (setValue: (value: any) => void, formData: Record) => void; } /** * Configuración específica para campos de tipo 'lookup' */ export interface LookupConfig { /** Nombre del recurso o entidad a buscar en el servidor */ entity: string; /** Campos que se mostrarán en la lista de resultados del dropdown de búsqueda */ displayFields: string[]; /** Función asíncrona que realiza la petición HTTP */ fetcher: Fetcher; /** Campo que actúa como valor único e identificador (default: 'id') */ valueField?: string; } /** * Definición de un campo del MasterCrud * Controla tanto la tabla como el formulario */ /** * Definición de una pestaña del formulario (renderizado automático en tabs). * Uso opcional en MasterCrud vía la prop `formTabs`. */ export interface MasterCrudFormTab { /** Identificador único de la pestaña. */ key: string; /** Etiqueta visible de la pestaña. */ label: string; /** * accessorKeys de los campos que se renderizan en esta pestaña. Alternativa a * marcar cada campo con `config.tab`. Los campos no asignados a ninguna pestaña * caen en la primera. */ fields?: string[]; /** Icono opcional a la izquierda de la etiqueta. */ icon?: ReactNode; } export interface MasterCrudField { /** * Key del objeto JSON que contiene el valor * @example 'nombre', 'cliente.direccion' */ accessorKey: keyof T | string; /** * Etiqueta visible en español * Se usa tanto en el header de la tabla como en el label del formulario */ header: string; /** * Tipo de componente a renderizar */ type: FieldType; /** * Configuración adicional del campo */ config?: FieldConfig; /** * Opciones para campos de tipo 'select' */ options?: SelectFieldOption[]; /** * Configuración para campos de tipo 'lookup' */ lookupConfig?: LookupConfig; /** * Función de render personalizada para la tabla * Si se provee, sobrescribe el render automático */ renderCell?: (value: any, row: T, index: number) => ReactNode; /** * Función de render personalizada para el formulario * Si se provee, sobrescribe el render automático */ renderInput?: (value: any, onChange: (value: any) => void, field: MasterCrudField, formData: Record) => ReactNode; } /** * Definición de una compañía para MasterCrud */ export interface MasterCrudCompany { /** Identificador único interno de la compañía (UUID o ID de base de datos) */ uuid: string | number; /** Código visible de la compañía para visualización */ code: string | number; /** Nombre de la compañía */ name: string; /** Si está seleccionada por defecto */ isSelectedByDefault?: boolean; /** Datos adicionales */ [key: string]: any; } /** * Parámetros para la consulta de listado */ export interface GetAllParams { /** Número de página (1-indexed) */ page: number; /** Cantidad de registros por página */ limit: number; /** Término de búsqueda global */ search?: string; /** Campo específico donde buscar (si no se define, es global) */ searchField?: string; /** Campo por el cual ordenar */ sortBy?: string; /** Dirección del ordenamiento */ sortDirection?: 'asc' | 'desc'; /** Filtros adicionales */ filters?: Record; } /** * Respuesta del servicio de listado */ export interface GetAllResponse { /** Array de datos */ data: T[]; /** Total de registros (para paginación) */ total: number; } /** * Contrato que debe implementar el servicio de datos * Permite al MasterCrud interactuar con cualquier backend */ export interface CrudService { /** * Obtener listado paginado de registros */ getAll: (params: GetAllParams, companyData?: any) => Promise>; /** * Obtener un registro por su ID */ getById?: (id: string | number, companyData?: any) => Promise; /** * Crear un nuevo registro */ create: (data: Partial, companyData?: any) => Promise; /** * Actualizar un registro existente */ update: (id: string | number, data: Partial, companyData?: any) => Promise; /** * Eliminar un registro o cambiar su estado (Soft Delete) */ delete: (id: string | number, companyData?: any, operation?: MasterCrudOperationType) => Promise; /** * Obtener los archivos asociados a un registro. * Se llama automáticamente al abrir un registro en edición. */ getFiles?: (id: string | number, companyData?: any) => Promise; /** * Subir un nuevo archivo y asociarlo al registro. * Se llama cuando el usuario arrastra/selecciona un archivo en la galería. * Después de completarse, MasterCrud vuelve a llamar getFiles para refrescar. */ uploadFile?: (id: string | number, file: File, companyData?: any) => Promise; /** * Eliminar un archivo de un registro. * Después de completarse, MasterCrud elimina el recurso del estado local. */ deleteFile?: (id: string | number, uuid: string, companyData?: any) => Promise; /** * Persistir la asociación final de archivos seleccionados con el registro. * Se llama automáticamente al guardar el registro (después del create/update). * Si no se implementa, onAccept no hace nada en modo automático. */ saveFiles?: (id: string | number, selected: GalleryResource[], companyData?: any) => Promise; } /** * Definición de permisos para operaciones CRUD */ export interface CrudPermissions { /** Permite leer el listado y el detalle de registros */ canRead?: boolean; /** Permite crear nuevos registros */ canCreate?: boolean; /** Permite editar registros existentes */ canUpdate?: boolean; /** Permite eliminar registros físicamente */ canDelete?: boolean; /** Permite activar/desactivar registros (Soft Delete) */ canChangeStatus?: boolean; /** Permite realizar sobreescrituras en modo multicompañía */ canOverrideMultiCompany?: boolean; /** Permite editar campos dinámicos (EAV) en bloque sobre múltiples registros */ canBulkUpdate?: boolean; } /** * Tipos de operación para el método delete del servicio */ export type MasterCrudOperationType = 'delete' | 'activate' | 'deactivate'; /** * Definición de una acción personalizada para un registro */ export interface MasterCrudAction { /** * Identificador único de la acción (opcional, usado para identificar acciones por defecto como 'edit' o 'delete') */ id?: string; /** * Nombre de la acción (se muestra en el menú). * Puede ser un string estático o una función que dependa del registro. */ name: string | ((record: T) => string); /** * Función callback que se ejecuta al seleccionar la acción. * Para acciones grupales, recibe el array de registros seleccionados. */ action?: (record: T | T[], companyData?: any) => void; /** * Contenido a renderizar en un modal al seleccionar la acción. * Si se define, `action` es opcional (o se ejecuta antes de abrir el modal). * El componente recibirá el registro completo como prop `data`. */ modalContent?: ReactNode | ((record: T) => ReactNode); /** * Icono opcional para el menú. * Puede ser un ReactNode estático o una función que dependa del registro. */ icon?: ReactNode | ((record: T) => ReactNode); /** * Orden de aparición en el menú (menor número = más arriba) * @default 0 */ order?: number; /** * Si la acción está deshabilitada */ disabled?: boolean | ((record: T) => boolean); /** * Si la acción es visible. * Si es false, el botón o item de menú no se renderizará. */ visible?: boolean | ((record: T) => boolean); /** * Define dónde se muestra la acción en el listado/toolbar * - 'direct': Botón visible directamente * - 'menu': Dentro del menú desplegable de acciones * @default 'menu' */ displayType?: 'direct' | 'menu'; /** * Indica si la acción es aplicable a una selección múltiple de registros. * Si es true, aparecerá en el menú de acciones grupales del toolbar. */ isGroupAction?: boolean; } /** * Configuración del estado vacío (cuando no hay registros) */ export interface MasterCrudEmptyStateConfig { /** Imagen alusiva (URL o componente) */ image?: ReactNode | string; /** Título del mensaje */ title?: string; /** Descripción o instrucciones */ description?: string; } /** * Estructura de un filtro de búsqueda avanzada. * Soporta anidación mediante grupos para composiciones lógicas complejas. */ export type AdvancedFilterConnector = 'AND' | 'OR'; export interface AdvancedFilter { id: string; /** Campo sobre el cual aplicar el filtro */ field?: string; /** Conector lógico para unir este filtro con el anterior o para sus hijos */ connector: AdvancedFilterConnector; /** Valor a buscar */ value?: any; /** Operador de comparación (ej: equals, contains, gt, lt) */ operator?: string; /** Sub-filtros para formar un grupo lógico */ children?: AdvancedFilter[]; /** Indica si es un grupo de filtros */ isGroup?: boolean; } /** * Configuración para conectar MasterCrud con un microservicio que usa * la librería Siesa.MasterPattern (.NET). * * Cuando se define este objeto, MasterCrud construye internamente un * CrudService apuntando a los endpoints estándar del backend. * No es necesario implementar un servicio manual. * * @example * ```tsx * ({ 'X-User-ID': userId }), * }} * ... * /> * ``` */ export interface MasterPatternConfig { /** URL base del microservicio. Ej: 'https://api.siesa.com' */ baseUrl: string; /** Path del recurso en la API (plural). Ej: 'currencies', 'bank-accounts' */ resourcePath: string; /** Tipo de maestro en Siesa.MasterPattern */ masterType?: 'GLOBAL' | 'UNIVERSAL' | 'COMPANY_SPECIFIC'; /** * Función para proveer headers de autenticación (X-User-ID, X-Username, etc.). * X-Company-ID se agrega automáticamente desde el selector de compañía. */ getHeaders?: () => Record | Promise>; /** * Campos a solicitar en SearchRequest.fields (PascalCase). * Si se omite, se auto-deriva de los accessorKey de MasterCrud.fields. */ requestFields?: string[]; /** * Mapper personalizado GetAllParams → body del POST /search. * Úsalo para personalizar por completo el cuerpo de la búsqueda. */ mapSearchRequest?: (params: GetAllParams, companyData?: any) => Record; } /** * Configuración del modo Maestro-Detalle integrado en MasterCrud. * * Cuando se define, el formulario del maestro permanece abierto tras guardar * y muestra una sección de detalle embebida debajo del formulario, eliminando * la necesidad de navegar de vuelta al listado para gestionar los registros * hijos. * * @example * ```tsx * * ``` */ export interface MasterCrudDetailConfig { /** * Título de la sección de detalle. * @default 'Detalles' */ title?: string; /** * Nombre de la entidad de detalle en singular (para mensajes y botones). * @example 'Línea', 'Ítem', 'Producto' */ entityName?: string; /** * Definición de los campos del formulario y tabla de detalle. */ fields: MasterCrudField[]; /** * Servicio CRUD para los registros de detalle. * Debe implementar getAll, create, update y delete. */ service: CrudService; /** * Nombre del campo en el registro de detalle que referencia al maestro. * Se inyecta automáticamente al crear un nuevo detalle. * @example 'orderId', 'headerId', 'invoiceCode' */ foreignKey: string; /** * Campo identificador único del registro de detalle. * @default 'id' */ idField?: string; /** * Registros por página en la tabla de detalle. * @default 5 */ pageSize?: number; /** * Si se muestra el botón de agregar detalle. * @default true */ showCreateButton?: boolean; /** * Mensaje cuando no hay registros de detalle. */ emptyMessage?: string; /** * Si se permite editar registros de detalle. * @default true */ allowEdit?: boolean; /** * Si se permite eliminar registros de detalle. * @default true */ allowDelete?: boolean; /** * Permisos granulares para las operaciones del detalle. */ permissions?: CrudPermissions; /** * Acciones personalizadas por registro de detalle. */ actions?: MasterCrudAction[]; /** * Ancho del modal de creación/edición del detalle. * @default '600px' */ formWidth?: string; /** * Número de columnas del formulario de detalle. * @default 2 */ formColumns?: number; /** * Texto del botón de agregar detalle. * Si no se define, se usa "Agregar {entityName}". */ createButtonText?: string; /** * Texto del botón guardar en el formulario de detalle. * @default 'Guardar' */ saveButtonText?: string; /** * Texto del botón cancelar en el formulario de detalle. * @default 'Cancelar' */ cancelButtonText?: string; /** * Callback ejecutado cuando se crea un registro de detalle. */ onDetailCreateSuccess?: (record: D) => void; /** * Callback ejecutado cuando se actualiza un registro de detalle. */ onDetailUpdateSuccess?: (record: D) => void; /** * Callback ejecutado cuando se elimina un registro de detalle. */ onDetailDeleteSuccess?: (id: string | number) => void; } /** * Props del componente MasterCrud */ export interface MasterCrudProps { /** * Título del módulo (se muestra en el header) * @example "Gestión de Clientes" */ title: string; /** * Nombre de la entidad en singular (para mensajes) * @example "Cliente" */ entityName: string; /** * Código del Feature para cargar entidades dinámicas (EAV) */ dynamicFeatureCode?: string; /** * Tipo de navegación para las acciones de creación/edición. * Si activeByCompany es true, el valor por defecto es 'page'. * Si activeByCompany es false, el valor por defecto es 'modal'. */ navigationType?: MasterCrudNavigationType; /** * Habilitar selección múltiple de registros * @default false */ enableMultiSelect?: boolean; /** * Configuración personalizada para cuando no hay registros */ emptyStateConfig?: MasterCrudEmptyStateConfig; /** * Acciones globales que aparecen en el Toolbar (antes del botón Nuevo) */ globalActions?: MasterCrudAction[]; /** * Acciones grupales que aparecen cuando hay registros seleccionados */ batchActions?: MasterCrudAction[]; /** * Idioma para la localización de textos y formatos. * Sigue el estándar ISO 639-1. * @default 'es' * @example 'en', 'fr' */ locale?: string; /** * Prefijo para buscar llaves de traducción de la interfaz (botones, buscador). * @default 'mastercrud' */ uiPrefix?: string; /** * Prefijo para buscar llaves de traducción de los campos (entidad). * Útil cuando el JSON de traducciones es agnóstico al componente. * @example 'colaboradores' -> buscará 'colaboradores.nombre' */ fieldsPrefix?: string; /** * Diccionario de traducciones externas para sobreescribir las internas. */ externalTranslations?: Record; /** * Función de traducción personalizada. * Permite que el componente sea agnóstico a librerías externas. */ t?: (key: string, defaultValue?: string) => string; /** * Definición de los campos de la entidad */ fields: MasterCrudField[]; /** * Lista de accessorKeys de los campos que se pueden usar para filtrar/buscar. * Si no se provee, se usan todos los campos visibles. */ filterableFields?: string[]; /** * Lista de accessorKeys de las columnas que muestran el embudo de filtro por columna * en la cabecera de la tabla. Si no se provee, cae en `filterableFields`. * Los filtros por columna viajan server-side reutilizando el canal de búsqueda avanzada * (retrocompatible con endpoints existentes). */ columnFilterableFields?: string[]; /** * Si se muestra la opción "Exportar" en el menú de más opciones del listado. * @default false */ showExport?: boolean; /** Callback al pulsar "Exportar". */ onExport?: () => void; /** * Alcance de filas por defecto en modo sobrescritura multicompañía (compañía ≠ Global): * 'all' = todos los registros (habilitados y no habilitados); 'company' = solo los habilitados. * @default 'all' */ defaultViewScope?: 'all' | 'company'; /** * Callback del enlace "Volver" de la barra de navegación del listado. * Si no se provee, el enlace usa `window.history.back()`. */ onBack?: () => void; /** * Organización opcional del formulario en pestañas. Cuando se provee (con más de una * pestaña), el detalle renderiza los campos agrupados en pestañas con el diseño del * prototipo. Los campos se asignan por `formTabs[].fields` o por `field.config.tab`. * Si no se provee, el formulario se renderiza como un único bloque continuo (comportamiento previo). */ formTabs?: MasterCrudFormTab[]; /** * Habilitar scroll infinito en la vista de tarjetas (Grid) * Si es true, la vista de grid usará scroll infinito en lugar de paginación * @default true */ enableGridInfiniteScroll?: boolean; /** * Servicio CRUD manual. Requerido cuando NO se define masterPattern. * Si ambos se definen, service tiene precedencia total. */ service?: CrudService; /** * Configuración para integración automática con Siesa.MasterPattern. * Cuando se define, MasterCrud construye internamente el CrudService * apuntando a los endpoints estándar del backend. * Requiere que service NO esté definido (o que service sea undefined). */ masterPattern?: MasterPatternConfig; /** * Campo que contiene el ID único de cada registro * @default 'id' */ idField?: keyof T | string; /** * Registros por página * @default 10 */ pageSize?: number; /** * Si se muestra el botón de crear * @default true */ showCreateButton?: boolean; /** * Si se muestra el campo de búsqueda * @default true */ showSearch?: boolean; /** * Indica si se muestran los acciones de edición/eliminar por fila * @default true */ showRowActions?: boolean; /** * Nombre del campo en el registro que representa el estado activo/inactivo. * Úsalo cuando tu API devuelve el campo con un nombre diferente al default (p. ej. 'activo', 'active', 'enabled'). * @default 'isActive' */ statusField?: string; /** * Indica si se permite revertir el estado de un registro (activar después de desactivar). * Si es false, el botón de acción desaparecerá cuando el registro esté en estado inactivo/desvinculado. * @default true */ allowStatusReversion?: boolean; /** * Texto personalizado para el tooltip de desactivación o desvinculación. * En modo multicompañía (sucursal), si no se provee, se usará 'Desvincular'. */ deactivateTooltip?: string; /** * Texto personalizado para el tooltip de activación o vinculación. */ activateTooltip?: string; /** * Si se permite eliminar registros * @default true */ allowDelete?: boolean; /** * Indica si se maneja Soft Delete (activar/desactivar) en lugar de eliminación física. * Si es true, se requiere el permiso canChangeStatus para realizar estas acciones. * @default true */ softDelete?: boolean; /** * Configuración de permisos granulada. * Si se define, tiene precedencia sobre `showCreateButton`, `showRowActions` y `allowDelete`. */ permissions?: CrudPermissions; /** * Callback ejecutado antes de crear un registro. * Si retorna false (o promesa false), cancela la operación. */ onBeforeCreate?: (data: Partial) => Promise | boolean; /** * Callback ejecutado antes de actualizar un registro. * Si retorna false (o promesa false), cancela la operación. */ onBeforeUpdate?: (id: string | number, data: Partial) => Promise | boolean; /** * Callback ejecutado antes de iniciar el proceso de eliminación. * Si retorna false (o promesa false), cancela la operación (no muestra confirmación). */ onBeforeDelete?: (record: T) => Promise | boolean; /** * Callback cuando se crea un registro exitosamente */ onCreateSuccess?: (record: T) => void; /** * Callback cuando se actualiza un registro exitosamente */ onUpdateSuccess?: (record: T) => void; /** * Callback cuando se elimina un registro exitosamente */ onDeleteSuccess?: (id: string | number) => void; /** * Callback cuando ocurre un error */ onError?: (error: Error, action: 'create' | 'update' | 'delete' | 'fetch') => void; /** * Texto del botón crear * @default 'Crear {entityName}' */ createButtonText?: string; /** * Texto del botón guardar en el formulario * @default 'Guardar' */ saveButtonText?: string; /** * Texto del botón cancelar en el formulario * @default 'Cancelar' */ cancelButtonText?: string; /** * Mensaje cuando no hay datos * @default 'No hay registros disponibles' */ emptyMessage?: string; /** * Clases CSS adicionales para el contenedor */ className?: string; /** * ID único para accesibilidad */ id?: string; /** * Renderizado personalizado para la toolbar */ renderToolbar?: (props: { onSearch: (term: string) => void; onCreate: () => void; searchTerm: string; }) => ReactNode; /** * Acciones adicionales por fila */ rowActions?: (row: T, index: number) => ReactNode; /** * Ancho del contenedor del formulario * @default '600px' */ formWidth?: string; /** * Número de columnas del grid del formulario * @default 2 */ formColumns?: number; /** * Si se deben ocultar las pestañas automáticas de entidades dinámicas. * Útil cuando se usa renderForm para implementar un tabulado propio. * @default false */ hideDynamicTabs?: boolean; /** * Si se muestra el toggle de vista (grid/list) en la toolbar * @default false */ showViewToggle?: boolean; /** * Si se muestra el botón de búsqueda avanzada * @default false */ showAdvancedSearch?: boolean; /** * Acciones personalizadas por registro */ actions?: MasterCrudAction[]; /** * Configuración del historial de auditoría. * Si `enabled` es true, muestra el ícono de reloj en la columna de acciones. * El consumidor es responsable de evaluar el permiso antes de pasar `enabled: true`. */ auditHistory?: AuditHistoryProps; /** * Renderizado personalizado del contenido de la tarjeta en la vista de grid. * Permite personalizar cómo se muestra la información dentro de la tarjeta, * mientras MasterCRUDCard mantiene el contenedor, estilos y dropdown de acciones. * * @param data - El registro de datos a renderizar * @param index - El índice del registro en la lista * @returns ReactNode con el contenido visual personalizado * * @example * ```tsx * renderCardContent={(data, index) => ( *
*

{data.nombre}

*

{data.descripcion}

* *
* )} * ``` */ renderCardContent?: (data: T, index: number) => ReactNode; /** * Renderizado personalizado del formulario de creación/edición. * Si se define, sustituye completamente al MasterCrudForm por defecto. * Recibe todas las props necesarias para manejar el estado y envío. */ renderForm?: (props: MasterCrudFormProps) => ReactNode; /** * Si se muestra la acción de editar por defecto en el menú * @default true */ showDefaultEditAction?: boolean; /** * Si el modal de edición debe mostrarse maximizado (pantalla completa) * @default false */ maximizeEditModal?: boolean; /** * Si se muestra el modal de acciones personalizadas debe mostrarse maximizado por defecto * @default true */ maximizeCustomModal?: boolean; /** * Indica si se debe realizar una llamada al método getById del servicio al entrar en modo edición. * Si es true, se cargará el registro completo desde el servidor. * Si es false, se utilizarán los datos presentes en el listado de la tabla. * @default true */ fetchDetailOnEdit?: boolean; /** * Habilita el manejo automático de errores mediante Toasts internos. * Si es true, el componente mostrará notificaciones visuales (Toasts) cuando ocurran errores. * Si es false, solo se ejecutará el callback `onError`. * @default true */ internalErrorHandling?: boolean; /** * Opciones de ordenamiento para el dropdown */ sortOptions?: SortOption[]; /** * Opciones de filtro para el dropdown */ filterOptions?: FilterOption[]; /** * Si se usa el nuevo MasterCrudToolbar * @default true */ useToolbar?: boolean; /** * Callback cuando cambia la vista */ onViewChange?: (view: ToolbarViewType) => void; /** * Si se muestran los conectores (Y/O) entre campos en la búsqueda avanzada. * Por defecto es false. * @default false */ showAdvancedSearchConnectors?: boolean; /** * Cantidad máxima de campos que pueden participar en la búsqueda avanzada. * @default 4 */ maxAdvancedFilters?: number; /** * Si el contenedor de búsqueda y filtros es visible inicialmente * @default false */ initialFiltersVisible?: boolean; /** * Badges informativos que aparecen debajo del título en modo página. * Útil para mostrar estados, prioridades, etc. */ headerBadges?: ((record: T | null) => { label: string; color?: any; }[]) | { label: string; color?: any; }[]; /** * Renderizado personalizado en la sección central del toolbar en modo página. * Útil para agregar selectores de compañía, filtros rápidos, etc. */ renderHeaderExtra?: (record: T | null) => ReactNode; /** * Habilita la selección de compañía en el toolbar * @default false */ activeByCompany?: boolean; /** * Indica si la selección de compañía es obligatoria. * Si es true, la opción "Global" se oculta del selector. * @default false */ companyRequired?: boolean; /** * Lista de compañías disponibles */ companies?: MasterCrudCompany[]; /** * Callback cuando cambia la compañía seleccionada. * En modo edición/página, también recibe el ID del registro actual. */ onCompanyChange?: (company: MasterCrudCompany, recordId?: string | number) => void; /** * Si es true, muestra un diálogo de alerta central cuando hay errores de validación. * @default false */ showValidationErrorDialog?: boolean; /** * Callback ejecutado cuando se confirma la vinculación en el modal. * Recibe el ID del registro y el array de UUIDs de compañías seleccionadas. */ onConfirmLinking?: (recordId: string | number, companyUuids: (string | number)[]) => void; /** * Función para verificar si un registro ya está vinculado a una compañía (estado inicial). */ isCompanyLinked?: (recordId: string | number, companyUuid: string | number) => boolean; /** * Determina si se deben mostrar los valores globales debajo de los campos por defecto. * @default false */ showGlobalValues?: boolean; /** * Callback ejecutado cuando un campo con 'checkMatchesOnBlur' pierde el foco. */ onSearchMatches?: (field: MasterCrudField, value: string) => Promise | MatchResult[]; /** * Callback ejecutado cuando se completa una operación de edición masiva de campos dinámicos. */ onBulkUpdateComplete?: (result: BulkUpdateResult) => void; /** * Código (path) del Feature que habilita la Gestión de Archivos para este MasterCrud. */ featCodeFileManager?: string; /** * Configuración manual del FileUploaderGallery (modo escape hatch). * Si se omite y el servicio implementa getFiles/uploadFile/deleteFile, * MasterCrud construye la configuración automáticamente. */ fileManagerConfig?: FileManagerConfig; /** * Extensiones permitidas separadas por coma. Ej: '.pdf,.docx,.jpg' * @default extensiones corporativas estándar (pdf, office, imágenes comunes) */ fileAcceptedExtensions?: string; /** * Tamaño máximo por archivo individual, en MB. * @default 10 */ fileMaxSizeMB?: number; /** * Tamaño máximo acumulado de todos los archivos del registro, en MB. * @default 50 */ fileTotalMaxSizeMB?: number; /** * Permite subir varios archivos a la vez. * En false, solo se acepta un archivo por operación de subida. * @default true */ fileMultiple?: boolean; /** * Configuración del modo Maestro-Detalle. * * Cuando se define: * - Tras crear el maestro, el formulario permanece en modo EDIT (no regresa al listado). * - Se muestra una sección de detalle embebida debajo del formulario del maestro. * - El usuario gestiona los registros de detalle sin salir de la pantalla actual. * * El navigationType por defecto cambia a 'page' cuando detailConfig está presente, * para ofrecer el espacio necesario. Puede sobreescribirse con navigationType explícito. */ detailConfig?: MasterCrudDetailConfig; } /** * Payload enviado al endpoint de actualización masiva */ export interface BulkUpdatePayload { recordIds: string[]; entityPatches: Array<{ entityCode: string; /** key = field CODE (string), nunca Guid */ fieldValues: Record; }>; } /** * Resultado final de una operación de actualización masiva */ export interface BulkUpdateResult { processed: number; total: number; succeeded: number; failed: number; errors: Array<{ recordId: string; reason: string; }>; } /** * Estado de progreso en tiempo real durante la operación masiva */ export interface BulkProgressState { processed: number; total: number; succeeded: number; failed: number; isComplete: boolean; } /** * Fases del editor de campos masivo */ export type EditorPhase = 'loading' | 'editing' | 'confirming' | 'processing' | 'completed'; /** * Props del componente MasterCrudTable */ export interface MasterCrudTableProps { /** Definición de campos */ fields: MasterCrudField[]; /** Datos a mostrar */ data: T[]; /** Si está cargando */ loading?: boolean; /** Mensaje cuando no hay datos */ emptyMessage?: string; /** Callback para ordenar */ onSort?: (column: string, direction: 'asc' | 'desc' | null) => void; /** Columna actual de ordenamiento */ sortColumn?: string; /** Dirección actual de ordenamiento */ sortDirection?: 'asc' | 'desc' | null; /** Configuración de paginación */ pagination?: { currentPage: number; totalPages: number; onPageChange: (page: number) => void; pageSize?: number; pageSizeOptions?: number[]; onPageSizeChange?: (size: number) => void; totalRecords?: number; previousLabel?: string; nextLabel?: string; perPageLabel?: string; recordsLabel?: string; goToPageLabel?: string; }; /** Si se muestran acciones por fila */ showRowActions?: boolean; /** Callback para editar */ onEdit?: (row: T, index: number) => void; /** Callback para eliminar */ onDelete?: (row: T, index: number) => void; /** Acciones personalizadas por fila */ rowActions?: (row: T, index: number) => ReactNode; /** Clases CSS adicionales */ className?: string; /** Idioma para formatos (ISO 639-1) */ locale?: string; /** Acciones combinadas (default + custom) para cada fila */ actions?: MasterCrudAction[]; /** Habilitar selección múltiple */ enableMultiSelect?: boolean; /** Registros seleccionados actualmente */ selectedIds?: (string | number)[]; /** Callback cuando cambia la selección */ onSelectionChange?: (ids: (string | number)[]) => void; /** Si el header de columnas queda fijo (sticky) al hacer scroll vertical. @default false */ stickyHeader?: boolean; /** accessorKeys de columnas que muestran el embudo de filtro por columna. */ columnFilterableFields?: string[]; /** Valores actuales de los filtros por columna (field → valor). */ columnFilters?: Record; /** Callback cuando cambia un filtro por columna (server-side, debounced en texto). */ onColumnFilterChange?: (field: string, value: string, operator: 'contains' | 'equals') => void; /** Muestra la primera columna de estado por compañía (multicompañía, compañía ≠ Global). */ showCompanyStatusColumn?: boolean; /** Predicado: ¿el registro está habilitado en la compañía seleccionada? */ isRecordCompanyEnabled?: (row: any) => boolean; /** Callback al pulsar el badge de estado por compañía de una fila (abre modal habilitar). */ onCompanyStatusClick?: (row: any) => void; /** Función de traducción inyectada por el padre */ t: (key: string, defaultValue?: string) => string; } /** * Props del componente MasterCrudForm */ export interface MasterCrudFormProps { /** Función de traducción inyectada por el padre */ t: (key: string, defaultValue?: string) => string; /** ID único para el formulario */ id?: string; /** Definición de campos */ fields: MasterCrudField[]; /** Nombre de la entidad (ej: "Item") */ entityName?: string; /** Código del Feature para cargar entidades dinámicas (EAV) */ dynamicFeatureCode?: string; /** Datos iniciales del formulario (para edición) */ initialData?: Partial; /** Si está en modo edición */ isEditing?: boolean; /** Si está cargando/guardando */ loading?: boolean; /** Callback al enviar el formulario */ onSubmit: (data: Partial) => void | Promise; /** Callback al cancelar */ onCancel: () => void; /** Texto del botón guardar */ saveButtonText?: string; /** Texto del botón cancelar */ cancelButtonText?: string; /** Número de columnas del grid */ columns?: number; /** Ancho del formulario */ width?: string; /** Clases CSS adicionales */ className?: string; /** Acciones disponibles en el formulario (solo modo edición) */ actions?: MasterCrudAction[]; /** * Si se deben ocultar el header y el footer internos del formulario. * Útil cuando el contenedor padre ya provee estas acciones (ej: modo Página). * @default false */ hideHeaderFooter?: boolean; /** * Si el botón de guardado debe ocupar todo el ancho del contenedor. * Se usa típicamente en modo Sidebar. * @default false */ fullWidthButton?: boolean; /** * Si el formulario debe estar en modo solo lectura (todos los campos deshabilitados). * @default false */ isReadOnly?: boolean; /** * Nombre de la propiedad que actúa como identificador único. * @default 'id' */ idField?: string; /** Compañía seleccionada actualmente */ selectedCompany?: MasterCrudCompany; /** Si está activa la funcionalidad por compañía */ activeByCompany?: boolean; /** Indica si la selección de compañía es obligatoria (oculta Global) */ companyRequired?: boolean; /** * Determina si se deben mostrar los valores globales debajo de los campos. * @default false */ showGlobalValues?: boolean; /** Callback para buscar coincidencias onBlur */ onSearchMatches?: (field: MasterCrudField, value: string) => void; /** Permite realizar sobreescrituras en modo multicompañía */ canOverrideMultiCompany?: boolean; /** Lista de todas las compañías (incluyendo Global) */ companies?: MasterCrudCompany[]; /** Callback cuando cambia la compañía en el selector del formulario */ onCompanyChange?: (company: MasterCrudCompany, recordId?: string | number) => void; /** Callback para cambiar la visibilidad de valores globales */ onShowGlobalValuesChange?: (show: boolean) => void; /** Callback para disparar la acción 'Usar datos globales' */ onUseGlobalData?: () => void; /** Callback para disparar la creación de un nuevo registro */ onCreateNew?: () => void; /** Callback para abrir el modal de vinculación de compañías */ onLinkCompanies?: () => void; /** * Renderizado personalizado del contenido del formulario. * Si se define, se renderiza en el área de contenido manteniendo el Layout (Header/Footer). */ renderContent?: (props: MasterCrudFormProps & { renderField: (accessorKey: string, inputRenderer: (value: any, onChange: (val: any) => void) => ReactNode) => ReactNode; }) => ReactNode; /** Valores actuales del formulario (para uso en renderContent) */ values?: Partial; /** Función para actualizar un valor (para uso en renderContent) */ setValue?: (name: string, value: any) => void; /** Errores actuales de validación (para uso en renderContent) */ errors?: Record; /** Si se deben ocultar las pestañas automáticas de entidades dinámicas */ hideDynamicTabs?: boolean; /** Función para registrar referencias de componentes dinámicos internos */ registerDynamicRef?: (entityCode: string, ref: any) => void; /** Metadatos de la característica dinámica cargada */ dynamicFeature?: any; /** * Si es true, muestra un diálogo de alerta central cuando hay errores de validación. * @default false */ showValidationErrorDialog?: boolean; /** * Organización opcional del formulario en pestañas. Con más de una pestaña, los campos * se agrupan y renderizan en pestañas (diseño del prototipo). Asignación por * `formTabs[].fields` o `field.config.tab`; los no asignados van a la primera pestaña. */ formTabs?: MasterCrudFormTab[]; /** Código (path) del Feature de Gestión de Archivos */ featCodeFileManager?: string; /** Configuración del FileUploaderGallery embebido */ fileManagerConfig?: FileManagerConfig; } /** * Estado interno del formulario */ export interface FormState { /** Valores del formulario */ values: Partial; /** Errores de validación por campo */ errors: Record; /** Si el formulario ha sido tocado */ touched: Record; /** Si el formulario está siendo enviado */ isSubmitting: boolean; } /** * Tipo de vista para el toggle de la toolbar */ export type ToolbarViewType = 'grid' | 'list'; /** * Opción de ordenamiento para el dropdown de la toolbar */ export interface SortOption { /** Valor único de la opción */ value: string; /** Etiqueta visible */ label: string; /** Campo por el cual ordenar */ field: string; /** Dirección del ordenamiento */ direction: 'asc' | 'desc'; } /** * Opción de filtro para el dropdown de la toolbar */ export interface FilterOption { /** Valor único del filtro */ value: string; /** Etiqueta visible */ label: string; /** Icono opcional */ icon?: ReactNode; } /** * Props del componente MasterCrudToolbar */ export interface MasterCrudToolbarProps { /** * Título que se muestra a la izquierda */ title: string; /** * Placeholder del input de búsqueda * @default 'Buscar' */ searchPlaceholder?: string; /** * Valor actual de la búsqueda */ searchValue?: string; /** * Callback cuando cambia el valor de búsqueda */ onSearchChange?: (value: string) => void; /** * Si se muestra el campo de búsqueda * @default true */ showSearch?: boolean; /** * Opciones del dropdown de filtros */ filterOptions?: FilterOption[]; /** * Filtro seleccionado actualmente */ selectedFilter?: string; /** * Callback cuando cambia el filtro */ onFilterChange?: (value: string) => void; /** * Si se muestra el dropdown de filtros * @default true */ showFilter?: boolean; /** * Texto del botón de filtros * @default 'Filtrar' */ filterButtonText?: string; /** * Opciones del dropdown de ordenamiento */ sortOptions?: SortOption[]; /** * Ordenamiento seleccionado actualmente (nombre del campo) */ selectedSort?: string; /** * Dirección del ordenamiento actual */ sortDirection?: 'asc' | 'desc'; /** * Callback cuando cambia el ordenamiento (campo) */ onSortChange?: (value: string) => void; /** * Callback cuando cambia la dirección del ordenamiento */ onSortDirectionChange?: (direction: 'asc' | 'desc') => void; /** * Si se muestra el dropdown de ordenamiento * @default true */ showSort?: boolean; /** * Texto del botón de ordenamiento * @default 'Ordenar' */ sortButtonText?: string; /** * Vista actual (grid o list) * @default 'list' */ viewType?: ToolbarViewType; /** * Callback cuando cambia la vista */ onViewChange?: (view: ToolbarViewType) => void; /** * Si se muestra el toggle de vista * @default true */ showViewToggle?: boolean; /** * Si se muestra el botón de búsqueda avanzada * @default false */ showAdvancedSearch?: boolean; /** * Contenido adicional a renderizar en la toolbar */ extraContent?: ReactNode; /** * Clases CSS adicionales */ className?: string; /** Acciones globales adicionales */ globalActions?: MasterCrudAction[]; /** Acciones grupales (cuando hay selección) */ batchActions?: MasterCrudAction[]; /** Si hay registros seleccionados actualmente */ hasSelection?: boolean; /** Si la búsqueda avanzada está activa */ isAdvancedSearchActive?: boolean; /** Callback para alternar búsqueda avanzada */ onAdvancedSearchToggle?: () => void; /** Si el contenedor de búsqueda y filtros es visible inicialmente */ initialFiltersVisible?: boolean; /** Habilita la selección de compañía */ activeByCompany?: boolean; /** Indica si la selección de compañía es obligatoria */ companyRequired?: boolean; /** Lista de compañías disponibles */ companies?: MasterCrudCompany[]; /** Compañía seleccionada actualmente */ selectedCompany?: MasterCrudCompany; /** Callback cuando cambia la compañía */ onCompanyChange?: (company: MasterCrudCompany, recordId?: string | number) => void; /** Si se muestra la opción "Exportar" en el menú de más opciones. @default false */ showExport?: boolean; /** Callback al pulsar "Exportar" */ onExport?: () => void; /** * Alcance de filas en modo sobrescritura multicompañía (Global + compañía seleccionada). * 'all' = todos los registros; 'company' = solo los habilitados en la compañía. */ viewScope?: 'all' | 'company'; /** Callback cuando cambia el alcance de filas */ onViewScopeChange?: (scope: 'all' | 'company') => void; /** * Si se muestran los dos switches de alcance ("Registros a mostrar") en el menú. * Solo aplica con compañía seleccionada distinta de Global. * @default false */ showViewScope?: boolean; /** Callback del enlace "Volver" de la barra de navegación. Si no se provee, usa history.back(). */ onBack?: () => void; /** Función de traducción inyectada por el padre */ t: (key: string, defaultValue?: string) => string; } //# sourceMappingURL=MasterCrud.types.d.ts.map