frontend-plus-react 0.5.0 → 0.6.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/CLAUDE.md +1 -0
- package/README.md +1 -1
- package/dist/index.cjs +961 -921
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +767 -727
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/components/grid/GenericDataGrid.tsx","../src/hooks/useApiCall.ts","../src/contexts/SnackbarContext.tsx","../src/utils/errores.ts","../src/env.ts","../src/utils/fetchApi.ts","../src/store/index.ts","../src/store/clientContextSlice.ts","../src/store/routerSlice.ts","../src/store/menuUiSlice.ts","../src/components/ConfirmDialog.tsx","../src/components/grid/DataGridOptionsDialog.tsx","../src/components/grid/utils/helpers.ts","../src/utils/routeUtils.ts","../src/types/index.ts","../src/hooks/grid/useGridActions.ts","../src/hooks/grid/useGridDataView.ts","../src/components/grid/utils/typedControls.ts","../src/hooks/grid/useGridEvents.ts","../src/components/grid/detalles.tsx","../src/pages/WScreens.tsx","../src/components/grid/condiciones.ts","../src/hooks/grid/teclasDeCelda.ts","../src/utils/fixedFields.ts","../src/utils/referencias.ts","../src/utils/expanders.ts","../src/hooks/grid/useGridLayout.ts","../src/hooks/grid/useGridMenu.ts","../src/hooks/grid/useGridColumns.ts","../src/utils/functions.tsx","../src/components/grid/renderers/headerCellRenderers.tsx","../src/components/grid/estadoGrilla.ts","../src/components/grid/FilterInputRender.tsx","../src/components/forms/TypedField.tsx","../src/components/grid/renderers/summaryCellRenderers.tsx","../src/components/grid/renderers/cellRenderers.tsx","../src/components/grid/ExampleClientSideComponent.tsx","../src/components/grid/FallbackClientSideRenderer.tsx","../src/components/grid/clientSides.ts","../src/components/grid/InputRenderer.tsx","../src/hooks/useTableRecordSave.ts","../src/components/grid/MenuDeOpciones.tsx","../src/components/grid/SelectorDeFecha.tsx","../src/components/grid/VentanaDeTexto.tsx","../src/components/grid/renderers/editCellRenderers.tsx","../src/components/grid/ImportDialog.tsx","../src/components/grid/ExportDialog.tsx","../src/pages/VerticalEditorPage.tsx","../src/utils/parametrosDeTabla.ts","../src/rutaBase.ts","../src/App.tsx","../src/components/MainLayout.tsx","../src/contexts/AppContext.tsx","../src/components/SideMenu.tsx","../src/utils/menuInicial.ts","../src/hooks/useLogout.tsx","../src/components/SessionExpiredMessage.tsx","../src/pages/PantallaInicial.tsx","../src/pages/LogoutPage.tsx","../src/pages/LoginPage.tsx","../src/components/ProcedureForm.tsx","../src/pages/procedure-results/SuccessDisplay.tsx","../src/pages/procedure-results/resultsOk.ts","../src/components/PrivateRoute.tsx","../src/components/InitialRedirectHandler.tsx","../src/pages/GenericDataGridPage.tsx","../src/pages/PantallaDeRuta.tsx","../src/pages/MenuTablePage.tsx","../src/puente/PantallaBP.tsx","../src/puente/clienteBP.ts","../src/puente/ConsolaBP.tsx","../src/puente/pantallaDeBP.ts","../src/puente/habilitacion.ts","../src/puente/GrillaBP.tsx","../src/puente/CapturaBP.tsx","../src/puente/capturaDeNavegacion.ts"],"sourcesContent":["export { default as GenericDataGrid } from './components/grid/GenericDataGrid';\nexport { clientSides, extendClientSides } from './components/grid/clientSides';\nexport { extendConditions } from './components/grid/condiciones';\nexport { ConfirmDialog } from './components/ConfirmDialog';\n\n// Componente de rutas genérico\nexport { FrontendPlusReactRoutes, FrontendPlusProviders, default as App } from './App';\nexport type { WScreenMap, ResultsOksMap, ClientSidesMap, AppProps, CondicionDeDetalle, CondicionesMap, FilaDeDatos } from './types';\n\nexport { default as MainLayout } from './components/MainLayout';\n\n// Contextos\nexport { SnackbarProvider, useSnackbar } from './contexts/SnackbarContext';\nexport { AppProvider, useApp } from './contexts/AppContext';\n\n// Hooks\nexport { useApiCall, useApiCallWithoutSnackbar } from './hooks/useApiCall';\nexport { default as useLogout } from './hooks/useLogout';\n\n// Tipos\nexport * from './types'; // Exporta todas las interfaces y tipos\n\n// Utilidades\nexport { cambiarGuionesBajosPorEspacios } from './utils/functions';\n\n//pages\nexport { wScreens, extendWScreens } from './pages/WScreens';\nexport { resultsOk as resultComponents, extendResultsOk } from './pages/procedure-results/resultsOk';\n\n// Puente con el cliente de backend-plus (grilla y wScreens que todavía no están en React)\nexport { GrillaBP } from './puente/GrillaBP';\nexport { habilitarPuenteBP } from './puente/habilitacion';\n\nexport type { WScreenProps } from './types/index';\nexport type { ResultOkProps } from './types/index';\nexport type { ClientSideProps } from './types/index';\n","// src/components/GenericDataGrid.tsx\nimport React, { useState, useEffect, useCallback, useContext, useMemo, useRef } from 'react';\nimport { DataGrid, DataGridHandle, RenderCellProps, CellSelectArgs, Column, ColumnWidth } from 'react-data-grid';\nimport 'react-data-grid/lib/styles.css';\nimport './GenericDataGrid.css';\n\nimport { useApiCall } from '../../hooks/useApiCall';\nimport { CircularProgress, Typography, Box, Alert, useTheme, Dialog } from '@mui/material';\n\nimport { useSnackbar } from '../../contexts/SnackbarContext';\nimport { useClientContext } from '../../store';\nimport { mensajeDeError, requerido } from '../../utils/errores';\n\nimport { FilaDeDatos, FilaDeResumen, FixedField, TableDefinition, Ancestor, TeclaDeEditor } from '../../types';\n\nimport { ConfirmDialog } from '../ConfirmDialog';\nimport { DataGridOptionsDialog } from './DataGridOptionsDialog';\n\nimport { DetailTable, FieldDefinition } from 'backend-plus';\nimport { getPrimaryKeyValues, sameValue, Typer, NEW_ROW_INDICATOR } from './utils/helpers';\nimport { useGridActions } from '../../hooks/grid/useGridActions';\nimport { useGridDataView } from '../../hooks/grid/useGridDataView';\nimport { useGridEvents } from '../../hooks/grid/useGridEvents';\nimport { useGridLayout } from '../../hooks/grid/useGridLayout';\nimport { useGridMenu } from '../../hooks/grid/useGridMenu';\nimport { calcularAnchosDeColumnas, useGridColumns } from '../../hooks/grid/useGridColumns';\nimport { ImportDialog } from './ImportDialog';\nimport { ExportDialog } from './ExportDialog';\nimport { VentanaDeTexto } from './VentanaDeTexto';\nimport { VerticalEditorPage } from '../../pages/VerticalEditorPage';\nimport { cambiarGuionesBajosPorEspacios } from '../../utils/functions';\nimport { aFiltrosIniciales, FiltroInicial, fusionar } from '../../utils/parametrosDeTabla';\nimport { rutaBase } from '../../rutaBase';\nimport { crearEstadoGrilla, EstadoGrillaContext, MapaFeedback, VistaGrillaContext } from './estadoGrilla';\nimport {\n AltoDisponibleContext, ALTO_INICIAL_DETALLE, ALTO_MINIMO_DETALLE, CacheGrillasContext, DetalleWScreen, calcularFixedFieldsDetalle,\n claveDetalle, DETALLE, DetallesGrillaContext, esFilaDetalle, FilaDetalle, GrillaGuardada\n} from './detalles';\n\n\nexport const SUMMARY_ROW_HEIGHT = 35;\nexport const FILTER_ROW_HEIGHT = 35;\nexport const HEADER_ROW_HEIGHT = 30;\nexport const ROW_HEIGHT = 30;\nexport const MIN_BODY_HEIGHT = 260;\n\n/** Cuánto dura el feedback verde de una celda grabada */\nconst DURACION_FEEDBACK_EXITO = 3000;\n\n/** Alto del título de una grilla anidada (para informar cuánto necesita) */\nconst ALTO_TITULO_ANIDADA = 26;\n/** Alto mínimo de una grilla anidada sin filas (alcanza para el mensaje \"No hay filas\") */\nconst ALTO_MINIMO_CUERPO_ANIDADA = 120;\n/** Espacio extra bajo una grilla anidada: lo justo para una barra de scroll horizontal */\nconst HOLGURA_ANIDADA = 24;\n/** Ancho mínimo de una grilla anidada: que alcance para el título */\nconst ANCHO_MINIMO_ANIDADA = 240;\n/** Lo que el detalle ocupa de más que su grilla: sangría a la izquierda, margen a la derecha y la línea de color */\nconst SANGRIA_DETALLE = 28 + 8 + 3;\n/** Espacio que se deja bajo la grilla principal al calcular cuánto alto tiene disponible */\nconst MARGEN_INFERIOR_PAGINA = 24;\n/** Valores por omisión de backend-plus (myOwn.firstDisplayCount y myOwn.firstDisplayOverLimit) */\nconst PRIMERAS_FILAS_ANIDADA = 20;\nconst LIMITE_PRIMERAS_FILAS_ANIDADA = 30;\n\ninterface GenericDataGridProps {\n tableName: string;\n fixedFields?: FixedField[];\n /** Como en backend-plus (`fc` de la dirección o del menú): la fila de filtros con la que se abre la grilla */\n filterColumns?: FiltroInicial[][];\n /** `td`: lo que se le cambia a la definición de la tabla que manda el servidor */\n tableDef?: Record<string, unknown>;\n /** `pf` y `pick` de backend-plus: viajan con el pedido de datos */\n parameterFunctions?: Record<string, unknown>;\n pick?: string;\n /** Abre un detalle en otra pestaña (Ctrl+click o botón del medio en el botón del detalle) */\n onOpenDetail?: (tableName: string, fixedFields: FixedField[], label: string, ancestors: Ancestor[]) => void;\n gridStyles?: React.CSSProperties;\n ancestors?: Ancestor[];\n /** Es una grilla de detalle desplegada dentro de otra grilla */\n anidada?: boolean;\n /** Título a mostrar en lugar del de la tabla (por omisión, el título o nombre de la tabla, igual que la grilla principal) */\n titulo?: string;\n /** Clave en el CacheGrillasContext para volver a mostrarse sin recargar */\n claveCache?: string;\n /** La grilla anidada informa el alto que necesita para mostrarse completa */\n onAltoDeseado?: (alto: number) => void;\n /** La grilla anidada informa el ancho que necesita (sus columnas y los detalles que tenga abiertos) */\n onAnchoDeseado?: (ancho: number) => void;\n /** Alto máximo que puede tener esta grilla anidada (lo calcula la de arriba a partir de la ventana) */\n altoDisponible?: number;\n /**\n * Tope opcional para el alto de las filas de detalle (en px). Sin tope (lo normal) cada detalle\n * se muestra completo, como en backend-plus; con tope, el detalle más alto scrollea por dentro\n * y virtualiza sus propias filas (útil si hay detalles de miles de filas).\n */\n altoMaximoDetalle?: number;\n}\n\nexport { NEW_ROW_INDICATOR };\n/** Las filas de datos y de resumen que maneja esta grilla */\nexport type ColumnaDeGrilla = Column<FilaDeDatos, FilaDeResumen>;\n\ninterface BaseCustomColumn extends ColumnaDeGrilla {\n customType: 'default' | 'detail' | 'action',\n tableDefinition: TableDefinition,\n}\n\n/**\n * Columna de un campo. Solo lleva datos que no cambian mientras se edita\n * (el feedback y los cambios locales viven en el EstadoGrilla).\n */\nexport interface DefaultColumn extends BaseCustomColumn {\n customType: 'default';\n fieldDef: FieldDefinition;\n typer: Typer;\n isMandatory: boolean;\n isNumeric: boolean;\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>,\n primaryKey: string[],\n fixedFields: FixedField[] | undefined,\n registrarEditor: (procesar: (key: string) => void) => () => void,\n handleKeyPressInEditor: (rowIndex: number, columnKey: string, event: TeclaDeEditor, currentColumns: ColumnaDeGrilla[], handleCommit: (currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => Promise<void>, valorActual: unknown) => boolean\n}\n\nexport interface DetailColumn extends BaseCustomColumn {\n customType: 'detail';\n detailTable: DetailTable,\n primaryKey: string[],\n detailKey: string\n}\n\nexport interface ActionColumn extends BaseCustomColumn {\n customType: 'action';\n handleDeleteRow: (row: FilaDeDatos) => void;\n handleAddRow: (row?: FilaDeDatos) => void;\n handleVerticalEditRow: (row: FilaDeDatos) => void;\n}\n\n\nexport type CustomColumn = DefaultColumn | DetailColumn | ActionColumn;\n\n/** ¿Es una de las columnas que arma esta librería (campo, detalle o acciones)? */\nexport const esColumnaPropia = (columna: ColumnaDeGrilla): columna is CustomColumn => 'customType' in columna;\n\nconst SIN_ANCESTROS: Ancestor[] = [];\n\n/** Cargas de tabla (estructura + datos) que están en curso, por tabla y filtros */\nconst cargasEnCurso = new Map<string, Promise<{ definition: TableDefinition, data: FilaDeDatos[] }>>();\n\n/**\n * Componente auxiliar para mostrar un registro padre como una grilla estática.\n * Es de solo lectura: antes intentaba grabar con 'table_data_save', que no existe en backend-plus,\n * y además las columnas no tenían editor, así que la edición nunca funcionó.\n */\nconst StaticAncestorGrid: React.FC<{\n ancestor: Ancestor,\n index: number,\n fieldsToHide?: FixedField[],\n}> = ({ ancestor, index, fieldsToHide }) => {\n const theme = useTheme();\n const { tableDefinition, row } = ancestor; // ya no usamos fixedFields del propio objeto para filtrar SE a sí mismo\n\n const columns: Column<FilaDeDatos>[] = useMemo(() => {\n // Ocultamos los campos que el nivel SUPERIOR usó para filtrar a ESTE ancestro\n const filteredFields = tableDefinition.fields.filter(field =>\n !fieldsToHide?.some(ff => ff.fieldName === field.name)\n );\n\n return filteredFields.map(field => {\n const isPrimaryKey = tableDefinition.primaryKey.includes(field.name);\n return {\n key: field.name,\n name: field.label || cambiarGuionesBajosPorEspacios(field.name),\n width: 150,\n editable: false,\n renderHeaderCell: () => (\n <Box sx={{ p: 0.5, height: '100%', display: 'flex', alignItems: 'center' }}>\n <Typography\n variant=\"caption\"\n sx={{\n fontWeight: 'bold',\n textDecoration: isPrimaryKey ? 'underline' : 'none',\n fontSize: '0.930rem'\n }}\n >\n {field.label || cambiarGuionesBajosPorEspacios(field.name)}\n </Typography>\n </Box>\n ),\n renderCell: (props: RenderCellProps<FilaDeDatos>) => (\n <Box sx={{ px: 1, height: '100%', display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ fontSize: '0.875rem' }} noWrap>\n {String(props.row[field.name] ?? '')}\n </Typography>\n </Box>\n )\n };\n });\n }, [tableDefinition, fieldsToHide]);\n\n return (\n <Box sx={{\n mb: 1.5,\n ml: index * 4, // El primer ancestro (index 0) no tiene margen, los siguientes sí\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n overflow: 'hidden',\n boxShadow: theme.shadows[1],\n bgcolor: 'background.paper'\n }}>\n <Box sx={{\n bgcolor: 'primary.main',\n color: 'primary.contrastText',\n px: 1.5,\n py: 0.5,\n borderBottom: '1px solid',\n borderColor: 'divider',\n display: 'flex',\n alignItems: 'center',\n fontWeight: 'bold',\n gap: 1\n }}>\n <Typography variant=\"subtitle2\" component=\"div\">\n {cambiarGuionesBajosPorEspacios(ancestor.tableName)}\n </Typography>\n </Box>\n <DataGrid\n className='rdg-light'\n columns={columns}\n rows={[row]}\n headerRowHeight={HEADER_ROW_HEIGHT}\n rowHeight={ROW_HEIGHT}\n rowKeyGetter={() => 'single-row'}\n style={{ height: 78 }}\n />\n </Box>\n );\n};\n\nconst GenericDataGrid: React.FC<GenericDataGridProps> = ({\n tableName,\n fixedFields: fixedFieldsRecibidos,\n filterColumns: filterColumnsRecibidos,\n tableDef: tableDefRecibido,\n parameterFunctions: parameterFunctionsRecibido,\n pick: pickRecibido,\n gridStyles,\n onOpenDetail,\n ancestors = SIN_ANCESTROS,\n anidada = false,\n titulo,\n claveCache,\n onAltoDeseado,\n onAnchoDeseado,\n altoDisponible,\n altoMaximoDetalle,\n}) => {\n // fixedFields suele llegar como un array nuevo en cada render del padre:\n // se estabiliza por contenido para no volver a pedir los datos ni recrear las columnas\n const claveFixedFields = JSON.stringify(fixedFieldsRecibidos ?? null);\n const fixedFields = useMemo(() => fixedFieldsRecibidos, [claveFixedFields]); // eslint-disable-line react-hooks/exhaustive-deps\n // lo mismo con el resto de lo que llega de la dirección o del menú\n const claveParametros = JSON.stringify([filterColumnsRecibidos ?? null, tableDefRecibido ?? null, parameterFunctionsRecibido ?? null, pickRecibido ?? null]);\n const parametros = useMemo(\n () => ({ filterColumns: filterColumnsRecibidos, tableDef: tableDefRecibido, parameterFunctions: parameterFunctionsRecibido, pick: pickRecibido }),\n [claveParametros] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n // Cache compartido por toda la jerarquía de grillas (la grilla de más arriba lo crea)\n const cacheHeredado = useContext(CacheGrillasContext);\n const [cachePropio] = useState(() => new Map<string, GrillaGuardada>());\n const cache = cacheHeredado ?? cachePropio;\n // Lo guardado de esta grilla (si ya se mostró antes y salió de la pantalla por la virtualización)\n const [guardada] = useState(() => claveCache ? cache.get(claveCache) : undefined);\n // Qué tabla+fixedFields están cargados; si viene del cache ya están y no se vuelve a pedir\n const claveCargadaRef = useRef<string | null>(guardada ? `${tableName}|${claveFixedFields}|${claveParametros}` : null);\n\n const [tableDefinition, setTableDefinition] = useState<TableDefinition | null>(guardada?.tableDefinition ?? null);\n const [tableData, setTableData] = useState<FilaDeDatos[]>(guardada?.tableData ?? []);\n const [cargandoInicial, setCargandoInicial] = useState(!guardada);\n const [errorDeCarga, setErrorDeCarga] = useState<string | null>(null);\n const [anchosCalculados, setAnchosCalculados] = useState<Map<string, number>>(() => guardada?.anchosCalculados ?? new Map());\n // Detalles desplegados (claves `${rowId}|${abr}`) y el alto que pidió cada grilla anidada\n const [detallesAbiertos, setDetallesAbiertos] = useState<string[]>(guardada?.detallesAbiertos ?? []);\n const [altosDetalle, setAltosDetalle] = useState<Map<string, number>>(() => guardada?.altosDetalle ?? new Map());\n // Ancho que pidió cada grilla anidada (para ensanchar esta grilla si el detalle es más ancho que sus columnas)\n const [anchosDetalle, setAnchosDetalle] = useState<Map<string, number>>(() => new Map());\n const [selectedRows, setSelectedRows] = useState((): ReadonlySet<string> => new Set());\n const [, setSelectedCell] = useState<CellSelectArgs<FilaDeDatos, FilaDeResumen> | undefined>(undefined);\n\n // feedback y cambios locales por celda: fuera del estado de React (ver estadoGrilla.ts)\n const [estadoGrilla] = useState(crearEstadoGrilla);\n const theme = useTheme();\n\n const [openDataGridOptions, setOpenDataGridOptions] = useState(false);\n const [dataGridOptionsAnchorEl, setDataGridOptionsAnchorEl] = useState<HTMLElement | null>(null);\n\n const [exitingRowIds, setExitingRowIds] = useState<Set<string>>(new Set());\n\n const { showError } = useSnackbar();\n const { username: usuario } = useClientContext();\n const dataGridRef = useRef<DataGridHandle>(null);\n const rowAtEditStartRef = useRef<FilaDeDatos | null>(null); // Captura la fila cuando abre el vertical editor\n const [openImportDialog, setOpenImportDialog] = useState(false);\n const [openExportDialog, setOpenExportDialog] = useState(false);\n const [columnWidths, setColumnWidths] = useState<ReadonlyMap<string, ColumnWidth>>(() => new Map());\n // loading solo indica que hay una llamada en curso (borrar, importar, refrescar):\n // ya no reemplaza la grilla por un spinner, eso es solo para la carga inicial\n const { callApi, callApiUpload, loading } = useApiCall();\n\n const primaryKey = useMemo(() => {\n if (!tableDefinition) return ['id'];\n return tableDefinition.primaryKey && tableDefinition.primaryKey.length > 0 ? tableDefinition.primaryKey : ['id'];\n }, [tableDefinition]);\n\n const {\n sortColumns, setSortColumns, getInitialSortColumns, handleSortColumnsChange, resetSorting,\n filters, setFilters, isFilterRowVisible, toggleFilterVisibility, aplicarFiltrosIniciales,\n filteredRows, summaryData, resetView\n } = useGridDataView({ tableData, tableDefinition });\n\n // ── Detalles desplegados ─────────────────────────────────────────────────\n const abiertos = useMemo(() => new Set(detallesAbiertos), [detallesAbiertos]);\n // Se reusan los objetos de las filas de detalle mientras no cambie su fila padre:\n // así react-data-grid no redibuja la grilla anidada en cada render\n const filasDetalleRef = useRef(new Map<string, FilaDetalle>());\n\n /** filteredRows con las filas de detalle intercaladas debajo de su fila padre (en el orden de detailTables) */\n const filasMostradas = useMemo(() => {\n const detailTables = tableDefinition?.detailTables ?? [];\n if (abiertos.size === 0 || detailTables.length === 0) return filteredRows;\n const anteriores = filasDetalleRef.current;\n const nuevas = new Map<string, FilaDetalle>();\n const filas: FilaDeDatos[] = [];\n filteredRows.forEach(row => {\n filas.push(row);\n const rowId = getPrimaryKeyValues(row, primaryKey);\n detailTables.forEach(detailTable => {\n const clave = claveDetalle(rowId, detailTable);\n if (!abiertos.has(clave)) return;\n const anterior = anteriores.get(clave);\n const filaDetalle: FilaDetalle = anterior && anterior.filaPadre === row ? anterior : {\n [DETALLE]: true,\n clave,\n filaPadre: row,\n detailTable,\n fixedFields: calcularFixedFieldsDetalle(detailTable, row),\n };\n nuevas.set(clave, filaDetalle);\n filas.push(filaDetalle);\n });\n });\n filasDetalleRef.current = nuevas;\n return filas;\n }, [filteredRows, abiertos, tableDefinition, primaryKey]);\n\n const altoDeDetalle = useCallback((clave: string) => altosDetalle.get(clave) ?? ALTO_INICIAL_DETALLE, [altosDetalle]);\n const altoDetalles = useMemo(() =>\n filasMostradas.reduce((total, row) => esFilaDetalle(row) ? total + altoDeDetalle(row.clave) : total, 0)\n , [filasMostradas, altoDeDetalle]);\n\n const {\n handleAddRow, handleConfirmDelete, handleDeleteRow,\n openConfirmDialog, rowToDelete, handleImportFile,\n handleVerticalEditRow, openVerticalEditDialog,\n setOpenVerticalEditDialog, rowToEditVertical,\n setRowToEditVertical\n } = useGridActions({\n tableDefinition, tableName, primaryKey,\n fixedFields, setExitingRowIds, setLocalCellChanges: estadoGrilla.setCambiosLocales,\n setSelectedRows, setTableData, callApi, callApiUpload,\n resetView\n });\n\n // Como en backend-plus: hasta firstDisplayOverLimit filas se muestran todas; si hay más, solo\n // firstDisplayCount. En BP el resto se pide con \"ver más\"; acá se scrollea (con virtualización).\n // Solo aplica a las grillas anidadas: la principal ocupa la pantalla y scrollea siempre.\n const firstDisplayCount = tableDefinition?.firstDisplayCount ?? PRIMERAS_FILAS_ANIDADA;\n const firstDisplayOverLimit = tableDefinition?.firstDisplayOverLimit ?? LIMITE_PRIMERAS_FILAS_ANIDADA;\n const filasVisiblesMax = anidada ? (filteredRows.length <= firstDisplayOverLimit ? filteredRows.length : firstDisplayCount) : undefined;\n\n // Una grilla anidada sin campos con totales no necesita pie: era una fila vacía que ocupaba lugar\n const conPie = !anidada || !!tableDefinition?.fields.some(field => field.aggregate);\n\n const { gridHeight, bottomSummaryRow, summaryRows, filterHeight } = useGridLayout({\n filteredRows, isFilterRowVisible, tableData, summaryData, filters, setFilters, altoDetalles,\n altoMinimoCuerpo: anidada ? ALTO_MINIMO_CUERPO_ANIDADA : MIN_BODY_HEIGHT,\n holgura: anidada ? HOLGURA_ANIDADA : undefined,\n filasVisiblesMax,\n conPie,\n });\n\n // F9 en una celda que no se edita: el contenido completo (JSON o texto largo) bajo la celda\n const [contenidoDeCelda, setContenidoDeCelda] = useState<{ anclaje: HTMLElement, texto: string } | null>(null);\n const verContenido = useCallback((anclaje: HTMLElement, texto: string) => setContenidoDeCelda({ anclaje, texto }), []);\n const cerrarContenidoDeCelda = useCallback(() => {\n const anclaje = contenidoDeCelda?.anclaje;\n setContenidoDeCelda(null);\n setTimeout(() => anclaje?.focus(), 0);\n }, [contenidoDeCelda]);\n\n const { handleCellDoubleClick, handleCellClick, handleCellKeyDown, handleCellMouseDown, handleKeyPressInEditor, registrarEditor, handleSelectedCellChange, handleRowsChange } = useGridEvents({\n dataGridRef, filteredRows: filasMostradas, fixedFields, tableData, tableDefinition, setSelectedCell, setTableData, setSortColumns, verContenido\n });\n\n // Al cambiar de tabla se limpia todo (no al montar: puede venir restaurada del cache)\n const tableNameAnteriorRef = useRef(tableName);\n useEffect(() => {\n if (tableNameAnteriorRef.current === tableName) return;\n tableNameAnteriorRef.current = tableName;\n setDetallesAbiertos([]);\n setAltosDetalle(new Map());\n resetView();\n setSelectedRows(new Set());\n setTableDefinition(null);\n setTableData([]);\n estadoGrilla.setFeedback(new Map());\n estadoGrilla.setCambiosLocales(new Map());\n setExitingRowIds(new Set());\n setColumnWidths(new Map());\n }, [tableName, estadoGrilla, resetView]);\n\n useEffect(() => {\n const claveCarga = `${tableName}|${claveFixedFields}|${claveParametros}`;\n if (claveCargadaRef.current === claveCarga) {\n // Restaurada del cache: no se vuelve a pedir (como backend-plus al volver a desplegar un detalle)\n if (guardada) setSortColumns(guardada.sortColumns);\n return;\n }\n let vigente = true;\n const fetchDataAndDefinition = async () => {\n setCargandoInicial(true);\n setErrorDeCarga(null);\n try {\n // Si ya hay una carga igual en curso (el modo desarrollo de React monta los efectos dos veces) se espera esa\n const clavePedido = `${tableName}|${claveFixedFields}|${JSON.stringify([parametros.parameterFunctions ?? null, parametros.pick ?? null])}`;\n let pedido = cargasEnCurso.get(clavePedido);\n if (!pedido) {\n const nuevo = (async () => {\n const definition = requerido(\n await callApi<TableDefinition>('table_structure', { table: tableName }, { isCritical: true }),\n `la estructura de '${tableName}'`\n );\n const validFixedFields = fixedFields?.filter(ff =>\n definition.fields.some(field => field.name === ff.fieldName)\n );\n const data = requerido(\n await callApi<FilaDeDatos[]>('table_data', {\n table: tableName,\n fixedFields: validFixedFields ?? [],\n // como backend-plus (TableConnector.getData); solo si vienen, para no cambiarle el pedido a quien no los usa\n ...(parametros.parameterFunctions ? { paramfun: parametros.parameterFunctions } : {}),\n ...(parametros.pick !== undefined ? { pick: parametros.pick } : {})\n }, { isCritical: true }),\n `los datos de '${tableName}'`\n );\n return { definition, data };\n })();\n cargasEnCurso.set(clavePedido, nuevo);\n // cuando termina se saca: un refrescar posterior tiene que volver a pedir\n const sacar = () => { cargasEnCurso.delete(clavePedido); };\n nuevo.then(sacar, sacar);\n pedido = nuevo;\n }\n const { definition: definicionDelServidor, data } = await pedido;\n if (!vigente) return;\n claveCargadaRef.current = claveCarga;\n // como backend-plus (TableConnector.getStructure): lo de `td` se mezcla sobre la definición del servidor\n const definition = parametros.tableDef ? fusionar(definicionDelServidor, parametros.tableDef) : definicionDelServidor;\n setTableDefinition(definition);\n setSortColumns(getInitialSortColumns(definition));\n setAnchosCalculados(calcularAnchosDeColumnas(definition, data));\n setTableData(data);\n // la fila de filtros con la que se abre: la de la dirección o del menú (`fc`) o, si no, la de la definición\n const filasDeFiltros = parametros.filterColumns ?? aFiltrosIniciales(definition.filterColumns);\n if (filasDeFiltros.length > 1) console.warn(`'${tableName}': varias filas de filtros (alternativas) todavía no están soportadas; se aplica la primera`);\n if (filasDeFiltros.length > 0) aplicarFiltrosIniciales(filasDeFiltros[0], definition);\n } catch (err) {\n if (!vigente) return;\n setTableDefinition(null);\n setTableData([]);\n const mensaje = `Error al cargar datos para la tabla '${tableName}': ${mensajeDeError(err)}`;\n setErrorDeCarga(mensaje);\n showError(mensaje);\n } finally {\n if (vigente) setCargandoInicial(false);\n }\n };\n fetchDataAndDefinition();\n // si cambia la tabla antes de que llegue la respuesta, se descarta la respuesta vieja\n return () => { vigente = false; };\n }, [tableName, claveFixedFields, fixedFields, claveParametros, parametros, showError, callApi, getInitialSortColumns, setSortColumns, aplicarFiltrosIniciales, guardada]);\n\n // Se guarda el estado para poder volver a mostrar la grilla tal cual si se desmonta\n useEffect(() => {\n if (!claveCache || !tableDefinition || cargandoInicial) return;\n cache.set(claveCache, { tableDefinition, tableData, anchosCalculados, sortColumns, detallesAbiertos, altosDetalle });\n }, [cache, claveCache, tableDefinition, tableData, anchosCalculados, sortColumns, detallesAbiertos, altosDetalle, cargandoInicial]);\n\n // El feedback de éxito se borra solo al rato; el de error queda hasta la próxima edición\n useEffect(() => {\n let timer: ReturnType<typeof setTimeout> | null = null;\n const desuscribir = estadoGrilla.suscribir(() => {\n const hayExitos = Array.from(estadoGrilla.obtenerFeedback().values()).some(f => f.type === 'success');\n if (!hayExitos || timer) return;\n timer = setTimeout(() => {\n timer = null;\n estadoGrilla.setFeedback(anterior => {\n const nuevo: MapaFeedback = new Map(anterior);\n let cambio = false;\n for (const [cellId, feedback] of anterior.entries()) {\n if (feedback.type === 'success') {\n nuevo.delete(cellId);\n cambio = true;\n }\n }\n return cambio ? nuevo : anterior;\n });\n }, DURACION_FEEDBACK_EXITO);\n });\n return () => {\n desuscribir();\n if (timer) clearTimeout(timer);\n };\n }, [estadoGrilla]);\n\n const resetSortingDeLaTabla = useCallback(() => {\n if (tableDefinition) resetSorting(tableDefinition);\n }, [tableDefinition, resetSorting]);\n const abrirImportacion = useCallback(() => setOpenImportDialog(true), []);\n const abrirExportacion = useCallback(() => setOpenExportDialog(true), []);\n\n const { menuOptions } = useGridMenu({\n tableDefinition,\n tableName,\n fixedFields,\n setTableData,\n callApi,\n resetSorting: tableDefinition ? resetSortingDeLaTabla : undefined,\n triggerImport: abrirImportacion,\n triggerExport: abrirExportacion,\n });\n\n const abrirOpcionesDeGrilla = useCallback((anchorEl: HTMLElement) => {\n setDataGridOptionsAnchorEl(anchorEl);\n setOpenDataGridOptions(true);\n }, []);\n\n const editarEnFicha = useCallback((row: FilaDeDatos) => {\n rowAtEditStartRef.current = { ...row }; // Capturar snapshot antes de abrir\n handleVerticalEditRow(row);\n }, [handleVerticalEditRow]);\n\n const { columns } = useGridColumns({\n tableDefinition,\n anchosCalculados,\n fixedFields,\n primaryKey,\n setTableData,\n handleKeyPressInEditor,\n registrarEditor,\n handleDeleteRow,\n handleAddRow,\n handleVerticalEditRow: editarEnFicha,\n toggleFilterVisibility,\n abrirOpcionesDeGrilla,\n });\n\n /** Abre el detalle en otra pestaña (lo que antes hacía siempre el botón del detalle) */\n const abrirDetalleEnPestana = useCallback((row: FilaDeDatos, detailTable: DetailTable) => {\n if (!onOpenDetail || !tableDefinition || !detailTable.table) return;\n // Se acumulan los filtros que ya tiene la grilla actual (heredados de arriba), sin repetir campos\n const porCampo = new Map<string, FixedField>();\n [...(fixedFields ?? []), ...calcularFixedFieldsDetalle(detailTable, row)].forEach(ff => porCampo.set(ff.fieldName, ff));\n const allFixedFields = Array.from(porCampo.values());\n const filterDescription = allFixedFields\n .map(ff => `${cambiarGuionesBajosPorEspacios(ff.fieldName)}: ${ff.value}`)\n .join(', ');\n const label = `${cambiarGuionesBajosPorEspacios(detailTable.table).toUpperCase()} (${filterDescription})`;\n const newAncestors: Ancestor[] = [...ancestors, { tableName: tableDefinition.name, row, tableDefinition, fixedFields: allFixedFields }];\n onOpenDetail(detailTable.table, allFixedFields, label, newAncestors);\n }, [onOpenDetail, tableDefinition, fixedFields, ancestors]);\n\n /** Un detalle wScreen en otra pestaña: la ruta de la pantalla con los fixedFields del detalle como query string */\n const abrirWScreenEnPestana = useCallback((row: FilaDeDatos, detailTable: DetailTable) => {\n const parametros = new URLSearchParams();\n calcularFixedFieldsDetalle(detailTable, row).forEach(ff => parametros.set(ff.fieldName, String(ff.value)));\n window.open(`${window.location.origin}${rutaBase}/wScreens/${detailTable.wScreen}?${parametros}`, '_blank');\n }, []);\n\n const alternarDetalle = useCallback((row: FilaDeDatos, rowId: string, detailTable: DetailTable, enOtraPestana: boolean) => {\n if (enOtraPestana && detailTable.wScreen) {\n abrirWScreenEnPestana(row, detailTable);\n return;\n }\n if (enOtraPestana && onOpenDetail) {\n abrirDetalleEnPestana(row, detailTable);\n return;\n }\n const clave = claveDetalle(rowId, detailTable);\n setDetallesAbiertos(anteriores => anteriores.includes(clave) ? anteriores.filter(c => c !== clave) : [...anteriores, clave]);\n }, [onOpenDetail, abrirDetalleEnPestana, abrirWScreenEnPestana]);\n\n // Lo que se ve de esta grilla (el área de scroll de react-data-grid) y el ancho de su barra de scroll vertical\n const [medidas, setMedidas] = useState<{ ancho: number, alto: number, barra: number } | null>(null);\n useEffect(() => {\n const elemento = dataGridRef.current?.element;\n if (!elemento) return;\n const medir = () => setMedidas(anteriores => {\n const nuevas = { ancho: elemento.clientWidth, alto: elemento.clientHeight, barra: elemento.offsetWidth - elemento.clientWidth };\n return anteriores && anteriores.ancho === nuevas.ancho && anteriores.alto === nuevas.alto && anteriores.barra === nuevas.barra ? anteriores : nuevas;\n });\n medir();\n const observador = new ResizeObserver(medir);\n observador.observe(elemento);\n return () => observador.disconnect();\n }, [tableDefinition, cargandoInicial]);\n const anchoVisible = medidas?.ancho ?? null;\n\n // Alto que puede ocupar esta grilla: la principal, lo que queda de la ventana; una anidada, lo que le asigna la de arriba.\n // Se calcula de arriba hacia abajo y no con el alto medido: una grilla de pocas filas es baja, y si se acotara a\n // su propio alto el detalle quedaría aplastado.\n const [altoDeVentana, setAltoDeVentana] = useState<number | null>(null);\n useEffect(() => {\n if (anidada) return;\n const elemento = dataGridRef.current?.element;\n if (!elemento) return;\n const calcular = () => setAltoDeVentana(Math.max(0, Math.round(window.innerHeight - elemento.getBoundingClientRect().top - MARGEN_INFERIOR_PAGINA)));\n calcular();\n window.addEventListener('resize', calcular);\n return () => window.removeEventListener('resize', calcular);\n }, [anidada, tableDefinition, cargandoInicial]);\n // Una grilla que una wScreen arma dentro de un detalle recibe su alto máximo por contexto (menos el título de la grilla)\n const altoDeContexto = useContext(AltoDisponibleContext);\n const altoDeEstaGrilla = anidada\n ? altoDisponible ?? (altoDeContexto === undefined ? undefined : altoDeContexto - ALTO_TITULO_ANIDADA)\n : altoDeVentana ?? undefined;\n\n // Los detalles de esta grilla se acotan a ese alto (menos el encabezado, el pie y la fila padre), así el detalle\n // completo —con su propio pie— entra sin tener que scrollear la grilla de arriba. Con más contenido que eso\n // el detalle scrollea por dentro y virtualiza sus filas.\n const altoMaximoDetalleEfectivo = altoMaximoDetalle ?? (altoDeEstaGrilla !== undefined\n ? Math.max(ALTO_MINIMO_DETALLE, altoDeEstaGrilla - HEADER_ROW_HEIGHT - (conPie ? SUMMARY_ROW_HEIGHT : 0) - ROW_HEIGHT - (isFilterRowVisible ? FILTER_ROW_HEIGHT : 0))\n : undefined);\n\n const cambiarAnchoDetalle = useCallback((clave: string, ancho: number) => {\n setAnchosDetalle(anteriores => anteriores.get(clave) === ancho ? anteriores : new Map(anteriores).set(clave, ancho));\n }, []);\n\n const cambiarAltoDetalle = useCallback((clave: string, altoDeseado: number) => {\n const alto = Math.min(Math.max(Math.round(altoDeseado), ALTO_MINIMO_DETALLE), altoMaximoDetalleEfectivo ?? Infinity);\n setAltosDetalle(anteriores => anteriores.get(clave) === alto ? anteriores : new Map(anteriores).set(clave, alto));\n }, [altoMaximoDetalleEfectivo]);\n\n const renderDetalle = useCallback((fila: FilaDetalle) => {\n const { detailTable } = fila;\n return (\n <div\n className=\"fpr-detalle\"\n // el ancho lo da el contenido (ver .fpr-detalle) pero no pasa de lo visible de esta grilla\n style={{ maxWidth: anchoVisible ? anchoVisible - 2 : undefined }}\n // las teclas de la grilla anidada no tienen que mover la selección de esta grilla\n onKeyDown={(e) => e.stopPropagation()}\n >\n {detailTable.table\n ? <GenericDataGrid\n tableName={detailTable.table}\n fixedFields={fila.fixedFields}\n anidada\n claveCache={`${claveCache ?? tableName}>${fila.clave}`}\n onAltoDeseado={(alto) => cambiarAltoDetalle(fila.clave, alto)}\n onAnchoDeseado={(ancho) => cambiarAnchoDetalle(fila.clave, ancho)}\n altoDisponible={altoMaximoDetalleEfectivo === undefined ? undefined : altoMaximoDetalleEfectivo - ALTO_TITULO_ANIDADA - 8}\n onOpenDetail={onOpenDetail}\n ancestors={tableDefinition ? [...ancestors, { tableName: tableDefinition.name, row: fila.filaPadre, tableDefinition, fixedFields: fila.fixedFields }] : ancestors}\n />\n : detailTable.wScreen\n ? <DetalleWScreen\n fila={fila}\n altoMaximo={altoMaximoDetalleEfectivo}\n onAlto={(alto) => cambiarAltoDetalle(fila.clave, alto)}\n onAncho={(ancho) => cambiarAnchoDetalle(fila.clave, ancho)}\n />\n : <Alert severity=\"warning\">El detalle '{detailTable.abr}' no tiene ni tabla ni wScreen.</Alert>\n }\n </div>\n );\n }, [anchoVisible, claveCache, tableName, cambiarAltoDetalle, cambiarAnchoDetalle, altoMaximoDetalleEfectivo, onOpenDetail, tableDefinition, ancestors]);\n\n const detallesGrilla = useMemo(() => ({ abiertos, alternarDetalle, renderDetalle }), [abiertos, alternarDetalle, renderDetalle]);\n\n // La grilla anidada le informa a su padre cuánto alto necesita para mostrarse completa\n const altoDeseado = cargandoInicial ? ALTO_INICIAL_DETALLE : gridHeight + ALTO_TITULO_ANIDADA + 8;\n useEffect(() => {\n onAltoDeseado?.(altoDeseado);\n }, [onAltoDeseado, altoDeseado]);\n\n const rowKeyGetter = useCallback((row: FilaDeDatos) => esFilaDetalle(row) ? `detalle:${row.clave}` : getPrimaryKeyValues(row, primaryKey), [primaryKey]);\n const rowHeight = useCallback((row: FilaDeDatos) => esFilaDetalle(row) ? altoDeDetalle(row.clave) : ROW_HEIGHT, [altoDeDetalle]);\n // Las filas que se están borrando se marcan con una clase en lugar de copiar cada fila con un style:\n // copiar las filas en cada render anula la memorización de filas de react-data-grid\n const rowClass = useCallback((row: FilaDeDatos) =>\n esFilaDetalle(row) ? 'fpr-fila-detalle'\n : exitingRowIds.has(getPrimaryKeyValues(row, primaryKey)) ? 'fpr-fila-saliendo' : undefined\n , [exitingRowIds, primaryKey]);\n // Una grilla anidada es tan ancha como sus columnas (más la barra de scroll si la tiene), como las tablas de backend-plus;\n // sin esto el título y el pie quedaban estirados mucho más allá de los datos\n const anchoContenido = useMemo(() => {\n if (!anidada || columns.length === 0) return undefined;\n const sumaColumnas = columns.reduce((total, col) => {\n const medido = columnWidths.get(col.key as string)?.width;\n return total + (typeof medido === 'number' ? medido : typeof col.width === 'number' ? col.width : 100);\n }, 0);\n const propio = sumaColumnas + (medidas?.barra ?? 0) + 2;\n // si algún detalle abierto es más ancho que las columnas, esta grilla se ensancha para contenerlo\n const anchoDetalleMax = detallesAbiertos.reduce((max, clave) => Math.max(max, anchosDetalle.get(clave) ?? 0), 0);\n return Math.max(ANCHO_MINIMO_ANIDADA, propio, anchoDetalleMax > 0 ? anchoDetalleMax + SANGRIA_DETALLE + (medidas?.barra ?? 0) + 2 : 0);\n }, [anidada, columns, columnWidths, medidas?.barra, detallesAbiertos, anchosDetalle]);\n useEffect(() => {\n if (anchoContenido !== undefined) onAnchoDeseado?.(anchoContenido);\n }, [onAnchoDeseado, anchoContenido]);\n const estiloGrilla = useMemo<React.CSSProperties>(() => ({ height: '100%', width: '100%', boxSizing: 'border-box', ...gridStyles }), [gridStyles]);\n const bottomSummaryRows = useMemo(() => conPie ? [bottomSummaryRow] : [], [conPie, bottomSummaryRow]);\n const vistaGrilla = useMemo(() => ({ isFilterRowVisible }), [isFilterRowVisible]);\n\n const getFallBackMessage = () => {\n if (tableData.length === 0) {\n return \"No hay filas para mostrar\";\n }\n if (filteredRows.length === 0) {\n // Aquí ya sabemos que tableData.length > 0 implícitamente por el if anterior\n return \"No se encontraron resultados para tu búsqueda\";\n }\n return null;\n };\n\n const fallBackMessage = getFallBackMessage();\n\n if (cargandoInicial) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"200px\">\n <CircularProgress />\n <Typography variant=\"h6\" sx={{ ml: 2 }}>Cargando tabla...</Typography>\n </Box>\n );\n }\n\n if (errorDeCarga) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"200px\">\n <Alert severity=\"error\">{errorDeCarga}</Alert>\n </Box>\n );\n }\n\n if (!tableDefinition) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"200px\">\n <Alert severity=\"warning\">No se pudo cargar la definición de la tabla.</Alert>\n </Box>\n );\n }\n return (\n <CacheGrillasContext.Provider value={cache}>\n <Box sx={{ pt: anidada ? 0 : 2, display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box', width: anchoContenido ?? undefined, maxWidth: '100%' }}>\n {/* Primero los ancestros jerárquicos (en una grilla anidada el padre está justo arriba) */}\n {!anidada && ancestors.length > 0 && (\n <Box sx={{ px: 2, pt: 0, pb: 1, display: 'flex', flexDirection: 'column' }}>\n {ancestors.map((ancestor, index) => (\n <StaticAncestorGrid\n key={index}\n ancestor={ancestor}\n index={index}\n fieldsToHide={index > 0 ? ancestors[index - 1].fixedFields : []}\n />\n ))}\n </Box>\n )}\n\n {/* Luego el título de la grilla actual */}\n <Box\n sx={{\n backgroundColor: theme.palette.primary.main,\n color: theme.palette.primary.contrastText,\n px: 1.5,\n py: 0.5,\n textAlign: 'left',\n fontWeight: 'bold',\n borderBottom: `1px solid ${theme.palette.divider}`,\n borderTopLeftRadius: theme.shape.borderRadius,\n borderTopRightRadius: theme.shape.borderRadius,\n ml: anidada ? 0 : 2 + (ancestors.length * 4), // Margen dinámico basado en jerarquía\n mr: anidada ? 0 : 2,\n minHeight: anidada ? ALTO_TITULO_ANIDADA : undefined,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n gap: 1\n }}\n >\n <Typography variant=\"subtitle2\" component=\"div\">\n {titulo ?? cambiarGuionesBajosPorEspacios(tableDefinition.title || tableDefinition.name)} - {filteredRows.length === tableData.length ? `${tableData.length} registros`\n : `${filteredRows.length} (F)`\n }\n </Typography>\n {loading && <CircularProgress size={14} color=\"inherit\" />}\n </Box>\n\n <Box\n sx={{\n // Una grilla anidada se acota con px (el alto que le dio la de arriba, o el de una wScreen): una wScreen\n // tiene alto automático y ahí un porcentaje no se resuelve. La principal ocupa lo que quede de la pantalla.\n height: anidada ? `${altoDeEstaGrilla === undefined ? gridHeight : Math.min(gridHeight, altoDeEstaGrilla)}px` : `min(${gridHeight}px, 100%)`,\n maxHeight: '100%',\n boxSizing: 'border-box',\n position: 'relative', // CRÍTICO para el centrado del fallback\n overflowX: 'auto',\n overflowY: 'auto',\n px: anidada ? 0 : 2,\n ml: anidada ? 0 : ancestors.length * 4, // Indentación de la grilla principal\n pb: anidada ? 0 : 3,\n }}\n >\n <EstadoGrillaContext.Provider value={estadoGrilla}>\n <VistaGrillaContext.Provider value={vistaGrilla}>\n <DetallesGrillaContext.Provider value={detallesGrilla}>\n <DataGrid<FilaDeDatos, FilaDeResumen, string>\n className='rdg-light'\n ref={dataGridRef}\n columns={columns}\n rows={filasMostradas}\n rowClass={rowClass}\n columnWidths={columnWidths}\n onColumnWidthsChange={setColumnWidths}\n enableVirtualization={true}\n rowKeyGetter={rowKeyGetter}\n onSelectedRowsChange={setSelectedRows}\n onSelectedCellChange={handleSelectedCellChange}\n onRowsChange={handleRowsChange}\n sortColumns={sortColumns}\n onSortColumnsChange={handleSortColumnsChange}\n selectedRows={selectedRows}\n rowHeight={rowHeight}\n style={estiloGrilla}\n headerRowHeight={HEADER_ROW_HEIGHT}\n topSummaryRows={summaryRows}\n bottomSummaryRows={bottomSummaryRows}\n summaryRowHeight={SUMMARY_ROW_HEIGHT}\n onCellMouseDown={handleCellMouseDown}\n onCellDoubleClick={handleCellDoubleClick}\n onCellClick={handleCellClick}\n onCellKeyDown={handleCellKeyDown}\n />\n </DetallesGrillaContext.Provider>\n </VistaGrillaContext.Provider>\n </EstadoGrillaContext.Provider>\n\n {/* Fallbacks */}\n {fallBackMessage && (\n <Box\n sx={{\n position: 'absolute',\n top: filterHeight + HEADER_ROW_HEIGHT + 1,\n bottom: anidada ? (conPie ? SUMMARY_ROW_HEIGHT : 0) + 4 : 102,\n left: 18,\n right: 0,\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n backgroundColor: 'rgba(255, 255, 255, 0.8)',\n pointerEvents: 'none',\n zIndex: tableData.length === 0 ? 10 : 1, // Mantenemos el zIndex superior para el estado vacío total\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {fallBackMessage}\n </Typography>\n </Box>\n )}\n </Box>\n <ExportDialog\n open={openExportDialog}\n onClose={() => setOpenExportDialog(false)}\n tableDefinition={tableDefinition}\n tableData={tableData}\n filteredRows={filteredRows}\n username={usuario || 'anonymous'}\n />\n <ConfirmDialog\n open={openConfirmDialog}\n onClose={handleConfirmDelete}\n title=\"Confirmar Eliminación\"\n message={rowToDelete && rowToDelete[NEW_ROW_INDICATOR]\n ? `¿Estás seguro de que quieres eliminar esta nueva fila (ID: ${getPrimaryKeyValues(rowToDelete, primaryKey)}) localmente?`\n : rowToDelete\n ? `¿Estás seguro de que quieres eliminar la fila con ID: ${getPrimaryKeyValues(rowToDelete, primaryKey)} de la base de datos? Esta acción es irreversible.`\n : '¿Estás seguro de que quieres eliminar este registro? Esta acción es irreversible.'\n }\n />\n <DataGridOptionsDialog\n open={openDataGridOptions}\n onClose={() => setOpenDataGridOptions(false)}\n options={menuOptions}\n anchorEl={dataGridOptionsAnchorEl}\n />\n <ImportDialog\n open={openImportDialog}\n onClose={() => setOpenImportDialog(false)}\n onImport={handleImportFile}\n loading={loading}\n />\n\n\n {/* Modal de Editor Vertical */}\n <Dialog\n open={openVerticalEditDialog}\n onClose={() => setOpenVerticalEditDialog(false)}\n maxWidth=\"sm\"\n fullWidth\n >\n {rowToEditVertical && (\n <VerticalEditorPage\n tableName={tableName}\n tableDefinition={tableDefinition}\n initialData={rowToEditVertical}\n fixedFields={fixedFields}\n isNewRow={!!rowToEditVertical[NEW_ROW_INDICATOR]}\n onSaveSuccess={(savedRow, isNewItem) => {\n setRowToEditVertical(savedRow);\n setTableData(prevData => {\n const newData = [...prevData];\n const pK = isNewItem ? getPrimaryKeyValues(rowToEditVertical, primaryKey) : getPrimaryKeyValues(savedRow, primaryKey);\n const index = newData.findIndex(r => getPrimaryKeyValues(r, primaryKey) === pK);\n if (index !== -1) {\n newData[index] = savedRow;\n } else {\n newData.unshift(savedRow);\n }\n return newData;\n });\n }}\n onClose={() => {\n // Resaltar los campos que cambiaron respecto al estado original al abrir,\n // comparando con lo que devolvió el servidor (captura triggers también)\n const originalRow = rowAtEditStartRef.current;\n const serverRow = rowToEditVertical;\n if (originalRow && serverRow) {\n const rowId = getPrimaryKeyValues(serverRow, primaryKey);\n const changedFields = tableDefinition.fields\n .filter(f => !sameValue(originalRow[f.name], serverRow[f.name]))\n .map(f => f.name);\n if (changedFields.length > 0) {\n estadoGrilla.setFeedback(prev => {\n const next = new Map(prev);\n changedFields.forEach(fieldName => {\n next.set(`${rowId}-${fieldName}`, { rowId, columnKey: fieldName, type: 'success' });\n });\n return next;\n });\n }\n }\n rowAtEditStartRef.current = null;\n setOpenVerticalEditDialog(false);\n }}\n />\n )}\n </Dialog>\n {contenidoDeCelda && (\n <VentanaDeTexto anclaje={contenidoDeCelda.anclaje} texto={contenidoDeCelda.texto} editable={false} onAceptar={cerrarContenidoDeCelda} onCerrar={cerrarContenidoDeCelda} />\n )}\n </Box>\n </CacheGrillasContext.Provider>\n );\n\n};\nexport default GenericDataGrid;\n","import { useState, useCallback } from 'react';\r\nimport { useSnackbar } from '../contexts/SnackbarContext';\r\nimport { executeBackendProcedure as baseExecuteBackendProcedure, executeBackendUpload as baseExecuteBackendUpload } from '../utils/fetchApi';\r\nimport { UseApiCallResult, CallApiOptions, ParametrosDeProcedimiento } from '../types';\r\nimport { mensajeDeError } from '../utils/errores';\r\n\r\n/** Lo que se atrapó en un catch, como Error (puede venir cualquier cosa) */\r\nconst comoError = (err: unknown): Error => err instanceof Error ? err : new Error(mensajeDeError(err));\r\n\r\nexport const useApiCall = (): UseApiCallResult => {\r\n const { showSnackbar } = useSnackbar();\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const callApi = useCallback(async <R = unknown>(\r\n procedureName: string,\r\n params: ParametrosDeProcedimiento,\r\n opts: CallApiOptions = { isCritical: true }\r\n ): Promise<R | undefined> => {\r\n setLoading(true);\r\n if (opts.isCritical) setError(null);\r\n try {\r\n const result = await baseExecuteBackendProcedure<R>(procedureName, params);\r\n return result === null ? undefined : result;\r\n } catch (err) {\r\n if (opts.isCritical) setError(comoError(err));\r\n let snackbarMessage = `Error inesperado al ejecutar '${procedureName}'.`;\r\n if (err instanceof TypeError) {\r\n snackbarMessage = `Problema de conexión: ${err.message}. Por favor, revisa tu internet o intenta de nuevo.`;\r\n } else if (err instanceof Error) {\r\n snackbarMessage = err.message;\r\n }\r\n showSnackbar(snackbarMessage, 'error');\r\n throw err\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [showSnackbar]);\r\n\r\n const callApiUpload = useCallback(async <R = unknown>(\r\n procedureName: string,\r\n file: File,\r\n params: ParametrosDeProcedimiento,\r\n opts: CallApiOptions = { isCritical: true }\r\n ): Promise<R | undefined> => {\r\n setLoading(true);\r\n if (opts.isCritical) setError(null);\r\n try {\r\n const result = await baseExecuteBackendUpload<R>(procedureName, file, params);\r\n return result === null ? undefined : result;\r\n } catch (err) {\r\n if (opts.isCritical) setError(comoError(err));\r\n let snackbarMessage = `Error inesperado al subir archivo en '${procedureName}'.`;\r\n if (err instanceof TypeError) {\r\n snackbarMessage = `Problema de conexión: ${err.message}. Por favor, revisa tu internet o intenta de nuevo.`;\r\n } else if (err instanceof Error) {\r\n snackbarMessage = err.message;\r\n }\r\n showSnackbar(snackbarMessage, 'error');\r\n throw err\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [showSnackbar]);\r\n\r\n return { callApi, callApiUpload, loading, error };\r\n};\r\n\r\nexport const useApiCallWithoutSnackbar = (): UseApiCallResult => {\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const callApi = useCallback(async <R = unknown>(\r\n procedureName: string,\r\n params: ParametrosDeProcedimiento,\r\n _opts: CallApiOptions = { isCritical: true }\r\n ): Promise<R | undefined> => {\r\n setLoading(true);\r\n setError(null);\r\n \r\n try {\r\n const result = await baseExecuteBackendProcedure<R>(procedureName, params);\r\n return result === null ? undefined : result;\r\n } catch (err) {\r\n // Manejamos el error internamente (opcionalmente logueando) pero NO usamos el Snackbar.\r\n setError(comoError(err)); \r\n console.error(`Error en la llamada API (WithoutSnackbar) para ${procedureName}:`, err);\r\n throw err;\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n const callApiUpload = useCallback(async <R = unknown>(\r\n procedureName: string,\r\n file: File,\r\n params: ParametrosDeProcedimiento,\r\n _opts: CallApiOptions = { isCritical: true }\r\n ): Promise<R | undefined> => {\r\n setLoading(true);\r\n setError(null);\r\n \r\n try {\r\n const result = await baseExecuteBackendUpload<R>(procedureName, file, params);\r\n return result === null ? undefined : result;\r\n } catch (err) {\r\n setError(comoError(err)); \r\n console.error(`Error en la subida API (WithoutSnackbar) para ${procedureName}:`, err);\r\n throw err;\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n return { callApi, callApiUpload, loading, error };\r\n};","// src/contexts/SnackbarContext.tsx\r\nimport React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';\r\nimport MuiAlert, { AlertProps } from '@mui/material/Alert';\r\nimport Snackbar from '@mui/material/Snackbar';\r\nimport { SnackbarContextType, SnackbarProviderProps, SnackbarState } from '../types';\r\n\r\n// Definir el tipo de las props para el Alert en el Snackbar\r\nconst SnackbarAlert = React.forwardRef<HTMLDivElement, AlertProps>(function SnackbarAlert(props, ref) {\r\n return <MuiAlert elevation={6} ref={ref} variant=\"filled\" {...props} />;\r\n});\r\n\r\n// Crear el contexto\r\nconst SnackbarContext = createContext<SnackbarContextType | undefined>(undefined);\r\n\r\n// Valores por defecto para la duración (en milisegundos)\r\nconst DEFAULT_SUCCESS_DURATION = 3000; \r\nconst DEFAULT_ERROR_DURATION = 10000; \r\n\r\nexport const SnackbarProvider: React.FC<SnackbarProviderProps> = ({ children }) => {\r\n const [snackbar, setSnackbar] = useState<SnackbarState>({\r\n open: false,\r\n message: '',\r\n severity: 'info',\r\n duration: DEFAULT_SUCCESS_DURATION, // Valor inicial\r\n });\r\n\r\n const showSnackbar = useCallback((message: string, severity: 'success' | 'error' | 'info' | 'warning', duration?: number | null) => {\r\n // Determina la duración final\r\n const finalDuration = duration !== undefined && duration !== null\r\n ? duration\r\n : (severity === 'error' ? DEFAULT_ERROR_DURATION : DEFAULT_SUCCESS_DURATION);\r\n\r\n setSnackbar({\r\n open: true,\r\n message,\r\n severity,\r\n duration: finalDuration,\r\n });\r\n }, []);\r\n\r\n // Adapta los métodos de conveniencia para aceptar la duración opcional\r\n const showSuccess = useCallback((message: string, duration?: number | null) => showSnackbar(message, 'success', duration), [showSnackbar]);\r\n const showError = useCallback((message: string, duration?: number | null) => showSnackbar(message, 'error', duration), [showSnackbar]);\r\n const showWarning = useCallback((message: string, duration?: number | null) => showSnackbar(message, 'warning', duration), [showSnackbar]);\r\n const showInfo = useCallback((message: string, duration?: number | null) => showSnackbar(message, 'info', duration), [showSnackbar]);\r\n\r\n const handleCloseSnackbar = (event?: React.SyntheticEvent | Event, reason?: string) => {\r\n if (reason === 'clickaway') {\r\n return;\r\n }\r\n setSnackbar(prev => ({ ...prev, open: false }));\r\n };\r\n\r\n return (\r\n <SnackbarContext.Provider value={{ showSnackbar, showSuccess, showError, showWarning, showInfo }}>\r\n {children}\r\n <Snackbar\r\n open={snackbar.open}\r\n autoHideDuration={snackbar.duration} // Usa la duración del estado\r\n onClose={handleCloseSnackbar}\r\n anchorOrigin={{ vertical: 'top', horizontal: 'center' }}\r\n >\r\n <SnackbarAlert onClose={handleCloseSnackbar} severity={snackbar.severity}>\r\n {snackbar.message}\r\n </SnackbarAlert>\r\n </Snackbar>\r\n </SnackbarContext.Provider>\r\n );\r\n};\r\n\r\n// Hook personalizado para consumir el contexto\r\nexport const useSnackbar = () => {\r\n const context = useContext(SnackbarContext);\r\n if (context === undefined) {\r\n throw new Error('useSnackbar must be used within a SnackbarProvider');\r\n }\r\n return context;\r\n};","// src/utils/errores.ts\n\n/** El mensaje de lo que se atrapó en un catch (en TypeScript es `unknown`: puede no ser un Error) */\nexport function mensajeDeError(error: unknown, porDefecto: string = 'Error desconocido'): string {\n if (error instanceof Error) return error.message || porDefecto;\n if (typeof error === 'string' && error !== '') return error;\n return porDefecto;\n}\n\n/** Un valor que tiene que existir (por ejemplo, el resultado de un procedimiento): si no, se informa qué faltó */\nexport function requerido<T>(valor: T | null | undefined, descripcion: string): T {\n if (valor === null || valor === undefined) {\n throw new Error(`El servidor no devolvió ${descripcion}`);\n }\n return valor;\n}\n","// frontend-plus-react/src/config/env.ts\r\n\r\ninterface EnvVariables {\r\n backendUrl: string;\r\n // Agrega aquí cualquier otra variable de entorno que tu librería necesite\r\n}\r\n\r\n// Función auxiliar para obtener la variable de entorno de forma agnóstica al bundler\r\nconst getEnvVar = (key: string): string => {\r\n // Intenta leer de import.meta.env (Vite)\r\n // import.meta.env lo define Vite; la librería no depende de los tipos de vite/client\r\n const viteEnv = (import.meta as ImportMeta & { env?: Record<string, string | undefined> }).env;\r\n if (viteEnv) {\r\n const viteKey = `VITE_${key}`;\r\n const valor = viteEnv[viteKey];\r\n if (valor) {\r\n return valor;\r\n }\r\n }\r\n\r\n // Intenta leer de process.env (CRA, Node.js)\r\n if (typeof process !== 'undefined' && process.env) {\r\n const craKey = `REACT_APP_${key}`;\r\n if (process.env[craKey]) {\r\n return process.env[craKey]!; // '!' para asegurar que no es undefined después de la verificación\r\n }\r\n // También intenta sin prefijo para otros entornos Node.js (menos común para frontends)\r\n if (process.env[key]) {\r\n return process.env[key]!;\r\n }\r\n }\r\n\r\n // Si no se encuentra, puedes lanzar un error o devolver un valor por defecto\r\n console.warn(`Variable de entorno \"${key}\" no encontrada. Asegúrate de definirla con el prefijo correcto (VITE_ o REACT_APP_).`);\r\n return ''; // O lanza un error: throw new Error(`Variable de entorno ${key} no definida.`);\r\n};\r\n\r\n// Exporta un objeto con todas las variables de entorno que tu librería necesita\r\nexport const envConfig: EnvVariables = {\r\n backendUrl: getEnvVar('BACKEND_URL'),\r\n // Agrega aquí las demás variables siguiendo el mismo patrón\r\n};","import { FetchApiOptions, ParametrosDeProcedimiento } from \"../types\";\r\nimport { mensajeDeError } from \"./errores\";\r\nimport { envConfig } from '../env'; // Ajusta la ruta según donde lo hayas guardado\r\nimport JSON4all from 'json4all';\r\nimport { ProcedureDef } from 'backend-plus';\r\n\r\ntype DefinicionesDeProcedimientos = Record<string, ProcedureDef>;\r\n\r\n/**\r\n * Fuente de las definiciones de procedimientos que manda el servidor en client-setup.\r\n * La registra el store para que este módulo no lo importe (evita la dependencia circular).\r\n */\r\nlet obtenerDefinicionesDeProcedimientos: () => DefinicionesDeProcedimientos | undefined = () => undefined;\r\n\r\nexport function registrarFuenteDeProcedimientos(fuente: () => DefinicionesDeProcedimientos | undefined) {\r\n obtenerDefinicionesDeProcedimientos = fuente;\r\n}\r\n\r\n/** Codifica un valor igual que lo hace my-ajax.js (myAjax.encoders), que es como lo decodifica el servidor */\r\nfunction codificarParametro(valor: unknown, encoding: string | undefined, procedureName: string, paramName: string): string {\r\n switch (encoding ?? 'JSON4all') {\r\n case 'plain':\r\n if (typeof valor === 'object' && valor !== null) {\r\n throw new Error(`El parámetro '${paramName}' de '${procedureName}' es plain y no admite objetos`);\r\n }\r\n return String(valor);\r\n case 'JSON':\r\n return JSON.stringify(valor);\r\n case 'JSON4all':\r\n return JSON4all.stringify(valor);\r\n default:\r\n throw new Error(`Encoding '${encoding}' no soportado para el parámetro '${paramName}' de '${procedureName}'`);\r\n }\r\n}\r\n\r\n/**\r\n * Arma los parámetros codificados según la definición del procedimiento.\r\n * Si el procedimiento no se conoce todavía (por ejemplo antes de cargar client-setup)\r\n * se usa la codificación anterior: primitivos tal cual y objetos con JSON4all.\r\n */\r\nexport function codificarParametros(procedureName: string, params: Record<string, unknown>): { codificados: Record<string, string>, method: 'get' | 'post' } {\r\n const definicion = obtenerDefinicionesDeProcedimientos()?.[procedureName];\r\n const codificados: Record<string, string> = {};\r\n if (!definicion) {\r\n for (const [nombre, valor] of Object.entries(params)) {\r\n if (valor === undefined) continue;\r\n codificados[nombre] = typeof valor === 'object' && valor !== null ? JSON4all.stringify(valor) : String(valor);\r\n }\r\n return { codificados, method: 'post' };\r\n }\r\n const declarados = new Set(definicion.parameters.map(p => p.name));\r\n for (const nombre of Object.keys(params)) {\r\n if (!declarados.has(nombre)) {\r\n console.warn(`El parámetro '${nombre}' no está declarado en el procedimiento '${procedureName}' y no se envía`);\r\n }\r\n }\r\n for (const paramDef of definicion.parameters) {\r\n const valor = params[paramDef.name];\r\n // si no viene, el servidor usa el defaultValue (o informa que falta)\r\n if (valor === undefined) continue;\r\n codificados[paramDef.name] = codificarParametro(valor, paramDef.encoding, procedureName, paramDef.name);\r\n }\r\n return { codificados, method: definicion.method ?? 'post' };\r\n}\r\n\r\n\r\n/**\r\n * Función genérica para realizar peticiones a la API.\r\n * Configurada para enviar cookies por defecto.\r\n *\r\n * @param endPoint La URL a la que se realizará la petición.\r\n * @param options Opciones de la petición fetch (método, headers, body, etc.).\r\n * @returns La respuesta de la petición.\r\n */\r\nexport async function fetchApi(endPoint: string, options: FetchApiOptions): Promise<Response> {\r\n const BACKEND_URL = envConfig.backendUrl;\r\n // sinContentType: el navegador arma el Content-Type solo (FormData lleva su boundary)\r\n const { sinContentType, ...opcionesDeFetch } = options;\r\n const headers = new Headers(opcionesDeFetch.headers);\r\n if (!sinContentType && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/x-www-form-urlencoded');\r\n }\r\n const config: RequestInit = {\r\n ...opcionesDeFetch,\r\n headers,\r\n credentials: 'include',\r\n };\r\n if (config.method === 'GET' || config.method === 'HEAD') {\r\n delete config.body;\r\n }\r\n return await fetch(BACKEND_URL + endPoint, config);\r\n}\r\n\r\n/** El servidor informa un error de un procedimiento con { error: { message } } */\r\nfunction esRespuestaConError(resultado: unknown): resultado is { error: { message: string } } {\r\n if (typeof resultado !== 'object' || resultado === null || !('error' in resultado)) return false;\r\n const { error } = resultado;\r\n return typeof error === 'object' && error !== null && 'message' in error;\r\n}\r\n\r\n/**\r\n * Interpreta la respuesta de un procedimiento y la devuelve con la forma que declara quien llama (T).\r\n * Es el único lugar donde se afirma esa forma: el servidor manda JSON4all y su estructura la define cada procedimiento.\r\n */\r\nfunction resultadoDeProcedimiento<T>(texto: string): T {\r\n const resultado: unknown = parseBackendResponse(texto);\r\n if (esRespuestaConError(resultado)) {\r\n throw new Error(resultado.error.message);\r\n }\r\n return resultado as T;\r\n}\r\n\r\nasync function ejecutar<T>(descripcion: string, procedureName: string, pedido: Promise<Response>): Promise<T | null> {\r\n const response = await pedido;\r\n if (response.ok) {\r\n return resultadoDeProcedimiento<T>(await response.text());\r\n }\r\n const errorTextDefinition = await response.text();\r\n throw new Error(`Error al ejecutar ${descripcion} '${procedureName}': ${response.status} - ${errorTextDefinition}`);\r\n}\r\n\r\nexport const executeBackendProcedure = async <T = unknown>(procedureName: string, params: ParametrosDeProcedimiento): Promise<T | null> => {\r\n const { codificados, method } = codificarParametros(procedureName, params);\r\n const query = new URLSearchParams(codificados);\r\n return ejecutar<T>('procedure', procedureName, method === 'get'\r\n ? fetchApi(`/${procedureName}?${query}`, { method: 'GET' })\r\n : fetchApi(`/${procedureName}`, { body: query, method: 'POST' }));\r\n}\r\n\r\nfunction parseBackendResponse(text: string): unknown {\r\n const trimmedText = text.trim();\r\n\r\n // El backend puede enviar múltiples líneas de progreso (JSONs por línea) \r\n // seguidas de un separador '--' y finalmente el resultado JSON.\r\n // Ejemplo:\r\n // {\"progress\":{}}\r\n // --\r\n // {\"uploaded\":{...}}\r\n\r\n // Buscamos el separador '--' que esté en una línea sola o después de un salto de línea\r\n const separatorMatch = trimmedText.match(/\\n--\\n/);\r\n let finalPart = trimmedText;\r\n\r\n if (separatorMatch) {\r\n // Si encontramos el separador, tomamos todo lo que está después del último separador\r\n const parts = trimmedText.split(/\\n--\\n/);\r\n finalPart = parts[parts.length - 1];\r\n } else {\r\n // Fallback: buscar '--' al principio o después de un salto de línea simple\r\n const parts = trimmedText.split(/\\n--/);\r\n if (parts.length > 1) {\r\n finalPart = parts[parts.length - 1];\r\n } else if (trimmedText.startsWith('--')) {\r\n finalPart = trimmedText.replace(/^--\\s*/, '');\r\n }\r\n }\r\n\r\n try {\r\n return JSON4all.parse(finalPart.trim());\r\n } catch (e) {\r\n // Si falla el parseo de la parte final, intentamos parsear la última línea que sea un JSON válido\r\n // Esto es un último recurso si el formato del separador varía\r\n const lines = trimmedText.split('\\n');\r\n for (let i = lines.length - 1; i >= 0; i--) {\r\n const line = lines[i].trim();\r\n if (line && line.startsWith('{') && line.endsWith('}')) {\r\n try {\r\n return JSON4all.parse(line);\r\n } catch (innerE) {\r\n continue;\r\n }\r\n }\r\n }\r\n throw new Error(`No se pudo parsear la respuesta del servidor: ${mensajeDeError(e)}`);\r\n }\r\n}\r\n\r\nexport const executeBackendUpload = async <T = unknown>(procedureName: string, file: File, params: ParametrosDeProcedimiento): Promise<T | null> => {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n // Añadimos el resto de parámetros al formData (los multipart no admiten encoding plain)\r\n const { codificados } = codificarParametros(procedureName, params);\r\n for (const [key, value] of Object.entries(codificados)) {\r\n formData.append(key, value);\r\n }\r\n\r\n return ejecutar<T>('upload', procedureName, fetchApi(`/${procedureName}`, { body: formData, method: 'POST', sinContentType: true }));\r\n}\r\n","// src/store/index.ts\r\n// ... tus imports existentes (incluyendo routerReducer) ...\r\nimport { configureStore, combineReducers } from '@reduxjs/toolkit';\r\nimport {\r\n persistStore,\r\n persistReducer,\r\n FLUSH,\r\n REHYDRATE,\r\n PAUSE,\r\n PERSIST,\r\n PURGE,\r\n REGISTER,\r\n} from 'redux-persist';\r\nimport storage from 'redux-persist/lib/storage';\r\n\r\nimport clientContextReducer from './clientContextSlice';\r\nimport routerReducer from './routerSlice';\r\nimport menuUiReducer from './menuUiSlice';\r\nimport { registrarFuenteDeProcedimientos } from '../utils/fetchApi';\r\n\r\nimport { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';\r\nimport { ClientContextState, MenuUiState } from '../types';\r\n\r\nconst rootReducer = combineReducers({\r\n clientContext: clientContextReducer,\r\n router: routerReducer,\r\n menuUi: menuUiReducer,\r\n // ...otros slices\r\n});\r\n\r\nconst persistConfig = {\r\n key: 'root',\r\n storage,\r\n whitelist: ['clientContext', 'router', 'menuUi'],\r\n};\r\n\r\nconst persistedReducer = persistReducer(persistConfig, rootReducer);\r\n\r\nexport const store = configureStore({\r\n reducer: persistedReducer,\r\n middleware: (getDefaultMiddleware) =>\r\n getDefaultMiddleware({\r\n serializableCheck: {\r\n ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],\r\n },\r\n }),\r\n});\r\n\r\nexport const persistor = persistStore(store);\r\n\r\n// fetchApi codifica los parámetros según las definiciones que vinieron en client-setup\r\nregistrarFuenteDeProcedimientos(() => {\r\n const { procedure, procedures } = store.getState().clientContext;\r\n if (procedure && Object.keys(procedure).length > 0) return procedure;\r\n return procedures?.length ? Object.fromEntries(procedures.map(p => [p.action, p])) : undefined;\r\n});\r\n\r\nexport type RootState = ReturnType<typeof store.getState>;\r\nexport type AppDispatch = typeof store.dispatch;\r\n\r\nexport const useAppDispatch: () => AppDispatch = useDispatch;\r\nexport const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;\r\n\r\n// Hooks selectores personalizados existentes\r\nexport const useClientContext = (): ClientContextState => {\r\n return useAppSelector(state => state.clientContext);\r\n};\r\n\r\nexport const useClientContextStatus = (): ClientContextState['status'] => {\r\n return useAppSelector(state => state.clientContext.status);\r\n};\r\n\r\nexport const useClientContextError = (): ClientContextState['error'] => {\r\n return useAppSelector(state => state.clientContext.error);\r\n};\r\n\r\nexport const useCurrentPath = (): string => {\r\n return useAppSelector(state => state.router.currentPath);\r\n};\r\n\r\nexport const usePreviousPath = (): string | null => {\r\n return useAppSelector(state => state.router.previousPath);\r\n};\r\n\r\n// Selectores para menuUi\r\nexport const useMenuUi = (): MenuUiState => {\r\n return useAppSelector(state => state.menuUi);\r\n};\r\n\r\nexport const useIsDrawerOpen = (): boolean => {\r\n return useAppSelector(state => state.menuUi.isDrawerOpen);\r\n};\r\n\r\nexport const useSubMenuOpenState = (menuName: string): boolean => {\r\n return useAppSelector(state => state.menuUi.subMenuOpenStates[menuName] || false);\r\n};\r\n\r\nexport const useRedirectPath = (): string | null => {\r\n return useAppSelector(state => state.router.redirectPath);\r\n};","// src/store/clientContextSlice.ts\r\nimport { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';\r\nimport { MenuInfoBase, ProcedureDef, AppConfigClientSetup } from \"backend-plus\";\r\nimport { Details } from 'express-useragent'; // Asumiendo que 'Details' viene de aquí\r\nimport { fetchApi } from '../utils/fetchApi'; // Tu utilidad fetchApi\r\nimport { ClientContextState } from '../types';\r\nimport { mensajeDeError } from '../utils/errores';\r\n\r\n// Estado inicial para el slice de clientContext\r\nconst initialState: ClientContextState = {\r\n menu: [],\r\n procedure: {},\r\n procedures: [],\r\n config: {} as AppConfigClientSetup, // Inicializa con un objeto vacío o valores por defecto adecuados\r\n useragent: {} as Details, // Igual aquí\r\n username: '',\r\n status: 'idle', // Por defecto, sin carga\r\n error: null, // Sin error\r\n};\r\n\r\n// **Thunk asíncrono para cargar los datos del clientContext**\r\n// Esto reemplaza tu función 'loadClientContextData'\r\nexport const fetchClientContext = createAsyncThunk(\r\n 'clientContext/fetchClientContext',\r\n async (_, { rejectWithValue }) => {\r\n try {\r\n const setupResponse = await fetchApi('/client-setup', { method: 'GET' });\r\n if (!setupResponse.ok) {\r\n const errorData = await setupResponse.text();\r\n throw new Error(errorData || 'Error al obtener configuración del cliente');\r\n }\r\n const clientSetup = await setupResponse.json();\r\n\r\n // Retorna los datos cargados. El 'status' y 'error' se manejan automáticamente\r\n // en los 'extraReducers'.\r\n return clientSetup as Omit<ClientContextState, 'status' | 'error'>;\r\n } catch (error) {\r\n // Usa rejectWithValue para pasar el mensaje de error al estado 'failed'\r\n return rejectWithValue(mensajeDeError(error, 'Error desconocido al cargar el contexto'));\r\n }\r\n }\r\n);\r\n\r\n// Creación del slice de Redux para clientContext\r\nconst clientContextSlice = createSlice({\r\n name: 'clientContext',\r\n initialState,\r\n reducers: {\r\n // Un reducer para limpiar el contexto del cliente (ej. al cerrar sesión)\r\n clearClientContext: (state) => {\r\n // Restablece el estado a su valor inicial\r\n Object.assign(state, initialState);\r\n },\r\n // Puedes añadir otros reducers aquí si necesitas modificar partes del clientContext directamente\r\n // pero el thunk es la forma preferida para la carga inicial.\r\n },\r\n // **extraReducers maneja las acciones del thunk asíncrono**\r\n extraReducers: (builder) => {\r\n builder\r\n // Cuando la petición está pendiente (cargando)\r\n .addCase(fetchClientContext.pending, (state) => {\r\n state.status = 'loading';\r\n state.error = null;\r\n })\r\n // Cuando la petición se completa con éxito\r\n .addCase(fetchClientContext.fulfilled, (state, action: PayloadAction<Omit<ClientContextState, 'status' | 'error'>>) => {\r\n state.status = 'succeeded';\r\n // Asigna los datos recibidos al estado\r\n state.menu = action.payload.menu;\r\n state.procedure = action.payload.procedure;\r\n state.procedures = action.payload.procedures;\r\n state.config = action.payload.config;\r\n state.useragent = action.payload.useragent;\r\n state.username = action.payload.username;\r\n state.error = null;\r\n })\r\n // Cuando la petición falla\r\n .addCase(fetchClientContext.rejected, (state, action) => {\r\n state.status = 'failed';\r\n state.error = action.payload as string || 'Fallo al cargar el contexto del cliente';\r\n // Puedes optar por limpiar el resto del estado o mantener el último valor si el error no es crítico\r\n // Object.assign(state, initialState); // Si quieres limpiar todo en caso de error\r\n });\r\n },\r\n});\r\n\r\nexport const { clearClientContext } = clientContextSlice.actions; // Exporta la acción generada\r\nexport default clientContextSlice.reducer; // Exporta el reducer","// src/store/routerSlice.ts\r\nimport { createSlice, PayloadAction } from '@reduxjs/toolkit';\r\nimport { RouterState } from '../types';\r\n\r\nconst initialState: RouterState = {\r\n currentPath: '/',\r\n previousPath: null,\r\n redirectPath: null, // <--- Inicializamos en null\r\n};\r\n\r\nconst routerSlice = createSlice({\r\n name: 'router',\r\n initialState,\r\n reducers: {\r\n setCurrentPath: (state, action: PayloadAction<string>) => {\r\n state.previousPath = state.currentPath;\r\n state.currentPath = action.payload;\r\n },\r\n setRedirectPath: (state, action: PayloadAction<string | null>) => { // <--- Nuevo reducer\r\n state.redirectPath = action.payload;\r\n },\r\n resetRouterState: (state) => {\r\n state.currentPath = '/';\r\n state.previousPath = null;\r\n state.redirectPath = null; // <--- Asegúrate de resetearlo también\r\n },\r\n },\r\n});\r\n\r\nexport const { setCurrentPath, setRedirectPath, resetRouterState } = routerSlice.actions; // <--- Exporta el nuevo action\r\nexport default routerSlice.reducer;","// src/store/menuUiSlice.ts\r\nimport { createSlice, PayloadAction } from '@reduxjs/toolkit';\r\nimport { MenuUiState } from '../types';\r\n\r\nconst initialState: MenuUiState = {\r\n isDrawerOpen: false,\r\n subMenuOpenStates: {},\r\n};\r\n\r\nconst menuUiSlice = createSlice({\r\n name: 'menuUi',\r\n initialState,\r\n reducers: {\r\n setDrawerOpen: (state, action: PayloadAction<boolean>) => {\r\n state.isDrawerOpen = action.payload;\r\n },\r\n toggleDrawer: (state) => {\r\n state.isDrawerOpen = !state.isDrawerOpen;\r\n },\r\n setSubMenuOpen: (state, action: PayloadAction<{ menuName: string; isOpen: boolean }>) => {\r\n state.subMenuOpenStates[action.payload.menuName] = action.payload.isOpen;\r\n },\r\n toggleSubMenu: (state, action: PayloadAction<string>) => {\r\n state.subMenuOpenStates[action.payload] = !state.subMenuOpenStates[action.payload];\r\n },\r\n // --- NUEVO REDUCER: Establecer el estado de todos los submenús ---\r\n setAllSubMenusOpen: (state, action: PayloadAction<boolean>) => {\r\n // Itera sobre todas las claves existentes y las establece al valor dado\r\n for (const menuName in state.subMenuOpenStates) {\r\n if (Object.prototype.hasOwnProperty.call(state.subMenuOpenStates, menuName)) {\r\n state.subMenuOpenStates[menuName] = action.payload;\r\n }\r\n }\r\n // Importante: Si hay nuevos submenús que no están en subMenuOpenStates,\r\n // no se verán afectados por este bucle.\r\n // Una estrategia más robusta para casos donde el menú puede cambiar dinámicamente\r\n // sería obtener todos los nombres de menú recursivamente desde clientContext.menu\r\n // y actualizar sus estados. Por ahora, asumimos que los que ya están en el estado\r\n // son los que queremos controlar.\r\n },\r\n resetMenuUiState: (state) => {\r\n Object.assign(state, initialState);\r\n },\r\n },\r\n});\r\n\r\nexport const {\r\n setDrawerOpen,\r\n toggleDrawer,\r\n setSubMenuOpen,\r\n toggleSubMenu,\r\n setAllSubMenusOpen, // ¡Exportamos la nueva acción!\r\n resetMenuUiState,\r\n} = menuUiSlice.actions;\r\n\r\nexport default menuUiSlice.reducer;","import Dialog from \"@mui/material/Dialog\";\nimport { ConfirmDialogProps } from \"../types\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogContentText from \"@mui/material/DialogContentText\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport Button from \"@mui/material/Button\";\n\nexport const ConfirmDialog: React.FC<ConfirmDialogProps> = ({ open, onClose, title, message }) => {\n return (\n <Dialog\n open={open}\n onClose={() => onClose(false)}\n aria-labelledby=\"confirm-dialog-title\"\n aria-describedby=\"confirm-dialog-description\"\n >\n <DialogTitle id=\"confirm-dialog-title\">{title}</DialogTitle>\n <DialogContent>\n <DialogContentText id=\"confirm-dialog-description\">\n {message}\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => onClose(false)} color=\"primary\">\n Cancelar\n </Button>\n <Button onClick={() => onClose(true)} color=\"error\" autoFocus>\n Confirmar\n </Button>\n </DialogActions>\n </Dialog>\n );\n};","import {\n Popover,\n List,\n ListItemButton,\n ListItemIcon,\n ListItemText,\n Paper,\n CircularProgress,\n Box\n} from '@mui/material';\nimport { useState } from 'react';\nimport { DataGridOption } from '../../types';\n\ninterface DataGridOptionsDialogProps {\n open: boolean;\n onClose: () => void;\n options: DataGridOption[];\n anchorEl: HTMLElement | null;\n}\n\nexport const DataGridOptionsDialog: React.FC<DataGridOptionsDialogProps> = ({ \n open, \n onClose, \n options,\n anchorEl\n}) => {\n const [isExecuting, setIsExecuting] = useState(false);\n\n const handleOptionClick = async (handler: () => Promise<void>) => {\n try {\n setIsExecuting(true);\n await handler();\n } catch (error) {\n console.error('Error ejecutando handler:', error);\n } finally {\n setIsExecuting(false);\n onClose();\n }\n };\n\n return (\n <>\n {open && (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.2)',\n zIndex: 1200,\n pointerEvents: 'none'\n }}\n />\n )}\n <Popover\n open={open}\n anchorEl={anchorEl}\n onClose={onClose}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left',\n }}\n transformOrigin={{\n vertical: 'top',\n horizontal: 'left',\n }}\n slotProps={{\n paper: {\n sx: {\n zIndex: 1300,\n }\n }\n }}\n >\n <Paper sx={{ minWidth: 150, maxWidth: 350 }}>\n {isExecuting && (\n <Box \n sx={{ \n display: 'flex', \n justifyContent: 'center', \n alignItems: 'center',\n p: 5\n }}\n >\n <CircularProgress size={40} />\n </Box>\n )}\n {!isExecuting && (\n <List sx={{ \n py: 0,\n '& .MuiListItemButton-root': { \n py: 0.5, \n px: 1.5,\n minHeight: 36\n },\n '& .MuiListItemIcon-root': { \n minWidth: 32 \n },\n '& .MuiListItemText-primary': { \n fontSize: '0.85rem' \n },\n '& .MuiSvgIcon-root': {\n fontSize: 18\n }\n }}>\n {options\n .filter(option => option.visible !== false)\n .map((option) => (\n <ListItemButton \n key={option.id}\n onClick={() => handleOptionClick(option.handler)}\n >\n <ListItemIcon>\n {option.icon}\n </ListItemIcon>\n <ListItemText primary={option.label} />\n </ListItemButton>\n ))\n }\n </List>\n )}\n </Paper>\n </Popover>\n </>\n );\n};","// src/components/grid/utils/helpers.ts\nimport typeStore, { Typer } from 'type-store';\nimport { DOM_TEMP_ID, FieldDefinition, FilaDeDatos } from '../../../types';\n\nexport type { Typer };\n\nexport const typerFrom = (fieldDef: FieldDefinition): Typer => typeStore.typerFrom(fieldDef);\n\n/**\n * Indicadores especiales para filas\n */\nexport const NEW_ROW_INDICATOR = '$new';\nexport { DOM_TEMP_ID };\n\n/**\n * Obtiene los valores de la clave primaria de una fila concatenados en un string único\n * @param row - Fila de datos\n * @param primaryKey - Array con los nombres de las columnas que forman la PK\n * @returns String único que identifica la fila\n */\nexport const getPrimaryKeyValues = (row: FilaDeDatos, primaryKey: string[]): string => {\n // Si la fila tiene un ID de DOM explícito (Symbol), lo usamos\n if (row[DOM_TEMP_ID]) return String(row[DOM_TEMP_ID]);\n\n return primaryKey.map(key => {\n return row[key] !== undefined && row[key] !== null\n ? String(row[key])\n : 'NULL_OR_UNDEFINED';\n }).join('|');\n};\n\n/**\n * Compara dos valores de forma robusta, manejando Fechas y nulos.\n * Basado en sameValue de best-globals/backend-plus.\n */\nexport const sameValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();\n \n // Tratamos null y undefined como equivalentes para el propósito de \"valor vacío\" \n // en celdas de grilla si uno de ellos está presente y el otro no.\n const isAEmpty = a === null || a === undefined;\n const isBEmpty = b === null || b === undefined;\n if (isAEmpty && isBEmpty) return true;\n\n return false;\n};\n\n\n/**\n * Obtiene la clave única para una celda.\n * @param row La fila actual.\n * @param columnKey La clave de la columna.\n * @param primaryKey La clave primaria de la tabla.\n * @returns Clave única de la celda (ej: \"pkValue1|pkValue2|columnKey\").\n */\nexport const getCellKey = (row: FilaDeDatos, columnKey: string, primaryKey: string[]): string => {\n return `${getPrimaryKeyValues(row, primaryKey)}|${columnKey}`;\n};\n\n/**\n * Checks if a given database field type name corresponds to a numeric field.\n * @param typeName - The string representing the database field type\n * @returns boolean\n */\nexport const isNumericType = (typeName?: string): boolean => {\n return ['integer', 'bigint', 'decimal', 'double', 'float', 'number'].includes(typeName || '');\n};\n\n/**\n * Checks if a given database field type name corresponds to a date or time field.\n * @param typeName - The string representing the database field type\n * @returns boolean\n */\nexport const isDateTimeType = (typeName?: string): boolean => {\n const t = typeName?.toLowerCase() || '';\n return ['date', 'time', 'timestamp', 'interval', 'tsrange', 'time_range', 'daterange'].some(type => t.includes(type));\n};\n\n/**\n * Compara dos valores del mismo tipo (números, fechas, textos, bigint). Un número contra un texto numérico se compara\n * como número (el valor de un filtro que no se pudo tipar sigue siendo un texto). null si no son comparables.\n */\nexport const compararValores = (a: unknown, b: unknown): number | null => {\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : a > b ? 1 : 0;\n if (typeof a === 'bigint' && typeof b === 'bigint') return a < b ? -1 : a > b ? 1 : 0;\n const esNumerico = (v: unknown): v is string => typeof v === 'string' && v.trim() !== '' && !Number.isNaN(Number(v));\n if (typeof a === 'number' && esNumerico(b)) return a - Number(b);\n if (esNumerico(a) && typeof b === 'number') return Number(a) - b;\n return null;\n};\n\nexport const filterOperators = [\n { value: '=', label: '=' },\n { value: '~', label: '~' },\n { value: '!~', label: '!~' },\n { value: '\\u2205', label: '∅' }, // vacío\n { value: '!=\\u2205', label: '!=∅' }, // no vacío\n { value: '>', label: '>' },\n { value: '>=', label: '>=' },\n { value: '<', label: '<' },\n { value: '<=', label: '<=' },\n { value: '!=', label: '!=' }\n];\n\nexport const getDefaultOperatorForType = (typeName?: string): string => {\n const isText = ['text', 'varchar', 'char'].some(t => typeName?.toLowerCase().includes(t)) \n || (!isNumericType(typeName) && !isDateTimeType(typeName) && typeName?.toLowerCase() !== 'boolean');\n return isText ? '~' : '=';\n};","// frontend-plus-react/src/utils/routeUtils.ts (o dentro de App.tsx)\r\nimport React, { Children, isValidElement, ReactNode } from 'react';\r\n\r\n/**\r\n * Recorre recursivamente los hijos de React buscando componentes que tengan una prop 'path'.\r\n * Esto permite pasar <Route path=\"...\"> o Fragmentos <><Route.../></> y extraer los strings.\r\n */\r\nexport const extractPathsFromRoutes = (node: ReactNode): string[] => {\r\n const paths: string[] = [];\r\n\r\n Children.forEach(node, (child) => {\r\n if (!isValidElement(child)) return;\r\n\r\n // Caso 1: Es un <Route path=\"/algo\" ... /> directo\r\n if (child.props.path) {\r\n paths.push(child.props.path);\r\n }\r\n\r\n // Caso 2: Es un Fragmento <>...</> o un componente wrapper que tiene children\r\n // (Tus rutas unlogged vienen dentro de un Fragment <>)\r\n if (child.props.children) {\r\n paths.push(...extractPathsFromRoutes(child.props.children));\r\n }\r\n });\r\n\r\n return paths;\r\n};","import { AppConfigClientSetup, DetailTable, MenuInfoBase, ProcedureDef, TableDefinition, FieldDefinition } from \"backend-plus\";\r\nimport { Details } from \"express-useragent\";\r\nimport { ReactNode, FC, KeyboardEvent, Dispatch, SetStateAction } from \"react\";\r\nimport { Column, RenderCellProps, RenderHeaderCellProps } from \"react-data-grid\";\r\n\r\nexport type { FieldDefinition, TableDefinition };\r\n\r\n/**\r\n * Marca con la que una fila nueva conserva su identidad en la grilla mientras se edita (antes de tener su clave primaria).\r\n * Es un Symbol: no aparece en Object.keys ni en JSON.stringify, pero el spread de la fila (...fila) la conserva.\r\n */\r\nexport const DOM_TEMP_ID: unique symbol = Symbol('domTempId');\r\n\r\n/** Una fila de datos de una tabla: los valores salen del servidor ya decodificados (JSON4all) */\r\nexport type FilaDeDatos = Record<string, unknown> & { [DOM_TEMP_ID]?: string };\r\n\r\nexport interface WScreenProps {\r\n screenName: string;\r\n /**\r\n * Parámetros de la pantalla como objeto plano (en backend-plus, el primer argumento de `mainAction`).\r\n * Como detalle de una grilla son los fixedFields del detalle; en una ruta, la query string.\r\n */\r\n params?: Record<string, unknown>;\r\n}\r\n\r\n/** Decide, para una fila, si se muestra el botón de un detalle (`condition` del DetailTable) */\r\nexport type CondicionDeDetalle = (fila: FilaDeDatos) => boolean;\r\nexport type CondicionesMap = Record<string, CondicionDeDetalle>;\r\n\r\nexport interface ClientSideProps extends RenderCellProps<FilaDeDatos, FilaDeResumen> {\r\n fieldDefinition: FieldDefinition;\r\n tableDefinition: TableDefinition;\r\n primaryKey: string[];\r\n}\r\n\r\nexport interface ResultOkProps {\r\n /** Lo que devolvió el procedimiento (la forma depende de cada procedimiento) */\r\n data: unknown;\r\n}\r\n\r\nexport type WScreenMap = Record<string, FC<WScreenProps>>;\r\nexport type ClientSidesMap = Record<string, FC<ClientSideProps>>;\r\nexport type ResultsOksMap = Record<string, FC<ResultOkProps>>;\r\n\r\nexport interface AppProps {\r\n myRoutes?: ReactNode;\r\n myUnloggedRoutes?: ReactNode;\r\n unloggedLayout?: React.ComponentType;\r\n myWScreens?: WScreenMap;\r\n myClientSides?: ClientSidesMap;\r\n myResultsOk?: ResultsOksMap;\r\n /** Condiciones de los detalles, por nombre (la `condition` de un DetailTable) */\r\n myConditions?: CondicionesMap;\r\n}\r\n\r\nexport {extractPathsFromRoutes} from \"../utils/routeUtils\";\r\n\r\n// --- MODIFICACIONES DE FEEDBACK ---\r\n\r\n\r\nexport interface CellFeedback {\r\n rowId: string;\r\n columnKey: string;\r\n type: 'success' | 'error';\r\n message?: string; // Corregido el tipo de 'message' a 'string'\r\n}\r\n\r\n// Tipo para el mapa de feedback, usando una clave string (rowId-columnKey)\r\nexport type CellFeedbackMap = Map<string, CellFeedback>; \r\n// Nota: También podrías usar Record<string, CellFeedback> si prefieres objetos.\r\n\r\n/** Lo que el manejo de teclas del editor necesita saber de la tecla (sirve para un evento real o uno encolado) */\r\nexport type TeclaDeEditor = Pick<React.KeyboardEvent, 'key' | 'target'> & Partial<Pick<React.KeyboardEvent, 'shiftKey' | 'ctrlKey' | 'altKey' | 'metaKey'>>;\r\n\r\nexport interface InputRendererProps<R extends FilaDeDatos, S> {\r\n column: Column<R, S>,\r\n row: R,\r\n rowIdx: number;\r\n onRowChange: (row: R, commitChanges?: boolean) => void;\r\n onClose: (commitChanges?: boolean, shouldFocusCell?: boolean) => void;\r\n tableDefinition: TableDefinition,\r\n // el feedback y los cambios locales se leen y escriben en el EstadoGrilla (ver estadoGrilla.ts)\r\n /** Con cada tecla del editor. `valorActual` es el texto que tiene el editor; devuelve true si la tecla la resolvió la grilla (dejó la celda) y hay que cancelar su efecto nativo */\r\n onKeyPress?: (rowIndex: number, columnKey: string, event: TeclaDeEditor, handleCommit:(currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => Promise<void>, valorActual: unknown) => boolean;\r\n /** El editor se registra para recibir las teclas (F4) que caen en la celda antes de que él tenga el foco; devuelve la baja */\r\n registrarEditor?: (procesar: (key: string) => void) => () => void;\r\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;\r\n primaryKey: string[];\r\n}\r\n\r\n// ------------------------------------\r\n\r\nexport interface Ancestor {\r\n tableName: string;\r\n row: FilaDeDatos;\r\n tableDefinition: TableDefinition;\r\n fixedFields?: FixedField[];\r\n}\r\n\r\nexport interface GenericDataGridProps {\r\n tableName: string;\r\n fixedFields?: FixedField[];\r\n onOpenDetail?: (tableName: string, fixedFields: FixedField[], label: string, ancestors: Ancestor[]) => void;\r\n gridStyles?: React.CSSProperties;\r\n ancestors?: Ancestor[];\r\n}\r\n\r\nexport interface FixedField {\r\n fieldName: string;\r\n value: unknown;\r\n until?: unknown;\r\n /** Como en backend-plus (my-tables.js): un fixedField con `range` o `until` no fija el valor de la columna (la deja visible y editable) */\r\n range?: boolean;\r\n /** Como en backend-plus: el campo fijo no oculta su columna */\r\n show?: boolean;\r\n}\r\n\r\nexport interface ConfirmDialogProps {\r\n open: boolean;\r\n onClose: (confirm: boolean) => void;\r\n title: string;\r\n message: string;\r\n}\r\n\r\n/** El filtro de una columna: un texto (busca que contenga) o un operador con su valor */\r\nexport type FiltroDeColumna = string | { operator?: string; value?: string };\r\nexport type FiltrosDeGrilla = Record<string, FiltroDeColumna>;\r\n\r\n/** Una entrada de la fila de totales: qué se calculó (suma, promedio...) y su valor */\r\nexport type EntradaDeTotal = { type: string; value: number };\r\n\r\n/** Fila de resumen con los filtros de cada columna (arriba de la grilla, cuando se muestran los filtros) */\r\nexport type FilaDeFiltros = {\r\n id: 'filterRow';\r\n filters: FiltrosDeGrilla;\r\n setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;\r\n};\r\n\r\n/** Fila de resumen con los totales de las columnas que los tienen (pie de la grilla) */\r\nexport type FilaDeTotales = {\r\n id: 'bottomSummaryRow';\r\n totales: Record<string, EntradaDeTotal>;\r\n};\r\n\r\n/** Las filas de resumen de react-data-grid: filtros o totales */\r\nexport type FilaDeResumen = FilaDeFiltros | FilaDeTotales;\r\n\r\nexport interface FilterRendererProps<R extends FilaDeDatos, S> {\r\n column: Column<R, S>;\r\n filters: FiltrosDeGrilla;\r\n setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;\r\n}\r\n\r\nexport interface GenericDataGridForDetailProps {\r\n parentRow: FilaDeDatos; // La fila padre\r\n detailTableDefinition: DetailTable;\r\n parentTableName: string; // Añadido: Propiedad parentTableName\r\n}\r\n\r\nexport interface SideMenuProps {\r\n onMenuItemClick?: () => void;\r\n}\r\n\r\nexport interface MenuListItemProps {\r\n item: MenuInfoBase;\r\n level: number;\r\n onMenuItemClick?: () => void;\r\n}\r\n\r\nexport interface CustomCellProps<R extends FilaDeDatos, SR> extends RenderCellProps<R, SR> {\r\n column: RenderCellProps<R, SR>['column'] & { isPK?: boolean };\r\n}\r\n\r\nexport interface CustomHeaderCellProps<R, SR> extends RenderHeaderCellProps<R, SR> {\r\n column: RenderHeaderCellProps<R, SR>['column'] & { isPK?: boolean };\r\n}\r\n\r\nexport interface AppContextType {\r\n isLoggedIn: boolean;\r\n setIsLoggedIn: (loggedIn: boolean) => void;\r\n checkSession: () => Promise<void>;\r\n showSessionExpiredMessage: boolean;\r\n setShowSessionExpiredMessage: (show: boolean) => void;\r\n handleExpiredSessionRedirect: () => void;\r\n}\r\n\r\nexport interface AppProviderProps {\r\n children: ReactNode;\r\n publicPaths?: string[];\r\n}\r\n\r\nexport interface PrivateRouteProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport interface SnackbarState {\r\n open: boolean;\r\n message: string;\r\n severity: 'success' | 'error' | 'info' | 'warning';\r\n duration?: number | null; // Añadimos 'duration' opcional (en ms)\r\n}\r\n\r\n// Definir el tipo del contexto\r\nexport interface SnackbarContextType {\r\n // showSnackbar ahora acepta una duración opcional\r\n showSnackbar: (message: string, severity: 'success' | 'error' | 'info' | 'warning', duration?: number | null) => void;\r\n showSuccess: (message: string, duration?: number | null) => void;\r\n showError: (message: string, duration?: number | null) => void;\r\n showWarning: (message: string, duration?: number | null) => void;\r\n showInfo: (message: string, duration?: number | null) => void;\r\n}\r\n\r\nexport interface SnackbarProviderProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport interface CallApiOptions {\r\n isCritical?: boolean;\r\n}\r\n\r\n/** Los parámetros de un procedimiento por nombre; se codifican según la definición que manda el servidor */\r\nexport type ParametrosDeProcedimiento = Record<string, unknown>;\r\n\r\nexport interface UseApiCallResult {\r\n /** R es la forma del resultado de ese procedimiento (el servidor puede no devolver nada: undefined) */\r\n callApi: <R = unknown>(procedureName: string, params: ParametrosDeProcedimiento, opts?: CallApiOptions) => Promise<R | undefined>;\r\n callApiUpload: <R = unknown>(procedureName: string, file: File, params: ParametrosDeProcedimiento, opts?: CallApiOptions) => Promise<R | undefined>;\r\n loading: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport interface ClientContextState {\r\n menu: MenuInfoBase[];\r\n procedure: { [key: string]: ProcedureDef };\r\n procedures: ProcedureDef[];\r\n config: AppConfigClientSetup;\r\n useragent: Details;\r\n username: string;\r\n status: 'idle' | 'loading' | 'succeeded' | 'failed'; // Estado de la petición\r\n error: string | null; // Mensaje de error\r\n}\r\n\r\nexport interface MenuUiState {\r\n isDrawerOpen: boolean;\r\n subMenuOpenStates: { [key: string]: boolean }; // Para persistir el estado de los submenús por su 'name'\r\n}\r\n\r\nexport interface RouterState {\r\n currentPath: string;\r\n previousPath: string | null;\r\n redirectPath: string | null; // <--- ¡Nueva propiedad!\r\n}\r\n\r\nexport interface FetchApiOptions extends RequestInit {\r\n /** No poner Content-Type: lo arma el navegador (por ejemplo con un FormData, que lleva su boundary) */\r\n sinContentType?: boolean;\r\n}\r\n\r\nexport interface DataGridOption {\r\n id: string;\r\n label: string;\r\n icon: React.ReactNode;\r\n handler: () => Promise<void>;\r\n visible?: boolean;\r\n}\r","import { useState, useCallback, useEffect } from 'react';\r\nimport { useSnackbar } from '../../contexts/SnackbarContext';\r\nimport { NEW_ROW_INDICATOR, getPrimaryKeyValues } from '../../components/grid/utils/helpers';\r\nimport { DOM_TEMP_ID, FilaDeDatos, FixedField, TableDefinition, UseApiCallResult } from '../../types';\r\nimport { ImportOptions } from '../../components/grid/ImportDialog';\r\nimport { mensajeDeError, requerido } from '../../utils/errores';\r\n\r\n/** Lo que devuelve table_upload al terminar la importación de un archivo */\r\ninterface RespuestaDeImportacion {\r\n uploaded?: {\r\n inserted: number;\r\n updated: number;\r\n skipped: number;\r\n deleted: number;\r\n skippedColumns?: string[];\r\n };\r\n message?: string;\r\n}\r\n\r\ninterface UseGridActionsProps {\r\n tableDefinition: TableDefinition | null,\r\n tableName: string,\r\n fixedFields?: FixedField[];\r\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;\r\n setSelectedRows: React.Dispatch<React.SetStateAction<ReadonlySet<string>>>;\r\n primaryKey: string[],\r\n setLocalCellChanges: React.Dispatch<React.SetStateAction<Map<string, Set<string>>>>,\r\n setExitingRowIds: React.Dispatch<React.SetStateAction<Set<string>>>,\r\n callApi: UseApiCallResult['callApi'];\r\n callApiUpload: UseApiCallResult['callApiUpload'];\r\n resetView?: () => void;\r\n}\r\n\r\nexport const useGridActions = ({\r\n tableDefinition, tableName, fixedFields, setTableData,\r\n setSelectedRows, primaryKey, setLocalCellChanges,\r\n setExitingRowIds, callApi, callApiUpload, resetView\r\n}: UseGridActionsProps) => {\r\n\r\n const [rowToDelete, setRowToDelete] = useState<FilaDeDatos | null>(null);\r\n const [openConfirmDialog, setOpenConfirmDialog] = useState(false);\r\n\r\n // Estado para Vertical Editor\r\n const [openVerticalEditDialog, setOpenVerticalEditDialog] = useState(false);\r\n const [rowToEditVertical, setRowToEditVertical] = useState<FilaDeDatos | null>(null);\r\n\r\n const { showSuccess, showError, showWarning, showInfo } = useSnackbar();\r\n\r\n useEffect(() => {\r\n setLocalCellChanges(new Map());\r\n setOpenConfirmDialog(false);\r\n setRowToDelete(null);\r\n setOpenVerticalEditDialog(false);\r\n setRowToEditVertical(null);\r\n }, [tableName, setLocalCellChanges]);\r\n\r\n\r\n const handleAddRow = useCallback((currentRow?: FilaDeDatos) => {\r\n if (!tableDefinition) {\r\n showWarning('No se puede agregar una fila sin la definición de la tabla.');\r\n return;\r\n }\r\n\r\n // 1. Resetear filtros y sorters para que la fila aparezca donde corresponde\r\n if (resetView) resetView();\r\n\r\n const newRow: FilaDeDatos = {};\r\n tableDefinition.fields.forEach(field => {\r\n newRow[field.name] = null;\r\n });\r\n newRow[NEW_ROW_INDICATOR] = true;\r\n\r\n // 1.5. Generar un ID temporal único usando un Symbol para evitar duplicados en la vista (React Keys)\r\n // Usamos un Symbol para que NO aparezca en JSON.stringify ni Object.keys, pero \r\n // al asignarlo directamente es ENUMERABLE, por lo que el operador spread (...row) lo conserva.\r\n newRow[DOM_TEMP_ID] = `new_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\r\n\r\n // 2. Autocompletar con fixedFields\r\n if (fixedFields) {\r\n fixedFields.forEach(fixedField => {\r\n newRow[fixedField.fieldName] = fixedField.value;\r\n });\r\n }\r\n\r\n setTableData(prevData => {\r\n if (currentRow) {\r\n const currentRowId = getPrimaryKeyValues(currentRow, primaryKey);\r\n const index = prevData.findIndex(row => getPrimaryKeyValues(row, primaryKey) === currentRowId);\r\n\r\n if (index !== -1) {\r\n const newData = [...prevData];\r\n newData.splice(index + 1, 0, newRow);\r\n return newData;\r\n }\r\n }\r\n return [newRow, ...prevData];\r\n });\r\n setSelectedRows(new Set());\r\n\r\n const tempRowId = getPrimaryKeyValues(newRow, primaryKey);\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n const mandatoryEditableColumns = new Set<string>();\r\n\r\n tableDefinition.fields.forEach(field => {\r\n const isMandatory = (field.nullable === false || field.isPk);\r\n const isEditable = field.editable !== false;\r\n\r\n if (isMandatory && isEditable) {\r\n mandatoryEditableColumns.add(field.name);\r\n }\r\n });\r\n newMap.set(tempRowId, mandatoryEditableColumns);\r\n return newMap;\r\n });\r\n\r\n }, [tableDefinition, fixedFields, primaryKey, showWarning, setTableData, setSelectedRows, setLocalCellChanges, resetView]);\r\n\r\n const handleDeleteRow = useCallback((row: FilaDeDatos) => {\r\n setRowToDelete(row);\r\n setOpenConfirmDialog(true);\r\n }, []);\r\n\r\n const handleVerticalEditRow = useCallback((row: FilaDeDatos) => {\r\n setRowToEditVertical(row);\r\n setOpenVerticalEditDialog(true);\r\n }, []);\r\n\r\n\r\n const handleConfirmDelete = useCallback(async (confirm: boolean) => {\r\n setOpenConfirmDialog(false);\r\n if (!confirm || !rowToDelete) {\r\n showWarning('Eliminación cancelada por el usuario.');\r\n setRowToDelete(null);\r\n return;\r\n }\r\n\r\n if (!tableDefinition || !tableName) {\r\n showError('No se puede eliminar la fila sin la definición de la tabla o el nombre de la tabla.');\r\n setRowToDelete(null);\r\n return;\r\n }\r\n\r\n const rowId = getPrimaryKeyValues(rowToDelete, primaryKey);\r\n\r\n setExitingRowIds(prev => new Set(prev).add(rowId));\r\n\r\n setTimeout(async () => {\r\n if (rowToDelete[NEW_ROW_INDICATOR]) {\r\n setTimeout(() => {\r\n setTableData(prevData => prevData.filter(row => getPrimaryKeyValues(row, primaryKey) !== rowId));\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n newMap.delete(rowId);\r\n return newMap;\r\n });\r\n setSelectedRows(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n setExitingRowIds(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n showInfo(`Fila no guardada '${rowId}' eliminada localmente.`);\r\n setRowToDelete(null);\r\n }, 500);\r\n return;\r\n }\r\n\r\n try {\r\n const primaryKeyValues = tableDefinition.primaryKey.map((key) => rowToDelete[key]);\r\n await callApi('table_record_delete', {\r\n table: tableName,\r\n primaryKeyValues: primaryKeyValues\r\n }, { isCritical: false });\r\n\r\n console.log(`Fila con ID ${rowId} eliminada exitosamente del backend.`);\r\n setTimeout(() => {\r\n setTableData(prevData => prevData.filter(row => getPrimaryKeyValues(row, primaryKey) !== rowId));\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n newMap.delete(rowId);\r\n return newMap;\r\n });\r\n setSelectedRows(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n setExitingRowIds(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n showSuccess(`Fila '${rowId}' eliminada exitosamente.`);\r\n setRowToDelete(null);\r\n }, 500);\r\n\r\n } catch (err) {\r\n console.error(`Error al eliminar la fila '${rowId}':`, err);\r\n showError(`Error al eliminar la fila '${rowId}': ${mensajeDeError(err, 'Error desconocido')}`);\r\n setExitingRowIds(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n setRowToDelete(null);\r\n }\r\n }, 10);\r\n }, [\r\n rowToDelete, tableDefinition, tableName, primaryKey,\r\n showInfo, showSuccess, showError, showWarning,\r\n setTableData, setLocalCellChanges, setSelectedRows,\r\n setExitingRowIds, callApi\r\n ]);\r\n\r\n const handleImportFile = useCallback(async (file: File, options: ImportOptions) => {\r\n try {\r\n const result = await callApiUpload<RespuestaDeImportacion>('table_upload', file, {\r\n table: tableName,\r\n prefilledFields: fixedFields,\r\n ...options\r\n }, { isCritical: false });\r\n\r\n if (result && result.uploaded) {\r\n const { inserted, updated, skipped, deleted, skippedColumns } = result.uploaded;\r\n const messages = [];\r\n if (inserted > 0) messages.push(`${inserted} fila(s) insertada(s).`);\r\n if (updated > 0) messages.push(`${updated} fila(s) actualizada(s).`);\r\n if (skipped > 0) messages.push(`${skipped} fila(s) omitida(s).`);\r\n if (deleted > 0) messages.push(`${deleted} fila(s) eliminada(s).`);\r\n if (skippedColumns && skippedColumns.length > 0) messages.push(`Columnas omitidas: ${skippedColumns.join(', ')}`);\r\n\r\n showSuccess(messages.join('\\n') || 'Importación finalizada sin cambios detectados.');\r\n\r\n // Refrescar los datos después de la importación\r\n const newData = requerido(await callApi<FilaDeDatos[]>('table_data', {\r\n table: tableName,\r\n fixedFields: fixedFields\r\n }), 'los datos de la tabla');\r\n setTableData(newData);\r\n } else if (result && result.message) {\r\n showSuccess(result.message);\r\n }\r\n } catch (err) {\r\n console.error('Error durante la importación:', err);\r\n showError(`Importación cancelada: ${mensajeDeError(err, 'Error desconocido')}`);\r\n }\r\n }, [tableName, fixedFields, callApiUpload, callApi, showSuccess, showError, setTableData]);\r\n\r\n return {\r\n handleDeleteRow,\r\n handleConfirmDelete,\r\n handleAddRow,\r\n handleImportFile,\r\n handleVerticalEditRow,\r\n openConfirmDialog,\r\n rowToDelete,\r\n openVerticalEditDialog,\r\n setOpenVerticalEditDialog,\r\n rowToEditVertical,\r\n setRowToEditVertical\r\n };\r\n\r\n};\r\n","// src/hooks/grid/useGridDataView.ts\nimport { useState, useCallback, useMemo, useRef } from 'react';\nimport { TableDefinition, FieldDefinition, FilaDeDatos, FiltrosDeGrilla, EntradaDeTotal } from '../../types';\nimport typeStore from 'type-store';\nimport { compararValores, sameValue } from '../../components/grid/utils/helpers';\nimport { getBestTypedValue } from '../../components/grid/utils/typedControls';\nimport { FiltroInicial } from '../../utils/parametrosDeTabla';\n\nexport type SortColumn = { columnKey: string; direction: 'ASC' | 'DESC' };\n\nexport type SummaryEntry = EntradaDeTotal;\nexport type SummaryData = Record<string, SummaryEntry>;\n\n/** Si cambian más filas que esto se considera una recarga (refrescar, importar) y se vuelve a ordenar y filtrar */\nconst MAXIMO_FILAS_EDICION_PUNTUAL = 3;\n\ninterface UseGridDataViewProps {\n tableData: FilaDeDatos[];\n tableDefinition: TableDefinition | null;\n}\n\nexport const useGridDataView = ({ tableData, tableDefinition }: UseGridDataViewProps) => {\n\n // ── Sorting ──────────────────────────────────────────────────────────────\n const [sortColumns, setSortColumns] = useState<readonly SortColumn[]>([]);\n\n const getInitialSortColumns = useCallback((def: TableDefinition): readonly SortColumn[] => {\n if (def.sortColumns && def.sortColumns.length > 0) {\n return def.sortColumns.map(sc => ({\n columnKey: sc.column,\n direction: sc.order === -1 ? 'DESC' : 'ASC'\n }));\n }\n const pk = def.primaryKey && def.primaryKey.length > 0 ? def.primaryKey : ['id'];\n return pk.map(pkField => ({ columnKey: pkField, direction: 'ASC' }));\n }, []);\n\n const resetSorting = useCallback((def: TableDefinition) => {\n setSortColumns(getInitialSortColumns(def));\n }, [getInitialSortColumns]);\n\n const handleSortColumnsChange = useCallback((nextSortColumns: readonly SortColumn[]) => {\n if (nextSortColumns.length === 0) {\n setSortColumns([]);\n return;\n }\n const lastClicked = nextSortColumns[nextSortColumns.length - 1];\n const { columnKey, direction } = lastClicked;\n setSortColumns(prev => {\n const existingIndex = prev.findIndex(c => c.columnKey === columnKey);\n const newSort = [...prev];\n if (existingIndex !== -1) newSort.splice(existingIndex, 1);\n newSort.unshift({ columnKey, direction });\n return newSort;\n });\n }, []);\n\n // ── Filters ───────────────────────────────────────────────────────────────\n const [filters, setFilters] = useState<FiltrosDeGrilla>({});\n const [isFilterRowVisible, setIsFilterRowVisible] = useState(false);\n\n const toggleFilterVisibility = useCallback(() => {\n setIsFilterRowVisible(prev => {\n if (prev) setFilters({});\n return !prev;\n });\n }, []);\n\n /**\n * Los filtros con los que se abre la grilla (`fc` de la dirección o del menú, `filterColumns` de la definición): se\n * muestra la fila de filtros ya completa, como `createRowFilter` de backend-plus. El valor se escribe como lo escribiría\n * el usuario en el filtro de esa columna (un booleano `true` es «sí», por ejemplo).\n */\n const aplicarFiltrosIniciales = useCallback((condiciones: FiltroInicial[], definicion: TableDefinition) => {\n const iniciales: FiltrosDeGrilla = {};\n condiciones.forEach(({ column, operator, value }) => {\n const fieldDef = definicion.fields.find(f => f.name === column);\n let texto = value === null || value === undefined ? '' : String(value);\n if (fieldDef && value !== null && value !== undefined) {\n try { texto = typeStore.typerFrom(fieldDef).toLocalString(value); } catch (e) { /* queda como texto */ }\n }\n iniciales[column] = { operator, value: texto };\n });\n setFilters(iniciales);\n setIsFilterRowVisible(true);\n }, []);\n\n // ── filteredRows ──────────────────────────────────────────────────────────\n // Lo último que se calculó, para no reordenar ni refiltrar cuando solo se editaron algunas filas\n const vistaAnteriorRef = useRef<{\n tableData: FilaDeDatos[], filas: FilaDeDatos[], filters: FiltrosDeGrilla, isFilterRowVisible: boolean,\n sortColumns: readonly SortColumn[], tableDefinition: TableDefinition | null\n } | null>(null);\n\n const filteredRows = useMemo(() => {\n const anterior = vistaAnteriorRef.current;\n if (anterior\n && anterior.filters === filters\n && anterior.isFilterRowVisible === isFilterRowVisible\n && anterior.sortColumns === sortColumns\n && anterior.tableDefinition === tableDefinition\n && anterior.tableData.length === tableData.length\n ) {\n // Misma vista y misma cantidad de filas: si solo cambiaron unas pocas (una edición),\n // se reemplazan en su lugar. Así la fila que se está editando no salta a otra posición\n // ni desaparece por el filtro; se reacomoda cuando el usuario cambia el orden o los filtros.\n const reemplazos = new Map<FilaDeDatos, FilaDeDatos>();\n for (let i = 0; i < tableData.length && reemplazos.size <= MAXIMO_FILAS_EDICION_PUNTUAL; i++) {\n if (anterior.tableData[i] !== tableData[i]) reemplazos.set(anterior.tableData[i], tableData[i]);\n }\n if (reemplazos.size <= MAXIMO_FILAS_EDICION_PUNTUAL) {\n const filas = reemplazos.size === 0 ? anterior.filas : anterior.filas.map(fila => reemplazos.get(fila) ?? fila);\n vistaAnteriorRef.current = { ...anterior, tableData, filas };\n return filas;\n }\n }\n\n let rows = [...tableData];\n\n // 1. Filtrado\n if (isFilterRowVisible) {\n Object.keys(filters).forEach(key => {\n const filterObj = filters[key];\n if (!filterObj) return;\n\n let operator = '~';\n let filterValue = '';\n\n if (typeof filterObj === 'string') {\n filterValue = filterObj;\n } else {\n operator = filterObj.operator || '~';\n filterValue = filterObj.value || '';\n }\n\n if (filterValue === '' && operator !== '∅' && operator !== '!=∅') return;\n\n const fieldDef = tableDefinition?.fields.find(f => f.name === key);\n const typer = fieldDef ? typeStore.typerFrom(fieldDef) : null;\n\n // Intentamos parsear el valor del filtro al tipo real para comparaciones matemáticas/fechas\n let parsedFilterValue: unknown = filterValue;\n if (typer && filterValue !== '' && !['~', '!~', '∅', '!=∅'].includes(operator)) {\n try {\n parsedFilterValue = getBestTypedValue(filterValue, typer);\n } catch (e) {\n // Si falla el parseo (ej: fecha incompleta), seguimos con el valor original\n }\n }\n\n rows = rows.filter(row => {\n const rawValue = row[key];\n\n // cellValue se usa para búsquedas parciales (~) o como fallback\n let cellValue = '';\n if (typer && rawValue !== null && rawValue !== undefined) {\n try { cellValue = typer.toLocalString(rawValue).toLowerCase(); }\n catch (e) { cellValue = String(rawValue).toLowerCase(); }\n } else {\n cellValue = String(rawValue || '').toLowerCase();\n }\n\n const fValLower = String(filterValue).toLowerCase();\n\n switch (operator) {\n case '=':\n return sameValue(rawValue, parsedFilterValue) || cellValue === fValLower;\n case '!=':\n return !sameValue(rawValue, parsedFilterValue) && cellValue !== fValLower;\n case '~': return cellValue.includes(fValLower);\n case '!~': return !cellValue.includes(fValLower);\n case '∅': return rawValue === null || rawValue === undefined || rawValue === '';\n case '!=∅': return rawValue !== null && rawValue !== undefined && rawValue !== '';\n case '>': {\n const orden = compararValores(rawValue, parsedFilterValue);\n return orden !== null && orden > 0;\n }\n case '>=': {\n const orden = compararValores(rawValue, parsedFilterValue);\n return orden !== null && orden >= 0;\n }\n case '<': {\n const orden = compararValores(rawValue, parsedFilterValue);\n return orden !== null && orden < 0;\n }\n case '<=': {\n const orden = compararValores(rawValue, parsedFilterValue);\n return orden !== null && orden <= 0;\n }\n default: return cellValue.includes(fValLower);\n }\n });\n });\n }\n\n // 2. Sorting\n if (sortColumns.length > 0) {\n rows.sort((a, b) => {\n for (const sort of sortColumns) {\n const { columnKey, direction } = sort;\n const valA = a[columnKey];\n const valB = b[columnKey];\n if (valA === valB) continue;\n if (valA === null || valA === undefined) return direction === 'ASC' ? -1 : 1;\n if (valB === null || valB === undefined) return direction === 'ASC' ? 1 : -1;\n let comparison = 0;\n if (typeof valA === 'number' && typeof valB === 'number') {\n comparison = valA - valB;\n } else {\n comparison = String(valA).localeCompare(String(valB));\n }\n if (comparison !== 0) return direction === 'ASC' ? comparison : -comparison;\n }\n return 0;\n });\n }\n\n vistaAnteriorRef.current = { tableData, filas: rows, filters, isFilterRowVisible, sortColumns, tableDefinition };\n return rows;\n }, [tableData, filters, isFilterRowVisible, tableDefinition, sortColumns]);\n\n // ── Summary Data (totales calculados) ─────────────────────────────────────\n const summaryData: SummaryData = useMemo(() => {\n if (!tableDefinition) return {};\n const result: SummaryData = {};\n\n tableDefinition.fields.forEach((field: FieldDefinition) => {\n const agg = field.aggregate;\n if (!agg) return;\n\n const values = filteredRows\n .map(r => r[field.name])\n .filter(v => v !== null && v !== undefined);\n\n if (values.length === 0 && agg !== 'count' && agg !== 'countTrue') return;\n\n switch (agg) {\n case 'sum':\n result[field.name] = { type: 'sum', value: values.reduce<number>((a, b) => a + Number(b), 0) };\n break;\n case 'avg':\n const sum = values.reduce<number>((a, b) => a + Number(b), 0);\n result[field.name] = { type: 'avg', value: sum / values.length };\n break;\n case 'min':\n result[field.name] = { type: 'min', value: Math.min(...values.map(v => Number(v))) };\n break;\n case 'max':\n result[field.name] = { type: 'max', value: Math.max(...values.map(v => Number(v))) };\n break;\n case 'count':\n result[field.name] = { type: 'count', value: values.length };\n break;\n case 'countTrue':\n result[field.name] = { type: 'countTrue', value: filteredRows.filter(r => r[field.name] === true).length };\n break;\n }\n });\n\n return result;\n }, [tableDefinition, filteredRows]);\n\n // ── Reset (llamar al cambiar de tabla) ────────────────────────────────────\n const resetView = useCallback(() => {\n setFilters({});\n setIsFilterRowVisible(false);\n setSortColumns([]);\n }, []);\n\n return {\n // Sorting\n sortColumns,\n setSortColumns,\n getInitialSortColumns,\n handleSortColumnsChange,\n resetSorting,\n // Filters\n filters,\n setFilters,\n isFilterRowVisible,\n toggleFilterVisibility,\n aplicarFiltrosIniciales,\n // Data\n filteredRows,\n summaryData,\n // Reset\n resetView,\n };\n};\n","// src/components/grid/utils/typedControls.ts\nimport { Typer } from 'type-store';\n\nexport const BestControls = {\n text: {\n translates: {\n true: { '\\n': '\\u21b5' },\n false: { '\\u21b5': '\\n' }\n },\n translate: (right: boolean, textValue: string): string => {\n const map = BestControls.text.translates[right ? 'true' : 'false'] as Record<string, string>;\n let result = textValue;\n for (const char in map) {\n result = result.replace(new RegExp(char, 'g'), map[char]);\n }\n return result;\n },\n getTypedValue: (plainValue: unknown, typer: Typer): unknown => {\n if (plainValue === null || plainValue === undefined) {\n return plainValue;\n }\n return typer.fromUserInput(String(plainValue));\n }\n },\n number: {\n getTypedValue: (plainValue: unknown): number | null => {\n // En React, plainValue suele ser el string del input\n if (plainValue === '' || plainValue === null || plainValue === undefined) {\n return null;\n }\n const num = Number(plainValue);\n return isNaN(num) ? null : num;\n }\n },\n boolean: {\n getTypedValue: (plainValue: unknown): boolean | null => {\n if (typeof plainValue === 'boolean') return plainValue;\n if (plainValue === null || plainValue === undefined) return null;\n\n const value = String(plainValue).trim();\n const falseInitials: Record<string, boolean> = {\n 'n': true, 'N': true, '0': true, '2': true, 'F': true, 'f': true,\n '\\u043d': true, '\\u041d': true // cyrillic N\n };\n\n if (value === '') return null;\n return !falseInitials[value[0]];\n }\n },\n date: {\n getTypedValue: (plainValue: unknown): Date | null => {\n if (plainValue === null || plainValue === undefined || plainValue === '') return null;\n if (plainValue instanceof Date) return plainValue;\n\n const parts = String(plainValue).trim().split(/[-\\/]/).map(s => s.trim());\n if (parts.length !== 3) return null;\n\n let day: number, month: number, year: number;\n\n // Detectar si es YYYY-MM-DD o DD/MM/YYYY\n if (parts[0].length === 4) {\n // YYYY-MM-DD\n year = Number(parts[0]);\n month = Number(parts[1]) - 1;\n day = Number(parts[2]);\n } else {\n // DD/MM/YYYY\n // Requisito: Años si o si con 4 dígitos\n if (parts[2].length !== 4) return null;\n\n day = Number(parts[0]);\n month = Number(parts[1]) - 1;\n year = Number(parts[2]);\n }\n\n const date = new Date(year, month, day);\n // Validar que sea una fecha real (ej: no 31/02/2024)\n if (date.getFullYear() === year && date.getMonth() === month && date.getDate() === day) {\n return Object.assign(date, { isRealDate: true });\n }\n return null;\n }\n }\n};\n\n/**\n * Función principal para obtener el valor tipado según el tipo del typer.\n */\nexport const getBestTypedValue = (valor: unknown, typer: Typer): unknown => {\n\n const value = valor === '' ? typer.emptyValue : valor;\n\n const typeName = typer.typeName;\n\n if (['integer', 'bigint', 'decimal', 'double', 'float', 'number'].includes(typeName)) {\n return BestControls.number.getTypedValue(value);\n }\n if (typeName === 'boolean') {\n return BestControls.boolean.getTypedValue(value);\n }\n if (typeName === 'date' || (typeName && typeName.toLowerCase().includes('timestamp'))) {\n return BestControls.date.getTypedValue(value);\n }\n\n return BestControls.text.getTypedValue(value, typer);\n};\n\n/**\n * Lo que se escribió en la celda como valor del tipo del campo. Lo escrito que no es un valor del tipo (letras en un entero, una fecha que\n * no existe) lanza un Error: `getBestTypedValue` lo convierte en null, y eso borraba el valor de la celda, o «no cambiaba» y no se guardaba\n * ni se avisaba. Con la celda vacía el valor es null, como siempre.\n */\nexport const tiparEntrada = (valor: unknown, typer: Typer): unknown => {\n // solo espacios en un número o una fecha es vacío (Number(' ') da 0)\n const entrada = typeof valor === 'string' && valor.trim() === '' && typer.typeName !== 'text' ? '' : valor;\n const tipado = getBestTypedValue(entrada, typer);\n if (tipado === null && typeof entrada === 'string' && entrada !== '') {\n throw new Error(`«${valor}» no es un valor válido para este campo (${typer.typeName})`);\n }\n return tipado;\n};\n\n/** Si un carácter que se tipea no entra en el tipo del campo (una letra en un entero): backend-plus no lo deja escribir (`rejectedChar`) */\nexport const caracterRechazado = (caracter: string, typer: Typer): boolean => caracter.length === 1 && typer.rejectedChar(caracter);\n\n/** Si el texto pegado tiene algún carácter que el tipo del campo rechaza */\nexport const textoPegadoRechazado = (texto: string, typer: Typer): boolean => Array.from(texto).some(caracter => typer.rejectedChar(caracter));\n","import { TableDefinition } from 'backend-plus';\r\nimport { useCallback, useRef } from 'react';\r\nimport { CellMouseArgs, CellKeyDownArgs, CellMouseEvent, CellKeyboardEvent, DataGridHandle, CellSelectArgs, RowsChangeData } from 'react-data-grid';\r\nimport { ColumnaDeGrilla, esColumnaPropia } from '../../components/grid/GenericDataGrid';\r\nimport { DOM_TEMP_ID, FilaDeDatos, FilaDeResumen, FixedField, TeclaDeEditor } from '../../types';\r\nimport { esFilaDetalle } from '../../components/grid/detalles';\r\nimport { SortColumn } from './useGridDataView';\r\nimport { direccionDeSalida, posicionDelCursor } from './teclasDeCelda';\r\nimport { valorFijoDe } from '../../utils/fixedFields';\r\nimport { textoParaVer, tipoDeExpander } from '../../utils/expanders';\r\n\r\ninterface UseGridEventsProps {\r\n dataGridRef: React.RefObject<DataGridHandle>\r\n tableDefinition: TableDefinition | null,\r\n fixedFields: FixedField[] | undefined\r\n /** Las filas tal como se muestran: filtradas, ordenadas y con las filas de detalle intercaladas */\r\n filteredRows: FilaDeDatos[],\r\n tableData: FilaDeDatos[],\r\n setSelectedCell: React.Dispatch<React.SetStateAction<CellSelectArgs<FilaDeDatos, FilaDeResumen> | undefined>>,\r\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>,\r\n setSortColumns: React.Dispatch<React.SetStateAction<readonly SortColumn[]>>,\r\n /** F9 en una celda que no se edita (JSON o texto largo): la grilla muestra el contenido completo bajo la celda */\r\n verContenido?: (anclaje: HTMLElement, texto: string) => void\r\n}\r\n\r\n/** Cuánto tiempo (ms) espera una tecla encolada a que se monte el editor (si no, se descarta) */\r\nconst VIGENCIA_TECLA_PENDIENTE = 400;\r\n\r\n/** Cuánto tiempo (ms) vale el valor que F4 acaba de copiar para la pulsación siguiente */\r\nconst VIGENCIA_VALOR_PENDIENTE_F4 = 1000;\r\n\r\nexport const useGridEvents = ({dataGridRef, tableDefinition, filteredRows, tableData, fixedFields, setSelectedCell, setTableData, setSortColumns, verContenido}: UseGridEventsProps) => {\r\n\r\n // handleKeyPressInEditor viaja dentro de las columnas: tiene que ser estable para no recrearlas\r\n // en cada cambio de datos, así que lee las filas visibles y la definición a través de refs.\r\n const filteredRowsRef = useRef(filteredRows);\r\n filteredRowsRef.current = filteredRows;\r\n const tableDefinitionRef = useRef(tableDefinition);\r\n tableDefinitionRef.current = tableDefinition;\r\n // F4 copia el valor de la fila de arriba: lo que se copió y todavía no llegó a las filas (fila|columna → valor)\r\n const valoresF4Pendientes = useRef(new Map<string, { valor: unknown, cuando: number }>());\r\n\r\n // F4 apretado manda más teclas por segundo que lo que tarda en abrirse el editor de la celda siguiente:\r\n // las que llegan en el hueco caen en la celda y no en el editor. Cada editor se registra acá; si hay uno,\r\n // la tecla se le pasa directo; si no hay (todavía no se montó), se guarda y la toma el próximo que se registre.\r\n const teclasPendientes = useRef<{ key: string, cuando: number }[]>([]);\r\n const editorActivo = useRef<((key: string) => void) | null>(null);\r\n // la última tecla que se apretó en un editor: → solo deja la celda si la anterior también fue → (como backend-plus)\r\n const teclaAnteriorRef = useRef<string | null>(null);\r\n const registrarEditor = useCallback((procesar: (key: string) => void): (() => void) => {\r\n editorActivo.current = procesar;\r\n const cola = teclasPendientes.current;\r\n while (cola.length > 0 && Date.now() - cola[0].cuando > VIGENCIA_TECLA_PENDIENTE) cola.shift();\r\n const pendiente = cola.shift();\r\n if (pendiente) procesar(pendiente.key);\r\n return () => { if (editorActivo.current === procesar) editorActivo.current = null; };\r\n }, []);\r\n\r\n const handleKeyPressInEditor = useCallback((rowIndex: number, columnKey: string, event: TeclaDeEditor, currentColumns: ColumnaDeGrilla[], handleCommit:(currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => Promise<void>, valorActual: unknown): boolean => {\r\n // true si la tecla la resolvió la grilla (dejó la celda o no había adónde ir): el editor cancela su efecto nativo (el Tab, por ejemplo)\r\n let resuelta = false;\r\n const tableDefinition = tableDefinitionRef.current;\r\n const filteredRows = filteredRowsRef.current;\r\n if (dataGridRef.current && tableDefinition) {\r\n const currentColumnIndex = currentColumns.findIndex(col => col.key === columnKey);\r\n const editableColumns = currentColumns.filter(col => {\r\n const fieldDefinition = tableDefinition.fields.find(f => f.name === col.key);\r\n return col.key !== 'actionsColumn' && !(esColumnaPropia(col) && col.customType === 'detail') && (fieldDefinition?.editable !== false && !fieldDefinition?.clientSide);\r\n });\r\n const {key, target} = event;\r\n // como backend-plus: lo que hace la flecha → depende de la tecla anterior (ver direccionDeSalida)\r\n const teclaAnterior = teclaAnteriorRef.current;\r\n teclaAnteriorRef.current = key;\r\n const direccion = direccionDeSalida(event, posicionDelCursor(target), teclaAnterior);\r\n\r\n if (currentColumnIndex !== -1 && editableColumns.length > 0) {\r\n const editableColumnKeys = editableColumns.map(col => col.key);\r\n let currentEditableColumnIndex = editableColumnKeys.indexOf(columnKey);\r\n \r\n // Se llama en el mismo tick que el commit de la celda: react-data-grid junta ambos cambios y pasa directo\r\n // del editor de una celda al de la siguiente. Con un setTimeout en el medio, las teclas que llegan\r\n // mientras el editor nuevo todavía no existe se pierden (sobre todo con F4 apretado).\r\n const hacerFoco = ({rowIdx, idx}:{rowIdx:number, idx: number}) =>\r\n dataGridRef.current?.selectCell({ rowIdx, idx }, { enableEditor: true, shouldFocusCell: true });\r\n \r\n // Índice de la próxima fila de datos en la dirección indicada, salteando las filas de detalle (-1 si no hay)\r\n const filaDeDatos = (desde: number, paso: 1 | -1) => {\r\n let i = desde + paso;\r\n while (i >= 0 && i < filteredRows.length && esFilaDetalle(filteredRows[i])) i += paso;\r\n return i >= 0 && i < filteredRows.length ? i : -1;\r\n };\r\n\r\n const calcularColumnaSiguiente = ()=>{\r\n let nextEditableColumnIndex = currentEditableColumnIndex + 1;\r\n let nextRowIndex = rowIndex;\r\n if (nextEditableColumnIndex >= editableColumns.length) {\r\n nextEditableColumnIndex = 0;\r\n nextRowIndex = filaDeDatos(rowIndex, 1);\r\n if (nextRowIndex === -1) {\r\n nextRowIndex = filaDeDatos(-1, 1);\r\n }\r\n }\r\n const nextColumnKey = editableColumnKeys[nextEditableColumnIndex];\r\n const nextColumnIndex = currentColumns.findIndex(col => col.key === nextColumnKey);\r\n return {rowIdx: nextRowIndex, idx:nextColumnIndex}\r\n }\r\n \r\n const indiceDeColumna = (indiceEditable: number) => currentColumns.findIndex(col => col.key === editableColumnKeys[indiceEditable]);\r\n // Shift+Tab: la celda editable anterior; al pasar la primera, la última de la fila de arriba (null si no hay)\r\n const calcularColumnaAnteriorConSalto = () => {\r\n if (currentEditableColumnIndex > 0) return {rowIdx: rowIndex, idx: indiceDeColumna(currentEditableColumnIndex - 1)};\r\n const filaAnterior = filaDeDatos(rowIndex, -1);\r\n return filaAnterior === -1 ? null : {rowIdx: filaAnterior, idx: indiceDeColumna(editableColumnKeys.length - 1)};\r\n }\r\n // ← y →: la celda editable de al lado en la misma fila (null en los extremos: no saltan de fila)\r\n const calcularColumnaVecina = (paso: 1 | -1) => {\r\n const vecina = currentEditableColumnIndex + paso;\r\n return vecina < 0 || vecina >= editableColumnKeys.length ? null : {rowIdx: rowIndex, idx: indiceDeColumna(vecina)};\r\n }\r\n // ↑ y ↓: la misma columna en la fila de datos de arriba o de abajo (null si no hay)\r\n const calcularFilaVecina = (paso: 1 | -1) => {\r\n const vecina = filaDeDatos(rowIndex, paso);\r\n return vecina === -1 ? null : {rowIdx: vecina, idx: currentColumnIndex};\r\n }\r\n \r\n const calcularFilaSiguiente = ()=>{\r\n const nextRowIndex = filaDeDatos(rowIndex, 1);\r\n return {rowIdx: nextRowIndex === -1 ? rowIndex : nextRowIndex, idx:currentColumnIndex}\r\n }\r\n\r\n \r\n if (currentEditableColumnIndex !== -1) {\r\n switch(true){\r\n case (direccion !== null): {\r\n // la tecla es de la grilla aunque no haya adónde ir (en el borde): no se deja que haga lo suyo\r\n resuelta = true;\r\n const destino = {\r\n siguiente: calcularColumnaSiguiente,\r\n anterior: calcularColumnaAnteriorConSalto,\r\n izquierda: () => calcularColumnaVecina(-1),\r\n derecha: () => calcularColumnaVecina(1),\r\n arriba: () => calcularFilaVecina(-1),\r\n abajo: () => calcularFilaVecina(1),\r\n }[direccion]();\r\n if (destino) {\r\n // como dejar la celda de cualquier otra forma: se guarda (el cambio de la fila se aplica en el acto\r\n // y el guardado sigue en segundo plano) y el foco pasa a la celda de destino\r\n void handleCommit(valorActual, true, true);\r\n hacerFoco(destino);\r\n }\r\n break;\r\n }\r\n case (key === 'F4'): {\r\n // no hace falta limpiar el orden: useGridDataView mantiene las filas en su lugar al editar\r\n const {rowIdx, idx} = calcularFilaSiguiente();\r\n const indiceAnterior = filaDeDatos(rowIndex, -1);\r\n const previousRow = indiceAnterior === -1 ? undefined : filteredRows[indiceAnterior]\r\n if(previousRow){\r\n // El valor de la fila de arriba puede ser uno que acabamos de copiar y que todavía no se\r\n // redibujó (con la tecla apretada llegan más pulsaciones que renders): se usa el pendiente.\r\n const pendiente = valoresF4Pendientes.current.get(`${indiceAnterior}|${columnKey}`);\r\n const valorACopiar = pendiente && Date.now() - pendiente.cuando < VIGENCIA_VALOR_PENDIENTE_F4\r\n ? pendiente.valor\r\n : previousRow[columnKey];\r\n valoresF4Pendientes.current.set(`${rowIndex}|${columnKey}`, { valor: valorACopiar, cuando: Date.now() });\r\n // El guardado sigue en segundo plano y el foco avanza ya: esperar la respuesta del servidor\r\n // antes de pasar a la fila siguiente hacía perder las pulsaciones que llegaban mientras tanto\r\n // (el feedback o el error de cada celda igual se muestran cuando llega la respuesta).\r\n void handleCommit(valorACopiar, true, true)\r\n // este editor se va: las teclas que lleguen hasta que se registre el de la fila siguiente\r\n // se guardan (si no, se procesarían otra vez sobre esta fila y no avanzarían)\r\n editorActivo.current = null;\r\n hacerFoco({rowIdx ,idx})\r\n }\r\n break;\r\n }\r\n default: break;\r\n }\r\n } \r\n } \r\n }\r\n return resuelta;\r\n }, [dataGridRef]);\r\n\r\n const handleCellKeyDown = useCallback((args: CellKeyDownArgs<FilaDeDatos, FilaDeResumen>, event: CellKeyboardEvent) => {\r\n // F9 o Alt+↓ (los expanders de backend-plus) en una celda seleccionada: si se edita, se abre el editor y este abre el menú, el calendario\r\n // o la ventana de texto; si no se edita, se muestra el contenido completo (JSON o texto largo)\r\n const esExpander = event.key === 'F9' && !event.shiftKey && !event.ctrlKey && !event.altKey || event.key === 'ArrowDown' && event.altKey && !event.shiftKey && !event.ctrlKey;\r\n if (esExpander && !(event.target instanceof HTMLInputElement) && !esFilaDetalle(args.row)) {\r\n const campo = tableDefinitionRef.current?.fields.find(f => f.name === args.column.key);\r\n const editable = typeof args.column.editable === 'function' ? args.column.editable(args.row) : args.column.editable === true;\r\n const tipo = campo ? tipoDeExpander(campo, editable) : null;\r\n if (campo && editable && tipo !== null) {\r\n teclasPendientes.current.push({ key: 'F9', cuando: Date.now() });\r\n dataGridRef.current?.selectCell({ rowIdx: args.rowIdx, idx: args.column.idx }, { enableEditor: true, shouldFocusCell: true });\r\n event.preventGridDefault();\r\n return;\r\n }\r\n if (campo && !editable && (tipo === 'json' || tipo === 'texto') && verContenido && event.target instanceof HTMLElement) {\r\n verContenido(event.target, textoParaVer(args.row[campo.name]));\r\n event.preventGridDefault();\r\n return;\r\n }\r\n }\r\n if (event.key === 'F4' && !(event.target instanceof HTMLInputElement) && !esFilaDetalle(args.row)) {\r\n const fieldDefinition = tableDefinitionRef.current?.fields.find(f => f.name === args.column.key);\r\n if (fieldDefinition && fieldDefinition.editable !== false && !fieldDefinition.clientSide) {\r\n if (editorActivo.current) editorActivo.current(event.key);\r\n else teclasPendientes.current.push({ key: event.key, cuando: Date.now() });\r\n event.preventGridDefault();\r\n return;\r\n }\r\n }\r\n if (['Enter', 'Tab', 'ArrowDown', 'ArrowUp','ArrowRight', 'ArrowLeft'].includes(event.key)) {\r\n event.preventGridDefault();\r\n }\r\n }, [dataGridRef, verContenido]);\r\n\r\n const handleCellMouseDown = useCallback((_args: CellMouseArgs<FilaDeDatos, FilaDeResumen>, event: CellMouseEvent) => {\r\n event.preventGridDefault(); \r\n }, []);\r\n\r\n const handleCellDoubleClick = useCallback((_args: CellMouseArgs<FilaDeDatos, FilaDeResumen>, event: CellMouseEvent) => {\r\n event.preventGridDefault(); \r\n }, []);\r\n\r\n const handleSelectedCellChange = useCallback((args: CellSelectArgs<FilaDeDatos, FilaDeResumen> | undefined) => {\r\n setSelectedCell(args);\r\n }, [setSelectedCell]);\r\n\r\n const handleCellClick = useCallback((args: CellMouseArgs<FilaDeDatos, FilaDeResumen>, _event: CellMouseEvent) => {\r\n // la fila de detalle contiene otra grilla: los clicks son de esa grilla\r\n if (esFilaDetalle(args.row)) return;\r\n const fieldDefinition = tableDefinition?.fields.find(f => f.name === args.column.key);\r\n const isFixedField = valorFijoDe(fixedFields, args.column.key) !== undefined;\r\n const isEditable = fieldDefinition?.editable !== false && !isFixedField;\r\n if(isEditable){\r\n // antes se limpiaba el orden al clickear; ya no hace falta porque al editar\r\n // las filas no se reordenan hasta que el usuario cambie el orden o los filtros\r\n args.selectCell(true);\r\n } else if (fieldDefinition && tipoDeExpander(fieldDefinition, false) !== null) {\r\n // una celda que no se edita también se selecciona (sin editor): F9 muestra su contenido completo, como en backend-plus\r\n args.selectCell(false);\r\n }\r\n }, [tableDefinition, fixedFields]);\r\n\r\n const handleRowsChange = useCallback((updatedRows: FilaDeDatos[], data: RowsChangeData<FilaDeDatos, FilaDeResumen>) => {\r\n // updatedRows son las filas visibles (filtradas y ordenadas), no todo tableData:\r\n // se reemplazan en tableData solo las filas editadas, buscándolas por identidad.\r\n const reemplazos = new Map<FilaDeDatos, FilaDeDatos>();\r\n data.indexes.forEach(index => {\r\n const oldRow = filteredRows[index];\r\n const newRow = updatedRows[index];\r\n if (!oldRow || !newRow || esFilaDetalle(oldRow)) return;\r\n // Preservar la identidad (DOM_TEMP_ID) de las filas editadas\r\n if (oldRow[DOM_TEMP_ID] && !newRow[DOM_TEMP_ID]) {\r\n newRow[DOM_TEMP_ID] = oldRow[DOM_TEMP_ID];\r\n }\r\n reemplazos.set(oldRow, newRow);\r\n });\r\n if (reemplazos.size === 0) return;\r\n setTableData(prevData => prevData.map(row => reemplazos.get(row) ?? row));\r\n }, [setTableData, filteredRows]);\r\n\r\n return {\r\n handleCellMouseDown,\r\n handleCellDoubleClick,\r\n handleCellClick,\r\n handleCellKeyDown,\r\n handleKeyPressInEditor,\r\n registrarEditor,\r\n handleSelectedCellChange,\r\n handleRowsChange\r\n };\r\n};","// src/components/grid/detalles.tsx\n// Grillas de detalle desplegadas dentro de la grilla, como lo hace backend-plus (my-tables.js):\n// al apretar el botón de un detalle se inserta debajo de la fila una \"fila de detalle\" que ocupa\n// todas las columnas y contiene otra grilla (que a su vez puede desplegar sus propios detalles).\nimport React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';\nimport { IconButton, Tooltip } from '@mui/material';\nimport KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';\nimport KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport { DetailTable } from 'backend-plus';\nimport { FilaDeDatos, FixedField, TableDefinition } from '../../types';\nimport { SortColumn } from '../../hooks/grid/useGridDataView';\nimport { FallbackWScreen, wScreens } from '../../pages/WScreens';\nimport { cumpleCondicion } from './condiciones';\n\n/** Marca de las filas de detalle (es un Symbol para que no se confunda con ningún campo) */\nexport const DETALLE = Symbol('detalle');\n\n// un type y no una interface: así es asignable a FilaDeDatos (las interfaces no tienen firma de índice implícita)\nexport type FilaDetalle = {\n [DETALLE]: true;\n /** `${rowId}|${abr}`: identifica el detalle dentro de su grilla */\n clave: string;\n filaPadre: FilaDeDatos;\n detailTable: DetailTable;\n fixedFields: FixedField[];\n};\n\nexport const esFilaDetalle = (row: unknown): row is FilaDetalle =>\n typeof row === 'object' && row !== null && (row as Partial<FilaDetalle>)[DETALLE] === true;\n\nexport const claveDetalle = (rowId: string, detailTable: DetailTable) => `${rowId}|${detailTable.abr}`;\n\n/** Alto de la fila de detalle mientras la grilla anidada todavía no informó cuánto necesita */\nexport const ALTO_INICIAL_DETALLE = 140;\nexport const ALTO_MINIMO_DETALLE = 80;\n\n/** Un campo de DetailTable['fields'] normalizado; `until` lo acepta my-tables.js aunque no esté en el .d.ts */\ninterface CampoDeDetalle {\n target: string;\n source?: string;\n value?: unknown;\n tieneValor: boolean;\n nullMeansAll?: boolean;\n until?: unknown;\n}\n\nconst normalizarCampoDeDetalle = (campo: DetailTable['fields'][number]): CampoDeDetalle => {\n if (typeof campo === 'string') return { target: campo, source: campo, tieneValor: false };\n if ('value' in campo) return { target: campo.target, value: campo.value, tieneValor: true, until: (campo as { until?: unknown }).until };\n return { target: campo.target, source: campo.source, tieneValor: false, nullMeansAll: campo.nullMeansAll, until: (campo as { until?: unknown }).until };\n};\n\n/** Los fixedFields de la grilla de detalle a partir de la fila padre (mismo cálculo que calculateFixedFields de my-tables.js) */\nexport function calcularFixedFieldsDetalle(detailTable: DetailTable, row: FilaDeDatos): FixedField[] {\n return detailTable.fields\n .map(normalizarCampoDeDetalle)\n .filter(campo => !campo.nullMeansAll || (campo.source !== undefined && row[campo.source] != null))\n .map(campo => {\n const fixedField: FixedField = {\n fieldName: campo.target,\n value: campo.tieneValor ? campo.value : row[campo.source!],\n };\n if (campo.until !== undefined) fixedField.until = campo.until;\n return fixedField;\n });\n}\n\n/** Lo que una grilla anidada guarda para volver a mostrarse sin recargar (por ejemplo al scrollear) */\nexport interface GrillaGuardada {\n tableDefinition: TableDefinition;\n tableData: FilaDeDatos[];\n anchosCalculados: Map<string, number>;\n sortColumns: readonly SortColumn[];\n detallesAbiertos: string[];\n altosDetalle: Map<string, number>;\n}\n\n/**\n * Las filas de detalle salen del DOM cuando se scrollea (virtualización) y su grilla se desmonta.\n * Este cache, compartido por toda la jerarquía de grillas, permite volver a mostrarlas como estaban,\n * igual que backend-plus reusa el divDetail al volver a desplegar.\n */\nexport const CacheGrillasContext = createContext<Map<string, GrillaGuardada> | null>(null);\n\ninterface DetallesGrilla {\n abiertos: ReadonlySet<string>;\n alternarDetalle: (row: FilaDeDatos, rowId: string, detailTable: DetailTable, enOtraPestana: boolean) => void;\n renderDetalle: (fila: FilaDetalle) => React.ReactNode;\n}\n\nexport const DetallesGrillaContext = createContext<DetallesGrilla | null>(null);\n\nconst useDetallesGrilla = () => {\n const detalles = useContext(DetallesGrillaContext);\n if (!detalles) throw new Error('falta DetallesGrillaContext.Provider');\n return detalles;\n};\n\ninterface BotonDetalleProps {\n row: FilaDeDatos;\n rowId: string;\n detailTable: DetailTable;\n deshabilitado: boolean;\n}\n\n/** Botón de la columna de un detalle: despliega/repliega; con Ctrl o el botón del medio lo abre en otra pestaña */\nexport function BotonDetalle({ row, rowId, detailTable, deshabilitado }: BotonDetalleProps) {\n const { abiertos, alternarDetalle } = useDetallesGrilla();\n // la `condition` del detalle (función de la aplicación) puede ocultar el botón para algunas filas\n if (!cumpleCondicion(detailTable.condition, row)) return <div className=\"fpr-boton-detalle\" />;\n const abierto = abiertos.has(claveDetalle(rowId, detailTable));\n const titulo = deshabilitado\n ? 'Grabá la fila para ver sus detalles'\n : `${detailTable.label || detailTable.abr}${abierto ? ' (replegar)' : ''} — Ctrl+click: en otra pestaña`;\n return (\n <Tooltip title={titulo}>\n <span className=\"fpr-boton-detalle\">\n <IconButton\n size=\"small\"\n disabled={deshabilitado}\n onClick={(e) => alternarDetalle(row, rowId, detailTable, e.ctrlKey || e.metaKey)}\n onAuxClick={(e) => { if (e.button === 1) alternarDetalle(row, rowId, detailTable, true); }}\n >\n {abierto ? <KeyboardArrowDownIcon fontSize=\"small\" /> : <KeyboardArrowRightIcon fontSize=\"small\" />}\n </IconButton>\n </span>\n </Tooltip>\n );\n}\n\n/**\n * Alto máximo que puede ocupar lo que se dibuja dentro de un detalle. Lo usan las grillas que una wScreen arma\n * adentro de un detalle para acotarse sin que la wScreen tenga que saber de alturas.\n */\nexport const AltoDisponibleContext = createContext<number | undefined>(undefined);\n\n/** Alto que ocupan las filas de detalle además de su contenido (el padding de .fpr-detalle) */\nconst MARGEN_VERTICAL_DETALLE = 8;\n\ninterface DetalleWScreenProps {\n fila: FilaDetalle;\n /** Alto máximo de la fila de detalle: si el contenido lo pasa, scrollea */\n altoMaximo?: number;\n /** El contenido informa el alto que necesita para mostrarse completo */\n onAlto: (alto: number) => void;\n /** El contenido informa el ancho que necesita */\n onAncho: (ancho: number) => void;\n}\n\n/**\n * Detalle que en lugar de otra tabla es una pantalla propia de la aplicación (`wScreen`), por ejemplo las anotaciones\n * de un ticket en gestik. Recibe como `params` los fixedFields del detalle, igual que `mainAction(params, div)` de backend-plus.\n */\nexport function DetalleWScreen({ fila, altoMaximo, onAlto, onAncho }: DetalleWScreenProps) {\n const nombre = fila.detailTable.wScreen ?? '';\n const Pantalla = wScreens[nombre];\n const params = useMemo(\n () => Object.fromEntries(fila.fixedFields.map(fixedField => [fixedField.fieldName, fixedField.value])),\n [fila.fixedFields]\n );\n const contenido = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const elemento = contenido.current;\n if (!elemento) return;\n const medir = () => {\n onAlto(elemento.offsetHeight + MARGEN_VERTICAL_DETALLE);\n onAncho(elemento.offsetWidth);\n };\n medir();\n const observador = new ResizeObserver(medir);\n observador.observe(elemento);\n return () => observador.disconnect();\n }, [onAlto, onAncho]);\n return (\n <AltoDisponibleContext.Provider value={altoMaximo === undefined ? undefined : altoMaximo - MARGEN_VERTICAL_DETALLE}>\n <div className=\"fpr-detalle-wscreen\">\n <div ref={contenido} className=\"fpr-detalle-wscreen-contenido\">\n {Pantalla\n ? <Pantalla screenName={nombre} params={params} />\n : <FallbackWScreen screenName={nombre} />}\n </div>\n </div>\n </AltoDisponibleContext.Provider>\n );\n}\n\n/** Contenido de la fila de detalle (lo arma GenericDataGrid para evitar una importación circular) */\nexport function CeldaDetalle({ fila }: { fila: FilaDetalle }) {\n const { renderDetalle } = useDetallesGrilla();\n return <>{renderDetalle(fila)}</>;\n}\n","import React from 'react';\r\nimport { Box, Typography, Paper } from '@mui/material';\r\nimport ConstructionIcon from '@mui/icons-material/Construction';\r\nimport WarningAmberIcon from '@mui/icons-material/WarningAmber'; // Asegúrate de importar el icono de advertencia\r\n\r\nimport { WScreenProps } from '../types';\r\n\r\n\r\n// Ejemplo de un componente de wScreen personalizado\r\nconst MyCustomDashboard: React.FC<WScreenProps> = ({ screenName }) => {\r\n return (\r\n <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%' }}>\r\n <Paper elevation={3} sx={{ p: 4, textAlign: 'center', maxWidth: 600 }}>\r\n <Typography variant=\"h5\" component=\"h1\" gutterBottom>\r\n ¡Bienvenido a tu WScreen: {screenName}!\r\n </Typography>\r\n <Typography variant=\"body1\" color=\"text.secondary\">\r\n Esta es una pantalla de interfaz de usuario personalizada.\r\n </Typography>\r\n <Box sx={{ mt: 3 }}>\r\n <ConstructionIcon sx={{ fontSize: 80, color: 'primary.main' }} />\r\n </Box>\r\n </Paper>\r\n </Box>\r\n );\r\n};\r\n\r\nexport const FallbackWScreen: React.FC<WScreenProps> = ({ screenName }) => {\r\n return (\r\n <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%' }}>\r\n <Paper elevation={3} sx={{ p: 4, textAlign: 'center', maxWidth: 600, backgroundColor: 'warning.light', border: '1px solid orange' }}>\r\n <Typography variant=\"h5\" component=\"h1\" gutterBottom color=\"warning.dark\">\r\n WScreen No Implementada\r\n </Typography>\r\n <Typography variant=\"body1\" color=\"warning.dark\">\r\n No se encontró un componente implementado para la **WScreen**: **`\"${screenName}\"`**.\r\n Por favor, verifica el `menuType` en la definición del menú o asegúrate de que el componente esté correctamente importado y mapeado en el frontend.\r\n </Typography>\r\n <Box sx={{ mt: 3 }}>\r\n <WarningAmberIcon sx={{ fontSize: 80, color: 'warning.dark' }} />\r\n </Box>\r\n </Paper>\r\n </Box>\r\n );\r\n};\r\n\r\n// Mapeo de nombres de wScreens a sus componentes\r\nexport let wScreens: Record<string, React.FC<WScreenProps>> = {\r\n 'MyCustomDashboard': MyCustomDashboard,\r\n // Agrega más componentes de wScreen aquí:\r\n // 'AnotherWScreen': AnotherWScreenComponent,\r\n};\r\n\r\nexport const extendWScreens = (newWScreens: Record<string, React.FC<WScreenProps>>) => {\r\n wScreens = { ...wScreens, ...newWScreens };\r\n console.log('wScreens extendidos:', wScreens);\r\n};","// src/components/grid/condiciones.ts\n// Condiciones de los detalles (`condition` de un DetailTable): funciones de la aplicación que deciden, fila por fila,\n// si el botón del detalle se muestra. Es el equivalente de `my.conditions` de backend-plus.\nimport { CondicionDeDetalle, CondicionesMap, FilaDeDatos } from '../../types';\n\nexport let condiciones: CondicionesMap = {};\n\nexport const extendConditions = (nuevas: CondicionesMap) => {\n condiciones = { ...condiciones, ...nuevas };\n};\n\nconst avisadas = new Set<string>();\n\n/**\n * ¿Se muestra el detalle para esta fila? Sin `condition` siempre se muestra.\n * Si la condición no está registrada se avisa una vez por consola y se muestra: ocultar el detalle por un error\n * de configuración escondería datos sin dejar rastro.\n */\nexport function cumpleCondicion(nombre: string | undefined, fila: FilaDeDatos): boolean {\n if (!nombre) return true;\n const condicion: CondicionDeDetalle | undefined = condiciones[nombre];\n if (!condicion) {\n if (!avisadas.has(nombre)) {\n avisadas.add(nombre);\n console.warn(`La condición '${nombre}' de un detalle no está registrada (extendConditions / myConditions): se muestra el detalle.`);\n }\n return true;\n }\n return condicion(fila);\n}\n","// Cómo se sale de una celda en edición con el teclado, igual que backend-plus (`captureKeys` de my-things.js).\n// Dejar la celda por cualquier camino la guarda; lo que cambia es adónde va el foco y cuándo una tecla deja la celda\n// o solo mueve el cursor dentro del texto.\n\n/** Hacia dónde va el foco. `siguiente` y `anterior` saltan de fila al pasar el borde; `izquierda` y `derecha` no */\nexport type DireccionDeSalida = 'siguiente' | 'anterior' | 'izquierda' | 'derecha' | 'arriba' | 'abajo';\n\nexport interface TeclaPulsada {\n key: string;\n shiftKey?: boolean;\n ctrlKey?: boolean;\n altKey?: boolean;\n metaKey?: boolean;\n}\n\n/** Dónde está el cursor dentro del texto del editor */\nexport interface PosicionDelCursor {\n vacio: boolean;\n alPrincipio: boolean;\n alFinal: boolean;\n}\n\n/**\n * La dirección a la que la tecla manda el foco, o null si la tecla no deja la celda (sigue su curso: mueve el cursor, escribe...).\n * - Enter y Tab van a la celda siguiente; Shift+Tab a la anterior. Shift+Enter no hace nada especial.\n * - ↑ y ↓ pasan a la misma columna de la fila de arriba o de abajo.\n * - ← pasa a la celda de la izquierda si el texto está vacío o el cursor está al principio.\n * - → pasa a la de la derecha si el texto está vacío, o si la tecla anterior también fue → y el cursor está al final\n * (hace falta apretarla dos veces: la primera solo llega al final).\n * Con Ctrl, Alt o Meta ninguna tecla deja la celda.\n */\nexport function direccionDeSalida(tecla: TeclaPulsada, cursor: PosicionDelCursor, teclaAnterior: string | null): DireccionDeSalida | null {\n if (tecla.ctrlKey || tecla.altKey || tecla.metaKey) return null;\n const conShift = !!tecla.shiftKey;\n switch (tecla.key) {\n case 'Enter': return conShift ? null : 'siguiente';\n case 'Tab': return conShift ? 'anterior' : 'siguiente';\n case 'ArrowUp': return conShift ? null : 'arriba';\n case 'ArrowDown': return conShift ? null : 'abajo';\n case 'ArrowLeft': return !conShift && (cursor.vacio || cursor.alPrincipio) ? 'izquierda' : null;\n case 'ArrowRight': return !conShift && (cursor.vacio || (teclaAnterior === 'ArrowRight' && cursor.alFinal)) ? 'derecha' : null;\n default: return null;\n }\n}\n\n/** La posición del cursor en un campo de texto (o, si el elemento no es uno, la de un texto vacío) */\nexport function posicionDelCursor(elemento: EventTarget | null): PosicionDelCursor {\n if (elemento instanceof HTMLInputElement || elemento instanceof HTMLTextAreaElement) {\n // como backend-plus, se mira donde termina la selección\n const fin = elemento.selectionEnd ?? elemento.value.length;\n return { vacio: elemento.value === '', alPrincipio: fin === 0, alFinal: fin === elemento.value.length };\n }\n return { vacio: true, alPrincipio: true, alFinal: true };\n}\n","// Qué hace un fixedField con su columna, igual que `TableConnector` de backend-plus (my-tables.js): un fixedField con\n// `range` o con `until` no fija el valor (la columna sigue siendo una columna común y se puede filtrar); uno que fija el\n// valor oculta su columna, salvo que declare `show`.\nimport { FixedField } from '../types';\n\n/** ¿El fixedField fija el valor de su columna? (los de `range` y `until` son un rango: no) */\nexport function fijaElValor(fixedField: FixedField): boolean {\n return !fixedField.range && fixedField.until === undefined;\n}\n\n/** El fixedField que fija el valor de la columna, si lo hay */\nexport function valorFijoDe(fixedFields: FixedField[] | undefined, columna: string): FixedField | undefined {\n return fixedFields?.find(f => f.fieldName === columna && fijaElValor(f));\n}\n\n/** ¿La columna tiene un valor fijo y no se muestra? (`show` la deja a la vista) */\nexport function columnaOcultaPorFixedField(fixedFields: FixedField[] | undefined, columna: string): boolean {\n const fijo = valorFijoDe(fixedFields, columna);\n return fijo !== undefined && !fijo.show;\n}\n","import { executeBackendProcedure } from './fetchApi';\nimport { requerido } from './errores';\nimport { FieldDefinition, FilaDeDatos, TableDefinition } from '../types';\n\n/**\n * El «expander» de referencias de backend-plus (F9 o Alt+↓ en una celda con FK o softFK, `myOwn.ExpanderReferences` en my-tables.js):\n * un menú con las filas de la tabla referenciada (su clave y sus campos `isName`) para elegir el valor de la celda.\n */\n\n/** Par origen → destino de una FK (`source` es la columna de esta tabla, `target` la de la referenciada) */\nexport type ParDeCampos = { source: string, target: string };\n\n/**\n * Un campo con `references`. `tableDefAdapt` (backend-plus) agrega `referencesFields` al campo origen de cada FK y softFK; un campo\n * que declara `references` a mano (como las asignaciones de dmencu) no lo tiene. `backend-plus.d.ts` no declara `referencesFields` ni\n * `skipReferenceLookup`, que el cliente de backend-plus usa.\n */\nexport type CampoConReferencia = FieldDefinition & { references: string, referencesFields?: ParDeCampos[], skipReferenceLookup?: boolean };\n\nconst esParDeCampos = (par: unknown): par is ParDeCampos =>\n typeof par === 'object' && par !== null && 'source' in par && 'target' in par && typeof par.source === 'string' && typeof par.target === 'string';\n\n/** El campo tiene menú de elección (como `ExpanderReferences.whenType`: tiene `references` y no pide saltear el lookup) */\nexport const esCampoConReferencia = (campo: FieldDefinition): campo is CampoConReferencia =>\n typeof campo.references === 'string' && campo.references !== '' &&\n (!('referencesFields' in campo) || campo.referencesFields === undefined || Array.isArray(campo.referencesFields) && campo.referencesFields.every(esParDeCampos)) &&\n !('skipReferenceLookup' in campo && campo.skipReferenceLookup);\n\n/** Los campos con los que se nombra una fila de la tabla: `lookupFields` si los hay y, si no, los `isName` (`nameFields`) */\nexport const camposDeNombre = (definicion: TableDefinition): string[] => {\n const comoTextos = (campos: unknown): string[] => Array.isArray(campos) ? campos.filter((c): c is string => typeof c === 'string') : [];\n if (definicion.lookupFields && definicion.lookupFields.length > 0) return definicion.lookupFields;\n return 'nameFields' in definicion ? comoTextos(definicion.nameFields) : [];\n};\n\n/** Nombre del campo que dice si la fila está activa (`activeRecords.fieldName`), si la tabla lo declara */\nconst campoDeActivo = (definicion: TableDefinition): string | null =>\n 'activeRecords' in definicion && typeof definicion.activeRecords === 'object' && definicion.activeRecords !== null &&\n 'fieldName' in definicion.activeRecords && typeof definicion.activeRecords.fieldName === 'string'\n ? definicion.activeRecords.fieldName : null;\n\nexport type OpcionDeReferencia = {\n valor: unknown,\n /** Lo que se muestra de la fila: las columnas de la clave y los campos de nombre */\n etiquetas: string[],\n /** Con qué se ordena y contra qué se busca */\n texto: string,\n};\n\nconst comoTexto = (valor: unknown): string => valor === null || valor === undefined ? '' : String(valor);\n\n/**\n * Las opciones del menú de un campo con referencia, como `ExpanderReferences.dialogInput` de backend-plus:\n * - sin las filas inactivas (`activeRecords`);\n * - en una FK compuesta, solo las filas que coinciden con los valores de la fila en las columnas anteriores a la última;\n * - con el valor de la última columna de la referencia; la etiqueta lleva la clave y los campos de nombre;\n * - ordenadas por esa etiqueta y, si el campo admite nulo, con la opción vacía al final.\n */\nexport const opcionesDeReferencia = (campo: CampoConReferencia, definicionReferenciada: TableDefinition, filasReferenciadas: FilaDeDatos[], filaActual: FilaDeDatos, admiteNulo: boolean): OpcionDeReferencia[] => {\n // sin referencesFields (un campo que declara references a mano) el valor es la última columna de la clave, como en backend-plus\n const pares = campo.referencesFields ?? [];\n const columnaDelValor = pares.length > 0 ? pares[pares.length - 1].target : definicionReferenciada.primaryKey[definicionReferenciada.primaryKey.length - 1];\n const anteriores = pares.slice(0, -1);\n const activo = campoDeActivo(definicionReferenciada);\n const nombres = camposDeNombre(definicionReferenciada);\n const columnasDeEtiqueta = nombres.length > 0 ? [...definicionReferenciada.primaryKey, ...nombres] : definicionReferenciada.primaryKey;\n const opciones: OpcionDeReferencia[] = filasReferenciadas\n .filter(fila => (activo === null || !!fila[activo]) && anteriores.every(par => comoTexto(fila[par.target]) === comoTexto(filaActual[par.source])))\n .map(fila => {\n const etiquetas = columnasDeEtiqueta.map(columna => comoTexto(fila[columna]));\n return { valor: fila[columnaDelValor], etiquetas, texto: `${etiquetas.join(', ')}; ${definicionReferenciada.primaryKey.join(', ')}` };\n });\n const ordenar = new Intl.Collator('es', { numeric: true, sensitivity: 'base' });\n opciones.sort((a, b) => ordenar.compare(a.texto, b.texto));\n if (admiteNulo) opciones.push({ valor: null, etiquetas: ['', '(vacío)'], texto: '' });\n return opciones;\n};\n\n/** Las opciones que contienen lo buscado (sin distinguir mayúsculas ni tildes; las palabras sueltas se buscan por separado). La opción vacía no se filtra */\nexport const filtrarOpciones = (opciones: OpcionDeReferencia[], busqueda: string): OpcionDeReferencia[] => {\n const simplificar = (texto: string) => texto.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();\n const palabras = simplificar(busqueda).split(/\\s+/).filter(p => p !== '');\n if (palabras.length === 0) return opciones;\n return opciones.filter(opcion => opcion.valor === null || palabras.every(p => simplificar(opcion.etiquetas.join(' ')).includes(p)));\n};\n\nexport type ReferenciaCargada = { definicion: TableDefinition, filas: FilaDeDatos[] };\n\nconst VIGENCIA_DE_REFERENCIA_MS = 2 * 60 * 1000;\nconst referencias = new Map<string, { cargada: Promise<ReferenciaCargada>, cuando: number }>();\n\n/**\n * La definición y las filas de la tabla referenciada. Se guardan un rato: elegir de a varias celdas seguidas no pide la tabla cada vez\n * (backend-plus las guarda hasta recargar la página; acá vencen, así una tabla que se edita en otra pestaña se ve al rato).\n */\nexport const cargarReferencia = (tabla: string): Promise<ReferenciaCargada> => {\n const guardada = referencias.get(tabla);\n if (guardada && Date.now() - guardada.cuando < VIGENCIA_DE_REFERENCIA_MS) return guardada.cargada;\n const cargada = (async (): Promise<ReferenciaCargada> => {\n const [definicion, filas] = await Promise.all([\n executeBackendProcedure<TableDefinition>('table_structure', { table: tabla }),\n executeBackendProcedure<FilaDeDatos[]>('table_data', { table: tabla, fixedFields: [] }),\n ]);\n return { definicion: requerido(definicion, `la estructura de '${tabla}'`), filas: requerido(filas, `los datos de '${tabla}'`) };\n })();\n referencias.set(tabla, { cargada, cuando: Date.now() });\n cargada.catch(() => { if (referencias.get(tabla)?.cargada === cargada) referencias.delete(tabla); });\n return cargada;\n};\n\n/** Para los tests: olvidar lo guardado */\nexport const olvidarReferencias = (): void => referencias.clear();\n","import { FieldDefinition } from '../types';\nimport { esCampoConReferencia, OpcionDeReferencia } from './referencias';\n\n/**\n * Los «expanders» de backend-plus (`TypedControls.Expanders`, que se abren con F9 o Alt+↓ en una celda): según el tipo del campo,\n * un menú, un calendario o una ventana para ver o editar el contenido. Acá se decide cuál corresponde y se arman sus datos.\n */\nexport type TipoDeExpander = 'referencia' | 'booleano' | 'opciones' | 'fecha' | 'texto' | 'json';\n\n/**\n * Cuál es el expander del campo, en el orden en que backend-plus los prueba: JSON de solo lectura, referencias, booleano, `options`, fecha\n * y, para cualquier campo que no sea clientSide, la ventana de texto. En una celda que no se puede editar solo valen el JSON y el texto\n * (`whenReadOnly`): sirven para ver el contenido completo.\n * Un jsonb editable usa la ventana de texto: el expander de JSON de backend-plus intenta editarlo con una variable sin definir.\n */\nexport const tipoDeExpander = (campo: FieldDefinition, editable: boolean): TipoDeExpander | null => {\n if (campo.typeName === 'jsonb' && !editable) return 'json';\n if (editable) {\n if (esCampoConReferencia(campo)) return 'referencia';\n if (campo.typeName === 'boolean') return 'booleano';\n if (campo.options && campo.options.length > 0) return 'opciones';\n if (campo.typeName === 'date') return 'fecha';\n }\n return campo.clientSide ? null : 'texto';\n};\n\nconst SI_NO_VACIO = { si: 'Sí', no: 'No', vacio: '(vacío)' };\n\n/** Sí, No y, si el campo admite nulo, vacío (el menú del expander de booleanos) */\nexport const opcionesDeBooleano = (admiteNulo: boolean): OpcionDeReferencia[] => {\n const opciones: OpcionDeReferencia[] = [\n { valor: true, etiquetas: [SI_NO_VACIO.si], texto: SI_NO_VACIO.si },\n { valor: false, etiquetas: [SI_NO_VACIO.no], texto: SI_NO_VACIO.no },\n ];\n if (admiteNulo) opciones.push({ valor: null, etiquetas: ['', SI_NO_VACIO.vacio], texto: '' });\n return opciones;\n};\n\n/** El menú del expander de `options`: cada opción con su valor y, si la tiene, su etiqueta; sin ordenar (en el orden que declaró el backend) */\nexport const opcionesDeEnumerado = (opciones: NonNullable<FieldDefinition['options']>, admiteNulo: boolean): OpcionDeReferencia[] => {\n const resultado: OpcionDeReferencia[] = opciones.map(opcion => typeof opcion === 'string'\n ? { valor: opcion, etiquetas: [opcion], texto: opcion }\n : { valor: opcion.option, etiquetas: [String(opcion.option), opcion.label], texto: `${opcion.option} ${opcion.label}` });\n if (admiteNulo) resultado.push({ valor: null, etiquetas: ['', SI_NO_VACIO.vacio], texto: '' });\n return resultado;\n};\n\nexport type DiaDelCalendario = { anio: number, mes: number, dia: number, delMesMostrado: boolean };\n\n/** Año, mes (0 a 11) y día */\nexport type FechaSimple = { anio: number, mes: number, dia: number };\n\n/** Lo que vale como fecha en una celda (un `Date` o un texto `AAAA-MM-DD`); null si no es una fecha */\nexport const fechaDeValor = (valor: unknown): FechaSimple | null => {\n if (valor instanceof Date && !isNaN(valor.getTime())) return { anio: valor.getFullYear(), mes: valor.getMonth(), dia: valor.getDate() };\n if (typeof valor === 'string') {\n const partes = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(valor);\n if (partes) return { anio: Number(partes[1]), mes: Number(partes[2]) - 1, dia: Number(partes[3]) };\n }\n return null;\n};\n\n/** La fecha como la entiende el editor de la celda (`AAAA-MM-DD`) */\nexport const fechaATexto = ({ anio, mes, dia }: FechaSimple): string =>\n `${String(anio).padStart(4, '0')}-${String(mes + 1).padStart(2, '0')}-${String(dia).padStart(2, '0')}`;\n\n/** Las semanas (lunes a domingo) que muestra el calendario de un mes: completas, con los días del mes anterior y del siguiente que las rellenan */\nexport const semanasDelMes = (anio: number, mes: number): DiaDelCalendario[][] => {\n const primero = new Date(anio, mes, 1);\n const desfase = (primero.getDay() + 6) % 7; // lunes = 0\n const diasDelMes = new Date(anio, mes + 1, 0).getDate();\n const semanas: DiaDelCalendario[][] = [];\n for (let celda = 0; celda < Math.ceil((desfase + diasDelMes) / 7) * 7; celda++) {\n const fecha = new Date(anio, mes, 1 - desfase + celda);\n const dia: DiaDelCalendario = { anio: fecha.getFullYear(), mes: fecha.getMonth(), dia: fecha.getDate(), delMesMostrado: fecha.getMonth() === mes };\n if (celda % 7 === 0) semanas.push([]);\n semanas[semanas.length - 1].push(dia);\n }\n return semanas;\n};\n\n/** Lo que se muestra de un valor en la ventana de solo lectura: un JSON con sangría, o el texto tal cual */\nexport const textoParaVer = (valor: unknown): string => {\n if (valor === null || valor === undefined) return '';\n if (typeof valor === 'object' && !(valor instanceof Date)) return JSON.stringify(valor, null, 2);\n return String(valor);\n};\n","// src/hooks/grid/useGridLayout.ts\nimport { useMemo } from 'react';\nimport { SummaryData } from './useGridDataView';\nimport { FilaDeDatos, FilaDeFiltros, FilaDeTotales, FiltrosDeGrilla } from '../../types';\nimport { FILTER_ROW_HEIGHT, HEADER_ROW_HEIGHT, MIN_BODY_HEIGHT, ROW_HEIGHT, SUMMARY_ROW_HEIGHT } from '../../components/grid/GenericDataGrid';\n\ninterface UseGridLayoutProps {\n filteredRows: FilaDeDatos[];\n isFilterRowVisible: boolean;\n tableData: FilaDeDatos[];\n summaryData: SummaryData;\n filters: FiltrosDeGrilla;\n setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;\n /** Alto que agregan las filas de detalle desplegadas */\n altoDetalles?: number;\n /** Alto mínimo cuando no hay filas para mostrar (las grillas anidadas usan uno menor) */\n altoMinimoCuerpo?: number;\n /** Espacio extra debajo de la grilla (barra de scroll, margen) */\n holgura?: number;\n /**\n * Cuántas filas de datos se muestran a la vez antes de scrollear por dentro (y virtualizar):\n * la grilla crece según su contenido hasta ese tope. Sin valor, crece sin límite.\n */\n filasVisiblesMax?: number;\n /** Si la grilla muestra la fila de pie (totales); sin ella no se reserva su alto */\n conPie?: boolean;\n}\n\nexport const useGridLayout = ({\n filteredRows,\n isFilterRowVisible,\n tableData,\n summaryData,\n filters,\n setFilters,\n altoDetalles = 0,\n altoMinimoCuerpo = MIN_BODY_HEIGHT,\n // 24 px de margen inferior de la caja que contiene la grilla + lo que ocupa una barra de scroll horizontal (que le quita\n // alto a la grilla y, si no sobra lugar, hace aparecer una barra vertical aunque haya una sola fila)\n holgura = 24 + 24,\n filasVisiblesMax,\n conPie = true,\n}: UseGridLayoutProps) => {\n\n const gridHeight = useMemo(() => {\n const filterHeight = isFilterRowVisible ? FILTER_ROW_HEIGHT : 0;\n const filasParaAlto = filasVisiblesMax === undefined ? filteredRows.length : Math.min(filteredRows.length, filasVisiblesMax);\n let calculatedHeight = (filasParaAlto * ROW_HEIGHT) + altoDetalles + HEADER_ROW_HEIGHT + filterHeight + (conPie ? SUMMARY_ROW_HEIGHT : 0) + holgura;\n if (tableData.length > 0 && filteredRows.length === 0 && isFilterRowVisible || tableData.length === 0) {\n calculatedHeight = Math.max(calculatedHeight, altoMinimoCuerpo) + filterHeight;\n }\n return calculatedHeight;\n }, [filteredRows.length, isFilterRowVisible, tableData.length, altoDetalles, altoMinimoCuerpo, holgura, filasVisiblesMax, conPie]);\n\n const bottomSummaryRow = useMemo((): FilaDeTotales => {\n return { id: 'bottomSummaryRow', totales: summaryData };\n }, [summaryData]);\n\n const summaryRows = useMemo((): FilaDeFiltros[] | undefined => {\n if (!isFilterRowVisible) return undefined;\n return [{ id: 'filterRow', filters, setFilters }];\n }, [isFilterRowVisible, filters, setFilters]);\n\n const filterHeight = isFilterRowVisible ? FILTER_ROW_HEIGHT : 0;\n\n return { gridHeight, bottomSummaryRow, summaryRows, filterHeight };\n};\n","// src/hooks/grid/useGridMenu.ts\nimport React, { useMemo, useCallback, useState } from 'react';\nimport RefreshIcon from '@mui/icons-material/Refresh';\nimport ViewColumnIcon from '@mui/icons-material/ViewColumn';\nimport VisibilityOffIcon from '@mui/icons-material/VisibilityOff';\nimport FileDownloadIcon from '@mui/icons-material/FileDownload';\nimport FileUploadIcon from '@mui/icons-material/FileUpload';\nimport DeleteSweepIcon from '@mui/icons-material/DeleteSweep';\nimport FilterAltIcon from '@mui/icons-material/FilterAlt';\nimport { useSnackbar } from '../../contexts/SnackbarContext';\nimport { DataGridOption, FilaDeDatos, TableDefinition, FixedField, UseApiCallResult } from '../../types';\nimport { mensajeDeError, requerido } from '../../utils/errores';\n\ninterface UseGridMenuProps {\n tableDefinition: TableDefinition | null;\n tableName: string;\n fixedFields?: FixedField[];\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;\n callApi: UseApiCallResult['callApi'];\n resetSorting?: () => void;\n triggerImport?: () => void;\n triggerExport?: () => void;\n}\n\nexport const useGridMenu = ({\n tableDefinition,\n tableName,\n fixedFields,\n setTableData,\n callApi,\n resetSorting,\n triggerImport,\n triggerExport,\n}: UseGridMenuProps) => {\n\n const { showSuccess, showError, showWarning } = useSnackbar();\n\n const menuOptions = useMemo((): DataGridOption[] => {\n if (!tableDefinition) return [];\n\n const options: DataGridOption[] = [];\n\n // Refrescar (siempre visible)\n options.push({\n id: 'refresh',\n label: 'refrescar la grilla desde la base de datos',\n icon: React.createElement(RefreshIcon),\n handler: async () => {\n try {\n const data = requerido(await callApi<FilaDeDatos[]>('table_data', { table: tableName, fixedFields }), 'los datos de la tabla');\n setTableData(data);\n if (resetSorting) resetSorting();\n showSuccess('Grilla refrescada correctamente');\n } catch (err) {\n showError(`Error al refrescar: ${mensajeDeError(err, 'Error desconocido')}`);\n }\n },\n visible: true\n });\n\n // Mostrar columnas relacionadas\n options.push({\n id: 'showColumns',\n label: 'mostrar las columnas relacionadas',\n icon: React.createElement(ViewColumnIcon),\n handler: async () => { console.log('Mostrar columnas relacionadas'); },\n visible: true\n });\n\n // Ocultar/mostrar columnas\n options.push({\n id: 'hideColumns',\n label: 'ocultar o mostrar columnas',\n icon: React.createElement(VisibilityOffIcon),\n handler: async () => { console.log('Ocultar/mostrar columnas'); },\n visible: true\n });\n\n if (tableDefinition.allow?.export) {\n options.push({\n id: 'export',\n label: 'exportar',\n icon: React.createElement(FileDownloadIcon),\n handler: async () => {\n if (triggerExport) triggerExport();\n else showWarning('La funcionalidad de exportación no está disponible en este momento.');\n },\n visible: true\n });\n }\n\n if (tableDefinition.allow?.import) {\n options.push({\n id: 'import',\n label: 'importar (Excel/Tab)',\n icon: React.createElement(FileUploadIcon),\n handler: async () => {\n if (triggerImport) triggerImport();\n else showWarning('La funcionalidad de importación no está disponible en este momento.');\n },\n visible: true\n });\n }\n\n if (tableDefinition.allow?.delete) {\n options.push({\n id: 'deleteAll',\n label: 'borrar todos los registros',\n icon: React.createElement(DeleteSweepIcon),\n handler: async () => {\n showWarning('Esta operación eliminará TODOS los registros de la tabla');\n },\n visible: true\n });\n }\n\n options.push({\n id: 'completeTable',\n label: 'tabla completa y filtrada',\n icon: React.createElement(FilterAltIcon),\n handler: async () => { console.log('Toggle tabla completa/filtrada'); },\n visible: true\n });\n\n return options;\n }, [tableDefinition, tableName, fixedFields, setTableData, callApi, resetSorting, triggerImport, triggerExport, showSuccess, showError, showWarning]);\n\n return { menuOptions };\n};\n","// src/hooks/grid/useGridColumns.ts\nimport React, { useMemo } from 'react';\nimport { RenderCellProps, RenderHeaderCellProps, RenderSummaryCellProps, RenderEditCellProps, ColSpanArgs } from 'react-data-grid';\nimport { cambiarGuionesBajosPorEspacios } from '../../utils/functions';\nimport { FieldDefinition, FilaDeDatos, FilaDeResumen, FixedField, TableDefinition } from '../../types';\nimport { ColumnaDeGrilla, CustomColumn, DefaultColumn, DetailColumn, ActionColumn } from '../../components/grid/GenericDataGrid';\nimport { ActionsColumnHeaderCell, defaultColumnHeaderCellRenderer, detailColumnCellHeaderRenderer } from '../../components/grid/renderers/headerCellRenderers';\nimport { actionsColumnSummaryCellRenderer, defaultColumnSummaryCellRenderer, detailColumnCellSummaryRenderer } from '../../components/grid/renderers/summaryCellRenderers';\nimport { allColumnsCellRenderer } from '../../components/grid/renderers/cellRenderers';\nimport { allColumnsEditCellRenderer } from '../../components/grid/renderers/editCellRenderers';\nimport { isNumericType, typerFrom } from '../../components/grid/utils/helpers';\nimport { esFilaDetalle } from '../../components/grid/detalles';\nimport { columnaOcultaPorFixedField, valorFijoDe } from '../../utils/fixedFields';\n\nconst estimateTextWidth = (text: string): number => text.length * 8 + 16;\n\n/**\n * Calcula el ancho de cada columna a partir del título y de una muestra de filas.\n * Se llama una sola vez por carga de datos (no en cada render).\n */\nexport const calcularAnchosDeColumnas = (tableDefinition: TableDefinition, filas: Record<string, unknown>[]): Map<string, number> => {\n const anchos = new Map<string, number>();\n const muestra = filas.slice(0, 50);\n tableDefinition.fields.forEach((fieldDef: FieldDefinition) => {\n // como backend-plus (my-tables.js): un ancho que declara el backend para el campo (`width`, en píxeles) manda sobre el calculado\n if (typeof fieldDef.width === 'number' && fieldDef.width > 0) {\n anchos.set(fieldDef.name, fieldDef.width);\n return;\n }\n const headerLabel = fieldDef.label || fieldDef.name;\n const type = fieldDef.typeName?.toLowerCase() || '';\n let typeMinWidth = 60;\n if (type.includes('boolean') || type.includes('checkbox')) typeMinWidth = 60;\n else if (type.includes('date') || type.includes('timestamp')) typeMinWidth = 110;\n else if (type.includes('integer') || type.includes('numeric')) typeMinWidth = 80;\n\n const headerWidth = estimateTextWidth(headerLabel) + 20;\n const typer = typerFrom(fieldDef);\n const maxDataWidth = muestra.reduce((maxW, row) => {\n const valor = row[fieldDef.name];\n if (valor === null || valor === undefined) return maxW;\n let texto: string;\n try { texto = typer.toLocalString(valor); }\n catch { texto = String(valor); }\n return Math.max(maxW, estimateTextWidth(texto));\n }, 0);\n anchos.set(fieldDef.name, Math.min(Math.max(headerWidth, maxDataWidth, typeMinWidth), 500));\n });\n return anchos;\n};\n\ninterface UseGridColumnsProps {\n tableDefinition: TableDefinition | null;\n anchosCalculados: Map<string, number>;\n fixedFields?: FixedField[];\n primaryKey: string[];\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;\n handleKeyPressInEditor: DefaultColumn['handleKeyPressInEditor'];\n registrarEditor: DefaultColumn['registrarEditor'];\n handleDeleteRow: (row: FilaDeDatos) => void;\n handleAddRow: (row?: FilaDeDatos) => void;\n handleVerticalEditRow: (row: FilaDeDatos) => void;\n toggleFilterVisibility: () => void;\n abrirOpcionesDeGrilla: (anchorEl: HTMLElement) => void;\n}\n\n/**\n * Arma las columnas de la grilla.\n * Las dependencias son solo las que cambian la estructura (definición, campos fijos, anchos calculados\n * y callbacks estables). Los datos, el feedback por celda, el orden y la fila de filtros NO están acá:\n * si estuvieran, cada edición recrearía todas las columnas y react-data-grid redibujaría toda la grilla.\n */\nexport const useGridColumns = ({\n tableDefinition,\n anchosCalculados,\n fixedFields,\n primaryKey,\n setTableData,\n handleKeyPressInEditor,\n registrarEditor,\n handleDeleteRow,\n handleAddRow,\n handleVerticalEditRow,\n toggleFilterVisibility,\n abrirOpcionesDeGrilla,\n}: UseGridColumnsProps) => {\n\n const columns: CustomColumn[] = useMemo(() => {\n if (!tableDefinition) return [];\n const fieldsToShow = tableDefinition.fields.filter((field: FieldDefinition) => {\n // como backend-plus: el campo fijo oculta su columna salvo que declare `show`; los de rango (range, until) no la ocultan\n return !columnaOcultaPorFixedField(fixedFields, field.name) && field.visible !== false;\n });\n\n const defaultColumns: CustomColumn[] = fieldsToShow.map((fieldDef: FieldDefinition) => {\n const isFixedField = valorFijoDe(fixedFields, fieldDef.name) !== undefined;\n const isFieldEditable = fieldDef.editable !== false && !isFixedField;\n const colWidth = anchosCalculados.get(fieldDef.name) ?? estimateTextWidth(fieldDef.label || fieldDef.name) + 20;\n const column: DefaultColumn = {\n key: fieldDef.name,\n customType: 'default',\n tableDefinition,\n fieldDef,\n typer: typerFrom(fieldDef),\n isMandatory: tableDefinition.primaryKey.includes(fieldDef.name) || fieldDef.nullable === false,\n isNumeric: isNumericType(fieldDef.typeName),\n primaryKey,\n fixedFields,\n setTableData,\n name: fieldDef.label || cambiarGuionesBajosPorEspacios(fieldDef.name),\n resizable: true,\n sortable: true,\n editable: isFieldEditable,\n handleKeyPressInEditor,\n registrarEditor,\n width: colWidth,\n minWidth: 40,\n renderHeaderCell: (props: RenderHeaderCellProps<FilaDeDatos, FilaDeResumen>) => defaultColumnHeaderCellRenderer(props, fieldDef),\n renderSummaryCell: (props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => defaultColumnSummaryCellRenderer(props, fixedFields),\n };\n return column;\n });\n\n const availableActions = [\n tableDefinition.allow?.insert,\n tableDefinition.allow?.delete,\n tableDefinition.allow?.['vertical-edit']\n ].filter(Boolean).length;\n const actionColumnWidth = 60 + (availableActions * 10);\n\n const actionsColumn: ActionColumn = {\n key: 'actionsColumn',\n customType: 'action',\n tableDefinition,\n handleDeleteRow,\n handleAddRow,\n handleVerticalEditRow,\n name: 'filterCol',\n width: actionColumnWidth,\n editable: false,\n resizable: false,\n sortable: false,\n renderHeaderCell: () => React.createElement(ActionsColumnHeaderCell, {\n toggleFilterVisibility,\n handleDataGridOptions: (e: React.MouseEvent<HTMLElement>) => abrirOpcionesDeGrilla(e.currentTarget),\n handleAddRow,\n allowInsert: tableDefinition.allow?.insert,\n }),\n renderSummaryCell: (props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => actionsColumnSummaryCellRenderer(props),\n };\n\n const detailColumns: CustomColumn[] = (tableDefinition.detailTables ?? []).map(detailTable => {\n const detailKey = `detail_${detailTable.abr}`;\n const column: DetailColumn = {\n key: detailKey,\n customType: 'detail',\n tableDefinition,\n detailTable,\n primaryKey,\n detailKey,\n name: detailTable.label || `Detalle ${detailTable.abr}`,\n resizable: false,\n sortable: false,\n editable: false,\n width: 30,\n minWidth: 30,\n renderHeaderCell: (props: RenderHeaderCellProps<FilaDeDatos, FilaDeResumen>) => detailColumnCellHeaderRenderer(props, detailTable),\n renderSummaryCell: (props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => detailColumnCellSummaryRenderer(props),\n };\n return column;\n });\n\n const allColumns: CustomColumn[] = [actionsColumn, ...detailColumns, ...defaultColumns];\n const cantidadDeColumnas = allColumns.length;\n // En una fila de detalle la columna de acciones ocupa todo el ancho y muestra la grilla anidada\n const colSpanDetalle = (args: ColSpanArgs<FilaDeDatos, FilaDeResumen>) => args.type === 'ROW' && esFilaDetalle(args.row) ? cantidadDeColumnas : undefined;\n return allColumns.map(col => ({\n ...col,\n colSpan: col.customType === 'action' ? colSpanDetalle : col.colSpan,\n // el editor guarda por su cuenta (Enter, Tab, flechas y, al perder el foco, el clic afuera): con commitOnOutsideClick react-data-grid\n // lo cierra sin guardar y los menús (F9), que son un modal, cuentan como «afuera»\n editorOptions: { closeOnExternalRowChange: false, commitOnOutsideClick: false },\n renderEditCell: (props: RenderEditCellProps<FilaDeDatos, FilaDeResumen>) => allColumnsEditCellRenderer(props, allColumns as ColumnaDeGrilla[]),\n renderCell: (props: RenderCellProps<FilaDeDatos, FilaDeResumen>) => allColumnsCellRenderer(props),\n }));\n\n }, [tableDefinition, anchosCalculados, fixedFields, primaryKey, setTableData, handleKeyPressInEditor, registrarEditor, handleDeleteRow, handleAddRow, handleVerticalEditRow, toggleFilterVisibility, abrirOpcionesDeGrilla]);\n\n return { columns };\n};\n","import * as JSON4all from \"json4all\";\r\nimport { store } from '../store';\r\nimport { TableDefinition } from \"backend-plus\";\r\nimport { FixedField } from \"../types\";\r\n\r\nexport const cambiarGuionesBajosPorEspacios = (texto:string)=>texto.replace(/_/g,' ');\r\nexport const quitarGuionesBajos = (texto:string)=>texto.replace(/_/g,'');\r\n\r\nconst getAppPrefix = () => {\r\n const state = store.getState();\r\n const { config } = state.clientContext;\r\n return /*config.appName+*/'prefijo_'+ config.version+'_';\r\n}\r\n\r\nconst getRawLocalVar = (varName: string) =>localStorage.getItem(getAppPrefix()+varName);\r\n\r\nconst getLocalVar = (varName: string) =>{\r\n let rawData = getRawLocalVar(varName);\r\n if(rawData){\r\n return JSON4all.parse(rawData);\r\n }else{\r\n return null\r\n }\r\n}\r\n\r\nconst setLocalVar = (varName: string, value: unknown) => localStorage.setItem(getAppPrefix()+varName, JSON4all.stringify(value))\r\n\r\n\r\nconst existsLocalVar = (varName: string) => !!getRawLocalVar(varName);\r\n\r\n\r\nconst removeLocalVar = (varName: string) => localStorage.removeItem(getAppPrefix()+varName);\r\n\r\nconst getSessionVar = (varName: string) =>{\r\n if(existsSessionVar(varName)){\r\n return JSON4all.parse(sessionStorage.getItem(getAppPrefix()+varName)!);\r\n }else{\r\n return null\r\n }\r\n}\r\n\r\nconst setSessionVar = (varName: string, value: unknown) => sessionStorage.setItem(getAppPrefix()+varName, JSON4all.stringify(value))\r\n\r\n\r\nconst existsSessionVar = (varName: string) => !!(sessionStorage.getItem(getAppPrefix()+varName))?true:false\r\n\r\n\r\nconst removeSessionVar = (varName: string) => sessionStorage.removeItem(getAppPrefix()+varName);\r\n","import { Box, IconButton, Tooltip, Typography } from \"@mui/material\";\r\nimport SearchIcon from '@mui/icons-material/Search';\r\nimport SearchOffIcon from '@mui/icons-material/SearchOff';\r\nimport MoreVertIcon from '@mui/icons-material/MoreVert';\r\nimport AddIcon from '@mui/icons-material/Add';\r\nimport { RenderHeaderCellProps } from \"react-data-grid\";\r\nimport { DetailTable } from \"backend-plus\";\r\nimport { FieldDefinition, FilaDeDatos, FilaDeResumen } from \"../../../types\";\r\nimport { useVistaGrilla } from \"../estadoGrilla\";\r\n\r\nexport const defaultColumnHeaderCellRenderer = (props: RenderHeaderCellProps<FilaDeDatos, FilaDeResumen>, fieldDef: FieldDefinition) => {\r\n // react-data-grid ya informa el orden de la columna: no hace falta pasar sortColumns\r\n // (y así las columnas no se recrean cada vez que cambia el orden)\r\n const { column, sortDirection, priority } = props;\r\n const isPrimaryKey = fieldDef.isPk;\r\n\r\n // Lógica de flecha de ordenación (priority viene solo cuando hay más de una columna ordenada)\r\n const sorted = sortDirection !== undefined;\r\n const sortIndex = (priority ?? 1) - 1;\r\n\r\n // Colores degradados según la prioridad\r\n const getSortColor = (index: number) => {\r\n if (index === 0) return '#000000'; // Principal: Negro\r\n if (index === 1) return '#555555'; // Secundario: Gris oscuro\r\n if (index === 2) return '#888888'; // Terciario: Gris medio\r\n return '#AAAAAA'; // Otros: Gris claro\r\n };\r\n\r\n const sortColor = sorted ? getSortColor(sortIndex) : 'transparent';\r\n\r\n return (\r\n <Box\r\n sx={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n padding: '4px',\r\n boxSizing: 'border-box',\r\n height: '100%',\r\n width: '100%',\r\n }}\r\n >\r\n <Typography\r\n variant=\"body2\"\r\n sx={{\r\n fontWeight: 'bold',\r\n textDecoration: isPrimaryKey ? 'underline' : 'none',\r\n fontSize: '0.930rem',\r\n textAlign: 'left',\r\n flexGrow: 1,\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap'\r\n }}\r\n >\r\n {column.name}\r\n </Typography>\r\n {sorted && (\r\n <Box sx={{ color: sortColor, display: 'flex', alignItems: 'center', ml: 0.5, fontSize: '0.7rem' }}>\r\n {sortDirection === 'ASC' ? '\\u25B2' : '\\u25BC'}\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\ninterface ActionsColumnHeaderCellProps {\r\n toggleFilterVisibility: () => void;\r\n handleDataGridOptions: (event: React.MouseEvent<HTMLElement>) => void;\r\n handleAddRow: () => void;\r\n allowInsert?: boolean;\r\n}\r\n\r\n/** Es un componente (y no una función que se llama al armar las columnas) para leer la fila de filtros del contexto */\r\nexport const ActionsColumnHeaderCell = ({\r\n toggleFilterVisibility,\r\n handleDataGridOptions,\r\n handleAddRow,\r\n allowInsert\r\n}: ActionsColumnHeaderCellProps) => {\r\n const { isFilterRowVisible } = useVistaGrilla();\r\n return (\r\n <Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 0.2 }}>\r\n {allowInsert && (\r\n <IconButton\r\n color=\"inherit\"\r\n onClick={() => handleAddRow()}\r\n size=\"small\"\r\n title=\"Añadir registro\"\r\n sx={{ p: 0.25 }}\r\n >\r\n <AddIcon sx={{ fontSize: 18, color: 'success.main' }} />\r\n </IconButton>\r\n )}\r\n <IconButton\r\n color=\"inherit\"\r\n onClick={toggleFilterVisibility}\r\n size=\"small\"\r\n title={isFilterRowVisible ? 'Ocultar filtros' : 'Mostrar filtros'}\r\n sx={{ p: 0.25 }}\r\n >\r\n {isFilterRowVisible ? <SearchOffIcon sx={{ fontSize: 18 }} /> : <SearchIcon sx={{ fontSize: 18 }} />}\r\n </IconButton>\r\n <IconButton\r\n onClick={(event) => handleDataGridOptions(event)}\r\n size=\"small\"\r\n title=\"Opciones de esta tabla\"\r\n sx={{ p: 0.25 }}\r\n >\r\n <MoreVertIcon sx={{ fontSize: 20, variant: \"filled\", size: \"small\" }} />\r\n </IconButton>\r\n </Box>\r\n );\r\n};\r\n\r\nexport const detailColumnCellHeaderRenderer = (props: RenderHeaderCellProps<FilaDeDatos, FilaDeResumen>, detailTable: DetailTable) =>\r\n <Tooltip title={detailTable.label}>\r\n <Typography variant=\"caption\" sx={{ fontSize: '0.75rem', fontWeight: 'bold' }}>\r\n {detailTable.abr.toUpperCase()}\r\n </Typography>\r\n </Tooltip>","// src/components/grid/estadoGrilla.ts\nimport { createContext, useContext, useSyncExternalStore } from 'react';\nimport { CellFeedback } from '../../types';\n\nexport type MapaFeedback = Map<string, CellFeedback>;\nexport type MapaCambiosLocales = Map<string, Set<string>>;\n\ntype Actualizacion<T> = T | ((anterior: T) => T);\n\n/**\n * Estado de una grilla que cambia celda por celda (feedback de guardado y campos pendientes).\n * Vive fuera de React para que un cambio en una celda no obligue a recrear las columnas\n * ni a redibujar toda la grilla: cada celda se suscribe solo a lo suyo.\n */\nexport interface EstadoGrilla {\n obtenerFeedback(): MapaFeedback;\n obtenerCambiosLocales(): MapaCambiosLocales;\n setFeedback(actualizacion: Actualizacion<MapaFeedback>): void;\n setCambiosLocales(actualizacion: Actualizacion<MapaCambiosLocales>): void;\n suscribir(alCambiar: () => void): () => void;\n}\n\nexport function crearEstadoGrilla(): EstadoGrilla {\n let feedback: MapaFeedback = new Map();\n let cambiosLocales: MapaCambiosLocales = new Map();\n const suscriptores = new Set<() => void>();\n const avisar = () => suscriptores.forEach(alCambiar => alCambiar());\n return {\n obtenerFeedback: () => feedback,\n obtenerCambiosLocales: () => cambiosLocales,\n setFeedback(actualizacion) {\n const nuevo = typeof actualizacion === 'function' ? actualizacion(feedback) : actualizacion;\n if (nuevo !== feedback) {\n feedback = nuevo;\n avisar();\n }\n },\n setCambiosLocales(actualizacion) {\n const nuevo = typeof actualizacion === 'function' ? actualizacion(cambiosLocales) : actualizacion;\n if (nuevo !== cambiosLocales) {\n cambiosLocales = nuevo;\n avisar();\n }\n },\n suscribir(alCambiar) {\n suscriptores.add(alCambiar);\n return () => { suscriptores.delete(alCambiar); };\n },\n };\n}\n\nexport const EstadoGrillaContext = createContext<EstadoGrilla | null>(null);\n\nexport function useEstadoGrilla(): EstadoGrilla {\n const estado = useContext(EstadoGrillaContext);\n if (!estado) {\n throw new Error('useEstadoGrilla debe usarse dentro de EstadoGrillaContext.Provider');\n }\n return estado;\n}\n\n/** Feedback de una celda; solo redibuja cuando cambia el de esa celda */\nexport function useFeedbackCelda(cellKey: string): CellFeedback | undefined {\n const estado = useEstadoGrilla();\n return useSyncExternalStore(estado.suscribir, () => estado.obtenerFeedback().get(cellKey));\n}\n\n/** Si la celda está marcada como pendiente de completar (fila nueva sin grabar) */\nexport function useCeldaPendiente(rowId: string, columnKey: string): boolean {\n const estado = useEstadoGrilla();\n return useSyncExternalStore(estado.suscribir, () => estado.obtenerCambiosLocales().get(rowId)?.has(columnKey) ?? false);\n}\n\n/** Estado de la vista que necesitan algunos encabezados (sin pasar por las columnas) */\nexport interface VistaGrilla {\n isFilterRowVisible: boolean;\n}\n\nexport const VistaGrillaContext = createContext<VistaGrilla>({ isFilterRowVisible: false });\n\nexport const useVistaGrilla = () => useContext(VistaGrillaContext);\n\nexport type { Actualizacion };\n","import { InputBase, Box } from \"@mui/material\";\r\nimport React from \"react\";\r\nimport { FieldDefinition, FiltroDeColumna, FiltrosDeGrilla } from \"../../types\";\r\nimport { FILTER_ROW_HEIGHT } from \"./GenericDataGrid\";\r\nimport { TypedField } from \"../forms/TypedField\";\r\nimport { filterOperators, getDefaultOperatorForType } from \"./utils/helpers\";\r\n\r\ninterface FilterInputRendererProps {\r\n columnKey: string;\r\n currentFilter: FiltroDeColumna | undefined;\r\n setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;\r\n fieldDef: FieldDefinition | undefined;\r\n}\r\n\r\nfunction FilterInputRenderer({\r\n columnKey,\r\n currentFilter,\r\n setFilters,\r\n fieldDef\r\n}: FilterInputRendererProps) {\r\n\r\n if (fieldDef) {\r\n // Determinamos estado de operador y valor\r\n let operator = getDefaultOperatorForType(fieldDef.typeName);\r\n let val = '';\r\n if (currentFilter && typeof currentFilter === 'object') {\r\n operator = currentFilter.operator || operator;\r\n val = currentFilter.value || '';\r\n } else if (typeof currentFilter === 'string') {\r\n val = currentFilter;\r\n }\r\n\r\n const handleFilterChange = (newOp: string, newVal: unknown) => {\r\n const formattedVal = (newVal === null || newVal === undefined) ? '' : String(newVal);\r\n setFilters(prev => ({\r\n ...prev,\r\n [columnKey]: { operator: newOp, value: formattedVal }\r\n }));\r\n };\r\n\r\n return (\r\n <Box sx={{ mt: 0, display: 'flex', alignItems: 'center', gap: 0.5, pr: 1 }}>\r\n <select\r\n value={operator}\r\n onChange={(e) => handleFilterChange(e.target.value, val)}\r\n style={{\r\n height: FILTER_ROW_HEIGHT - 5,\r\n minWidth: 40,\r\n backgroundColor: 'white',\r\n border: '1px solid #ccc',\r\n borderRadius: '4px',\r\n fontSize: '0.8rem',\r\n padding: '0 2px',\r\n textAlign: 'center',\r\n cursor: 'pointer'\r\n }}\r\n >\r\n {filterOperators.map(op => (\r\n <option key={op.value} value={op.value}>\r\n {op.label}\r\n </option>\r\n ))}\r\n </select>\r\n <TypedField\r\n fieldDef={fieldDef}\r\n isFilterMode={true}\r\n value={val}\r\n onChange={(_name, newValue, _isValid) => handleFilterChange(operator, newValue)}\r\n />\r\n </Box>\r\n );\r\n }\r\n\r\n // Fallback original por si no hay fieldDef\r\n return (\r\n <InputBase\r\n value={currentFilter || ''}\r\n onChange={(e) =>\r\n setFilters(prev => ({\r\n ...prev,\r\n [columnKey]: e.target.value\r\n }))\r\n }\r\n sx={{\r\n width: '100%',\r\n height: 25,\r\n margin: '0',\r\n border: '1px solid #ccc',\r\n borderRadius: '4px',\r\n padding: '2px 4px',\r\n fontSize: '0.8rem',\r\n boxSizing: 'border-box',\r\n backgroundColor: 'background.paper'\r\n }}\r\n onClick={(e) => e.stopPropagation()}\r\n />\r\n );\r\n}\r\n\r\nexport default React.memo(FilterInputRenderer);\r\n","import React, { useState, useEffect, useMemo, useCallback } from 'react';\nimport {\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n FormHelperText,\n InputAdornment,\n useTheme\n} from '@mui/material';\nimport { FieldDefinition } from '../../types';\nimport typeStore from 'type-store';\nimport type { SelectChangeEvent } from '@mui/material/Select';\nimport { isNumericType, isDateTimeType } from '../grid/utils/helpers';\nimport { useRef } from 'react';\nimport { FILTER_ROW_HEIGHT } from '../grid/GenericDataGrid';\nimport { mensajeDeError } from '../../utils/errores';\n\ninterface TypedFieldProps {\n fieldDef: FieldDefinition;\n value: unknown;\n onChange: (fieldName: string, newValue: unknown, isValid: boolean) => void;\n onCommit?: (fieldName: string, newValue: unknown, isValid: boolean) => void;\n disabled?: boolean;\n isFilterMode?: boolean; // Para usarse en los headers de filtro (más compacto)\n feedback?: 'success' | 'error'; // Indicador de éxito/error proveniente del evento save\n isMandatory?: boolean; \n}\n\nexport const TypedField: React.FC<TypedFieldProps> = ({ fieldDef, value, onChange, onCommit, disabled, isFilterMode = false, feedback, isMandatory = false }) => {\n const theme = useTheme();\n\n const typer = useMemo(() => typeStore.typerFrom(fieldDef), [fieldDef]);\n const isBoolean = fieldDef.typeName === 'boolean';\n\n // Para campos de texto/números/fechas mantenemos un valor en string (local config)\n const [localString, setLocalString] = useState<string>(() => {\n if (isBoolean) return '';\n if (isFilterMode) return value !== null && value !== undefined ? String(value) : '';\n try {\n return value !== null ? typer.toLocalString(value) : '';\n } catch (e) {\n return value === null || value === undefined ? '' : String(value);\n }\n });\n const [errorMsg, setErrorMsg] = useState<string | null>(null);\n const lastValueRef = useRef<unknown>(value);\n const lastCommittedStringRef = useRef<string>(localString); // Para evitar commit sin cambios\n\n // Inicializar el valor visual desde el valor tipado (solo si cambia externamente)\n useEffect(() => {\n if (!isBoolean) {\n // Solo actualizamos localString si el value real cambió desde fuera (no por nuestro propio tipeo)\n if (value !== lastValueRef.current) {\n lastValueRef.current = value;\n if (isFilterMode) {\n setLocalString(value !== null && value !== undefined ? String(value) : '');\n } else {\n try {\n setLocalString(value !== null ? typer.toLocalString(value) : '');\n } catch (e) {\n setLocalString(value === null || value === undefined ? '' : String(value));\n }\n }\n setErrorMsg(null);\n }\n }\n }, [value, typer, isBoolean, isFilterMode]);\n\n const handleTextChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const newValueString = e.target.value;\n setLocalString(newValueString);\n let parsedValue = null;\n let isValid = true;\n let pErrorMsg: string | null = null;\n\n if (isFilterMode) {\n // En modo filtro devolvemos el crudo para permitir \"includes()\" y tipeo parcial\n parsedValue = newValueString;\n } else {\n try {\n parsedValue = typer.fromLocalString(newValueString);\n typer.validateTypedData(parsedValue);\n } catch (err) {\n isValid = false;\n pErrorMsg = mensajeDeError(err);\n }\n }\n\n setErrorMsg(pErrorMsg);\n lastValueRef.current = parsedValue; // Seteamos el ref para que el useEffect no lo pise al re-renderizar\n // Enviamos el parsedValue siempre, pero marcamos si es válido o no\n onChange(fieldDef.name, parsedValue, isValid);\n\n }, [typer, onChange, fieldDef.name, isFilterMode]);\n\n const handleTextCommit = useCallback(() => {\n if (!onCommit) return;\n // No enviar commit si el string no cambió desde el último commit\n if (localString === lastCommittedStringRef.current) return;\n lastCommittedStringRef.current = localString;\n \n let parsedValue = null;\n let isValid = true;\n\n if (isFilterMode) {\n parsedValue = localString;\n } else {\n try {\n parsedValue = typer.fromLocalString(localString);\n typer.validateTypedData(parsedValue);\n } catch (err) {\n isValid = false;\n }\n }\n onCommit(fieldDef.name, parsedValue, isValid);\n }, [onCommit, fieldDef.name, localString, isFilterMode, typer]);\n\n const handleTextKeyDown = useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleTextCommit();\n // Optionally, we could unfocus the target here: e.currentTarget.blur()\n }\n }, [handleTextCommit]);\n\n const handleBooleanChange = useCallback((e: SelectChangeEvent<string>) => {\n const val = e.target.value;\n let finalValue: boolean | null = null;\n if (val === 'true') finalValue = true;\n if (val === 'false') finalValue = false;\n\n let isValid = true;\n let pErrorMsg: string | null = null;\n\n if (finalValue === null && fieldDef.nullable === false) {\n isValid = false;\n pErrorMsg = 'Este campo no puede ser nulo.';\n }\n\n onChange(fieldDef.name, finalValue, isValid);\n if (onCommit) {\n onCommit(fieldDef.name, finalValue, isValid);\n }\n }, [onChange, onCommit, fieldDef]);\n\n const isDisabled = isFilterMode ? false : disabled || fieldDef.editable === false;\n\n if (isBoolean) {\n let boolVal = '';\n if (value === true) boolVal = 'true';\n else if (value === false) boolVal = 'false';\n\n const isEmpty = boolVal === '';\n let defaultBgColor = undefined;\n if (isMandatory && isEmpty && !isFilterMode) {\n defaultBgColor = '#e3f2fd';\n }\n // Feedback styling matching GenericDataGrid\n const cellBackgroundColor = feedback === 'error' ? theme.palette.error.light : feedback === 'success' ? theme.palette.success.light : defaultBgColor;\n const transitionStyle = 'background-color 0.3s ease-in-out';\n\n return (\n <FormControl fullWidth error={!!errorMsg && !isFilterMode} disabled={isDisabled} size=\"small\" margin=\"dense\">\n {!isFilterMode && <InputLabel shrink>{fieldDef.name}</InputLabel>}\n <Select\n value={boolVal}\n label={!isFilterMode ? fieldDef.name : undefined}\n onChange={handleBooleanChange}\n startAdornment={\n isMandatory && isEmpty && !isFilterMode\n ? <InputAdornment position=\"start\" sx={{ color: 'text.secondary', ml: 0.5 }}>*</InputAdornment>\n : undefined\n }\n displayEmpty\n sx={isFilterMode\n ? { height: 25, fontSize: '0.8rem', backgroundColor: 'background.paper' }\n : { \n backgroundColor: cellBackgroundColor, \n transition: transitionStyle,\n }}\n >\n\n {fieldDef.nullable !== false && (\n <MenuItem value=\"\"><em>Nulo / Sin Especificar</em></MenuItem>\n )}\n <MenuItem value=\"true\">Sí (Verdadero)</MenuItem>\n <MenuItem value=\"false\">No (Falso)</MenuItem>\n </Select>\n {errorMsg && !isFilterMode && <FormHelperText>{errorMsg}</FormHelperText>}\n </FormControl>\n );\n\n }\n\n // Determinar el input type nativo en base a las 3 categorias del helper\n let inputType = 'text';\n if (isNumericType(fieldDef.typeName)) {\n inputType = 'number';\n }\n // Las fechas/tiempos y LOS FILTROS se mantienen como \"text\" por ahora para facilitar filtro parcial\n // sin pickers nativos y sin bloqueo de entrada de caracteres no numéricos parciales.\n\n\n\n const isEmptyText = localString === null || localString === undefined || String(localString).trim() === '';\n let defaultTextBgColor = undefined;\n if (isMandatory && isEmptyText && !isFilterMode) {\n defaultTextBgColor = '#e3f2fd';\n }\n\n // Feedback styling matching GenericDataGrid \n const cellBackgroundColor = feedback === 'error' ? theme.palette.error.light : feedback === 'success' ? theme.palette.success.light : defaultTextBgColor;\n const transitionStyle = 'background-color 0.3s ease-in-out';\n\n return (\n <TextField\n fullWidth\n type={inputType}\n label={isFilterMode ? undefined : fieldDef.name}\n variant=\"outlined\"\n size=\"small\"\n margin={isFilterMode ? \"none\" : \"dense\"}\n value={localString}\n onChange={handleTextChange}\n disabled={isDisabled}\n error={!!errorMsg && !isFilterMode}\n helperText={!isFilterMode ? (errorMsg || (isMandatory ? 'Obligatorio' : '')) : undefined}\n InputLabelProps={!isFilterMode ? { shrink: true } : undefined}\n InputProps={!isFilterMode && isMandatory && isEmptyText ? {\n startAdornment: (\n <InputAdornment position=\"start\" sx={{ color: 'text.secondary', mr: 0.5 }}>*</InputAdornment>\n )\n } : {}}\n inputProps={{\n style: {\n textAlign: isNumericType(fieldDef.typeName) ? 'right' : 'left',\n }\n }}\n sx={isFilterMode\n ? {\n '& .MuiOutlinedInput-root': {\n backgroundColor: 'background.paper',\n height: FILTER_ROW_HEIGHT - 5,\n minWidth: 50,\n fontSize: '0.8rem',\n padding: '0px'\n }\n }\n : { \n '& .MuiOutlinedInput-root': { \n backgroundColor: cellBackgroundColor, \n transition: transitionStyle \n },\n '& .MuiOutlinedInput-input': {\n padding: '6px 12px'\n }\n }}\n onBlur={handleTextCommit}\n onKeyDown={handleTextKeyDown}\n />\n );\n\n\n\n};\n","import { RenderSummaryCellProps } from \"react-data-grid\";\r\nimport { FilaDeDatos, FilaDeResumen, FixedField } from \"../../../types\";\r\nimport FilterInputRenderer from \"../FilterInputRender\";\r\nimport { esColumnaPropia } from \"../GenericDataGrid\";\r\nimport { Box } from \"@mui/material\";\r\nimport { valorFijoDe } from \"../../../utils/fixedFields\";\r\n\r\n// La fila de filtros solo existe en topSummaryRows cuando está visible, por eso no hace falta recibir isFilterRowVisible\r\nexport const defaultColumnSummaryCellRenderer = (props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>, fixedFields: FixedField[] | undefined) => {\r\n const { column, row } = props;\r\n\r\n // Solo mostramos filtros en la fila dedicada a filtros\r\n if (row.id === 'filterRow') {\r\n const { filters, setFilters } = row; // Extraemos del row para estabilidad de columnas\r\n // Si el campo es fijo, no debe aparecer en los filtros\r\n // El filtro solo se muestra si el campo no tiene un valor fijo (los fixedFields de rango, con 'until' o 'range', sí se filtran)\r\n if (valorFijoDe(fixedFields, column.key)) return null;\r\n\r\n const fieldDef = esColumnaPropia(column) && column.customType === 'default' ? column.fieldDef : undefined;\r\n return (\r\n <Box sx={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center' }}>\r\n <FilterInputRenderer\r\n columnKey={column.key}\r\n currentFilter={filters[column.key]}\r\n setFilters={setFilters}\r\n fieldDef={fieldDef}\r\n />\r\n </Box>\r\n );\r\n }\r\n\r\n // Para la fila de totales (el pie de la grilla):\r\n const summaryData = row.totales[column.key];\r\n if (!summaryData) return null;\r\n\r\n const { type, value } = summaryData;\r\n let label = '';\r\n let formattedValue: string | number = value;\r\n\r\n // Formateo básico para números\r\n if (typeof value === 'number') {\r\n if (type === 'avg') {\r\n formattedValue = value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\r\n } else {\r\n formattedValue = value.toLocaleString();\r\n }\r\n }\r\n\r\n switch (type) {\r\n case 'sum': label = 'Tot: '; break;\r\n case 'avg': label = 'Prom: '; break;\r\n case 'min': label = 'Min: '; break;\r\n case 'max': label = 'Max: '; break;\r\n case 'count': label = 'Cant: '; break;\r\n case 'countTrue': label = 'Cant Sí: '; break;\r\n }\r\n\r\n return (\r\n <Box sx={{\r\n px: 0,\r\n fontWeight: 'bold',\r\n fontSize: '0.875rem',\r\n color: 'text.primary',\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n textAlign: 'right'\r\n }}>\r\n {label}{formattedValue}\r\n </Box>\r\n );\r\n}\r\n\r\nexport const actionsColumnSummaryCellRenderer = (_props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => null\r\n\r\nexport const detailColumnCellSummaryRenderer = (_props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => null","import React from 'react';\r\nimport { RenderCellProps } from 'react-data-grid';\r\nimport { FilaDeDatos, FilaDeResumen } from '../../../types';\r\nimport { Box, Button, Tooltip, useTheme } from '@mui/material';\r\nimport ViewHeadlineIcon from '@mui/icons-material/ViewHeadline';\r\nimport DeleteIcon from '@mui/icons-material/Delete';\r\nimport AddIcon from '@mui/icons-material/Add';\r\nimport { DefaultColumn, esColumnaPropia, NEW_ROW_INDICATOR } from '../GenericDataGrid';\r\nimport { clientSides } from '../clientSides';\r\nimport FallbackClientSideRenderer from '../FallbackClientSideRenderer';\r\nimport { OverridableComponent } from '@mui/material/OverridableComponent';\r\nimport { SvgIconTypeMap } from '@mui/material/SvgIcon';\r\nimport { getPrimaryKeyValues } from '../utils/helpers';\r\nimport { useCeldaPendiente, useFeedbackCelda } from '../estadoGrilla';\r\nimport { BotonDetalle, CeldaDetalle, esFilaDetalle } from '../detalles';\r\nimport { valorFijoDe } from '../../../utils/fixedFields';\r\n\r\n\r\ntype ActionButtonDefinition = {\r\n action: 'insert' | 'delete' | 'vertical-edit'\r\n handler: (row: FilaDeDatos) => void\r\n title: string\r\n icon: OverridableComponent<SvgIconTypeMap<{}, \"svg\">> & { muiName: string }\r\n color: 'success' | 'error' | 'primary'\r\n}\r\n\r\ninterface CeldaDatoProps {\r\n row: FilaDeDatos;\r\n columnKey: string;\r\n defaultColumn: DefaultColumn;\r\n}\r\n\r\n/**\r\n * Celda de datos. Es un componente memorizado: solo se redibuja cuando cambia su fila\r\n * o el feedback/pendiente de esta celda (que lee del EstadoGrilla, no de la columna).\r\n * Se usan elementos y clases simples en lugar de Box/Typography/Tooltip de MUI\r\n * porque hay una por cada celda visible.\r\n */\r\nconst CeldaDato = React.memo(function CeldaDato({ row, columnKey, defaultColumn }: CeldaDatoProps) {\r\n const theme = useTheme();\r\n const { primaryKey, fixedFields, typer, isMandatory, isNumeric } = defaultColumn;\r\n const rowId = getPrimaryKeyValues(row, primaryKey);\r\n const currentCellFeedback = useFeedbackCelda(`${rowId}-${columnKey}`);\r\n const pendiente = useCeldaPendiente(rowId, columnKey);\r\n\r\n const value = row[columnKey];\r\n const hasValue = value !== null && value !== undefined && String(value).trim() !== '';\r\n\r\n let cellBackgroundColor = 'transparent';\r\n if (currentCellFeedback) {\r\n cellBackgroundColor = currentCellFeedback.type === 'error' ? theme.palette.error.light : theme.palette.success.light;\r\n } else if (valorFijoDe(fixedFields, columnKey)) {\r\n cellBackgroundColor = theme.palette.action.selected;\r\n } else if ((isMandatory && !hasValue) || pendiente) {\r\n cellBackgroundColor = '#e3f2fd'; // Celeste premium suave\r\n }\r\n\r\n // TODO: Capturar valor fecha\r\n // Usamos TypeStore para el formateo de visualización\r\n let displayValue: string = value !== null && value !== undefined ? typer.toLocalString(value) : '';\r\n if (!hasValue && isMandatory) {\r\n displayValue = '* ' + displayValue; // Agrega el asterisco si está vacío y es requerido\r\n }\r\n\r\n const signal = displayValue === '--' ? 'typed-controls-signal-no-data' : displayValue === '//' ? 'typed-controls-signal-unknown-data' : '';\r\n const celda = (\r\n <div\r\n className={`fpr-celda ${isNumeric ? 'fpr-celda-numerica' : ''} ${signal} ${row[NEW_ROW_INDICATOR] ? 'new-row-cell-animation' : ''}`}\r\n style={{ backgroundColor: cellBackgroundColor }}\r\n >\r\n {displayValue}\r\n </div>\r\n );\r\n // El Tooltip de MUI solo se arma cuando hay un mensaje que mostrar\r\n return currentCellFeedback?.message\r\n ? <Tooltip title={currentCellFeedback.message}>{celda}</Tooltip>\r\n : celda;\r\n});\r\n\r\nexport const allColumnsCellRenderer = (\r\n props: RenderCellProps<FilaDeDatos, FilaDeResumen>\r\n) => {\r\n const { row, column } = props;\r\n if (!esColumnaPropia(column)) return undefined;\r\n\r\n // Fila de detalle: la columna de acciones ocupa todo el ancho (colSpan) y muestra la grilla anidada\r\n if (esFilaDetalle(row)) {\r\n return column.customType === 'action' ? <CeldaDetalle fila={row} /> : null;\r\n }\r\n\r\n // Lógica para renderizar una celda normal\r\n switch (column.customType) {\r\n case 'default': {\r\n const defaultColumn = column;\r\n const { fieldDef, tableDefinition, primaryKey } = defaultColumn;\r\n\r\n if (fieldDef?.clientSide) {\r\n const ClientSideComponent = clientSides[fieldDef.clientSide];\r\n if (ClientSideComponent) {\r\n return <ClientSideComponent {...props} fieldDefinition={fieldDef} tableDefinition={tableDefinition} primaryKey={primaryKey} />;\r\n }\r\n return <FallbackClientSideRenderer {...props} fieldDefinition={fieldDef} tableDefinition={tableDefinition} primaryKey={primaryKey} />;\r\n }\r\n\r\n return <CeldaDato row={row} columnKey={props.column.key} defaultColumn={defaultColumn} />;\r\n }\r\n\r\n case 'detail': {\r\n const { detailTable, primaryKey } = column;\r\n return (\r\n <BotonDetalle\r\n row={row}\r\n rowId={getPrimaryKeyValues(row, primaryKey)}\r\n detailTable={detailTable}\r\n deshabilitado={!!row[NEW_ROW_INDICATOR]}\r\n />\r\n );\r\n }\r\n\r\n case 'action': {\r\n const { tableDefinition, handleDeleteRow, handleAddRow, handleVerticalEditRow } = column;\r\n\r\n\r\n const gridActionButtons: ActionButtonDefinition[] = [\r\n {\r\n action: 'insert',\r\n handler: handleAddRow,\r\n icon: AddIcon,\r\n title: 'Agregar registro',\r\n color: 'success',\r\n },\r\n {\r\n action: 'delete',\r\n handler: handleDeleteRow,\r\n icon: DeleteIcon,\r\n title: 'Eliminar registro',\r\n color: 'error',\r\n },\r\n {\r\n action: 'vertical-edit',\r\n handler: handleVerticalEditRow,\r\n\r\n icon: ViewHeadlineIcon,\r\n title: 'Editar registro en forma de ficha',\r\n color: 'primary',\r\n }\r\n ];\r\n\r\n return (\r\n <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%', gap: 0.5 }}>\r\n {gridActionButtons.map(actionDef => (\r\n tableDefinition.allow![actionDef.action] ? <Button\r\n key={actionDef.action}\r\n variant=\"outlined\"\r\n color={actionDef.color}\r\n size=\"small\"\r\n onClick={() => actionDef.handler(row)}\r\n title={actionDef.title}\r\n sx={{ width: 22, minWidth: 22, height: 22, '& .MuiButton-startIcon': { m: 0 } }}\r\n >\r\n <actionDef.icon sx={{ fontSize: 18 }} />\r\n </Button> : null\r\n ))}\r\n </Box>\r\n );\r\n }\r\n\r\n default:\r\n return undefined;\r\n }\r\n};","import React from 'react';\r\nimport { Box, Typography } from '@mui/material';\r\nimport { ClientSideProps } from '../../types';\r\n\r\nconst ExampleClientSideComponent: React.FC<ClientSideProps> = ({ row, column, fieldDefinition }) => {\r\n // Accede al valor de la celda:\r\n const value = row[column.key];\r\n\r\n // Accede a la definición del campo si necesitas propiedades específicas\r\n // const customProperty = fieldDefinition?.customProp;\r\n\r\n return (\r\n <Box sx={{\r\n padding: 0.5,\r\n width: '100%',\r\n height: '100%',\r\n display: 'flex',\r\n alignItems: 'center',\r\n backgroundColor: value === 'Special' ? 'lightblue' : 'transparent'\r\n }}>\r\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\r\n {value ? `CS: ${value}` : 'N/A'}\r\n </Typography>\r\n {/* Puedes añadir lógica o UI más compleja aquí */}\r\n </Box>\r\n );\r\n};\r\n\r\nexport default ExampleClientSideComponent;","import React from 'react';\r\nimport { Box, Typography, Tooltip } from '@mui/material';\r\nimport WarningAmberIcon from '@mui/icons-material/WarningAmber';\r\nimport { ClientSideProps } from '../../types'; // Asegúrate de importar la interfaz\r\n\r\nconst FallbackClientSideRenderer: React.FC<ClientSideProps> = ({ column, row, fieldDefinition }) => {\r\n const clientSideName = fieldDefinition?.clientSide || 'Desconocido';\r\n\r\n return (\r\n <Tooltip title={`Componente clientSide '${clientSideName}' no implementado. Mostrando valor por defecto.`}>\r\n <Box\r\n sx={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between', // Para espaciar el valor y el icono\r\n gap: 1, // Espacio entre elementos\r\n width: '100%',\r\n height: '100%',\r\n paddingLeft: '8px',\r\n backgroundColor: 'rgba(255, 193, 7, 0.1)', // Amarillo claro para advertencia\r\n border: '1px dashed rgba(255, 193, 7, 0.5)',\r\n boxSizing: 'border-box',\r\n overflow: 'hidden', // Evita que el contenido se desborde\r\n }}\r\n >\r\n <Typography variant=\"body2\" noWrap sx={{ flexGrow: 1 }}>\r\n {clientSideName + ' not found'}\r\n </Typography>\r\n <WarningAmberIcon\r\n sx={{\r\n fontSize: 18,\r\n color: 'warning.main',\r\n marginRight: '4px' // Pequeño margen a la derecha del icono\r\n }}\r\n />\r\n </Box>\r\n </Tooltip>\r\n );\r\n};\r\n\r\nexport default FallbackClientSideRenderer;","import React from 'react';\nimport { ClientSideProps, FieldDefinition, TableDefinition } from '../../types';\n\nimport ExampleClientSideComponent from './ExampleClientSideComponent';\nimport FallbackClientSideRenderer from './FallbackClientSideRenderer';\n\nexport let clientSides: Record<string, React.FC<ClientSideProps>> = {\n 'ExampleClientSideComponent': ExampleClientSideComponent as React.FC<ClientSideProps>,\n 'FallbackClientSideRenderer': FallbackClientSideRenderer as React.FC<ClientSideProps>,\n};\n\nexport const extendClientSides = (newRenderers: Record<string, React.FC<ClientSideProps>>) => {\n clientSides = { ...clientSides, ...newRenderers };\n console.log('clientSides extendidos:', clientSides);\n};","import React, { useCallback, useEffect, useMemo, useState } from \"react\";\r\nimport { Column } from \"react-data-grid\";\r\nimport { useSnackbar } from \"../../contexts/SnackbarContext\";\r\nimport { useApiCall } from \"../../hooks/useApiCall\";\r\nimport { useTableRecordSave } from \"../../hooks/useTableRecordSave\";\r\nimport { mensajeDeError, requerido } from \"../../utils/errores\";\r\nimport InputBase from \"@mui/material/InputBase\";\r\nimport IconButton from \"@mui/material/IconButton\";\r\nimport ArrowDropDownIcon from \"@mui/icons-material/ArrowDropDown\";\r\nimport CalendarMonthIcon from \"@mui/icons-material/CalendarMonth\";\r\nimport SearchIcon from \"@mui/icons-material/Search\";\r\n// Importamos CellFeedbackMap junto con InputRendererProps\r\nimport { InputRendererProps, CellFeedback, FilaDeDatos } from \"../../types\";\r\n// getCellKey deben provenir de tu archivo GenericDataGrid\r\nimport { NEW_ROW_INDICATOR } from \"./GenericDataGrid\";\r\nimport { Tooltip, useTheme } from \"@mui/material\";\r\nimport { useEstadoGrilla, useFeedbackCelda } from \"./estadoGrilla\";\r\nimport { getPrimaryKeyValues, getCellKey, isNumericType, sameValue } from \"./utils/helpers\";\r\nimport { caracterRechazado, textoPegadoRechazado, tiparEntrada } from \"./utils/typedControls\";\r\nimport { MenuDeOpciones } from \"./MenuDeOpciones\";\r\nimport { SelectorDeFecha } from \"./SelectorDeFecha\";\r\nimport { VentanaDeTexto } from \"./VentanaDeTexto\";\r\nimport { cargarReferencia, esCampoConReferencia, opcionesDeReferencia, OpcionDeReferencia } from \"../../utils/referencias\";\r\nimport { fechaDeValor, opcionesDeBooleano, opcionesDeEnumerado, tipoDeExpander } from \"../../utils/expanders\";\r\nimport typeStore from 'type-store';\r\n\r\n\r\nfunction findChangedValues(oldRowData: FilaDeDatos, newRowData: FilaDeDatos, isNewRow: boolean) {\r\n let changes: string[] = [];\r\n for (const key in newRowData) {\r\n // Si la propiedad existe en ambos Y el valor es diferente, O es una nueva fila\r\n if (Object.prototype.hasOwnProperty.call(oldRowData, key) && oldRowData[key] !== newRowData[key] || isNewRow) {\r\n changes.push(key);\r\n }\r\n }\r\n return changes;\r\n}\r\n\r\nfunction InputRenderer<R extends FilaDeDatos, S>({\r\n column,\r\n row,\r\n rowIdx,\r\n tableDefinition,\r\n onRowChange,\r\n onClose,\r\n onKeyPress,\r\n registrarEditor,\r\n setTableData,\r\n primaryKey\r\n}: InputRendererProps<R, S>) {\r\n const estadoGrilla = useEstadoGrilla();\r\n const setCellFeedbackMap = estadoGrilla.setFeedback;\r\n const setLocalCellChanges = estadoGrilla.setCambiosLocales;\r\n // el editor solo se abre para columnas de campos de la tabla\r\n const fieldDefinition = requerido(tableDefinition.fields.find(f => f.name === column.key), `el campo '${column.key}'`);\r\n const typer = useMemo(() => typeStore.typerFrom(fieldDefinition), [fieldDefinition]);\r\n const [editingValue, setEditingValue] = useState<unknown>(() => row[column.key] !== null ? isNumericType(fieldDefinition?.typeName) ? row[column.key] : typer.toLocalString(row[column.key]) : null);\r\n const isCommitInProgress = React.useRef(false);\r\n // el expander del tipo (F9): mientras está abierto el editor pierde el foco y no por eso se guarda\r\n const [expansion, setExpansion] = useState<{ tipo: 'opciones', opciones: OpcionDeReferencia[], busqueda: string } | { tipo: 'fecha' } | { tipo: 'texto' } | null>(null);\r\n const menuAbierto = React.useRef(false);\r\n const entradaRef = React.useRef<HTMLInputElement | null>(null);\r\n\r\n const theme = useTheme();\r\n const { showSuccess, showError } = useSnackbar();\r\n const { saveRecord } = useTableRecordSave();\r\n\r\n\r\n // Generamos la clave única para la celda manualmente usando el formato esperado\r\n const rowId = getPrimaryKeyValues(row, primaryKey);\r\n const cellKey = `${rowId}-${column.key}`;\r\n\r\n const currentCellFeedback = useFeedbackCelda(cellKey);\r\n // ------------------------\r\n\r\n const isMandatory = tableDefinition.primaryKey.includes(column.key) || (fieldDefinition?.nullable === false);\r\n const isEmpty = editingValue === null || editingValue === undefined || String(editingValue).trim() === '';\r\n\r\n let cellBackgroundColor;\r\n let cellBorderColor = '#ccc';\r\n\r\n if (currentCellFeedback) {\r\n cellBackgroundColor = currentCellFeedback.type === 'error' ? theme.palette.error.light : theme.palette.success.light;\r\n cellBorderColor = currentCellFeedback.type === 'error' ? theme.palette.error.main : theme.palette.success.main;\r\n }\r\n\r\n const initialRowId = useMemo(() => getPrimaryKeyValues(row, primaryKey), [row, primaryKey]);\r\n\r\n const handleCommit = useCallback(async (currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => {\r\n if (isCommitInProgress.current) return;\r\n isCommitInProgress.current = true;\r\n\r\n let processedNewValue: unknown;\r\n try {\r\n // Usamos la lógica de BestControls portada de TypedControls.js (tiparEntrada avisa si lo escrito no es un valor del tipo)\r\n processedNewValue = tiparEntrada(currentValue, typer);\r\n // Validamos contra el esquema del typer\r\n typer.validateTypedData(processedNewValue);\r\n } catch (err) {\r\n showError(mensajeDeError(err));\r\n // y la celda queda marcada con el error (antes un valor que no era del tipo no se guardaba y no se veía nada)\r\n const claveDeLaCelda = `${getPrimaryKeyValues(row, tableDefinition.primaryKey)}-${column.key}`;\r\n setCellFeedbackMap(prev => new Map(prev).set(claveDeLaCelda, { rowId: getPrimaryKeyValues(row, tableDefinition.primaryKey), columnKey: column.key, type: 'error', message: mensajeDeError(err) } satisfies CellFeedback));\r\n isCommitInProgress.current = false;\r\n return; // Detenemos el commit ante errores\r\n }\r\n\r\n\r\n const potentialUpdatedRow: FilaDeDatos = { ...row, [column.key]: processedNewValue };\r\n\r\n const arePKValuesFilled = tableDefinition.primaryKey.every(key =>\r\n potentialUpdatedRow[key] !== undefined && potentialUpdatedRow[key] !== null && String(potentialUpdatedRow[key]).trim() !== ''\r\n );\r\n const isNewRow = !arePKValuesFilled || !!potentialUpdatedRow[NEW_ROW_INDICATOR];\r\n\r\n const isMandatoryField = tableDefinition.primaryKey.includes(column.key) || (tableDefinition.fields.find(f => f.name === column.key)?.nullable === false);\r\n const isMandatoryFieldEmpty = isNewRow && isMandatoryField && (processedNewValue === null || processedNewValue === undefined || String(processedNewValue).trim() === '');\r\n\r\n // Usamos sameValue para una comparación robusta (Dates, nulls, etc)\r\n if (sameValue(processedNewValue, row[column.key]) && !isNewRow && !isMandatoryFieldEmpty) {\r\n console.log(\"No se guardó: el valor no cambió.\");\r\n isCommitInProgress.current = false;\r\n return;\r\n }\r\n\r\n const oldRowData = { ...row };\r\n const primaryKeyValuesForBackend = tableDefinition.primaryKey.map(key => potentialUpdatedRow[key]);\r\n const currentRowIdBeforeUpdate = getPrimaryKeyValues(oldRowData, tableDefinition.primaryKey);\r\n\r\n onRowChange({ ...row, [column.key]: processedNewValue }, true);\r\n\r\n if (isNewRow) {\r\n const areAllMandatoryFieldsFilled = tableDefinition.fields.every(fieldDef => {\r\n const isMandatory = (fieldDef.nullable === false || fieldDef.isPk);\r\n const fieldValue = potentialUpdatedRow[fieldDef.name];\r\n return !isMandatory || (fieldValue !== null && fieldValue !== undefined && String(fieldValue).trim() !== '');\r\n });\r\n\r\n if (!areAllMandatoryFieldsFilled) {\r\n console.log(\"Nueva fila: Faltan campos obligatorios. No se guarda en el backend todavía.\");\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n const currentColumnsInRow = newMap.get(initialRowId) || new Set();\r\n\r\n tableDefinition.fields.forEach(fieldDef => {\r\n const isMandatory = (fieldDef.nullable === false || fieldDef.isPk);\r\n const isEditable = fieldDef.editable !== false;\r\n const fieldValue = potentialUpdatedRow[fieldDef.name];\r\n if (isMandatory && isEditable && (fieldValue === null || fieldValue === undefined || String(fieldValue).trim() === '')) {\r\n currentColumnsInRow.add(fieldDef.name);\r\n } else {\r\n currentColumnsInRow.delete(fieldDef.name);\r\n }\r\n });\r\n newMap.set(initialRowId, currentColumnsInRow);\r\n return newMap;\r\n });\r\n isCommitInProgress.current = false;\r\n return;\r\n } else {\r\n console.log(\"Nueva fila: Todos los campos obligatorios están llenos. Procediendo a guardar.\");\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n newMap.delete(initialRowId);\r\n return newMap;\r\n });\r\n }\r\n }\r\n\r\n try {\r\n const response = await saveRecord({\r\n tableDefinition,\r\n isNewRow,\r\n formData: potentialUpdatedRow,\r\n initialData: oldRowData,\r\n targetFieldName: column.key\r\n });\r\n\r\n\r\n // si el servidor no devolvió la fila se sigue con la que se mandó\r\n const responseRow: FilaDeDatos = response.row ?? potentialUpdatedRow;\r\n\r\n let finalRowIdForFeedback: string;\r\n let persistedRowData: FilaDeDatos;\r\n\r\n if (response.row && isNewRow) {\r\n persistedRowData = { ...responseRow };\r\n setTableData(prevData => {\r\n const newData = [...prevData];\r\n const originalRowId = getPrimaryKeyValues(oldRowData, tableDefinition.primaryKey);\r\n const rowIndex = newData.findIndex(r => getPrimaryKeyValues(r, tableDefinition.primaryKey) === originalRowId);\r\n if (rowIndex !== -1) {\r\n newData[rowIndex] = persistedRowData;\r\n } else {\r\n const newPrimaryKeyId = getPrimaryKeyValues(persistedRowData, tableDefinition.primaryKey);\r\n const newRowIndex = newData.findIndex(r => getPrimaryKeyValues(r, tableDefinition.primaryKey) === newPrimaryKeyId);\r\n if (newRowIndex !== -1) {\r\n newData[newRowIndex] = persistedRowData;\r\n }\r\n }\r\n return newData;\r\n });\r\n finalRowIdForFeedback = getPrimaryKeyValues(persistedRowData, tableDefinition.primaryKey);\r\n } else {\r\n persistedRowData = responseRow;\r\n const isPrimaryKeyColumn = tableDefinition.primaryKey.includes(column.key);\r\n finalRowIdForFeedback = isPrimaryKeyColumn\r\n ? getPrimaryKeyValues(potentialUpdatedRow, tableDefinition.primaryKey)\r\n : currentRowIdBeforeUpdate;\r\n // La fila que devolvió el servidor (con lo que calcularon triggers y defaults) reemplaza a la que tenemos.\r\n // Se hace sobre los datos y no con onRowChange del editor: para cuando llega la respuesta el editor ya se cerró\r\n // y el foco puede estar en otra celda; onRowChange(fila, true) cerraría el editor de esa otra celda.\r\n const mismaFila = (fila: FilaDeDatos) => tableDefinition.primaryKey.every(k => fila[k] === potentialUpdatedRow[k]);\r\n setTableData(prevData => prevData.map(fila => mismaFila(fila) ? { ...fila, ...responseRow } : fila));\r\n }\r\n\r\n // --- MULTI-FEEDBACK DE ÉXITO ---\r\n const changes = findChangedValues(oldRowData, responseRow, isNewRow);\r\n setCellFeedbackMap(prevFeedback => {\r\n // Siempre usamos new Map(prevFeedback) para garantizar inmutabilidad\r\n const newFeedback = new Map(prevFeedback);\r\n\r\n changes.forEach((key) => {\r\n // Creamos la clave única de forma más robusta (idealmente usando getCellKey)\r\n const keyForMap = `${finalRowIdForFeedback}-${key}`;\r\n newFeedback.set(keyForMap, { rowId: finalRowIdForFeedback, columnKey: key, type: 'success' } as CellFeedback);\r\n });\r\n\r\n return newFeedback;\r\n });\r\n // -------------------------------\r\n\r\n } catch (err) {\r\n console.error('Error al guardar el registro:', err);\r\n\r\n // --- MULTI-FEEDBACK DE ERROR ---\r\n const keyForMap = `${currentRowIdBeforeUpdate}-${column.key}`;\r\n setCellFeedbackMap(prevFeedback => {\r\n // Siempre usamos new Map(prevFeedback) para garantizar inmutabilidad\r\n const newFeedback = new Map(prevFeedback);\r\n newFeedback.set(keyForMap, {\r\n rowId: currentRowIdBeforeUpdate,\r\n columnKey: column.key,\r\n type: 'error',\r\n message: mensajeDeError(err)\r\n } satisfies CellFeedback);\r\n return newFeedback;\r\n });\r\n // -------------------------------\r\n } finally {\r\n if (!closeEditor) {\r\n isCommitInProgress.current = false;\r\n }\r\n }\r\n }, [\r\n column, row, onRowChange, tableDefinition.primaryKey,\r\n tableDefinition.fields, showSuccess, showError, setCellFeedbackMap, onClose,\r\n setTableData, setLocalCellChanges, initialRowId, typer\r\n ]);\r\n\r\n\r\n // los expanders de backend-plus (F9 o Alt+↓): según el tipo del campo, un menú (referencias, booleano, options), un calendario (fecha) o\r\n // una ventana de texto; escribir en una referencia un carácter que el tipo rechaza (una letra en un entero) abre el menú ya filtrado\r\n const tipoExpander = tipoDeExpander(fieldDefinition, fieldDefinition.editable !== false);\r\n const abrirExpander = useCallback(async (busqueda: string) => {\r\n if (tipoExpander === null || tipoExpander === 'json' || menuAbierto.current) return;\r\n menuAbierto.current = true;\r\n const admiteNulo = fieldDefinition.nullable !== false;\r\n try {\r\n if (tipoExpander === 'referencia' && esCampoConReferencia(fieldDefinition)) {\r\n const { definicion, filas } = await cargarReferencia(fieldDefinition.references);\r\n setExpansion({ tipo: 'opciones', opciones: opcionesDeReferencia(fieldDefinition, definicion, filas, row, admiteNulo), busqueda });\r\n } else if (tipoExpander === 'booleano') {\r\n setExpansion({ tipo: 'opciones', opciones: opcionesDeBooleano(admiteNulo), busqueda });\r\n } else if (tipoExpander === 'opciones') {\r\n setExpansion({ tipo: 'opciones', opciones: opcionesDeEnumerado(fieldDefinition.options ?? [], admiteNulo), busqueda });\r\n } else {\r\n setExpansion({ tipo: tipoExpander === 'fecha' ? 'fecha' : 'texto' });\r\n }\r\n } catch (err) {\r\n menuAbierto.current = false;\r\n showError(mensajeDeError(err));\r\n }\r\n }, [tipoExpander, fieldDefinition, row, showError]);\r\n\r\n const cerrarExpansion = useCallback(() => {\r\n menuAbierto.current = false;\r\n setExpansion(null);\r\n // el menú es un modal que retiene el foco hasta que se desmonta\r\n setTimeout(() => entradaRef.current?.focus(), 0);\r\n }, []);\r\n\r\n // lo que se elige se escribe en la celda y se guarda\r\n const confirmarEnExpansion = useCallback((nuevo: unknown) => {\r\n setEditingValue(nuevo);\r\n cerrarExpansion();\r\n void handleCommit(nuevo, true, false);\r\n }, [cerrarExpansion, handleCommit]);\r\n // una opción (de un menú) es un valor tipado: un número o un lógico tal cual, lo demás como el texto que se escribiría\r\n const elegirOpcion = useCallback((valor: unknown) => {\r\n confirmarEnExpansion(valor === null || valor === undefined ? null : typeof valor === 'boolean' || isNumericType(fieldDefinition.typeName) ? valor : typer.toLocalString(valor));\r\n }, [fieldDefinition.typeName, typer, confirmarEnExpansion]);\r\n\r\n const handleKeyDown = useCallback(async (event: React.KeyboardEvent) => {\r\n if (tipoExpander !== null && (event.key === 'F9' && !event.shiftKey && !event.ctrlKey && !event.altKey || event.key === 'ArrowDown' && event.altKey && !event.shiftKey && !event.ctrlKey)) {\r\n event.preventDefault();\r\n void abrirExpander('');\r\n return;\r\n }\r\n if (event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey && caracterRechazado(event.key, typer)) {\r\n // como backend-plus (rejectedChar): lo que el tipo no admite no se escribe (una letra en un entero); en una referencia abre el menú filtrado\r\n event.preventDefault();\r\n if (tipoExpander === 'referencia') void abrirExpander(event.key);\r\n return;\r\n }\r\n if (event.key === 'F4') {\r\n event.preventDefault();\r\n if (onKeyPress) {\r\n onKeyPress(rowIdx, column.key, event, handleCommit, editingValue);\r\n }\r\n return;\r\n }\r\n\r\n // Con cada tecla (la grilla lleva la cuenta de la anterior): ella decide si deja la celda (Enter, Tab, flechas; ver\r\n // direccionDeSalida) y, si la deja, guarda y pasa el foco. Una flecha que solo mueve el cursor dentro del texto no guarda.\r\n // Lo que resuelve la grilla no puede hacer además lo suyo: el Tab nativo mandaría el foco a otro lado.\r\n if (onKeyPress?.(rowIdx, column.key, event, handleCommit, editingValue)) {\r\n event.preventDefault();\r\n }\r\n }, [handleCommit, editingValue, column.key, rowIdx, onKeyPress, tipoExpander, abrirExpander, typer]);\r\n\r\n // Teclas (F4) que caen en la celda antes de que este editor tenga el foco (o antes de que exista):\r\n // el grid se las pasa a través de registrarEditor; siempre se usa la versión más reciente del manejador\r\n const procesarTecla = React.useRef<(key: string) => void>(() => {});\r\n procesarTecla.current = (key: string) => {\r\n if (key === 'F9') {\r\n void abrirExpander('');\r\n } else if (key === 'F4' && onKeyPress) {\r\n onKeyPress(rowIdx, column.key, { key, target: document.activeElement ?? document.body }, handleCommit, editingValue);\r\n }\r\n };\r\n useEffect(() => registrarEditor?.((key) => procesarTecla.current(key)), []); // eslint-disable-line react-hooks/exhaustive-deps\r\n\r\n const handleBlur = useCallback(() => {\r\n if (menuAbierto.current) return;\r\n handleCommit(editingValue, true, false);\r\n }, [handleCommit, editingValue]);\r\n\r\n const isFieldEditable = fieldDefinition?.editable !== false;\r\n\r\n const isNumeric = isNumericType(fieldDefinition?.typeName);\r\n\r\n const editor = (\r\n <InputBase\r\n autoFocus\r\n value={editingValue}\r\n onChange={(e) => setEditingValue(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n onPaste={(e: React.ClipboardEvent) => {\r\n const pegado = e.clipboardData.getData('text');\r\n if (textoPegadoRechazado(pegado, typer)) {\r\n e.preventDefault();\r\n showError(`«${pegado}» no es un valor válido para este campo (${typer.typeName})`);\r\n }\r\n }}\r\n onBlur={handleBlur}\r\n inputRef={entradaRef}\r\n placeholder={isMandatory ? '*' : ''}\r\n className={editingValue === '--' ? 'typed-controls-signal-no-data' : editingValue === '//' ? 'typed-controls-signal-unknown-data' : ''}\r\n sx={{\r\n width: '100%',\r\n margin: '0',\r\n border: `1px solid ${cellBorderColor}`,\r\n borderRadius: '4px',\r\n padding: '2px 4px',\r\n fontSize: '0.8rem',\r\n boxSizing: 'border-box',\r\n backgroundColor: cellBackgroundColor,\r\n input: {\r\n textAlign: isNumeric ? 'right' : 'left',\r\n }\r\n }}\r\n onClick={(e) => e.stopPropagation()}\r\n disabled={!isFieldEditable}\r\n // la lupa de backend-plus: junto al editor, un icono según el expander (flecha de menú, calendario o lupa de texto) que lo abre con un clic\r\n endAdornment={tipoExpander !== null && tipoExpander !== 'json' && (\r\n <IconButton\r\n size=\"small\"\r\n tabIndex={-1}\r\n aria-label={tipoExpander === 'fecha' ? 'elegir fecha' : tipoExpander === 'texto' ? 'ver el contenido completo' : 'elegir un valor'}\r\n title={tipoExpander === 'fecha' ? 'elegir fecha (F9)' : tipoExpander === 'texto' ? 'ver el contenido completo (F9)' : 'elegir un valor (F9)'}\r\n // el editor no pierde el foco (perderlo lo guardaría) y el clic no llega a la celda\r\n onMouseDown={(e) => e.preventDefault()}\r\n onClick={(e) => { e.stopPropagation(); void abrirExpander(''); }}\r\n sx={{ p: 0, ml: 0.25, color: 'primary.main', '&:hover': { backgroundColor: 'action.hover' } }}\r\n >\r\n {tipoExpander === 'fecha' ? <CalendarMonthIcon sx={{ fontSize: 16 }} /> : tipoExpander === 'texto' ? <SearchIcon sx={{ fontSize: 16 }} /> : <ArrowDropDownIcon sx={{ fontSize: 20 }} />}\r\n </IconButton>\r\n )}\r\n />\r\n\r\n );\r\n const conMenu = (\r\n <>\r\n {editor}\r\n {expansion && entradaRef.current && (expansion.tipo === 'opciones' ? (\r\n <MenuDeOpciones\r\n anclaje={entradaRef.current}\r\n opciones={expansion.opciones}\r\n busquedaInicial={expansion.busqueda}\r\n onElegir={elegirOpcion}\r\n onCerrar={cerrarExpansion}\r\n />\r\n ) : expansion.tipo === 'fecha' ? (\r\n <SelectorDeFecha\r\n anclaje={entradaRef.current}\r\n fechaActual={fechaDeValor(row[column.key])}\r\n admiteNulo={fieldDefinition.nullable !== false}\r\n onElegir={confirmarEnExpansion}\r\n onCerrar={cerrarExpansion}\r\n />\r\n ) : (\r\n <VentanaDeTexto\r\n anclaje={entradaRef.current}\r\n texto={editingValue === null || editingValue === undefined ? '' : String(editingValue)}\r\n editable\r\n onAceptar={confirmarEnExpansion}\r\n onCerrar={cerrarExpansion}\r\n />\r\n ))}\r\n </>\r\n );\r\n return currentCellFeedback?.message\r\n ? <Tooltip title={currentCellFeedback.message}><div style={{ width: '100%' }}>{conMenu}</div></Tooltip>\r\n : conMenu;\r\n}\r\n\r\nexport default InputRenderer;","import { useCallback } from 'react';\nimport { useApiCall } from './useApiCall';\nimport { FilaDeDatos, TableDefinition } from '../types';\nimport { requerido } from '../utils/errores';\n\n/** Lo que devuelve table_record_save: la fila como quedó en la base (con lo que calcularon triggers y valores por omisión) */\nexport interface RespuestaDeGuardado {\n row?: FilaDeDatos;\n}\n\nexport interface SaveRecordParams {\n tableDefinition: TableDefinition; // se guarda en la tabla que nombra su definición (`name`), no en la física (`tableName`)\n isNewRow: boolean;\n formData: FilaDeDatos; // The entire row state after changes (or potential updated row)\n initialData: FilaDeDatos; // The row before the change\n targetFieldName?: string; // If provided, it's an update to a specific field. Si no, asume mandar todo (o si es $new)\n}\n\nexport const useTableRecordSave = () => {\n const { callApi, loading, error } = useApiCall();\n\n const saveRecord = useCallback(async ({\n tableDefinition,\n isNewRow,\n formData,\n initialData,\n targetFieldName\n }: SaveRecordParams) => {\n const status = isNewRow ? 'new' : 'update';\n let rowToSend: FilaDeDatos = {};\n\n if (isNewRow) {\n tableDefinition.fields.forEach(fieldDef => {\n const fieldValue = formData[fieldDef.name];\n if (fieldValue !== undefined && fieldValue !== null && String(fieldValue).trim() !== '') {\n rowToSend[fieldDef.name] = fieldValue;\n } else if (tableDefinition.primaryKey.includes(fieldDef.name) && (fieldValue === null || fieldValue === undefined || String(fieldValue).trim() === '')) {\n // Conservamos pk vacías para backend-plus\n rowToSend[fieldDef.name] = fieldValue;\n }\n });\n delete rowToSend['$new'];\n } else {\n // Es un update, a un campo específico (como InputRenderer y VerticalEditor autosave)\n if (targetFieldName) {\n rowToSend[targetFieldName] = formData[targetFieldName];\n } else {\n // Por si se quiere guardar la fila entera forzosamente\n Object.keys(formData).forEach(key => {\n if (formData[key] !== initialData[key]) {\n rowToSend[key] = formData[key];\n }\n });\n }\n // como backend-plus: en un update `newRow` lleva solo lo que cambió; la clave viaja aparte (`primaryKeyValues`).\n // Si se agregara, un campo con `table` (de otra tabla) dejaría la tabla principal con un update de solo claves\n // («orden de modificación sin columnas a modificar»).\n }\n\n const primaryKeyValuesForBackend = isNewRow ? [] : tableDefinition.primaryKey.map(key => initialData[key]);\n\n const response = await callApi<RespuestaDeGuardado>('table_record_save', {\n // como backend-plus (`depot.def.name`): el servidor busca la definición por este nombre. Una definición que guarda en\n // otra tabla (`tableName`, con campos de `table`) no existe con el nombre de la física: ahí faltarían sus campos\n table: tableDefinition.name,\n primaryKeyValues: primaryKeyValuesForBackend,\n newRow: rowToSend,\n oldRow: isNewRow ? {} : initialData,\n status\n });\n\n return requerido(response, 'la respuesta del guardado');\n }, [callApi]);\n\n return { saveRecord, loading, error };\n};\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { InputBase, List, ListItemButton, Popover, Typography } from '@mui/material';\nimport { filtrarOpciones, OpcionDeReferencia } from '../../utils/referencias';\n\ninterface MenuDeOpcionesProps {\n /** El elemento bajo el que se abre (el editor de la celda) */\n anclaje: HTMLElement;\n opciones: OpcionDeReferencia[];\n /** Lo que se escribió para abrir el menú (una letra en un campo numérico): ya filtra */\n busquedaInicial: string;\n onElegir: (valor: unknown) => void;\n onCerrar: () => void;\n}\n\n/**\n * Menú para elegir una opción (una fila de la tabla referenciada, Sí/No, un valor de `options`), como el `miniMenuPromise` de los\n * expanders de backend-plus: se filtra escribiendo, ↑ y ↓ recorren, Enter elige y Escape cierra.\n */\nexport function MenuDeOpciones({ anclaje, opciones, busquedaInicial, onElegir, onCerrar }: MenuDeOpcionesProps) {\n const [busqueda, setBusqueda] = useState(busquedaInicial);\n const [indice, setIndice] = useState(0);\n const visibles = useMemo(() => filtrarOpciones(opciones, busqueda), [opciones, busqueda]);\n const elegida = useRef<HTMLDivElement | null>(null);\n useEffect(() => { elegida.current?.scrollIntoView({ block: 'nearest' }); }, [indice, visibles]);\n\n const alApretar = (evento: React.KeyboardEvent) => {\n if (evento.key === 'ArrowDown') {\n setIndice(i => Math.min(i + 1, visibles.length - 1));\n } else if (evento.key === 'ArrowUp') {\n setIndice(i => Math.max(i - 1, 0));\n } else if (evento.key === 'Enter') {\n if (visibles[indice]) onElegir(visibles[indice].valor);\n } else if (evento.key === 'F9' || evento.key === 'Escape') {\n onCerrar();\n } else {\n return;\n }\n evento.preventDefault();\n evento.stopPropagation();\n };\n\n return (\n <Popover\n open\n anchorEl={anclaje}\n onClose={onCerrar}\n disableRestoreFocus\n anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}\n slotProps={{ paper: { sx: { minWidth: 300, maxWidth: 520 } } }}\n >\n <InputBase\n autoFocus\n fullWidth\n value={busqueda}\n onChange={(e) => { setBusqueda(e.target.value); setIndice(0); }}\n onKeyDown={alApretar}\n placeholder=\"buscar\"\n sx={{ px: 1, py: 0.5, fontSize: '0.8rem', borderBottom: '1px solid #ccc' }}\n inputProps={{ 'aria-label': 'buscar' }}\n />\n <List dense disablePadding sx={{ maxHeight: 320, overflowY: 'auto' }} role=\"listbox\">\n {visibles.length === 0 && <Typography variant=\"body2\" sx={{ p: 1, color: 'text.secondary' }}>sin coincidencias</Typography>}\n {visibles.map((opcion, i) => (\n <ListItemButton\n key={i}\n ref={i === indice ? elegida : null}\n selected={i === indice}\n role=\"option\"\n onClick={() => onElegir(opcion.valor)}\n sx={{ gap: 2, py: 0.25, fontSize: '0.8rem' }}\n >\n {opcion.etiquetas.map((etiqueta, j) => (\n <span key={j} style={{ fontWeight: j === 0 ? 600 : 400, minWidth: j === 0 ? 40 : undefined }}>{etiqueta}</span>\n ))}\n </ListItemButton>\n ))}\n </List>\n </Popover>\n );\n}\n","import React, { useState } from 'react';\nimport { Button, IconButton, Popover, Typography } from '@mui/material';\nimport { fechaATexto, FechaSimple, semanasDelMes } from '../../utils/expanders';\n\nconst MESES = ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];\nconst DIAS = ['lu', 'ma', 'mi', 'ju', 'vi', 'sá', 'do'];\n\ninterface SelectorDeFechaProps {\n anclaje: HTMLElement;\n /** La fecha que tiene la celda (la que se muestra elegida y desde donde se parte); si no hay, hoy */\n fechaActual: FechaSimple | null;\n admiteNulo: boolean;\n /** La fecha elegida como AAAA-MM-DD, o null para dejarla vacía */\n onElegir: (fecha: string | null) => void;\n onCerrar: () => void;\n}\n\nconst mismaFecha = (a: FechaSimple, b: FechaSimple | null) => b !== null && a.anio === b.anio && a.mes === b.mes && a.dia === b.dia;\n\nconst MOVIMIENTOS: Record<string, [number, number]> = {\n ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [-7, 0], ArrowDown: [7, 0], PageUp: [0, -1], PageDown: [0, 1],\n};\n\n/** Calendario para elegir una fecha, como el `pickDate` (Pikaday) de typed-controls: clic o flechas y Enter; Re Pág y Av Pág cambian de mes */\nexport function SelectorDeFecha({ anclaje, fechaActual, admiteNulo, onElegir, onCerrar }: SelectorDeFechaProps) {\n const hoy = new Date();\n const fechaDeHoy: FechaSimple = { anio: hoy.getFullYear(), mes: hoy.getMonth(), dia: hoy.getDate() };\n const [cursor, setCursor] = useState<FechaSimple>(fechaActual ?? fechaDeHoy);\n\n const mover = (dias: number, meses: number) => {\n const nueva = new Date(cursor.anio, cursor.mes + meses, cursor.dia + dias);\n setCursor({ anio: nueva.getFullYear(), mes: nueva.getMonth(), dia: nueva.getDate() });\n };\n\n const alApretar = (evento: React.KeyboardEvent) => {\n if (evento.key in MOVIMIENTOS) {\n mover(...MOVIMIENTOS[evento.key]);\n } else if (evento.key === 'Enter') {\n onElegir(fechaATexto(cursor));\n } else if (evento.key === 'F9' || evento.key === 'Escape') {\n onCerrar();\n } else {\n return;\n }\n evento.preventDefault();\n evento.stopPropagation();\n };\n\n return (\n <Popover open anchorEl={anclaje} onClose={onCerrar} disableRestoreFocus anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}>\n <div tabIndex={0} ref={(el) => el?.focus()} onKeyDown={alApretar} style={{ padding: 8, outline: 'none', width: 252 }} aria-label=\"calendario\">\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>\n <IconButton size=\"small\" aria-label=\"mes anterior\" onClick={() => mover(0, -1)}>‹</IconButton>\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>{MESES[cursor.mes]} {cursor.anio}</Typography>\n <IconButton size=\"small\" aria-label=\"mes siguiente\" onClick={() => mover(0, 1)}>›</IconButton>\n </div>\n <table style={{ borderCollapse: 'collapse', width: '100%', textAlign: 'center', fontSize: '0.8rem' }}>\n <thead><tr>{DIAS.map(d => <th key={d} style={{ fontWeight: 400, color: '#777' }}>{d}</th>)}</tr></thead>\n <tbody>\n {semanasDelMes(cursor.anio, cursor.mes).map((semana, i) => (\n <tr key={i}>\n {semana.map(dia => {\n const elegido = mismaFecha(dia, fechaActual);\n const enCursor = mismaFecha(dia, cursor);\n return (\n <td key={`${dia.mes}-${dia.dia}`} style={{ padding: 1 }}>\n <button\n type=\"button\"\n tabIndex={-1}\n onClick={() => onElegir(fechaATexto(dia))}\n style={{\n width: 32, height: 26, border: enCursor ? '1px solid #1976d2' : '1px solid transparent', borderRadius: 4, cursor: 'pointer',\n background: elegido ? '#1976d2' : 'transparent', color: elegido ? '#fff' : dia.delMesMostrado ? '#000' : '#aaa',\n }}\n >{dia.dia}</button>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 4 }}>\n <Button size=\"small\" onClick={() => onElegir(fechaATexto(fechaDeHoy))}>hoy</Button>\n {admiteNulo && <Button size=\"small\" onClick={() => onElegir(null)}>vacío</Button>}\n </div>\n </div>\n </Popover>\n );\n}\n","import React, { useState } from 'react';\nimport { Button, InputBase, Popover } from '@mui/material';\n\ninterface VentanaDeTextoProps {\n anclaje: HTMLElement;\n texto: string;\n /** Si se puede editar: con Aceptar (o Ctrl+Enter) devuelve el texto; si no, solo muestra el contenido completo (JSON o texto largo) */\n editable: boolean;\n onAceptar: (texto: string) => void;\n onCerrar: () => void;\n}\n\n/** La ventana de texto de backend-plus (`promptPromise` / `alertPromise` del expander de texto): ver o editar el contenido completo de una celda */\nexport function VentanaDeTexto({ anclaje, texto, editable, onAceptar, onCerrar }: VentanaDeTextoProps) {\n const [valor, setValor] = useState(texto);\n const alApretar = (evento: React.KeyboardEvent) => {\n if (evento.key === 'Escape' || evento.key === 'F9') {\n onCerrar();\n } else if (evento.key === 'Enter' && evento.ctrlKey && editable) {\n onAceptar(valor);\n } else {\n return;\n }\n evento.preventDefault();\n evento.stopPropagation();\n };\n return (\n <Popover open anchorEl={anclaje} onClose={onCerrar} disableRestoreFocus anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}>\n <div style={{ padding: 8, minWidth: 320, maxWidth: 640 }}>\n <InputBase\n autoFocus\n multiline\n fullWidth\n readOnly={!editable}\n value={valor}\n onChange={(e) => setValor(e.target.value)}\n onKeyDown={alApretar}\n minRows={2}\n maxRows={16}\n sx={{ fontFamily: 'monospace', fontSize: '0.8rem', border: '1px solid #ccc', borderRadius: '4px', p: 0.5 }}\n inputProps={{ 'aria-label': 'contenido de la celda' }}\n />\n {editable && (\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 4 }}>\n <Button size=\"small\" onClick={onCerrar}>cancelar</Button>\n <Button size=\"small\" variant=\"contained\" onClick={() => onAceptar(valor)}>aceptar</Button>\n </div>\n )}\n </div>\n </Popover>\n );\n}\n","import { RenderEditCellProps } from \"react-data-grid\";\r\nimport InputRenderer from \"../InputRenderer\";\r\nimport { ColumnaDeGrilla, esColumnaPropia } from \"../GenericDataGrid\";\r\nimport { FilaDeDatos, FilaDeResumen } from \"../../../types\";\r\nimport { valorFijoDe } from \"../../../utils/fixedFields\";\r\n\r\nexport const allColumnsEditCellRenderer = (\r\n props: RenderEditCellProps<FilaDeDatos, FilaDeResumen>, \r\n allColumns: ColumnaDeGrilla[]\r\n) => {\r\n const column = props.column;\r\n if (!esColumnaPropia(column)) return undefined;\r\n switch (column.customType) {\r\n case 'default': {\r\n const defaultColumn = column;\r\n const {\r\n tableDefinition,\r\n fixedFields,\r\n primaryKey,\r\n setTableData,\r\n handleKeyPressInEditor,\r\n registrarEditor\r\n } = defaultColumn\r\n \r\n if (column.editable) { \r\n const fieldDefinition = tableDefinition.fields.find(f => f.name === column.key);\r\n \r\n // Busca si el campo es fijo en el array fixedFields\r\n const isFixedField = valorFijoDe(fixedFields, column.key) !== undefined;\r\n const isFieldEditableByUI = fieldDefinition?.editable !== false && !isFixedField; // No editable si es fijo\r\n \r\n if (!isFieldEditableByUI) {\r\n // Si el campo no es editable por UI (ej. por ser fijo), no renderizamos el editor\r\n return undefined\r\n }\r\n \r\n // El feedback (color y mensaje) lo lee InputRenderer del EstadoGrilla\r\n return (\r\n <InputRenderer\r\n {...props}\r\n tableDefinition={tableDefinition}\r\n onKeyPress={(rowIndex, columnKey, event, handleCommit, valorActual) => handleKeyPressInEditor(rowIndex, columnKey, event, allColumns, handleCommit, valorActual)}\r\n registrarEditor={registrarEditor}\r\n setTableData={setTableData}\r\n primaryKey={primaryKey}\r\n />\r\n )\r\n }\r\n return undefined\r\n }\r\n case 'detail': {\r\n return undefined\r\n }\r\n case 'action': {\r\n return undefined\r\n }\r\n default:\r\n return undefined;\r\n }\r\n\r\n}","import React, { useState } from 'react';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n FormControlLabel,\n Checkbox,\n Box,\n Typography,\n Stack,\n Divider\n} from '@mui/material';\nimport FileUploadIcon from '@mui/icons-material/FileUpload';\n\ninterface ImportDialogProps {\n open: boolean;\n onClose: () => void;\n onImport: (file: File, options: ImportOptions) => Promise<void>;\n loading: boolean;\n}\n\nexport interface ImportOptions {\n skipUnknownFieldsAtImport: boolean;\n simplificateSpaces: boolean;\n replaceNewLineWithSpace: boolean;\n}\n\nexport const ImportDialog: React.FC<ImportDialogProps> = ({ open, onClose, onImport, loading }) => {\n const [file, setFile] = useState<File | null>(null);\n const [options, setOptions] = useState<ImportOptions>({\n skipUnknownFieldsAtImport: false,\n simplificateSpaces: true,\n replaceNewLineWithSpace: true\n });\n\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files[0]) {\n setFile(e.target.files[0]);\n }\n };\n\n const handleOptionChange = (name: keyof ImportOptions) => (e: React.ChangeEvent<HTMLInputElement>) => {\n setOptions(prev => ({ ...prev, [name]: e.target.checked }));\n };\n\n const handleConfirm = async () => {\n if (file) {\n await onImport(file, options);\n onClose();\n }\n };\n\n return (\n <Dialog open={open} onClose={loading ? undefined : onClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>\n <FileUploadIcon color=\"primary\" />\n Importar datos desde archivo\n </DialogTitle>\n <DialogContent dividers>\n <Stack spacing={3} sx={{ mt: 1 }}>\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom fontWeight=\"bold\">\n Seleccionar archivo\n </Typography>\n <Box\n sx={{\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n p: 2,\n textAlign: 'center',\n bgcolor: 'action.hover',\n position: 'relative'\n }}\n >\n <input\n type=\"file\"\n accept=\".xlsx, .xls, .tab\"\n onChange={handleFileChange}\n style={{\n opacity: 0,\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n cursor: 'pointer'\n }}\n />\n <Typography variant=\"body2\" color={file ? \"textPrimary\" : \"textSecondary\"}>\n {file ? file.name : \"Haz clic aquí para seleccionar un archivo\"}\n </Typography>\n </Box>\n </Box>\n\n <Divider />\n\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom fontWeight=\"bold\">\n Opciones de importación\n </Typography>\n <Stack>\n <FormControlLabel\n control={\n <Checkbox\n checked={options.skipUnknownFieldsAtImport}\n onChange={handleOptionChange('skipUnknownFieldsAtImport')}\n size=\"small\"\n />\n }\n label={<Typography variant=\"body2\">Omitir campos desconocidos al importar</Typography>}\n />\n <FormControlLabel\n control={\n <Checkbox\n checked={options.simplificateSpaces}\n onChange={handleOptionChange('simplificateSpaces')}\n size=\"small\"\n />\n }\n label={<Typography variant=\"body2\">Simplificar espacios</Typography>}\n />\n <FormControlLabel\n control={\n <Checkbox\n checked={options.replaceNewLineWithSpace}\n onChange={handleOptionChange('replaceNewLineWithSpace')}\n size=\"small\"\n />\n }\n label={<Typography variant=\"body2\">Reemplazar saltos de línea con espacios</Typography>}\n />\n </Stack>\n </Box>\n </Stack>\n </DialogContent>\n <DialogActions sx={{ px: 3, py: 2 }}>\n <Button onClick={onClose} disabled={loading}>\n Cancelar\n </Button>\n <Button\n onClick={handleConfirm}\n variant=\"contained\"\n disabled={!file || loading}\n startIcon={<FileUploadIcon />}\n >\n {loading ? 'Procesando...' : 'Confirmar Importación'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useState } from 'react';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n FormControl,\n FormLabel,\n RadioGroup,\n FormControlLabel,\n Radio,\n Checkbox,\n FormGroup,\n Box,\n Typography,\n CircularProgress\n} from '@mui/material';\nimport * as XLSX from 'xlsx';\nimport typeAr from 'like-ar';\nimport typeStore from 'type-store';\nimport { FieldDefinition, FilaDeDatos, TableDefinition } from '../../types';\n\ninterface ExportDialogProps {\n open: boolean;\n onClose: () => void;\n tableDefinition: TableDefinition;\n tableData: FilaDeDatos[];\n filteredRows: FilaDeDatos[];\n username?: string;\n}\n\ntype ExportFormat = 'xlsx' | 'csv' | 'tab';\n\nexport const ExportDialog: React.FC<ExportDialogProps> = ({\n open,\n onClose,\n tableDefinition,\n tableData,\n filteredRows,\n username = 'anonymous'\n}) => {\n const [format, setFormat] = useState<ExportFormat>('xlsx');\n const [options, setOptions] = useState({\n fromOtherTables: true,\n readOnly: true,\n hiddens: false\n });\n const [isPreparing, setIsPreparing] = useState(false);\n\n const handleOptionChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n setOptions({\n ...options,\n [event.target.name]: event.target.checked\n });\n };\n\n const handlePrepareExport = () => {\n setIsPreparing(true);\n // Pequeño delay para que se vea el spinner y no bloquee el hilo principal inmediatamente\n setTimeout(async () => {\n try {\n await executeExport();\n } finally {\n setIsPreparing(false);\n onClose();\n }\n }, 100);\n };\n\n const executeExport = async () => {\n const fieldsDef2Export = tableDefinition.fields.filter((fieldDef: FieldDefinition) => {\n const isVisible = fieldDef.visible !== false;\n // Simplificación de la lógica legada adaptada a FieldDefinition\n return (fieldDef.inTable !== false || options.fromOtherTables)\n && isVisible\n && (fieldDef.editable !== false || options.readOnly || (tableDefinition.primaryKey?.includes(fieldDef.name)))\n // Nota: La lógica de 'hiddens' en la grilla original dependía de 'grid.view.hiddenColumns'.\n // Aquí, si hiddens es false, solo exportamos los visibles.\n && (isVisible || options.hiddens);\n });\n\n if (format === 'xlsx') {\n await excelExport(fieldsDef2Export);\n } else {\n textExport(fieldsDef2Export);\n }\n };\n\n const textExport = (fieldsToExport: FieldDefinition[]) => {\n let separator = ',';\n let dotExtension = '.csv';\n let replacer = (txt: string) => {\n return /[\\n,\"]/.test(txt) ? '\"' + txt.replace(/\"/g, '\"\"') + '\"' : txt;\n };\n\n if (format === 'tab') {\n separator = '|';\n dotExtension = '.tab';\n const trans: Record<string, string> = {\n '|': '\\\\x7C',\n '\\\\': '\\\\\\\\',\n '\\r': '\\\\r',\n '\\n': '\\\\n',\n };\n replacer = (txt: string) => {\n return txt.replace(/[|\\\\\\r\\n]/g, (char) => trans[char] || char);\n };\n }\n\n const data: string[][] = [];\n // Cabeceras\n data.push(fieldsToExport.map(f => f.name));\n\n // Filas\n filteredRows.forEach(row => {\n data.push(fieldsToExport.map(fieldDef => {\n const value = row[fieldDef.name];\n if (value == null) return '';\n // Usamos type-store para formatear a string plano\n return typeStore.typerFrom(fieldDef).toPlainString(value);\n }));\n });\n\n const content = data.map(line => line.map(replacer).join(separator)).join('\\r\\n');\n downloadFile(content, `${tableDefinition.name}${dotExtension}`, 'text/plain');\n };\n\n const excelExport = async (fieldsToExport: FieldDefinition[]) => {\n const wb = XLSX.utils.book_new();\n const ws: XLSX.WorkSheet = {};\n\n const STYLE_HEADER = { font: { bold: true, underline: true } };\n const STYLE_PK = { font: { bold: true } };\n const STYLE_LOOKUP = { font: { color: { rgb: \"5588DD\" } } };\n\n // Hoja 1: Datos\n fieldsToExport.forEach((field, iColumn) => {\n ws[XLSX.utils.encode_cell({ c: iColumn, r: 0 })] = { t: 's', v: field.name, s: STYLE_HEADER };\n });\n\n filteredRows.forEach((row, iRow) => {\n fieldsToExport.forEach((fieldDef, iColumn) => {\n const value = row[fieldDef.name];\n if (value != null) {\n const typer = typeStore.typerFrom(fieldDef);\n const excelValue = typer.toExcelValue(value);\n const excelType = typer.toExcelType(value);\n const cell: XLSX.CellObject = { t: excelType, v: excelValue };\n\n if (fieldDef.typeName === 'interval') {\n cell.z = '[h]:mm:ss';\n }\n if (tableDefinition.primaryKey?.includes(fieldDef.name)) {\n cell.s = STYLE_PK;\n } else if (fieldDef.editable === false) {\n cell.s = STYLE_LOOKUP;\n }\n ws[XLSX.utils.encode_cell({ c: iColumn, r: iRow + 1 })] = cell;\n }\n });\n });\n\n ws[\"!ref\"] = XLSX.utils.encode_range({\n s: { c: 0, r: 0 },\n e: { c: fieldsToExport.length - 1, r: filteredRows.length }\n });\n\n // Hoja 2: Metadata\n const metaWs: XLSX.WorkSheet = {};\n let metaRow = 0;\n\n const addMetaRow = (label: string, value: string) => {\n metaWs[XLSX.utils.encode_cell({ c: 0, r: metaRow })] = { t: 's', v: label, s: STYLE_HEADER };\n metaWs[XLSX.utils.encode_cell({ c: 1, r: metaRow })] = { t: 's', v: value };\n metaRow++;\n };\n\n addMetaRow('table', tableDefinition.name);\n addMetaRow('date', new Date().toISOString());\n addMetaRow('user', username);\n\n metaWs[\"!ref\"] = XLSX.utils.encode_range({\n s: { c: 0, r: 0 },\n e: { c: 1, r: metaRow - 1 }\n });\n\n const sheet1name = tableDefinition.name.length > 27 ? tableDefinition.name.slice(0, 27) + '...' : tableDefinition.name;\n const sheet2name = tableDefinition.name !== \"metadata\" ? \"metadata\" : \"meta-data\";\n\n XLSX.utils.book_append_sheet(wb, ws, sheet1name);\n XLSX.utils.book_append_sheet(wb, metaWs, sheet2name);\n\n const wbFile = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });\n const buf = new ArrayBuffer(wbFile.length);\n const view = new Uint8Array(buf);\n for (let i = 0; i < wbFile.length; i++) view[i] = wbFile.charCodeAt(i) & 0xFF;\n\n downloadFile(buf, `${tableDefinition.name}.xlsx`, 'application/octet-stream');\n };\n\n const downloadFile = (content: BlobPart, fileName: string, mimeType: string) => {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = fileName;\n document.body.appendChild(a);\n a.click();\n setTimeout(() => {\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n }, 0);\n };\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>Exportar Datos</DialogTitle>\n <DialogContent>\n {isPreparing ? (\n <Box display=\"flex\" flexDirection=\"column\" alignItems=\"center\" py={4}>\n <CircularProgress />\n <Typography sx={{ mt: 2 }}>Preparando exportación...</Typography>\n </Box>\n ) : (\n <Box sx={{ mt: 1 }}>\n <FormControl component=\"fieldset\">\n <FormLabel component=\"legend\">Formato</FormLabel>\n <RadioGroup\n value={format}\n onChange={(e) => setFormat(e.target.value as ExportFormat)}\n >\n <FormControlLabel value=\"xlsx\" control={<Radio />} label=\"Excel (.xlsx)\" />\n <FormControlLabel value=\"tab\" control={<Radio />} label=\"Tabulado (.tab | )\" />\n <FormControlLabel value=\"csv\" control={<Radio />} label=\"CSV (.csv , )\" />\n </RadioGroup>\n </FormControl>\n\n <Box sx={{ mt: 3 }}>\n <Typography variant=\"subtitle2\" gutterBottom>Opciones de campos:</Typography>\n <FormGroup>\n <FormControlLabel\n control={<Checkbox checked={options.fromOtherTables} onChange={handleOptionChange} name=\"fromOtherTables\" />}\n label=\"Desde otras tablas\"\n />\n <FormControlLabel\n control={<Checkbox checked={options.readOnly} onChange={handleOptionChange} name=\"readOnly\" />}\n label=\"Solo lectura\"\n />\n <FormControlLabel\n control={<Checkbox checked={options.hiddens} onChange={handleOptionChange} name=\"hiddens\" />}\n label=\"Ocultos\"\n />\n </FormGroup>\n </Box>\n </Box>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={isPreparing}>Cancelar</Button>\n <Button onClick={handlePrepareExport} variant=\"contained\" disabled={isPreparing}>\n Descargar\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useState, useCallback, useMemo, useRef } from 'react';\nimport {\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n Alert,\n Box,\n CircularProgress,\n IconButton,\n Typography\n} from '@mui/material';\nimport CloseIcon from '@mui/icons-material/Close';\nimport { FilaDeDatos, TableDefinition, FixedField } from '../types';\nimport { TypedField } from '../components/forms/TypedField';\nimport { useTableRecordSave } from '../hooks/useTableRecordSave';\nimport { getPrimaryKeyValues, sameValue } from '../components/grid/utils/helpers';\n\nimport typeStore from 'type-store';\nimport { mensajeDeError } from '../utils/errores';\n\nexport interface VerticalEditorPageProps {\n tableName: string;\n tableDefinition: TableDefinition;\n initialData: FilaDeDatos; // Puede tener nulos\n fixedFields?: FixedField[];\n isNewRow?: boolean;\n onSaveSuccess: (savedRow: FilaDeDatos, isNewRow: boolean) => void;\n onClose: () => void;\n}\n\nexport const VerticalEditorPage: React.FC<VerticalEditorPageProps> = ({\n tableName,\n tableDefinition,\n initialData,\n fixedFields,\n isNewRow = false,\n onSaveSuccess,\n onClose: onClose\n}) => {\n const { saveRecord } = useTableRecordSave();\n\n // Convertimos isNewRow y formData en referencias dinámicas para permitir múltiples auto-saves\n const [internalIsNewRow, setInternalIsNewRow] = useState(isNewRow);\n\n // Estado principal del formulario\n const [formData, setFormData] = useState<FilaDeDatos>(initialData);\n\n // Rastreamos el último estado guardado en backend para evitar tráfico redundante\n const [lastSavedData, setLastSavedData] = useState<FilaDeDatos>(initialData);\n\n // Rastreamos validez de campos individualmente\n const [validityMap, setValidityMap] = useState<Record<string, boolean>>({});\n\n const [globalError, setGlobalError] = useState<string | null>(null);\n const [isSaving, setIsSaving] = useState(false);\n\n const [fieldFeedback, setFieldFeedback] = useState<Record<string, 'success' | 'error'>>({});\n const feedbackTimersRef = useRef<Record<string, ReturnType<typeof setTimeout>>>({}); // timer por campo\n\n const handleFieldChange = useCallback((fieldName: string, newValue: unknown, isValid: boolean) => {\n setFormData(prev => ({ ...prev, [fieldName]: newValue }));\n\n setValidityMap(prev => ({ ...prev, [fieldName]: isValid }));\n setGlobalError(null);\n }, []);\n\n const isFormValid = useMemo(() => {\n // Un campo es inválido si explícitamente está marcado como false por DynamicField\n for (const key in validityMap) {\n if (validityMap[key] === false) return false;\n }\n\n // Además, verificar campos no nulos (isPk o nullable===false)\n for (const field of tableDefinition.fields) {\n const isMandatory = field.nullable === false || field.isPk;\n const val = formData[field.name];\n if (isMandatory && (val === null || val === undefined || String(val).trim() === '')) {\n // Hay fixed fields? Si es fixed field e inicializa como nulo (raro), igual no es válido.\n // Usualmente el initialData ya trae los fixedFields poblados\n return false;\n }\n }\n\n return true;\n }, [formData, validityMap, tableDefinition.fields]);\n\n const handleCommit = useCallback(async (fieldName: string, newValue: unknown, isValid: boolean) => {\n // En un autosave, si el campo es inválido localmente, no enviamos\n if (!isValid) return;\n\n // Diff check: si el valor es el mismo que está guardado en backend, no persistimos.\n // Usamos sameValue para comparación robusta (Dates, null/undefined, etc.)\n if (sameValue(newValue, lastSavedData[fieldName])) {\n return;\n }\n\n const potentialUpdatedRow = { ...formData, [fieldName]: newValue };\n\n let areAllMandatoryFieldsFilled = true;\n for (const field of tableDefinition.fields) {\n const isMandatory = field.nullable === false || field.isPk;\n const val = potentialUpdatedRow[field.name];\n if (isMandatory && (val === null || val === undefined || String(val).trim() === '')) {\n areAllMandatoryFieldsFilled = false;\n break;\n }\n }\n\n if (internalIsNewRow && !areAllMandatoryFieldsFilled) {\n // Faltan campos para poder insertar la fila nueva, no guardamos en el backend todavía\n return;\n }\n\n setIsSaving(true);\n setGlobalError(null);\n\n try {\n const response = await saveRecord({\n tableDefinition,\n isNewRow: internalIsNewRow,\n formData: potentialUpdatedRow,\n initialData,\n targetFieldName: fieldName\n });\n\n const responseRow = { ...response.row };\n\n // Actualizamos la data interna y nuestro \"último guardado\" con lo devuelto por el servidor\n setFormData(prev => ({ ...prev, ...responseRow }));\n setLastSavedData(prev => ({ ...prev, ...responseRow }));\n\n\n // Si la logramos crear, ya no es \"nueva\"\n const wasNewItem = internalIsNewRow;\n if (internalIsNewRow) {\n setInternalIsNewRow(false);\n }\n\n if (wasNewItem) {\n // Primer guardado: pintar todos los campos con valor en la respuesta del servidor\n const feedbackPatch: Record<string, 'success'> = {};\n for (const key of Object.keys(responseRow)) {\n const val = responseRow[key];\n if (val !== null && val !== undefined && val !== '') {\n feedbackPatch[key] = 'success';\n if (feedbackTimersRef.current[key]) clearTimeout(feedbackTimersRef.current[key]);\n feedbackTimersRef.current[key] = setTimeout(() =>\n setFieldFeedback(prev => { const n = { ...prev }; delete n[key]; return n; })\n , 3000);\n }\n }\n setFieldFeedback(prev => ({ ...prev, ...feedbackPatch }));\n } else {\n // Guardado parcial: solo el campo modificado\n setFieldFeedback(prev => ({ ...prev, [fieldName]: 'success' }));\n if (feedbackTimersRef.current[fieldName]) clearTimeout(feedbackTimersRef.current[fieldName]);\n feedbackTimersRef.current[fieldName] = setTimeout(() =>\n setFieldFeedback(prev => { const n = { ...prev }; delete n[fieldName]; return n; })\n , 3000);\n }\n\n onSaveSuccess(responseRow, wasNewItem);\n\n } catch (err) {\n setGlobalError(mensajeDeError(err, 'Error desconocido al guardar.'));\n // Error multi-feedback: cada campo tiene su propio timer\n setFieldFeedback(prev => ({ ...prev, [fieldName]: 'error' }));\n if (feedbackTimersRef.current[fieldName]) clearTimeout(feedbackTimersRef.current[fieldName]);\n feedbackTimersRef.current[fieldName] = setTimeout(() =>\n setFieldFeedback(prev => { const n = { ...prev }; delete n[fieldName]; return n; })\n , 5000);\n\n } finally {\n setIsSaving(false);\n }\n }, [\n saveRecord, formData, initialData, internalIsNewRow,\n tableName, tableDefinition, onSaveSuccess\n ]);\n\n\n\n\n // Campos a mostrar: todos menos los audit o sistemáticos (opcional, por ahora todos los fields expuestos en la UI)\n // También consideramos ocultar los fixed fields o mostrarlos deshabilitados\n return (\n <Box sx={{ p: 2, display: 'flex', flexDirection: 'column', height: '100%' }}>\n <DialogTitle sx={{ px: 0, pt: 0, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <Typography component=\"div\" variant=\"h6\">\n {isNewRow ? 'Nuevo Registro' : 'Editar Registro'} - {tableDefinition.title || tableName}\n </Typography>\n <IconButton onClick={onClose} size=\"small\" edge=\"end\">\n <CloseIcon />\n </IconButton>\n </DialogTitle>\n\n <DialogContent dividers sx={{ px: 2, flexGrow: 1, py: 1 }}>\n {globalError && (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {globalError}\n </Alert>\n )}\n\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>\n {tableDefinition.fields.map(field => {\n const isFixed = fixedFields?.some(ff => ff.fieldName === field.name);\n const isMandatory = field.nullable === false || tableDefinition.primaryKey.includes(field.name);\n return (\n <Box key={field.name} sx={{ width: '100%', maxWidth: '100%' }}>\n <TypedField\n fieldDef={field}\n value={formData[field.name]}\n onChange={handleFieldChange}\n onCommit={handleCommit}\n disabled={isFixed || field.editable === false}\n feedback={fieldFeedback[field.name]}\n isMandatory={isMandatory}\n />\n </Box>\n\n );\n })}\n </Box>\n\n </DialogContent>\n\n <DialogActions sx={{ px: 0, pb: 0, pt: 2, justifyContent: 'space-between', alignItems: 'center' }}>\n <Box>\n {isSaving && <CircularProgress size={20} sx={{ ml: 2 }} />}\n <span style={{ marginLeft: 10, fontSize: '0.8rem', color: '#666' }}>\n {isSaving ? 'Guardando...' : 'Cambios auto-guardados'}\n </span>\n </Box>\n <Button variant=\"outlined\" onClick={onClose}>Cerrar</Button>\n </DialogActions>\n </Box>\n );\n};\n\n","// Los parámetros con los que backend-plus abre una tabla (`wScreens.table` de my-menu.js): vienen de la dirección\n// (`#table=usuarios&ff=...&fc=...&td=...`) y del ítem de menú; los de la dirección pisan a los del ítem.\nimport JSON4all from 'json4all';\nimport { FixedField } from '../types';\n\n/** Una condición de la fila de filtros: `operator` es uno de los de backend-plus (=, ~, !~, >, >=, <, <=, !=, ∅, !=∅, /R/i) */\nexport interface FiltroInicial {\n column: string;\n operator: string;\n value: unknown;\n}\n\nexport interface ParametrosDeTabla {\n fixedFields?: FixedField[];\n /** Las filas de filtros que se muestran al abrir (cada fila es una alternativa; las condiciones de una fila se cumplen todas) */\n filterColumns?: FiltroInicial[][];\n /** Lo que se le cambia a la definición de la tabla que manda el servidor (se mezcla en profundidad) */\n tableDef?: Record<string, unknown>;\n parameterFunctions?: Record<string, unknown>;\n pick?: string;\n label?: string;\n}\n\ntype Registro = Record<string, unknown>;\n\nfunction esRegistro(valor: unknown): valor is Registro {\n return typeof valor === 'object' && valor !== null && !Array.isArray(valor) && !(valor instanceof Date);\n}\n\n/** Los parámetros que en la dirección van codificados con json4all (UriSearchToObjectParams de my-ajax.js) */\nconst CODIFICADOS = ['ff', 'fc', 'td', 'pf'];\n\n/**\n * Lee una dirección de backend-plus (`#a=1&b=...` o `?a=1&b=...`) como lo hace `UriSearchToObject`: se parte por `&` y por el\n * primer `=`, se decodifica con decodeURIComponent (no con URLSearchParams: ahí `+` sería un espacio) y los parámetros que\n * van en json4all se interpretan. Un valor que no se puede interpretar se ignora.\n */\nexport function parametrosDeDireccion(direccion: string): Registro {\n const resultado: Registro = {};\n direccion.split('&').forEach(par => {\n const sinMarca = par.replace(/^[#?]/, '');\n const igual = sinMarca.indexOf('=');\n if (igual === -1) return;\n const nombre = sinMarca.slice(0, igual);\n try {\n const texto = decodeURIComponent(sinMarca.slice(igual + 1));\n resultado[nombre] = CODIFICADOS.includes(nombre) ? JSON4all.parse(texto) : texto;\n } catch (err) {\n console.warn(`No se pudo interpretar el parámetro '${nombre}' de la dirección:`, err);\n }\n });\n return resultado;\n}\n\n/**\n * Los fixedFields: backend-plus admite un arreglo [{fieldName, value, ...}] o un objeto {campo: valor}\n * (`wScreens.table` de my-menu.js). Lo que no tiene forma de fixedField se descarta.\n */\nexport function aFixedFields(valor: unknown): FixedField[] {\n if (Array.isArray(valor)) {\n return valor.flatMap((par: unknown): FixedField[] => {\n if (!esRegistro(par) || typeof par.fieldName !== 'string') return [];\n const fixedField: FixedField = { fieldName: par.fieldName, value: par.value };\n if (par.until !== undefined) fixedField.until = par.until;\n if (typeof par.range === 'boolean') fixedField.range = par.range;\n if (typeof par.show === 'boolean') fixedField.show = par.show;\n return [fixedField];\n });\n }\n if (esRegistro(valor)) return Object.entries(valor).map(([fieldName, value]) => ({ fieldName, value }));\n return [];\n}\n\n/**\n * Las filas de filtros de `filterColumns`: un arreglo de condiciones (una fila) o un arreglo de arreglos (varias filas, que\n * son alternativas), como `createRowFilter` de my-tables.js. Una condición sin columna o sin operador no filtra nada.\n * El cliente de backend-plus llama `column` a la columna (el .d.ts de MenuInfoTable dice `fieldName` para el `fc` del menú,\n * pero my-menu.js se lo pasa tal cual a `filterColumns`): se aceptan las dos.\n */\nexport function aFiltrosIniciales(valor: unknown): FiltroInicial[][] {\n if (!Array.isArray(valor) || valor.length === 0) return [];\n const filas: unknown[] = Array.isArray(valor[0]) ? valor : [valor];\n return filas\n .map((fila: unknown): FiltroInicial[] => Array.isArray(fila)\n ? fila.flatMap((condicion: unknown): FiltroInicial[] => {\n if (!esRegistro(condicion) || typeof condicion.operator !== 'string') return [];\n const columna = condicion.column ?? condicion.fieldName;\n return typeof columna === 'string' ? [{ column: columna, operator: condicion.operator, value: condicion.value }] : [];\n })\n : [])\n .filter(fila => fila.length > 0);\n}\n\n/** Los parámetros de una tabla a partir de lo que declara el ítem de menú y de la dirección (esta última manda) */\nexport function parametrosDeTabla(item: Registro | undefined, direccion: string): ParametrosDeTabla {\n const origen: Registro = { ...item, ...parametrosDeDireccion(direccion) };\n const parametros: ParametrosDeTabla = {};\n const fixedFields = aFixedFields(origen.ff ?? origen.fixedFields);\n if (fixedFields.length > 0) parametros.fixedFields = fixedFields;\n const filterColumns = aFiltrosIniciales(origen.fc);\n if (filterColumns.length > 0) parametros.filterColumns = filterColumns;\n if (esRegistro(origen.td)) parametros.tableDef = origen.td;\n if (esRegistro(origen.pf)) parametros.parameterFunctions = origen.pf;\n if (typeof origen.pick === 'string') parametros.pick = origen.pick;\n if (typeof origen.label === 'string') parametros.label = origen.label;\n return parametros;\n}\n\n/** Mezcla en profundidad (como `changing` de best-globals): los objetos se mezclan, todo lo demás (arreglos incluidos) se reemplaza */\nexport function fusionar<T extends object>(base: T, cambios: Registro): T {\n const resultado = { ...base };\n for (const [clave, valor] of Object.entries(cambios)) {\n const actual: unknown = Reflect.get(resultado, clave);\n Reflect.set(resultado, clave, esRegistro(actual) && esRegistro(valor) ? fusionar(actual, valor) : valor);\n }\n return resultado;\n}\n","// Prefijo de las rutas de la aplicación cuando no está en la raíz del sitio (por ejemplo `/ggs2026/react` cuando la sirve\n// el propio backend). Lo define el bundler de la aplicación (`base` de Vite); sin prefijo es una cadena vacía.\nconst baseDelBundler: string = (import.meta as ImportMeta & { env?: Record<string, string | undefined> }).env?.BASE_URL ?? '/';\n\nexport const rutaBase: string = baseDelBundler.replace(/\\/+$/, '');\n","// frontend-plus-react/src/App.tsx (modificado en tu librería)\n\nimport React, { useEffect, useMemo } from 'react';\nimport './App.css';\nimport { BrowserRouter, Routes, Route, useLocation, Navigate, Outlet } from 'react-router-dom';\nimport MainLayout from './components/MainLayout';\nimport SessionExpiredMessage from './components/SessionExpiredMessage';\nimport { AppProvider } from './contexts/AppContext';\nimport PantallaInicial from './pages/PantallaInicial';\nimport LogoutPage from './pages/LogoutPage';\nimport LoginPage from './pages/LoginPage';\nimport ProcedureForm from './components/ProcedureForm';\n\nimport { Provider } from 'react-redux';\nimport { PersistGate } from 'redux-persist/integration/react';\nimport { store, persistor } from './store';\n\nimport PrivateRoute from './components/PrivateRoute';\nimport InitialRedirectHandler from './components/InitialRedirectHandler';\nimport { useAppDispatch } from './store';\nimport { setCurrentPath } from './store/routerSlice';\n\nimport { SnackbarProvider } from './contexts/SnackbarContext';\nimport GenericDataGridPage from './pages/GenericDataGridPage';\nimport { PantallaDeRuta } from './pages/PantallaDeRuta';\nimport MenuTablePage from './pages/MenuTablePage';\n\nimport { wScreens, extendWScreens } from './pages/WScreens';\nimport { PantallaDeRespaldo, PaginaBP } from './puente/PantallaBP';\nimport { PaginaGrillaBP } from './puente/GrillaBP';\nimport { CapturaBP } from './puente/CapturaBP';\nimport { habilitarPuenteBP, puenteBPHabilitado } from './puente/habilitacion';\nimport { extendClientSides } from './components/grid/clientSides';\nimport { extendConditions } from './components/grid/condiciones';\nimport { extendResultsOk } from './pages/procedure-results/resultsOk';\nimport typeStore from 'type-store';\n\nimport {\n extractPathsFromRoutes,\n AppProps,\n WScreenMap,\n ClientSidesMap,\n ResultsOksMap,\n ClientSideProps,\n ResultOkProps\n} from './types';\n\n\nimport { envConfig } from './env';\nimport { rutaBase } from './rutaBase';\n\nconst LocationTracker: React.FC = () => {\n const location = useLocation();\n const dispatch = useAppDispatch();\n useEffect(() => {\n dispatch(setCurrentPath(location.pathname + location.search));\n }, [location, dispatch]);\n return null;\n};\n\ninterface RoutesProps {\n myRoutes?: React.ReactNode;\n myUnloggedRoutes?: React.ReactNode;\n layout?: React.ComponentType;\n unloggedLayout?: React.ComponentType;\n}\n\nexport function FrontendPlusReactRoutes({ myRoutes, myUnloggedRoutes, layout: Layout = MainLayout, unloggedLayout: UnloggedLayout }: RoutesProps) {\n return (\n <>\n {/* Fuera de las rutas privadas: con la sesión válida pero el estado todavía sin cargar, PrivateRoute manda\n a /login; desde acá se vuelve a la ruta guardada (o a la pantalla de inicio) apenas el estado termina de cargarse. */}\n <InitialRedirectHandler />\n {puenteBPHabilitado() && <CapturaBP />}\n <Routes>\n <Route element={UnloggedLayout ? <UnloggedLayout /> : <Outlet />}>\n <Route path=\"/login\" element={<LoginPage />} />\n <Route path=\"/logout\" element={<LogoutPage />} />\n {myUnloggedRoutes}\n </Route>\n\n <Route element={\n <PrivateRoute>\n <Layout />\n </PrivateRoute>\n }>\n {/* la pantalla de inicio la define el backend (selectedByDefault), como en backend-plus */}\n <Route path=\"/\" element={<PantallaInicial />} />\n <Route path=\"/table/:tableName\" element={<GenericDataGridPage />} />\n <Route path=\"/menu/:menuName\" element={<MenuTablePage />} />\n <Route path=\"/procedures/:procedureName\" element={<ProcedureForm />} />\n {Object.keys(wScreens).map((screenName) => (\n <Route\n key={screenName}\n path={`/wScreens/${screenName}`}\n element={<PantallaDeRuta screenName={screenName} />}\n />\n ))}\n <Route path=\"/wScreens-fallback/:screenName\" element={<PantallaDeRespaldo />} />\n {puenteBPHabilitado() && <Route path=\"/bp/menu\" element={<PaginaBP />} />}\n {puenteBPHabilitado() && <Route path=\"/bp/table/:tableName\" element={<PaginaGrillaBP />} />}\n\n {myRoutes}\n\n <Route path=\"*\" element={<div style={{ marginTop: '20px', marginLeft: \"10px\" }}>404 - Recurso No Encontrado</div>} />\n </Route>\n\n <Route path=\"*\" element={<Navigate to=\"/\" replace />} />\n </Routes>\n </>\n );\n}\n\n\n// Los tipos WScreenMap, ClientSidesMap, ResultsOksMap y AppProps ahora se importan de ./types\n\n\nexport const FrontendPlusProviders: React.FC<{ children: React.ReactNode, publicPaths: string[] }> = ({ children, publicPaths }) => {\n return (\n <Provider store={store}>\n <PersistGate loading={null} persistor={persistor}>\n <BrowserRouter basename={rutaBase}>\n <LocationTracker /> {/* El tracker necesita estar dentro del Router */}\n <AppProvider publicPaths={publicPaths}>\n <SnackbarProvider>\n {children}\n <SessionExpiredMessage />\n </SnackbarProvider>\n </AppProvider>\n </BrowserRouter>\n </PersistGate>\n </Provider>\n );\n};\n\nconst App = ({\n myRoutes,\n myUnloggedRoutes,\n unloggedLayout,\n myWScreens,\n myClientSides,\n myResultsOk,\n myConditions,\n puenteBP\n}: AppProps & {\n /** Las wScreens que la aplicación no registró se piden al cliente de backend-plus (mismo origen que el backend) */\n puenteBP?: boolean\n}) => {\n // Lo que aporta la aplicación se registra antes del primer render (y no en un efecto): las rutas de las wScreens\n // se arman a partir del registro al renderizar, y un detalle puede necesitar una condición o una wScreen enseguida.\n useMemo(() => {\n if (myClientSides) {\n extendClientSides(myClientSides);\n }\n if (myResultsOk) {\n extendResultsOk(myResultsOk);\n }\n if (myWScreens) {\n extendWScreens(myWScreens);\n }\n if (myConditions) {\n extendConditions(myConditions);\n }\n if (puenteBP) {\n habilitarPuenteBP();\n }\n }, [myClientSides, myResultsOk, myWScreens, myConditions, puenteBP]);\n\n useEffect(() => {\n // --- DINAMICO: Configuración de TypeStore en español ---\n typeStore.messages = typeStore.i18n.messages.es;\n typeStore.locale = typeStore.i18n.locale.es;\n\n // --- DINAMICO: Favicon desde el Backend ---\n const link = (document.querySelector(\"link[rel*='icon']\") || document.createElement('link')) as HTMLLinkElement;\n link.type = 'image/png';\n link.rel = 'shortcut icon';\n link.href = `${envConfig.backendUrl}/img/logo-128.png`;\n document.getElementsByTagName('head')[0].appendChild(link);\n }, []);\n\n const safePaths = React.useMemo(() => {\n const extracted = extractPathsFromRoutes(myUnloggedRoutes);\n // Siempre añadimos login y logout como rutas base seguras\n return ['/login', '/logout', ...extracted];\n }, [myUnloggedRoutes]);\n return (\n <FrontendPlusProviders publicPaths={safePaths}>\n <FrontendPlusReactRoutes\n myRoutes={myRoutes}\n myUnloggedRoutes={myUnloggedRoutes}\n unloggedLayout={unloggedLayout} // Pasamos el layout\n />\n </FrontendPlusProviders>\n );\n}\n\nexport default App;","// src/components/MainLayout.tsx\r\nimport React, { useEffect } from 'react';\r\nimport { Outlet } from 'react-router-dom'; // ¡IMPORTA Outlet AQUÍ!\r\nimport {\r\n Box, AppBar, Toolbar, Typography, Button, Drawer, CssBaseline,\r\n IconButton, useTheme, useMediaQuery\r\n} from '@mui/material';\r\nimport { useApp } from '../contexts/AppContext';\r\nimport { useNavigate } from 'react-router-dom'; // No estoy seguro si 'useNavigate' es necesario aquí si solo se usa en SideMenu o para Logout\r\nimport LogoutIcon from '@mui/icons-material/Logout';\r\nimport MenuIcon from '@mui/icons-material/Menu';\r\nimport ChevronLeftIcon from '@mui/icons-material/ChevronLeft';\r\nimport SideMenu from './SideMenu';\r\nimport useLogout from '../hooks/useLogout';\r\nimport { envConfig } from '../env';\r\n\r\n// Importamos los hooks de Redux y las acciones del nuevo slice\r\nimport { useAppDispatch, useIsDrawerOpen } from '../store';\r\nimport { setDrawerOpen, toggleDrawer } from '../store/menuUiSlice';\r\n\r\n\r\nconst drawerWidth = 240;\r\n\r\n// const MainLayout: React.FC<MainLayoutProps> = ({ children }) => { // Quitamos { children }\r\nconst MainLayout: React.FC = () => { // MainLayout ya no recibe 'children' como prop\r\n const { clientContext } = useApp();\r\n const logout = useLogout();\r\n\r\n const isDrawerOpen = useIsDrawerOpen();\r\n const dispatch = useAppDispatch();\r\n\r\n const theme = useTheme();\r\n const isMobile = useMediaQuery(theme.breakpoints.down('sm'));\r\n\r\n useEffect(() => {\r\n if (isMobile && isDrawerOpen) {\r\n dispatch(setDrawerOpen(false));\r\n }\r\n }, [isMobile, isDrawerOpen, dispatch]);\r\n\r\n const handleToggleDrawer = () => {\r\n dispatch(toggleDrawer());\r\n };\r\n\r\n const handleDrawerCloseMobile = () => {\r\n if (isMobile && isDrawerOpen) {\r\n dispatch(setDrawerOpen(false));\r\n }\r\n };\r\n\r\n // Si clientContext no existe (por ejemplo, al cargar o si la sesión falla),\r\n // muestra un mensaje de carga.\r\n // Esto es importante para evitar errores si los datos no están disponibles.\r\n if (!clientContext) {\r\n return (\r\n <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>\r\n <Typography>Cargando diseño de la aplicación...</Typography>\r\n </Box>\r\n );\r\n }\r\n\r\n return (\r\n <Box sx={{ display: 'flex', height: '100vh' }}>\r\n <CssBaseline />\r\n <AppBar\r\n position=\"fixed\"\r\n sx={{\r\n zIndex: (theme) => theme.zIndex.drawer + 1,\r\n transition: (theme) => theme.transitions.create(['width', 'margin'], {\r\n easing: theme.transitions.easing.sharp,\r\n duration: theme.transitions.duration.leavingScreen,\r\n }),\r\n ...(isDrawerOpen && {\r\n width: `calc(100% - ${drawerWidth}px)`,\r\n marginLeft: `${drawerWidth}px`,\r\n transition: (theme) => theme.transitions.create(['width', 'margin'], {\r\n easing: theme.transitions.easing.easeOut,\r\n duration: theme.transitions.duration.enteringScreen,\r\n }),\r\n }),\r\n }}\r\n >\r\n <Toolbar>\r\n <IconButton\r\n color=\"inherit\"\r\n aria-label=\"open drawer\"\r\n onClick={handleToggleDrawer}\r\n edge=\"start\"\r\n sx={{\r\n marginRight: 2,\r\n }}\r\n >\r\n {isDrawerOpen ? <ChevronLeftIcon /> : <MenuIcon />}\r\n </IconButton>\r\n {!isDrawerOpen && (\r\n <img\r\n src={`${envConfig.backendUrl}/img/logo-128.png`}\r\n alt=\"logo\"\r\n style={{ height: '32px', marginRight: '12px', objectFit: 'contain' }}\r\n />\r\n )}\r\n <Typography variant=\"h6\" noWrap component=\"div\" sx={{ flexGrow: 1 }}>\r\n {isDrawerOpen ? '' : clientContext.config.title}\r\n </Typography>\r\n <Typography variant=\"subtitle1\" component=\"div\" sx={{ mr: 2 }}>\r\n {clientContext.username}\r\n </Typography>\r\n <Button\r\n color=\"inherit\"\r\n onClick={logout}\r\n startIcon={<LogoutIcon />}\r\n >\r\n Salir\r\n </Button>\r\n </Toolbar>\r\n </AppBar>\r\n <Drawer\r\n variant=\"persistent\"\r\n anchor=\"left\"\r\n open={isDrawerOpen}\r\n sx={{\r\n width: drawerWidth,\r\n flexShrink: 0,\r\n '& .MuiDrawer-paper': {\r\n width: drawerWidth,\r\n boxSizing: 'border-box',\r\n overflowX: 'hidden',\r\n overflowY: 'auto',\r\n },\r\n }}\r\n >\r\n <Toolbar\r\n sx={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'flex-start',\r\n px: 1,\r\n fontSize: '1.3rem',\r\n minHeight: theme.mixins.toolbar.minHeight,\r\n gap: 1.5\r\n }}\r\n >\r\n <img\r\n src={`${envConfig.backendUrl}/img/logo-128.png`}\r\n alt=\"logo\"\r\n style={{ height: '32px', objectFit: 'contain' }}\r\n />\r\n <Typography variant=\"h6\" sx={{ fontWeight: 'bold' }}>\r\n {clientContext.config.title}\r\n </Typography>\r\n </Toolbar>\r\n {/* Pasamos la función para cerrar el drawer al SideMenu */}\r\n <SideMenu onMenuItemClick={handleDrawerCloseMobile} />\r\n </Drawer>\r\n <Box\r\n component=\"main\"\r\n sx={{\r\n flexGrow: 1,\r\n transition: (theme) => theme.transitions.create('margin', {\r\n easing: theme.transitions.easing.sharp,\r\n duration: theme.transitions.duration.leavingScreen,\r\n }),\r\n marginLeft: `-${drawerWidth}px`,\r\n ...(isDrawerOpen && {\r\n transition: (theme) => theme.transitions.create('margin', {\r\n easing: theme.transitions.easing.easeOut,\r\n duration: theme.transitions.duration.enteringScreen,\r\n }),\r\n marginLeft: 0,\r\n }),\r\n minWidth: 0,\r\n flexShrink: 0,\r\n height: '100%',\r\n boxSizing: 'border-box',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n width: isDrawerOpen ? `calc(100% - ${drawerWidth}px)` : '100%',\r\n }}\r\n >\r\n {/* ¡AQUÍ ES DONDE DEBE IR EL CONTENIDO DE LA RUTA ANIDADA! */}\r\n {/* Elimina {children} y reemplázalo por <Outlet /> */}\r\n {/* El lugar de la AppBar (mide lo mismo que ella en cualquier tamaño de pantalla) queda fuera del scroll:\r\n si el contenido scrolleara desde el borde de la ventana, la barra taparía el extremo de la barra de desplazamiento. */}\r\n <Toolbar />\r\n <Box sx={{ flex: 1, minHeight: 0, overflowY: 'auto', display: 'flex', flexDirection: 'column' }}>\r\n <Outlet />\r\n </Box>\r\n </Box>\r\n </Box>\r\n );\r\n};\r\n\r\nexport default MainLayout;","// src/contexts/AppContext.tsx\r\nimport React, { createContext, useContext, useState, useEffect, ReactNode, useCallback, useMemo, useRef } from 'react';\r\nimport { fetchApi } from '../utils/fetchApi';\r\nimport { useLocation, useNavigate } from 'react-router-dom';\r\n\r\n// Importa los hooks de Redux y las acciones de tu slice\r\nimport { useSelector, useDispatch } from 'react-redux';\r\nimport { RootState, AppDispatch } from '../store'; // Asegúrate de que la ruta sea correcta\r\nimport { fetchClientContext, clearClientContext } from '../store/clientContextSlice'; // Importa el thunk y la acción\r\nimport { AppContextType, AppProviderProps } from '../types';\r\n\r\n// Crea el contexto con un valor por defecto que se sobrescribirá\r\nconst AuthContext = createContext<AppContextType | undefined>(undefined);\r\n\r\n// Hook personalizado para usar el contexto de autenticación\r\nexport const useApp = () => {\r\n const context = useContext(AuthContext);\r\n if (!context) {\r\n throw new Error('useApp must be used within an AppProvider');\r\n }\r\n // Si quieres que 'useApp' siga devolviendo 'clientContext', lo obtendrías aquí de Redux:\r\n const clientContext = useSelector((state: RootState) => state.clientContext);\r\n return { ...context, clientContext };\r\n};\r\n\r\nexport const AppProvider: React.FC<AppProviderProps> = ({ children, publicPaths=[] }) => {\r\n console.log(\"AppProvider rendered/re-rendered\");\r\n\r\n const [isLoggedIn, setIsLoggedIn] = useState(false);\r\n const [showSessionExpiredMessage, setShowSessionExpiredMessage] = useState(false);\r\n\r\n const isRedirectingRef = useRef(false);\r\n // ¿Esta persona tuvo una sesión (en esta carga de la página o guardada de antes)? Solo entonces corresponde\r\n // avisar que \"la sesión expiró\"; quien entra por primera vez sin sesión va al login sin ese cartel.\r\n const huboSesionRef = useRef(false);\r\n \r\n \r\n \r\n const dispatch = useDispatch<AppDispatch>();\r\n const clientContext = useSelector((state: RootState) => state.clientContext);\r\n // si lo guardado de la visita anterior decía que había sesión, vencerse es un \"expiró\" y no una primera visita\r\n if (clientContext.status === 'succeeded') huboSesionRef.current = true;\r\n \r\n const navigate = useNavigate();\r\n\r\n const handleExpiredSessionRedirect = useCallback(() => {\r\n // A. Activamos el bloqueo inmediatamente\r\n isRedirectingRef.current = true;\r\n \r\n // B. Cerramos modal y navegamos\r\n setShowSessionExpiredMessage(false);\r\n setIsLoggedIn(false);\r\n navigate('/login', { replace: true });\r\n\r\n // C. (Opcional) Desbloqueamos después de un tiempo prudencial por si el usuario vuelve\r\n setTimeout(() => {\r\n isRedirectingRef.current = false;\r\n }, 2000);\r\n }, [navigate]);\r\n\r\n const location = useLocation();\r\n\r\n const isCurrentPathPublic = useCallback(() => {\r\n const current = location.pathname;\r\n // Verificamos si la ruta actual está en la lista de permitidas\r\n // Usamos startsWith para manejar sub-rutas si fuera necesario, o coincidencia exacta\r\n return publicPaths.some(path => current === path || current.startsWith(`${path}/`));\r\n }, [location.pathname, publicPaths]);\r\n\r\n // Memoizamos checkSession. Ahora despachará acciones de Redux.\r\n const checkSession = useCallback(async () => {\r\n if (isRedirectingRef.current) return;\r\n try {\r\n const response = await fetchApi('/keep-alive.json', { method: 'GET' });\r\n if (isRedirectingRef.current) return;\r\n if (response.ok) {\r\n huboSesionRef.current = true;\r\n setIsLoggedIn(true);\r\n setShowSessionExpiredMessage(false);\r\n\r\n // Si clientContext no está cargado o si hubo un error previo,\r\n // despachamos el thunk para cargarlo.\r\n // Con redux-persist, si ya estaba en localStorage, 'clientContext.status' será 'succeeded'.\r\n if (clientContext.status === 'idle' || clientContext.status === 'failed') {\r\n console.log(\"Client context needs loading/reloading, dispatching fetchClientContext thunk...\");\r\n try {\r\n // .unwrap() para que el 'catch' capture los errores del thunk\r\n await dispatch(fetchClientContext()).unwrap();\r\n console.log(\"Client context data loaded successfully by Redux.\");\r\n } catch (loadError) {\r\n console.error(\"Error loading client context data during session check:\", loadError);\r\n // No es necesario lanzar el error, el estado de Redux ya lo manejará\r\n }\r\n }\r\n } else {\r\n // Si la sesión no es válida, limpiamos el estado de autenticación y el clientContext en Redux\r\n setIsLoggedIn(false);\r\n dispatch(clearClientContext()); // Disparar la acción para limpiar el clientContext en Redux\r\n\r\n if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current) {\r\n console.warn(\"Sesión inválida en ruta protegida. Mostrando modal.\");\r\n setShowSessionExpiredMessage(true);\r\n } else {\r\n console.log(\"Sesión inválida, pero no hay nada que avisar (ruta pública o nunca hubo sesión).\");\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\"Error checking session:\", error);\r\n setIsLoggedIn(false);\r\n dispatch(clearClientContext()); // Limpiar clientContext en Redux\r\n const isPublic = publicPaths.some(p => location.pathname === p || location.pathname.startsWith(p + '/'));\r\n if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current) {\r\n setShowSessionExpiredMessage(true);\r\n }\r\n }\r\n }, [dispatch, clientContext.status, showSessionExpiredMessage, isCurrentPathPublic]); // Dependencias: dispatch (estable), y el estado de carga del clientContext\r\n\r\n useEffect(() => {\r\n checkSession();\r\n\r\n const intervalId = setInterval(checkSession, 15 * 1000);\r\n\r\n return () => {\r\n clearInterval(intervalId);\r\n };\r\n }, [checkSession]); // Se ejecuta una vez al montar, y se re-ejecuta si checkSession cambia (no debería si useCallback está bien)\r\n\r\n // ¡La CLAVE! Memoizamos el objeto de contexto.\r\n // clientContext y setClientContext ya NO van aquí, se acceden directamente vía Redux.\r\n const authContextValue = useMemo(() => ({\r\n isLoggedIn,\r\n setIsLoggedIn,\r\n checkSession,\r\n showSessionExpiredMessage,\r\n setShowSessionExpiredMessage,\r\n handleExpiredSessionRedirect\r\n }), [\r\n isLoggedIn,\r\n setIsLoggedIn,\r\n checkSession,\r\n showSessionExpiredMessage,\r\n setShowSessionExpiredMessage,\r\n handleExpiredSessionRedirect\r\n ]);\r\n\r\n return (\r\n <AuthContext.Provider value={authContextValue}>\r\n {children}\r\n </AuthContext.Provider>\r\n );\r\n};","import React, { useEffect } from 'react';\r\nimport { List, ListItem, ListItemButton, ListItemIcon, ListItemText, Divider, Box, Typography, Collapse } from '@mui/material';\r\nimport ExpandLess from '@mui/icons-material/ExpandLess';\r\nimport ExpandMore from '@mui/icons-material/ExpandMore';\r\nimport TableChartIcon from '@mui/icons-material/TableChart';\r\nimport FolderIcon from '@mui/icons-material/Folder';\r\nimport DnsIcon from '@mui/icons-material/Dns';\r\nimport WarningAmberIcon from '@mui/icons-material/WarningAmber';\r\nimport DesktopWindowsIcon from '@mui/icons-material/DesktopWindows';\r\nimport UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';\r\nimport UnfoldLessIcon from '@mui/icons-material/UnfoldLess';\r\n\r\nimport { useNavigate, useLocation } from 'react-router-dom';\r\nimport { useApp } from '../contexts/AppContext';\r\nimport { MenuInfoBase, MenuInfoTable, MenuInfoProc, MenuInfoMenu } from \"backend-plus\";\r\nimport { blue, orange, teal, red, grey } from '@mui/material/colors';\r\n\r\nimport { useSubMenuOpenState, useAppDispatch, useAppSelector } from '../store';\r\nimport { toggleSubMenu, setAllSubMenusOpen, setSubMenuOpen } from '../store/menuUiSlice';\r\nimport { MenuListItemProps, SideMenuProps } from '../types';\r\n\r\nimport { wScreens } from '../pages/WScreens';\r\nimport { rutaDeItemDeMenu } from '../utils/menuInicial';\r\n\r\nconst MenuListItem: React.FC<MenuListItemProps> = ({ item, level, onMenuItemClick }) => {\r\n const navigate = useNavigate();\r\n const location = useLocation();\r\n\r\n const open = useSubMenuOpenState(item.name);\r\n const dispatch = useAppDispatch();\r\n\r\n const handleClick = () => {\r\n if (item.menuType === \"menu\") {\r\n dispatch(toggleSubMenu(item.name));\r\n } else {\r\n navigate(rutaDeItemDeMenu(item));\r\n if (onMenuItemClick) {\r\n onMenuItemClick();\r\n }\r\n }\r\n };\r\n\r\n const getIcon = (item: MenuInfoBase) => {\r\n switch (item.menuType) {\r\n case \"table\":\r\n return <TableChartIcon sx={{ color: blue[700] }} />;\r\n case \"proc\":\r\n return <DnsIcon sx={{ color: teal[700] }} />;\r\n case \"menu\":\r\n return <FolderIcon sx={{ color: orange[800] }} />;\r\n default:\r\n const WScreenComponent = wScreens[item.menuType];\r\n if (WScreenComponent !== undefined) {\r\n return <DesktopWindowsIcon sx={{ color: grey[700] }} />;\r\n }\r\n return <WarningAmberIcon sx={{ color: red[500] }} />;\r\n }\r\n };\r\n\r\n const currentPath = location.pathname;\r\n let isSelected = false;\r\n\r\n // Obtener tableName de la URL si existe\r\n const pathParts = currentPath.split('/');\r\n const urlTableName = pathParts[1] === 'table' ? pathParts[2] : null;\r\n\r\n if (item.menuType === \"table\") {\r\n // Seleccionado si coincide la ruta de menú o la ruta directa de tabla\r\n isSelected = currentPath === `/menu/${item.name}` || (urlTableName === (item.table || item.name));\r\n } else if (item.menuType === \"proc\") {\r\n isSelected = currentPath === `/procedures/${item.name}`;\r\n } else if (item.menuType !== \"menu\") {\r\n isSelected = currentPath.startsWith(`/wScreens/${item.menuType}`) || currentPath.startsWith(`/wScreens-fallback/${item.menuType}`);\r\n }\r\n\r\n // SCROLL AUTOMÁTICO: Cuando se selecciona un ítem, lo traemos a la vista\r\n const itemRef = React.useRef<HTMLDivElement>(null);\r\n useEffect(() => {\r\n if (isSelected && itemRef.current) {\r\n itemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\r\n }\r\n }, [isSelected]);\r\n\r\n return (\r\n <div ref={itemRef}>\r\n <ListItemButton\r\n onClick={handleClick}\r\n sx={{ pl: level * 2 }}\r\n selected={isSelected}\r\n >\r\n <ListItemIcon sx={{ minWidth: '38px' }}>\r\n {getIcon(item)}\r\n </ListItemIcon>\r\n <ListItemText primary={item.label || item.name} />\r\n {item.menuType === \"menu\" ? (open ? <ExpandLess /> : <ExpandMore />) : null}\r\n </ListItemButton>\r\n {item.menuType === \"menu\" && (\r\n <Collapse in={open} timeout=\"auto\" unmountOnExit>\r\n <List component=\"div\" disablePadding>\r\n {(item as MenuInfoMenu).menuContent.map((subItem) => (\r\n <MenuListItem\r\n key={subItem.name}\r\n item={subItem}\r\n level={level + 1}\r\n onMenuItemClick={onMenuItemClick}\r\n />\r\n ))}\r\n </List>\r\n </Collapse>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\n\r\nconst SideMenu: React.FC<SideMenuProps> = ({ onMenuItemClick }) => {\r\n\r\n const { clientContext } = useApp();\r\n const navigate = useNavigate();\r\n const location = useLocation();\r\n const dispatch = useAppDispatch();\r\n\r\n const subMenuOpenStates = useAppSelector(state => state.menuUi.subMenuOpenStates);\r\n\r\n const allSubMenusKeys = Object.keys(subMenuOpenStates);\r\n const openSubMenusCount = allSubMenusKeys.filter(key => subMenuOpenStates[key]).length;\r\n const isMostlyOpen = allSubMenusKeys.length > 0 && openSubMenusCount / allSubMenusKeys.length > 0.5;\r\n\r\n const handleToggleAllSubMenus = () => {\r\n dispatch(setAllSubMenusOpen(!isMostlyOpen));\r\n };\r\n\r\n if (!clientContext || !clientContext.menu) {\r\n return (\r\n <Box sx={{ p: 2, textAlign: 'center' }}>\r\n <Typography variant=\"body2\" color=\"text.secondary\">Cargando menú...</Typography>\r\n </Box>\r\n );\r\n }\r\n\r\n // AUTO-EXPANSIÓN: Expandir padres según la ruta actual\r\n useEffect(() => {\r\n if (!clientContext?.menu) return;\r\n\r\n const expandParents = (items: MenuInfoBase[], targetPath: string, parentNames: string[] = []): boolean => {\r\n const pathParts = targetPath.split('/');\r\n const urlTableName = pathParts[1] === 'table' ? pathParts[2] : null;\r\n\r\n for (const item of items) {\r\n let matches = false;\r\n if (item.menuType === \"table\") {\r\n matches = targetPath === `/menu/${item.name}` || (urlTableName === (item.table || item.name));\r\n } else if (item.menuType === \"proc\") {\r\n matches = targetPath === `/procedures/${item.name}`;\r\n } else if (item.menuType !== \"menu\") {\r\n matches = targetPath.startsWith(`/wScreens/${item.menuType}`) || targetPath.startsWith(`/wScreens-fallback/${item.menuType}`);\r\n }\r\n\r\n if (matches) {\r\n // Encontramos el ítem, expandimos todos sus padres\r\n parentNames.forEach(name => {\r\n dispatch(setSubMenuOpen({ menuName: name, isOpen: true }));\r\n });\r\n return true;\r\n }\r\n\r\n if (item.menuType === \"menu\") {\r\n const found = expandParents((item as MenuInfoMenu).menuContent, targetPath, [...parentNames, item.name]);\r\n if (found) return true;\r\n }\r\n }\r\n return false;\r\n };\r\n\r\n expandParents(clientContext.menu, location.pathname);\r\n }, [location.pathname, clientContext, dispatch]);\r\n\r\n return (\r\n <Box sx={{ width: '100%', flexShrink: 0 }}>\r\n {clientContext.menu.some(item => item.menuType === \"menu\") && (\r\n <ListItem disablePadding>\r\n <ListItemButton onClick={handleToggleAllSubMenus}>\r\n <ListItemIcon sx={{ minWidth: '38px' }}>\r\n {isMostlyOpen ? <UnfoldLessIcon /> : <UnfoldMoreIcon />}\r\n </ListItemIcon>\r\n <ListItemText primary={isMostlyOpen ? \"colapsar \" : \"expandir\"} />\r\n </ListItemButton>\r\n </ListItem>\r\n )}\r\n <Divider />\r\n <List>\r\n {clientContext.menu.map((menuItem: MenuInfoBase) => (\r\n <MenuListItem\r\n key={menuItem.name}\r\n item={menuItem}\r\n level={1}\r\n onMenuItemClick={onMenuItemClick}\r\n />\r\n ))}\r\n </List>\r\n </Box>\r\n );\r\n};\r\n\r\nexport default SideMenu;","// La pantalla de inicio la define el backend, como en backend-plus: es el ítem de menú con `selectedByDefault`.\nimport { MenuInfoBase, MenuInfoMenu } from 'backend-plus';\nimport { wScreens } from '../pages/WScreens';\n\n/**\n * La ruta que muestra un ítem de menú: las tablas van a `/menu/:nombre` (con `td` y `fc` en la query string; los\n * `fixedFields` los toma la página del propio menú), los procedimientos a `/procedures/:nombre` y lo demás son wScreens.\n */\nexport function rutaDeItemDeMenu(item: MenuInfoBase): string {\n if (item.menuType === 'table') {\n const consulta = new URLSearchParams();\n if (item.td) consulta.append('td', JSON.stringify(item.td));\n if (item.fc) consulta.append('fc', JSON.stringify(item.fc));\n return `/menu/${item.name}${consulta.size ? `?${consulta.toString()}` : ''}`;\n }\n if (item.menuType === 'proc') return `/procedures/${item.name}`;\n if (wScreens[item.menuType] !== undefined) return `/wScreens/${item.menuType}`;\n console.warn(`Menu type '${item.menuType}' no reconocido o WScreen no mapeada.`);\n return `/wScreens-fallback/${item.menuType}`;\n}\n\n/**\n * El ítem que se muestra al entrar: como `my-menu.js`, el que declara `selectedByDefault` y, si es un grupo, el que\n * declare así adentro. Si el backend no declara ninguno (o el grupo no tiene uno por omisión) no hay pantalla de inicio.\n */\nexport function itemInicial(menu: MenuInfoBase[]): MenuInfoBase | undefined {\n const elegido = menu.find(item => item.selectedByDefault);\n if (!elegido) return undefined;\n return elegido.menuType === 'menu' ? itemInicial((elegido as MenuInfoMenu).menuContent) : elegido;\n}\n","// src/hooks/useLogout.ts\r\nimport { useCallback } from 'react';\r\nimport { useNavigate } from 'react-router-dom';\r\n\r\n// Importa los hooks de Redux y la acción clearClientContext\r\nimport { useDispatch } from 'react-redux';\r\nimport { AppDispatch } from '../store'; // Asegúrate de que la ruta sea correcta\r\nimport { clearClientContext } from '../store/clientContextSlice'; // Importa la acción para limpiar el contexto\r\n\r\nimport { useApp } from '../contexts/AppContext'; // Para acceder a setIsLoggedIn\r\nimport { fetchApi } from '../utils/fetchApi'; // Para la llamada a la API\r\n\r\n/**\r\n * Hook personalizado para manejar la lógica de cierre de sesión.\r\n * Limpia el estado del frontend y realiza la petición al backend.\r\n * @returns Una función 'logout' que, al ser llamada, cierra la sesión.\r\n */\r\nconst useLogout = () => {\r\n // Obtenemos las funciones del contexto de la aplicación\r\n const { setIsLoggedIn } = useApp(); // setClientContext ya no viene de useApp()\r\n\r\n // Obtenemos el dispatch de Redux\r\n const dispatch = useDispatch<AppDispatch>();\r\n\r\n // Obtenemos la función de navegación de React Router\r\n const navigate = useNavigate();\r\n\r\n // Utilizamos useCallback para memoizar la función 'logout'\r\n const logout = useCallback(async () => {\r\n try {\r\n const response = await fetchApi('/logout', { method: 'GET' }); // Asumiendo que tienes un endpoint /logout\r\n \r\n // Puedes ajustar esta lógica de verificación de respuesta\r\n // Si el backend siempre devuelve 401 para un logout exitoso (lo cual es inusual,\r\n // normalmente es un 200 OK y luego el backend invalida la sesión),\r\n // entonces tu lógica actual es válida.\r\n if (response.status === 401 || response.ok) { // Considera response.ok también si el backend da 200\r\n console.log('Sesión cerrada correctamente o ya deslogueado');\r\n } else {\r\n console.error('Error al cerrar sesión:', await response.text());\r\n // Si el logout falla en el backend, podrías decidir no limpiar el estado local\r\n // o mostrar un error al usuario. Por ahora, lo limpiamos igual.\r\n }\r\n } catch (error) {\r\n console.error('Error de red al intentar cerrar sesión:', error);\r\n } finally {\r\n // Siempre limpiamos el estado de login en el frontend,\r\n // independientemente del éxito o fracaso de la petición al backend.\r\n setIsLoggedIn(false); // Actualiza el estado de login en el contexto\r\n \r\n // --- ¡CAMBIO CLAVE AQUÍ! ---\r\n // Despacha la acción de Redux para limpiar el clientContext\r\n dispatch(clearClientContext()); \r\n\r\n navigate('/login', { replace: true }); // Redirige a /login\r\n }\r\n }, [setIsLoggedIn, dispatch, navigate]); // Dependencias: setIsLoggedIn, dispatch (estable), navigate (estable)\r\n\r\n return logout; // Devolvemos la función 'logout'\r\n};\r\n\r\nexport default useLogout;","import React from 'react';\r\nimport { Box, Typography, Button, Modal } from '@mui/material';\r\nimport { useApp } from '../contexts/AppContext'; // Importa useApp para el control del modal\r\nimport { useNavigate } from 'react-router-dom';\r\n\r\nconst style = {\r\n position: 'absolute' as 'absolute',\r\n top: '50%',\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n width: 400,\r\n bgcolor: 'background.paper',\r\n border: '2px solid #000',\r\n boxShadow: 24,\r\n p: 4,\r\n textAlign: 'center',\r\n borderRadius: 2,\r\n};\r\n\r\nconst SessionExpiredMessage: React.FC = () => {\r\n const { showSessionExpiredMessage, setShowSessionExpiredMessage, setIsLoggedIn, handleExpiredSessionRedirect } = useApp();\r\n const handleClose = () => {\r\n handleExpiredSessionRedirect(); \r\n };\r\n\r\n return (\r\n <Modal\r\n open={showSessionExpiredMessage}\r\n // Proporciona una función vacía para onClose. Esto satisface el tipo de prop\r\n // y evita que el modal se cierre por defecto al hacer clic en el backdrop.\r\n onClose={() => {}} \r\n aria-labelledby=\"session-expired-modal-title\"\r\n aria-describedby=\"session-expired-modal-description\"\r\n disableEscapeKeyDown={true} // Impide cerrar con la tecla Escape\r\n slotProps={{\r\n backdrop: {\r\n // Esto es crucial en MUI v5: detiene la propagación del evento de clic en el backdrop,\r\n // impidiendo que el modal se cierre al hacer clic fuera.\r\n onClick: (event) => event.stopPropagation(),\r\n sx: {\r\n backgroundColor: 'rgba(100, 100, 100, 0.95)', // Ajusta el color y la opacidad para un fondo más oscuro\r\n },\r\n },\r\n }}\r\n >\r\n <Box sx={style}>\r\n <Typography id=\"session-expired-modal-title\" variant=\"h6\" component=\"h2\" gutterBottom>\r\n ¡Sesión Expirada!\r\n </Typography>\r\n <Typography id=\"session-expired-modal-description\" sx={{ mt: 2, mb: 3 }}>\r\n Tu sesión ha expirado por inactividad o por seguridad. Por favor, inicia sesión de nuevo.\r\n </Typography>\r\n <Button variant=\"contained\" color=\"primary\" onClick={handleClose}>\r\n Ir al Login\r\n </Button>\r\n </Box>\r\n </Modal>\r\n );\r\n};\r\n\r\nexport default SessionExpiredMessage;\r\n","import React from 'react';\nimport { Navigate } from 'react-router-dom';\nimport { useApp } from '../contexts/AppContext';\nimport { itemInicial, rutaDeItemDeMenu } from '../utils/menuInicial';\n\n/** Ruta `/`: va a la pantalla de inicio que declara el backend (`selectedByDefault`). Si no declara ninguna, queda vacía, como en backend-plus. */\nconst PantallaInicial: React.FC = () => {\n const { clientContext } = useApp();\n const item = clientContext.menu ? itemInicial(clientContext.menu) : undefined;\n return item ? <Navigate to={rutaDeItemDeMenu(item)} replace /> : null;\n};\n\nexport default PantallaInicial;\n","// src/pages/LogoutPage.tsx\r\nimport React, { useEffect } from 'react';\r\nimport { Box, Typography, CircularProgress } from '@mui/material';\r\nimport useLogout from '../hooks/useLogout';\r\n\r\nconst LogoutPage: React.FC = () => {\r\n const logout = useLogout(); \r\n\r\n useEffect(() => {\r\n // Llama a la función de logout del hook cuando el componente se monta\r\n logout();\r\n }, [logout]); // Dependencia: la función 'logout' en sí misma\r\n\r\n return (\r\n <Box sx={{\r\n display: 'flex',\r\n flexDirection: 'column',\r\n justifyContent: 'center',\r\n alignItems: 'center',\r\n height: '100vh',\r\n textAlign: 'center'\r\n }}>\r\n <CircularProgress size={60} />\r\n <Typography variant=\"h6\" sx={{ mt: 2 }}>Cerrando sesión...</Typography>\r\n </Box>\r\n );\r\n};\r\n\r\nexport default LogoutPage;","import React, { useState } from 'react';\r\nimport { Button, TextField, Typography, Alert, AlertTitle, Box, CircularProgress } from '@mui/material'; // Importaciones de MUI\r\nimport { useForm, Controller } from 'react-hook-form';\r\nimport { zodResolver } from '@hookform/resolvers/zod';\r\nimport * as z from 'zod';\r\n\r\n// --- ¡NUEVAS IMPORTACIONES DE REDUX! ---\r\nimport { useDispatch } from 'react-redux';\r\nimport { AppDispatch } from '../store'; // Asegúrate de que la ruta sea correcta\r\nimport { fetchClientContext } from '../store/clientContextSlice'; // Importa el thunk para cargar el contexto\r\n\r\nimport { useApp } from '../contexts/AppContext'; // Sigue importando useApp para setIsLoggedIn y checkSession\r\nimport { fetchApi } from '../utils/fetchApi'; // Tu utilidad fetchApi\r\nimport { envConfig } from '../env';\r\nimport { mensajeDeError } from '../utils/errores';\r\n\r\n// Define el esquema de validación con Zod\r\nconst loginSchema = z.object({\r\n username: z.string().min(1, \"El usuario es requerido\"), // Añadir validación básica\r\n password: z.string().min(1, \"La contraseña es requerida\"), // Añadir validación básica\r\n});\r\n\r\n// Define el tipo para los datos del formulario\r\ntype LoginFormValues = z.infer<typeof loginSchema>;\r\n\r\nconst LoginPage = () => {\r\n const [loginError, setLoginError] = useState<string | null>(null);\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n\r\n // Obtiene setIsLoggedIn y checkSession del contexto de la aplicación\r\n const { setIsLoggedIn, checkSession } = useApp();\r\n\r\n // Obtiene el dispatch de Redux\r\n const dispatch = useDispatch<AppDispatch>();\r\n\r\n const {\r\n control,\r\n handleSubmit,\r\n formState: { errors },\r\n reset\r\n } = useForm<LoginFormValues>({\r\n resolver: zodResolver(loginSchema),\r\n defaultValues: {\r\n username: '',\r\n password: '',\r\n },\r\n });\r\n\r\n const onSubmit = async (data: LoginFormValues) => {\r\n setIsSubmitting(true);\r\n setLoginError(null); // Limpia cualquier error anterior\r\n\r\n try {\r\n const body = new URLSearchParams({\r\n username: data.username,\r\n password: data.password\r\n });\r\n\r\n const loginResponse = await fetchApi('/login', {\r\n method: 'POST',\r\n body, // URLSearchParams se envía directamente como body para form-urlencoded\r\n // Asegúrate de que el Content-Type en el backend maneje 'application/x-www-form-urlencoded'\r\n });\r\n\r\n // Si el backend redirige a /menu, significa que el login fue exitoso.\r\n // Si la respuesta no es OK, o si la URL no termina en /menu, asumimos un fallo.\r\n if (!loginResponse.ok || !loginResponse.url.endsWith('/menu')) {\r\n //const errorText = await loginResponse.text();\r\n const errorText = 'Credenciales incorrectas o error al iniciar sesión';\r\n throw new Error(errorText);\r\n }\r\n\r\n // Si el login fue exitoso, actualiza el estado de la sesión y carga el contexto del cliente\r\n setIsLoggedIn(true);\r\n\r\n // --- ¡CAMBIO CLAVE AQUÍ! ---\r\n // Despacha el thunk de Redux para cargar el clientContext.\r\n // El .unwrap() es importante para poder capturar errores directamente con try/catch.\r\n await dispatch(fetchClientContext()).unwrap();\r\n\r\n console.log(\"Login exitoso y clientContext cargado.\");\r\n // el destino lo decide InitialRedirectHandler (la ruta que se pidió antes del login o, si no hubo, el inicio): navegar acá\r\n // también pisaría esa ruta con la pantalla de inicio\r\n reset(); // Limpia el formulario\r\n } catch (error) {\r\n console.error(\"Error al iniciar sesión:\", error);\r\n setIsLoggedIn(false); // Marca como no logueado en caso de error\r\n // setClientContext(null); // No necesitas llamar a setClientContext aquí, Redux lo maneja\r\n setLoginError(mensajeDeError(error, 'Ocurrió un error al iniciar sesión. Inténtalo de nuevo.'));\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n };\r\n\r\n return (\r\n <Box\r\n display=\"flex\"\r\n alignItems=\"center\"\r\n justifyContent=\"center\"\r\n minHeight=\"100vh\"\r\n bgcolor=\"#f5f5f5\"\r\n >\r\n <Box\r\n width=\"100%\"\r\n maxWidth=\"400px\"\r\n padding={4}\r\n bgcolor=\"white\"\r\n borderRadius={8}\r\n boxShadow={3}\r\n >\r\n <Box sx={{ display: 'flex', justifyContent: 'center', mb: 3 }}>\r\n <img\r\n src={`${envConfig.backendUrl}/img/login-logo-icon.png`}\r\n alt=\"Logo\"\r\n style={{ height: '192px', maxWidth: '100%', objectFit: 'contain' }}\r\n />\r\n </Box>\r\n <Typography variant=\"h5\" align=\"center\" gutterBottom sx={{ fontWeight: 'bold', color: 'primary.main' }}>\r\n Iniciar Sesión\r\n </Typography>\r\n <form onSubmit={handleSubmit(onSubmit)} className=\"space-y-4\">\r\n <div>\r\n <label htmlFor=\"username\">\r\n <Typography variant=\"subtitle1\" component=\"div\" sx={{ fontWeight: 'medium' }}>\r\n Usuario\r\n </Typography>\r\n </label>\r\n <Controller\r\n name=\"username\"\r\n control={control}\r\n render={({ field }) => (\r\n <TextField\r\n {...field}\r\n type=\"text\" // Cambiado de \"username\" a \"text\" que es más estándar para input type\r\n placeholder=\"Ingresa tu usuario\"\r\n fullWidth\r\n margin=\"normal\"\r\n error={!!errors.username}\r\n helperText={errors.username?.message}\r\n disabled={isSubmitting}\r\n />\r\n )}\r\n />\r\n </div>\r\n <div>\r\n <label htmlFor=\"password\">\r\n <Typography variant=\"subtitle1\" component=\"div\" sx={{ fontWeight: 'medium' }}>\r\n Contraseña\r\n </Typography>\r\n </label>\r\n <Controller\r\n name=\"password\"\r\n control={control}\r\n render={({ field }) => (\r\n <TextField\r\n {...field}\r\n type=\"password\"\r\n placeholder=\"Ingresa tu contraseña\"\r\n fullWidth\r\n margin=\"normal\"\r\n error={!!errors.password}\r\n helperText={errors.password?.message}\r\n disabled={isSubmitting}\r\n />\r\n )}\r\n />\r\n </div>\r\n <Button type=\"submit\" fullWidth variant=\"contained\" color=\"primary\" disabled={isSubmitting} sx={{ mt: 2 }}>\r\n {isSubmitting ? (\r\n <>\r\n <CircularProgress size={20} sx={{ mr: 1 }} />\r\n Iniciando sesión...\r\n </>\r\n ) : (\r\n 'Iniciar Sesión'\r\n )}\r\n </Button>\r\n </form>\r\n {loginError && (\r\n <Alert severity=\"error\" sx={{ mt: 2, maxHeight: '200px' }}> {/* Ya no necesitas alertProps */}\r\n <AlertTitle>Error</AlertTitle>\r\n {loginError}\r\n </Alert>\r\n )}\r\n </Box>\r\n </Box>\r\n );\r\n};\r\n\r\nexport default LoginPage;","import React, { useState, FormEvent, ChangeEvent, useCallback, useMemo } from 'react';\r\nimport { ProcedureDef, ProcedureParameter } from \"backend-plus\";\r\nimport { useParams } from 'react-router-dom';\r\nimport { useApp } from '../contexts/AppContext';\r\nimport { mensajeDeError } from '../utils/errores';\r\n\r\nimport { useApiCall } from '../hooks/useApiCall';\r\n\r\nimport {\r\n TextField,\r\n Button,\r\n FormControl,\r\n InputLabel,\r\n Select,\r\n MenuItem,\r\n Checkbox,\r\n FormControlLabel,\r\n FormHelperText,\r\n Typography,\r\n Box,\r\n CircularProgress,\r\n Alert,\r\n Divider,\r\n Paper\r\n} from '@mui/material';\r\nimport { SelectChangeEvent } from '@mui/material/Select';\r\nimport { cambiarGuionesBajosPorEspacios } from '../utils/functions';\r\n\r\n// Importamos el mapa de componentes de resultado\r\nimport { resultsOk } from '../pages/procedure-results/resultsOk';\r\n\r\nconst ProcedureForm: React.FC = () => {\r\n const { procedureName } = useParams<{ procedureName: string }>();\r\n const { clientContext } = useApp();\r\n\r\n const { callApi, loading: apiLoading, error: apiError } = useApiCall();\r\n\r\n const [validationErrors, setValidationErrors] = useState<Record<string, string | null>>({});\r\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\r\n const [executionLog, setExecutionLog] = useState<string[]>([]);\r\n const [procedureResultData, setProcedureResultData] = useState<unknown>(null);\r\n\r\n const procedure: ProcedureDef | undefined = useMemo(() => {\r\n if (procedureName && clientContext?.procedure) {\r\n return clientContext.procedure[procedureName];\r\n }\r\n return undefined;\r\n }, [procedureName, clientContext]);\r\n\r\n const [formValues, setFormValues] = useState<Record<string, unknown>>(() => {\r\n if (!procedure) return {};\r\n const initialValues: Record<string, unknown> = {};\r\n procedure.parameters.forEach(param => {\r\n if (param.defaultValue !== undefined) {\r\n initialValues[param.name] = param.defaultValue;\r\n } else if (param.specialDefaultValue) {\r\n switch (param.specialDefaultValue) {\r\n case 'current_date':\r\n initialValues[param.name] = new Date().toISOString().split('T')[0];\r\n break;\r\n case 'current_datetime':\r\n const now = new Date();\r\n initialValues[param.name] = now.toISOString().substring(0, 16);\r\n break;\r\n default:\r\n initialValues[param.name] = param.specialDefaultValue;\r\n break;\r\n }\r\n } else {\r\n initialValues[param.name] = '';\r\n }\r\n });\r\n return initialValues;\r\n });\r\n\r\n React.useEffect(() => {\r\n if (procedure) {\r\n setValidationErrors({});\r\n setSuccessMessage(null);\r\n setProcedureResultData(null);\r\n const newInitialValues: Record<string, unknown> = {};\r\n procedure.parameters.forEach(param => {\r\n if (param.defaultValue !== undefined) {\r\n newInitialValues[param.name] = param.defaultValue;\r\n } else if (param.specialDefaultValue) {\r\n switch (param.specialDefaultValue) {\r\n case 'current_date':\r\n newInitialValues[param.name] = new Date().toISOString().split('T')[0];\r\n break;\r\n case 'current_datetime':\r\n const now = new Date();\r\n newInitialValues[param.name] = now.toISOString().substring(0, 16);\r\n break;\r\n default:\r\n newInitialValues[param.name] = param.specialDefaultValue;\r\n break;\r\n }\r\n } else {\r\n newInitialValues[param.name] = '';\r\n }\r\n });\r\n setFormValues(newInitialValues);\r\n setExecutionLog([]);\r\n }\r\n }, [procedure]);\r\n\r\n const addLogMessage = useCallback((message: string, isError: boolean = false) => {\r\n const timestamp = new Date().toLocaleTimeString('es-AR');\r\n setExecutionLog(prev => [...prev, `[${timestamp}] ${isError ? 'ERROR: ' : ''}${message}`]);\r\n }, []);\r\n\r\n const handleChange = useCallback((e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | SelectChangeEvent<unknown>, child?: React.ReactNode) => {\r\n const { name, value } = e.target;\r\n let newValue: unknown = value;\r\n\r\n if ((e.target as HTMLInputElement).type === 'checkbox') {\r\n newValue = (e.target as HTMLInputElement).checked;\r\n }\r\n\r\n setFormValues(prevValues => ({\r\n ...prevValues,\r\n [name]: newValue,\r\n }));\r\n setValidationErrors(prev => ({ ...prev, [name]: null }));\r\n setSuccessMessage(null);\r\n setProcedureResultData(null);\r\n }, []);\r\n\r\n const handleSubmit = async (e: FormEvent) => {\r\n e.preventDefault();\r\n setValidationErrors({});\r\n setSuccessMessage(null);\r\n setExecutionLog([]);\r\n setProcedureResultData(null);\r\n\r\n if (!procedure) {\r\n addLogMessage('Error: La definición del procedimiento no está disponible.', true);\r\n return;\r\n }\r\n\r\n addLogMessage(`Iniciando ejecución del procedimiento: ${procedure.action}...`);\r\n\r\n const paramsToSend: Record<string, unknown> = {};\r\n let currentValidationErrors: Record<string, string | null> = {};\r\n let hasOverallError = false;\r\n\r\n for (const param of procedure.parameters) {\r\n let value = formValues[param.name];\r\n let processedValue: unknown = value;\r\n let paramError: string | null = null;\r\n\r\n switch (param.typeName) {\r\n case 'decimal':\r\n case 'double':\r\n if (value === '' || value === null) {\r\n processedValue = null;\r\n } else {\r\n processedValue = parseFloat(String(value));\r\n if (typeof processedValue === 'number' && isNaN(processedValue)) {\r\n paramError = `El valor para \"${param.label || param.name}\" no es un número válido.`;\r\n }\r\n }\r\n break;\r\n case 'integer':\r\n case 'bigint':\r\n if (value === '' || value === null) {\r\n processedValue = null;\r\n } else {\r\n processedValue = parseInt(String(value), 10);\r\n if (typeof processedValue === 'number' && isNaN(processedValue)) {\r\n paramError = `El valor para \"${param.label || param.name}\" no es un entero válido.`;\r\n }\r\n }\r\n break;\r\n case 'boolean':\r\n processedValue = !!value;\r\n break;\r\n case 'jsonb':\r\n case 'jsona':\r\n if (value === '' || value === null) {\r\n processedValue = null;\r\n } else {\r\n try {\r\n processedValue = JSON.parse(String(value));\r\n } catch (jsonError) {\r\n paramError = `Error en el formato JSON para \"${param.label || param.name}\": ${mensajeDeError(jsonError)}`;\r\n }\r\n }\r\n break;\r\n case 'bytea':\r\n processedValue = value instanceof File ? value : (value === '' ? null : value);\r\n break;\r\n case 'text':\r\n case 'date':\r\n case 'timestamp':\r\n case 'time':\r\n case 'interval':\r\n case 'tsrange':\r\n case 'time_range':\r\n case 'daterange':\r\n default:\r\n processedValue = value === '' ? null : value;\r\n break;\r\n }\r\n\r\n const hasDefaultValue = param.defaultValue !== undefined || param.specialDefaultValue !== undefined;\r\n const isEmpty = processedValue === null || processedValue === undefined || (typeof processedValue === 'string' && processedValue.trim() === '');\r\n \r\n if (!hasDefaultValue && isEmpty) {\r\n paramError = `El campo \"${param.label || param.name}\" es obligatorio y no puede estar vacío.`;\r\n }\r\n\r\n if (paramError) {\r\n currentValidationErrors[param.name] = paramError;\r\n hasOverallError = true;\r\n addLogMessage(`Error de validación para \"${param.label || param.name}\": ${paramError}`, true);\r\n }\r\n\r\n paramsToSend[param.name] = processedValue;\r\n }\r\n\r\n if (hasOverallError) {\r\n setValidationErrors(currentValidationErrors);\r\n addLogMessage('No se puede ejecutar el procedimiento debido a errores de validación en el formulario.', true);\r\n return;\r\n }\r\n\r\n addLogMessage(`Parámetros a enviar: ${JSON.stringify(paramsToSend, null, 2)}`);\r\n\r\n try {\r\n addLogMessage('Invocando procedimiento a través de useApiCall...');\r\n const result = await callApi(procedure.action, paramsToSend);\r\n \r\n if (result !== undefined) {\r\n setSuccessMessage(procedure.resultOk || 'Procedimiento ejecutado con éxito.');\r\n addLogMessage('Procedimiento ejecutado con éxito.');\r\n addLogMessage(`Resultado: ${JSON.stringify(result, null, 2)}`);\r\n setProcedureResultData(result);\r\n console.log('Resultado del procedimiento:', result);\r\n } else {\r\n addLogMessage('Procedimiento finalizado con errores.', true);\r\n }\r\n } catch (err) {\r\n console.error('Error al ejecutar el procedimiento (capturado en ProcedureForm):', err);\r\n addLogMessage(`Error en la ejecución: ${mensajeDeError(err, 'Error desconocido.')}`, true);\r\n } finally {\r\n addLogMessage('Ejecución finalizada.');\r\n }\r\n };\r\n\r\n const renderInputField = useCallback((param: ProcedureParameter) => {\r\n const hasError = !!validationErrors[param.name];\r\n const errorMessage = validationErrors[param.name];\r\n\r\n const commonProps = {\r\n id: param.name,\r\n name: param.name,\r\n label: param.label || param.name,\r\n variant: \"outlined\" as \"outlined\",\r\n fullWidth: true,\r\n margin: \"dense\" as \"dense\",\r\n required: !(param.defaultValue !== undefined || param.specialDefaultValue !== undefined),\r\n size: \"small\" as \"small\",\r\n helperText: errorMessage || param.description,\r\n error: hasError,\r\n disabled: apiLoading,\r\n onChange: handleChange,\r\n sx: { fontSize: '0.8rem' }\r\n };\r\n\r\n if (param.options && param.options.length > 0) {\r\n return (\r\n <FormControl fullWidth margin=\"dense\" size=\"small\" error={hasError} required={commonProps.required}>\r\n <InputLabel id={`${param.name}-label`} sx={{ fontSize: '0.8rem' }}>{param.label || param.name}</InputLabel>\r\n <Select\r\n labelId={`${param.name}-label`}\r\n value={formValues[param.name] === undefined ? '' : formValues[param.name]}\r\n onChange={handleChange}\r\n name={param.name}\r\n id={param.name}\r\n label={param.label || param.name}\r\n disabled={apiLoading}\r\n sx={{ fontSize: '0.8rem' }}\r\n renderValue={(selected) => (\r\n <Typography sx={{ fontSize: '0.8rem' }}>{selected as string}</Typography>\r\n )}\r\n >\r\n {(!commonProps.required || formValues[param.name] === '') && <MenuItem value=\"\" sx={{ fontSize: '0.8rem' }}><em>Seleccione...</em></MenuItem>}\r\n {param.options.map(option => (\r\n <MenuItem key={option} value={option} sx={{ fontSize: '0.8rem' }}>\r\n {option}\r\n </MenuItem>\r\n ))}\r\n </Select>\r\n {(param.description || errorMessage) && <FormHelperText sx={{ fontSize: '0.7rem' }}>{errorMessage || param.description}</FormHelperText>}\r\n </FormControl>\r\n );\r\n }\r\n\r\n switch (param.typeName) {\r\n case 'text':\r\n case 'interval':\r\n case 'tsrange':\r\n case 'time_range':\r\n case 'daterange':\r\n return <TextField type=\"text\" value={formValues[param.name] || ''} {...commonProps} />;\r\n case 'decimal':\r\n case 'double':\r\n return <TextField type=\"number\" inputProps={{ step: \"any\", style: { fontSize: '0.8rem' } }} value={formValues[param.name] || ''} {...commonProps} />;\r\n case 'integer':\r\n case 'bigint':\r\n return <TextField type=\"number\" inputProps={{ step: \"1\", style: { fontSize: '0.8rem' } }} value={formValues[param.name] || ''} {...commonProps} />;\r\n case 'boolean':\r\n return (\r\n <FormControlLabel\r\n control={\r\n <Checkbox\r\n checked={!!formValues[param.name]}\r\n onChange={handleChange}\r\n name={param.name}\r\n id={param.name}\r\n disabled={apiLoading}\r\n size=\"small\"\r\n />\r\n }\r\n label={<Typography sx={{ fontSize: '0.8rem', color: hasError ? 'error.main' : 'inherit' }}>{param.label || param.name}</Typography>}\r\n sx={{ mt: 1, mb: 0 }}\r\n />\r\n );\r\n case 'date':\r\n case 'timestamp':\r\n case 'time':\r\n return <TextField type={param.typeName === 'date' ? 'date' : param.typeName === 'timestamp' ? 'datetime-local' : 'time'} value={formValues[param.name] || ''} {...commonProps} InputLabelProps={{ shrink: true, sx: { fontSize: '0.8rem' } }} />;\r\n case 'jsonb':\r\n case 'jsona':\r\n return <TextField multiline rows={5} value={formValues[param.name] || ''} {...commonProps} InputProps={{ sx: { fontSize: '0.8rem' } }} />;\r\n case 'bytea':\r\n return (\r\n <FormControl fullWidth margin=\"dense\" error={hasError} required={commonProps.required}>\r\n <Typography variant=\"body2\" color=\"textSecondary\" component=\"label\" htmlFor={param.name} sx={{ fontSize: '0.8rem', color: hasError ? 'error.main' : 'text.secondary' }}>\r\n {param.label || param.name}\r\n </Typography>\r\n <input\r\n type=\"file\"\r\n id={param.name}\r\n name={param.name}\r\n onChange={(e: ChangeEvent<HTMLInputElement>) => {\r\n const files = e.target.files;\r\n setFormValues(prev => ({ ...prev, [param.name]: files && files.length > 0 ? files[0] : undefined }));\r\n }}\r\n disabled={apiLoading}\r\n style={{ padding: '6px 0', fontSize: '0.8rem' }}\r\n />\r\n {(param.description || errorMessage) && <FormHelperText sx={{ fontSize: '0.7rem' }}>{errorMessage || param.description}</FormHelperText>}\r\n </FormControl>\r\n );\r\n default:\r\n return <TextField type=\"text\" value={formValues[param.name] || ''} {...commonProps} />;\r\n }\r\n }, [formValues, handleChange, apiLoading, validationErrors]);\r\n\r\n // Contenido a renderizar dentro de la sección de resultado\r\n const ResultDisplayContent = useMemo(() => {\r\n if (!procedureResultData || !procedure?.resultOk) return null;\r\n\r\n const ComponentToRender = resultsOk[procedure.resultOk];\r\n\r\n if (ComponentToRender) {\r\n return <ComponentToRender data={procedureResultData} />;\r\n } else {\r\n return (\r\n <Paper elevation={2} sx={{ p: 2, mt: 0, bgcolor: 'warning.light' }}>\r\n <Typography variant=\"h6\" sx={{ fontSize: '1rem', mb: 1 }}>\r\n el componente para resolver el resultOk: '{procedure.resultOk}' definido en el procedure no existe o no está mapeado.\r\n </Typography>\r\n <Box sx={{ \r\n bgcolor: 'warning.dark', \r\n p: 1, \r\n borderRadius: 1, \r\n }}>\r\n <pre style={{ margin: 0, fontSize: '0.75rem', color: 'white' }}>\r\n {JSON.stringify(procedureResultData, null, 2)}\r\n </pre>\r\n </Box>\r\n </Paper>\r\n );\r\n }\r\n }, [procedureResultData, procedure]);\r\n\r\n\r\n if (!procedure) {\r\n return (\r\n <Box sx={{\r\n p: 2,\r\n width: '100%',\r\n boxSizing: 'border-box',\r\n mt: 0,\r\n bgcolor: 'background.paper',\r\n borderRadius: 2,\r\n boxShadow: 3,\r\n textAlign: 'center'\r\n }}>\r\n <CircularProgress sx={{ mb: 2 }} />\r\n <Typography variant=\"h6\" color=\"text.secondary\">\r\n Cargando procedimiento o no encontrado...\r\n </Typography>\r\n </Box>\r\n );\r\n }\r\n\r\n return (\r\n // Esta es la Box más externa que ahora controlará el scroll\r\n <Box sx={{\r\n p: 2,\r\n ml: 1,\r\n mt: 2,\r\n mr: 1,\r\n boxSizing: 'border-box',\r\n bgcolor: 'background.paper',\r\n borderRadius: 2,\r\n boxShadow: 3,\r\n fontSize: '0.8rem',\r\n maxHeight: 'calc(100vh - 100px)', // Ajusta esta altura máxima según el espacio disponible en tu layout\r\n overflowY: 'auto', // Habilita el scroll vertical para todo el contenido de esta Box\r\n overflowX: 'hidden' // Oculta el scroll horizontal si no es necesario para el contenedor principal\r\n }}>\r\n <Typography variant=\"h5\" component=\"h2\" gutterBottom align=\"left\">\r\n {cambiarGuionesBajosPorEspacios(procedure.proceedLabel || procedure.action)}\r\n </Typography>\r\n\r\n {Object.values(validationErrors).some(err => err !== null) && (\r\n <Alert severity=\"error\" sx={{ mb: 2, fontSize: '0.8rem' }}>\r\n Por favor, corrige los errores en el formulario antes de enviar.\r\n <ul>\r\n {Object.values(validationErrors).map((err, idx) => err && <li key={idx}>{err}</li>)}\r\n </ul>\r\n </Alert>\r\n )}\r\n {!Object.values(validationErrors).some(err => err !== null) && apiError && <Alert severity=\"error\" sx={{ mb: 2, fontSize: '0.8rem' }}>{apiError.message}</Alert>}\r\n {!Object.values(validationErrors).some(err => err !== null) && !apiError && successMessage && <Alert severity=\"success\" sx={{ mb: 2, fontSize: '0.8rem' }}>{successMessage}</Alert>}\r\n\r\n <form onSubmit={handleSubmit}>\r\n {procedure.parameters.map(param => (\r\n <div key={param.name}>\r\n {renderInputField(param)}\r\n </div>\r\n ))}\r\n\r\n <Button\r\n type=\"submit\"\r\n variant=\"contained\"\r\n color=\"primary\"\r\n disabled={apiLoading || Object.values(validationErrors).some(err => err !== null)}\r\n sx={{ mt: 2 }}\r\n size=\"medium\"\r\n startIcon={apiLoading ? <CircularProgress size={20} color=\"inherit\" /> : null}\r\n >\r\n {apiLoading ? 'Ejecutando...' : (procedure.proceedLabel || 'Ejecutar')}\r\n </Button>\r\n </form>\r\n\r\n {/* Renderizamos el contenido del resultado, sin scroll propio aquí */}\r\n {ResultDisplayContent && (\r\n <Box sx={{ mt: 2 }}> {/* Margen superior para separar del botón */}\r\n {ResultDisplayContent}\r\n </Box>\r\n )}\r\n \r\n {/* Área para el registro de ejecución del procedimiento */}\r\n {executionLog.length > 0 && (\r\n <Box sx={{ mt: 3 }}>\r\n <Divider sx={{ mb: 2 }} />\r\n <Typography variant=\"h6\" gutterBottom sx={{ fontSize: '0.9rem' }}>\r\n Registro de Ejecución\r\n </Typography>\r\n <Box\r\n sx={{\r\n bgcolor: 'grey.100',\r\n p: 2,\r\n borderRadius: 1,\r\n maxHeight: '200px', // Este log puede tener su propio scroll interno si es muy largo\r\n overflowY: 'auto',\r\n fontFamily: 'monospace',\r\n fontSize: '0.75rem',\r\n whiteSpace: 'pre-wrap',\r\n }}\r\n >\r\n {executionLog.map((log, index) => (\r\n <Typography\r\n key={index}\r\n component=\"pre\"\r\n sx={{\r\n margin: 0,\r\n color: log.includes('ERROR:') ? 'error.main' : 'text.primary',\r\n fontSize: 'inherit'\r\n }}\r\n >\r\n {log}\r\n </Typography>\r\n ))}\r\n </Box>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n};\r\n\r\nexport default ProcedureForm;","import React from 'react';\r\nimport { Box, Typography, Paper } from '@mui/material';\r\nimport { ResultOkProps } from '../../types';\r\n\r\nconst SuccessDisplay: React.FC<ResultOkProps> = ({ data }) => {\r\n return (\r\n <Paper elevation={2} sx={{ p: 2, mt: 2, bgcolor: 'success.light', color: 'success.contrastText' }}>\r\n <Typography variant=\"h6\" sx={{ fontSize: '1rem', mb: 1 }}>\r\n ¡Operación Exitosa!\r\n </Typography>\r\n <Typography variant=\"body2\" sx={{ fontSize: '0.85rem' }}>\r\n Detalles del resultado:\r\n </Typography>\r\n <Box sx={{ bgcolor: 'success.dark', p: 1, borderRadius: 1, mt: 1, overflowX: 'auto' }}>\r\n <pre style={{ margin: 0, fontSize: '0.75rem', color: 'white' }}>\r\n {JSON.stringify(data, null, 2)}\r\n </pre>\r\n </Box>\r\n <Typography variant=\"body2\" sx={{ fontSize: '0.8rem', mt: 1 }}>\r\n Puedes revisar los detalles anteriores o cerrar este mensaje.\r\n </Typography>\r\n </Paper>\r\n );\r\n};\r\n\r\nexport default SuccessDisplay;","import React from 'react';\r\n\r\nimport SuccessDisplay from './SuccessDisplay';\r\n\r\nimport { ResultOkProps } from '../../types';\r\n\r\n// La definición de ResultOkProps se movió a ../../types\r\n\r\n\r\ntype ResultComponentMap = {\r\n [key: string]: React.FC<ResultOkProps>; \r\n};\r\n\r\nexport let resultsOk: ResultComponentMap = {\r\n 'successful_operation': SuccessDisplay as React.FC<ResultOkProps>,\r\n};\r\n\r\nexport const extendResultsOk = (newComponents: ResultComponentMap) => {\r\n resultsOk = { ...resultsOk, ...newComponents };\r\n};","// src/components/PrivateRoute.tsx\r\nimport React, { useEffect, ReactNode } from 'react';\r\nimport { Navigate, useLocation } from 'react-router-dom';\r\nimport { useAppDispatch, useClientContextStatus } from '../store'; // Importa useDispatch, useClientContextStatus\r\nimport { setRedirectPath } from '../store/routerSlice'; // Importa la acción para guardar la ruta de redirección\r\nimport { PrivateRouteProps } from '../types';\r\n\r\nconst PrivateRoute: React.FC<PrivateRouteProps> = ({ children }) => {\r\n const location = useLocation();\r\n const dispatch = useAppDispatch();\r\n const clientContextStatus = useClientContextStatus(); // Obtiene el estado de Redux\r\n\r\n const isAuthenticated = clientContextStatus === 'succeeded';\r\n const isLoadingAuth = clientContextStatus === 'loading';\r\n\r\n console.log(`[PrivateRoute] Render: Path=${location.pathname}, AuthStatus=${clientContextStatus}, IsAuthenticated=${isAuthenticated}, IsLoadingAuth=${isLoadingAuth}`);\r\n\r\n useEffect(() => {\r\n console.log(`[PrivateRoute] Effect: Path=${location.pathname}, AuthStatus=${clientContextStatus}, IsAuthenticated=${isAuthenticated}, IsLoadingAuth=${isLoadingAuth}`);\r\n // Condición para guardar la URL:\r\n // 1. El usuario NO está autenticado.\r\n // 2. NO estamos en el proceso de carga de autenticación (ya sabemos que no está autenticado).\r\n // 3. NO estamos ya en la página de login (para evitar guardar /login).\r\n if (!isAuthenticated && !isLoadingAuth && location.pathname !== '/login') {\r\n const path = location.pathname + location.search + location.hash;\r\n console.log(`[PrivateRoute] ACTION: Dispatching setRedirectPath(${path})`);\r\n dispatch(setRedirectPath(path));\r\n } else if (isAuthenticated) {\r\n console.log(`[PrivateRoute] User is authenticated, no redirectPath set.`);\r\n } else if (isLoadingAuth) {\r\n console.log(`[PrivateRoute] Still loading authentication.`);\r\n } else if (location.pathname === '/login') {\r\n console.log(`[PrivateRoute] Currently on /login, not setting redirectPath.`);\r\n }\r\n }, [isAuthenticated, isLoadingAuth, location, dispatch, clientContextStatus]); // Añadir clientContextStatus a las dependencias del efecto\r\n\r\n if (isLoadingAuth) {\r\n console.log(`[PrivateRoute] Returning loading state...`);\r\n return <p>Verificando autenticación...</p>;\r\n }\r\n\r\n if (!isAuthenticated) {\r\n console.log(`[PrivateRoute] User not authenticated, navigating to /login`);\r\n return <Navigate to=\"/login\" replace />;\r\n }\r\n\r\n console.log(`[PrivateRoute] User authenticated, rendering children.`);\r\n return <>{children}</>;\r\n};\r\n\r\nexport default PrivateRoute;","// src/components/InitialRedirectHandler.tsx\r\nimport React, { useEffect, useRef } from 'react';\r\nimport { useLocation, useNavigate } from 'react-router-dom';\r\nimport { useAppDispatch, useRedirectPath, useClientContextStatus } from '../store'; // <--- ¡Asegúrate de importar useClientContextStatus de aquí!\r\nimport { setCurrentPath, setRedirectPath } from '../store/routerSlice';\r\n\r\nconst InitialRedirectHandler: React.FC = () => {\r\n const location = useLocation();\r\n const navigate = useNavigate();\r\n const dispatch = useAppDispatch();\r\n const redirectPath = useRedirectPath();\r\n const clientContextStatus = useClientContextStatus(); // <--- ¡Ahora lo obtienes de Redux!\r\n\r\n // Efecto para despachar la ruta actual al cambiar\r\n useEffect(() => {\r\n dispatch(setCurrentPath(location.pathname + location.search));\r\n }, [location, dispatch]);\r\n\r\n // Después de navegar a la ruta guardada, el efecto de abajo se vuelve a ejecutar (redirectPath pasó a null) mientras\r\n // la ruta todavía figura como /login: sin esta marca caería en \"ir a la pantalla de inicio por defecto\" y se perdería el destino.\r\n const navegandoRef = useRef(false);\r\n useEffect(() => {\r\n navegandoRef.current = false;\r\n }, [location.pathname]);\r\n\r\n // Efecto para manejar la redirección inicial (después de login o recarga si ya estaba logueado)\r\n useEffect(() => {\r\n // Asegúrate de que el clientContext esté cargado/autenticado antes de intentar redirigir\r\n if (clientContextStatus === 'succeeded') {\r\n if (redirectPath && redirectPath !== '/login') { // Evita redirigir a /login\r\n console.log('Redirigiendo a la ruta guardada:', redirectPath);\r\n navegandoRef.current = true;\r\n navigate(redirectPath);\r\n dispatch(setRedirectPath(null)); // Limpia la ruta guardada después de usarla\r\n } else if (!navegandoRef.current && location.pathname === '/login') {\r\n // Si el usuario está en /login y ya está autenticado y no hay ruta de redirección\r\n // lo mandamos a / (PantallaInicial lo lleva a la pantalla de inicio que declara el backend).\r\n console.log('Usuario autenticado, redirigiendo a la pantalla de inicio.');\r\n navigate('/');\r\n }\r\n }\r\n }, [clientContextStatus, redirectPath, location.pathname, navigate, dispatch]);\r\n\r\n return null; // No renderiza nada\r\n};\r\n\r\nexport default InitialRedirectHandler;","// src/pages/GenericDataGridPage.tsx\r\nimport React, { useState, useCallback, useEffect, useMemo } from 'react';\r\nimport { useParams, useLocation } from 'react-router-dom';\r\nimport { Box, Alert, Typography, Tabs, Tab, IconButton } from '@mui/material';\r\nimport CloseIcon from '@mui/icons-material/Close';\r\nimport GenericDataGrid from '../components/grid/GenericDataGrid';\r\nimport { FixedField, Ancestor } from '../types';\r\nimport { cambiarGuionesBajosPorEspacios } from '../utils/functions';\r\nimport { ParametrosDeTabla, parametrosDeTabla } from '../utils/parametrosDeTabla';\r\n\r\ninterface TabItem {\r\n id: string;\r\n label: string;\r\n tableName: string;\r\n fixedFields: FixedField[];\r\n /** Lo que declara la dirección o el ítem de menú de la tabla principal (filtros iniciales, `td`, `pf`, `pick`) */\r\n parametros?: ParametrosDeTabla;\r\n ancestors?: Ancestor[];\r\n}\r\n\r\ninterface GenericDataGridPageProps {\r\n initialTableName?: string;\r\n initialFixedFields?: FixedField[];\r\n /** Los parámetros de la tabla que ya resolvió quien la llama (el menú); sin ellos se leen de la dirección */\r\n parametros?: ParametrosDeTabla;\r\n}\r\n\r\nconst GenericDataGridPage: React.FC<GenericDataGridPageProps> = ({ \r\n initialTableName, \r\n initialFixedFields = [],\r\n parametros: parametrosRecibidos\r\n}) => {\r\n const { tableName: urlTableName } = useParams<{ tableName?: string }>();\r\n const effectiveTableName = initialTableName || urlTableName;\r\n \r\n const [tabs, setTabs] = useState<TabItem[]>([]);\r\n const [activeTabIdx, setActiveTabIdx] = useState(0);\r\n\r\n // Evitar loop infinito: solo reinicializar si el nombre de tabla cambia realmente\r\n // o si los parámetros de filtrado base cambian.\r\n const ffKey = JSON.stringify(initialFixedFields);\r\n\r\n const location = useLocation();\r\n // como backend-plus: lo de la dirección (`ff`, `fc`, `td`, `pf`, `pick`, `label`) pisa a lo del menú\r\n const parametros = useMemo(() => parametrosRecibidos ?? parametrosDeTabla(undefined, location.search), [parametrosRecibidos, location.search]);\r\n const fixedFieldsIniciales = initialFixedFields.length > 0 ? initialFixedFields : (parametros.fixedFields ?? []);\r\n const parametrosKey = JSON.stringify(parametros);\r\n React.useEffect(() => {\r\n if (effectiveTableName) {\r\n // Cada vez que cambia la tabla base (por menú o URL), reseteamos las pestañas\r\n setTabs([{\r\n id: `main-${effectiveTableName}-${ffKey}-${parametrosKey}`, // ID único que incluye filtros\r\n label: parametros.label ?? effectiveTableName.replace(/_/g, ' ').toUpperCase(),\r\n tableName: effectiveTableName,\r\n fixedFields: fixedFieldsIniciales,\r\n parametros,\r\n ancestors: []\r\n }]);\r\n setActiveTabIdx(0);\r\n }\r\n }, [effectiveTableName, ffKey, parametrosKey, location.key]);\r\n\r\n const handleOpenDetail = useCallback((tableName: string, fixedFields: FixedField[], label: string, ancestors: Ancestor[]) => {\r\n const tabId = `${tableName}-${JSON.stringify(fixedFields)}`;\r\n \r\n setTabs(prevTabs => {\r\n const existingTabIdx = prevTabs.findIndex(t => t.id === tabId);\r\n if (existingTabIdx !== -1) {\r\n setActiveTabIdx(existingTabIdx);\r\n return prevTabs;\r\n }\r\n const newTabs = [...prevTabs, { id: tabId, label, tableName, fixedFields, ancestors }];\r\n setActiveTabIdx(newTabs.length - 1);\r\n return newTabs;\r\n });\r\n }, []);\r\n\r\n const handleCloseTab = (index: number, event: React.MouseEvent) => {\r\n event.stopPropagation();\r\n if (index === 0) return; \r\n\r\n const newTabs = tabs.filter((_, i) => i !== index);\r\n setTabs(newTabs);\r\n if (activeTabIdx >= newTabs.length) setActiveTabIdx(newTabs.length - 1);\r\n else if (activeTabIdx === index) setActiveTabIdx(index - 1);\r\n };\r\n\r\n if (!effectiveTableName) {\r\n return (\r\n <Box p={3}><Alert severity=\"error\">No se especificó una tabla.</Alert></Box>\r\n );\r\n }\r\n\r\n return (\r\n <Box sx={{ width: '100%', height: 'calc(100vh - 64px)', display: 'flex', flexDirection: 'column' }}>\r\n <Box sx={{ borderBottom: 1, borderColor: 'divider', bgcolor: 'background.paper' }}>\r\n <Tabs value={activeTabIdx} onChange={(_, v) => setActiveTabIdx(v)} variant=\"scrollable\">\r\n {tabs.map((tab, index) => (\r\n <Tab \r\n key={tab.id} \r\n label={\r\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>\r\n <Typography variant=\"caption\" sx={{ fontWeight: 'bold' }}>{tab.label}</Typography>\r\n {index !== 0 && (\r\n <IconButton size=\"small\" onClick={(e) => handleCloseTab(index, e)}>\r\n <CloseIcon fontSize=\"inherit\" />\r\n </IconButton>\r\n )}\r\n </Box>\r\n } \r\n />\r\n ))}\r\n </Tabs>\r\n </Box>\r\n\r\n {tabs.map((tab, index) => (\r\n <Box\r\n key={tab.id}\r\n role=\"tabpanel\"\r\n hidden={activeTabIdx !== index}\r\n sx={{ flexGrow: 1, display: activeTabIdx === index ? 'flex' : 'none', flexDirection: 'column', overflow: 'hidden' }}\r\n >\r\n <GenericDataGrid \r\n tableName={tab.tableName} \r\n fixedFields={tab.fixedFields} \r\n filterColumns={tab.parametros?.filterColumns}\r\n tableDef={tab.parametros?.tableDef}\r\n parameterFunctions={tab.parametros?.parameterFunctions}\r\n pick={tab.parametros?.pick}\r\n onOpenDetail={handleOpenDetail} \r\n ancestors={tab.ancestors}\r\n />\r\n </Box>\r\n ))}\r\n </Box>\r\n );\r\n};\r\n\r\nexport default GenericDataGridPage;","// src/pages/PantallaDeRuta.tsx\nimport React, { useMemo } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { Box } from '@mui/material';\nimport { FallbackWScreen, wScreens } from './WScreens';\n\n/** Una wScreen como página: los parámetros vienen de la query string (es como se abre un detalle wScreen en otra pestaña) */\nexport const PantallaDeRuta: React.FC<{ screenName: string }> = ({ screenName }) => {\n const [busqueda] = useSearchParams();\n const params = useMemo(() => Object.fromEntries(busqueda.entries()), [busqueda]);\n const Pantalla = wScreens[screenName];\n return (\n <Box sx={{ pt: 3, pl: 4 }}>\n {Pantalla\n ? <Pantalla screenName={screenName} params={params} />\n : <FallbackWScreen screenName={screenName} />}\n </Box>\n );\n};\n","// src/pages/MenuTablePage.tsx\r\n\r\nimport React, { useMemo } from 'react';\r\nimport { useLocation, useParams } from 'react-router-dom';\r\nimport { Box, Alert, Typography } from '@mui/material';\r\nimport GenericDataGridPage from './GenericDataGridPage'; // Importamos el contenedor de tabs\r\nimport { useApp } from '../contexts/AppContext'; // Necesitamos el contexto para acceder al menú\r\nimport { MenuInfoBase, MenuInfoMenu, MenuInfoTable } from 'backend-plus'; // Asegúrate de importar tus tipos de menú\r\nimport { FixedField } from '../types';\r\nimport { aFixedFields, ParametrosDeTabla, parametrosDeTabla } from '../utils/parametrosDeTabla';\r\n\r\n/**\r\n * Los fixedFields que declara un ítem de menú (`ff`). backend-plus admite dos formas, igual que su cliente (my-menu.js):\r\n * un arreglo [{fieldName, value}] o un objeto {campo: valor}.\r\n */\r\nexport function fixedFieldsDeMenu(ff: MenuInfoTable['ff'] | Record<string, unknown> | undefined): FixedField[] {\r\n return aFixedFields(ff);\r\n}\r\n\r\nconst MenuTablePage: React.FC = () => {\r\n // Obtiene el `menuName` de la URL para buscar en clientContext.menu\r\n const { menuName } = useParams<{ menuName?: string }>();\r\n const { clientContext } = useApp();\r\n const { search } = useLocation();\r\n\r\n // Función auxiliar para encontrar un ítem de menú recursivamente\r\n const findMenuItem = (items: MenuInfoBase[], nameToFind: string): MenuInfoTable | undefined => {\r\n for (const item of items) {\r\n if (item.name === nameToFind && item.menuType === \"table\") {\r\n return item as MenuInfoTable;\r\n }\r\n if (item.menuType === \"menu\" && (item as MenuInfoMenu).menuContent) {\r\n const found = findMenuItem((item as MenuInfoMenu).menuContent, nameToFind);\r\n if (found) return found;\r\n }\r\n }\r\n return undefined;\r\n };\r\n\r\n // Usa useMemo para determinar el tableName y los fixedFields desde el menú\r\n const { currentTableName, currentFixedFields, parametros } = useMemo(() => {\r\n let tableName: string | undefined = undefined;\r\n let fixedFields: FixedField[] = [];\r\n let parametros: ParametrosDeTabla = {};\r\n \r\n if (menuName && clientContext && clientContext.menu) {\r\n const menuItem = findMenuItem(clientContext.menu, menuName);\r\n if (menuItem) {\r\n tableName = menuItem.table || menuItem.name;\r\n // como backend-plus (wScreens.table de my-menu.js): lo que declara el ítem (ff, fc, td, pf, label...) y, encima,\r\n // lo de la dirección\r\n parametros = parametrosDeTabla(menuItem, search);\r\n fixedFields = parametros.fixedFields ?? [];\r\n } else {\r\n console.warn(`[${menuName}] Ítem de menú '${menuName}' no encontrado o no es de tipo 'table' en clientContext.menu.`);\r\n // Opcional: podrías redirigir o mostrar un error más amigable\r\n }\r\n }\r\n\r\n return {\r\n currentTableName: tableName,\r\n currentFixedFields: fixedFields,\r\n parametros,\r\n };\r\n }, [menuName, clientContext, search]);\r\n\r\n if (!currentTableName) {\r\n // Muestra un mensaje de error si el nombre de la tabla no se pudo determinar\r\n return (\r\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"200px\">\r\n <Alert severity=\"error\">\r\n <Typography variant=\"h6\">Error: No se pudo cargar la información de la tabla.</Typography>\r\n <Typography variant=\"body1\">Verifique la configuración del menú o el nombre de la tabla en la URL.</Typography>\r\n </Alert>\r\n </Box>\r\n );\r\n }\r\n\r\n // Renderiza GenericDataGrid con los datos obtenidos del menú\r\n return (\r\n <GenericDataGridPage \r\n initialTableName={currentTableName} \r\n initialFixedFields={currentFixedFields} \r\n parametros={parametros}\r\n />\r\n );\r\n};\r\n\r\nexport default MenuTablePage;","import React, { useEffect, useRef, useState } from 'react';\nimport { Navigate, useLocation, useParams } from 'react-router-dom';\nimport { Alert, Box, CircularProgress } from '@mui/material';\nimport { MenuInfoBase, MenuInfoMenu } from 'backend-plus';\nimport { envConfig } from '../env';\nimport { useApp } from '../contexts/AppContext';\nimport { FallbackWScreen } from '../pages/WScreens';\nimport { mensajeDeError } from '../utils/errores';\nimport { cargarClienteBP } from './clienteBP';\nimport { ConsolaBP } from './ConsolaBP';\nimport { resolverPantallaBP, rutaDePantallaBP } from './pantallaDeBP';\nimport { puenteBPHabilitado } from './habilitacion';\n\nfunction buscarItemDeMenu(items: MenuInfoBase[], tipo: string): MenuInfoBase | undefined {\n for (const item of items) {\n if (item.menuType === tipo) return item;\n if (item.menuType === 'menu') {\n const encontrado = buscarItemDeMenu((item as MenuInfoMenu).menuContent, tipo);\n if (encontrado) return encontrado;\n }\n }\n return undefined;\n}\n\ninterface PantallaBPProps {\n nombre: string;\n /** Los parámetros en el formato de dirección de backend-plus: `#w=...&up=...` (o con `?`) */\n direccion: string;\n}\n\n/** Una wScreen de backend-plus (la de la propia aplicación o una de las del framework) dibujada en su `#main_layout` */\nexport const PantallaBP: React.FC<PantallaBPProps> = ({ nombre, direccion }) => {\n const { clientContext } = useApp();\n const [cargando, setCargando] = useState<boolean>(true);\n const [error, setError] = useState<string | null>(null);\n const [noExiste, setNoExiste] = useState<boolean>(false);\n const contenedor = useRef<HTMLDivElement>(null);\n const menu = clientContext.menu;\n\n useEffect(() => {\n const elemento = contenedor.current;\n if (!elemento) return;\n let vigente = true;\n setError(null);\n setNoExiste(false);\n setCargando(true);\n cargarClienteBP(envConfig.backendUrl)\n .then(myOwn => {\n if (!vigente) return;\n setCargando(false);\n const pantalla = resolverPantallaBP(myOwn, nombre, elemento);\n if (!pantalla) {\n setNoExiste(true);\n return;\n }\n elemento.replaceChildren();\n // como en backend-plus: lo que declara el ítem de menú, pisado por la dirección (leída con su propio decodificador:\n // `up`, `ff`, `detailing` llegan como objetos), y la pantalla se ejecuta con myOwn como `this`\n const item = menu ? buscarItemDeMenu(menu, nombre) : undefined;\n const desdeLaDireccion = myOwn.UriSearchToObject?.(direccion) ?? Object.fromEntries(new URLSearchParams(direccion.replace(/^[#?]/, '')).entries());\n const parametros: Record<string, unknown> = { ...item, ...desdeLaDireccion };\n myOwn.preDisplayPage?.(parametros, pantalla.definicion, nombre);\n pantalla.ejecutar.call(myOwn, parametros);\n })\n .catch((err: unknown) => {\n if (!vigente) return;\n setCargando(false);\n setError(mensajeDeError(err));\n });\n return () => {\n vigente = false;\n elemento.replaceChildren();\n };\n }, [nombre, direccion, menu]);\n\n if (noExiste) return <FallbackWScreen screenName={nombre} />;\n return (\n // el indicador de carga flota: si ocupara lugar, el contenido de backend-plus empezaría más abajo mientras carga y subiría al terminar\n <Box sx={{ position: 'relative' }}>\n {cargando && <CircularProgress size={24} sx={{ position: 'absolute', top: 16, left: 16 }} />}\n {error && <Alert severity=\"error\" sx={{ m: 2 }}>{error}</Alert>}\n {/* backend-plus dibuja sus pantallas en el elemento con este id */}\n <div id=\"main_layout\" ref={contenedor} />\n <ConsolaBP />\n </Box>\n );\n};\n\n/** Ruta de las wScreens que la aplicación no registró: con el puente encendido se pasan a la ruta de las pantallas de backend-plus */\nexport const PantallaDeRespaldo: React.FC = () => {\n const { screenName = '' } = useParams<{ screenName: string }>();\n const { search } = useLocation();\n if (!puenteBPHabilitado()) {\n return <Box sx={{ p: 3 }}><FallbackWScreen screenName={screenName} /></Box>;\n }\n return <Navigate to={rutaDePantallaBP(screenName, search)} replace />;\n};\n\n/**\n * La página de backend-plus dentro de la aplicación: `/bp/menu#w=<pantalla>&...`. Se parece a `<base-url>/menu` a propósito:\n * el código de backend-plus y de las aplicaciones arma direcciones a partir de la ubicación (`location.pathname + '/../menu#...'`,\n * `location.pathname + '#w=...'` para abrir en otra pestaña) y así caen en esta misma ruta, que lee los parámetros como BP\n * (del hash, o de la query).\n */\nexport const PaginaBP: React.FC = () => {\n const { hash, search } = useLocation();\n const direccion = hash || search;\n const nombre = new URLSearchParams(direccion.replace(/^[#?]/, '')).get('w');\n if (!puenteBPHabilitado()) return <Navigate to=\"/\" replace />;\n if (!nombre) return <Box sx={{ p: 3 }}><Alert severity=\"warning\">Falta la pantalla de backend-plus (<code>#w=...</code>).</Alert></Box>;\n return <PantallaBP key={nombre} nombre={nombre} direccion={direccion} />;\n};\n","// Puente con el cliente de backend-plus: carga sus scripts en la página (los mismos que arma el servidor con\n// `clientIncludes`) para poder usar su grilla y sus wScreens donde esta librería todavía no tiene una versión propia.\n// Solo funciona en el mismo origen que el backend (comparte la sesión), por ejemplo bajo `<base-url>/react/`.\nimport { FixedField } from '../types';\n\nexport interface OpcionesGrillaBP {\n fixedFields?: FixedField[];\n tableDef?: Record<string, unknown>;\n}\n\n/** Una wScreen de backend-plus: dibuja en `#main_layout` a partir de los parámetros de la dirección */\nexport type PantallaBP = (parametros: Record<string, unknown>) => void;\n\n/**\n * Las formas de wScreen que entiende `showPage` de backend-plus: una función, o un objeto con `mainFunction`, o un\n * objeto con `parameters` y `mainAction` (muestra el formulario de parámetros y después ejecuta `mainAction`).\n * `procAux` también vive en `wScreens`: es un objeto con `showParams`.\n */\nexport interface DefinicionPantallaBP {\n mainFunction?: PantallaBP;\n parameters?: unknown[];\n mainAction?: unknown;\n resultClass?: string;\n proceedLabel?: string;\n pageTitle?: string;\n showParams?: (definicion: Record<string, unknown>, contenedor: HTMLElement, parametros: Record<string, unknown>, accion: unknown) => void;\n}\n\nexport interface MyOwnBP {\n autoSetup(): Promise<unknown>;\n wScreens: Record<string, PantallaBP | DefinicionPantallaBP | undefined>;\n /** Lee una dirección de backend-plus (`#w=...&up=...` o `?...`) con los decodificadores de cada parámetro */\n UriSearchToObject?(direccion: string): Record<string, unknown>;\n preDisplayPage?(parametros: Record<string, unknown>, pantalla: DefinicionPantallaBP, nombre: string): void;\n tableGrid(nombreTabla: string, elemento: HTMLElement, opciones: OpcionesGrillaBP): unknown;\n}\n\ndeclare global {\n interface Window {\n myOwn?: MyOwnBP;\n my?: MyOwnBP;\n }\n}\n\nexport interface RecursosBP {\n hojasDeEstilo: string[];\n scripts: string[];\n}\n\n/** Las hojas de estilo y los scripts de la página que sirve backend-plus, en el orden en que los carga */\nexport function extraerRecursos(html: string): RecursosBP {\n const documento = new DOMParser().parseFromString(html, 'text/html');\n const referencias = (selector: string, atributo: string): string[] =>\n Array.from(documento.querySelectorAll(selector))\n .map(elemento => elemento.getAttribute(atributo))\n .filter((ruta): ruta is string => !!ruta);\n return {\n hojasDeEstilo: referencias('link[rel=stylesheet]', 'href'),\n scripts: referencias('script[src]', 'src'),\n };\n}\n\n/**\n * El cliente de backend-plus arma sus direcciones (`lib/...`, `img/...`, los procedimientos) relativas a la página:\n * con un `<base>` apuntando a la base-url se comporta como si estuviera en su propia página.\n */\nfunction fijarBase(urlBackend: string): void {\n const href = new URL(urlBackend.replace(/\\/*$/, '/'), window.location.href).href;\n let base = document.querySelector('base');\n if (!base) {\n base = document.createElement('base');\n document.head.prepend(base);\n }\n base.href = href;\n}\n\nfunction cargarScript(ruta: string): Promise<void> {\n return new Promise((resolver, rechazar) => {\n const script = document.createElement('script');\n script.async = false;\n script.src = ruta;\n script.onload = () => resolver();\n script.onerror = () => rechazar(new Error(`No se pudo cargar ${ruta}`));\n document.head.appendChild(script);\n });\n}\n\n/**\n * El cliente de backend-plus se engancha al evento `load` de la ventana para mostrar su página (`showPage`), que acá\n * no tiene dónde dibujar (`#total-layout`) y falla; su manejador de errores deja un cartel como primer hijo del `body`\n * que corre toda la aplicación hacia abajo. Por eso se lo carga recién cuando la ventana terminó de cargar.\n */\nexport function esperarCargaDeVentana(): Promise<void> {\n if (document.readyState === 'complete') return Promise.resolve();\n return new Promise(resolver => window.addEventListener('load', () => resolver(), { once: true }));\n}\n\n/**\n * Mientras corre `inyectar`, ignora que lo que se está cargando registre escuchas de esos eventos de la ventana.\n * backend-plus (my-menu.js) escucha `popstate` para mostrar su página, salvo que la dirección termine en `/react`: sin esto\n * la aplicación quedaría atada a ese nombre. Con la navegación en manos del router de la aplicación, esa escucha sobra.\n */\nexport async function sinEscuchasDeVentana<R>(tipos: string[], inyectar: () => Promise<R>): Promise<R> {\n const original = window.addEventListener;\n const filtrada = function (this: Window, tipo: string, escucha: EventListenerOrEventListenerObject, opciones?: boolean | AddEventListenerOptions): void {\n if (tipos.includes(tipo)) return;\n original.call(this, tipo, escucha, opciones);\n };\n window.addEventListener = filtrada;\n try {\n return await inyectar();\n } finally {\n if (window.addEventListener === filtrada) window.addEventListener = original;\n }\n}\n\nasync function cargarScripts(rutas: string[]): Promise<void> {\n for (const ruta of rutas) {\n await cargarScript(ruta);\n }\n}\n\nlet carga: Promise<MyOwnBP> | null = null;\n\n/** Carga (una sola vez) el cliente de backend-plus y lo deja listo para usar */\nexport function cargarClienteBP(urlBackend: string): Promise<MyOwnBP> {\n if (!carga) {\n carga = cargarSinMemoria(urlBackend).catch((error: unknown) => {\n carga = null;\n throw error;\n });\n }\n return carga;\n}\n\nasync function cargarSinMemoria(urlBackend: string): Promise<MyOwnBP> {\n const destino = new URL(urlBackend, window.location.href);\n if (destino.origin !== window.location.origin) {\n throw new Error(`El cliente de backend-plus solo se puede usar desde el mismo origen que el backend (${destino.origin} no es ${window.location.origin}).`);\n }\n fijarBase(urlBackend);\n const respuesta = await fetch(`${destino.pathname.replace(/\\/*$/, '')}/menu`, { credentials: 'include' });\n if (!respuesta.ok || /\\/login\\/?$/.test(new URL(respuesta.url).pathname)) {\n throw new Error('No hay una sesión iniciada en el backend.');\n }\n const recursos = extraerRecursos(await respuesta.text());\n recursos.hojasDeEstilo.forEach(ruta => {\n const enlace = document.createElement('link');\n enlace.rel = 'stylesheet';\n enlace.href = ruta;\n document.head.appendChild(enlace);\n });\n await esperarCargaDeVentana();\n await sinEscuchasDeVentana(['popstate'], () => cargarScripts(recursos.scripts));\n const myOwn = window.myOwn;\n if (!myOwn) throw new Error('El backend no definió myOwn.');\n // backend-plus lo hace al cargar la ventana, y esta ya está cargada; parte de su código usa el global `my`\n window.my = myOwn;\n await myOwn.autoSetup();\n return myOwn;\n}\n","import React, { useEffect } from 'react';\nimport { Box } from '@mui/material';\n\n/** Lo que tardan en llegar las dos llamadas de backend-plus por un mismo error (en la misma tarea): más que eso es otro error igual */\nconst MARGEN_DE_REPETIDO_MS = 1000;\n\n/**\n * backend-plus registra algunos errores dos veces: el de una llamada ajax lo registra `my-ajax.js` y el `catch` de «proceder»\n * (`my-menu.js`) lo vuelve a registrar. Si ya hay un aviso con ese texto que llegó recién, el repetido se descarta.\n */\nexport function descartarRepetidos(consola: HTMLElement, agregados: Node[], ahora: number): void {\n for (const nodo of agregados) {\n if (!(nodo instanceof HTMLElement)) continue;\n const repetido = Array.from(consola.children).find(\n (otro): otro is HTMLElement => otro instanceof HTMLElement && otro !== nodo && otro.className === nodo.className && otro.textContent === nodo.textContent\n && ahora - Number(otro.dataset.instante) < MARGEN_DE_REPETIDO_MS\n );\n if (repetido) nodo.remove();\n else nodo.dataset.instante = String(ahora);\n }\n}\n\n/**\n * Los mensajes y errores de backend-plus (`myOwn.log`): los escribe en todos los `#mini-console` que haya y, si no hay ninguno,\n * al principio del `body`, donde empujan toda la aplicación hacia abajo. Acá flotan en la esquina de arriba a la derecha,\n * debajo de la barra, como avisos, y él mismo los retira (los errores a los 30 segundos, los mensajes a los 3).\n * Las hojas de estilo de backend-plus y de las aplicaciones también le dan estilo a `#mini-console` (por id y con\n * `width`, `max-height`, `top`...): `&#mini-console` (clase + id) les gana y se reponen esos valores.\n */\nexport const ConsolaBP: React.FC = () => {\n useEffect(() => {\n const consola = document.getElementById('mini-console');\n if (!consola) return;\n const observador = new MutationObserver(registros =>\n descartarRepetidos(consola, registros.flatMap(registro => Array.from(registro.addedNodes)), Date.now())\n );\n observador.observe(consola, { childList: true });\n return () => observador.disconnect();\n }, []);\n return (\n <Box\n id=\"mini-console\"\n sx={(tema) => ({\n '&#mini-console': {\n position: 'fixed',\n top: { xs: 64, sm: 72 },\n left: 'auto',\n right: 16,\n bottom: 'auto',\n float: 'none',\n zIndex: tema.zIndex.snackbar,\n width: 'auto',\n minWidth: 0,\n maxWidth: 'min(480px, calc(100vw - 32px))',\n minHeight: 0,\n maxHeight: 'none',\n overflow: 'visible',\n padding: 0,\n border: 'none',\n borderRadius: 0,\n boxShadow: 'none',\n backgroundColor: 'transparent',\n fontSize: 'inherit',\n display: 'flex',\n flexDirection: 'column',\n gap: 1,\n },\n '&#mini-console:not(:empty)': { display: 'flex' },\n '&#mini-console:empty': { display: 'none' },\n '& > div': {\n p: '6px 12px',\n borderRadius: 1,\n boxShadow: tema.shadows[3],\n fontSize: '0.875rem',\n whiteSpace: 'pre-wrap',\n overflowWrap: 'anywhere',\n backgroundColor: tema.palette.grey[800],\n color: tema.palette.common.white,\n },\n '& > .status-error': {\n backgroundColor: tema.palette.error.main,\n color: tema.palette.error.contrastText,\n },\n })}\n />\n );\n};\n","// Cómo se ejecuta una wScreen de backend-plus, igual que `showPage` de my-menu.js\nimport type { DefinicionPantallaBP, MyOwnBP, PantallaBP } from './clienteBP';\n\nexport interface PantallaResuelta {\n definicion: DefinicionPantallaBP;\n ejecutar: PantallaBP;\n}\n\n/** Ruta de las pantallas de backend-plus dentro de la aplicación: se parece a la página de BP (`<base-url>/menu#w=...`) */\nexport const RUTA_PANTALLA_BP = '/bp/menu';\n\n/** La ruta de una pantalla de backend-plus con sus parámetros, en el formato de dirección de BP (`#w=<pantalla>&...`) */\nexport function rutaDePantallaBP(nombre: string, parametros: string = ''): string {\n const resto = parametros.replace(/^[#?]/, '').replace(/(^|&)w=[^&]*/, '').replace(/^&/, '');\n return `${RUTA_PANTALLA_BP}#w=${encodeURIComponent(nombre)}${resto ? `&${resto}` : ''}`;\n}\n\n/**\n * Las direcciones de backend-plus a las que apunta lo que él arma (el botón «proceder» de una wScreen con parámetros, los\n * vínculos de la ayuda, `location.href = 'login'` al vencer la sesión): son páginas del backend y recargan todo. Dentro de\n * esta aplicación hay que quedarse en ella: devuelve la ruta equivalente, o null si la dirección es de otra cosa.\n */\nexport function rutaDeEnlaceBP(href: string, baseDelDocumento: string, urlBackend: string): string | null {\n const enlace = new URL(href, baseDelDocumento);\n const base = `${urlBackend.replace(/\\/*$/, '/')}`;\n const pagina = (nombre: string) => new URL(`${base}${nombre}`, baseDelDocumento);\n if (enlace.origin !== pagina('menu').origin) return null;\n if (enlace.pathname === pagina('login').pathname) return '/login';\n if (enlace.pathname === pagina('logout').pathname) return '/logout';\n if (enlace.pathname !== pagina('menu').pathname) return null;\n const texto = (enlace.hash || enlace.search).slice(1);\n const nombre = new URLSearchParams(texto).get('w');\n return nombre ? rutaDePantallaBP(nombre, texto) : null;\n}\n\n/** La función que dibuja la pantalla, o null si backend-plus no la tiene (o no tiene cómo dibujarla) */\nexport function resolverPantallaBP(myOwn: MyOwnBP, nombre: string, contenedor: HTMLElement): PantallaResuelta | null {\n const registrada = myOwn.wScreens[nombre];\n if (!registrada) return null;\n if (typeof registrada === 'function') {\n return { definicion: { mainFunction: registrada }, ejecutar: registrada };\n }\n if (registrada.mainFunction) {\n return { definicion: registrada, ejecutar: registrada.mainFunction };\n }\n const { parameters, mainAction, resultClass, proceedLabel } = registrada;\n const procAux = myOwn.wScreens.procAux;\n const mostrarParametros = typeof procAux === 'object' ? procAux.showParams : undefined;\n if (parameters && mostrarParametros) {\n return {\n definicion: registrada,\n ejecutar: parametros => mostrarParametros({ parameters, resultClass, proceedLabel }, contenedor, parametros, mainAction),\n };\n }\n return null;\n}\n","// El puente con backend-plus es opcional: la aplicación lo enciende (prop `puenteBP` de `App`) y recién entonces las\n// wScreens que no están en `myWScreens` se piden al cliente de backend-plus en lugar de mostrar «no implementada».\nlet habilitado = false;\n\nexport const habilitarPuenteBP = (): void => {\n habilitado = true;\n};\n\nexport const puenteBPHabilitado = (): boolean => habilitado;\n","import React, { useEffect, useRef, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Alert, Box, CircularProgress } from '@mui/material';\nimport { envConfig } from '../env';\nimport { FixedField } from '../types';\nimport { mensajeDeError } from '../utils/errores';\nimport { cargarClienteBP } from './clienteBP';\nimport { ConsolaBP } from './ConsolaBP';\n\ninterface GrillaBPProps {\n tabla: string;\n fixedFields?: FixedField[];\n}\n\n/** La grilla de backend-plus (`myOwn.tableGrid`) dentro de una página React: conserva todo lo que ya hace esa grilla */\nexport const GrillaBP: React.FC<GrillaBPProps> = ({ tabla, fixedFields }) => {\n const contenedor = useRef<HTMLDivElement>(null);\n const [cargando, setCargando] = useState<boolean>(true);\n const [error, setError] = useState<string | null>(null);\n // los fixedFields llegan como arreglo nuevo en cada render: la grilla solo se rearma si cambia su contenido\n const clave = JSON.stringify(fixedFields ?? []);\n\n useEffect(() => {\n const elemento = contenedor.current;\n if (!elemento) return;\n let vigente = true;\n setError(null);\n setCargando(true);\n cargarClienteBP(envConfig.backendUrl)\n .then(myOwn => {\n if (!vigente) return;\n setCargando(false);\n elemento.replaceChildren();\n myOwn.tableGrid(tabla, elemento, { fixedFields: JSON.parse(clave) as FixedField[] });\n })\n .catch((err: unknown) => {\n if (!vigente) return;\n setCargando(false);\n setError(mensajeDeError(err));\n });\n return () => {\n vigente = false;\n elemento.replaceChildren();\n };\n }, [tabla, clave]);\n\n return (\n // el indicador de carga flota: si ocupara lugar, el contenido de backend-plus empezaría más abajo mientras carga y subiría al terminar\n <Box sx={{ position: 'relative' }}>\n {cargando && <CircularProgress size={24} sx={{ position: 'absolute', top: 16, left: 16 }} />}\n {error && <Alert severity=\"error\" sx={{ m: 2 }}>{error}</Alert>}\n <div ref={contenedor} />\n <ConsolaBP />\n </Box>\n );\n};\n\n/** Ruta `/bp/table/:tableName`: una tabla con la grilla de backend-plus, para compararla con la de esta librería */\nexport const PaginaGrillaBP: React.FC = () => {\n const { tableName = '' } = useParams<{ tableName: string }>();\n return <GrillaBP key={tableName} tabla={tableName} />;\n};\n","import React, { useEffect } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { envConfig } from '../env';\nimport { capturarNavegacionBP } from './capturaDeNavegacion';\n\n/**\n * «proceder», los vínculos, `location.href = ...` y los avisos de backend-plus (el de «Iniciar sesión», por ejemplo) apuntan\n * a páginas del backend: se resuelven dentro de la aplicación. Se monta una sola vez y no depende de que haya una pantalla de\n * backend-plus en la página: su cliente sigue vivo (y sus avisos en el `body`) aunque el usuario ya esté en una pantalla de React.\n */\nexport const CapturaBP: React.FC = () => {\n const navigate = useNavigate();\n useEffect(() => capturarNavegacionBP(envConfig.backendUrl, navigate), [navigate]);\n return null;\n};\n","// Mientras hay una pantalla de backend-plus en la página, lo que ella (o el código de la aplicación sobre backend-plus)\n// manda a las páginas del backend se resuelve dentro de la aplicación: clics en enlaces, asignaciones a `location` y\n// `history.replaceState`.\nimport { rutaBase } from '../rutaBase';\nimport { rutaDeEnlaceBP } from './pantallaDeBP';\n\ninterface EventoNavegacion extends Event {\n canIntercept: boolean;\n destination: { url: string; sameDocument: boolean };\n downloadRequest: string | null;\n}\n\ninterface ApiNavegacion {\n addEventListener(tipo: 'navigate', escucha: (evento: EventoNavegacion) => void): void;\n removeEventListener(tipo: 'navigate', escucha: (evento: EventoNavegacion) => void): void;\n}\n\n// la Navigation API no está en todos los navegadores (ni en los tipos del DOM de esta versión de TypeScript)\nfunction apiDeNavegacion(): ApiNavegacion | null {\n const api: unknown = Reflect.get(window, 'navigation');\n return typeof api === 'object' && api !== null && 'addEventListener' in api && typeof api.addEventListener === 'function'\n ? api as ApiNavegacion\n : null;\n}\n\n/** Empieza a capturar; devuelve la función que deja de hacerlo */\nexport function capturarNavegacionBP(urlBackend: string, navegar: (ruta: string) => void): () => void {\n const alHacerClick = (evento: MouseEvent) => {\n if (evento.defaultPrevented || evento.button !== 0 || evento.ctrlKey || evento.metaKey || evento.shiftKey) return;\n const enlace = evento.target instanceof Element ? evento.target.closest('a[href]') : null;\n if (!enlace || enlace.hasAttribute('download') || (enlace.getAttribute('target') ?? '_self') !== '_self') return;\n const ruta = rutaDeEnlaceBP(enlace.getAttribute('href') ?? '', document.baseURI, urlBackend);\n if (ruta) {\n // el botón «proceder» de backend-plus además tiene su propio onclick (history.pushState a `<base-url>/menu#...` y\n // showPage): si corriera, la dirección quedaría fuera de la aplicación\n evento.preventDefault();\n evento.stopPropagation();\n navegar(ruta);\n }\n };\n // cubre los enlaces de los diálogos y de todo lo que backend-plus cuelga fuera de su contenedor\n document.addEventListener('click', alHacerClick, true);\n\n // `location.href = ...` no pasa por ningún clic: donde existe, la Navigation API avisa de cualquier navegación\n const navegacion = apiDeNavegacion();\n const alNavegar = (evento: EventoNavegacion) => {\n if (!evento.cancelable || evento.destination.sameDocument || evento.downloadRequest !== null) return;\n const ruta = rutaDeEnlaceBP(evento.destination.url, document.baseURI, urlBackend);\n if (ruta) {\n evento.preventDefault();\n navegar(ruta);\n }\n };\n navegacion?.addEventListener('navigate', alNavegar);\n\n // al editar un parámetro, backend-plus reescribe la dirección (`myOwn.replaceAddrParams`: `history.replaceState(null, null, 'menu#...')`)\n // y, con el `<base>` en la base-url del backend, eso deja la aplicación; `replaceState` no pasa por la Navigation API\n const reemplazarOriginal = history.replaceState;\n const reemplazar: History['replaceState'] = (estado, titulo, url) => {\n const ruta = url === undefined || url === null ? null : rutaDeEnlaceBP(String(url), document.baseURI, urlBackend);\n reemplazarOriginal.call(history, estado, titulo, ruta === null ? url : rutaBase + ruta);\n };\n history.replaceState = reemplazar;\n\n return () => {\n if (history.replaceState === reemplazar) history.replaceState = reemplazarOriginal;\n document.removeEventListener('click', alHacerClick, true);\n navegacion?.removeEventListener('navigate', alNavegar);\n };\n}\n"],"mappings":"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","names":["import_react","import_react","React","SnackbarAlert","MuiAlert","Snackbar","JSON4all","import_material","import_toolkit","import_toolkit","initialState","import_toolkit","initialState","storage","import_jsx_runtime","Dialog","DialogTitle","DialogContent","DialogContentText","DialogActions","Button","import_react","import_jsx_runtime","import_react","typeStore","import_react","import_react","import_type_store","condiciones","typeStore","import_react","import_react","import_material","import_material","import_jsx_runtime","ConstructionIcon","WarningAmberIcon","import_jsx_runtime","KeyboardArrowDownIcon","KeyboardArrowRightIcon","tableDefinition","filteredRows","import_react","filterHeight","import_react","React","RefreshIcon","ViewColumnIcon","VisibilityOffIcon","FileDownloadIcon","FileUploadIcon","DeleteSweepIcon","FilterAltIcon","import_react","JSON4all","import_material","import_react","import_jsx_runtime","AddIcon","SearchOffIcon","SearchIcon","MoreVertIcon","import_material","import_react","import_react","import_material","import_type_store","import_react","import_jsx_runtime","typeStore","cellBackgroundColor","transitionStyle","import_jsx_runtime","React","import_material","import_jsx_runtime","import_react","import_material","import_Add","import_material","import_jsx_runtime","import_material","import_WarningAmber","import_jsx_runtime","WarningAmberIcon","import_jsx_runtime","React","CeldaDato","AddIcon","DeleteIcon","ViewHeadlineIcon","import_react","import_react","import_Search","import_material","import_react","import_material","import_jsx_runtime","import_react","import_material","import_jsx_runtime","import_react","import_material","import_jsx_runtime","import_type_store","import_jsx_runtime","typeStore","React","isMandatory","InputBase","IconButton","CalendarMonthIcon","SearchIcon","ArrowDropDownIcon","import_jsx_runtime","React","import_react","import_material","import_FileUpload","import_jsx_runtime","FileUploadIcon","import_react","import_material","import_type_store","import_jsx_runtime","typeStore","import_react","import_material","import_jsx_runtime","CloseIcon","import_json4all","JSON4all","import_meta","import_jsx_runtime","definition","data","parametros","altoDeseado","import_react","import_react_router_dom","import_react","import_react_router_dom","import_material","import_react","import_react_redux","import_jsx_runtime","import_react","import_material","import_WarningAmber","import_react_router_dom","import_jsx_runtime","item","TableChartIcon","DnsIcon","FolderIcon","DesktopWindowsIcon","WarningAmberIcon","React","ExpandLess","ExpandMore","UnfoldLessIcon","UnfoldMoreIcon","import_react","import_react_router_dom","import_react_redux","import_jsx_runtime","theme","ChevronLeftIcon","MenuIcon","LogoutIcon","import_material","import_jsx_runtime","import_react_router_dom","import_jsx_runtime","import_react","import_material","import_jsx_runtime","import_react","import_material","import_react_redux","import_jsx_runtime","import_react","import_react_router_dom","import_material","import_material","import_jsx_runtime","import_jsx_runtime","React","import_react_redux","import_react","import_react","import_react_router_dom","import_jsx_runtime","import_react","import_react_router_dom","import_react","import_react_router_dom","import_material","import_Close","import_jsx_runtime","React","CloseIcon","import_react","import_react_router_dom","import_material","import_jsx_runtime","import_react","import_react_router_dom","import_material","import_jsx_runtime","parametros","import_react","import_react_router_dom","import_material","referencias","import_react","import_material","import_jsx_runtime","nombre","import_jsx_runtime","import_react","import_react_router_dom","import_material","import_jsx_runtime","import_react","import_react_router_dom","import_type_store","import_jsx_runtime","typeStore","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/grid/GenericDataGrid.tsx","../src/hooks/useApiCall.ts","../src/contexts/SnackbarContext.tsx","../src/utils/errores.ts","../src/env.ts","../src/utils/fetchApi.ts","../src/store/index.ts","../src/store/clientContextSlice.ts","../src/store/routerSlice.ts","../src/store/menuUiSlice.ts","../src/components/ConfirmDialog.tsx","../src/components/grid/DataGridOptionsDialog.tsx","../src/components/grid/utils/helpers.ts","../src/utils/routeUtils.ts","../src/types/index.ts","../src/hooks/grid/useGridActions.ts","../src/hooks/grid/useGridDataView.ts","../src/components/grid/utils/typedControls.ts","../src/hooks/grid/useGridEvents.ts","../src/components/grid/detalles.tsx","../src/pages/WScreens.tsx","../src/components/grid/condiciones.ts","../src/hooks/grid/teclasDeCelda.ts","../src/utils/fixedFields.ts","../src/utils/referencias.ts","../src/utils/expanders.ts","../src/hooks/grid/useGridLayout.ts","../src/hooks/grid/useGridMenu.ts","../src/hooks/grid/useGridColumns.ts","../src/utils/functions.tsx","../src/components/grid/renderers/headerCellRenderers.tsx","../src/components/grid/estadoGrilla.ts","../src/components/grid/FilterInputRender.tsx","../src/components/forms/TypedField.tsx","../src/hooks/grid/useExpander.tsx","../src/components/grid/MenuDeOpciones.tsx","../src/components/grid/SelectorDeFecha.tsx","../src/components/grid/VentanaDeTexto.tsx","../src/components/grid/renderers/summaryCellRenderers.tsx","../src/components/grid/renderers/cellRenderers.tsx","../src/components/grid/ExampleClientSideComponent.tsx","../src/components/grid/FallbackClientSideRenderer.tsx","../src/components/grid/clientSides.ts","../src/components/grid/InputRenderer.tsx","../src/hooks/useTableRecordSave.ts","../src/components/grid/renderers/editCellRenderers.tsx","../src/components/grid/ImportDialog.tsx","../src/components/grid/ExportDialog.tsx","../src/pages/VerticalEditorPage.tsx","../src/utils/parametrosDeTabla.ts","../src/rutaBase.ts","../src/App.tsx","../src/components/MainLayout.tsx","../src/contexts/AppContext.tsx","../src/components/SideMenu.tsx","../src/utils/menuInicial.ts","../src/hooks/useLogout.tsx","../src/components/SessionExpiredMessage.tsx","../src/pages/PantallaInicial.tsx","../src/pages/LogoutPage.tsx","../src/pages/LoginPage.tsx","../src/components/ProcedureForm.tsx","../src/pages/procedure-results/SuccessDisplay.tsx","../src/pages/procedure-results/resultsOk.ts","../src/components/PrivateRoute.tsx","../src/components/InitialRedirectHandler.tsx","../src/pages/GenericDataGridPage.tsx","../src/pages/PantallaDeRuta.tsx","../src/pages/MenuTablePage.tsx","../src/puente/PantallaBP.tsx","../src/puente/clienteBP.ts","../src/puente/ConsolaBP.tsx","../src/puente/pantallaDeBP.ts","../src/puente/habilitacion.ts","../src/puente/GrillaBP.tsx","../src/puente/CapturaBP.tsx","../src/puente/capturaDeNavegacion.ts"],"sourcesContent":["export { default as GenericDataGrid } from './components/grid/GenericDataGrid';\nexport { clientSides, extendClientSides } from './components/grid/clientSides';\nexport { extendConditions } from './components/grid/condiciones';\nexport { ConfirmDialog } from './components/ConfirmDialog';\n\n// Componente de rutas genérico\nexport { FrontendPlusReactRoutes, FrontendPlusProviders, default as App } from './App';\nexport type { WScreenMap, ResultsOksMap, ClientSidesMap, AppProps, CondicionDeDetalle, CondicionesMap, FilaDeDatos } from './types';\n\nexport { default as MainLayout } from './components/MainLayout';\n\n// Contextos\nexport { SnackbarProvider, useSnackbar } from './contexts/SnackbarContext';\nexport { AppProvider, useApp } from './contexts/AppContext';\n\n// Hooks\nexport { useApiCall, useApiCallWithoutSnackbar } from './hooks/useApiCall';\nexport { default as useLogout } from './hooks/useLogout';\n\n// Tipos\nexport * from './types'; // Exporta todas las interfaces y tipos\n\n// Utilidades\nexport { cambiarGuionesBajosPorEspacios } from './utils/functions';\n\n//pages\nexport { wScreens, extendWScreens } from './pages/WScreens';\nexport { resultsOk as resultComponents, extendResultsOk } from './pages/procedure-results/resultsOk';\n\n// Puente con el cliente de backend-plus (grilla y wScreens que todavía no están en React)\nexport { GrillaBP } from './puente/GrillaBP';\nexport { habilitarPuenteBP } from './puente/habilitacion';\n\nexport type { WScreenProps } from './types/index';\nexport type { ResultOkProps } from './types/index';\nexport type { ClientSideProps } from './types/index';\n","// src/components/GenericDataGrid.tsx\nimport React, { useState, useEffect, useCallback, useContext, useMemo, useRef } from 'react';\nimport { DataGrid, DataGridHandle, RenderCellProps, CellSelectArgs, Column, ColumnWidth } from 'react-data-grid';\nimport 'react-data-grid/lib/styles.css';\nimport './GenericDataGrid.css';\n\nimport { useApiCall } from '../../hooks/useApiCall';\nimport { CircularProgress, Typography, Box, Alert, useTheme, Dialog } from '@mui/material';\n\nimport { useSnackbar } from '../../contexts/SnackbarContext';\nimport { useClientContext } from '../../store';\nimport { mensajeDeError, requerido } from '../../utils/errores';\n\nimport { FilaDeDatos, FilaDeResumen, FixedField, TableDefinition, Ancestor, TeclaDeEditor } from '../../types';\n\nimport { ConfirmDialog } from '../ConfirmDialog';\nimport { DataGridOptionsDialog } from './DataGridOptionsDialog';\n\nimport { DetailTable, FieldDefinition } from 'backend-plus';\nimport { getPrimaryKeyValues, sameValue, Typer, NEW_ROW_INDICATOR } from './utils/helpers';\nimport { useGridActions } from '../../hooks/grid/useGridActions';\nimport { useGridDataView } from '../../hooks/grid/useGridDataView';\nimport { useGridEvents } from '../../hooks/grid/useGridEvents';\nimport { useGridLayout } from '../../hooks/grid/useGridLayout';\nimport { useGridMenu } from '../../hooks/grid/useGridMenu';\nimport { calcularAnchosDeColumnas, useGridColumns } from '../../hooks/grid/useGridColumns';\nimport { ImportDialog } from './ImportDialog';\nimport { ExportDialog } from './ExportDialog';\nimport { VentanaDeTexto } from './VentanaDeTexto';\nimport { VerticalEditorPage } from '../../pages/VerticalEditorPage';\nimport { cambiarGuionesBajosPorEspacios } from '../../utils/functions';\nimport { aFiltrosIniciales, FiltroInicial, fusionar } from '../../utils/parametrosDeTabla';\nimport { rutaBase } from '../../rutaBase';\nimport { crearEstadoGrilla, EstadoGrillaContext, MapaFeedback, VistaGrillaContext } from './estadoGrilla';\nimport {\n AltoDisponibleContext, ALTO_INICIAL_DETALLE, ALTO_MINIMO_DETALLE, CacheGrillasContext, DetalleWScreen, calcularFixedFieldsDetalle,\n claveDetalle, DETALLE, DetallesGrillaContext, esFilaDetalle, FilaDetalle, GrillaGuardada\n} from './detalles';\n\n\nexport const SUMMARY_ROW_HEIGHT = 35;\nexport const FILTER_ROW_HEIGHT = 35;\nexport const HEADER_ROW_HEIGHT = 30;\nexport const ROW_HEIGHT = 30;\nexport const MIN_BODY_HEIGHT = 260;\n\n/** Cuánto dura el feedback verde de una celda grabada */\nconst DURACION_FEEDBACK_EXITO = 3000;\n\n/** Alto del título de una grilla anidada (para informar cuánto necesita) */\nconst ALTO_TITULO_ANIDADA = 26;\n/** Alto mínimo de una grilla anidada sin filas (alcanza para el mensaje \"No hay filas\") */\nconst ALTO_MINIMO_CUERPO_ANIDADA = 120;\n/** Espacio extra bajo una grilla anidada: lo justo para una barra de scroll horizontal */\nconst HOLGURA_ANIDADA = 24;\n/** Ancho mínimo de una grilla anidada: que alcance para el título */\nconst ANCHO_MINIMO_ANIDADA = 240;\n/** Lo que el detalle ocupa de más que su grilla: sangría a la izquierda, margen a la derecha y la línea de color */\nconst SANGRIA_DETALLE = 28 + 8 + 3;\n/** Espacio que se deja bajo la grilla principal al calcular cuánto alto tiene disponible */\nconst MARGEN_INFERIOR_PAGINA = 24;\n/** Valores por omisión de backend-plus (myOwn.firstDisplayCount y myOwn.firstDisplayOverLimit) */\nconst PRIMERAS_FILAS_ANIDADA = 20;\nconst LIMITE_PRIMERAS_FILAS_ANIDADA = 30;\n\ninterface GenericDataGridProps {\n tableName: string;\n fixedFields?: FixedField[];\n /** Como en backend-plus (`fc` de la dirección o del menú): la fila de filtros con la que se abre la grilla */\n filterColumns?: FiltroInicial[][];\n /** `td`: lo que se le cambia a la definición de la tabla que manda el servidor */\n tableDef?: Record<string, unknown>;\n /** `pf` y `pick` de backend-plus: viajan con el pedido de datos */\n parameterFunctions?: Record<string, unknown>;\n pick?: string;\n /** Abre un detalle en otra pestaña (Ctrl+click o botón del medio en el botón del detalle) */\n onOpenDetail?: (tableName: string, fixedFields: FixedField[], label: string, ancestors: Ancestor[]) => void;\n gridStyles?: React.CSSProperties;\n ancestors?: Ancestor[];\n /** Es una grilla de detalle desplegada dentro de otra grilla */\n anidada?: boolean;\n /** Título a mostrar en lugar del de la tabla (por omisión, el título o nombre de la tabla, igual que la grilla principal) */\n titulo?: string;\n /** Clave en el CacheGrillasContext para volver a mostrarse sin recargar */\n claveCache?: string;\n /** La grilla anidada informa el alto que necesita para mostrarse completa */\n onAltoDeseado?: (alto: number) => void;\n /** La grilla anidada informa el ancho que necesita (sus columnas y los detalles que tenga abiertos) */\n onAnchoDeseado?: (ancho: number) => void;\n /** Alto máximo que puede tener esta grilla anidada (lo calcula la de arriba a partir de la ventana) */\n altoDisponible?: number;\n /**\n * Tope opcional para el alto de las filas de detalle (en px). Sin tope (lo normal) cada detalle\n * se muestra completo, como en backend-plus; con tope, el detalle más alto scrollea por dentro\n * y virtualiza sus propias filas (útil si hay detalles de miles de filas).\n */\n altoMaximoDetalle?: number;\n}\n\nexport { NEW_ROW_INDICATOR };\n/** Las filas de datos y de resumen que maneja esta grilla */\nexport type ColumnaDeGrilla = Column<FilaDeDatos, FilaDeResumen>;\n\ninterface BaseCustomColumn extends ColumnaDeGrilla {\n customType: 'default' | 'detail' | 'action',\n tableDefinition: TableDefinition,\n}\n\n/**\n * Columna de un campo. Solo lleva datos que no cambian mientras se edita\n * (el feedback y los cambios locales viven en el EstadoGrilla).\n */\nexport interface DefaultColumn extends BaseCustomColumn {\n customType: 'default';\n fieldDef: FieldDefinition;\n typer: Typer;\n isMandatory: boolean;\n isNumeric: boolean;\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>,\n primaryKey: string[],\n fixedFields: FixedField[] | undefined,\n registrarEditor: (procesar: (key: string) => void) => () => void,\n handleKeyPressInEditor: (rowIndex: number, columnKey: string, event: TeclaDeEditor, currentColumns: ColumnaDeGrilla[], handleCommit: (currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => Promise<void>, valorActual: unknown) => boolean\n}\n\nexport interface DetailColumn extends BaseCustomColumn {\n customType: 'detail';\n detailTable: DetailTable,\n primaryKey: string[],\n detailKey: string\n}\n\nexport interface ActionColumn extends BaseCustomColumn {\n customType: 'action';\n handleDeleteRow: (row: FilaDeDatos) => void;\n handleAddRow: (row?: FilaDeDatos) => void;\n handleVerticalEditRow: (row: FilaDeDatos) => void;\n}\n\n\nexport type CustomColumn = DefaultColumn | DetailColumn | ActionColumn;\n\n/** ¿Es una de las columnas que arma esta librería (campo, detalle o acciones)? */\nexport const esColumnaPropia = (columna: ColumnaDeGrilla): columna is CustomColumn => 'customType' in columna;\n\nconst SIN_ANCESTROS: Ancestor[] = [];\n\n/** Cargas de tabla (estructura + datos) que están en curso, por tabla y filtros */\nconst cargasEnCurso = new Map<string, Promise<{ definition: TableDefinition, data: FilaDeDatos[] }>>();\n\n/**\n * Componente auxiliar para mostrar un registro padre como una grilla estática.\n * Es de solo lectura: antes intentaba grabar con 'table_data_save', que no existe en backend-plus,\n * y además las columnas no tenían editor, así que la edición nunca funcionó.\n */\nconst StaticAncestorGrid: React.FC<{\n ancestor: Ancestor,\n index: number,\n fieldsToHide?: FixedField[],\n}> = ({ ancestor, index, fieldsToHide }) => {\n const theme = useTheme();\n const { tableDefinition, row } = ancestor; // ya no usamos fixedFields del propio objeto para filtrar SE a sí mismo\n\n const columns: Column<FilaDeDatos>[] = useMemo(() => {\n // Ocultamos los campos que el nivel SUPERIOR usó para filtrar a ESTE ancestro\n const filteredFields = tableDefinition.fields.filter(field =>\n !fieldsToHide?.some(ff => ff.fieldName === field.name)\n );\n\n return filteredFields.map(field => {\n const isPrimaryKey = tableDefinition.primaryKey.includes(field.name);\n return {\n key: field.name,\n name: field.label || cambiarGuionesBajosPorEspacios(field.name),\n width: 150,\n editable: false,\n renderHeaderCell: () => (\n <Box sx={{ p: 0.5, height: '100%', display: 'flex', alignItems: 'center' }}>\n <Typography\n variant=\"caption\"\n sx={{\n fontWeight: 'bold',\n textDecoration: isPrimaryKey ? 'underline' : 'none',\n fontSize: '0.930rem'\n }}\n >\n {field.label || cambiarGuionesBajosPorEspacios(field.name)}\n </Typography>\n </Box>\n ),\n renderCell: (props: RenderCellProps<FilaDeDatos>) => (\n <Box sx={{ px: 1, height: '100%', display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ fontSize: '0.875rem' }} noWrap>\n {String(props.row[field.name] ?? '')}\n </Typography>\n </Box>\n )\n };\n });\n }, [tableDefinition, fieldsToHide]);\n\n return (\n <Box sx={{\n mb: 1.5,\n ml: index * 4, // El primer ancestro (index 0) no tiene margen, los siguientes sí\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n overflow: 'hidden',\n boxShadow: theme.shadows[1],\n bgcolor: 'background.paper'\n }}>\n <Box sx={{\n bgcolor: 'primary.main',\n color: 'primary.contrastText',\n px: 1.5,\n py: 0.5,\n borderBottom: '1px solid',\n borderColor: 'divider',\n display: 'flex',\n alignItems: 'center',\n fontWeight: 'bold',\n gap: 1\n }}>\n <Typography variant=\"subtitle2\" component=\"div\">\n {cambiarGuionesBajosPorEspacios(ancestor.tableName)}\n </Typography>\n </Box>\n <DataGrid\n className='rdg-light'\n columns={columns}\n rows={[row]}\n headerRowHeight={HEADER_ROW_HEIGHT}\n rowHeight={ROW_HEIGHT}\n rowKeyGetter={() => 'single-row'}\n style={{ height: 78 }}\n />\n </Box>\n );\n};\n\nconst GenericDataGrid: React.FC<GenericDataGridProps> = ({\n tableName,\n fixedFields: fixedFieldsRecibidos,\n filterColumns: filterColumnsRecibidos,\n tableDef: tableDefRecibido,\n parameterFunctions: parameterFunctionsRecibido,\n pick: pickRecibido,\n gridStyles,\n onOpenDetail,\n ancestors = SIN_ANCESTROS,\n anidada = false,\n titulo,\n claveCache,\n onAltoDeseado,\n onAnchoDeseado,\n altoDisponible,\n altoMaximoDetalle,\n}) => {\n // fixedFields suele llegar como un array nuevo en cada render del padre:\n // se estabiliza por contenido para no volver a pedir los datos ni recrear las columnas\n const claveFixedFields = JSON.stringify(fixedFieldsRecibidos ?? null);\n const fixedFields = useMemo(() => fixedFieldsRecibidos, [claveFixedFields]); // eslint-disable-line react-hooks/exhaustive-deps\n // lo mismo con el resto de lo que llega de la dirección o del menú\n const claveParametros = JSON.stringify([filterColumnsRecibidos ?? null, tableDefRecibido ?? null, parameterFunctionsRecibido ?? null, pickRecibido ?? null]);\n const parametros = useMemo(\n () => ({ filterColumns: filterColumnsRecibidos, tableDef: tableDefRecibido, parameterFunctions: parameterFunctionsRecibido, pick: pickRecibido }),\n [claveParametros] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n // Cache compartido por toda la jerarquía de grillas (la grilla de más arriba lo crea)\n const cacheHeredado = useContext(CacheGrillasContext);\n const [cachePropio] = useState(() => new Map<string, GrillaGuardada>());\n const cache = cacheHeredado ?? cachePropio;\n // Lo guardado de esta grilla (si ya se mostró antes y salió de la pantalla por la virtualización)\n const [guardada] = useState(() => claveCache ? cache.get(claveCache) : undefined);\n // Qué tabla+fixedFields están cargados; si viene del cache ya están y no se vuelve a pedir\n const claveCargadaRef = useRef<string | null>(guardada ? `${tableName}|${claveFixedFields}|${claveParametros}` : null);\n\n const [tableDefinition, setTableDefinition] = useState<TableDefinition | null>(guardada?.tableDefinition ?? null);\n const [tableData, setTableData] = useState<FilaDeDatos[]>(guardada?.tableData ?? []);\n const [cargandoInicial, setCargandoInicial] = useState(!guardada);\n const [errorDeCarga, setErrorDeCarga] = useState<string | null>(null);\n const [anchosCalculados, setAnchosCalculados] = useState<Map<string, number>>(() => guardada?.anchosCalculados ?? new Map());\n // Detalles desplegados (claves `${rowId}|${abr}`) y el alto que pidió cada grilla anidada\n const [detallesAbiertos, setDetallesAbiertos] = useState<string[]>(guardada?.detallesAbiertos ?? []);\n const [altosDetalle, setAltosDetalle] = useState<Map<string, number>>(() => guardada?.altosDetalle ?? new Map());\n // Ancho que pidió cada grilla anidada (para ensanchar esta grilla si el detalle es más ancho que sus columnas)\n const [anchosDetalle, setAnchosDetalle] = useState<Map<string, number>>(() => new Map());\n const [selectedRows, setSelectedRows] = useState((): ReadonlySet<string> => new Set());\n const [, setSelectedCell] = useState<CellSelectArgs<FilaDeDatos, FilaDeResumen> | undefined>(undefined);\n\n // feedback y cambios locales por celda: fuera del estado de React (ver estadoGrilla.ts)\n const [estadoGrilla] = useState(crearEstadoGrilla);\n const theme = useTheme();\n\n const [openDataGridOptions, setOpenDataGridOptions] = useState(false);\n const [dataGridOptionsAnchorEl, setDataGridOptionsAnchorEl] = useState<HTMLElement | null>(null);\n\n const [exitingRowIds, setExitingRowIds] = useState<Set<string>>(new Set());\n\n const { showError } = useSnackbar();\n const { username: usuario } = useClientContext();\n const dataGridRef = useRef<DataGridHandle>(null);\n const rowAtEditStartRef = useRef<FilaDeDatos | null>(null); // Captura la fila cuando abre el vertical editor\n const [openImportDialog, setOpenImportDialog] = useState(false);\n const [openExportDialog, setOpenExportDialog] = useState(false);\n const [columnWidths, setColumnWidths] = useState<ReadonlyMap<string, ColumnWidth>>(() => new Map());\n // loading solo indica que hay una llamada en curso (borrar, importar, refrescar):\n // ya no reemplaza la grilla por un spinner, eso es solo para la carga inicial\n const { callApi, callApiUpload, loading } = useApiCall();\n\n const primaryKey = useMemo(() => {\n if (!tableDefinition) return ['id'];\n return tableDefinition.primaryKey && tableDefinition.primaryKey.length > 0 ? tableDefinition.primaryKey : ['id'];\n }, [tableDefinition]);\n\n const {\n sortColumns, setSortColumns, getInitialSortColumns, handleSortColumnsChange, resetSorting,\n filters, setFilters, isFilterRowVisible, toggleFilterVisibility, aplicarFiltrosIniciales,\n filteredRows, summaryData, resetView\n } = useGridDataView({ tableData, tableDefinition });\n\n // ── Detalles desplegados ─────────────────────────────────────────────────\n const abiertos = useMemo(() => new Set(detallesAbiertos), [detallesAbiertos]);\n // Se reusan los objetos de las filas de detalle mientras no cambie su fila padre:\n // así react-data-grid no redibuja la grilla anidada en cada render\n const filasDetalleRef = useRef(new Map<string, FilaDetalle>());\n\n /** filteredRows con las filas de detalle intercaladas debajo de su fila padre (en el orden de detailTables) */\n const filasMostradas = useMemo(() => {\n const detailTables = tableDefinition?.detailTables ?? [];\n if (abiertos.size === 0 || detailTables.length === 0) return filteredRows;\n const anteriores = filasDetalleRef.current;\n const nuevas = new Map<string, FilaDetalle>();\n const filas: FilaDeDatos[] = [];\n filteredRows.forEach(row => {\n filas.push(row);\n const rowId = getPrimaryKeyValues(row, primaryKey);\n detailTables.forEach(detailTable => {\n const clave = claveDetalle(rowId, detailTable);\n if (!abiertos.has(clave)) return;\n const anterior = anteriores.get(clave);\n const filaDetalle: FilaDetalle = anterior && anterior.filaPadre === row ? anterior : {\n [DETALLE]: true,\n clave,\n filaPadre: row,\n detailTable,\n fixedFields: calcularFixedFieldsDetalle(detailTable, row),\n };\n nuevas.set(clave, filaDetalle);\n filas.push(filaDetalle);\n });\n });\n filasDetalleRef.current = nuevas;\n return filas;\n }, [filteredRows, abiertos, tableDefinition, primaryKey]);\n\n const altoDeDetalle = useCallback((clave: string) => altosDetalle.get(clave) ?? ALTO_INICIAL_DETALLE, [altosDetalle]);\n const altoDetalles = useMemo(() =>\n filasMostradas.reduce((total, row) => esFilaDetalle(row) ? total + altoDeDetalle(row.clave) : total, 0)\n , [filasMostradas, altoDeDetalle]);\n\n const {\n handleAddRow, handleConfirmDelete, handleDeleteRow,\n openConfirmDialog, rowToDelete, handleImportFile,\n handleVerticalEditRow, openVerticalEditDialog,\n setOpenVerticalEditDialog, rowToEditVertical,\n setRowToEditVertical\n } = useGridActions({\n tableDefinition, tableName, primaryKey,\n fixedFields, setExitingRowIds, setLocalCellChanges: estadoGrilla.setCambiosLocales,\n setSelectedRows, setTableData, callApi, callApiUpload,\n resetView\n });\n\n // Como en backend-plus: hasta firstDisplayOverLimit filas se muestran todas; si hay más, solo\n // firstDisplayCount. En BP el resto se pide con \"ver más\"; acá se scrollea (con virtualización).\n // Solo aplica a las grillas anidadas: la principal ocupa la pantalla y scrollea siempre.\n const firstDisplayCount = tableDefinition?.firstDisplayCount ?? PRIMERAS_FILAS_ANIDADA;\n const firstDisplayOverLimit = tableDefinition?.firstDisplayOverLimit ?? LIMITE_PRIMERAS_FILAS_ANIDADA;\n const filasVisiblesMax = anidada ? (filteredRows.length <= firstDisplayOverLimit ? filteredRows.length : firstDisplayCount) : undefined;\n\n // Una grilla anidada sin campos con totales no necesita pie: era una fila vacía que ocupaba lugar\n const conPie = !anidada || !!tableDefinition?.fields.some(field => field.aggregate);\n\n const { gridHeight, bottomSummaryRow, summaryRows, filterHeight } = useGridLayout({\n filteredRows, isFilterRowVisible, tableData, summaryData, filters, setFilters, altoDetalles,\n altoMinimoCuerpo: anidada ? ALTO_MINIMO_CUERPO_ANIDADA : MIN_BODY_HEIGHT,\n holgura: anidada ? HOLGURA_ANIDADA : undefined,\n filasVisiblesMax,\n conPie,\n });\n\n // F9 en una celda que no se edita: el contenido completo (JSON o texto largo) bajo la celda\n const [contenidoDeCelda, setContenidoDeCelda] = useState<{ anclaje: HTMLElement, texto: string } | null>(null);\n const verContenido = useCallback((anclaje: HTMLElement, texto: string) => setContenidoDeCelda({ anclaje, texto }), []);\n const cerrarContenidoDeCelda = useCallback(() => {\n const anclaje = contenidoDeCelda?.anclaje;\n setContenidoDeCelda(null);\n setTimeout(() => anclaje?.focus(), 0);\n }, [contenidoDeCelda]);\n\n const { handleCellDoubleClick, handleCellClick, handleCellKeyDown, handleCellMouseDown, handleKeyPressInEditor, registrarEditor, handleSelectedCellChange, handleRowsChange } = useGridEvents({\n dataGridRef, filteredRows: filasMostradas, fixedFields, tableData, tableDefinition, setSelectedCell, setTableData, setSortColumns, verContenido\n });\n\n // Al cambiar de tabla se limpia todo (no al montar: puede venir restaurada del cache)\n const tableNameAnteriorRef = useRef(tableName);\n useEffect(() => {\n if (tableNameAnteriorRef.current === tableName) return;\n tableNameAnteriorRef.current = tableName;\n setDetallesAbiertos([]);\n setAltosDetalle(new Map());\n resetView();\n setSelectedRows(new Set());\n setTableDefinition(null);\n setTableData([]);\n estadoGrilla.setFeedback(new Map());\n estadoGrilla.setCambiosLocales(new Map());\n setExitingRowIds(new Set());\n setColumnWidths(new Map());\n }, [tableName, estadoGrilla, resetView]);\n\n useEffect(() => {\n const claveCarga = `${tableName}|${claveFixedFields}|${claveParametros}`;\n if (claveCargadaRef.current === claveCarga) {\n // Restaurada del cache: no se vuelve a pedir (como backend-plus al volver a desplegar un detalle)\n if (guardada) setSortColumns(guardada.sortColumns);\n return;\n }\n let vigente = true;\n const fetchDataAndDefinition = async () => {\n setCargandoInicial(true);\n setErrorDeCarga(null);\n try {\n // Si ya hay una carga igual en curso (el modo desarrollo de React monta los efectos dos veces) se espera esa\n const clavePedido = `${tableName}|${claveFixedFields}|${JSON.stringify([parametros.parameterFunctions ?? null, parametros.pick ?? null])}`;\n let pedido = cargasEnCurso.get(clavePedido);\n if (!pedido) {\n const nuevo = (async () => {\n const definition = requerido(\n await callApi<TableDefinition>('table_structure', { table: tableName }, { isCritical: true }),\n `la estructura de '${tableName}'`\n );\n const validFixedFields = fixedFields?.filter(ff =>\n definition.fields.some(field => field.name === ff.fieldName)\n );\n const data = requerido(\n await callApi<FilaDeDatos[]>('table_data', {\n table: tableName,\n fixedFields: validFixedFields ?? [],\n // como backend-plus (TableConnector.getData); solo si vienen, para no cambiarle el pedido a quien no los usa\n ...(parametros.parameterFunctions ? { paramfun: parametros.parameterFunctions } : {}),\n ...(parametros.pick !== undefined ? { pick: parametros.pick } : {})\n }, { isCritical: true }),\n `los datos de '${tableName}'`\n );\n return { definition, data };\n })();\n cargasEnCurso.set(clavePedido, nuevo);\n // cuando termina se saca: un refrescar posterior tiene que volver a pedir\n const sacar = () => { cargasEnCurso.delete(clavePedido); };\n nuevo.then(sacar, sacar);\n pedido = nuevo;\n }\n const { definition: definicionDelServidor, data } = await pedido;\n if (!vigente) return;\n claveCargadaRef.current = claveCarga;\n // como backend-plus (TableConnector.getStructure): lo de `td` se mezcla sobre la definición del servidor\n const definition = parametros.tableDef ? fusionar(definicionDelServidor, parametros.tableDef) : definicionDelServidor;\n setTableDefinition(definition);\n setSortColumns(getInitialSortColumns(definition));\n setAnchosCalculados(calcularAnchosDeColumnas(definition, data));\n setTableData(data);\n // la fila de filtros con la que se abre: la de la dirección o del menú (`fc`) o, si no, la de la definición\n const filasDeFiltros = parametros.filterColumns ?? aFiltrosIniciales(definition.filterColumns);\n if (filasDeFiltros.length > 1) console.warn(`'${tableName}': varias filas de filtros (alternativas) todavía no están soportadas; se aplica la primera`);\n if (filasDeFiltros.length > 0) aplicarFiltrosIniciales(filasDeFiltros[0], definition);\n } catch (err) {\n if (!vigente) return;\n setTableDefinition(null);\n setTableData([]);\n const mensaje = `Error al cargar datos para la tabla '${tableName}': ${mensajeDeError(err)}`;\n setErrorDeCarga(mensaje);\n showError(mensaje);\n } finally {\n if (vigente) setCargandoInicial(false);\n }\n };\n fetchDataAndDefinition();\n // si cambia la tabla antes de que llegue la respuesta, se descarta la respuesta vieja\n return () => { vigente = false; };\n }, [tableName, claveFixedFields, fixedFields, claveParametros, parametros, showError, callApi, getInitialSortColumns, setSortColumns, aplicarFiltrosIniciales, guardada]);\n\n // Se guarda el estado para poder volver a mostrar la grilla tal cual si se desmonta\n useEffect(() => {\n if (!claveCache || !tableDefinition || cargandoInicial) return;\n cache.set(claveCache, { tableDefinition, tableData, anchosCalculados, sortColumns, detallesAbiertos, altosDetalle });\n }, [cache, claveCache, tableDefinition, tableData, anchosCalculados, sortColumns, detallesAbiertos, altosDetalle, cargandoInicial]);\n\n // El feedback de éxito se borra solo al rato; el de error queda hasta la próxima edición\n useEffect(() => {\n let timer: ReturnType<typeof setTimeout> | null = null;\n const desuscribir = estadoGrilla.suscribir(() => {\n const hayExitos = Array.from(estadoGrilla.obtenerFeedback().values()).some(f => f.type === 'success');\n if (!hayExitos || timer) return;\n timer = setTimeout(() => {\n timer = null;\n estadoGrilla.setFeedback(anterior => {\n const nuevo: MapaFeedback = new Map(anterior);\n let cambio = false;\n for (const [cellId, feedback] of anterior.entries()) {\n if (feedback.type === 'success') {\n nuevo.delete(cellId);\n cambio = true;\n }\n }\n return cambio ? nuevo : anterior;\n });\n }, DURACION_FEEDBACK_EXITO);\n });\n return () => {\n desuscribir();\n if (timer) clearTimeout(timer);\n };\n }, [estadoGrilla]);\n\n const resetSortingDeLaTabla = useCallback(() => {\n if (tableDefinition) resetSorting(tableDefinition);\n }, [tableDefinition, resetSorting]);\n const abrirImportacion = useCallback(() => setOpenImportDialog(true), []);\n const abrirExportacion = useCallback(() => setOpenExportDialog(true), []);\n\n const { menuOptions } = useGridMenu({\n tableDefinition,\n tableName,\n fixedFields,\n setTableData,\n callApi,\n resetSorting: tableDefinition ? resetSortingDeLaTabla : undefined,\n triggerImport: abrirImportacion,\n triggerExport: abrirExportacion,\n });\n\n const abrirOpcionesDeGrilla = useCallback((anchorEl: HTMLElement) => {\n setDataGridOptionsAnchorEl(anchorEl);\n setOpenDataGridOptions(true);\n }, []);\n\n const editarEnFicha = useCallback((row: FilaDeDatos) => {\n rowAtEditStartRef.current = { ...row }; // Capturar snapshot antes de abrir\n handleVerticalEditRow(row);\n }, [handleVerticalEditRow]);\n\n const { columns } = useGridColumns({\n tableDefinition,\n anchosCalculados,\n fixedFields,\n primaryKey,\n setTableData,\n handleKeyPressInEditor,\n registrarEditor,\n handleDeleteRow,\n handleAddRow,\n handleVerticalEditRow: editarEnFicha,\n toggleFilterVisibility,\n abrirOpcionesDeGrilla,\n });\n\n /** Abre el detalle en otra pestaña (lo que antes hacía siempre el botón del detalle) */\n const abrirDetalleEnPestana = useCallback((row: FilaDeDatos, detailTable: DetailTable) => {\n if (!onOpenDetail || !tableDefinition || !detailTable.table) return;\n // Se acumulan los filtros que ya tiene la grilla actual (heredados de arriba), sin repetir campos\n const porCampo = new Map<string, FixedField>();\n [...(fixedFields ?? []), ...calcularFixedFieldsDetalle(detailTable, row)].forEach(ff => porCampo.set(ff.fieldName, ff));\n const allFixedFields = Array.from(porCampo.values());\n const filterDescription = allFixedFields\n .map(ff => `${cambiarGuionesBajosPorEspacios(ff.fieldName)}: ${ff.value}`)\n .join(', ');\n const label = `${cambiarGuionesBajosPorEspacios(detailTable.table).toUpperCase()} (${filterDescription})`;\n const newAncestors: Ancestor[] = [...ancestors, { tableName: tableDefinition.name, row, tableDefinition, fixedFields: allFixedFields }];\n onOpenDetail(detailTable.table, allFixedFields, label, newAncestors);\n }, [onOpenDetail, tableDefinition, fixedFields, ancestors]);\n\n /** Un detalle wScreen en otra pestaña: la ruta de la pantalla con los fixedFields del detalle como query string */\n const abrirWScreenEnPestana = useCallback((row: FilaDeDatos, detailTable: DetailTable) => {\n const parametros = new URLSearchParams();\n calcularFixedFieldsDetalle(detailTable, row).forEach(ff => parametros.set(ff.fieldName, String(ff.value)));\n window.open(`${window.location.origin}${rutaBase}/wScreens/${detailTable.wScreen}?${parametros}`, '_blank');\n }, []);\n\n const alternarDetalle = useCallback((row: FilaDeDatos, rowId: string, detailTable: DetailTable, enOtraPestana: boolean) => {\n if (enOtraPestana && detailTable.wScreen) {\n abrirWScreenEnPestana(row, detailTable);\n return;\n }\n if (enOtraPestana && onOpenDetail) {\n abrirDetalleEnPestana(row, detailTable);\n return;\n }\n const clave = claveDetalle(rowId, detailTable);\n setDetallesAbiertos(anteriores => anteriores.includes(clave) ? anteriores.filter(c => c !== clave) : [...anteriores, clave]);\n }, [onOpenDetail, abrirDetalleEnPestana, abrirWScreenEnPestana]);\n\n // Lo que se ve de esta grilla (el área de scroll de react-data-grid) y el ancho de su barra de scroll vertical\n const [medidas, setMedidas] = useState<{ ancho: number, alto: number, barra: number } | null>(null);\n useEffect(() => {\n const elemento = dataGridRef.current?.element;\n if (!elemento) return;\n const medir = () => setMedidas(anteriores => {\n const nuevas = { ancho: elemento.clientWidth, alto: elemento.clientHeight, barra: elemento.offsetWidth - elemento.clientWidth };\n return anteriores && anteriores.ancho === nuevas.ancho && anteriores.alto === nuevas.alto && anteriores.barra === nuevas.barra ? anteriores : nuevas;\n });\n medir();\n const observador = new ResizeObserver(medir);\n observador.observe(elemento);\n return () => observador.disconnect();\n }, [tableDefinition, cargandoInicial]);\n const anchoVisible = medidas?.ancho ?? null;\n\n // Alto que puede ocupar esta grilla: la principal, lo que queda de la ventana; una anidada, lo que le asigna la de arriba.\n // Se calcula de arriba hacia abajo y no con el alto medido: una grilla de pocas filas es baja, y si se acotara a\n // su propio alto el detalle quedaría aplastado.\n const [altoDeVentana, setAltoDeVentana] = useState<number | null>(null);\n useEffect(() => {\n if (anidada) return;\n const elemento = dataGridRef.current?.element;\n if (!elemento) return;\n const calcular = () => setAltoDeVentana(Math.max(0, Math.round(window.innerHeight - elemento.getBoundingClientRect().top - MARGEN_INFERIOR_PAGINA)));\n calcular();\n window.addEventListener('resize', calcular);\n return () => window.removeEventListener('resize', calcular);\n }, [anidada, tableDefinition, cargandoInicial]);\n // Una grilla que una wScreen arma dentro de un detalle recibe su alto máximo por contexto (menos el título de la grilla)\n const altoDeContexto = useContext(AltoDisponibleContext);\n const altoDeEstaGrilla = anidada\n ? altoDisponible ?? (altoDeContexto === undefined ? undefined : altoDeContexto - ALTO_TITULO_ANIDADA)\n : altoDeVentana ?? undefined;\n\n // Los detalles de esta grilla se acotan a ese alto (menos el encabezado, el pie y la fila padre), así el detalle\n // completo —con su propio pie— entra sin tener que scrollear la grilla de arriba. Con más contenido que eso\n // el detalle scrollea por dentro y virtualiza sus filas.\n const altoMaximoDetalleEfectivo = altoMaximoDetalle ?? (altoDeEstaGrilla !== undefined\n ? Math.max(ALTO_MINIMO_DETALLE, altoDeEstaGrilla - HEADER_ROW_HEIGHT - (conPie ? SUMMARY_ROW_HEIGHT : 0) - ROW_HEIGHT - (isFilterRowVisible ? FILTER_ROW_HEIGHT : 0))\n : undefined);\n\n const cambiarAnchoDetalle = useCallback((clave: string, ancho: number) => {\n setAnchosDetalle(anteriores => anteriores.get(clave) === ancho ? anteriores : new Map(anteriores).set(clave, ancho));\n }, []);\n\n const cambiarAltoDetalle = useCallback((clave: string, altoDeseado: number) => {\n const alto = Math.min(Math.max(Math.round(altoDeseado), ALTO_MINIMO_DETALLE), altoMaximoDetalleEfectivo ?? Infinity);\n setAltosDetalle(anteriores => anteriores.get(clave) === alto ? anteriores : new Map(anteriores).set(clave, alto));\n }, [altoMaximoDetalleEfectivo]);\n\n const renderDetalle = useCallback((fila: FilaDetalle) => {\n const { detailTable } = fila;\n return (\n <div\n className=\"fpr-detalle\"\n // el ancho lo da el contenido (ver .fpr-detalle) pero no pasa de lo visible de esta grilla\n style={{ maxWidth: anchoVisible ? anchoVisible - 2 : undefined }}\n // las teclas de la grilla anidada no tienen que mover la selección de esta grilla\n onKeyDown={(e) => e.stopPropagation()}\n >\n {detailTable.table\n ? <GenericDataGrid\n tableName={detailTable.table}\n fixedFields={fila.fixedFields}\n anidada\n claveCache={`${claveCache ?? tableName}>${fila.clave}`}\n onAltoDeseado={(alto) => cambiarAltoDetalle(fila.clave, alto)}\n onAnchoDeseado={(ancho) => cambiarAnchoDetalle(fila.clave, ancho)}\n altoDisponible={altoMaximoDetalleEfectivo === undefined ? undefined : altoMaximoDetalleEfectivo - ALTO_TITULO_ANIDADA - 8}\n onOpenDetail={onOpenDetail}\n ancestors={tableDefinition ? [...ancestors, { tableName: tableDefinition.name, row: fila.filaPadre, tableDefinition, fixedFields: fila.fixedFields }] : ancestors}\n />\n : detailTable.wScreen\n ? <DetalleWScreen\n fila={fila}\n altoMaximo={altoMaximoDetalleEfectivo}\n onAlto={(alto) => cambiarAltoDetalle(fila.clave, alto)}\n onAncho={(ancho) => cambiarAnchoDetalle(fila.clave, ancho)}\n />\n : <Alert severity=\"warning\">El detalle '{detailTable.abr}' no tiene ni tabla ni wScreen.</Alert>\n }\n </div>\n );\n }, [anchoVisible, claveCache, tableName, cambiarAltoDetalle, cambiarAnchoDetalle, altoMaximoDetalleEfectivo, onOpenDetail, tableDefinition, ancestors]);\n\n const detallesGrilla = useMemo(() => ({ abiertos, alternarDetalle, renderDetalle }), [abiertos, alternarDetalle, renderDetalle]);\n\n // La grilla anidada le informa a su padre cuánto alto necesita para mostrarse completa\n const altoDeseado = cargandoInicial ? ALTO_INICIAL_DETALLE : gridHeight + ALTO_TITULO_ANIDADA + 8;\n useEffect(() => {\n onAltoDeseado?.(altoDeseado);\n }, [onAltoDeseado, altoDeseado]);\n\n const rowKeyGetter = useCallback((row: FilaDeDatos) => esFilaDetalle(row) ? `detalle:${row.clave}` : getPrimaryKeyValues(row, primaryKey), [primaryKey]);\n const rowHeight = useCallback((row: FilaDeDatos) => esFilaDetalle(row) ? altoDeDetalle(row.clave) : ROW_HEIGHT, [altoDeDetalle]);\n // Las filas que se están borrando se marcan con una clase en lugar de copiar cada fila con un style:\n // copiar las filas en cada render anula la memorización de filas de react-data-grid\n const rowClass = useCallback((row: FilaDeDatos) =>\n esFilaDetalle(row) ? 'fpr-fila-detalle'\n : exitingRowIds.has(getPrimaryKeyValues(row, primaryKey)) ? 'fpr-fila-saliendo' : undefined\n , [exitingRowIds, primaryKey]);\n // Una grilla anidada es tan ancha como sus columnas (más la barra de scroll si la tiene), como las tablas de backend-plus;\n // sin esto el título y el pie quedaban estirados mucho más allá de los datos\n const anchoContenido = useMemo(() => {\n if (!anidada || columns.length === 0) return undefined;\n const sumaColumnas = columns.reduce((total, col) => {\n const medido = columnWidths.get(col.key as string)?.width;\n return total + (typeof medido === 'number' ? medido : typeof col.width === 'number' ? col.width : 100);\n }, 0);\n const propio = sumaColumnas + (medidas?.barra ?? 0) + 2;\n // si algún detalle abierto es más ancho que las columnas, esta grilla se ensancha para contenerlo\n const anchoDetalleMax = detallesAbiertos.reduce((max, clave) => Math.max(max, anchosDetalle.get(clave) ?? 0), 0);\n return Math.max(ANCHO_MINIMO_ANIDADA, propio, anchoDetalleMax > 0 ? anchoDetalleMax + SANGRIA_DETALLE + (medidas?.barra ?? 0) + 2 : 0);\n }, [anidada, columns, columnWidths, medidas?.barra, detallesAbiertos, anchosDetalle]);\n useEffect(() => {\n if (anchoContenido !== undefined) onAnchoDeseado?.(anchoContenido);\n }, [onAnchoDeseado, anchoContenido]);\n const estiloGrilla = useMemo<React.CSSProperties>(() => ({ height: '100%', width: '100%', boxSizing: 'border-box', ...gridStyles }), [gridStyles]);\n const bottomSummaryRows = useMemo(() => conPie ? [bottomSummaryRow] : [], [conPie, bottomSummaryRow]);\n const vistaGrilla = useMemo(() => ({ isFilterRowVisible }), [isFilterRowVisible]);\n\n const getFallBackMessage = () => {\n if (tableData.length === 0) {\n return \"No hay filas para mostrar\";\n }\n if (filteredRows.length === 0) {\n // Aquí ya sabemos que tableData.length > 0 implícitamente por el if anterior\n return \"No se encontraron resultados para tu búsqueda\";\n }\n return null;\n };\n\n const fallBackMessage = getFallBackMessage();\n\n if (cargandoInicial) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"200px\">\n <CircularProgress />\n <Typography variant=\"h6\" sx={{ ml: 2 }}>Cargando tabla...</Typography>\n </Box>\n );\n }\n\n if (errorDeCarga) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"200px\">\n <Alert severity=\"error\">{errorDeCarga}</Alert>\n </Box>\n );\n }\n\n if (!tableDefinition) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"200px\">\n <Alert severity=\"warning\">No se pudo cargar la definición de la tabla.</Alert>\n </Box>\n );\n }\n return (\n <CacheGrillasContext.Provider value={cache}>\n <Box sx={{ pt: anidada ? 0 : 2, display: 'flex', flexDirection: 'column', height: '100%', boxSizing: 'border-box', width: anchoContenido ?? undefined, maxWidth: '100%' }}>\n {/* Primero los ancestros jerárquicos (en una grilla anidada el padre está justo arriba) */}\n {!anidada && ancestors.length > 0 && (\n <Box sx={{ px: 2, pt: 0, pb: 1, display: 'flex', flexDirection: 'column' }}>\n {ancestors.map((ancestor, index) => (\n <StaticAncestorGrid\n key={index}\n ancestor={ancestor}\n index={index}\n fieldsToHide={index > 0 ? ancestors[index - 1].fixedFields : []}\n />\n ))}\n </Box>\n )}\n\n {/* Luego el título de la grilla actual */}\n <Box\n sx={{\n backgroundColor: theme.palette.primary.main,\n color: theme.palette.primary.contrastText,\n px: 1.5,\n py: 0.5,\n textAlign: 'left',\n fontWeight: 'bold',\n borderBottom: `1px solid ${theme.palette.divider}`,\n borderTopLeftRadius: theme.shape.borderRadius,\n borderTopRightRadius: theme.shape.borderRadius,\n ml: anidada ? 0 : 2 + (ancestors.length * 4), // Margen dinámico basado en jerarquía\n mr: anidada ? 0 : 2,\n minHeight: anidada ? ALTO_TITULO_ANIDADA : undefined,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n gap: 1\n }}\n >\n <Typography variant=\"subtitle2\" component=\"div\">\n {titulo ?? cambiarGuionesBajosPorEspacios(tableDefinition.title || tableDefinition.name)} - {filteredRows.length === tableData.length ? `${tableData.length} registros`\n : `${filteredRows.length} (F)`\n }\n </Typography>\n {loading && <CircularProgress size={14} color=\"inherit\" />}\n </Box>\n\n <Box\n sx={{\n // Una grilla anidada se acota con px (el alto que le dio la de arriba, o el de una wScreen): una wScreen\n // tiene alto automático y ahí un porcentaje no se resuelve. La principal ocupa lo que quede de la pantalla.\n height: anidada ? `${altoDeEstaGrilla === undefined ? gridHeight : Math.min(gridHeight, altoDeEstaGrilla)}px` : `min(${gridHeight}px, 100%)`,\n maxHeight: '100%',\n boxSizing: 'border-box',\n position: 'relative', // CRÍTICO para el centrado del fallback\n overflowX: 'auto',\n overflowY: 'auto',\n px: anidada ? 0 : 2,\n ml: anidada ? 0 : ancestors.length * 4, // Indentación de la grilla principal\n pb: anidada ? 0 : 3,\n }}\n >\n <EstadoGrillaContext.Provider value={estadoGrilla}>\n <VistaGrillaContext.Provider value={vistaGrilla}>\n <DetallesGrillaContext.Provider value={detallesGrilla}>\n <DataGrid<FilaDeDatos, FilaDeResumen, string>\n className='rdg-light'\n ref={dataGridRef}\n columns={columns}\n rows={filasMostradas}\n rowClass={rowClass}\n columnWidths={columnWidths}\n onColumnWidthsChange={setColumnWidths}\n enableVirtualization={true}\n rowKeyGetter={rowKeyGetter}\n onSelectedRowsChange={setSelectedRows}\n onSelectedCellChange={handleSelectedCellChange}\n onRowsChange={handleRowsChange}\n sortColumns={sortColumns}\n onSortColumnsChange={handleSortColumnsChange}\n selectedRows={selectedRows}\n rowHeight={rowHeight}\n style={estiloGrilla}\n headerRowHeight={HEADER_ROW_HEIGHT}\n topSummaryRows={summaryRows}\n bottomSummaryRows={bottomSummaryRows}\n summaryRowHeight={SUMMARY_ROW_HEIGHT}\n onCellMouseDown={handleCellMouseDown}\n onCellDoubleClick={handleCellDoubleClick}\n onCellClick={handleCellClick}\n onCellKeyDown={handleCellKeyDown}\n />\n </DetallesGrillaContext.Provider>\n </VistaGrillaContext.Provider>\n </EstadoGrillaContext.Provider>\n\n {/* Fallbacks */}\n {fallBackMessage && (\n <Box\n sx={{\n position: 'absolute',\n top: filterHeight + HEADER_ROW_HEIGHT + 1,\n bottom: anidada ? (conPie ? SUMMARY_ROW_HEIGHT : 0) + 4 : 102,\n left: 18,\n right: 0,\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n backgroundColor: 'rgba(255, 255, 255, 0.8)',\n pointerEvents: 'none',\n zIndex: tableData.length === 0 ? 10 : 1, // Mantenemos el zIndex superior para el estado vacío total\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {fallBackMessage}\n </Typography>\n </Box>\n )}\n </Box>\n <ExportDialog\n open={openExportDialog}\n onClose={() => setOpenExportDialog(false)}\n tableDefinition={tableDefinition}\n tableData={tableData}\n filteredRows={filteredRows}\n username={usuario || 'anonymous'}\n />\n <ConfirmDialog\n open={openConfirmDialog}\n onClose={handleConfirmDelete}\n title=\"Confirmar Eliminación\"\n message={rowToDelete && rowToDelete[NEW_ROW_INDICATOR]\n ? `¿Estás seguro de que quieres eliminar esta nueva fila (ID: ${getPrimaryKeyValues(rowToDelete, primaryKey)}) localmente?`\n : rowToDelete\n ? `¿Estás seguro de que quieres eliminar la fila con ID: ${getPrimaryKeyValues(rowToDelete, primaryKey)} de la base de datos? Esta acción es irreversible.`\n : '¿Estás seguro de que quieres eliminar este registro? Esta acción es irreversible.'\n }\n />\n <DataGridOptionsDialog\n open={openDataGridOptions}\n onClose={() => setOpenDataGridOptions(false)}\n options={menuOptions}\n anchorEl={dataGridOptionsAnchorEl}\n />\n <ImportDialog\n open={openImportDialog}\n onClose={() => setOpenImportDialog(false)}\n onImport={handleImportFile}\n loading={loading}\n />\n\n\n {/* Modal de Editor Vertical */}\n <Dialog\n open={openVerticalEditDialog}\n onClose={() => setOpenVerticalEditDialog(false)}\n maxWidth=\"sm\"\n fullWidth\n >\n {rowToEditVertical && (\n <VerticalEditorPage\n tableName={tableName}\n tableDefinition={tableDefinition}\n initialData={rowToEditVertical}\n fixedFields={fixedFields}\n isNewRow={!!rowToEditVertical[NEW_ROW_INDICATOR]}\n onSaveSuccess={(savedRow, isNewItem) => {\n setRowToEditVertical(savedRow);\n setTableData(prevData => {\n const newData = [...prevData];\n const pK = isNewItem ? getPrimaryKeyValues(rowToEditVertical, primaryKey) : getPrimaryKeyValues(savedRow, primaryKey);\n const index = newData.findIndex(r => getPrimaryKeyValues(r, primaryKey) === pK);\n if (index !== -1) {\n newData[index] = savedRow;\n } else {\n newData.unshift(savedRow);\n }\n return newData;\n });\n }}\n onClose={() => {\n // Resaltar los campos que cambiaron respecto al estado original al abrir,\n // comparando con lo que devolvió el servidor (captura triggers también)\n const originalRow = rowAtEditStartRef.current;\n const serverRow = rowToEditVertical;\n if (originalRow && serverRow) {\n const rowId = getPrimaryKeyValues(serverRow, primaryKey);\n const changedFields = tableDefinition.fields\n .filter(f => !sameValue(originalRow[f.name], serverRow[f.name]))\n .map(f => f.name);\n if (changedFields.length > 0) {\n estadoGrilla.setFeedback(prev => {\n const next = new Map(prev);\n changedFields.forEach(fieldName => {\n next.set(`${rowId}-${fieldName}`, { rowId, columnKey: fieldName, type: 'success' });\n });\n return next;\n });\n }\n }\n rowAtEditStartRef.current = null;\n setOpenVerticalEditDialog(false);\n }}\n />\n )}\n </Dialog>\n {contenidoDeCelda && (\n <VentanaDeTexto anclaje={contenidoDeCelda.anclaje} texto={contenidoDeCelda.texto} editable={false} onAceptar={cerrarContenidoDeCelda} onCerrar={cerrarContenidoDeCelda} />\n )}\n </Box>\n </CacheGrillasContext.Provider>\n );\n\n};\nexport default GenericDataGrid;\n","import { useState, useCallback } from 'react';\r\nimport { useSnackbar } from '../contexts/SnackbarContext';\r\nimport { executeBackendProcedure as baseExecuteBackendProcedure, executeBackendUpload as baseExecuteBackendUpload } from '../utils/fetchApi';\r\nimport { UseApiCallResult, CallApiOptions, ParametrosDeProcedimiento } from '../types';\r\nimport { mensajeDeError } from '../utils/errores';\r\n\r\n/** Lo que se atrapó en un catch, como Error (puede venir cualquier cosa) */\r\nconst comoError = (err: unknown): Error => err instanceof Error ? err : new Error(mensajeDeError(err));\r\n\r\nexport const useApiCall = (): UseApiCallResult => {\r\n const { showSnackbar } = useSnackbar();\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const callApi = useCallback(async <R = unknown>(\r\n procedureName: string,\r\n params: ParametrosDeProcedimiento,\r\n opts: CallApiOptions = { isCritical: true }\r\n ): Promise<R | undefined> => {\r\n setLoading(true);\r\n if (opts.isCritical) setError(null);\r\n try {\r\n const result = await baseExecuteBackendProcedure<R>(procedureName, params);\r\n return result === null ? undefined : result;\r\n } catch (err) {\r\n if (opts.isCritical) setError(comoError(err));\r\n let snackbarMessage = `Error inesperado al ejecutar '${procedureName}'.`;\r\n if (err instanceof TypeError) {\r\n snackbarMessage = `Problema de conexión: ${err.message}. Por favor, revisa tu internet o intenta de nuevo.`;\r\n } else if (err instanceof Error) {\r\n snackbarMessage = err.message;\r\n }\r\n showSnackbar(snackbarMessage, 'error');\r\n throw err\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [showSnackbar]);\r\n\r\n const callApiUpload = useCallback(async <R = unknown>(\r\n procedureName: string,\r\n file: File,\r\n params: ParametrosDeProcedimiento,\r\n opts: CallApiOptions = { isCritical: true }\r\n ): Promise<R | undefined> => {\r\n setLoading(true);\r\n if (opts.isCritical) setError(null);\r\n try {\r\n const result = await baseExecuteBackendUpload<R>(procedureName, file, params);\r\n return result === null ? undefined : result;\r\n } catch (err) {\r\n if (opts.isCritical) setError(comoError(err));\r\n let snackbarMessage = `Error inesperado al subir archivo en '${procedureName}'.`;\r\n if (err instanceof TypeError) {\r\n snackbarMessage = `Problema de conexión: ${err.message}. Por favor, revisa tu internet o intenta de nuevo.`;\r\n } else if (err instanceof Error) {\r\n snackbarMessage = err.message;\r\n }\r\n showSnackbar(snackbarMessage, 'error');\r\n throw err\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [showSnackbar]);\r\n\r\n return { callApi, callApiUpload, loading, error };\r\n};\r\n\r\nexport const useApiCallWithoutSnackbar = (): UseApiCallResult => {\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const callApi = useCallback(async <R = unknown>(\r\n procedureName: string,\r\n params: ParametrosDeProcedimiento,\r\n _opts: CallApiOptions = { isCritical: true }\r\n ): Promise<R | undefined> => {\r\n setLoading(true);\r\n setError(null);\r\n \r\n try {\r\n const result = await baseExecuteBackendProcedure<R>(procedureName, params);\r\n return result === null ? undefined : result;\r\n } catch (err) {\r\n // Manejamos el error internamente (opcionalmente logueando) pero NO usamos el Snackbar.\r\n setError(comoError(err)); \r\n console.error(`Error en la llamada API (WithoutSnackbar) para ${procedureName}:`, err);\r\n throw err;\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n const callApiUpload = useCallback(async <R = unknown>(\r\n procedureName: string,\r\n file: File,\r\n params: ParametrosDeProcedimiento,\r\n _opts: CallApiOptions = { isCritical: true }\r\n ): Promise<R | undefined> => {\r\n setLoading(true);\r\n setError(null);\r\n \r\n try {\r\n const result = await baseExecuteBackendUpload<R>(procedureName, file, params);\r\n return result === null ? undefined : result;\r\n } catch (err) {\r\n setError(comoError(err)); \r\n console.error(`Error en la subida API (WithoutSnackbar) para ${procedureName}:`, err);\r\n throw err;\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n return { callApi, callApiUpload, loading, error };\r\n};","// src/contexts/SnackbarContext.tsx\r\nimport React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';\r\nimport MuiAlert, { AlertProps } from '@mui/material/Alert';\r\nimport Snackbar from '@mui/material/Snackbar';\r\nimport { SnackbarContextType, SnackbarProviderProps, SnackbarState } from '../types';\r\n\r\n// Definir el tipo de las props para el Alert en el Snackbar\r\nconst SnackbarAlert = React.forwardRef<HTMLDivElement, AlertProps>(function SnackbarAlert(props, ref) {\r\n return <MuiAlert elevation={6} ref={ref} variant=\"filled\" {...props} />;\r\n});\r\n\r\n// Crear el contexto\r\nconst SnackbarContext = createContext<SnackbarContextType | undefined>(undefined);\r\n\r\n// Valores por defecto para la duración (en milisegundos)\r\nconst DEFAULT_SUCCESS_DURATION = 3000; \r\nconst DEFAULT_ERROR_DURATION = 10000; \r\n\r\nexport const SnackbarProvider: React.FC<SnackbarProviderProps> = ({ children }) => {\r\n const [snackbar, setSnackbar] = useState<SnackbarState>({\r\n open: false,\r\n message: '',\r\n severity: 'info',\r\n duration: DEFAULT_SUCCESS_DURATION, // Valor inicial\r\n });\r\n\r\n const showSnackbar = useCallback((message: string, severity: 'success' | 'error' | 'info' | 'warning', duration?: number | null) => {\r\n // Determina la duración final\r\n const finalDuration = duration !== undefined && duration !== null\r\n ? duration\r\n : (severity === 'error' ? DEFAULT_ERROR_DURATION : DEFAULT_SUCCESS_DURATION);\r\n\r\n setSnackbar({\r\n open: true,\r\n message,\r\n severity,\r\n duration: finalDuration,\r\n });\r\n }, []);\r\n\r\n // Adapta los métodos de conveniencia para aceptar la duración opcional\r\n const showSuccess = useCallback((message: string, duration?: number | null) => showSnackbar(message, 'success', duration), [showSnackbar]);\r\n const showError = useCallback((message: string, duration?: number | null) => showSnackbar(message, 'error', duration), [showSnackbar]);\r\n const showWarning = useCallback((message: string, duration?: number | null) => showSnackbar(message, 'warning', duration), [showSnackbar]);\r\n const showInfo = useCallback((message: string, duration?: number | null) => showSnackbar(message, 'info', duration), [showSnackbar]);\r\n\r\n const handleCloseSnackbar = (event?: React.SyntheticEvent | Event, reason?: string) => {\r\n if (reason === 'clickaway') {\r\n return;\r\n }\r\n setSnackbar(prev => ({ ...prev, open: false }));\r\n };\r\n\r\n return (\r\n <SnackbarContext.Provider value={{ showSnackbar, showSuccess, showError, showWarning, showInfo }}>\r\n {children}\r\n <Snackbar\r\n open={snackbar.open}\r\n autoHideDuration={snackbar.duration} // Usa la duración del estado\r\n onClose={handleCloseSnackbar}\r\n anchorOrigin={{ vertical: 'top', horizontal: 'center' }}\r\n >\r\n <SnackbarAlert onClose={handleCloseSnackbar} severity={snackbar.severity}>\r\n {snackbar.message}\r\n </SnackbarAlert>\r\n </Snackbar>\r\n </SnackbarContext.Provider>\r\n );\r\n};\r\n\r\n// Hook personalizado para consumir el contexto\r\nexport const useSnackbar = () => {\r\n const context = useContext(SnackbarContext);\r\n if (context === undefined) {\r\n throw new Error('useSnackbar must be used within a SnackbarProvider');\r\n }\r\n return context;\r\n};","// src/utils/errores.ts\n\n/** El mensaje de lo que se atrapó en un catch (en TypeScript es `unknown`: puede no ser un Error) */\nexport function mensajeDeError(error: unknown, porDefecto: string = 'Error desconocido'): string {\n if (error instanceof Error) return error.message || porDefecto;\n if (typeof error === 'string' && error !== '') return error;\n return porDefecto;\n}\n\n/** Un valor que tiene que existir (por ejemplo, el resultado de un procedimiento): si no, se informa qué faltó */\nexport function requerido<T>(valor: T | null | undefined, descripcion: string): T {\n if (valor === null || valor === undefined) {\n throw new Error(`El servidor no devolvió ${descripcion}`);\n }\n return valor;\n}\n","// frontend-plus-react/src/config/env.ts\r\n\r\ninterface EnvVariables {\r\n backendUrl: string;\r\n // Agrega aquí cualquier otra variable de entorno que tu librería necesite\r\n}\r\n\r\n// Función auxiliar para obtener la variable de entorno de forma agnóstica al bundler\r\nconst getEnvVar = (key: string): string => {\r\n // Intenta leer de import.meta.env (Vite)\r\n // import.meta.env lo define Vite; la librería no depende de los tipos de vite/client\r\n const viteEnv = (import.meta as ImportMeta & { env?: Record<string, string | undefined> }).env;\r\n if (viteEnv) {\r\n const viteKey = `VITE_${key}`;\r\n const valor = viteEnv[viteKey];\r\n if (valor) {\r\n return valor;\r\n }\r\n }\r\n\r\n // Intenta leer de process.env (CRA, Node.js)\r\n if (typeof process !== 'undefined' && process.env) {\r\n const craKey = `REACT_APP_${key}`;\r\n if (process.env[craKey]) {\r\n return process.env[craKey]!; // '!' para asegurar que no es undefined después de la verificación\r\n }\r\n // También intenta sin prefijo para otros entornos Node.js (menos común para frontends)\r\n if (process.env[key]) {\r\n return process.env[key]!;\r\n }\r\n }\r\n\r\n // Si no se encuentra, puedes lanzar un error o devolver un valor por defecto\r\n console.warn(`Variable de entorno \"${key}\" no encontrada. Asegúrate de definirla con el prefijo correcto (VITE_ o REACT_APP_).`);\r\n return ''; // O lanza un error: throw new Error(`Variable de entorno ${key} no definida.`);\r\n};\r\n\r\n// Exporta un objeto con todas las variables de entorno que tu librería necesita\r\nexport const envConfig: EnvVariables = {\r\n backendUrl: getEnvVar('BACKEND_URL'),\r\n // Agrega aquí las demás variables siguiendo el mismo patrón\r\n};","import { FetchApiOptions, ParametrosDeProcedimiento } from \"../types\";\r\nimport { mensajeDeError } from \"./errores\";\r\nimport { envConfig } from '../env'; // Ajusta la ruta según donde lo hayas guardado\r\nimport JSON4all from 'json4all';\r\nimport { ProcedureDef } from 'backend-plus';\r\n\r\ntype DefinicionesDeProcedimientos = Record<string, ProcedureDef>;\r\n\r\n/**\r\n * Fuente de las definiciones de procedimientos que manda el servidor en client-setup.\r\n * La registra el store para que este módulo no lo importe (evita la dependencia circular).\r\n */\r\nlet obtenerDefinicionesDeProcedimientos: () => DefinicionesDeProcedimientos | undefined = () => undefined;\r\n\r\nexport function registrarFuenteDeProcedimientos(fuente: () => DefinicionesDeProcedimientos | undefined) {\r\n obtenerDefinicionesDeProcedimientos = fuente;\r\n}\r\n\r\n/** Codifica un valor igual que lo hace my-ajax.js (myAjax.encoders), que es como lo decodifica el servidor */\r\nfunction codificarParametro(valor: unknown, encoding: string | undefined, procedureName: string, paramName: string): string {\r\n switch (encoding ?? 'JSON4all') {\r\n case 'plain':\r\n if (typeof valor === 'object' && valor !== null) {\r\n throw new Error(`El parámetro '${paramName}' de '${procedureName}' es plain y no admite objetos`);\r\n }\r\n return String(valor);\r\n case 'JSON':\r\n return JSON.stringify(valor);\r\n case 'JSON4all':\r\n return JSON4all.stringify(valor);\r\n default:\r\n throw new Error(`Encoding '${encoding}' no soportado para el parámetro '${paramName}' de '${procedureName}'`);\r\n }\r\n}\r\n\r\n/**\r\n * Arma los parámetros codificados según la definición del procedimiento.\r\n * Si el procedimiento no se conoce todavía (por ejemplo antes de cargar client-setup)\r\n * se usa la codificación anterior: primitivos tal cual y objetos con JSON4all.\r\n */\r\nexport function codificarParametros(procedureName: string, params: Record<string, unknown>): { codificados: Record<string, string>, method: 'get' | 'post' } {\r\n const definicion = obtenerDefinicionesDeProcedimientos()?.[procedureName];\r\n const codificados: Record<string, string> = {};\r\n if (!definicion) {\r\n for (const [nombre, valor] of Object.entries(params)) {\r\n if (valor === undefined) continue;\r\n codificados[nombre] = typeof valor === 'object' && valor !== null ? JSON4all.stringify(valor) : String(valor);\r\n }\r\n return { codificados, method: 'post' };\r\n }\r\n const declarados = new Set(definicion.parameters.map(p => p.name));\r\n for (const nombre of Object.keys(params)) {\r\n if (!declarados.has(nombre)) {\r\n console.warn(`El parámetro '${nombre}' no está declarado en el procedimiento '${procedureName}' y no se envía`);\r\n }\r\n }\r\n for (const paramDef of definicion.parameters) {\r\n const valor = params[paramDef.name];\r\n // si no viene, el servidor usa el defaultValue (o informa que falta)\r\n if (valor === undefined) continue;\r\n codificados[paramDef.name] = codificarParametro(valor, paramDef.encoding, procedureName, paramDef.name);\r\n }\r\n return { codificados, method: definicion.method ?? 'post' };\r\n}\r\n\r\n\r\n/**\r\n * Función genérica para realizar peticiones a la API.\r\n * Configurada para enviar cookies por defecto.\r\n *\r\n * @param endPoint La URL a la que se realizará la petición.\r\n * @param options Opciones de la petición fetch (método, headers, body, etc.).\r\n * @returns La respuesta de la petición.\r\n */\r\nexport async function fetchApi(endPoint: string, options: FetchApiOptions): Promise<Response> {\r\n const BACKEND_URL = envConfig.backendUrl;\r\n // sinContentType: el navegador arma el Content-Type solo (FormData lleva su boundary)\r\n const { sinContentType, ...opcionesDeFetch } = options;\r\n const headers = new Headers(opcionesDeFetch.headers);\r\n if (!sinContentType && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/x-www-form-urlencoded');\r\n }\r\n const config: RequestInit = {\r\n ...opcionesDeFetch,\r\n headers,\r\n credentials: 'include',\r\n };\r\n if (config.method === 'GET' || config.method === 'HEAD') {\r\n delete config.body;\r\n }\r\n return await fetch(BACKEND_URL + endPoint, config);\r\n}\r\n\r\n/** El servidor informa un error de un procedimiento con { error: { message } } */\r\nfunction esRespuestaConError(resultado: unknown): resultado is { error: { message: string } } {\r\n if (typeof resultado !== 'object' || resultado === null || !('error' in resultado)) return false;\r\n const { error } = resultado;\r\n return typeof error === 'object' && error !== null && 'message' in error;\r\n}\r\n\r\n/**\r\n * Interpreta la respuesta de un procedimiento y la devuelve con la forma que declara quien llama (T).\r\n * Es el único lugar donde se afirma esa forma: el servidor manda JSON4all y su estructura la define cada procedimiento.\r\n */\r\nfunction resultadoDeProcedimiento<T>(texto: string): T {\r\n const resultado: unknown = parseBackendResponse(texto);\r\n if (esRespuestaConError(resultado)) {\r\n throw new Error(resultado.error.message);\r\n }\r\n return resultado as T;\r\n}\r\n\r\nasync function ejecutar<T>(descripcion: string, procedureName: string, pedido: Promise<Response>): Promise<T | null> {\r\n const response = await pedido;\r\n if (response.ok) {\r\n return resultadoDeProcedimiento<T>(await response.text());\r\n }\r\n const errorTextDefinition = await response.text();\r\n throw new Error(`Error al ejecutar ${descripcion} '${procedureName}': ${response.status} - ${errorTextDefinition}`);\r\n}\r\n\r\nexport const executeBackendProcedure = async <T = unknown>(procedureName: string, params: ParametrosDeProcedimiento): Promise<T | null> => {\r\n const { codificados, method } = codificarParametros(procedureName, params);\r\n const query = new URLSearchParams(codificados);\r\n return ejecutar<T>('procedure', procedureName, method === 'get'\r\n ? fetchApi(`/${procedureName}?${query}`, { method: 'GET' })\r\n : fetchApi(`/${procedureName}`, { body: query, method: 'POST' }));\r\n}\r\n\r\nfunction parseBackendResponse(text: string): unknown {\r\n const trimmedText = text.trim();\r\n\r\n // El backend puede enviar múltiples líneas de progreso (JSONs por línea) \r\n // seguidas de un separador '--' y finalmente el resultado JSON.\r\n // Ejemplo:\r\n // {\"progress\":{}}\r\n // --\r\n // {\"uploaded\":{...}}\r\n\r\n // Buscamos el separador '--' que esté en una línea sola o después de un salto de línea\r\n const separatorMatch = trimmedText.match(/\\n--\\n/);\r\n let finalPart = trimmedText;\r\n\r\n if (separatorMatch) {\r\n // Si encontramos el separador, tomamos todo lo que está después del último separador\r\n const parts = trimmedText.split(/\\n--\\n/);\r\n finalPart = parts[parts.length - 1];\r\n } else {\r\n // Fallback: buscar '--' al principio o después de un salto de línea simple\r\n const parts = trimmedText.split(/\\n--/);\r\n if (parts.length > 1) {\r\n finalPart = parts[parts.length - 1];\r\n } else if (trimmedText.startsWith('--')) {\r\n finalPart = trimmedText.replace(/^--\\s*/, '');\r\n }\r\n }\r\n\r\n try {\r\n return JSON4all.parse(finalPart.trim());\r\n } catch (e) {\r\n // Si falla el parseo de la parte final, intentamos parsear la última línea que sea un JSON válido\r\n // Esto es un último recurso si el formato del separador varía\r\n const lines = trimmedText.split('\\n');\r\n for (let i = lines.length - 1; i >= 0; i--) {\r\n const line = lines[i].trim();\r\n if (line && line.startsWith('{') && line.endsWith('}')) {\r\n try {\r\n return JSON4all.parse(line);\r\n } catch (innerE) {\r\n continue;\r\n }\r\n }\r\n }\r\n throw new Error(`No se pudo parsear la respuesta del servidor: ${mensajeDeError(e)}`);\r\n }\r\n}\r\n\r\nexport const executeBackendUpload = async <T = unknown>(procedureName: string, file: File, params: ParametrosDeProcedimiento): Promise<T | null> => {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n // Añadimos el resto de parámetros al formData (los multipart no admiten encoding plain)\r\n const { codificados } = codificarParametros(procedureName, params);\r\n for (const [key, value] of Object.entries(codificados)) {\r\n formData.append(key, value);\r\n }\r\n\r\n return ejecutar<T>('upload', procedureName, fetchApi(`/${procedureName}`, { body: formData, method: 'POST', sinContentType: true }));\r\n}\r\n","// src/store/index.ts\r\n// ... tus imports existentes (incluyendo routerReducer) ...\r\nimport { configureStore, combineReducers } from '@reduxjs/toolkit';\r\nimport {\r\n persistStore,\r\n persistReducer,\r\n FLUSH,\r\n REHYDRATE,\r\n PAUSE,\r\n PERSIST,\r\n PURGE,\r\n REGISTER,\r\n} from 'redux-persist';\r\nimport storage from 'redux-persist/lib/storage';\r\n\r\nimport clientContextReducer from './clientContextSlice';\r\nimport routerReducer from './routerSlice';\r\nimport menuUiReducer from './menuUiSlice';\r\nimport { registrarFuenteDeProcedimientos } from '../utils/fetchApi';\r\n\r\nimport { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';\r\nimport { ClientContextState, MenuUiState } from '../types';\r\n\r\nconst rootReducer = combineReducers({\r\n clientContext: clientContextReducer,\r\n router: routerReducer,\r\n menuUi: menuUiReducer,\r\n // ...otros slices\r\n});\r\n\r\nconst persistConfig = {\r\n key: 'root',\r\n storage,\r\n whitelist: ['clientContext', 'router', 'menuUi'],\r\n};\r\n\r\nconst persistedReducer = persistReducer(persistConfig, rootReducer);\r\n\r\nexport const store = configureStore({\r\n reducer: persistedReducer,\r\n middleware: (getDefaultMiddleware) =>\r\n getDefaultMiddleware({\r\n serializableCheck: {\r\n ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],\r\n },\r\n }),\r\n});\r\n\r\nexport const persistor = persistStore(store);\r\n\r\n// fetchApi codifica los parámetros según las definiciones que vinieron en client-setup\r\nregistrarFuenteDeProcedimientos(() => {\r\n const { procedure, procedures } = store.getState().clientContext;\r\n if (procedure && Object.keys(procedure).length > 0) return procedure;\r\n return procedures?.length ? Object.fromEntries(procedures.map(p => [p.action, p])) : undefined;\r\n});\r\n\r\nexport type RootState = ReturnType<typeof store.getState>;\r\nexport type AppDispatch = typeof store.dispatch;\r\n\r\nexport const useAppDispatch: () => AppDispatch = useDispatch;\r\nexport const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;\r\n\r\n// Hooks selectores personalizados existentes\r\nexport const useClientContext = (): ClientContextState => {\r\n return useAppSelector(state => state.clientContext);\r\n};\r\n\r\nexport const useClientContextStatus = (): ClientContextState['status'] => {\r\n return useAppSelector(state => state.clientContext.status);\r\n};\r\n\r\nexport const useClientContextError = (): ClientContextState['error'] => {\r\n return useAppSelector(state => state.clientContext.error);\r\n};\r\n\r\nexport const useCurrentPath = (): string => {\r\n return useAppSelector(state => state.router.currentPath);\r\n};\r\n\r\nexport const usePreviousPath = (): string | null => {\r\n return useAppSelector(state => state.router.previousPath);\r\n};\r\n\r\n// Selectores para menuUi\r\nexport const useMenuUi = (): MenuUiState => {\r\n return useAppSelector(state => state.menuUi);\r\n};\r\n\r\nexport const useIsDrawerOpen = (): boolean => {\r\n return useAppSelector(state => state.menuUi.isDrawerOpen);\r\n};\r\n\r\nexport const useSubMenuOpenState = (menuName: string): boolean => {\r\n return useAppSelector(state => state.menuUi.subMenuOpenStates[menuName] || false);\r\n};\r\n\r\nexport const useRedirectPath = (): string | null => {\r\n return useAppSelector(state => state.router.redirectPath);\r\n};","// src/store/clientContextSlice.ts\r\nimport { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';\r\nimport { MenuInfoBase, ProcedureDef, AppConfigClientSetup } from \"backend-plus\";\r\nimport { Details } from 'express-useragent'; // Asumiendo que 'Details' viene de aquí\r\nimport { fetchApi } from '../utils/fetchApi'; // Tu utilidad fetchApi\r\nimport { ClientContextState } from '../types';\r\nimport { mensajeDeError } from '../utils/errores';\r\n\r\n// Estado inicial para el slice de clientContext\r\nconst initialState: ClientContextState = {\r\n menu: [],\r\n procedure: {},\r\n procedures: [],\r\n config: {} as AppConfigClientSetup, // Inicializa con un objeto vacío o valores por defecto adecuados\r\n useragent: {} as Details, // Igual aquí\r\n username: '',\r\n status: 'idle', // Por defecto, sin carga\r\n error: null, // Sin error\r\n};\r\n\r\n// **Thunk asíncrono para cargar los datos del clientContext**\r\n// Esto reemplaza tu función 'loadClientContextData'\r\nexport const fetchClientContext = createAsyncThunk(\r\n 'clientContext/fetchClientContext',\r\n async (_, { rejectWithValue }) => {\r\n try {\r\n const setupResponse = await fetchApi('/client-setup', { method: 'GET' });\r\n if (!setupResponse.ok) {\r\n const errorData = await setupResponse.text();\r\n throw new Error(errorData || 'Error al obtener configuración del cliente');\r\n }\r\n const clientSetup = await setupResponse.json();\r\n\r\n // Retorna los datos cargados. El 'status' y 'error' se manejan automáticamente\r\n // en los 'extraReducers'.\r\n return clientSetup as Omit<ClientContextState, 'status' | 'error'>;\r\n } catch (error) {\r\n // Usa rejectWithValue para pasar el mensaje de error al estado 'failed'\r\n return rejectWithValue(mensajeDeError(error, 'Error desconocido al cargar el contexto'));\r\n }\r\n }\r\n);\r\n\r\n// Creación del slice de Redux para clientContext\r\nconst clientContextSlice = createSlice({\r\n name: 'clientContext',\r\n initialState,\r\n reducers: {\r\n // Un reducer para limpiar el contexto del cliente (ej. al cerrar sesión)\r\n clearClientContext: (state) => {\r\n // Restablece el estado a su valor inicial\r\n Object.assign(state, initialState);\r\n },\r\n // Puedes añadir otros reducers aquí si necesitas modificar partes del clientContext directamente\r\n // pero el thunk es la forma preferida para la carga inicial.\r\n },\r\n // **extraReducers maneja las acciones del thunk asíncrono**\r\n extraReducers: (builder) => {\r\n builder\r\n // Cuando la petición está pendiente (cargando)\r\n .addCase(fetchClientContext.pending, (state) => {\r\n state.status = 'loading';\r\n state.error = null;\r\n })\r\n // Cuando la petición se completa con éxito\r\n .addCase(fetchClientContext.fulfilled, (state, action: PayloadAction<Omit<ClientContextState, 'status' | 'error'>>) => {\r\n state.status = 'succeeded';\r\n // Asigna los datos recibidos al estado\r\n state.menu = action.payload.menu;\r\n state.procedure = action.payload.procedure;\r\n state.procedures = action.payload.procedures;\r\n state.config = action.payload.config;\r\n state.useragent = action.payload.useragent;\r\n state.username = action.payload.username;\r\n state.error = null;\r\n })\r\n // Cuando la petición falla\r\n .addCase(fetchClientContext.rejected, (state, action) => {\r\n state.status = 'failed';\r\n state.error = action.payload as string || 'Fallo al cargar el contexto del cliente';\r\n // Puedes optar por limpiar el resto del estado o mantener el último valor si el error no es crítico\r\n // Object.assign(state, initialState); // Si quieres limpiar todo en caso de error\r\n });\r\n },\r\n});\r\n\r\nexport const { clearClientContext } = clientContextSlice.actions; // Exporta la acción generada\r\nexport default clientContextSlice.reducer; // Exporta el reducer","// src/store/routerSlice.ts\r\nimport { createSlice, PayloadAction } from '@reduxjs/toolkit';\r\nimport { RouterState } from '../types';\r\n\r\nconst initialState: RouterState = {\r\n currentPath: '/',\r\n previousPath: null,\r\n redirectPath: null, // <--- Inicializamos en null\r\n};\r\n\r\nconst routerSlice = createSlice({\r\n name: 'router',\r\n initialState,\r\n reducers: {\r\n setCurrentPath: (state, action: PayloadAction<string>) => {\r\n state.previousPath = state.currentPath;\r\n state.currentPath = action.payload;\r\n },\r\n setRedirectPath: (state, action: PayloadAction<string | null>) => { // <--- Nuevo reducer\r\n state.redirectPath = action.payload;\r\n },\r\n resetRouterState: (state) => {\r\n state.currentPath = '/';\r\n state.previousPath = null;\r\n state.redirectPath = null; // <--- Asegúrate de resetearlo también\r\n },\r\n },\r\n});\r\n\r\nexport const { setCurrentPath, setRedirectPath, resetRouterState } = routerSlice.actions; // <--- Exporta el nuevo action\r\nexport default routerSlice.reducer;","// src/store/menuUiSlice.ts\r\nimport { createSlice, PayloadAction } from '@reduxjs/toolkit';\r\nimport { MenuUiState } from '../types';\r\n\r\nconst initialState: MenuUiState = {\r\n isDrawerOpen: false,\r\n subMenuOpenStates: {},\r\n};\r\n\r\nconst menuUiSlice = createSlice({\r\n name: 'menuUi',\r\n initialState,\r\n reducers: {\r\n setDrawerOpen: (state, action: PayloadAction<boolean>) => {\r\n state.isDrawerOpen = action.payload;\r\n },\r\n toggleDrawer: (state) => {\r\n state.isDrawerOpen = !state.isDrawerOpen;\r\n },\r\n setSubMenuOpen: (state, action: PayloadAction<{ menuName: string; isOpen: boolean }>) => {\r\n state.subMenuOpenStates[action.payload.menuName] = action.payload.isOpen;\r\n },\r\n toggleSubMenu: (state, action: PayloadAction<string>) => {\r\n state.subMenuOpenStates[action.payload] = !state.subMenuOpenStates[action.payload];\r\n },\r\n // --- NUEVO REDUCER: Establecer el estado de todos los submenús ---\r\n setAllSubMenusOpen: (state, action: PayloadAction<boolean>) => {\r\n // Itera sobre todas las claves existentes y las establece al valor dado\r\n for (const menuName in state.subMenuOpenStates) {\r\n if (Object.prototype.hasOwnProperty.call(state.subMenuOpenStates, menuName)) {\r\n state.subMenuOpenStates[menuName] = action.payload;\r\n }\r\n }\r\n // Importante: Si hay nuevos submenús que no están en subMenuOpenStates,\r\n // no se verán afectados por este bucle.\r\n // Una estrategia más robusta para casos donde el menú puede cambiar dinámicamente\r\n // sería obtener todos los nombres de menú recursivamente desde clientContext.menu\r\n // y actualizar sus estados. Por ahora, asumimos que los que ya están en el estado\r\n // son los que queremos controlar.\r\n },\r\n resetMenuUiState: (state) => {\r\n Object.assign(state, initialState);\r\n },\r\n },\r\n});\r\n\r\nexport const {\r\n setDrawerOpen,\r\n toggleDrawer,\r\n setSubMenuOpen,\r\n toggleSubMenu,\r\n setAllSubMenusOpen, // ¡Exportamos la nueva acción!\r\n resetMenuUiState,\r\n} = menuUiSlice.actions;\r\n\r\nexport default menuUiSlice.reducer;","import Dialog from \"@mui/material/Dialog\";\nimport { ConfirmDialogProps } from \"../types\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogContentText from \"@mui/material/DialogContentText\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport Button from \"@mui/material/Button\";\n\nexport const ConfirmDialog: React.FC<ConfirmDialogProps> = ({ open, onClose, title, message }) => {\n return (\n <Dialog\n open={open}\n onClose={() => onClose(false)}\n aria-labelledby=\"confirm-dialog-title\"\n aria-describedby=\"confirm-dialog-description\"\n >\n <DialogTitle id=\"confirm-dialog-title\">{title}</DialogTitle>\n <DialogContent>\n <DialogContentText id=\"confirm-dialog-description\">\n {message}\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => onClose(false)} color=\"primary\">\n Cancelar\n </Button>\n <Button onClick={() => onClose(true)} color=\"error\" autoFocus>\n Confirmar\n </Button>\n </DialogActions>\n </Dialog>\n );\n};","import {\n Popover,\n List,\n ListItemButton,\n ListItemIcon,\n ListItemText,\n Paper,\n CircularProgress,\n Box\n} from '@mui/material';\nimport { useState } from 'react';\nimport { DataGridOption } from '../../types';\n\ninterface DataGridOptionsDialogProps {\n open: boolean;\n onClose: () => void;\n options: DataGridOption[];\n anchorEl: HTMLElement | null;\n}\n\nexport const DataGridOptionsDialog: React.FC<DataGridOptionsDialogProps> = ({ \n open, \n onClose, \n options,\n anchorEl\n}) => {\n const [isExecuting, setIsExecuting] = useState(false);\n\n const handleOptionClick = async (handler: () => Promise<void>) => {\n try {\n setIsExecuting(true);\n await handler();\n } catch (error) {\n console.error('Error ejecutando handler:', error);\n } finally {\n setIsExecuting(false);\n onClose();\n }\n };\n\n return (\n <>\n {open && (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.2)',\n zIndex: 1200,\n pointerEvents: 'none'\n }}\n />\n )}\n <Popover\n open={open}\n anchorEl={anchorEl}\n onClose={onClose}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left',\n }}\n transformOrigin={{\n vertical: 'top',\n horizontal: 'left',\n }}\n slotProps={{\n paper: {\n sx: {\n zIndex: 1300,\n }\n }\n }}\n >\n <Paper sx={{ minWidth: 150, maxWidth: 350 }}>\n {isExecuting && (\n <Box \n sx={{ \n display: 'flex', \n justifyContent: 'center', \n alignItems: 'center',\n p: 5\n }}\n >\n <CircularProgress size={40} />\n </Box>\n )}\n {!isExecuting && (\n <List sx={{ \n py: 0,\n '& .MuiListItemButton-root': { \n py: 0.5, \n px: 1.5,\n minHeight: 36\n },\n '& .MuiListItemIcon-root': { \n minWidth: 32 \n },\n '& .MuiListItemText-primary': { \n fontSize: '0.85rem' \n },\n '& .MuiSvgIcon-root': {\n fontSize: 18\n }\n }}>\n {options\n .filter(option => option.visible !== false)\n .map((option) => (\n <ListItemButton \n key={option.id}\n onClick={() => handleOptionClick(option.handler)}\n >\n <ListItemIcon>\n {option.icon}\n </ListItemIcon>\n <ListItemText primary={option.label} />\n </ListItemButton>\n ))\n }\n </List>\n )}\n </Paper>\n </Popover>\n </>\n );\n};","// src/components/grid/utils/helpers.ts\nimport typeStore, { Typer } from 'type-store';\nimport { DOM_TEMP_ID, FieldDefinition, FilaDeDatos } from '../../../types';\n\nexport type { Typer };\n\nexport const typerFrom = (fieldDef: FieldDefinition): Typer => typeStore.typerFrom(fieldDef);\n\n/**\n * Indicadores especiales para filas\n */\nexport const NEW_ROW_INDICATOR = '$new';\nexport { DOM_TEMP_ID };\n\n/**\n * Obtiene los valores de la clave primaria de una fila concatenados en un string único\n * @param row - Fila de datos\n * @param primaryKey - Array con los nombres de las columnas que forman la PK\n * @returns String único que identifica la fila\n */\nexport const getPrimaryKeyValues = (row: FilaDeDatos, primaryKey: string[]): string => {\n // Si la fila tiene un ID de DOM explícito (Symbol), lo usamos\n if (row[DOM_TEMP_ID]) return String(row[DOM_TEMP_ID]);\n\n return primaryKey.map(key => {\n return row[key] !== undefined && row[key] !== null\n ? String(row[key])\n : 'NULL_OR_UNDEFINED';\n }).join('|');\n};\n\n/**\n * Compara dos valores de forma robusta, manejando Fechas y nulos.\n * Basado en sameValue de best-globals/backend-plus.\n */\nexport const sameValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();\n \n // Tratamos null y undefined como equivalentes para el propósito de \"valor vacío\" \n // en celdas de grilla si uno de ellos está presente y el otro no.\n const isAEmpty = a === null || a === undefined;\n const isBEmpty = b === null || b === undefined;\n if (isAEmpty && isBEmpty) return true;\n\n return false;\n};\n\n\n/**\n * Obtiene la clave única para una celda.\n * @param row La fila actual.\n * @param columnKey La clave de la columna.\n * @param primaryKey La clave primaria de la tabla.\n * @returns Clave única de la celda (ej: \"pkValue1|pkValue2|columnKey\").\n */\nexport const getCellKey = (row: FilaDeDatos, columnKey: string, primaryKey: string[]): string => {\n return `${getPrimaryKeyValues(row, primaryKey)}|${columnKey}`;\n};\n\n/**\n * Checks if a given database field type name corresponds to a numeric field.\n * @param typeName - The string representing the database field type\n * @returns boolean\n */\nexport const isNumericType = (typeName?: string): boolean => {\n return ['integer', 'bigint', 'decimal', 'double', 'float', 'number'].includes(typeName || '');\n};\n\n/**\n * Checks if a given database field type name corresponds to a date or time field.\n * @param typeName - The string representing the database field type\n * @returns boolean\n */\nexport const isDateTimeType = (typeName?: string): boolean => {\n const t = typeName?.toLowerCase() || '';\n return ['date', 'time', 'timestamp', 'interval', 'tsrange', 'time_range', 'daterange'].some(type => t.includes(type));\n};\n\n/**\n * Compara dos valores del mismo tipo (números, fechas, textos, bigint). Un número contra un texto numérico se compara\n * como número (el valor de un filtro que no se pudo tipar sigue siendo un texto). null si no son comparables.\n */\nexport const compararValores = (a: unknown, b: unknown): number | null => {\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : a > b ? 1 : 0;\n if (typeof a === 'bigint' && typeof b === 'bigint') return a < b ? -1 : a > b ? 1 : 0;\n const esNumerico = (v: unknown): v is string => typeof v === 'string' && v.trim() !== '' && !Number.isNaN(Number(v));\n if (typeof a === 'number' && esNumerico(b)) return a - Number(b);\n if (esNumerico(a) && typeof b === 'number') return Number(a) - b;\n return null;\n};\n\nexport const filterOperators = [\n { value: '=', label: '=' },\n { value: '~', label: '~' },\n { value: '!~', label: '!~' },\n { value: '\\u2205', label: '∅' }, // vacío\n { value: '!=\\u2205', label: '!=∅' }, // no vacío\n { value: '>', label: '>' },\n { value: '>=', label: '>=' },\n { value: '<', label: '<' },\n { value: '<=', label: '<=' },\n { value: '!=', label: '!=' }\n];\n\nexport const getDefaultOperatorForType = (typeName?: string): string => {\n const isText = ['text', 'varchar', 'char'].some(t => typeName?.toLowerCase().includes(t)) \n || (!isNumericType(typeName) && !isDateTimeType(typeName) && typeName?.toLowerCase() !== 'boolean');\n return isText ? '~' : '=';\n};","// frontend-plus-react/src/utils/routeUtils.ts (o dentro de App.tsx)\r\nimport React, { Children, isValidElement, ReactNode } from 'react';\r\n\r\n/**\r\n * Recorre recursivamente los hijos de React buscando componentes que tengan una prop 'path'.\r\n * Esto permite pasar <Route path=\"...\"> o Fragmentos <><Route.../></> y extraer los strings.\r\n */\r\nexport const extractPathsFromRoutes = (node: ReactNode): string[] => {\r\n const paths: string[] = [];\r\n\r\n Children.forEach(node, (child) => {\r\n if (!isValidElement(child)) return;\r\n\r\n // Caso 1: Es un <Route path=\"/algo\" ... /> directo\r\n if (child.props.path) {\r\n paths.push(child.props.path);\r\n }\r\n\r\n // Caso 2: Es un Fragmento <>...</> o un componente wrapper que tiene children\r\n // (Tus rutas unlogged vienen dentro de un Fragment <>)\r\n if (child.props.children) {\r\n paths.push(...extractPathsFromRoutes(child.props.children));\r\n }\r\n });\r\n\r\n return paths;\r\n};","import { AppConfigClientSetup, DetailTable, MenuInfoBase, ProcedureDef, TableDefinition, FieldDefinition } from \"backend-plus\";\r\nimport { Details } from \"express-useragent\";\r\nimport { ReactNode, FC, KeyboardEvent, Dispatch, SetStateAction } from \"react\";\r\nimport { Column, RenderCellProps, RenderHeaderCellProps } from \"react-data-grid\";\r\n\r\nexport type { FieldDefinition, TableDefinition };\r\n\r\n/**\r\n * Marca con la que una fila nueva conserva su identidad en la grilla mientras se edita (antes de tener su clave primaria).\r\n * Es un Symbol: no aparece en Object.keys ni en JSON.stringify, pero el spread de la fila (...fila) la conserva.\r\n */\r\nexport const DOM_TEMP_ID: unique symbol = Symbol('domTempId');\r\n\r\n/** Una fila de datos de una tabla: los valores salen del servidor ya decodificados (JSON4all) */\r\nexport type FilaDeDatos = Record<string, unknown> & { [DOM_TEMP_ID]?: string };\r\n\r\nexport interface WScreenProps {\r\n screenName: string;\r\n /**\r\n * Parámetros de la pantalla como objeto plano (en backend-plus, el primer argumento de `mainAction`).\r\n * Como detalle de una grilla son los fixedFields del detalle; en una ruta, la query string.\r\n */\r\n params?: Record<string, unknown>;\r\n}\r\n\r\n/** Decide, para una fila, si se muestra el botón de un detalle (`condition` del DetailTable) */\r\nexport type CondicionDeDetalle = (fila: FilaDeDatos) => boolean;\r\nexport type CondicionesMap = Record<string, CondicionDeDetalle>;\r\n\r\nexport interface ClientSideProps extends RenderCellProps<FilaDeDatos, FilaDeResumen> {\r\n fieldDefinition: FieldDefinition;\r\n tableDefinition: TableDefinition;\r\n primaryKey: string[];\r\n}\r\n\r\nexport interface ResultOkProps {\r\n /** Lo que devolvió el procedimiento (la forma depende de cada procedimiento) */\r\n data: unknown;\r\n}\r\n\r\nexport type WScreenMap = Record<string, FC<WScreenProps>>;\r\nexport type ClientSidesMap = Record<string, FC<ClientSideProps>>;\r\nexport type ResultsOksMap = Record<string, FC<ResultOkProps>>;\r\n\r\nexport interface AppProps {\r\n myRoutes?: ReactNode;\r\n myUnloggedRoutes?: ReactNode;\r\n unloggedLayout?: React.ComponentType;\r\n myWScreens?: WScreenMap;\r\n myClientSides?: ClientSidesMap;\r\n myResultsOk?: ResultsOksMap;\r\n /** Condiciones de los detalles, por nombre (la `condition` de un DetailTable) */\r\n myConditions?: CondicionesMap;\r\n}\r\n\r\nexport {extractPathsFromRoutes} from \"../utils/routeUtils\";\r\n\r\n// --- MODIFICACIONES DE FEEDBACK ---\r\n\r\n\r\nexport interface CellFeedback {\r\n rowId: string;\r\n columnKey: string;\r\n type: 'success' | 'error';\r\n message?: string; // Corregido el tipo de 'message' a 'string'\r\n}\r\n\r\n// Tipo para el mapa de feedback, usando una clave string (rowId-columnKey)\r\nexport type CellFeedbackMap = Map<string, CellFeedback>; \r\n// Nota: También podrías usar Record<string, CellFeedback> si prefieres objetos.\r\n\r\n/** Lo que el manejo de teclas del editor necesita saber de la tecla (sirve para un evento real o uno encolado) */\r\nexport type TeclaDeEditor = Pick<React.KeyboardEvent, 'key' | 'target'> & Partial<Pick<React.KeyboardEvent, 'shiftKey' | 'ctrlKey' | 'altKey' | 'metaKey'>>;\r\n\r\nexport interface InputRendererProps<R extends FilaDeDatos, S> {\r\n column: Column<R, S>,\r\n row: R,\r\n rowIdx: number;\r\n onRowChange: (row: R, commitChanges?: boolean) => void;\r\n onClose: (commitChanges?: boolean, shouldFocusCell?: boolean) => void;\r\n tableDefinition: TableDefinition,\r\n // el feedback y los cambios locales se leen y escriben en el EstadoGrilla (ver estadoGrilla.ts)\r\n /** Con cada tecla del editor. `valorActual` es el texto que tiene el editor; devuelve true si la tecla la resolvió la grilla (dejó la celda) y hay que cancelar su efecto nativo */\r\n onKeyPress?: (rowIndex: number, columnKey: string, event: TeclaDeEditor, handleCommit:(currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => Promise<void>, valorActual: unknown) => boolean;\r\n /** El editor se registra para recibir las teclas (F4) que caen en la celda antes de que él tenga el foco; devuelve la baja */\r\n registrarEditor?: (procesar: (key: string) => void) => () => void;\r\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;\r\n primaryKey: string[];\r\n}\r\n\r\n// ------------------------------------\r\n\r\nexport interface Ancestor {\r\n tableName: string;\r\n row: FilaDeDatos;\r\n tableDefinition: TableDefinition;\r\n fixedFields?: FixedField[];\r\n}\r\n\r\nexport interface GenericDataGridProps {\r\n tableName: string;\r\n fixedFields?: FixedField[];\r\n onOpenDetail?: (tableName: string, fixedFields: FixedField[], label: string, ancestors: Ancestor[]) => void;\r\n gridStyles?: React.CSSProperties;\r\n ancestors?: Ancestor[];\r\n}\r\n\r\nexport interface FixedField {\r\n fieldName: string;\r\n value: unknown;\r\n until?: unknown;\r\n /** Como en backend-plus (my-tables.js): un fixedField con `range` o `until` no fija el valor de la columna (la deja visible y editable) */\r\n range?: boolean;\r\n /** Como en backend-plus: el campo fijo no oculta su columna */\r\n show?: boolean;\r\n}\r\n\r\nexport interface ConfirmDialogProps {\r\n open: boolean;\r\n onClose: (confirm: boolean) => void;\r\n title: string;\r\n message: string;\r\n}\r\n\r\n/** El filtro de una columna: un texto (busca que contenga) o un operador con su valor */\r\nexport type FiltroDeColumna = string | { operator?: string; value?: string };\r\nexport type FiltrosDeGrilla = Record<string, FiltroDeColumna>;\r\n\r\n/** Una entrada de la fila de totales: qué se calculó (suma, promedio...) y su valor */\r\nexport type EntradaDeTotal = { type: string; value: number };\r\n\r\n/** Fila de resumen con los filtros de cada columna (arriba de la grilla, cuando se muestran los filtros) */\r\nexport type FilaDeFiltros = {\r\n id: 'filterRow';\r\n filters: FiltrosDeGrilla;\r\n setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;\r\n};\r\n\r\n/** Fila de resumen con los totales de las columnas que los tienen (pie de la grilla) */\r\nexport type FilaDeTotales = {\r\n id: 'bottomSummaryRow';\r\n totales: Record<string, EntradaDeTotal>;\r\n};\r\n\r\n/** Las filas de resumen de react-data-grid: filtros o totales */\r\nexport type FilaDeResumen = FilaDeFiltros | FilaDeTotales;\r\n\r\nexport interface FilterRendererProps<R extends FilaDeDatos, S> {\r\n column: Column<R, S>;\r\n filters: FiltrosDeGrilla;\r\n setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;\r\n}\r\n\r\nexport interface GenericDataGridForDetailProps {\r\n parentRow: FilaDeDatos; // La fila padre\r\n detailTableDefinition: DetailTable;\r\n parentTableName: string; // Añadido: Propiedad parentTableName\r\n}\r\n\r\nexport interface SideMenuProps {\r\n onMenuItemClick?: () => void;\r\n}\r\n\r\nexport interface MenuListItemProps {\r\n item: MenuInfoBase;\r\n level: number;\r\n onMenuItemClick?: () => void;\r\n}\r\n\r\nexport interface CustomCellProps<R extends FilaDeDatos, SR> extends RenderCellProps<R, SR> {\r\n column: RenderCellProps<R, SR>['column'] & { isPK?: boolean };\r\n}\r\n\r\nexport interface CustomHeaderCellProps<R, SR> extends RenderHeaderCellProps<R, SR> {\r\n column: RenderHeaderCellProps<R, SR>['column'] & { isPK?: boolean };\r\n}\r\n\r\nexport interface AppContextType {\r\n isLoggedIn: boolean;\r\n setIsLoggedIn: (loggedIn: boolean) => void;\r\n checkSession: () => Promise<void>;\r\n showSessionExpiredMessage: boolean;\r\n setShowSessionExpiredMessage: (show: boolean) => void;\r\n handleExpiredSessionRedirect: () => void;\r\n}\r\n\r\nexport interface AppProviderProps {\r\n children: ReactNode;\r\n publicPaths?: string[];\r\n}\r\n\r\nexport interface PrivateRouteProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport interface SnackbarState {\r\n open: boolean;\r\n message: string;\r\n severity: 'success' | 'error' | 'info' | 'warning';\r\n duration?: number | null; // Añadimos 'duration' opcional (en ms)\r\n}\r\n\r\n// Definir el tipo del contexto\r\nexport interface SnackbarContextType {\r\n // showSnackbar ahora acepta una duración opcional\r\n showSnackbar: (message: string, severity: 'success' | 'error' | 'info' | 'warning', duration?: number | null) => void;\r\n showSuccess: (message: string, duration?: number | null) => void;\r\n showError: (message: string, duration?: number | null) => void;\r\n showWarning: (message: string, duration?: number | null) => void;\r\n showInfo: (message: string, duration?: number | null) => void;\r\n}\r\n\r\nexport interface SnackbarProviderProps {\r\n children: ReactNode;\r\n}\r\n\r\nexport interface CallApiOptions {\r\n isCritical?: boolean;\r\n}\r\n\r\n/** Los parámetros de un procedimiento por nombre; se codifican según la definición que manda el servidor */\r\nexport type ParametrosDeProcedimiento = Record<string, unknown>;\r\n\r\nexport interface UseApiCallResult {\r\n /** R es la forma del resultado de ese procedimiento (el servidor puede no devolver nada: undefined) */\r\n callApi: <R = unknown>(procedureName: string, params: ParametrosDeProcedimiento, opts?: CallApiOptions) => Promise<R | undefined>;\r\n callApiUpload: <R = unknown>(procedureName: string, file: File, params: ParametrosDeProcedimiento, opts?: CallApiOptions) => Promise<R | undefined>;\r\n loading: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport interface ClientContextState {\r\n menu: MenuInfoBase[];\r\n procedure: { [key: string]: ProcedureDef };\r\n procedures: ProcedureDef[];\r\n config: AppConfigClientSetup;\r\n useragent: Details;\r\n username: string;\r\n status: 'idle' | 'loading' | 'succeeded' | 'failed'; // Estado de la petición\r\n error: string | null; // Mensaje de error\r\n}\r\n\r\nexport interface MenuUiState {\r\n isDrawerOpen: boolean;\r\n subMenuOpenStates: { [key: string]: boolean }; // Para persistir el estado de los submenús por su 'name'\r\n}\r\n\r\nexport interface RouterState {\r\n currentPath: string;\r\n previousPath: string | null;\r\n redirectPath: string | null; // <--- ¡Nueva propiedad!\r\n}\r\n\r\nexport interface FetchApiOptions extends RequestInit {\r\n /** No poner Content-Type: lo arma el navegador (por ejemplo con un FormData, que lleva su boundary) */\r\n sinContentType?: boolean;\r\n}\r\n\r\nexport interface DataGridOption {\r\n id: string;\r\n label: string;\r\n icon: React.ReactNode;\r\n handler: () => Promise<void>;\r\n visible?: boolean;\r\n}\r","import { useState, useCallback, useEffect } from 'react';\r\nimport { useSnackbar } from '../../contexts/SnackbarContext';\r\nimport { NEW_ROW_INDICATOR, getPrimaryKeyValues } from '../../components/grid/utils/helpers';\r\nimport { DOM_TEMP_ID, FilaDeDatos, FixedField, TableDefinition, UseApiCallResult } from '../../types';\r\nimport { ImportOptions } from '../../components/grid/ImportDialog';\r\nimport { mensajeDeError, requerido } from '../../utils/errores';\r\n\r\n/** Lo que devuelve table_upload al terminar la importación de un archivo */\r\ninterface RespuestaDeImportacion {\r\n uploaded?: {\r\n inserted: number;\r\n updated: number;\r\n skipped: number;\r\n deleted: number;\r\n skippedColumns?: string[];\r\n };\r\n message?: string;\r\n}\r\n\r\ninterface UseGridActionsProps {\r\n tableDefinition: TableDefinition | null,\r\n tableName: string,\r\n fixedFields?: FixedField[];\r\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;\r\n setSelectedRows: React.Dispatch<React.SetStateAction<ReadonlySet<string>>>;\r\n primaryKey: string[],\r\n setLocalCellChanges: React.Dispatch<React.SetStateAction<Map<string, Set<string>>>>,\r\n setExitingRowIds: React.Dispatch<React.SetStateAction<Set<string>>>,\r\n callApi: UseApiCallResult['callApi'];\r\n callApiUpload: UseApiCallResult['callApiUpload'];\r\n resetView?: () => void;\r\n}\r\n\r\nexport const useGridActions = ({\r\n tableDefinition, tableName, fixedFields, setTableData,\r\n setSelectedRows, primaryKey, setLocalCellChanges,\r\n setExitingRowIds, callApi, callApiUpload, resetView\r\n}: UseGridActionsProps) => {\r\n\r\n const [rowToDelete, setRowToDelete] = useState<FilaDeDatos | null>(null);\r\n const [openConfirmDialog, setOpenConfirmDialog] = useState(false);\r\n\r\n // Estado para Vertical Editor\r\n const [openVerticalEditDialog, setOpenVerticalEditDialog] = useState(false);\r\n const [rowToEditVertical, setRowToEditVertical] = useState<FilaDeDatos | null>(null);\r\n\r\n const { showSuccess, showError, showWarning, showInfo } = useSnackbar();\r\n\r\n useEffect(() => {\r\n setLocalCellChanges(new Map());\r\n setOpenConfirmDialog(false);\r\n setRowToDelete(null);\r\n setOpenVerticalEditDialog(false);\r\n setRowToEditVertical(null);\r\n }, [tableName, setLocalCellChanges]);\r\n\r\n\r\n const handleAddRow = useCallback((currentRow?: FilaDeDatos) => {\r\n if (!tableDefinition) {\r\n showWarning('No se puede agregar una fila sin la definición de la tabla.');\r\n return;\r\n }\r\n\r\n // 1. Resetear filtros y sorters para que la fila aparezca donde corresponde\r\n if (resetView) resetView();\r\n\r\n const newRow: FilaDeDatos = {};\r\n tableDefinition.fields.forEach(field => {\r\n newRow[field.name] = null;\r\n });\r\n newRow[NEW_ROW_INDICATOR] = true;\r\n\r\n // 1.5. Generar un ID temporal único usando un Symbol para evitar duplicados en la vista (React Keys)\r\n // Usamos un Symbol para que NO aparezca en JSON.stringify ni Object.keys, pero \r\n // al asignarlo directamente es ENUMERABLE, por lo que el operador spread (...row) lo conserva.\r\n newRow[DOM_TEMP_ID] = `new_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\r\n\r\n // 2. Autocompletar con fixedFields\r\n if (fixedFields) {\r\n fixedFields.forEach(fixedField => {\r\n newRow[fixedField.fieldName] = fixedField.value;\r\n });\r\n }\r\n\r\n setTableData(prevData => {\r\n if (currentRow) {\r\n const currentRowId = getPrimaryKeyValues(currentRow, primaryKey);\r\n const index = prevData.findIndex(row => getPrimaryKeyValues(row, primaryKey) === currentRowId);\r\n\r\n if (index !== -1) {\r\n const newData = [...prevData];\r\n newData.splice(index + 1, 0, newRow);\r\n return newData;\r\n }\r\n }\r\n return [newRow, ...prevData];\r\n });\r\n setSelectedRows(new Set());\r\n\r\n const tempRowId = getPrimaryKeyValues(newRow, primaryKey);\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n const mandatoryEditableColumns = new Set<string>();\r\n\r\n tableDefinition.fields.forEach(field => {\r\n const isMandatory = (field.nullable === false || field.isPk);\r\n const isEditable = field.editable !== false;\r\n\r\n if (isMandatory && isEditable) {\r\n mandatoryEditableColumns.add(field.name);\r\n }\r\n });\r\n newMap.set(tempRowId, mandatoryEditableColumns);\r\n return newMap;\r\n });\r\n\r\n }, [tableDefinition, fixedFields, primaryKey, showWarning, setTableData, setSelectedRows, setLocalCellChanges, resetView]);\r\n\r\n const handleDeleteRow = useCallback((row: FilaDeDatos) => {\r\n setRowToDelete(row);\r\n setOpenConfirmDialog(true);\r\n }, []);\r\n\r\n const handleVerticalEditRow = useCallback((row: FilaDeDatos) => {\r\n setRowToEditVertical(row);\r\n setOpenVerticalEditDialog(true);\r\n }, []);\r\n\r\n\r\n const handleConfirmDelete = useCallback(async (confirm: boolean) => {\r\n setOpenConfirmDialog(false);\r\n if (!confirm || !rowToDelete) {\r\n showWarning('Eliminación cancelada por el usuario.');\r\n setRowToDelete(null);\r\n return;\r\n }\r\n\r\n if (!tableDefinition || !tableName) {\r\n showError('No se puede eliminar la fila sin la definición de la tabla o el nombre de la tabla.');\r\n setRowToDelete(null);\r\n return;\r\n }\r\n\r\n const rowId = getPrimaryKeyValues(rowToDelete, primaryKey);\r\n\r\n setExitingRowIds(prev => new Set(prev).add(rowId));\r\n\r\n setTimeout(async () => {\r\n if (rowToDelete[NEW_ROW_INDICATOR]) {\r\n setTimeout(() => {\r\n setTableData(prevData => prevData.filter(row => getPrimaryKeyValues(row, primaryKey) !== rowId));\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n newMap.delete(rowId);\r\n return newMap;\r\n });\r\n setSelectedRows(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n setExitingRowIds(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n showInfo(`Fila no guardada '${rowId}' eliminada localmente.`);\r\n setRowToDelete(null);\r\n }, 500);\r\n return;\r\n }\r\n\r\n try {\r\n const primaryKeyValues = tableDefinition.primaryKey.map((key) => rowToDelete[key]);\r\n await callApi('table_record_delete', {\r\n table: tableName,\r\n primaryKeyValues: primaryKeyValues\r\n }, { isCritical: false });\r\n\r\n console.log(`Fila con ID ${rowId} eliminada exitosamente del backend.`);\r\n setTimeout(() => {\r\n setTableData(prevData => prevData.filter(row => getPrimaryKeyValues(row, primaryKey) !== rowId));\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n newMap.delete(rowId);\r\n return newMap;\r\n });\r\n setSelectedRows(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n setExitingRowIds(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n showSuccess(`Fila '${rowId}' eliminada exitosamente.`);\r\n setRowToDelete(null);\r\n }, 500);\r\n\r\n } catch (err) {\r\n console.error(`Error al eliminar la fila '${rowId}':`, err);\r\n showError(`Error al eliminar la fila '${rowId}': ${mensajeDeError(err, 'Error desconocido')}`);\r\n setExitingRowIds(prev => {\r\n const newSet = new Set(prev);\r\n newSet.delete(rowId);\r\n return newSet;\r\n });\r\n setRowToDelete(null);\r\n }\r\n }, 10);\r\n }, [\r\n rowToDelete, tableDefinition, tableName, primaryKey,\r\n showInfo, showSuccess, showError, showWarning,\r\n setTableData, setLocalCellChanges, setSelectedRows,\r\n setExitingRowIds, callApi\r\n ]);\r\n\r\n const handleImportFile = useCallback(async (file: File, options: ImportOptions) => {\r\n try {\r\n const result = await callApiUpload<RespuestaDeImportacion>('table_upload', file, {\r\n table: tableName,\r\n prefilledFields: fixedFields,\r\n ...options\r\n }, { isCritical: false });\r\n\r\n if (result && result.uploaded) {\r\n const { inserted, updated, skipped, deleted, skippedColumns } = result.uploaded;\r\n const messages = [];\r\n if (inserted > 0) messages.push(`${inserted} fila(s) insertada(s).`);\r\n if (updated > 0) messages.push(`${updated} fila(s) actualizada(s).`);\r\n if (skipped > 0) messages.push(`${skipped} fila(s) omitida(s).`);\r\n if (deleted > 0) messages.push(`${deleted} fila(s) eliminada(s).`);\r\n if (skippedColumns && skippedColumns.length > 0) messages.push(`Columnas omitidas: ${skippedColumns.join(', ')}`);\r\n\r\n showSuccess(messages.join('\\n') || 'Importación finalizada sin cambios detectados.');\r\n\r\n // Refrescar los datos después de la importación\r\n const newData = requerido(await callApi<FilaDeDatos[]>('table_data', {\r\n table: tableName,\r\n fixedFields: fixedFields\r\n }), 'los datos de la tabla');\r\n setTableData(newData);\r\n } else if (result && result.message) {\r\n showSuccess(result.message);\r\n }\r\n } catch (err) {\r\n console.error('Error durante la importación:', err);\r\n showError(`Importación cancelada: ${mensajeDeError(err, 'Error desconocido')}`);\r\n }\r\n }, [tableName, fixedFields, callApiUpload, callApi, showSuccess, showError, setTableData]);\r\n\r\n return {\r\n handleDeleteRow,\r\n handleConfirmDelete,\r\n handleAddRow,\r\n handleImportFile,\r\n handleVerticalEditRow,\r\n openConfirmDialog,\r\n rowToDelete,\r\n openVerticalEditDialog,\r\n setOpenVerticalEditDialog,\r\n rowToEditVertical,\r\n setRowToEditVertical\r\n };\r\n\r\n};\r\n","// src/hooks/grid/useGridDataView.ts\nimport { useState, useCallback, useMemo, useRef } from 'react';\nimport { TableDefinition, FieldDefinition, FilaDeDatos, FiltrosDeGrilla, EntradaDeTotal } from '../../types';\nimport typeStore from 'type-store';\nimport { compararValores, sameValue } from '../../components/grid/utils/helpers';\nimport { getBestTypedValue } from '../../components/grid/utils/typedControls';\nimport { FiltroInicial } from '../../utils/parametrosDeTabla';\n\nexport type SortColumn = { columnKey: string; direction: 'ASC' | 'DESC' };\n\nexport type SummaryEntry = EntradaDeTotal;\nexport type SummaryData = Record<string, SummaryEntry>;\n\n/** Si cambian más filas que esto se considera una recarga (refrescar, importar) y se vuelve a ordenar y filtrar */\nconst MAXIMO_FILAS_EDICION_PUNTUAL = 3;\n\ninterface UseGridDataViewProps {\n tableData: FilaDeDatos[];\n tableDefinition: TableDefinition | null;\n}\n\nexport const useGridDataView = ({ tableData, tableDefinition }: UseGridDataViewProps) => {\n\n // ── Sorting ──────────────────────────────────────────────────────────────\n const [sortColumns, setSortColumns] = useState<readonly SortColumn[]>([]);\n\n const getInitialSortColumns = useCallback((def: TableDefinition): readonly SortColumn[] => {\n if (def.sortColumns && def.sortColumns.length > 0) {\n return def.sortColumns.map(sc => ({\n columnKey: sc.column,\n direction: sc.order === -1 ? 'DESC' : 'ASC'\n }));\n }\n const pk = def.primaryKey && def.primaryKey.length > 0 ? def.primaryKey : ['id'];\n return pk.map(pkField => ({ columnKey: pkField, direction: 'ASC' }));\n }, []);\n\n const resetSorting = useCallback((def: TableDefinition) => {\n setSortColumns(getInitialSortColumns(def));\n }, [getInitialSortColumns]);\n\n const handleSortColumnsChange = useCallback((nextSortColumns: readonly SortColumn[]) => {\n if (nextSortColumns.length === 0) {\n setSortColumns([]);\n return;\n }\n const lastClicked = nextSortColumns[nextSortColumns.length - 1];\n const { columnKey, direction } = lastClicked;\n setSortColumns(prev => {\n const existingIndex = prev.findIndex(c => c.columnKey === columnKey);\n const newSort = [...prev];\n if (existingIndex !== -1) newSort.splice(existingIndex, 1);\n newSort.unshift({ columnKey, direction });\n return newSort;\n });\n }, []);\n\n // ── Filters ───────────────────────────────────────────────────────────────\n const [filters, setFilters] = useState<FiltrosDeGrilla>({});\n const [isFilterRowVisible, setIsFilterRowVisible] = useState(false);\n\n const toggleFilterVisibility = useCallback(() => {\n setIsFilterRowVisible(prev => {\n if (prev) setFilters({});\n return !prev;\n });\n }, []);\n\n /**\n * Los filtros con los que se abre la grilla (`fc` de la dirección o del menú, `filterColumns` de la definición): se\n * muestra la fila de filtros ya completa, como `createRowFilter` de backend-plus. El valor se escribe como lo escribiría\n * el usuario en el filtro de esa columna (un booleano `true` es «sí», por ejemplo).\n */\n const aplicarFiltrosIniciales = useCallback((condiciones: FiltroInicial[], definicion: TableDefinition) => {\n const iniciales: FiltrosDeGrilla = {};\n condiciones.forEach(({ column, operator, value }) => {\n const fieldDef = definicion.fields.find(f => f.name === column);\n let texto = value === null || value === undefined ? '' : String(value);\n if (fieldDef && value !== null && value !== undefined) {\n try { texto = typeStore.typerFrom(fieldDef).toLocalString(value); } catch (e) { /* queda como texto */ }\n }\n iniciales[column] = { operator, value: texto };\n });\n setFilters(iniciales);\n setIsFilterRowVisible(true);\n }, []);\n\n // ── filteredRows ──────────────────────────────────────────────────────────\n // Lo último que se calculó, para no reordenar ni refiltrar cuando solo se editaron algunas filas\n const vistaAnteriorRef = useRef<{\n tableData: FilaDeDatos[], filas: FilaDeDatos[], filters: FiltrosDeGrilla, isFilterRowVisible: boolean,\n sortColumns: readonly SortColumn[], tableDefinition: TableDefinition | null\n } | null>(null);\n\n const filteredRows = useMemo(() => {\n const anterior = vistaAnteriorRef.current;\n if (anterior\n && anterior.filters === filters\n && anterior.isFilterRowVisible === isFilterRowVisible\n && anterior.sortColumns === sortColumns\n && anterior.tableDefinition === tableDefinition\n && anterior.tableData.length === tableData.length\n ) {\n // Misma vista y misma cantidad de filas: si solo cambiaron unas pocas (una edición),\n // se reemplazan en su lugar. Así la fila que se está editando no salta a otra posición\n // ni desaparece por el filtro; se reacomoda cuando el usuario cambia el orden o los filtros.\n const reemplazos = new Map<FilaDeDatos, FilaDeDatos>();\n for (let i = 0; i < tableData.length && reemplazos.size <= MAXIMO_FILAS_EDICION_PUNTUAL; i++) {\n if (anterior.tableData[i] !== tableData[i]) reemplazos.set(anterior.tableData[i], tableData[i]);\n }\n if (reemplazos.size <= MAXIMO_FILAS_EDICION_PUNTUAL) {\n const filas = reemplazos.size === 0 ? anterior.filas : anterior.filas.map(fila => reemplazos.get(fila) ?? fila);\n vistaAnteriorRef.current = { ...anterior, tableData, filas };\n return filas;\n }\n }\n\n let rows = [...tableData];\n\n // 1. Filtrado\n if (isFilterRowVisible) {\n Object.keys(filters).forEach(key => {\n const filterObj = filters[key];\n if (!filterObj) return;\n\n let operator = '~';\n let filterValue = '';\n\n if (typeof filterObj === 'string') {\n filterValue = filterObj;\n } else {\n operator = filterObj.operator || '~';\n filterValue = filterObj.value || '';\n }\n\n if (filterValue === '' && operator !== '∅' && operator !== '!=∅') return;\n\n const fieldDef = tableDefinition?.fields.find(f => f.name === key);\n const typer = fieldDef ? typeStore.typerFrom(fieldDef) : null;\n\n // Intentamos parsear el valor del filtro al tipo real para comparaciones matemáticas/fechas\n let parsedFilterValue: unknown = filterValue;\n if (typer && filterValue !== '' && !['~', '!~', '∅', '!=∅'].includes(operator)) {\n try {\n parsedFilterValue = getBestTypedValue(filterValue, typer);\n } catch (e) {\n // Si falla el parseo (ej: fecha incompleta), seguimos con el valor original\n }\n }\n\n rows = rows.filter(row => {\n const rawValue = row[key];\n\n // cellValue se usa para búsquedas parciales (~) o como fallback\n let cellValue = '';\n if (typer && rawValue !== null && rawValue !== undefined) {\n try { cellValue = typer.toLocalString(rawValue).toLowerCase(); }\n catch (e) { cellValue = String(rawValue).toLowerCase(); }\n } else {\n cellValue = String(rawValue || '').toLowerCase();\n }\n\n const fValLower = String(filterValue).toLowerCase();\n\n switch (operator) {\n case '=':\n return sameValue(rawValue, parsedFilterValue) || cellValue === fValLower;\n case '!=':\n return !sameValue(rawValue, parsedFilterValue) && cellValue !== fValLower;\n case '~': return cellValue.includes(fValLower);\n case '!~': return !cellValue.includes(fValLower);\n case '∅': return rawValue === null || rawValue === undefined || rawValue === '';\n case '!=∅': return rawValue !== null && rawValue !== undefined && rawValue !== '';\n case '>': {\n const orden = compararValores(rawValue, parsedFilterValue);\n return orden !== null && orden > 0;\n }\n case '>=': {\n const orden = compararValores(rawValue, parsedFilterValue);\n return orden !== null && orden >= 0;\n }\n case '<': {\n const orden = compararValores(rawValue, parsedFilterValue);\n return orden !== null && orden < 0;\n }\n case '<=': {\n const orden = compararValores(rawValue, parsedFilterValue);\n return orden !== null && orden <= 0;\n }\n default: return cellValue.includes(fValLower);\n }\n });\n });\n }\n\n // 2. Sorting\n if (sortColumns.length > 0) {\n rows.sort((a, b) => {\n for (const sort of sortColumns) {\n const { columnKey, direction } = sort;\n const valA = a[columnKey];\n const valB = b[columnKey];\n if (valA === valB) continue;\n if (valA === null || valA === undefined) return direction === 'ASC' ? -1 : 1;\n if (valB === null || valB === undefined) return direction === 'ASC' ? 1 : -1;\n let comparison = 0;\n if (typeof valA === 'number' && typeof valB === 'number') {\n comparison = valA - valB;\n } else {\n comparison = String(valA).localeCompare(String(valB));\n }\n if (comparison !== 0) return direction === 'ASC' ? comparison : -comparison;\n }\n return 0;\n });\n }\n\n vistaAnteriorRef.current = { tableData, filas: rows, filters, isFilterRowVisible, sortColumns, tableDefinition };\n return rows;\n }, [tableData, filters, isFilterRowVisible, tableDefinition, sortColumns]);\n\n // ── Summary Data (totales calculados) ─────────────────────────────────────\n const summaryData: SummaryData = useMemo(() => {\n if (!tableDefinition) return {};\n const result: SummaryData = {};\n\n tableDefinition.fields.forEach((field: FieldDefinition) => {\n const agg = field.aggregate;\n if (!agg) return;\n\n const values = filteredRows\n .map(r => r[field.name])\n .filter(v => v !== null && v !== undefined);\n\n if (values.length === 0 && agg !== 'count' && agg !== 'countTrue') return;\n\n switch (agg) {\n case 'sum':\n result[field.name] = { type: 'sum', value: values.reduce<number>((a, b) => a + Number(b), 0) };\n break;\n case 'avg':\n const sum = values.reduce<number>((a, b) => a + Number(b), 0);\n result[field.name] = { type: 'avg', value: sum / values.length };\n break;\n case 'min':\n result[field.name] = { type: 'min', value: Math.min(...values.map(v => Number(v))) };\n break;\n case 'max':\n result[field.name] = { type: 'max', value: Math.max(...values.map(v => Number(v))) };\n break;\n case 'count':\n result[field.name] = { type: 'count', value: values.length };\n break;\n case 'countTrue':\n result[field.name] = { type: 'countTrue', value: filteredRows.filter(r => r[field.name] === true).length };\n break;\n }\n });\n\n return result;\n }, [tableDefinition, filteredRows]);\n\n // ── Reset (llamar al cambiar de tabla) ────────────────────────────────────\n const resetView = useCallback(() => {\n setFilters({});\n setIsFilterRowVisible(false);\n setSortColumns([]);\n }, []);\n\n return {\n // Sorting\n sortColumns,\n setSortColumns,\n getInitialSortColumns,\n handleSortColumnsChange,\n resetSorting,\n // Filters\n filters,\n setFilters,\n isFilterRowVisible,\n toggleFilterVisibility,\n aplicarFiltrosIniciales,\n // Data\n filteredRows,\n summaryData,\n // Reset\n resetView,\n };\n};\n","// src/components/grid/utils/typedControls.ts\nimport { Typer } from 'type-store';\n\nexport const BestControls = {\n text: {\n translates: {\n true: { '\\n': '\\u21b5' },\n false: { '\\u21b5': '\\n' }\n },\n translate: (right: boolean, textValue: string): string => {\n const map = BestControls.text.translates[right ? 'true' : 'false'] as Record<string, string>;\n let result = textValue;\n for (const char in map) {\n result = result.replace(new RegExp(char, 'g'), map[char]);\n }\n return result;\n },\n getTypedValue: (plainValue: unknown, typer: Typer): unknown => {\n if (plainValue === null || plainValue === undefined) {\n return plainValue;\n }\n return typer.fromUserInput(String(plainValue));\n }\n },\n number: {\n getTypedValue: (plainValue: unknown): number | null => {\n // En React, plainValue suele ser el string del input\n if (plainValue === '' || plainValue === null || plainValue === undefined) {\n return null;\n }\n const num = Number(plainValue);\n return isNaN(num) ? null : num;\n }\n },\n boolean: {\n getTypedValue: (plainValue: unknown): boolean | null => {\n if (typeof plainValue === 'boolean') return plainValue;\n if (plainValue === null || plainValue === undefined) return null;\n\n const value = String(plainValue).trim();\n const falseInitials: Record<string, boolean> = {\n 'n': true, 'N': true, '0': true, '2': true, 'F': true, 'f': true,\n '\\u043d': true, '\\u041d': true // cyrillic N\n };\n\n if (value === '') return null;\n return !falseInitials[value[0]];\n }\n },\n date: {\n getTypedValue: (plainValue: unknown): Date | null => {\n if (plainValue === null || plainValue === undefined || plainValue === '') return null;\n if (plainValue instanceof Date) return plainValue;\n\n const parts = String(plainValue).trim().split(/[-\\/]/).map(s => s.trim());\n if (parts.length !== 3) return null;\n\n let day: number, month: number, year: number;\n\n // Detectar si es YYYY-MM-DD o DD/MM/YYYY\n if (parts[0].length === 4) {\n // YYYY-MM-DD\n year = Number(parts[0]);\n month = Number(parts[1]) - 1;\n day = Number(parts[2]);\n } else {\n // DD/MM/YYYY\n // Requisito: Años si o si con 4 dígitos\n if (parts[2].length !== 4) return null;\n\n day = Number(parts[0]);\n month = Number(parts[1]) - 1;\n year = Number(parts[2]);\n }\n\n const date = new Date(year, month, day);\n // Validar que sea una fecha real (ej: no 31/02/2024)\n if (date.getFullYear() === year && date.getMonth() === month && date.getDate() === day) {\n return Object.assign(date, { isRealDate: true });\n }\n return null;\n }\n }\n};\n\n/**\n * Función principal para obtener el valor tipado según el tipo del typer.\n */\nexport const getBestTypedValue = (valor: unknown, typer: Typer): unknown => {\n\n const value = valor === '' ? typer.emptyValue : valor;\n\n const typeName = typer.typeName;\n\n if (['integer', 'bigint', 'decimal', 'double', 'float', 'number'].includes(typeName)) {\n return BestControls.number.getTypedValue(value);\n }\n if (typeName === 'boolean') {\n return BestControls.boolean.getTypedValue(value);\n }\n if (typeName === 'date' || (typeName && typeName.toLowerCase().includes('timestamp'))) {\n return BestControls.date.getTypedValue(value);\n }\n\n return BestControls.text.getTypedValue(value, typer);\n};\n\n/**\n * Lo que se escribió en la celda como valor del tipo del campo. Lo escrito que no es un valor del tipo (letras en un entero, una fecha que\n * no existe) lanza un Error: `getBestTypedValue` lo convierte en null, y eso borraba el valor de la celda, o «no cambiaba» y no se guardaba\n * ni se avisaba. Con la celda vacía el valor es null, como siempre.\n */\nexport const tiparEntrada = (valor: unknown, typer: Typer): unknown => {\n // solo espacios en un número o una fecha es vacío (Number(' ') da 0)\n const entrada = typeof valor === 'string' && valor.trim() === '' && typer.typeName !== 'text' ? '' : valor;\n const tipado = getBestTypedValue(entrada, typer);\n if (tipado === null && typeof entrada === 'string' && entrada !== '') {\n throw new Error(`«${valor}» no es un valor válido para este campo (${typer.typeName})`);\n }\n return tipado;\n};\n\n/** Si un carácter que se tipea no entra en el tipo del campo (una letra en un entero): backend-plus no lo deja escribir (`rejectedChar`) */\nexport const caracterRechazado = (caracter: string, typer: Typer): boolean => caracter.length === 1 && typer.rejectedChar(caracter);\n\n/** Si el texto pegado tiene algún carácter que el tipo del campo rechaza */\nexport const textoPegadoRechazado = (texto: string, typer: Typer): boolean => Array.from(texto).some(caracter => typer.rejectedChar(caracter));\n","import { TableDefinition } from 'backend-plus';\r\nimport { useCallback, useRef } from 'react';\r\nimport { CellMouseArgs, CellKeyDownArgs, CellMouseEvent, CellKeyboardEvent, DataGridHandle, CellSelectArgs, RowsChangeData } from 'react-data-grid';\r\nimport { ColumnaDeGrilla, esColumnaPropia } from '../../components/grid/GenericDataGrid';\r\nimport { DOM_TEMP_ID, FilaDeDatos, FilaDeResumen, FixedField, TeclaDeEditor } from '../../types';\r\nimport { esFilaDetalle } from '../../components/grid/detalles';\r\nimport { SortColumn } from './useGridDataView';\r\nimport { direccionDeSalida, posicionDelCursor } from './teclasDeCelda';\r\nimport { valorFijoDe } from '../../utils/fixedFields';\r\nimport { textoParaVer, tipoDeExpander } from '../../utils/expanders';\r\n\r\ninterface UseGridEventsProps {\r\n dataGridRef: React.RefObject<DataGridHandle>\r\n tableDefinition: TableDefinition | null,\r\n fixedFields: FixedField[] | undefined\r\n /** Las filas tal como se muestran: filtradas, ordenadas y con las filas de detalle intercaladas */\r\n filteredRows: FilaDeDatos[],\r\n tableData: FilaDeDatos[],\r\n setSelectedCell: React.Dispatch<React.SetStateAction<CellSelectArgs<FilaDeDatos, FilaDeResumen> | undefined>>,\r\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>,\r\n setSortColumns: React.Dispatch<React.SetStateAction<readonly SortColumn[]>>,\r\n /** F9 en una celda que no se edita (JSON o texto largo): la grilla muestra el contenido completo bajo la celda */\r\n verContenido?: (anclaje: HTMLElement, texto: string) => void\r\n}\r\n\r\n/** Cuánto tiempo (ms) espera una tecla encolada a que se monte el editor (si no, se descarta) */\r\nconst VIGENCIA_TECLA_PENDIENTE = 400;\r\n\r\n/** Cuánto tiempo (ms) vale el valor que F4 acaba de copiar para la pulsación siguiente */\r\nconst VIGENCIA_VALOR_PENDIENTE_F4 = 1000;\r\n\r\nexport const useGridEvents = ({dataGridRef, tableDefinition, filteredRows, tableData, fixedFields, setSelectedCell, setTableData, setSortColumns, verContenido}: UseGridEventsProps) => {\r\n\r\n // handleKeyPressInEditor viaja dentro de las columnas: tiene que ser estable para no recrearlas\r\n // en cada cambio de datos, así que lee las filas visibles y la definición a través de refs.\r\n const filteredRowsRef = useRef(filteredRows);\r\n filteredRowsRef.current = filteredRows;\r\n const tableDefinitionRef = useRef(tableDefinition);\r\n tableDefinitionRef.current = tableDefinition;\r\n // F4 copia el valor de la fila de arriba: lo que se copió y todavía no llegó a las filas (fila|columna → valor)\r\n const valoresF4Pendientes = useRef(new Map<string, { valor: unknown, cuando: number }>());\r\n\r\n // F4 apretado manda más teclas por segundo que lo que tarda en abrirse el editor de la celda siguiente:\r\n // las que llegan en el hueco caen en la celda y no en el editor. Cada editor se registra acá; si hay uno,\r\n // la tecla se le pasa directo; si no hay (todavía no se montó), se guarda y la toma el próximo que se registre.\r\n const teclasPendientes = useRef<{ key: string, cuando: number }[]>([]);\r\n const editorActivo = useRef<((key: string) => void) | null>(null);\r\n // la última tecla que se apretó en un editor: → solo deja la celda si la anterior también fue → (como backend-plus)\r\n const teclaAnteriorRef = useRef<string | null>(null);\r\n const registrarEditor = useCallback((procesar: (key: string) => void): (() => void) => {\r\n editorActivo.current = procesar;\r\n const cola = teclasPendientes.current;\r\n while (cola.length > 0 && Date.now() - cola[0].cuando > VIGENCIA_TECLA_PENDIENTE) cola.shift();\r\n const pendiente = cola.shift();\r\n if (pendiente) procesar(pendiente.key);\r\n return () => { if (editorActivo.current === procesar) editorActivo.current = null; };\r\n }, []);\r\n\r\n const handleKeyPressInEditor = useCallback((rowIndex: number, columnKey: string, event: TeclaDeEditor, currentColumns: ColumnaDeGrilla[], handleCommit:(currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => Promise<void>, valorActual: unknown): boolean => {\r\n // true si la tecla la resolvió la grilla (dejó la celda o no había adónde ir): el editor cancela su efecto nativo (el Tab, por ejemplo)\r\n let resuelta = false;\r\n const tableDefinition = tableDefinitionRef.current;\r\n const filteredRows = filteredRowsRef.current;\r\n if (dataGridRef.current && tableDefinition) {\r\n const currentColumnIndex = currentColumns.findIndex(col => col.key === columnKey);\r\n const editableColumns = currentColumns.filter(col => {\r\n const fieldDefinition = tableDefinition.fields.find(f => f.name === col.key);\r\n return col.key !== 'actionsColumn' && !(esColumnaPropia(col) && col.customType === 'detail') && (fieldDefinition?.editable !== false && !fieldDefinition?.clientSide);\r\n });\r\n const {key, target} = event;\r\n // como backend-plus: lo que hace la flecha → depende de la tecla anterior (ver direccionDeSalida)\r\n const teclaAnterior = teclaAnteriorRef.current;\r\n teclaAnteriorRef.current = key;\r\n const direccion = direccionDeSalida(event, posicionDelCursor(target), teclaAnterior);\r\n\r\n if (currentColumnIndex !== -1 && editableColumns.length > 0) {\r\n const editableColumnKeys = editableColumns.map(col => col.key);\r\n let currentEditableColumnIndex = editableColumnKeys.indexOf(columnKey);\r\n \r\n // Se llama en el mismo tick que el commit de la celda: react-data-grid junta ambos cambios y pasa directo\r\n // del editor de una celda al de la siguiente. Con un setTimeout en el medio, las teclas que llegan\r\n // mientras el editor nuevo todavía no existe se pierden (sobre todo con F4 apretado).\r\n const hacerFoco = ({rowIdx, idx}:{rowIdx:number, idx: number}) =>\r\n dataGridRef.current?.selectCell({ rowIdx, idx }, { enableEditor: true, shouldFocusCell: true });\r\n \r\n // Índice de la próxima fila de datos en la dirección indicada, salteando las filas de detalle (-1 si no hay)\r\n const filaDeDatos = (desde: number, paso: 1 | -1) => {\r\n let i = desde + paso;\r\n while (i >= 0 && i < filteredRows.length && esFilaDetalle(filteredRows[i])) i += paso;\r\n return i >= 0 && i < filteredRows.length ? i : -1;\r\n };\r\n\r\n const calcularColumnaSiguiente = ()=>{\r\n let nextEditableColumnIndex = currentEditableColumnIndex + 1;\r\n let nextRowIndex = rowIndex;\r\n if (nextEditableColumnIndex >= editableColumns.length) {\r\n nextEditableColumnIndex = 0;\r\n nextRowIndex = filaDeDatos(rowIndex, 1);\r\n if (nextRowIndex === -1) {\r\n nextRowIndex = filaDeDatos(-1, 1);\r\n }\r\n }\r\n const nextColumnKey = editableColumnKeys[nextEditableColumnIndex];\r\n const nextColumnIndex = currentColumns.findIndex(col => col.key === nextColumnKey);\r\n return {rowIdx: nextRowIndex, idx:nextColumnIndex}\r\n }\r\n \r\n const indiceDeColumna = (indiceEditable: number) => currentColumns.findIndex(col => col.key === editableColumnKeys[indiceEditable]);\r\n // Shift+Tab: la celda editable anterior; al pasar la primera, la última de la fila de arriba (null si no hay)\r\n const calcularColumnaAnteriorConSalto = () => {\r\n if (currentEditableColumnIndex > 0) return {rowIdx: rowIndex, idx: indiceDeColumna(currentEditableColumnIndex - 1)};\r\n const filaAnterior = filaDeDatos(rowIndex, -1);\r\n return filaAnterior === -1 ? null : {rowIdx: filaAnterior, idx: indiceDeColumna(editableColumnKeys.length - 1)};\r\n }\r\n // ← y →: la celda editable de al lado en la misma fila (null en los extremos: no saltan de fila)\r\n const calcularColumnaVecina = (paso: 1 | -1) => {\r\n const vecina = currentEditableColumnIndex + paso;\r\n return vecina < 0 || vecina >= editableColumnKeys.length ? null : {rowIdx: rowIndex, idx: indiceDeColumna(vecina)};\r\n }\r\n // ↑ y ↓: la misma columna en la fila de datos de arriba o de abajo (null si no hay)\r\n const calcularFilaVecina = (paso: 1 | -1) => {\r\n const vecina = filaDeDatos(rowIndex, paso);\r\n return vecina === -1 ? null : {rowIdx: vecina, idx: currentColumnIndex};\r\n }\r\n \r\n const calcularFilaSiguiente = ()=>{\r\n const nextRowIndex = filaDeDatos(rowIndex, 1);\r\n return {rowIdx: nextRowIndex === -1 ? rowIndex : nextRowIndex, idx:currentColumnIndex}\r\n }\r\n\r\n \r\n if (currentEditableColumnIndex !== -1) {\r\n switch(true){\r\n case (direccion !== null): {\r\n // la tecla es de la grilla aunque no haya adónde ir (en el borde): no se deja que haga lo suyo\r\n resuelta = true;\r\n const destino = {\r\n siguiente: calcularColumnaSiguiente,\r\n anterior: calcularColumnaAnteriorConSalto,\r\n izquierda: () => calcularColumnaVecina(-1),\r\n derecha: () => calcularColumnaVecina(1),\r\n arriba: () => calcularFilaVecina(-1),\r\n abajo: () => calcularFilaVecina(1),\r\n }[direccion]();\r\n if (destino) {\r\n // como dejar la celda de cualquier otra forma: se guarda (el cambio de la fila se aplica en el acto\r\n // y el guardado sigue en segundo plano) y el foco pasa a la celda de destino\r\n void handleCommit(valorActual, true, true);\r\n hacerFoco(destino);\r\n }\r\n break;\r\n }\r\n case (key === 'F4'): {\r\n // no hace falta limpiar el orden: useGridDataView mantiene las filas en su lugar al editar\r\n const {rowIdx, idx} = calcularFilaSiguiente();\r\n const indiceAnterior = filaDeDatos(rowIndex, -1);\r\n const previousRow = indiceAnterior === -1 ? undefined : filteredRows[indiceAnterior]\r\n if(previousRow){\r\n // El valor de la fila de arriba puede ser uno que acabamos de copiar y que todavía no se\r\n // redibujó (con la tecla apretada llegan más pulsaciones que renders): se usa el pendiente.\r\n const pendiente = valoresF4Pendientes.current.get(`${indiceAnterior}|${columnKey}`);\r\n const valorACopiar = pendiente && Date.now() - pendiente.cuando < VIGENCIA_VALOR_PENDIENTE_F4\r\n ? pendiente.valor\r\n : previousRow[columnKey];\r\n valoresF4Pendientes.current.set(`${rowIndex}|${columnKey}`, { valor: valorACopiar, cuando: Date.now() });\r\n // El guardado sigue en segundo plano y el foco avanza ya: esperar la respuesta del servidor\r\n // antes de pasar a la fila siguiente hacía perder las pulsaciones que llegaban mientras tanto\r\n // (el feedback o el error de cada celda igual se muestran cuando llega la respuesta).\r\n void handleCommit(valorACopiar, true, true)\r\n // este editor se va: las teclas que lleguen hasta que se registre el de la fila siguiente\r\n // se guardan (si no, se procesarían otra vez sobre esta fila y no avanzarían)\r\n editorActivo.current = null;\r\n hacerFoco({rowIdx ,idx})\r\n }\r\n break;\r\n }\r\n default: break;\r\n }\r\n } \r\n } \r\n }\r\n return resuelta;\r\n }, [dataGridRef]);\r\n\r\n const handleCellKeyDown = useCallback((args: CellKeyDownArgs<FilaDeDatos, FilaDeResumen>, event: CellKeyboardEvent) => {\r\n // F9 o Alt+↓ (los expanders de backend-plus) en una celda seleccionada: si se edita, se abre el editor y este abre el menú, el calendario\r\n // o la ventana de texto; si no se edita, se muestra el contenido completo (JSON o texto largo)\r\n const esExpander = event.key === 'F9' && !event.shiftKey && !event.ctrlKey && !event.altKey || event.key === 'ArrowDown' && event.altKey && !event.shiftKey && !event.ctrlKey;\r\n if (esExpander && !(event.target instanceof HTMLInputElement) && !esFilaDetalle(args.row)) {\r\n const campo = tableDefinitionRef.current?.fields.find(f => f.name === args.column.key);\r\n const editable = typeof args.column.editable === 'function' ? args.column.editable(args.row) : args.column.editable === true;\r\n const tipo = campo ? tipoDeExpander(campo, editable) : null;\r\n if (campo && editable && tipo !== null) {\r\n teclasPendientes.current.push({ key: 'F9', cuando: Date.now() });\r\n dataGridRef.current?.selectCell({ rowIdx: args.rowIdx, idx: args.column.idx }, { enableEditor: true, shouldFocusCell: true });\r\n event.preventGridDefault();\r\n return;\r\n }\r\n if (campo && !editable && (tipo === 'json' || tipo === 'texto') && verContenido && event.target instanceof HTMLElement) {\r\n verContenido(event.target, textoParaVer(args.row[campo.name]));\r\n event.preventGridDefault();\r\n return;\r\n }\r\n }\r\n if (event.key === 'F4' && !(event.target instanceof HTMLInputElement) && !esFilaDetalle(args.row)) {\r\n const fieldDefinition = tableDefinitionRef.current?.fields.find(f => f.name === args.column.key);\r\n if (fieldDefinition && fieldDefinition.editable !== false && !fieldDefinition.clientSide) {\r\n if (editorActivo.current) editorActivo.current(event.key);\r\n else teclasPendientes.current.push({ key: event.key, cuando: Date.now() });\r\n event.preventGridDefault();\r\n return;\r\n }\r\n }\r\n if (['Enter', 'Tab', 'ArrowDown', 'ArrowUp','ArrowRight', 'ArrowLeft'].includes(event.key)) {\r\n event.preventGridDefault();\r\n }\r\n }, [dataGridRef, verContenido]);\r\n\r\n const handleCellMouseDown = useCallback((_args: CellMouseArgs<FilaDeDatos, FilaDeResumen>, event: CellMouseEvent) => {\r\n event.preventGridDefault(); \r\n }, []);\r\n\r\n const handleCellDoubleClick = useCallback((_args: CellMouseArgs<FilaDeDatos, FilaDeResumen>, event: CellMouseEvent) => {\r\n event.preventGridDefault(); \r\n }, []);\r\n\r\n const handleSelectedCellChange = useCallback((args: CellSelectArgs<FilaDeDatos, FilaDeResumen> | undefined) => {\r\n setSelectedCell(args);\r\n }, [setSelectedCell]);\r\n\r\n const handleCellClick = useCallback((args: CellMouseArgs<FilaDeDatos, FilaDeResumen>, _event: CellMouseEvent) => {\r\n // la fila de detalle contiene otra grilla: los clicks son de esa grilla\r\n if (esFilaDetalle(args.row)) return;\r\n const fieldDefinition = tableDefinition?.fields.find(f => f.name === args.column.key);\r\n const isFixedField = valorFijoDe(fixedFields, args.column.key) !== undefined;\r\n const isEditable = fieldDefinition?.editable !== false && !isFixedField;\r\n if(isEditable){\r\n // antes se limpiaba el orden al clickear; ya no hace falta porque al editar\r\n // las filas no se reordenan hasta que el usuario cambie el orden o los filtros\r\n args.selectCell(true);\r\n } else if (fieldDefinition && tipoDeExpander(fieldDefinition, false) !== null) {\r\n // una celda que no se edita también se selecciona (sin editor): F9 muestra su contenido completo, como en backend-plus\r\n args.selectCell(false);\r\n }\r\n }, [tableDefinition, fixedFields]);\r\n\r\n const handleRowsChange = useCallback((updatedRows: FilaDeDatos[], data: RowsChangeData<FilaDeDatos, FilaDeResumen>) => {\r\n // updatedRows son las filas visibles (filtradas y ordenadas), no todo tableData:\r\n // se reemplazan en tableData solo las filas editadas, buscándolas por identidad.\r\n const reemplazos = new Map<FilaDeDatos, FilaDeDatos>();\r\n data.indexes.forEach(index => {\r\n const oldRow = filteredRows[index];\r\n const newRow = updatedRows[index];\r\n if (!oldRow || !newRow || esFilaDetalle(oldRow)) return;\r\n // Preservar la identidad (DOM_TEMP_ID) de las filas editadas\r\n if (oldRow[DOM_TEMP_ID] && !newRow[DOM_TEMP_ID]) {\r\n newRow[DOM_TEMP_ID] = oldRow[DOM_TEMP_ID];\r\n }\r\n reemplazos.set(oldRow, newRow);\r\n });\r\n if (reemplazos.size === 0) return;\r\n setTableData(prevData => prevData.map(row => reemplazos.get(row) ?? row));\r\n }, [setTableData, filteredRows]);\r\n\r\n return {\r\n handleCellMouseDown,\r\n handleCellDoubleClick,\r\n handleCellClick,\r\n handleCellKeyDown,\r\n handleKeyPressInEditor,\r\n registrarEditor,\r\n handleSelectedCellChange,\r\n handleRowsChange\r\n };\r\n};","// src/components/grid/detalles.tsx\n// Grillas de detalle desplegadas dentro de la grilla, como lo hace backend-plus (my-tables.js):\n// al apretar el botón de un detalle se inserta debajo de la fila una \"fila de detalle\" que ocupa\n// todas las columnas y contiene otra grilla (que a su vez puede desplegar sus propios detalles).\nimport React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';\nimport { IconButton, Tooltip } from '@mui/material';\nimport KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';\nimport KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport { DetailTable } from 'backend-plus';\nimport { FilaDeDatos, FixedField, TableDefinition } from '../../types';\nimport { SortColumn } from '../../hooks/grid/useGridDataView';\nimport { FallbackWScreen, wScreens } from '../../pages/WScreens';\nimport { cumpleCondicion } from './condiciones';\n\n/** Marca de las filas de detalle (es un Symbol para que no se confunda con ningún campo) */\nexport const DETALLE = Symbol('detalle');\n\n// un type y no una interface: así es asignable a FilaDeDatos (las interfaces no tienen firma de índice implícita)\nexport type FilaDetalle = {\n [DETALLE]: true;\n /** `${rowId}|${abr}`: identifica el detalle dentro de su grilla */\n clave: string;\n filaPadre: FilaDeDatos;\n detailTable: DetailTable;\n fixedFields: FixedField[];\n};\n\nexport const esFilaDetalle = (row: unknown): row is FilaDetalle =>\n typeof row === 'object' && row !== null && (row as Partial<FilaDetalle>)[DETALLE] === true;\n\nexport const claveDetalle = (rowId: string, detailTable: DetailTable) => `${rowId}|${detailTable.abr}`;\n\n/** Alto de la fila de detalle mientras la grilla anidada todavía no informó cuánto necesita */\nexport const ALTO_INICIAL_DETALLE = 140;\nexport const ALTO_MINIMO_DETALLE = 80;\n\n/** Un campo de DetailTable['fields'] normalizado; `until` lo acepta my-tables.js aunque no esté en el .d.ts */\ninterface CampoDeDetalle {\n target: string;\n source?: string;\n value?: unknown;\n tieneValor: boolean;\n nullMeansAll?: boolean;\n until?: unknown;\n}\n\nconst normalizarCampoDeDetalle = (campo: DetailTable['fields'][number]): CampoDeDetalle => {\n if (typeof campo === 'string') return { target: campo, source: campo, tieneValor: false };\n if ('value' in campo) return { target: campo.target, value: campo.value, tieneValor: true, until: (campo as { until?: unknown }).until };\n return { target: campo.target, source: campo.source, tieneValor: false, nullMeansAll: campo.nullMeansAll, until: (campo as { until?: unknown }).until };\n};\n\n/** Los fixedFields de la grilla de detalle a partir de la fila padre (mismo cálculo que calculateFixedFields de my-tables.js) */\nexport function calcularFixedFieldsDetalle(detailTable: DetailTable, row: FilaDeDatos): FixedField[] {\n return detailTable.fields\n .map(normalizarCampoDeDetalle)\n .filter(campo => !campo.nullMeansAll || (campo.source !== undefined && row[campo.source] != null))\n .map(campo => {\n const fixedField: FixedField = {\n fieldName: campo.target,\n value: campo.tieneValor ? campo.value : row[campo.source!],\n };\n if (campo.until !== undefined) fixedField.until = campo.until;\n return fixedField;\n });\n}\n\n/** Lo que una grilla anidada guarda para volver a mostrarse sin recargar (por ejemplo al scrollear) */\nexport interface GrillaGuardada {\n tableDefinition: TableDefinition;\n tableData: FilaDeDatos[];\n anchosCalculados: Map<string, number>;\n sortColumns: readonly SortColumn[];\n detallesAbiertos: string[];\n altosDetalle: Map<string, number>;\n}\n\n/**\n * Las filas de detalle salen del DOM cuando se scrollea (virtualización) y su grilla se desmonta.\n * Este cache, compartido por toda la jerarquía de grillas, permite volver a mostrarlas como estaban,\n * igual que backend-plus reusa el divDetail al volver a desplegar.\n */\nexport const CacheGrillasContext = createContext<Map<string, GrillaGuardada> | null>(null);\n\ninterface DetallesGrilla {\n abiertos: ReadonlySet<string>;\n alternarDetalle: (row: FilaDeDatos, rowId: string, detailTable: DetailTable, enOtraPestana: boolean) => void;\n renderDetalle: (fila: FilaDetalle) => React.ReactNode;\n}\n\nexport const DetallesGrillaContext = createContext<DetallesGrilla | null>(null);\n\nconst useDetallesGrilla = () => {\n const detalles = useContext(DetallesGrillaContext);\n if (!detalles) throw new Error('falta DetallesGrillaContext.Provider');\n return detalles;\n};\n\ninterface BotonDetalleProps {\n row: FilaDeDatos;\n rowId: string;\n detailTable: DetailTable;\n deshabilitado: boolean;\n}\n\n/** Botón de la columna de un detalle: despliega/repliega; con Ctrl o el botón del medio lo abre en otra pestaña */\nexport function BotonDetalle({ row, rowId, detailTable, deshabilitado }: BotonDetalleProps) {\n const { abiertos, alternarDetalle } = useDetallesGrilla();\n // la `condition` del detalle (función de la aplicación) puede ocultar el botón para algunas filas\n if (!cumpleCondicion(detailTable.condition, row)) return <div className=\"fpr-boton-detalle\" />;\n const abierto = abiertos.has(claveDetalle(rowId, detailTable));\n const titulo = deshabilitado\n ? 'Grabá la fila para ver sus detalles'\n : `${detailTable.label || detailTable.abr}${abierto ? ' (replegar)' : ''} — Ctrl+click: en otra pestaña`;\n return (\n <Tooltip title={titulo}>\n <span className=\"fpr-boton-detalle\">\n <IconButton\n size=\"small\"\n disabled={deshabilitado}\n onClick={(e) => alternarDetalle(row, rowId, detailTable, e.ctrlKey || e.metaKey)}\n onAuxClick={(e) => { if (e.button === 1) alternarDetalle(row, rowId, detailTable, true); }}\n >\n {abierto ? <KeyboardArrowDownIcon fontSize=\"small\" /> : <KeyboardArrowRightIcon fontSize=\"small\" />}\n </IconButton>\n </span>\n </Tooltip>\n );\n}\n\n/**\n * Alto máximo que puede ocupar lo que se dibuja dentro de un detalle. Lo usan las grillas que una wScreen arma\n * adentro de un detalle para acotarse sin que la wScreen tenga que saber de alturas.\n */\nexport const AltoDisponibleContext = createContext<number | undefined>(undefined);\n\n/** Alto que ocupan las filas de detalle además de su contenido (el padding de .fpr-detalle) */\nconst MARGEN_VERTICAL_DETALLE = 8;\n\ninterface DetalleWScreenProps {\n fila: FilaDetalle;\n /** Alto máximo de la fila de detalle: si el contenido lo pasa, scrollea */\n altoMaximo?: number;\n /** El contenido informa el alto que necesita para mostrarse completo */\n onAlto: (alto: number) => void;\n /** El contenido informa el ancho que necesita */\n onAncho: (ancho: number) => void;\n}\n\n/**\n * Detalle que en lugar de otra tabla es una pantalla propia de la aplicación (`wScreen`), por ejemplo las anotaciones\n * de un ticket en gestik. Recibe como `params` los fixedFields del detalle, igual que `mainAction(params, div)` de backend-plus.\n */\nexport function DetalleWScreen({ fila, altoMaximo, onAlto, onAncho }: DetalleWScreenProps) {\n const nombre = fila.detailTable.wScreen ?? '';\n const Pantalla = wScreens[nombre];\n const params = useMemo(\n () => Object.fromEntries(fila.fixedFields.map(fixedField => [fixedField.fieldName, fixedField.value])),\n [fila.fixedFields]\n );\n const contenido = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const elemento = contenido.current;\n if (!elemento) return;\n const medir = () => {\n onAlto(elemento.offsetHeight + MARGEN_VERTICAL_DETALLE);\n onAncho(elemento.offsetWidth);\n };\n medir();\n const observador = new ResizeObserver(medir);\n observador.observe(elemento);\n return () => observador.disconnect();\n }, [onAlto, onAncho]);\n return (\n <AltoDisponibleContext.Provider value={altoMaximo === undefined ? undefined : altoMaximo - MARGEN_VERTICAL_DETALLE}>\n <div className=\"fpr-detalle-wscreen\">\n <div ref={contenido} className=\"fpr-detalle-wscreen-contenido\">\n {Pantalla\n ? <Pantalla screenName={nombre} params={params} />\n : <FallbackWScreen screenName={nombre} />}\n </div>\n </div>\n </AltoDisponibleContext.Provider>\n );\n}\n\n/** Contenido de la fila de detalle (lo arma GenericDataGrid para evitar una importación circular) */\nexport function CeldaDetalle({ fila }: { fila: FilaDetalle }) {\n const { renderDetalle } = useDetallesGrilla();\n return <>{renderDetalle(fila)}</>;\n}\n","import React from 'react';\r\nimport { Box, Typography, Paper } from '@mui/material';\r\nimport ConstructionIcon from '@mui/icons-material/Construction';\r\nimport WarningAmberIcon from '@mui/icons-material/WarningAmber'; // Asegúrate de importar el icono de advertencia\r\n\r\nimport { WScreenProps } from '../types';\r\n\r\n\r\n// Ejemplo de un componente de wScreen personalizado\r\nconst MyCustomDashboard: React.FC<WScreenProps> = ({ screenName }) => {\r\n return (\r\n <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%' }}>\r\n <Paper elevation={3} sx={{ p: 4, textAlign: 'center', maxWidth: 600 }}>\r\n <Typography variant=\"h5\" component=\"h1\" gutterBottom>\r\n ¡Bienvenido a tu WScreen: {screenName}!\r\n </Typography>\r\n <Typography variant=\"body1\" color=\"text.secondary\">\r\n Esta es una pantalla de interfaz de usuario personalizada.\r\n </Typography>\r\n <Box sx={{ mt: 3 }}>\r\n <ConstructionIcon sx={{ fontSize: 80, color: 'primary.main' }} />\r\n </Box>\r\n </Paper>\r\n </Box>\r\n );\r\n};\r\n\r\nexport const FallbackWScreen: React.FC<WScreenProps> = ({ screenName }) => {\r\n return (\r\n <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%' }}>\r\n <Paper elevation={3} sx={{ p: 4, textAlign: 'center', maxWidth: 600, backgroundColor: 'warning.light', border: '1px solid orange' }}>\r\n <Typography variant=\"h5\" component=\"h1\" gutterBottom color=\"warning.dark\">\r\n WScreen No Implementada\r\n </Typography>\r\n <Typography variant=\"body1\" color=\"warning.dark\">\r\n No se encontró un componente implementado para la **WScreen**: **`\"${screenName}\"`**.\r\n Por favor, verifica el `menuType` en la definición del menú o asegúrate de que el componente esté correctamente importado y mapeado en el frontend.\r\n </Typography>\r\n <Box sx={{ mt: 3 }}>\r\n <WarningAmberIcon sx={{ fontSize: 80, color: 'warning.dark' }} />\r\n </Box>\r\n </Paper>\r\n </Box>\r\n );\r\n};\r\n\r\n// Mapeo de nombres de wScreens a sus componentes\r\nexport let wScreens: Record<string, React.FC<WScreenProps>> = {\r\n 'MyCustomDashboard': MyCustomDashboard,\r\n // Agrega más componentes de wScreen aquí:\r\n // 'AnotherWScreen': AnotherWScreenComponent,\r\n};\r\n\r\nexport const extendWScreens = (newWScreens: Record<string, React.FC<WScreenProps>>) => {\r\n wScreens = { ...wScreens, ...newWScreens };\r\n console.log('wScreens extendidos:', wScreens);\r\n};","// src/components/grid/condiciones.ts\n// Condiciones de los detalles (`condition` de un DetailTable): funciones de la aplicación que deciden, fila por fila,\n// si el botón del detalle se muestra. Es el equivalente de `my.conditions` de backend-plus.\nimport { CondicionDeDetalle, CondicionesMap, FilaDeDatos } from '../../types';\n\nexport let condiciones: CondicionesMap = {};\n\nexport const extendConditions = (nuevas: CondicionesMap) => {\n condiciones = { ...condiciones, ...nuevas };\n};\n\nconst avisadas = new Set<string>();\n\n/**\n * ¿Se muestra el detalle para esta fila? Sin `condition` siempre se muestra.\n * Si la condición no está registrada se avisa una vez por consola y se muestra: ocultar el detalle por un error\n * de configuración escondería datos sin dejar rastro.\n */\nexport function cumpleCondicion(nombre: string | undefined, fila: FilaDeDatos): boolean {\n if (!nombre) return true;\n const condicion: CondicionDeDetalle | undefined = condiciones[nombre];\n if (!condicion) {\n if (!avisadas.has(nombre)) {\n avisadas.add(nombre);\n console.warn(`La condición '${nombre}' de un detalle no está registrada (extendConditions / myConditions): se muestra el detalle.`);\n }\n return true;\n }\n return condicion(fila);\n}\n","// Cómo se sale de una celda en edición con el teclado, igual que backend-plus (`captureKeys` de my-things.js).\n// Dejar la celda por cualquier camino la guarda; lo que cambia es adónde va el foco y cuándo una tecla deja la celda\n// o solo mueve el cursor dentro del texto.\n\n/** Hacia dónde va el foco. `siguiente` y `anterior` saltan de fila al pasar el borde; `izquierda` y `derecha` no */\nexport type DireccionDeSalida = 'siguiente' | 'anterior' | 'izquierda' | 'derecha' | 'arriba' | 'abajo';\n\nexport interface TeclaPulsada {\n key: string;\n shiftKey?: boolean;\n ctrlKey?: boolean;\n altKey?: boolean;\n metaKey?: boolean;\n}\n\n/** Dónde está el cursor dentro del texto del editor */\nexport interface PosicionDelCursor {\n vacio: boolean;\n alPrincipio: boolean;\n alFinal: boolean;\n}\n\n/**\n * La dirección a la que la tecla manda el foco, o null si la tecla no deja la celda (sigue su curso: mueve el cursor, escribe...).\n * - Enter y Tab van a la celda siguiente; Shift+Tab a la anterior. Shift+Enter no hace nada especial.\n * - ↑ y ↓ pasan a la misma columna de la fila de arriba o de abajo.\n * - ← pasa a la celda de la izquierda si el texto está vacío o el cursor está al principio.\n * - → pasa a la de la derecha si el texto está vacío, o si la tecla anterior también fue → y el cursor está al final\n * (hace falta apretarla dos veces: la primera solo llega al final).\n * Con Ctrl, Alt o Meta ninguna tecla deja la celda.\n */\nexport function direccionDeSalida(tecla: TeclaPulsada, cursor: PosicionDelCursor, teclaAnterior: string | null): DireccionDeSalida | null {\n if (tecla.ctrlKey || tecla.altKey || tecla.metaKey) return null;\n const conShift = !!tecla.shiftKey;\n switch (tecla.key) {\n case 'Enter': return conShift ? null : 'siguiente';\n case 'Tab': return conShift ? 'anterior' : 'siguiente';\n case 'ArrowUp': return conShift ? null : 'arriba';\n case 'ArrowDown': return conShift ? null : 'abajo';\n case 'ArrowLeft': return !conShift && (cursor.vacio || cursor.alPrincipio) ? 'izquierda' : null;\n case 'ArrowRight': return !conShift && (cursor.vacio || (teclaAnterior === 'ArrowRight' && cursor.alFinal)) ? 'derecha' : null;\n default: return null;\n }\n}\n\n/** La posición del cursor en un campo de texto (o, si el elemento no es uno, la de un texto vacío) */\nexport function posicionDelCursor(elemento: EventTarget | null): PosicionDelCursor {\n if (elemento instanceof HTMLInputElement || elemento instanceof HTMLTextAreaElement) {\n // como backend-plus, se mira donde termina la selección\n const fin = elemento.selectionEnd ?? elemento.value.length;\n return { vacio: elemento.value === '', alPrincipio: fin === 0, alFinal: fin === elemento.value.length };\n }\n return { vacio: true, alPrincipio: true, alFinal: true };\n}\n","// Qué hace un fixedField con su columna, igual que `TableConnector` de backend-plus (my-tables.js): un fixedField con\n// `range` o con `until` no fija el valor (la columna sigue siendo una columna común y se puede filtrar); uno que fija el\n// valor oculta su columna, salvo que declare `show`.\nimport { FixedField } from '../types';\n\n/** ¿El fixedField fija el valor de su columna? (los de `range` y `until` son un rango: no) */\nexport function fijaElValor(fixedField: FixedField): boolean {\n return !fixedField.range && fixedField.until === undefined;\n}\n\n/** El fixedField que fija el valor de la columna, si lo hay */\nexport function valorFijoDe(fixedFields: FixedField[] | undefined, columna: string): FixedField | undefined {\n return fixedFields?.find(f => f.fieldName === columna && fijaElValor(f));\n}\n\n/** ¿La columna tiene un valor fijo y no se muestra? (`show` la deja a la vista) */\nexport function columnaOcultaPorFixedField(fixedFields: FixedField[] | undefined, columna: string): boolean {\n const fijo = valorFijoDe(fixedFields, columna);\n return fijo !== undefined && !fijo.show;\n}\n","import { executeBackendProcedure } from './fetchApi';\nimport { requerido } from './errores';\nimport { FieldDefinition, FilaDeDatos, TableDefinition } from '../types';\n\n/**\n * El «expander» de referencias de backend-plus (F9 o Alt+↓ en una celda con FK o softFK, `myOwn.ExpanderReferences` en my-tables.js):\n * un menú con las filas de la tabla referenciada (su clave y sus campos `isName`) para elegir el valor de la celda.\n */\n\n/** Par origen → destino de una FK (`source` es la columna de esta tabla, `target` la de la referenciada) */\nexport type ParDeCampos = { source: string, target: string };\n\n/**\n * Un campo con `references`. `tableDefAdapt` (backend-plus) agrega `referencesFields` al campo origen de cada FK y softFK; un campo\n * que declara `references` a mano (como las asignaciones de dmencu) no lo tiene. `backend-plus.d.ts` no declara `referencesFields` ni\n * `skipReferenceLookup`, que el cliente de backend-plus usa.\n */\nexport type CampoConReferencia = FieldDefinition & { references: string, referencesFields?: ParDeCampos[], skipReferenceLookup?: boolean };\n\nconst esParDeCampos = (par: unknown): par is ParDeCampos =>\n typeof par === 'object' && par !== null && 'source' in par && 'target' in par && typeof par.source === 'string' && typeof par.target === 'string';\n\n/** El campo tiene menú de elección (como `ExpanderReferences.whenType`: tiene `references` y no pide saltear el lookup) */\nexport const esCampoConReferencia = (campo: FieldDefinition): campo is CampoConReferencia =>\n typeof campo.references === 'string' && campo.references !== '' &&\n (!('referencesFields' in campo) || campo.referencesFields === undefined || Array.isArray(campo.referencesFields) && campo.referencesFields.every(esParDeCampos)) &&\n !('skipReferenceLookup' in campo && campo.skipReferenceLookup);\n\n/** Los campos con los que se nombra una fila de la tabla: `lookupFields` si los hay y, si no, los `isName` (`nameFields`) */\nexport const camposDeNombre = (definicion: TableDefinition): string[] => {\n const comoTextos = (campos: unknown): string[] => Array.isArray(campos) ? campos.filter((c): c is string => typeof c === 'string') : [];\n if (definicion.lookupFields && definicion.lookupFields.length > 0) return definicion.lookupFields;\n return 'nameFields' in definicion ? comoTextos(definicion.nameFields) : [];\n};\n\n/** Nombre del campo que dice si la fila está activa (`activeRecords.fieldName`), si la tabla lo declara */\nconst campoDeActivo = (definicion: TableDefinition): string | null =>\n 'activeRecords' in definicion && typeof definicion.activeRecords === 'object' && definicion.activeRecords !== null &&\n 'fieldName' in definicion.activeRecords && typeof definicion.activeRecords.fieldName === 'string'\n ? definicion.activeRecords.fieldName : null;\n\nexport type OpcionDeReferencia = {\n valor: unknown,\n /** Lo que se muestra de la fila: las columnas de la clave y los campos de nombre */\n etiquetas: string[],\n /** Con qué se ordena y contra qué se busca */\n texto: string,\n};\n\nconst comoTexto = (valor: unknown): string => valor === null || valor === undefined ? '' : String(valor);\n\n/**\n * Las opciones del menú de un campo con referencia, como `ExpanderReferences.dialogInput` de backend-plus:\n * - sin las filas inactivas (`activeRecords`);\n * - en una FK compuesta, solo las filas que coinciden con los valores de la fila en las columnas anteriores a la última (en un filtro no hay fila: se ofrecen todas);\n * - con el valor de la última columna de la referencia; la etiqueta lleva la clave y los campos de nombre;\n * - ordenadas por esa etiqueta y, si el campo admite nulo, con la opción vacía al final.\n */\nexport const opcionesDeReferencia = (campo: CampoConReferencia, definicionReferenciada: TableDefinition, filasReferenciadas: FilaDeDatos[], filaActual: FilaDeDatos | null, admiteNulo: boolean): OpcionDeReferencia[] => {\n // sin referencesFields (un campo que declara references a mano) el valor es la última columna de la clave, como en backend-plus\n const pares = campo.referencesFields ?? [];\n const columnaDelValor = pares.length > 0 ? pares[pares.length - 1].target : definicionReferenciada.primaryKey[definicionReferenciada.primaryKey.length - 1];\n const anteriores = pares.slice(0, -1);\n const activo = campoDeActivo(definicionReferenciada);\n const nombres = camposDeNombre(definicionReferenciada);\n const columnasDeEtiqueta = nombres.length > 0 ? [...definicionReferenciada.primaryKey, ...nombres] : definicionReferenciada.primaryKey;\n const opciones: OpcionDeReferencia[] = filasReferenciadas\n .filter(fila => (activo === null || !!fila[activo]) && (filaActual === null || anteriores.every(par => comoTexto(fila[par.target]) === comoTexto(filaActual[par.source]))))\n .map(fila => {\n const etiquetas = columnasDeEtiqueta.map(columna => comoTexto(fila[columna]));\n return { valor: fila[columnaDelValor], etiquetas, texto: `${etiquetas.join(', ')}; ${definicionReferenciada.primaryKey.join(', ')}` };\n });\n const ordenar = new Intl.Collator('es', { numeric: true, sensitivity: 'base' });\n opciones.sort((a, b) => ordenar.compare(a.texto, b.texto));\n if (admiteNulo) opciones.push({ valor: null, etiquetas: ['', '(vacío)'], texto: '' });\n return opciones;\n};\n\n/** Las opciones que contienen lo buscado (sin distinguir mayúsculas ni tildes; las palabras sueltas se buscan por separado). La opción vacía no se filtra */\nexport const filtrarOpciones = (opciones: OpcionDeReferencia[], busqueda: string): OpcionDeReferencia[] => {\n const simplificar = (texto: string) => texto.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();\n const palabras = simplificar(busqueda).split(/\\s+/).filter(p => p !== '');\n if (palabras.length === 0) return opciones;\n return opciones.filter(opcion => opcion.valor === null || palabras.every(p => simplificar(opcion.etiquetas.join(' ')).includes(p)));\n};\n\nexport type ReferenciaCargada = { definicion: TableDefinition, filas: FilaDeDatos[] };\n\nconst VIGENCIA_DE_REFERENCIA_MS = 2 * 60 * 1000;\nconst referencias = new Map<string, { cargada: Promise<ReferenciaCargada>, cuando: number }>();\n\n/**\n * La definición y las filas de la tabla referenciada. Se guardan un rato: elegir de a varias celdas seguidas no pide la tabla cada vez\n * (backend-plus las guarda hasta recargar la página; acá vencen, así una tabla que se edita en otra pestaña se ve al rato).\n */\nexport const cargarReferencia = (tabla: string): Promise<ReferenciaCargada> => {\n const guardada = referencias.get(tabla);\n if (guardada && Date.now() - guardada.cuando < VIGENCIA_DE_REFERENCIA_MS) return guardada.cargada;\n const cargada = (async (): Promise<ReferenciaCargada> => {\n const [definicion, filas] = await Promise.all([\n executeBackendProcedure<TableDefinition>('table_structure', { table: tabla }),\n executeBackendProcedure<FilaDeDatos[]>('table_data', { table: tabla, fixedFields: [] }),\n ]);\n return { definicion: requerido(definicion, `la estructura de '${tabla}'`), filas: requerido(filas, `los datos de '${tabla}'`) };\n })();\n referencias.set(tabla, { cargada, cuando: Date.now() });\n cargada.catch(() => { if (referencias.get(tabla)?.cargada === cargada) referencias.delete(tabla); });\n return cargada;\n};\n\n/** Para los tests: olvidar lo guardado */\nexport const olvidarReferencias = (): void => referencias.clear();\n","import { FieldDefinition } from '../types';\nimport { esCampoConReferencia, OpcionDeReferencia } from './referencias';\n\n/**\n * Los «expanders» de backend-plus (`TypedControls.Expanders`, que se abren con F9 o Alt+↓ en una celda): según el tipo del campo,\n * un menú, un calendario o una ventana para ver o editar el contenido. Acá se decide cuál corresponde y se arman sus datos.\n */\nexport type TipoDeExpander = 'referencia' | 'booleano' | 'opciones' | 'fecha' | 'texto' | 'json';\n\n/**\n * Cuál es el expander del campo, en el orden en que backend-plus los prueba: JSON de solo lectura, referencias, booleano, `options`, fecha\n * y, para cualquier campo que no sea clientSide, la ventana de texto. En una celda que no se puede editar solo valen el JSON y el texto\n * (`whenReadOnly`): sirven para ver el contenido completo.\n * Un jsonb editable usa la ventana de texto: el expander de JSON de backend-plus intenta editarlo con una variable sin definir.\n */\nexport const tipoDeExpander = (campo: FieldDefinition, editable: boolean): TipoDeExpander | null => {\n if (campo.typeName === 'jsonb' && !editable) return 'json';\n if (editable) {\n if (esCampoConReferencia(campo)) return 'referencia';\n if (campo.typeName === 'boolean') return 'booleano';\n if (campo.options && campo.options.length > 0) return 'opciones';\n if (campo.typeName === 'date') return 'fecha';\n }\n return campo.clientSide ? null : 'texto';\n};\n\nconst SI_NO_VACIO = { si: 'Sí', no: 'No', vacio: '(vacío)' };\n\n/** Sí, No y, si el campo admite nulo, vacío (el menú del expander de booleanos) */\nexport const opcionesDeBooleano = (admiteNulo: boolean): OpcionDeReferencia[] => {\n const opciones: OpcionDeReferencia[] = [\n { valor: true, etiquetas: [SI_NO_VACIO.si], texto: SI_NO_VACIO.si },\n { valor: false, etiquetas: [SI_NO_VACIO.no], texto: SI_NO_VACIO.no },\n ];\n if (admiteNulo) opciones.push({ valor: null, etiquetas: ['', SI_NO_VACIO.vacio], texto: '' });\n return opciones;\n};\n\n/** El menú del expander de `options`: cada opción con su valor y, si la tiene, su etiqueta; sin ordenar (en el orden que declaró el backend) */\nexport const opcionesDeEnumerado = (opciones: NonNullable<FieldDefinition['options']>, admiteNulo: boolean): OpcionDeReferencia[] => {\n const resultado: OpcionDeReferencia[] = opciones.map(opcion => typeof opcion === 'string'\n ? { valor: opcion, etiquetas: [opcion], texto: opcion }\n : { valor: opcion.option, etiquetas: [String(opcion.option), opcion.label], texto: `${opcion.option} ${opcion.label}` });\n if (admiteNulo) resultado.push({ valor: null, etiquetas: ['', SI_NO_VACIO.vacio], texto: '' });\n return resultado;\n};\n\nexport type DiaDelCalendario = { anio: number, mes: number, dia: number, delMesMostrado: boolean };\n\n/** Año, mes (0 a 11) y día */\nexport type FechaSimple = { anio: number, mes: number, dia: number };\n\n/** Lo que vale como fecha en una celda (un `Date` o un texto `AAAA-MM-DD`); null si no es una fecha */\nexport const fechaDeValor = (valor: unknown): FechaSimple | null => {\n if (valor instanceof Date && !isNaN(valor.getTime())) return { anio: valor.getFullYear(), mes: valor.getMonth(), dia: valor.getDate() };\n if (typeof valor === 'string') {\n const partes = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(valor);\n if (partes) return { anio: Number(partes[1]), mes: Number(partes[2]) - 1, dia: Number(partes[3]) };\n }\n return null;\n};\n\n/** La fecha como la entiende el editor de la celda (`AAAA-MM-DD`) */\nexport const fechaATexto = ({ anio, mes, dia }: FechaSimple): string =>\n `${String(anio).padStart(4, '0')}-${String(mes + 1).padStart(2, '0')}-${String(dia).padStart(2, '0')}`;\n\n/** Las semanas (lunes a domingo) que muestra el calendario de un mes: completas, con los días del mes anterior y del siguiente que las rellenan */\nexport const semanasDelMes = (anio: number, mes: number): DiaDelCalendario[][] => {\n const primero = new Date(anio, mes, 1);\n const desfase = (primero.getDay() + 6) % 7; // lunes = 0\n const diasDelMes = new Date(anio, mes + 1, 0).getDate();\n const semanas: DiaDelCalendario[][] = [];\n for (let celda = 0; celda < Math.ceil((desfase + diasDelMes) / 7) * 7; celda++) {\n const fecha = new Date(anio, mes, 1 - desfase + celda);\n const dia: DiaDelCalendario = { anio: fecha.getFullYear(), mes: fecha.getMonth(), dia: fecha.getDate(), delMesMostrado: fecha.getMonth() === mes };\n if (celda % 7 === 0) semanas.push([]);\n semanas[semanas.length - 1].push(dia);\n }\n return semanas;\n};\n\n/** Lo que se muestra de un valor en la ventana de solo lectura: un JSON con sangría, o el texto tal cual */\nexport const textoParaVer = (valor: unknown): string => {\n if (valor === null || valor === undefined) return '';\n if (typeof valor === 'object' && !(valor instanceof Date)) return JSON.stringify(valor, null, 2);\n return String(valor);\n};\n","// src/hooks/grid/useGridLayout.ts\nimport { useMemo } from 'react';\nimport { SummaryData } from './useGridDataView';\nimport { FilaDeDatos, FilaDeFiltros, FilaDeTotales, FiltrosDeGrilla } from '../../types';\nimport { FILTER_ROW_HEIGHT, HEADER_ROW_HEIGHT, MIN_BODY_HEIGHT, ROW_HEIGHT, SUMMARY_ROW_HEIGHT } from '../../components/grid/GenericDataGrid';\n\ninterface UseGridLayoutProps {\n filteredRows: FilaDeDatos[];\n isFilterRowVisible: boolean;\n tableData: FilaDeDatos[];\n summaryData: SummaryData;\n filters: FiltrosDeGrilla;\n setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;\n /** Alto que agregan las filas de detalle desplegadas */\n altoDetalles?: number;\n /** Alto mínimo cuando no hay filas para mostrar (las grillas anidadas usan uno menor) */\n altoMinimoCuerpo?: number;\n /** Espacio extra debajo de la grilla (barra de scroll, margen) */\n holgura?: number;\n /**\n * Cuántas filas de datos se muestran a la vez antes de scrollear por dentro (y virtualizar):\n * la grilla crece según su contenido hasta ese tope. Sin valor, crece sin límite.\n */\n filasVisiblesMax?: number;\n /** Si la grilla muestra la fila de pie (totales); sin ella no se reserva su alto */\n conPie?: boolean;\n}\n\nexport const useGridLayout = ({\n filteredRows,\n isFilterRowVisible,\n tableData,\n summaryData,\n filters,\n setFilters,\n altoDetalles = 0,\n altoMinimoCuerpo = MIN_BODY_HEIGHT,\n // 24 px de margen inferior de la caja que contiene la grilla + lo que ocupa una barra de scroll horizontal (que le quita\n // alto a la grilla y, si no sobra lugar, hace aparecer una barra vertical aunque haya una sola fila)\n holgura = 24 + 24,\n filasVisiblesMax,\n conPie = true,\n}: UseGridLayoutProps) => {\n\n const gridHeight = useMemo(() => {\n const filterHeight = isFilterRowVisible ? FILTER_ROW_HEIGHT : 0;\n const filasParaAlto = filasVisiblesMax === undefined ? filteredRows.length : Math.min(filteredRows.length, filasVisiblesMax);\n let calculatedHeight = (filasParaAlto * ROW_HEIGHT) + altoDetalles + HEADER_ROW_HEIGHT + filterHeight + (conPie ? SUMMARY_ROW_HEIGHT : 0) + holgura;\n if (tableData.length > 0 && filteredRows.length === 0 && isFilterRowVisible || tableData.length === 0) {\n calculatedHeight = Math.max(calculatedHeight, altoMinimoCuerpo) + filterHeight;\n }\n return calculatedHeight;\n }, [filteredRows.length, isFilterRowVisible, tableData.length, altoDetalles, altoMinimoCuerpo, holgura, filasVisiblesMax, conPie]);\n\n const bottomSummaryRow = useMemo((): FilaDeTotales => {\n return { id: 'bottomSummaryRow', totales: summaryData };\n }, [summaryData]);\n\n const summaryRows = useMemo((): FilaDeFiltros[] | undefined => {\n if (!isFilterRowVisible) return undefined;\n return [{ id: 'filterRow', filters, setFilters }];\n }, [isFilterRowVisible, filters, setFilters]);\n\n const filterHeight = isFilterRowVisible ? FILTER_ROW_HEIGHT : 0;\n\n return { gridHeight, bottomSummaryRow, summaryRows, filterHeight };\n};\n","// src/hooks/grid/useGridMenu.ts\nimport React, { useMemo, useCallback, useState } from 'react';\nimport RefreshIcon from '@mui/icons-material/Refresh';\nimport ViewColumnIcon from '@mui/icons-material/ViewColumn';\nimport VisibilityOffIcon from '@mui/icons-material/VisibilityOff';\nimport FileDownloadIcon from '@mui/icons-material/FileDownload';\nimport FileUploadIcon from '@mui/icons-material/FileUpload';\nimport DeleteSweepIcon from '@mui/icons-material/DeleteSweep';\nimport FilterAltIcon from '@mui/icons-material/FilterAlt';\nimport { useSnackbar } from '../../contexts/SnackbarContext';\nimport { DataGridOption, FilaDeDatos, TableDefinition, FixedField, UseApiCallResult } from '../../types';\nimport { mensajeDeError, requerido } from '../../utils/errores';\n\ninterface UseGridMenuProps {\n tableDefinition: TableDefinition | null;\n tableName: string;\n fixedFields?: FixedField[];\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;\n callApi: UseApiCallResult['callApi'];\n resetSorting?: () => void;\n triggerImport?: () => void;\n triggerExport?: () => void;\n}\n\nexport const useGridMenu = ({\n tableDefinition,\n tableName,\n fixedFields,\n setTableData,\n callApi,\n resetSorting,\n triggerImport,\n triggerExport,\n}: UseGridMenuProps) => {\n\n const { showSuccess, showError, showWarning } = useSnackbar();\n\n const menuOptions = useMemo((): DataGridOption[] => {\n if (!tableDefinition) return [];\n\n const options: DataGridOption[] = [];\n\n // Refrescar (siempre visible)\n options.push({\n id: 'refresh',\n label: 'refrescar la grilla desde la base de datos',\n icon: React.createElement(RefreshIcon),\n handler: async () => {\n try {\n const data = requerido(await callApi<FilaDeDatos[]>('table_data', { table: tableName, fixedFields }), 'los datos de la tabla');\n setTableData(data);\n if (resetSorting) resetSorting();\n showSuccess('Grilla refrescada correctamente');\n } catch (err) {\n showError(`Error al refrescar: ${mensajeDeError(err, 'Error desconocido')}`);\n }\n },\n visible: true\n });\n\n // Mostrar columnas relacionadas\n options.push({\n id: 'showColumns',\n label: 'mostrar las columnas relacionadas',\n icon: React.createElement(ViewColumnIcon),\n handler: async () => { console.log('Mostrar columnas relacionadas'); },\n visible: true\n });\n\n // Ocultar/mostrar columnas\n options.push({\n id: 'hideColumns',\n label: 'ocultar o mostrar columnas',\n icon: React.createElement(VisibilityOffIcon),\n handler: async () => { console.log('Ocultar/mostrar columnas'); },\n visible: true\n });\n\n if (tableDefinition.allow?.export) {\n options.push({\n id: 'export',\n label: 'exportar',\n icon: React.createElement(FileDownloadIcon),\n handler: async () => {\n if (triggerExport) triggerExport();\n else showWarning('La funcionalidad de exportación no está disponible en este momento.');\n },\n visible: true\n });\n }\n\n if (tableDefinition.allow?.import) {\n options.push({\n id: 'import',\n label: 'importar (Excel/Tab)',\n icon: React.createElement(FileUploadIcon),\n handler: async () => {\n if (triggerImport) triggerImport();\n else showWarning('La funcionalidad de importación no está disponible en este momento.');\n },\n visible: true\n });\n }\n\n if (tableDefinition.allow?.delete) {\n options.push({\n id: 'deleteAll',\n label: 'borrar todos los registros',\n icon: React.createElement(DeleteSweepIcon),\n handler: async () => {\n showWarning('Esta operación eliminará TODOS los registros de la tabla');\n },\n visible: true\n });\n }\n\n options.push({\n id: 'completeTable',\n label: 'tabla completa y filtrada',\n icon: React.createElement(FilterAltIcon),\n handler: async () => { console.log('Toggle tabla completa/filtrada'); },\n visible: true\n });\n\n return options;\n }, [tableDefinition, tableName, fixedFields, setTableData, callApi, resetSorting, triggerImport, triggerExport, showSuccess, showError, showWarning]);\n\n return { menuOptions };\n};\n","// src/hooks/grid/useGridColumns.ts\nimport React, { useMemo } from 'react';\nimport { RenderCellProps, RenderHeaderCellProps, RenderSummaryCellProps, RenderEditCellProps, ColSpanArgs } from 'react-data-grid';\nimport { cambiarGuionesBajosPorEspacios } from '../../utils/functions';\nimport { FieldDefinition, FilaDeDatos, FilaDeResumen, FixedField, TableDefinition } from '../../types';\nimport { ColumnaDeGrilla, CustomColumn, DefaultColumn, DetailColumn, ActionColumn } from '../../components/grid/GenericDataGrid';\nimport { ActionsColumnHeaderCell, defaultColumnHeaderCellRenderer, detailColumnCellHeaderRenderer } from '../../components/grid/renderers/headerCellRenderers';\nimport { actionsColumnSummaryCellRenderer, defaultColumnSummaryCellRenderer, detailColumnCellSummaryRenderer } from '../../components/grid/renderers/summaryCellRenderers';\nimport { allColumnsCellRenderer } from '../../components/grid/renderers/cellRenderers';\nimport { allColumnsEditCellRenderer } from '../../components/grid/renderers/editCellRenderers';\nimport { isNumericType, typerFrom } from '../../components/grid/utils/helpers';\nimport { esFilaDetalle } from '../../components/grid/detalles';\nimport { columnaOcultaPorFixedField, valorFijoDe } from '../../utils/fixedFields';\n\nconst estimateTextWidth = (text: string): number => text.length * 8 + 16;\n\n/**\n * Calcula el ancho de cada columna a partir del título y de una muestra de filas.\n * Se llama una sola vez por carga de datos (no en cada render).\n */\nexport const calcularAnchosDeColumnas = (tableDefinition: TableDefinition, filas: Record<string, unknown>[]): Map<string, number> => {\n const anchos = new Map<string, number>();\n const muestra = filas.slice(0, 50);\n tableDefinition.fields.forEach((fieldDef: FieldDefinition) => {\n // como backend-plus (my-tables.js): un ancho que declara el backend para el campo (`width`, en píxeles) manda sobre el calculado\n if (typeof fieldDef.width === 'number' && fieldDef.width > 0) {\n anchos.set(fieldDef.name, fieldDef.width);\n return;\n }\n const headerLabel = fieldDef.label || fieldDef.name;\n const type = fieldDef.typeName?.toLowerCase() || '';\n let typeMinWidth = 60;\n if (type.includes('boolean') || type.includes('checkbox')) typeMinWidth = 60;\n else if (type.includes('date') || type.includes('timestamp')) typeMinWidth = 110;\n else if (type.includes('integer') || type.includes('numeric')) typeMinWidth = 80;\n\n const headerWidth = estimateTextWidth(headerLabel) + 20;\n const typer = typerFrom(fieldDef);\n const maxDataWidth = muestra.reduce((maxW, row) => {\n const valor = row[fieldDef.name];\n if (valor === null || valor === undefined) return maxW;\n let texto: string;\n try { texto = typer.toLocalString(valor); }\n catch { texto = String(valor); }\n return Math.max(maxW, estimateTextWidth(texto));\n }, 0);\n anchos.set(fieldDef.name, Math.min(Math.max(headerWidth, maxDataWidth, typeMinWidth), 500));\n });\n return anchos;\n};\n\ninterface UseGridColumnsProps {\n tableDefinition: TableDefinition | null;\n anchosCalculados: Map<string, number>;\n fixedFields?: FixedField[];\n primaryKey: string[];\n setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;\n handleKeyPressInEditor: DefaultColumn['handleKeyPressInEditor'];\n registrarEditor: DefaultColumn['registrarEditor'];\n handleDeleteRow: (row: FilaDeDatos) => void;\n handleAddRow: (row?: FilaDeDatos) => void;\n handleVerticalEditRow: (row: FilaDeDatos) => void;\n toggleFilterVisibility: () => void;\n abrirOpcionesDeGrilla: (anchorEl: HTMLElement) => void;\n}\n\n/**\n * Arma las columnas de la grilla.\n * Las dependencias son solo las que cambian la estructura (definición, campos fijos, anchos calculados\n * y callbacks estables). Los datos, el feedback por celda, el orden y la fila de filtros NO están acá:\n * si estuvieran, cada edición recrearía todas las columnas y react-data-grid redibujaría toda la grilla.\n */\nexport const useGridColumns = ({\n tableDefinition,\n anchosCalculados,\n fixedFields,\n primaryKey,\n setTableData,\n handleKeyPressInEditor,\n registrarEditor,\n handleDeleteRow,\n handleAddRow,\n handleVerticalEditRow,\n toggleFilterVisibility,\n abrirOpcionesDeGrilla,\n}: UseGridColumnsProps) => {\n\n const columns: CustomColumn[] = useMemo(() => {\n if (!tableDefinition) return [];\n const fieldsToShow = tableDefinition.fields.filter((field: FieldDefinition) => {\n // como backend-plus: el campo fijo oculta su columna salvo que declare `show`; los de rango (range, until) no la ocultan\n return !columnaOcultaPorFixedField(fixedFields, field.name) && field.visible !== false;\n });\n\n const defaultColumns: CustomColumn[] = fieldsToShow.map((fieldDef: FieldDefinition) => {\n const isFixedField = valorFijoDe(fixedFields, fieldDef.name) !== undefined;\n const isFieldEditable = fieldDef.editable !== false && !isFixedField;\n const colWidth = anchosCalculados.get(fieldDef.name) ?? estimateTextWidth(fieldDef.label || fieldDef.name) + 20;\n const column: DefaultColumn = {\n key: fieldDef.name,\n customType: 'default',\n tableDefinition,\n fieldDef,\n typer: typerFrom(fieldDef),\n isMandatory: tableDefinition.primaryKey.includes(fieldDef.name) || fieldDef.nullable === false,\n isNumeric: isNumericType(fieldDef.typeName),\n primaryKey,\n fixedFields,\n setTableData,\n name: fieldDef.label || cambiarGuionesBajosPorEspacios(fieldDef.name),\n resizable: true,\n sortable: true,\n editable: isFieldEditable,\n handleKeyPressInEditor,\n registrarEditor,\n width: colWidth,\n minWidth: 40,\n renderHeaderCell: (props: RenderHeaderCellProps<FilaDeDatos, FilaDeResumen>) => defaultColumnHeaderCellRenderer(props, fieldDef),\n renderSummaryCell: (props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => defaultColumnSummaryCellRenderer(props, fixedFields),\n };\n return column;\n });\n\n const availableActions = [\n tableDefinition.allow?.insert,\n tableDefinition.allow?.delete,\n tableDefinition.allow?.['vertical-edit']\n ].filter(Boolean).length;\n const actionColumnWidth = 60 + (availableActions * 10);\n\n const actionsColumn: ActionColumn = {\n key: 'actionsColumn',\n customType: 'action',\n tableDefinition,\n handleDeleteRow,\n handleAddRow,\n handleVerticalEditRow,\n name: 'filterCol',\n width: actionColumnWidth,\n editable: false,\n resizable: false,\n sortable: false,\n renderHeaderCell: () => React.createElement(ActionsColumnHeaderCell, {\n toggleFilterVisibility,\n handleDataGridOptions: (e: React.MouseEvent<HTMLElement>) => abrirOpcionesDeGrilla(e.currentTarget),\n handleAddRow,\n allowInsert: tableDefinition.allow?.insert,\n }),\n renderSummaryCell: (props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => actionsColumnSummaryCellRenderer(props),\n };\n\n const detailColumns: CustomColumn[] = (tableDefinition.detailTables ?? []).map(detailTable => {\n const detailKey = `detail_${detailTable.abr}`;\n const column: DetailColumn = {\n key: detailKey,\n customType: 'detail',\n tableDefinition,\n detailTable,\n primaryKey,\n detailKey,\n name: detailTable.label || `Detalle ${detailTable.abr}`,\n resizable: false,\n sortable: false,\n editable: false,\n width: 30,\n minWidth: 30,\n renderHeaderCell: (props: RenderHeaderCellProps<FilaDeDatos, FilaDeResumen>) => detailColumnCellHeaderRenderer(props, detailTable),\n renderSummaryCell: (props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => detailColumnCellSummaryRenderer(props),\n };\n return column;\n });\n\n const allColumns: CustomColumn[] = [actionsColumn, ...detailColumns, ...defaultColumns];\n const cantidadDeColumnas = allColumns.length;\n // En una fila de detalle la columna de acciones ocupa todo el ancho y muestra la grilla anidada\n const colSpanDetalle = (args: ColSpanArgs<FilaDeDatos, FilaDeResumen>) => args.type === 'ROW' && esFilaDetalle(args.row) ? cantidadDeColumnas : undefined;\n return allColumns.map(col => ({\n ...col,\n colSpan: col.customType === 'action' ? colSpanDetalle : col.colSpan,\n // el editor guarda por su cuenta (Enter, Tab, flechas y, al perder el foco, el clic afuera): con commitOnOutsideClick react-data-grid\n // lo cierra sin guardar y los menús (F9), que son un modal, cuentan como «afuera»\n editorOptions: { closeOnExternalRowChange: false, commitOnOutsideClick: false },\n renderEditCell: (props: RenderEditCellProps<FilaDeDatos, FilaDeResumen>) => allColumnsEditCellRenderer(props, allColumns as ColumnaDeGrilla[]),\n renderCell: (props: RenderCellProps<FilaDeDatos, FilaDeResumen>) => allColumnsCellRenderer(props),\n }));\n\n }, [tableDefinition, anchosCalculados, fixedFields, primaryKey, setTableData, handleKeyPressInEditor, registrarEditor, handleDeleteRow, handleAddRow, handleVerticalEditRow, toggleFilterVisibility, abrirOpcionesDeGrilla]);\n\n return { columns };\n};\n","import * as JSON4all from \"json4all\";\r\nimport { store } from '../store';\r\nimport { TableDefinition } from \"backend-plus\";\r\nimport { FixedField } from \"../types\";\r\n\r\nexport const cambiarGuionesBajosPorEspacios = (texto:string)=>texto.replace(/_/g,' ');\r\nexport const quitarGuionesBajos = (texto:string)=>texto.replace(/_/g,'');\r\n\r\nconst getAppPrefix = () => {\r\n const state = store.getState();\r\n const { config } = state.clientContext;\r\n return /*config.appName+*/'prefijo_'+ config.version+'_';\r\n}\r\n\r\nconst getRawLocalVar = (varName: string) =>localStorage.getItem(getAppPrefix()+varName);\r\n\r\nconst getLocalVar = (varName: string) =>{\r\n let rawData = getRawLocalVar(varName);\r\n if(rawData){\r\n return JSON4all.parse(rawData);\r\n }else{\r\n return null\r\n }\r\n}\r\n\r\nconst setLocalVar = (varName: string, value: unknown) => localStorage.setItem(getAppPrefix()+varName, JSON4all.stringify(value))\r\n\r\n\r\nconst existsLocalVar = (varName: string) => !!getRawLocalVar(varName);\r\n\r\n\r\nconst removeLocalVar = (varName: string) => localStorage.removeItem(getAppPrefix()+varName);\r\n\r\nconst getSessionVar = (varName: string) =>{\r\n if(existsSessionVar(varName)){\r\n return JSON4all.parse(sessionStorage.getItem(getAppPrefix()+varName)!);\r\n }else{\r\n return null\r\n }\r\n}\r\n\r\nconst setSessionVar = (varName: string, value: unknown) => sessionStorage.setItem(getAppPrefix()+varName, JSON4all.stringify(value))\r\n\r\n\r\nconst existsSessionVar = (varName: string) => !!(sessionStorage.getItem(getAppPrefix()+varName))?true:false\r\n\r\n\r\nconst removeSessionVar = (varName: string) => sessionStorage.removeItem(getAppPrefix()+varName);\r\n","import { Box, IconButton, Tooltip, Typography } from \"@mui/material\";\r\nimport SearchIcon from '@mui/icons-material/Search';\r\nimport SearchOffIcon from '@mui/icons-material/SearchOff';\r\nimport MoreVertIcon from '@mui/icons-material/MoreVert';\r\nimport AddIcon from '@mui/icons-material/Add';\r\nimport { RenderHeaderCellProps } from \"react-data-grid\";\r\nimport { DetailTable } from \"backend-plus\";\r\nimport { FieldDefinition, FilaDeDatos, FilaDeResumen } from \"../../../types\";\r\nimport { useVistaGrilla } from \"../estadoGrilla\";\r\n\r\nexport const defaultColumnHeaderCellRenderer = (props: RenderHeaderCellProps<FilaDeDatos, FilaDeResumen>, fieldDef: FieldDefinition) => {\r\n // react-data-grid ya informa el orden de la columna: no hace falta pasar sortColumns\r\n // (y así las columnas no se recrean cada vez que cambia el orden)\r\n const { column, sortDirection, priority } = props;\r\n const isPrimaryKey = fieldDef.isPk;\r\n\r\n // Lógica de flecha de ordenación (priority viene solo cuando hay más de una columna ordenada)\r\n const sorted = sortDirection !== undefined;\r\n const sortIndex = (priority ?? 1) - 1;\r\n\r\n // Colores degradados según la prioridad\r\n const getSortColor = (index: number) => {\r\n if (index === 0) return '#000000'; // Principal: Negro\r\n if (index === 1) return '#555555'; // Secundario: Gris oscuro\r\n if (index === 2) return '#888888'; // Terciario: Gris medio\r\n return '#AAAAAA'; // Otros: Gris claro\r\n };\r\n\r\n const sortColor = sorted ? getSortColor(sortIndex) : 'transparent';\r\n\r\n return (\r\n <Box\r\n sx={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n padding: '4px',\r\n boxSizing: 'border-box',\r\n height: '100%',\r\n width: '100%',\r\n }}\r\n >\r\n <Typography\r\n variant=\"body2\"\r\n sx={{\r\n fontWeight: 'bold',\r\n textDecoration: isPrimaryKey ? 'underline' : 'none',\r\n fontSize: '0.930rem',\r\n textAlign: 'left',\r\n flexGrow: 1,\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap'\r\n }}\r\n >\r\n {column.name}\r\n </Typography>\r\n {sorted && (\r\n <Box sx={{ color: sortColor, display: 'flex', alignItems: 'center', ml: 0.5, fontSize: '0.7rem' }}>\r\n {sortDirection === 'ASC' ? '\\u25B2' : '\\u25BC'}\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\ninterface ActionsColumnHeaderCellProps {\r\n toggleFilterVisibility: () => void;\r\n handleDataGridOptions: (event: React.MouseEvent<HTMLElement>) => void;\r\n handleAddRow: () => void;\r\n allowInsert?: boolean;\r\n}\r\n\r\n/** Es un componente (y no una función que se llama al armar las columnas) para leer la fila de filtros del contexto */\r\nexport const ActionsColumnHeaderCell = ({\r\n toggleFilterVisibility,\r\n handleDataGridOptions,\r\n handleAddRow,\r\n allowInsert\r\n}: ActionsColumnHeaderCellProps) => {\r\n const { isFilterRowVisible } = useVistaGrilla();\r\n return (\r\n <Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 0.2 }}>\r\n {allowInsert && (\r\n <IconButton\r\n color=\"inherit\"\r\n onClick={() => handleAddRow()}\r\n size=\"small\"\r\n title=\"Añadir registro\"\r\n sx={{ p: 0.25 }}\r\n >\r\n <AddIcon sx={{ fontSize: 18, color: 'success.main' }} />\r\n </IconButton>\r\n )}\r\n <IconButton\r\n color=\"inherit\"\r\n onClick={toggleFilterVisibility}\r\n size=\"small\"\r\n title={isFilterRowVisible ? 'Ocultar filtros' : 'Mostrar filtros'}\r\n sx={{ p: 0.25 }}\r\n >\r\n {isFilterRowVisible ? <SearchOffIcon sx={{ fontSize: 18 }} /> : <SearchIcon sx={{ fontSize: 18 }} />}\r\n </IconButton>\r\n <IconButton\r\n onClick={(event) => handleDataGridOptions(event)}\r\n size=\"small\"\r\n title=\"Opciones de esta tabla\"\r\n sx={{ p: 0.25 }}\r\n >\r\n <MoreVertIcon sx={{ fontSize: 20, variant: \"filled\", size: \"small\" }} />\r\n </IconButton>\r\n </Box>\r\n );\r\n};\r\n\r\nexport const detailColumnCellHeaderRenderer = (props: RenderHeaderCellProps<FilaDeDatos, FilaDeResumen>, detailTable: DetailTable) =>\r\n <Tooltip title={detailTable.label}>\r\n <Typography variant=\"caption\" sx={{ fontSize: '0.75rem', fontWeight: 'bold' }}>\r\n {detailTable.abr.toUpperCase()}\r\n </Typography>\r\n </Tooltip>","// src/components/grid/estadoGrilla.ts\nimport { createContext, useContext, useSyncExternalStore } from 'react';\nimport { CellFeedback } from '../../types';\n\nexport type MapaFeedback = Map<string, CellFeedback>;\nexport type MapaCambiosLocales = Map<string, Set<string>>;\n\ntype Actualizacion<T> = T | ((anterior: T) => T);\n\n/**\n * Estado de una grilla que cambia celda por celda (feedback de guardado y campos pendientes).\n * Vive fuera de React para que un cambio en una celda no obligue a recrear las columnas\n * ni a redibujar toda la grilla: cada celda se suscribe solo a lo suyo.\n */\nexport interface EstadoGrilla {\n obtenerFeedback(): MapaFeedback;\n obtenerCambiosLocales(): MapaCambiosLocales;\n setFeedback(actualizacion: Actualizacion<MapaFeedback>): void;\n setCambiosLocales(actualizacion: Actualizacion<MapaCambiosLocales>): void;\n suscribir(alCambiar: () => void): () => void;\n}\n\nexport function crearEstadoGrilla(): EstadoGrilla {\n let feedback: MapaFeedback = new Map();\n let cambiosLocales: MapaCambiosLocales = new Map();\n const suscriptores = new Set<() => void>();\n const avisar = () => suscriptores.forEach(alCambiar => alCambiar());\n return {\n obtenerFeedback: () => feedback,\n obtenerCambiosLocales: () => cambiosLocales,\n setFeedback(actualizacion) {\n const nuevo = typeof actualizacion === 'function' ? actualizacion(feedback) : actualizacion;\n if (nuevo !== feedback) {\n feedback = nuevo;\n avisar();\n }\n },\n setCambiosLocales(actualizacion) {\n const nuevo = typeof actualizacion === 'function' ? actualizacion(cambiosLocales) : actualizacion;\n if (nuevo !== cambiosLocales) {\n cambiosLocales = nuevo;\n avisar();\n }\n },\n suscribir(alCambiar) {\n suscriptores.add(alCambiar);\n return () => { suscriptores.delete(alCambiar); };\n },\n };\n}\n\nexport const EstadoGrillaContext = createContext<EstadoGrilla | null>(null);\n\nexport function useEstadoGrilla(): EstadoGrilla {\n const estado = useContext(EstadoGrillaContext);\n if (!estado) {\n throw new Error('useEstadoGrilla debe usarse dentro de EstadoGrillaContext.Provider');\n }\n return estado;\n}\n\n/** Feedback de una celda; solo redibuja cuando cambia el de esa celda */\nexport function useFeedbackCelda(cellKey: string): CellFeedback | undefined {\n const estado = useEstadoGrilla();\n return useSyncExternalStore(estado.suscribir, () => estado.obtenerFeedback().get(cellKey));\n}\n\n/** Si la celda está marcada como pendiente de completar (fila nueva sin grabar) */\nexport function useCeldaPendiente(rowId: string, columnKey: string): boolean {\n const estado = useEstadoGrilla();\n return useSyncExternalStore(estado.suscribir, () => estado.obtenerCambiosLocales().get(rowId)?.has(columnKey) ?? false);\n}\n\n/** Estado de la vista que necesitan algunos encabezados (sin pasar por las columnas) */\nexport interface VistaGrilla {\n isFilterRowVisible: boolean;\n}\n\nexport const VistaGrillaContext = createContext<VistaGrilla>({ isFilterRowVisible: false });\n\nexport const useVistaGrilla = () => useContext(VistaGrillaContext);\n\nexport type { Actualizacion };\n","import { InputBase, Box } from \"@mui/material\";\r\nimport React from \"react\";\r\nimport { FieldDefinition, FiltroDeColumna, FiltrosDeGrilla } from \"../../types\";\r\nimport { FILTER_ROW_HEIGHT } from \"./GenericDataGrid\";\r\nimport { TypedField } from \"../forms/TypedField\";\r\nimport { filterOperators, getDefaultOperatorForType } from \"./utils/helpers\";\r\n\r\ninterface FilterInputRendererProps {\r\n columnKey: string;\r\n currentFilter: FiltroDeColumna | undefined;\r\n setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;\r\n fieldDef: FieldDefinition | undefined;\r\n}\r\n\r\nfunction FilterInputRenderer({\r\n columnKey,\r\n currentFilter,\r\n setFilters,\r\n fieldDef\r\n}: FilterInputRendererProps) {\r\n\r\n if (fieldDef) {\r\n // Determinamos estado de operador y valor\r\n let operator = getDefaultOperatorForType(fieldDef.typeName);\r\n let val = '';\r\n if (currentFilter && typeof currentFilter === 'object') {\r\n operator = currentFilter.operator || operator;\r\n val = currentFilter.value || '';\r\n } else if (typeof currentFilter === 'string') {\r\n val = currentFilter;\r\n }\r\n\r\n const handleFilterChange = (newOp: string, newVal: unknown) => {\r\n const formattedVal = (newVal === null || newVal === undefined) ? '' : String(newVal);\r\n setFilters(prev => ({\r\n ...prev,\r\n [columnKey]: { operator: newOp, value: formattedVal }\r\n }));\r\n };\r\n\r\n return (\r\n <Box sx={{ mt: 0, display: 'flex', alignItems: 'center', gap: 0.5, pr: 1 }}>\r\n <select\r\n value={operator}\r\n onChange={(e) => handleFilterChange(e.target.value, val)}\r\n style={{\r\n height: FILTER_ROW_HEIGHT - 5,\r\n minWidth: 40,\r\n backgroundColor: 'white',\r\n border: '1px solid #ccc',\r\n borderRadius: '4px',\r\n fontSize: '0.8rem',\r\n padding: '0 2px',\r\n textAlign: 'center',\r\n cursor: 'pointer'\r\n }}\r\n >\r\n {filterOperators.map(op => (\r\n <option key={op.value} value={op.value}>\r\n {op.label}\r\n </option>\r\n ))}\r\n </select>\r\n <TypedField\r\n fieldDef={fieldDef}\r\n isFilterMode={true}\r\n value={val}\r\n onChange={(_name, newValue, _isValid) => handleFilterChange(operator, newValue)}\r\n />\r\n </Box>\r\n );\r\n }\r\n\r\n // Fallback original por si no hay fieldDef\r\n return (\r\n <InputBase\r\n value={currentFilter || ''}\r\n onChange={(e) =>\r\n setFilters(prev => ({\r\n ...prev,\r\n [columnKey]: e.target.value\r\n }))\r\n }\r\n sx={{\r\n width: '100%',\r\n height: 25,\r\n margin: '0',\r\n border: '1px solid #ccc',\r\n borderRadius: '4px',\r\n padding: '2px 4px',\r\n fontSize: '0.8rem',\r\n boxSizing: 'border-box',\r\n backgroundColor: 'background.paper'\r\n }}\r\n onClick={(e) => e.stopPropagation()}\r\n />\r\n );\r\n}\r\n\r\nexport default React.memo(FilterInputRenderer);\r\n","import React, { useState, useEffect, useMemo, useCallback } from 'react';\nimport {\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n FormHelperText,\n InputAdornment,\n useTheme\n} from '@mui/material';\nimport { FieldDefinition } from '../../types';\nimport typeStore from 'type-store';\nimport type { SelectChangeEvent } from '@mui/material/Select';\nimport { isNumericType, isDateTimeType } from '../grid/utils/helpers';\nimport { useRef } from 'react';\nimport { FILTER_ROW_HEIGHT } from '../grid/GenericDataGrid';\nimport { mensajeDeError } from '../../utils/errores';\nimport { useExpander } from '../../hooks/grid/useExpander';\n\ninterface TypedFieldProps {\n fieldDef: FieldDefinition;\n value: unknown;\n onChange: (fieldName: string, newValue: unknown, isValid: boolean) => void;\n onCommit?: (fieldName: string, newValue: unknown, isValid: boolean) => void;\n disabled?: boolean;\n isFilterMode?: boolean; // Para usarse en los headers de filtro (más compacto)\n feedback?: 'success' | 'error'; // Indicador de éxito/error proveniente del evento save\n isMandatory?: boolean; \n}\n\nexport const TypedField: React.FC<TypedFieldProps> = ({ fieldDef, value, onChange, onCommit, disabled, isFilterMode = false, feedback, isMandatory = false }) => {\n const theme = useTheme();\n\n const typer = useMemo(() => typeStore.typerFrom(fieldDef), [fieldDef]);\n // en la fila de filtros el booleano es un texto con sus expanders, como los demás campos (se escribe s / n, o se elige con F9 o la lupa)\n const isBoolean = fieldDef.typeName === 'boolean' && !isFilterMode;\n\n // Para campos de texto/números/fechas mantenemos un valor en string (local config)\n const [localString, setLocalString] = useState<string>(() => {\n if (isBoolean) return '';\n if (isFilterMode) return value !== null && value !== undefined ? String(value) : '';\n try {\n return value !== null ? typer.toLocalString(value) : '';\n } catch (e) {\n return value === null || value === undefined ? '' : String(value);\n }\n });\n const [errorMsg, setErrorMsg] = useState<string | null>(null);\n const lastValueRef = useRef<unknown>(value);\n const lastCommittedStringRef = useRef<string>(localString); // Para evitar commit sin cambios\n\n // Inicializar el valor visual desde el valor tipado (solo si cambia externamente)\n useEffect(() => {\n if (!isBoolean) {\n // Solo actualizamos localString si el value real cambió desde fuera (no por nuestro propio tipeo)\n if (value !== lastValueRef.current) {\n lastValueRef.current = value;\n if (isFilterMode) {\n setLocalString(value !== null && value !== undefined ? String(value) : '');\n } else {\n try {\n setLocalString(value !== null ? typer.toLocalString(value) : '');\n } catch (e) {\n setLocalString(value === null || value === undefined ? '' : String(value));\n }\n }\n setErrorMsg(null);\n }\n }\n }, [value, typer, isBoolean, isFilterMode]);\n\n // Los expanders de backend-plus (F9 o Alt+↓ y la lupa) en la fila de filtros: las filas de la tabla referenciada, Sí / No, las `options` y un\n // calendario. Lo que se elige se escribe como texto del filtro. La ventana de texto no sirve para filtrar.\n const entradaRef = useRef<HTMLInputElement | null>(null);\n const [enfocado, setEnfocado] = useState(false);\n const escribirEnFiltro = useCallback((texto: string) => {\n setLocalString(texto);\n lastValueRef.current = texto;\n onChange(fieldDef.name, texto, true);\n }, [onChange, fieldDef.name]);\n const expander = useExpander({\n campo: fieldDef,\n editable: true,\n fila: null,\n admiteNulo: false,\n tipos: isFilterMode ? ['referencia', 'booleano', 'opciones', 'fecha'] : [],\n anclaje: entradaRef,\n textoActual: localString,\n valorActual: localString,\n alElegirValor: (valor: unknown) => escribirEnFiltro(valor === null || valor === undefined ? '' : typeof valor === 'boolean' ? typer.toLocalString(valor) : String(valor)),\n alConfirmarTexto: (texto: string | null) => escribirEnFiltro(texto ?? ''),\n alFallar: (mensaje: string) => setErrorMsg(mensaje),\n });\n\n const handleTextChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const newValueString = e.target.value;\n setLocalString(newValueString);\n let parsedValue = null;\n let isValid = true;\n let pErrorMsg: string | null = null;\n\n if (isFilterMode) {\n // En modo filtro devolvemos el crudo para permitir \"includes()\" y tipeo parcial\n parsedValue = newValueString;\n } else {\n try {\n parsedValue = typer.fromLocalString(newValueString);\n typer.validateTypedData(parsedValue);\n } catch (err) {\n isValid = false;\n pErrorMsg = mensajeDeError(err);\n }\n }\n\n setErrorMsg(pErrorMsg);\n lastValueRef.current = parsedValue; // Seteamos el ref para que el useEffect no lo pise al re-renderizar\n // Enviamos el parsedValue siempre, pero marcamos si es válido o no\n onChange(fieldDef.name, parsedValue, isValid);\n\n }, [typer, onChange, fieldDef.name, isFilterMode]);\n\n const handleTextCommit = useCallback(() => {\n if (!onCommit) return;\n // No enviar commit si el string no cambió desde el último commit\n if (localString === lastCommittedStringRef.current) return;\n lastCommittedStringRef.current = localString;\n \n let parsedValue = null;\n let isValid = true;\n\n if (isFilterMode) {\n parsedValue = localString;\n } else {\n try {\n parsedValue = typer.fromLocalString(localString);\n typer.validateTypedData(parsedValue);\n } catch (err) {\n isValid = false;\n }\n }\n onCommit(fieldDef.name, parsedValue, isValid);\n }, [onCommit, fieldDef.name, localString, isFilterMode, typer]);\n\n const handleTextKeyDown = useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {\n if (expander.manejarTecla(e)) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n handleTextCommit();\n // Optionally, we could unfocus the target here: e.currentTarget.blur()\n }\n }, [handleTextCommit, expander]);\n\n const handleBooleanChange = useCallback((e: SelectChangeEvent<string>) => {\n const val = e.target.value;\n let finalValue: boolean | null = null;\n if (val === 'true') finalValue = true;\n if (val === 'false') finalValue = false;\n\n let isValid = true;\n let pErrorMsg: string | null = null;\n\n if (finalValue === null && fieldDef.nullable === false) {\n isValid = false;\n pErrorMsg = 'Este campo no puede ser nulo.';\n }\n\n onChange(fieldDef.name, finalValue, isValid);\n if (onCommit) {\n onCommit(fieldDef.name, finalValue, isValid);\n }\n }, [onChange, onCommit, fieldDef]);\n\n const isDisabled = isFilterMode ? false : disabled || fieldDef.editable === false;\n\n if (isBoolean) {\n let boolVal = '';\n if (value === true) boolVal = 'true';\n else if (value === false) boolVal = 'false';\n\n const isEmpty = boolVal === '';\n let defaultBgColor = undefined;\n if (isMandatory && isEmpty && !isFilterMode) {\n defaultBgColor = '#e3f2fd';\n }\n // Feedback styling matching GenericDataGrid\n const cellBackgroundColor = feedback === 'error' ? theme.palette.error.light : feedback === 'success' ? theme.palette.success.light : defaultBgColor;\n const transitionStyle = 'background-color 0.3s ease-in-out';\n\n return (\n <FormControl fullWidth error={!!errorMsg && !isFilterMode} disabled={isDisabled} size=\"small\" margin=\"dense\">\n {!isFilterMode && <InputLabel shrink>{fieldDef.name}</InputLabel>}\n <Select\n value={boolVal}\n label={!isFilterMode ? fieldDef.name : undefined}\n onChange={handleBooleanChange}\n startAdornment={\n isMandatory && isEmpty && !isFilterMode\n ? <InputAdornment position=\"start\" sx={{ color: 'text.secondary', ml: 0.5 }}>*</InputAdornment>\n : undefined\n }\n displayEmpty\n sx={isFilterMode\n ? { height: 25, fontSize: '0.8rem', backgroundColor: 'background.paper' }\n : { \n backgroundColor: cellBackgroundColor, \n transition: transitionStyle,\n }}\n >\n\n {fieldDef.nullable !== false && (\n <MenuItem value=\"\"><em>Nulo / Sin Especificar</em></MenuItem>\n )}\n <MenuItem value=\"true\">Sí (Verdadero)</MenuItem>\n <MenuItem value=\"false\">No (Falso)</MenuItem>\n </Select>\n {errorMsg && !isFilterMode && <FormHelperText>{errorMsg}</FormHelperText>}\n </FormControl>\n );\n\n }\n\n // Determinar el input type nativo en base a las 3 categorias del helper\n let inputType = 'text';\n if (isNumericType(fieldDef.typeName)) {\n inputType = 'number';\n }\n // Las fechas/tiempos y LOS FILTROS se mantienen como \"text\" por ahora para facilitar filtro parcial\n // sin pickers nativos y sin bloqueo de entrada de caracteres no numéricos parciales.\n\n\n\n const isEmptyText = localString === null || localString === undefined || String(localString).trim() === '';\n let defaultTextBgColor = undefined;\n if (isMandatory && isEmptyText && !isFilterMode) {\n defaultTextBgColor = '#e3f2fd';\n }\n\n // Feedback styling matching GenericDataGrid \n const cellBackgroundColor = feedback === 'error' ? theme.palette.error.light : feedback === 'success' ? theme.palette.success.light : defaultTextBgColor;\n const transitionStyle = 'background-color 0.3s ease-in-out';\n\n return (\n <>\n <TextField\n fullWidth\n inputRef={entradaRef}\n type={inputType}\n label={isFilterMode ? undefined : fieldDef.name}\n variant=\"outlined\"\n size=\"small\"\n margin={isFilterMode ? \"none\" : \"dense\"}\n value={localString}\n onChange={handleTextChange}\n disabled={isDisabled}\n error={!!errorMsg && !isFilterMode}\n helperText={!isFilterMode ? (errorMsg || (isMandatory ? 'Obligatorio' : '')) : undefined}\n InputLabelProps={!isFilterMode ? { shrink: true } : undefined}\n InputProps={isFilterMode ? {\n // la lupa solo al enfocar el filtro, como en backend-plus\n endAdornment: enfocado || expander.abierto.current ? expander.lupa : undefined\n } : isMandatory && isEmptyText ? {\n startAdornment: (\n <InputAdornment position=\"start\" sx={{ color: 'text.secondary', mr: 0.5 }}>*</InputAdornment>\n )\n } : {}}\n inputProps={{\n style: {\n textAlign: isNumericType(fieldDef.typeName) ? 'right' : 'left',\n }\n }}\n sx={isFilterMode\n ? {\n '& .MuiOutlinedInput-root': {\n backgroundColor: 'background.paper',\n height: FILTER_ROW_HEIGHT - 5,\n minWidth: 50,\n fontSize: '0.8rem',\n padding: '0px'\n }\n }\n : { \n '& .MuiOutlinedInput-root': { \n backgroundColor: cellBackgroundColor, \n transition: transitionStyle \n },\n '& .MuiOutlinedInput-input': {\n padding: '6px 12px'\n }\n }}\n onFocus={() => setEnfocado(true)}\n onBlur={() => { setEnfocado(false); handleTextCommit(); }}\n onKeyDown={handleTextKeyDown}\n />\n {expander.ventana}\n </>\n );\n\n\n\n};\n","import React, { useCallback, useState } from 'react';\nimport IconButton from '@mui/material/IconButton';\nimport ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';\nimport CalendarMonthIcon from '@mui/icons-material/CalendarMonth';\nimport SearchIcon from '@mui/icons-material/Search';\nimport { FieldDefinition, FilaDeDatos } from '../../types';\nimport { MenuDeOpciones } from '../../components/grid/MenuDeOpciones';\nimport { SelectorDeFecha } from '../../components/grid/SelectorDeFecha';\nimport { VentanaDeTexto } from '../../components/grid/VentanaDeTexto';\nimport { cargarReferencia, esCampoConReferencia, opcionesDeReferencia, OpcionDeReferencia } from '../../utils/referencias';\nimport { fechaDeValor, opcionesDeBooleano, opcionesDeEnumerado, tipoDeExpander, TipoDeExpander } from '../../utils/expanders';\nimport { mensajeDeError } from '../../utils/errores';\n\ntype Expansion = { tipo: 'opciones', opciones: OpcionDeReferencia[], busqueda: string } | { tipo: 'fecha' } | { tipo: 'texto' };\n\ninterface UsoDeExpanderParams {\n campo: FieldDefinition;\n /** Si el valor se puede escribir (si no, solo valen los que muestran el contenido) */\n editable: boolean;\n /** La fila de la celda: en una FK compuesta el menú ofrece solo las filas que coinciden con ella. En un filtro, null */\n fila: FilaDeDatos | null;\n /** Si el menú ofrece también la opción vacía */\n admiteNulo: boolean;\n /** Limita cuáles expanders se usan (la fila de filtros no usa el booleano, que ya es una lista, ni la ventana de texto) */\n tipos?: TipoDeExpander[];\n /** El elemento bajo el que se abre y al que vuelve el foco al cerrar */\n anclaje: React.RefObject<HTMLElement | null>;\n /** El texto que tiene el campo ahora (la ventana de texto parte de él) */\n textoActual: string;\n /** El valor de la celda (el calendario parte de su fecha) */\n valorActual: unknown;\n /** Se eligió una opción de un menú: el valor tipado (el booleano, el de `options` o el de la referencia), o null */\n alElegirValor: (valor: unknown) => void;\n /** Se eligió una fecha (AAAA-MM-DD, o null) o se aceptó el texto de la ventana */\n alConfirmarTexto: (texto: string | null) => void;\n alFallar: (mensaje: string) => void;\n}\n\n/**\n * Los expanders de backend-plus (F9 o Alt+↓, y la lupa junto al campo): un menú, un calendario o una ventana de texto según el tipo del campo.\n * Lo usan el editor de la celda y la fila de filtros; cada uno dice qué hacer con lo que se elige.\n */\nexport const useExpander = ({ campo, editable, fila, admiteNulo, tipos, anclaje, textoActual, valorActual, alElegirValor, alConfirmarTexto, alFallar }: UsoDeExpanderParams) => {\n const [expansion, setExpansion] = useState<Expansion | null>(null);\n // mientras está abierto el campo pierde el foco y no por eso se guarda\n const abierto = React.useRef(false);\n\n const tipoDelCampo = tipoDeExpander(campo, editable);\n const tipo = tipoDelCampo === null || tipoDelCampo === 'json' || (tipos && !tipos.includes(tipoDelCampo)) ? null : tipoDelCampo;\n\n const abrir = useCallback(async (busqueda: string) => {\n if (tipo === null || abierto.current) return;\n abierto.current = true;\n try {\n if (tipo === 'referencia' && esCampoConReferencia(campo)) {\n const { definicion, filas } = await cargarReferencia(campo.references);\n setExpansion({ tipo: 'opciones', opciones: opcionesDeReferencia(campo, definicion, filas, fila, admiteNulo), busqueda });\n } else if (tipo === 'booleano') {\n setExpansion({ tipo: 'opciones', opciones: opcionesDeBooleano(admiteNulo), busqueda });\n } else if (tipo === 'opciones') {\n setExpansion({ tipo: 'opciones', opciones: opcionesDeEnumerado(campo.options ?? [], admiteNulo), busqueda });\n } else {\n setExpansion({ tipo: tipo === 'fecha' ? 'fecha' : 'texto' });\n }\n } catch (err) {\n abierto.current = false;\n alFallar(mensajeDeError(err));\n }\n }, [tipo, campo, fila, admiteNulo, alFallar]);\n\n const cerrar = useCallback(() => {\n abierto.current = false;\n setExpansion(null);\n // el menú es un modal que retiene el foco hasta que se desmonta\n setTimeout(() => anclaje.current?.focus(), 0);\n }, [anclaje]);\n\n const elegirValor = useCallback((valor: unknown) => {\n cerrar();\n alElegirValor(valor);\n }, [cerrar, alElegirValor]);\n\n const confirmarTexto = useCallback((texto: string | null) => {\n cerrar();\n alConfirmarTexto(texto);\n }, [cerrar, alConfirmarTexto]);\n\n /** F9 o Alt+↓: abre el expander; devuelve si la tecla era esa */\n const manejarTecla = useCallback((event: React.KeyboardEvent): boolean => {\n const esExpander = event.key === 'F9' && !event.shiftKey && !event.ctrlKey && !event.altKey ||\n event.key === 'ArrowDown' && event.altKey && !event.shiftKey && !event.ctrlKey;\n if (tipo === null || !esExpander) return false;\n event.preventDefault();\n void abrir('');\n return true;\n }, [tipo, abrir]);\n\n const titulo = tipo === 'fecha' ? 'elegir fecha (F9)' : tipo === 'texto' ? 'ver el contenido completo (F9)' : 'elegir un valor (F9)';\n /** La lupa de backend-plus: un icono según el expander que lo abre con un clic. El clic no le saca el foco al campo (perderlo lo guardaría) */\n const lupa = tipo === null ? null : (\n <IconButton\n size=\"small\"\n tabIndex={-1}\n aria-label={titulo.replace(' (F9)', '')}\n title={titulo}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => { e.stopPropagation(); void abrir(''); }}\n sx={{ p: 0, ml: 0.25, color: 'primary.main', '&:hover': { backgroundColor: 'action.hover' } }}\n >\n {tipo === 'fecha' ? <CalendarMonthIcon sx={{ fontSize: 16 }} /> : tipo === 'texto' ? <SearchIcon sx={{ fontSize: 16 }} /> : <ArrowDropDownIcon sx={{ fontSize: 20 }} />}\n </IconButton>\n );\n\n const base = anclaje.current;\n const ventana = !expansion || !base ? null : expansion.tipo === 'opciones' ? (\n <MenuDeOpciones anclaje={base} opciones={expansion.opciones} busquedaInicial={expansion.busqueda} onElegir={elegirValor} onCerrar={cerrar} />\n ) : expansion.tipo === 'fecha' ? (\n <SelectorDeFecha anclaje={base} fechaActual={fechaDeValor(valorActual)} admiteNulo={admiteNulo} onElegir={confirmarTexto} onCerrar={cerrar} />\n ) : (\n <VentanaDeTexto anclaje={base} texto={textoActual} editable onAceptar={confirmarTexto} onCerrar={cerrar} />\n );\n\n return { tipo, abierto, abrir, manejarTecla, lupa, ventana };\n};\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { InputBase, List, ListItemButton, Popover, Typography } from '@mui/material';\nimport { filtrarOpciones, OpcionDeReferencia } from '../../utils/referencias';\n\ninterface MenuDeOpcionesProps {\n /** El elemento bajo el que se abre (el editor de la celda) */\n anclaje: HTMLElement;\n opciones: OpcionDeReferencia[];\n /** Lo que se escribió para abrir el menú (una letra en un campo numérico): ya filtra */\n busquedaInicial: string;\n onElegir: (valor: unknown) => void;\n onCerrar: () => void;\n}\n\n/**\n * Menú para elegir una opción (una fila de la tabla referenciada, Sí/No, un valor de `options`), como el `miniMenuPromise` de los\n * expanders de backend-plus: se filtra escribiendo, ↑ y ↓ recorren, Enter elige y Escape cierra.\n */\nexport function MenuDeOpciones({ anclaje, opciones, busquedaInicial, onElegir, onCerrar }: MenuDeOpcionesProps) {\n const [busqueda, setBusqueda] = useState(busquedaInicial);\n const [indice, setIndice] = useState(0);\n const visibles = useMemo(() => filtrarOpciones(opciones, busqueda), [opciones, busqueda]);\n const elegida = useRef<HTMLDivElement | null>(null);\n useEffect(() => { elegida.current?.scrollIntoView({ block: 'nearest' }); }, [indice, visibles]);\n\n const alApretar = (evento: React.KeyboardEvent) => {\n if (evento.key === 'ArrowDown') {\n setIndice(i => Math.min(i + 1, visibles.length - 1));\n } else if (evento.key === 'ArrowUp') {\n setIndice(i => Math.max(i - 1, 0));\n } else if (evento.key === 'Enter') {\n if (visibles[indice]) onElegir(visibles[indice].valor);\n } else if (evento.key === 'F9' || evento.key === 'Escape') {\n onCerrar();\n } else {\n return;\n }\n evento.preventDefault();\n evento.stopPropagation();\n };\n\n return (\n <Popover\n open\n anchorEl={anclaje}\n onClose={onCerrar}\n disableRestoreFocus\n anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}\n slotProps={{ paper: { sx: { minWidth: 300, maxWidth: 520 } } }}\n >\n <InputBase\n autoFocus\n fullWidth\n value={busqueda}\n onChange={(e) => { setBusqueda(e.target.value); setIndice(0); }}\n onKeyDown={alApretar}\n placeholder=\"buscar\"\n sx={{ px: 1, py: 0.5, fontSize: '0.8rem', borderBottom: '1px solid #ccc' }}\n inputProps={{ 'aria-label': 'buscar' }}\n />\n <List dense disablePadding sx={{ maxHeight: 320, overflowY: 'auto' }} role=\"listbox\">\n {visibles.length === 0 && <Typography variant=\"body2\" sx={{ p: 1, color: 'text.secondary' }}>sin coincidencias</Typography>}\n {visibles.map((opcion, i) => (\n <ListItemButton\n key={i}\n ref={i === indice ? elegida : null}\n selected={i === indice}\n role=\"option\"\n onClick={() => onElegir(opcion.valor)}\n sx={{ gap: 2, py: 0.25, fontSize: '0.8rem' }}\n >\n {opcion.etiquetas.map((etiqueta, j) => (\n <span key={j} style={{ fontWeight: j === 0 ? 600 : 400, minWidth: j === 0 ? 40 : undefined }}>{etiqueta}</span>\n ))}\n </ListItemButton>\n ))}\n </List>\n </Popover>\n );\n}\n","import React, { useState } from 'react';\nimport { Button, IconButton, Popover, Typography } from '@mui/material';\nimport { fechaATexto, FechaSimple, semanasDelMes } from '../../utils/expanders';\n\nconst MESES = ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];\nconst DIAS = ['lu', 'ma', 'mi', 'ju', 'vi', 'sá', 'do'];\n\ninterface SelectorDeFechaProps {\n anclaje: HTMLElement;\n /** La fecha que tiene la celda (la que se muestra elegida y desde donde se parte); si no hay, hoy */\n fechaActual: FechaSimple | null;\n admiteNulo: boolean;\n /** La fecha elegida como AAAA-MM-DD, o null para dejarla vacía */\n onElegir: (fecha: string | null) => void;\n onCerrar: () => void;\n}\n\nconst mismaFecha = (a: FechaSimple, b: FechaSimple | null) => b !== null && a.anio === b.anio && a.mes === b.mes && a.dia === b.dia;\n\nconst MOVIMIENTOS: Record<string, [number, number]> = {\n ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [-7, 0], ArrowDown: [7, 0], PageUp: [0, -1], PageDown: [0, 1],\n};\n\n/** Calendario para elegir una fecha, como el `pickDate` (Pikaday) de typed-controls: clic o flechas y Enter; Re Pág y Av Pág cambian de mes */\nexport function SelectorDeFecha({ anclaje, fechaActual, admiteNulo, onElegir, onCerrar }: SelectorDeFechaProps) {\n const hoy = new Date();\n const fechaDeHoy: FechaSimple = { anio: hoy.getFullYear(), mes: hoy.getMonth(), dia: hoy.getDate() };\n const [cursor, setCursor] = useState<FechaSimple>(fechaActual ?? fechaDeHoy);\n\n const mover = (dias: number, meses: number) => {\n const nueva = new Date(cursor.anio, cursor.mes + meses, cursor.dia + dias);\n setCursor({ anio: nueva.getFullYear(), mes: nueva.getMonth(), dia: nueva.getDate() });\n };\n\n const alApretar = (evento: React.KeyboardEvent) => {\n if (evento.key in MOVIMIENTOS) {\n mover(...MOVIMIENTOS[evento.key]);\n } else if (evento.key === 'Enter') {\n onElegir(fechaATexto(cursor));\n } else if (evento.key === 'F9' || evento.key === 'Escape') {\n onCerrar();\n } else {\n return;\n }\n evento.preventDefault();\n evento.stopPropagation();\n };\n\n return (\n <Popover open anchorEl={anclaje} onClose={onCerrar} disableRestoreFocus anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}>\n <div tabIndex={0} ref={(el) => el?.focus()} onKeyDown={alApretar} style={{ padding: 8, outline: 'none', width: 252 }} aria-label=\"calendario\">\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>\n <IconButton size=\"small\" aria-label=\"mes anterior\" onClick={() => mover(0, -1)}>‹</IconButton>\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>{MESES[cursor.mes]} {cursor.anio}</Typography>\n <IconButton size=\"small\" aria-label=\"mes siguiente\" onClick={() => mover(0, 1)}>›</IconButton>\n </div>\n <table style={{ borderCollapse: 'collapse', width: '100%', textAlign: 'center', fontSize: '0.8rem' }}>\n <thead><tr>{DIAS.map(d => <th key={d} style={{ fontWeight: 400, color: '#777' }}>{d}</th>)}</tr></thead>\n <tbody>\n {semanasDelMes(cursor.anio, cursor.mes).map((semana, i) => (\n <tr key={i}>\n {semana.map(dia => {\n const elegido = mismaFecha(dia, fechaActual);\n const enCursor = mismaFecha(dia, cursor);\n return (\n <td key={`${dia.mes}-${dia.dia}`} style={{ padding: 1 }}>\n <button\n type=\"button\"\n tabIndex={-1}\n onClick={() => onElegir(fechaATexto(dia))}\n style={{\n width: 32, height: 26, border: enCursor ? '1px solid #1976d2' : '1px solid transparent', borderRadius: 4, cursor: 'pointer',\n background: elegido ? '#1976d2' : 'transparent', color: elegido ? '#fff' : dia.delMesMostrado ? '#000' : '#aaa',\n }}\n >{dia.dia}</button>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 4 }}>\n <Button size=\"small\" onClick={() => onElegir(fechaATexto(fechaDeHoy))}>hoy</Button>\n {admiteNulo && <Button size=\"small\" onClick={() => onElegir(null)}>vacío</Button>}\n </div>\n </div>\n </Popover>\n );\n}\n","import React, { useState } from 'react';\nimport { Button, InputBase, Popover } from '@mui/material';\n\ninterface VentanaDeTextoProps {\n anclaje: HTMLElement;\n texto: string;\n /** Si se puede editar: con Aceptar (o Ctrl+Enter) devuelve el texto; si no, solo muestra el contenido completo (JSON o texto largo) */\n editable: boolean;\n onAceptar: (texto: string) => void;\n onCerrar: () => void;\n}\n\n/** La ventana de texto de backend-plus (`promptPromise` / `alertPromise` del expander de texto): ver o editar el contenido completo de una celda */\nexport function VentanaDeTexto({ anclaje, texto, editable, onAceptar, onCerrar }: VentanaDeTextoProps) {\n const [valor, setValor] = useState(texto);\n const alApretar = (evento: React.KeyboardEvent) => {\n if (evento.key === 'Escape' || evento.key === 'F9') {\n onCerrar();\n } else if (evento.key === 'Enter' && evento.ctrlKey && editable) {\n onAceptar(valor);\n } else {\n return;\n }\n evento.preventDefault();\n evento.stopPropagation();\n };\n return (\n <Popover open anchorEl={anclaje} onClose={onCerrar} disableRestoreFocus anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}>\n <div style={{ padding: 8, minWidth: 320, maxWidth: 640 }}>\n <InputBase\n autoFocus\n multiline\n fullWidth\n readOnly={!editable}\n value={valor}\n onChange={(e) => setValor(e.target.value)}\n onKeyDown={alApretar}\n minRows={2}\n maxRows={16}\n sx={{ fontFamily: 'monospace', fontSize: '0.8rem', border: '1px solid #ccc', borderRadius: '4px', p: 0.5 }}\n inputProps={{ 'aria-label': 'contenido de la celda' }}\n />\n {editable && (\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 4 }}>\n <Button size=\"small\" onClick={onCerrar}>cancelar</Button>\n <Button size=\"small\" variant=\"contained\" onClick={() => onAceptar(valor)}>aceptar</Button>\n </div>\n )}\n </div>\n </Popover>\n );\n}\n","import { RenderSummaryCellProps } from \"react-data-grid\";\r\nimport { FilaDeDatos, FilaDeResumen, FixedField } from \"../../../types\";\r\nimport FilterInputRenderer from \"../FilterInputRender\";\r\nimport { esColumnaPropia } from \"../GenericDataGrid\";\r\nimport { Box } from \"@mui/material\";\r\nimport { valorFijoDe } from \"../../../utils/fixedFields\";\r\n\r\n// La fila de filtros solo existe en topSummaryRows cuando está visible, por eso no hace falta recibir isFilterRowVisible\r\nexport const defaultColumnSummaryCellRenderer = (props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>, fixedFields: FixedField[] | undefined) => {\r\n const { column, row } = props;\r\n\r\n // Solo mostramos filtros en la fila dedicada a filtros\r\n if (row.id === 'filterRow') {\r\n const { filters, setFilters } = row; // Extraemos del row para estabilidad de columnas\r\n // Si el campo es fijo, no debe aparecer en los filtros\r\n // El filtro solo se muestra si el campo no tiene un valor fijo (los fixedFields de rango, con 'until' o 'range', sí se filtran)\r\n if (valorFijoDe(fixedFields, column.key)) return null;\r\n\r\n const fieldDef = esColumnaPropia(column) && column.customType === 'default' ? column.fieldDef : undefined;\r\n return (\r\n <Box sx={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center' }}>\r\n <FilterInputRenderer\r\n columnKey={column.key}\r\n currentFilter={filters[column.key]}\r\n setFilters={setFilters}\r\n fieldDef={fieldDef}\r\n />\r\n </Box>\r\n );\r\n }\r\n\r\n // Para la fila de totales (el pie de la grilla):\r\n const summaryData = row.totales[column.key];\r\n if (!summaryData) return null;\r\n\r\n const { type, value } = summaryData;\r\n let label = '';\r\n let formattedValue: string | number = value;\r\n\r\n // Formateo básico para números\r\n if (typeof value === 'number') {\r\n if (type === 'avg') {\r\n formattedValue = value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\r\n } else {\r\n formattedValue = value.toLocaleString();\r\n }\r\n }\r\n\r\n switch (type) {\r\n case 'sum': label = 'Tot: '; break;\r\n case 'avg': label = 'Prom: '; break;\r\n case 'min': label = 'Min: '; break;\r\n case 'max': label = 'Max: '; break;\r\n case 'count': label = 'Cant: '; break;\r\n case 'countTrue': label = 'Cant Sí: '; break;\r\n }\r\n\r\n return (\r\n <Box sx={{\r\n px: 0,\r\n fontWeight: 'bold',\r\n fontSize: '0.875rem',\r\n color: 'text.primary',\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n textAlign: 'right'\r\n }}>\r\n {label}{formattedValue}\r\n </Box>\r\n );\r\n}\r\n\r\nexport const actionsColumnSummaryCellRenderer = (_props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => null\r\n\r\nexport const detailColumnCellSummaryRenderer = (_props: RenderSummaryCellProps<FilaDeResumen, FilaDeDatos>) => null","import React from 'react';\r\nimport { RenderCellProps } from 'react-data-grid';\r\nimport { FilaDeDatos, FilaDeResumen } from '../../../types';\r\nimport { Box, Button, Tooltip, useTheme } from '@mui/material';\r\nimport ViewHeadlineIcon from '@mui/icons-material/ViewHeadline';\r\nimport DeleteIcon from '@mui/icons-material/Delete';\r\nimport AddIcon from '@mui/icons-material/Add';\r\nimport { DefaultColumn, esColumnaPropia, NEW_ROW_INDICATOR } from '../GenericDataGrid';\r\nimport { clientSides } from '../clientSides';\r\nimport FallbackClientSideRenderer from '../FallbackClientSideRenderer';\r\nimport { OverridableComponent } from '@mui/material/OverridableComponent';\r\nimport { SvgIconTypeMap } from '@mui/material/SvgIcon';\r\nimport { getPrimaryKeyValues } from '../utils/helpers';\r\nimport { useCeldaPendiente, useFeedbackCelda } from '../estadoGrilla';\r\nimport { BotonDetalle, CeldaDetalle, esFilaDetalle } from '../detalles';\r\nimport { valorFijoDe } from '../../../utils/fixedFields';\r\n\r\n\r\ntype ActionButtonDefinition = {\r\n action: 'insert' | 'delete' | 'vertical-edit'\r\n handler: (row: FilaDeDatos) => void\r\n title: string\r\n icon: OverridableComponent<SvgIconTypeMap<{}, \"svg\">> & { muiName: string }\r\n color: 'success' | 'error' | 'primary'\r\n}\r\n\r\ninterface CeldaDatoProps {\r\n row: FilaDeDatos;\r\n columnKey: string;\r\n defaultColumn: DefaultColumn;\r\n}\r\n\r\n/**\r\n * Celda de datos. Es un componente memorizado: solo se redibuja cuando cambia su fila\r\n * o el feedback/pendiente de esta celda (que lee del EstadoGrilla, no de la columna).\r\n * Se usan elementos y clases simples en lugar de Box/Typography/Tooltip de MUI\r\n * porque hay una por cada celda visible.\r\n */\r\nconst CeldaDato = React.memo(function CeldaDato({ row, columnKey, defaultColumn }: CeldaDatoProps) {\r\n const theme = useTheme();\r\n const { primaryKey, fixedFields, typer, isMandatory, isNumeric } = defaultColumn;\r\n const rowId = getPrimaryKeyValues(row, primaryKey);\r\n const currentCellFeedback = useFeedbackCelda(`${rowId}-${columnKey}`);\r\n const pendiente = useCeldaPendiente(rowId, columnKey);\r\n\r\n const value = row[columnKey];\r\n const hasValue = value !== null && value !== undefined && String(value).trim() !== '';\r\n\r\n let cellBackgroundColor = 'transparent';\r\n if (currentCellFeedback) {\r\n cellBackgroundColor = currentCellFeedback.type === 'error' ? theme.palette.error.light : theme.palette.success.light;\r\n } else if (valorFijoDe(fixedFields, columnKey)) {\r\n cellBackgroundColor = theme.palette.action.selected;\r\n } else if ((isMandatory && !hasValue) || pendiente) {\r\n cellBackgroundColor = '#e3f2fd'; // Celeste premium suave\r\n }\r\n\r\n // TODO: Capturar valor fecha\r\n // Usamos TypeStore para el formateo de visualización\r\n let displayValue: string = value !== null && value !== undefined ? typer.toLocalString(value) : '';\r\n if (!hasValue && isMandatory) {\r\n displayValue = '* ' + displayValue; // Agrega el asterisco si está vacío y es requerido\r\n }\r\n\r\n const signal = displayValue === '--' ? 'typed-controls-signal-no-data' : displayValue === '//' ? 'typed-controls-signal-unknown-data' : '';\r\n const celda = (\r\n <div\r\n className={`fpr-celda ${isNumeric ? 'fpr-celda-numerica' : ''} ${signal} ${row[NEW_ROW_INDICATOR] ? 'new-row-cell-animation' : ''}`}\r\n style={{ backgroundColor: cellBackgroundColor }}\r\n >\r\n {displayValue}\r\n </div>\r\n );\r\n // El Tooltip de MUI solo se arma cuando hay un mensaje que mostrar\r\n return currentCellFeedback?.message\r\n ? <Tooltip title={currentCellFeedback.message}>{celda}</Tooltip>\r\n : celda;\r\n});\r\n\r\nexport const allColumnsCellRenderer = (\r\n props: RenderCellProps<FilaDeDatos, FilaDeResumen>\r\n) => {\r\n const { row, column } = props;\r\n if (!esColumnaPropia(column)) return undefined;\r\n\r\n // Fila de detalle: la columna de acciones ocupa todo el ancho (colSpan) y muestra la grilla anidada\r\n if (esFilaDetalle(row)) {\r\n return column.customType === 'action' ? <CeldaDetalle fila={row} /> : null;\r\n }\r\n\r\n // Lógica para renderizar una celda normal\r\n switch (column.customType) {\r\n case 'default': {\r\n const defaultColumn = column;\r\n const { fieldDef, tableDefinition, primaryKey } = defaultColumn;\r\n\r\n if (fieldDef?.clientSide) {\r\n const ClientSideComponent = clientSides[fieldDef.clientSide];\r\n if (ClientSideComponent) {\r\n return <ClientSideComponent {...props} fieldDefinition={fieldDef} tableDefinition={tableDefinition} primaryKey={primaryKey} />;\r\n }\r\n return <FallbackClientSideRenderer {...props} fieldDefinition={fieldDef} tableDefinition={tableDefinition} primaryKey={primaryKey} />;\r\n }\r\n\r\n return <CeldaDato row={row} columnKey={props.column.key} defaultColumn={defaultColumn} />;\r\n }\r\n\r\n case 'detail': {\r\n const { detailTable, primaryKey } = column;\r\n return (\r\n <BotonDetalle\r\n row={row}\r\n rowId={getPrimaryKeyValues(row, primaryKey)}\r\n detailTable={detailTable}\r\n deshabilitado={!!row[NEW_ROW_INDICATOR]}\r\n />\r\n );\r\n }\r\n\r\n case 'action': {\r\n const { tableDefinition, handleDeleteRow, handleAddRow, handleVerticalEditRow } = column;\r\n\r\n\r\n const gridActionButtons: ActionButtonDefinition[] = [\r\n {\r\n action: 'insert',\r\n handler: handleAddRow,\r\n icon: AddIcon,\r\n title: 'Agregar registro',\r\n color: 'success',\r\n },\r\n {\r\n action: 'delete',\r\n handler: handleDeleteRow,\r\n icon: DeleteIcon,\r\n title: 'Eliminar registro',\r\n color: 'error',\r\n },\r\n {\r\n action: 'vertical-edit',\r\n handler: handleVerticalEditRow,\r\n\r\n icon: ViewHeadlineIcon,\r\n title: 'Editar registro en forma de ficha',\r\n color: 'primary',\r\n }\r\n ];\r\n\r\n return (\r\n <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%', gap: 0.5 }}>\r\n {gridActionButtons.map(actionDef => (\r\n tableDefinition.allow![actionDef.action] ? <Button\r\n key={actionDef.action}\r\n variant=\"outlined\"\r\n color={actionDef.color}\r\n size=\"small\"\r\n onClick={() => actionDef.handler(row)}\r\n title={actionDef.title}\r\n sx={{ width: 22, minWidth: 22, height: 22, '& .MuiButton-startIcon': { m: 0 } }}\r\n >\r\n <actionDef.icon sx={{ fontSize: 18 }} />\r\n </Button> : null\r\n ))}\r\n </Box>\r\n );\r\n }\r\n\r\n default:\r\n return undefined;\r\n }\r\n};","import React from 'react';\r\nimport { Box, Typography } from '@mui/material';\r\nimport { ClientSideProps } from '../../types';\r\n\r\nconst ExampleClientSideComponent: React.FC<ClientSideProps> = ({ row, column, fieldDefinition }) => {\r\n // Accede al valor de la celda:\r\n const value = row[column.key];\r\n\r\n // Accede a la definición del campo si necesitas propiedades específicas\r\n // const customProperty = fieldDefinition?.customProp;\r\n\r\n return (\r\n <Box sx={{\r\n padding: 0.5,\r\n width: '100%',\r\n height: '100%',\r\n display: 'flex',\r\n alignItems: 'center',\r\n backgroundColor: value === 'Special' ? 'lightblue' : 'transparent'\r\n }}>\r\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\r\n {value ? `CS: ${value}` : 'N/A'}\r\n </Typography>\r\n {/* Puedes añadir lógica o UI más compleja aquí */}\r\n </Box>\r\n );\r\n};\r\n\r\nexport default ExampleClientSideComponent;","import React from 'react';\r\nimport { Box, Typography, Tooltip } from '@mui/material';\r\nimport WarningAmberIcon from '@mui/icons-material/WarningAmber';\r\nimport { ClientSideProps } from '../../types'; // Asegúrate de importar la interfaz\r\n\r\nconst FallbackClientSideRenderer: React.FC<ClientSideProps> = ({ column, row, fieldDefinition }) => {\r\n const clientSideName = fieldDefinition?.clientSide || 'Desconocido';\r\n\r\n return (\r\n <Tooltip title={`Componente clientSide '${clientSideName}' no implementado. Mostrando valor por defecto.`}>\r\n <Box\r\n sx={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between', // Para espaciar el valor y el icono\r\n gap: 1, // Espacio entre elementos\r\n width: '100%',\r\n height: '100%',\r\n paddingLeft: '8px',\r\n backgroundColor: 'rgba(255, 193, 7, 0.1)', // Amarillo claro para advertencia\r\n border: '1px dashed rgba(255, 193, 7, 0.5)',\r\n boxSizing: 'border-box',\r\n overflow: 'hidden', // Evita que el contenido se desborde\r\n }}\r\n >\r\n <Typography variant=\"body2\" noWrap sx={{ flexGrow: 1 }}>\r\n {clientSideName + ' not found'}\r\n </Typography>\r\n <WarningAmberIcon\r\n sx={{\r\n fontSize: 18,\r\n color: 'warning.main',\r\n marginRight: '4px' // Pequeño margen a la derecha del icono\r\n }}\r\n />\r\n </Box>\r\n </Tooltip>\r\n );\r\n};\r\n\r\nexport default FallbackClientSideRenderer;","import React from 'react';\nimport { ClientSideProps, FieldDefinition, TableDefinition } from '../../types';\n\nimport ExampleClientSideComponent from './ExampleClientSideComponent';\nimport FallbackClientSideRenderer from './FallbackClientSideRenderer';\n\nexport let clientSides: Record<string, React.FC<ClientSideProps>> = {\n 'ExampleClientSideComponent': ExampleClientSideComponent as React.FC<ClientSideProps>,\n 'FallbackClientSideRenderer': FallbackClientSideRenderer as React.FC<ClientSideProps>,\n};\n\nexport const extendClientSides = (newRenderers: Record<string, React.FC<ClientSideProps>>) => {\n clientSides = { ...clientSides, ...newRenderers };\n console.log('clientSides extendidos:', clientSides);\n};","import React, { useCallback, useEffect, useMemo, useState } from \"react\";\r\nimport { Column } from \"react-data-grid\";\r\nimport { useSnackbar } from \"../../contexts/SnackbarContext\";\r\nimport { useApiCall } from \"../../hooks/useApiCall\";\r\nimport { useTableRecordSave } from \"../../hooks/useTableRecordSave\";\r\nimport { mensajeDeError, requerido } from \"../../utils/errores\";\r\nimport InputBase from \"@mui/material/InputBase\";\r\n// Importamos CellFeedbackMap junto con InputRendererProps\r\nimport { InputRendererProps, CellFeedback, FilaDeDatos } from \"../../types\";\r\n// getCellKey deben provenir de tu archivo GenericDataGrid\r\nimport { NEW_ROW_INDICATOR } from \"./GenericDataGrid\";\r\nimport { Tooltip, useTheme } from \"@mui/material\";\r\nimport { useEstadoGrilla, useFeedbackCelda } from \"./estadoGrilla\";\r\nimport { getPrimaryKeyValues, getCellKey, isNumericType, sameValue } from \"./utils/helpers\";\r\nimport { caracterRechazado, textoPegadoRechazado, tiparEntrada } from \"./utils/typedControls\";\r\nimport { useExpander } from \"../../hooks/grid/useExpander\";\r\nimport typeStore from 'type-store';\r\n\r\n\r\nfunction findChangedValues(oldRowData: FilaDeDatos, newRowData: FilaDeDatos, isNewRow: boolean) {\r\n let changes: string[] = [];\r\n for (const key in newRowData) {\r\n // Si la propiedad existe en ambos Y el valor es diferente, O es una nueva fila\r\n if (Object.prototype.hasOwnProperty.call(oldRowData, key) && oldRowData[key] !== newRowData[key] || isNewRow) {\r\n changes.push(key);\r\n }\r\n }\r\n return changes;\r\n}\r\n\r\nfunction InputRenderer<R extends FilaDeDatos, S>({\r\n column,\r\n row,\r\n rowIdx,\r\n tableDefinition,\r\n onRowChange,\r\n onClose,\r\n onKeyPress,\r\n registrarEditor,\r\n setTableData,\r\n primaryKey\r\n}: InputRendererProps<R, S>) {\r\n const estadoGrilla = useEstadoGrilla();\r\n const setCellFeedbackMap = estadoGrilla.setFeedback;\r\n const setLocalCellChanges = estadoGrilla.setCambiosLocales;\r\n // el editor solo se abre para columnas de campos de la tabla\r\n const fieldDefinition = requerido(tableDefinition.fields.find(f => f.name === column.key), `el campo '${column.key}'`);\r\n const typer = useMemo(() => typeStore.typerFrom(fieldDefinition), [fieldDefinition]);\r\n const [editingValue, setEditingValue] = useState<unknown>(() => row[column.key] !== null ? isNumericType(fieldDefinition?.typeName) ? row[column.key] : typer.toLocalString(row[column.key]) : null);\r\n const isCommitInProgress = React.useRef(false);\r\n const entradaRef = React.useRef<HTMLInputElement | null>(null);\r\n\r\n const theme = useTheme();\r\n const { showSuccess, showError } = useSnackbar();\r\n const { saveRecord } = useTableRecordSave();\r\n\r\n\r\n // Generamos la clave única para la celda manualmente usando el formato esperado\r\n const rowId = getPrimaryKeyValues(row, primaryKey);\r\n const cellKey = `${rowId}-${column.key}`;\r\n\r\n const currentCellFeedback = useFeedbackCelda(cellKey);\r\n // ------------------------\r\n\r\n const isMandatory = tableDefinition.primaryKey.includes(column.key) || (fieldDefinition?.nullable === false);\r\n const isEmpty = editingValue === null || editingValue === undefined || String(editingValue).trim() === '';\r\n\r\n let cellBackgroundColor;\r\n let cellBorderColor = '#ccc';\r\n\r\n if (currentCellFeedback) {\r\n cellBackgroundColor = currentCellFeedback.type === 'error' ? theme.palette.error.light : theme.palette.success.light;\r\n cellBorderColor = currentCellFeedback.type === 'error' ? theme.palette.error.main : theme.palette.success.main;\r\n }\r\n\r\n const initialRowId = useMemo(() => getPrimaryKeyValues(row, primaryKey), [row, primaryKey]);\r\n\r\n const handleCommit = useCallback(async (currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => {\r\n if (isCommitInProgress.current) return;\r\n isCommitInProgress.current = true;\r\n\r\n let processedNewValue: unknown;\r\n try {\r\n // Usamos la lógica de BestControls portada de TypedControls.js (tiparEntrada avisa si lo escrito no es un valor del tipo)\r\n processedNewValue = tiparEntrada(currentValue, typer);\r\n // Validamos contra el esquema del typer\r\n typer.validateTypedData(processedNewValue);\r\n } catch (err) {\r\n showError(mensajeDeError(err));\r\n // y la celda queda marcada con el error (antes un valor que no era del tipo no se guardaba y no se veía nada)\r\n const claveDeLaCelda = `${getPrimaryKeyValues(row, tableDefinition.primaryKey)}-${column.key}`;\r\n setCellFeedbackMap(prev => new Map(prev).set(claveDeLaCelda, { rowId: getPrimaryKeyValues(row, tableDefinition.primaryKey), columnKey: column.key, type: 'error', message: mensajeDeError(err) } satisfies CellFeedback));\r\n isCommitInProgress.current = false;\r\n return; // Detenemos el commit ante errores\r\n }\r\n\r\n\r\n const potentialUpdatedRow: FilaDeDatos = { ...row, [column.key]: processedNewValue };\r\n\r\n const arePKValuesFilled = tableDefinition.primaryKey.every(key =>\r\n potentialUpdatedRow[key] !== undefined && potentialUpdatedRow[key] !== null && String(potentialUpdatedRow[key]).trim() !== ''\r\n );\r\n const isNewRow = !arePKValuesFilled || !!potentialUpdatedRow[NEW_ROW_INDICATOR];\r\n\r\n const isMandatoryField = tableDefinition.primaryKey.includes(column.key) || (tableDefinition.fields.find(f => f.name === column.key)?.nullable === false);\r\n const isMandatoryFieldEmpty = isNewRow && isMandatoryField && (processedNewValue === null || processedNewValue === undefined || String(processedNewValue).trim() === '');\r\n\r\n // Usamos sameValue para una comparación robusta (Dates, nulls, etc)\r\n if (sameValue(processedNewValue, row[column.key]) && !isNewRow && !isMandatoryFieldEmpty) {\r\n console.log(\"No se guardó: el valor no cambió.\");\r\n isCommitInProgress.current = false;\r\n return;\r\n }\r\n\r\n const oldRowData = { ...row };\r\n const primaryKeyValuesForBackend = tableDefinition.primaryKey.map(key => potentialUpdatedRow[key]);\r\n const currentRowIdBeforeUpdate = getPrimaryKeyValues(oldRowData, tableDefinition.primaryKey);\r\n\r\n onRowChange({ ...row, [column.key]: processedNewValue }, true);\r\n\r\n if (isNewRow) {\r\n const areAllMandatoryFieldsFilled = tableDefinition.fields.every(fieldDef => {\r\n const isMandatory = (fieldDef.nullable === false || fieldDef.isPk);\r\n const fieldValue = potentialUpdatedRow[fieldDef.name];\r\n return !isMandatory || (fieldValue !== null && fieldValue !== undefined && String(fieldValue).trim() !== '');\r\n });\r\n\r\n if (!areAllMandatoryFieldsFilled) {\r\n console.log(\"Nueva fila: Faltan campos obligatorios. No se guarda en el backend todavía.\");\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n const currentColumnsInRow = newMap.get(initialRowId) || new Set();\r\n\r\n tableDefinition.fields.forEach(fieldDef => {\r\n const isMandatory = (fieldDef.nullable === false || fieldDef.isPk);\r\n const isEditable = fieldDef.editable !== false;\r\n const fieldValue = potentialUpdatedRow[fieldDef.name];\r\n if (isMandatory && isEditable && (fieldValue === null || fieldValue === undefined || String(fieldValue).trim() === '')) {\r\n currentColumnsInRow.add(fieldDef.name);\r\n } else {\r\n currentColumnsInRow.delete(fieldDef.name);\r\n }\r\n });\r\n newMap.set(initialRowId, currentColumnsInRow);\r\n return newMap;\r\n });\r\n isCommitInProgress.current = false;\r\n return;\r\n } else {\r\n console.log(\"Nueva fila: Todos los campos obligatorios están llenos. Procediendo a guardar.\");\r\n setLocalCellChanges(prev => {\r\n const newMap = new Map(prev);\r\n newMap.delete(initialRowId);\r\n return newMap;\r\n });\r\n }\r\n }\r\n\r\n try {\r\n const response = await saveRecord({\r\n tableDefinition,\r\n isNewRow,\r\n formData: potentialUpdatedRow,\r\n initialData: oldRowData,\r\n targetFieldName: column.key\r\n });\r\n\r\n\r\n // si el servidor no devolvió la fila se sigue con la que se mandó\r\n const responseRow: FilaDeDatos = response.row ?? potentialUpdatedRow;\r\n\r\n let finalRowIdForFeedback: string;\r\n let persistedRowData: FilaDeDatos;\r\n\r\n if (response.row && isNewRow) {\r\n persistedRowData = { ...responseRow };\r\n setTableData(prevData => {\r\n const newData = [...prevData];\r\n const originalRowId = getPrimaryKeyValues(oldRowData, tableDefinition.primaryKey);\r\n const rowIndex = newData.findIndex(r => getPrimaryKeyValues(r, tableDefinition.primaryKey) === originalRowId);\r\n if (rowIndex !== -1) {\r\n newData[rowIndex] = persistedRowData;\r\n } else {\r\n const newPrimaryKeyId = getPrimaryKeyValues(persistedRowData, tableDefinition.primaryKey);\r\n const newRowIndex = newData.findIndex(r => getPrimaryKeyValues(r, tableDefinition.primaryKey) === newPrimaryKeyId);\r\n if (newRowIndex !== -1) {\r\n newData[newRowIndex] = persistedRowData;\r\n }\r\n }\r\n return newData;\r\n });\r\n finalRowIdForFeedback = getPrimaryKeyValues(persistedRowData, tableDefinition.primaryKey);\r\n } else {\r\n persistedRowData = responseRow;\r\n const isPrimaryKeyColumn = tableDefinition.primaryKey.includes(column.key);\r\n finalRowIdForFeedback = isPrimaryKeyColumn\r\n ? getPrimaryKeyValues(potentialUpdatedRow, tableDefinition.primaryKey)\r\n : currentRowIdBeforeUpdate;\r\n // La fila que devolvió el servidor (con lo que calcularon triggers y defaults) reemplaza a la que tenemos.\r\n // Se hace sobre los datos y no con onRowChange del editor: para cuando llega la respuesta el editor ya se cerró\r\n // y el foco puede estar en otra celda; onRowChange(fila, true) cerraría el editor de esa otra celda.\r\n const mismaFila = (fila: FilaDeDatos) => tableDefinition.primaryKey.every(k => fila[k] === potentialUpdatedRow[k]);\r\n setTableData(prevData => prevData.map(fila => mismaFila(fila) ? { ...fila, ...responseRow } : fila));\r\n }\r\n\r\n // --- MULTI-FEEDBACK DE ÉXITO ---\r\n const changes = findChangedValues(oldRowData, responseRow, isNewRow);\r\n setCellFeedbackMap(prevFeedback => {\r\n // Siempre usamos new Map(prevFeedback) para garantizar inmutabilidad\r\n const newFeedback = new Map(prevFeedback);\r\n\r\n changes.forEach((key) => {\r\n // Creamos la clave única de forma más robusta (idealmente usando getCellKey)\r\n const keyForMap = `${finalRowIdForFeedback}-${key}`;\r\n newFeedback.set(keyForMap, { rowId: finalRowIdForFeedback, columnKey: key, type: 'success' } as CellFeedback);\r\n });\r\n\r\n return newFeedback;\r\n });\r\n // -------------------------------\r\n\r\n } catch (err) {\r\n console.error('Error al guardar el registro:', err);\r\n\r\n // --- MULTI-FEEDBACK DE ERROR ---\r\n const keyForMap = `${currentRowIdBeforeUpdate}-${column.key}`;\r\n setCellFeedbackMap(prevFeedback => {\r\n // Siempre usamos new Map(prevFeedback) para garantizar inmutabilidad\r\n const newFeedback = new Map(prevFeedback);\r\n newFeedback.set(keyForMap, {\r\n rowId: currentRowIdBeforeUpdate,\r\n columnKey: column.key,\r\n type: 'error',\r\n message: mensajeDeError(err)\r\n } satisfies CellFeedback);\r\n return newFeedback;\r\n });\r\n // -------------------------------\r\n } finally {\r\n if (!closeEditor) {\r\n isCommitInProgress.current = false;\r\n }\r\n }\r\n }, [\r\n column, row, onRowChange, tableDefinition.primaryKey,\r\n tableDefinition.fields, showSuccess, showError, setCellFeedbackMap, onClose,\r\n setTableData, setLocalCellChanges, initialRowId, typer\r\n ]);\r\n\r\n\r\n // lo que se elige en un expander se escribe en la celda y se guarda\r\n const confirmarEnExpansion = useCallback((nuevo: unknown) => {\r\n setEditingValue(nuevo);\r\n void handleCommit(nuevo, true, false);\r\n }, [handleCommit]);\r\n // una opción (de un menú) es un valor tipado: un número o un lógico tal cual, lo demás como el texto que se escribiría\r\n const elegirOpcion = useCallback((valor: unknown) => {\r\n confirmarEnExpansion(valor === null || valor === undefined ? null : typeof valor === 'boolean' || isNumericType(fieldDefinition.typeName) ? valor : typer.toLocalString(valor));\r\n }, [fieldDefinition.typeName, typer, confirmarEnExpansion]);\r\n\r\n // los expanders de backend-plus (F9 o Alt+↓ y la lupa): según el tipo del campo, un menú (referencias, booleano, options), un calendario\r\n // (fecha) o una ventana de texto; escribir en una referencia un carácter que el tipo rechaza (una letra en un entero) abre el menú ya filtrado\r\n const expander = useExpander({\r\n campo: fieldDefinition,\r\n editable: fieldDefinition.editable !== false,\r\n fila: row,\r\n admiteNulo: fieldDefinition.nullable !== false,\r\n anclaje: entradaRef,\r\n textoActual: editingValue === null || editingValue === undefined ? '' : String(editingValue),\r\n valorActual: row[column.key],\r\n alElegirValor: elegirOpcion,\r\n alConfirmarTexto: confirmarEnExpansion,\r\n alFallar: showError,\r\n });\r\n\r\n const handleKeyDown = useCallback(async (event: React.KeyboardEvent) => {\r\n if (expander.manejarTecla(event)) return;\r\n if (event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey && caracterRechazado(event.key, typer)) {\r\n // como backend-plus (rejectedChar): lo que el tipo no admite no se escribe (una letra en un entero); en una referencia abre el menú filtrado\r\n event.preventDefault();\r\n if (expander.tipo === 'referencia') void expander.abrir(event.key);\r\n return;\r\n }\r\n if (event.key === 'F4') {\r\n event.preventDefault();\r\n if (onKeyPress) {\r\n onKeyPress(rowIdx, column.key, event, handleCommit, editingValue);\r\n }\r\n return;\r\n }\r\n\r\n // Con cada tecla (la grilla lleva la cuenta de la anterior): ella decide si deja la celda (Enter, Tab, flechas; ver\r\n // direccionDeSalida) y, si la deja, guarda y pasa el foco. Una flecha que solo mueve el cursor dentro del texto no guarda.\r\n // Lo que resuelve la grilla no puede hacer además lo suyo: el Tab nativo mandaría el foco a otro lado.\r\n if (onKeyPress?.(rowIdx, column.key, event, handleCommit, editingValue)) {\r\n event.preventDefault();\r\n }\r\n }, [handleCommit, editingValue, column.key, rowIdx, onKeyPress, expander, typer]);\r\n\r\n // Teclas (F4) que caen en la celda antes de que este editor tenga el foco (o antes de que exista):\r\n // el grid se las pasa a través de registrarEditor; siempre se usa la versión más reciente del manejador\r\n const procesarTecla = React.useRef<(key: string) => void>(() => {});\r\n procesarTecla.current = (key: string) => {\r\n if (key === 'F9') {\r\n void expander.abrir('');\r\n } else if (key === 'F4' && onKeyPress) {\r\n onKeyPress(rowIdx, column.key, { key, target: document.activeElement ?? document.body }, handleCommit, editingValue);\r\n }\r\n };\r\n useEffect(() => registrarEditor?.((key) => procesarTecla.current(key)), []); // eslint-disable-line react-hooks/exhaustive-deps\r\n\r\n const handleBlur = useCallback(() => {\r\n if (expander.abierto.current) return;\r\n handleCommit(editingValue, true, false);\r\n }, [handleCommit, editingValue]);\r\n\r\n const isFieldEditable = fieldDefinition?.editable !== false;\r\n\r\n const isNumeric = isNumericType(fieldDefinition?.typeName);\r\n\r\n const editor = (\r\n <InputBase\r\n autoFocus\r\n value={editingValue}\r\n onChange={(e) => setEditingValue(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n onPaste={(e: React.ClipboardEvent) => {\r\n const pegado = e.clipboardData.getData('text');\r\n if (textoPegadoRechazado(pegado, typer)) {\r\n e.preventDefault();\r\n showError(`«${pegado}» no es un valor válido para este campo (${typer.typeName})`);\r\n }\r\n }}\r\n onBlur={handleBlur}\r\n inputRef={entradaRef}\r\n placeholder={isMandatory ? '*' : ''}\r\n className={editingValue === '--' ? 'typed-controls-signal-no-data' : editingValue === '//' ? 'typed-controls-signal-unknown-data' : ''}\r\n sx={{\r\n width: '100%',\r\n margin: '0',\r\n border: `1px solid ${cellBorderColor}`,\r\n borderRadius: '4px',\r\n padding: '2px 4px',\r\n fontSize: '0.8rem',\r\n boxSizing: 'border-box',\r\n backgroundColor: cellBackgroundColor,\r\n input: {\r\n textAlign: isNumeric ? 'right' : 'left',\r\n }\r\n }}\r\n onClick={(e) => e.stopPropagation()}\r\n disabled={!isFieldEditable}\r\n endAdornment={expander.lupa}\r\n />\r\n\r\n );\r\n const conMenu = (\r\n <>\r\n {editor}\r\n {expander.ventana}\r\n </>\r\n );\r\n return currentCellFeedback?.message\r\n ? <Tooltip title={currentCellFeedback.message}><div style={{ width: '100%' }}>{conMenu}</div></Tooltip>\r\n : conMenu;\r\n}\r\n\r\nexport default InputRenderer;","import { useCallback } from 'react';\nimport { useApiCall } from './useApiCall';\nimport { FilaDeDatos, TableDefinition } from '../types';\nimport { requerido } from '../utils/errores';\n\n/** Lo que devuelve table_record_save: la fila como quedó en la base (con lo que calcularon triggers y valores por omisión) */\nexport interface RespuestaDeGuardado {\n row?: FilaDeDatos;\n}\n\nexport interface SaveRecordParams {\n tableDefinition: TableDefinition; // se guarda en la tabla que nombra su definición (`name`), no en la física (`tableName`)\n isNewRow: boolean;\n formData: FilaDeDatos; // The entire row state after changes (or potential updated row)\n initialData: FilaDeDatos; // The row before the change\n targetFieldName?: string; // If provided, it's an update to a specific field. Si no, asume mandar todo (o si es $new)\n}\n\nexport const useTableRecordSave = () => {\n const { callApi, loading, error } = useApiCall();\n\n const saveRecord = useCallback(async ({\n tableDefinition,\n isNewRow,\n formData,\n initialData,\n targetFieldName\n }: SaveRecordParams) => {\n const status = isNewRow ? 'new' : 'update';\n let rowToSend: FilaDeDatos = {};\n\n if (isNewRow) {\n tableDefinition.fields.forEach(fieldDef => {\n const fieldValue = formData[fieldDef.name];\n if (fieldValue !== undefined && fieldValue !== null && String(fieldValue).trim() !== '') {\n rowToSend[fieldDef.name] = fieldValue;\n } else if (tableDefinition.primaryKey.includes(fieldDef.name) && (fieldValue === null || fieldValue === undefined || String(fieldValue).trim() === '')) {\n // Conservamos pk vacías para backend-plus\n rowToSend[fieldDef.name] = fieldValue;\n }\n });\n delete rowToSend['$new'];\n } else {\n // Es un update, a un campo específico (como InputRenderer y VerticalEditor autosave)\n if (targetFieldName) {\n rowToSend[targetFieldName] = formData[targetFieldName];\n } else {\n // Por si se quiere guardar la fila entera forzosamente\n Object.keys(formData).forEach(key => {\n if (formData[key] !== initialData[key]) {\n rowToSend[key] = formData[key];\n }\n });\n }\n // como backend-plus: en un update `newRow` lleva solo lo que cambió; la clave viaja aparte (`primaryKeyValues`).\n // Si se agregara, un campo con `table` (de otra tabla) dejaría la tabla principal con un update de solo claves\n // («orden de modificación sin columnas a modificar»).\n }\n\n const primaryKeyValuesForBackend = isNewRow ? [] : tableDefinition.primaryKey.map(key => initialData[key]);\n\n const response = await callApi<RespuestaDeGuardado>('table_record_save', {\n // como backend-plus (`depot.def.name`): el servidor busca la definición por este nombre. Una definición que guarda en\n // otra tabla (`tableName`, con campos de `table`) no existe con el nombre de la física: ahí faltarían sus campos\n table: tableDefinition.name,\n primaryKeyValues: primaryKeyValuesForBackend,\n newRow: rowToSend,\n oldRow: isNewRow ? {} : initialData,\n status\n });\n\n return requerido(response, 'la respuesta del guardado');\n }, [callApi]);\n\n return { saveRecord, loading, error };\n};\n","import { RenderEditCellProps } from \"react-data-grid\";\r\nimport InputRenderer from \"../InputRenderer\";\r\nimport { ColumnaDeGrilla, esColumnaPropia } from \"../GenericDataGrid\";\r\nimport { FilaDeDatos, FilaDeResumen } from \"../../../types\";\r\nimport { valorFijoDe } from \"../../../utils/fixedFields\";\r\n\r\nexport const allColumnsEditCellRenderer = (\r\n props: RenderEditCellProps<FilaDeDatos, FilaDeResumen>, \r\n allColumns: ColumnaDeGrilla[]\r\n) => {\r\n const column = props.column;\r\n if (!esColumnaPropia(column)) return undefined;\r\n switch (column.customType) {\r\n case 'default': {\r\n const defaultColumn = column;\r\n const {\r\n tableDefinition,\r\n fixedFields,\r\n primaryKey,\r\n setTableData,\r\n handleKeyPressInEditor,\r\n registrarEditor\r\n } = defaultColumn\r\n \r\n if (column.editable) { \r\n const fieldDefinition = tableDefinition.fields.find(f => f.name === column.key);\r\n \r\n // Busca si el campo es fijo en el array fixedFields\r\n const isFixedField = valorFijoDe(fixedFields, column.key) !== undefined;\r\n const isFieldEditableByUI = fieldDefinition?.editable !== false && !isFixedField; // No editable si es fijo\r\n \r\n if (!isFieldEditableByUI) {\r\n // Si el campo no es editable por UI (ej. por ser fijo), no renderizamos el editor\r\n return undefined\r\n }\r\n \r\n // El feedback (color y mensaje) lo lee InputRenderer del EstadoGrilla\r\n return (\r\n <InputRenderer\r\n {...props}\r\n tableDefinition={tableDefinition}\r\n onKeyPress={(rowIndex, columnKey, event, handleCommit, valorActual) => handleKeyPressInEditor(rowIndex, columnKey, event, allColumns, handleCommit, valorActual)}\r\n registrarEditor={registrarEditor}\r\n setTableData={setTableData}\r\n primaryKey={primaryKey}\r\n />\r\n )\r\n }\r\n return undefined\r\n }\r\n case 'detail': {\r\n return undefined\r\n }\r\n case 'action': {\r\n return undefined\r\n }\r\n default:\r\n return undefined;\r\n }\r\n\r\n}","import React, { useState } from 'react';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n FormControlLabel,\n Checkbox,\n Box,\n Typography,\n Stack,\n Divider\n} from '@mui/material';\nimport FileUploadIcon from '@mui/icons-material/FileUpload';\n\ninterface ImportDialogProps {\n open: boolean;\n onClose: () => void;\n onImport: (file: File, options: ImportOptions) => Promise<void>;\n loading: boolean;\n}\n\nexport interface ImportOptions {\n skipUnknownFieldsAtImport: boolean;\n simplificateSpaces: boolean;\n replaceNewLineWithSpace: boolean;\n}\n\nexport const ImportDialog: React.FC<ImportDialogProps> = ({ open, onClose, onImport, loading }) => {\n const [file, setFile] = useState<File | null>(null);\n const [options, setOptions] = useState<ImportOptions>({\n skipUnknownFieldsAtImport: false,\n simplificateSpaces: true,\n replaceNewLineWithSpace: true\n });\n\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files[0]) {\n setFile(e.target.files[0]);\n }\n };\n\n const handleOptionChange = (name: keyof ImportOptions) => (e: React.ChangeEvent<HTMLInputElement>) => {\n setOptions(prev => ({ ...prev, [name]: e.target.checked }));\n };\n\n const handleConfirm = async () => {\n if (file) {\n await onImport(file, options);\n onClose();\n }\n };\n\n return (\n <Dialog open={open} onClose={loading ? undefined : onClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>\n <FileUploadIcon color=\"primary\" />\n Importar datos desde archivo\n </DialogTitle>\n <DialogContent dividers>\n <Stack spacing={3} sx={{ mt: 1 }}>\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom fontWeight=\"bold\">\n Seleccionar archivo\n </Typography>\n <Box\n sx={{\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n p: 2,\n textAlign: 'center',\n bgcolor: 'action.hover',\n position: 'relative'\n }}\n >\n <input\n type=\"file\"\n accept=\".xlsx, .xls, .tab\"\n onChange={handleFileChange}\n style={{\n opacity: 0,\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n cursor: 'pointer'\n }}\n />\n <Typography variant=\"body2\" color={file ? \"textPrimary\" : \"textSecondary\"}>\n {file ? file.name : \"Haz clic aquí para seleccionar un archivo\"}\n </Typography>\n </Box>\n </Box>\n\n <Divider />\n\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom fontWeight=\"bold\">\n Opciones de importación\n </Typography>\n <Stack>\n <FormControlLabel\n control={\n <Checkbox\n checked={options.skipUnknownFieldsAtImport}\n onChange={handleOptionChange('skipUnknownFieldsAtImport')}\n size=\"small\"\n />\n }\n label={<Typography variant=\"body2\">Omitir campos desconocidos al importar</Typography>}\n />\n <FormControlLabel\n control={\n <Checkbox\n checked={options.simplificateSpaces}\n onChange={handleOptionChange('simplificateSpaces')}\n size=\"small\"\n />\n }\n label={<Typography variant=\"body2\">Simplificar espacios</Typography>}\n />\n <FormControlLabel\n control={\n <Checkbox\n checked={options.replaceNewLineWithSpace}\n onChange={handleOptionChange('replaceNewLineWithSpace')}\n size=\"small\"\n />\n }\n label={<Typography variant=\"body2\">Reemplazar saltos de línea con espacios</Typography>}\n />\n </Stack>\n </Box>\n </Stack>\n </DialogContent>\n <DialogActions sx={{ px: 3, py: 2 }}>\n <Button onClick={onClose} disabled={loading}>\n Cancelar\n </Button>\n <Button\n onClick={handleConfirm}\n variant=\"contained\"\n disabled={!file || loading}\n startIcon={<FileUploadIcon />}\n >\n {loading ? 'Procesando...' : 'Confirmar Importación'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useState } from 'react';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n FormControl,\n FormLabel,\n RadioGroup,\n FormControlLabel,\n Radio,\n Checkbox,\n FormGroup,\n Box,\n Typography,\n CircularProgress\n} from '@mui/material';\nimport * as XLSX from 'xlsx';\nimport typeAr from 'like-ar';\nimport typeStore from 'type-store';\nimport { FieldDefinition, FilaDeDatos, TableDefinition } from '../../types';\n\ninterface ExportDialogProps {\n open: boolean;\n onClose: () => void;\n tableDefinition: TableDefinition;\n tableData: FilaDeDatos[];\n filteredRows: FilaDeDatos[];\n username?: string;\n}\n\ntype ExportFormat = 'xlsx' | 'csv' | 'tab';\n\nexport const ExportDialog: React.FC<ExportDialogProps> = ({\n open,\n onClose,\n tableDefinition,\n tableData,\n filteredRows,\n username = 'anonymous'\n}) => {\n const [format, setFormat] = useState<ExportFormat>('xlsx');\n const [options, setOptions] = useState({\n fromOtherTables: true,\n readOnly: true,\n hiddens: false\n });\n const [isPreparing, setIsPreparing] = useState(false);\n\n const handleOptionChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n setOptions({\n ...options,\n [event.target.name]: event.target.checked\n });\n };\n\n const handlePrepareExport = () => {\n setIsPreparing(true);\n // Pequeño delay para que se vea el spinner y no bloquee el hilo principal inmediatamente\n setTimeout(async () => {\n try {\n await executeExport();\n } finally {\n setIsPreparing(false);\n onClose();\n }\n }, 100);\n };\n\n const executeExport = async () => {\n const fieldsDef2Export = tableDefinition.fields.filter((fieldDef: FieldDefinition) => {\n const isVisible = fieldDef.visible !== false;\n // Simplificación de la lógica legada adaptada a FieldDefinition\n return (fieldDef.inTable !== false || options.fromOtherTables)\n && isVisible\n && (fieldDef.editable !== false || options.readOnly || (tableDefinition.primaryKey?.includes(fieldDef.name)))\n // Nota: La lógica de 'hiddens' en la grilla original dependía de 'grid.view.hiddenColumns'.\n // Aquí, si hiddens es false, solo exportamos los visibles.\n && (isVisible || options.hiddens);\n });\n\n if (format === 'xlsx') {\n await excelExport(fieldsDef2Export);\n } else {\n textExport(fieldsDef2Export);\n }\n };\n\n const textExport = (fieldsToExport: FieldDefinition[]) => {\n let separator = ',';\n let dotExtension = '.csv';\n let replacer = (txt: string) => {\n return /[\\n,\"]/.test(txt) ? '\"' + txt.replace(/\"/g, '\"\"') + '\"' : txt;\n };\n\n if (format === 'tab') {\n separator = '|';\n dotExtension = '.tab';\n const trans: Record<string, string> = {\n '|': '\\\\x7C',\n '\\\\': '\\\\\\\\',\n '\\r': '\\\\r',\n '\\n': '\\\\n',\n };\n replacer = (txt: string) => {\n return txt.replace(/[|\\\\\\r\\n]/g, (char) => trans[char] || char);\n };\n }\n\n const data: string[][] = [];\n // Cabeceras\n data.push(fieldsToExport.map(f => f.name));\n\n // Filas\n filteredRows.forEach(row => {\n data.push(fieldsToExport.map(fieldDef => {\n const value = row[fieldDef.name];\n if (value == null) return '';\n // Usamos type-store para formatear a string plano\n return typeStore.typerFrom(fieldDef).toPlainString(value);\n }));\n });\n\n const content = data.map(line => line.map(replacer).join(separator)).join('\\r\\n');\n downloadFile(content, `${tableDefinition.name}${dotExtension}`, 'text/plain');\n };\n\n const excelExport = async (fieldsToExport: FieldDefinition[]) => {\n const wb = XLSX.utils.book_new();\n const ws: XLSX.WorkSheet = {};\n\n const STYLE_HEADER = { font: { bold: true, underline: true } };\n const STYLE_PK = { font: { bold: true } };\n const STYLE_LOOKUP = { font: { color: { rgb: \"5588DD\" } } };\n\n // Hoja 1: Datos\n fieldsToExport.forEach((field, iColumn) => {\n ws[XLSX.utils.encode_cell({ c: iColumn, r: 0 })] = { t: 's', v: field.name, s: STYLE_HEADER };\n });\n\n filteredRows.forEach((row, iRow) => {\n fieldsToExport.forEach((fieldDef, iColumn) => {\n const value = row[fieldDef.name];\n if (value != null) {\n const typer = typeStore.typerFrom(fieldDef);\n const excelValue = typer.toExcelValue(value);\n const excelType = typer.toExcelType(value);\n const cell: XLSX.CellObject = { t: excelType, v: excelValue };\n\n if (fieldDef.typeName === 'interval') {\n cell.z = '[h]:mm:ss';\n }\n if (tableDefinition.primaryKey?.includes(fieldDef.name)) {\n cell.s = STYLE_PK;\n } else if (fieldDef.editable === false) {\n cell.s = STYLE_LOOKUP;\n }\n ws[XLSX.utils.encode_cell({ c: iColumn, r: iRow + 1 })] = cell;\n }\n });\n });\n\n ws[\"!ref\"] = XLSX.utils.encode_range({\n s: { c: 0, r: 0 },\n e: { c: fieldsToExport.length - 1, r: filteredRows.length }\n });\n\n // Hoja 2: Metadata\n const metaWs: XLSX.WorkSheet = {};\n let metaRow = 0;\n\n const addMetaRow = (label: string, value: string) => {\n metaWs[XLSX.utils.encode_cell({ c: 0, r: metaRow })] = { t: 's', v: label, s: STYLE_HEADER };\n metaWs[XLSX.utils.encode_cell({ c: 1, r: metaRow })] = { t: 's', v: value };\n metaRow++;\n };\n\n addMetaRow('table', tableDefinition.name);\n addMetaRow('date', new Date().toISOString());\n addMetaRow('user', username);\n\n metaWs[\"!ref\"] = XLSX.utils.encode_range({\n s: { c: 0, r: 0 },\n e: { c: 1, r: metaRow - 1 }\n });\n\n const sheet1name = tableDefinition.name.length > 27 ? tableDefinition.name.slice(0, 27) + '...' : tableDefinition.name;\n const sheet2name = tableDefinition.name !== \"metadata\" ? \"metadata\" : \"meta-data\";\n\n XLSX.utils.book_append_sheet(wb, ws, sheet1name);\n XLSX.utils.book_append_sheet(wb, metaWs, sheet2name);\n\n const wbFile = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });\n const buf = new ArrayBuffer(wbFile.length);\n const view = new Uint8Array(buf);\n for (let i = 0; i < wbFile.length; i++) view[i] = wbFile.charCodeAt(i) & 0xFF;\n\n downloadFile(buf, `${tableDefinition.name}.xlsx`, 'application/octet-stream');\n };\n\n const downloadFile = (content: BlobPart, fileName: string, mimeType: string) => {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = fileName;\n document.body.appendChild(a);\n a.click();\n setTimeout(() => {\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n }, 0);\n };\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>Exportar Datos</DialogTitle>\n <DialogContent>\n {isPreparing ? (\n <Box display=\"flex\" flexDirection=\"column\" alignItems=\"center\" py={4}>\n <CircularProgress />\n <Typography sx={{ mt: 2 }}>Preparando exportación...</Typography>\n </Box>\n ) : (\n <Box sx={{ mt: 1 }}>\n <FormControl component=\"fieldset\">\n <FormLabel component=\"legend\">Formato</FormLabel>\n <RadioGroup\n value={format}\n onChange={(e) => setFormat(e.target.value as ExportFormat)}\n >\n <FormControlLabel value=\"xlsx\" control={<Radio />} label=\"Excel (.xlsx)\" />\n <FormControlLabel value=\"tab\" control={<Radio />} label=\"Tabulado (.tab | )\" />\n <FormControlLabel value=\"csv\" control={<Radio />} label=\"CSV (.csv , )\" />\n </RadioGroup>\n </FormControl>\n\n <Box sx={{ mt: 3 }}>\n <Typography variant=\"subtitle2\" gutterBottom>Opciones de campos:</Typography>\n <FormGroup>\n <FormControlLabel\n control={<Checkbox checked={options.fromOtherTables} onChange={handleOptionChange} name=\"fromOtherTables\" />}\n label=\"Desde otras tablas\"\n />\n <FormControlLabel\n control={<Checkbox checked={options.readOnly} onChange={handleOptionChange} name=\"readOnly\" />}\n label=\"Solo lectura\"\n />\n <FormControlLabel\n control={<Checkbox checked={options.hiddens} onChange={handleOptionChange} name=\"hiddens\" />}\n label=\"Ocultos\"\n />\n </FormGroup>\n </Box>\n </Box>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={isPreparing}>Cancelar</Button>\n <Button onClick={handlePrepareExport} variant=\"contained\" disabled={isPreparing}>\n Descargar\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useState, useCallback, useMemo, useRef } from 'react';\nimport {\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n Alert,\n Box,\n CircularProgress,\n IconButton,\n Typography\n} from '@mui/material';\nimport CloseIcon from '@mui/icons-material/Close';\nimport { FilaDeDatos, TableDefinition, FixedField } from '../types';\nimport { TypedField } from '../components/forms/TypedField';\nimport { useTableRecordSave } from '../hooks/useTableRecordSave';\nimport { getPrimaryKeyValues, sameValue } from '../components/grid/utils/helpers';\n\nimport typeStore from 'type-store';\nimport { mensajeDeError } from '../utils/errores';\n\nexport interface VerticalEditorPageProps {\n tableName: string;\n tableDefinition: TableDefinition;\n initialData: FilaDeDatos; // Puede tener nulos\n fixedFields?: FixedField[];\n isNewRow?: boolean;\n onSaveSuccess: (savedRow: FilaDeDatos, isNewRow: boolean) => void;\n onClose: () => void;\n}\n\nexport const VerticalEditorPage: React.FC<VerticalEditorPageProps> = ({\n tableName,\n tableDefinition,\n initialData,\n fixedFields,\n isNewRow = false,\n onSaveSuccess,\n onClose: onClose\n}) => {\n const { saveRecord } = useTableRecordSave();\n\n // Convertimos isNewRow y formData en referencias dinámicas para permitir múltiples auto-saves\n const [internalIsNewRow, setInternalIsNewRow] = useState(isNewRow);\n\n // Estado principal del formulario\n const [formData, setFormData] = useState<FilaDeDatos>(initialData);\n\n // Rastreamos el último estado guardado en backend para evitar tráfico redundante\n const [lastSavedData, setLastSavedData] = useState<FilaDeDatos>(initialData);\n\n // Rastreamos validez de campos individualmente\n const [validityMap, setValidityMap] = useState<Record<string, boolean>>({});\n\n const [globalError, setGlobalError] = useState<string | null>(null);\n const [isSaving, setIsSaving] = useState(false);\n\n const [fieldFeedback, setFieldFeedback] = useState<Record<string, 'success' | 'error'>>({});\n const feedbackTimersRef = useRef<Record<string, ReturnType<typeof setTimeout>>>({}); // timer por campo\n\n const handleFieldChange = useCallback((fieldName: string, newValue: unknown, isValid: boolean) => {\n setFormData(prev => ({ ...prev, [fieldName]: newValue }));\n\n setValidityMap(prev => ({ ...prev, [fieldName]: isValid }));\n setGlobalError(null);\n }, []);\n\n const isFormValid = useMemo(() => {\n // Un campo es inválido si explícitamente está marcado como false por DynamicField\n for (const key in validityMap) {\n if (validityMap[key] === false) return false;\n }\n\n // Además, verificar campos no nulos (isPk o nullable===false)\n for (const field of tableDefinition.fields) {\n const isMandatory = field.nullable === false || field.isPk;\n const val = formData[field.name];\n if (isMandatory && (val === null || val === undefined || String(val).trim() === '')) {\n // Hay fixed fields? Si es fixed field e inicializa como nulo (raro), igual no es válido.\n // Usualmente el initialData ya trae los fixedFields poblados\n return false;\n }\n }\n\n return true;\n }, [formData, validityMap, tableDefinition.fields]);\n\n const handleCommit = useCallback(async (fieldName: string, newValue: unknown, isValid: boolean) => {\n // En un autosave, si el campo es inválido localmente, no enviamos\n if (!isValid) return;\n\n // Diff check: si el valor es el mismo que está guardado en backend, no persistimos.\n // Usamos sameValue para comparación robusta (Dates, null/undefined, etc.)\n if (sameValue(newValue, lastSavedData[fieldName])) {\n return;\n }\n\n const potentialUpdatedRow = { ...formData, [fieldName]: newValue };\n\n let areAllMandatoryFieldsFilled = true;\n for (const field of tableDefinition.fields) {\n const isMandatory = field.nullable === false || field.isPk;\n const val = potentialUpdatedRow[field.name];\n if (isMandatory && (val === null || val === undefined || String(val).trim() === '')) {\n areAllMandatoryFieldsFilled = false;\n break;\n }\n }\n\n if (internalIsNewRow && !areAllMandatoryFieldsFilled) {\n // Faltan campos para poder insertar la fila nueva, no guardamos en el backend todavía\n return;\n }\n\n setIsSaving(true);\n setGlobalError(null);\n\n try {\n const response = await saveRecord({\n tableDefinition,\n isNewRow: internalIsNewRow,\n formData: potentialUpdatedRow,\n initialData,\n targetFieldName: fieldName\n });\n\n const responseRow = { ...response.row };\n\n // Actualizamos la data interna y nuestro \"último guardado\" con lo devuelto por el servidor\n setFormData(prev => ({ ...prev, ...responseRow }));\n setLastSavedData(prev => ({ ...prev, ...responseRow }));\n\n\n // Si la logramos crear, ya no es \"nueva\"\n const wasNewItem = internalIsNewRow;\n if (internalIsNewRow) {\n setInternalIsNewRow(false);\n }\n\n if (wasNewItem) {\n // Primer guardado: pintar todos los campos con valor en la respuesta del servidor\n const feedbackPatch: Record<string, 'success'> = {};\n for (const key of Object.keys(responseRow)) {\n const val = responseRow[key];\n if (val !== null && val !== undefined && val !== '') {\n feedbackPatch[key] = 'success';\n if (feedbackTimersRef.current[key]) clearTimeout(feedbackTimersRef.current[key]);\n feedbackTimersRef.current[key] = setTimeout(() =>\n setFieldFeedback(prev => { const n = { ...prev }; delete n[key]; return n; })\n , 3000);\n }\n }\n setFieldFeedback(prev => ({ ...prev, ...feedbackPatch }));\n } else {\n // Guardado parcial: solo el campo modificado\n setFieldFeedback(prev => ({ ...prev, [fieldName]: 'success' }));\n if (feedbackTimersRef.current[fieldName]) clearTimeout(feedbackTimersRef.current[fieldName]);\n feedbackTimersRef.current[fieldName] = setTimeout(() =>\n setFieldFeedback(prev => { const n = { ...prev }; delete n[fieldName]; return n; })\n , 3000);\n }\n\n onSaveSuccess(responseRow, wasNewItem);\n\n } catch (err) {\n setGlobalError(mensajeDeError(err, 'Error desconocido al guardar.'));\n // Error multi-feedback: cada campo tiene su propio timer\n setFieldFeedback(prev => ({ ...prev, [fieldName]: 'error' }));\n if (feedbackTimersRef.current[fieldName]) clearTimeout(feedbackTimersRef.current[fieldName]);\n feedbackTimersRef.current[fieldName] = setTimeout(() =>\n setFieldFeedback(prev => { const n = { ...prev }; delete n[fieldName]; return n; })\n , 5000);\n\n } finally {\n setIsSaving(false);\n }\n }, [\n saveRecord, formData, initialData, internalIsNewRow,\n tableName, tableDefinition, onSaveSuccess\n ]);\n\n\n\n\n // Campos a mostrar: todos menos los audit o sistemáticos (opcional, por ahora todos los fields expuestos en la UI)\n // También consideramos ocultar los fixed fields o mostrarlos deshabilitados\n return (\n <Box sx={{ p: 2, display: 'flex', flexDirection: 'column', height: '100%' }}>\n <DialogTitle sx={{ px: 0, pt: 0, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <Typography component=\"div\" variant=\"h6\">\n {isNewRow ? 'Nuevo Registro' : 'Editar Registro'} - {tableDefinition.title || tableName}\n </Typography>\n <IconButton onClick={onClose} size=\"small\" edge=\"end\">\n <CloseIcon />\n </IconButton>\n </DialogTitle>\n\n <DialogContent dividers sx={{ px: 2, flexGrow: 1, py: 1 }}>\n {globalError && (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {globalError}\n </Alert>\n )}\n\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>\n {tableDefinition.fields.map(field => {\n const isFixed = fixedFields?.some(ff => ff.fieldName === field.name);\n const isMandatory = field.nullable === false || tableDefinition.primaryKey.includes(field.name);\n return (\n <Box key={field.name} sx={{ width: '100%', maxWidth: '100%' }}>\n <TypedField\n fieldDef={field}\n value={formData[field.name]}\n onChange={handleFieldChange}\n onCommit={handleCommit}\n disabled={isFixed || field.editable === false}\n feedback={fieldFeedback[field.name]}\n isMandatory={isMandatory}\n />\n </Box>\n\n );\n })}\n </Box>\n\n </DialogContent>\n\n <DialogActions sx={{ px: 0, pb: 0, pt: 2, justifyContent: 'space-between', alignItems: 'center' }}>\n <Box>\n {isSaving && <CircularProgress size={20} sx={{ ml: 2 }} />}\n <span style={{ marginLeft: 10, fontSize: '0.8rem', color: '#666' }}>\n {isSaving ? 'Guardando...' : 'Cambios auto-guardados'}\n </span>\n </Box>\n <Button variant=\"outlined\" onClick={onClose}>Cerrar</Button>\n </DialogActions>\n </Box>\n );\n};\n\n","// Los parámetros con los que backend-plus abre una tabla (`wScreens.table` de my-menu.js): vienen de la dirección\n// (`#table=usuarios&ff=...&fc=...&td=...`) y del ítem de menú; los de la dirección pisan a los del ítem.\nimport JSON4all from 'json4all';\nimport { FixedField } from '../types';\n\n/** Una condición de la fila de filtros: `operator` es uno de los de backend-plus (=, ~, !~, >, >=, <, <=, !=, ∅, !=∅, /R/i) */\nexport interface FiltroInicial {\n column: string;\n operator: string;\n value: unknown;\n}\n\nexport interface ParametrosDeTabla {\n fixedFields?: FixedField[];\n /** Las filas de filtros que se muestran al abrir (cada fila es una alternativa; las condiciones de una fila se cumplen todas) */\n filterColumns?: FiltroInicial[][];\n /** Lo que se le cambia a la definición de la tabla que manda el servidor (se mezcla en profundidad) */\n tableDef?: Record<string, unknown>;\n parameterFunctions?: Record<string, unknown>;\n pick?: string;\n label?: string;\n}\n\ntype Registro = Record<string, unknown>;\n\nfunction esRegistro(valor: unknown): valor is Registro {\n return typeof valor === 'object' && valor !== null && !Array.isArray(valor) && !(valor instanceof Date);\n}\n\n/** Los parámetros que en la dirección van codificados con json4all (UriSearchToObjectParams de my-ajax.js) */\nconst CODIFICADOS = ['ff', 'fc', 'td', 'pf'];\n\n/**\n * Lee una dirección de backend-plus (`#a=1&b=...` o `?a=1&b=...`) como lo hace `UriSearchToObject`: se parte por `&` y por el\n * primer `=`, se decodifica con decodeURIComponent (no con URLSearchParams: ahí `+` sería un espacio) y los parámetros que\n * van en json4all se interpretan. Un valor que no se puede interpretar se ignora.\n */\nexport function parametrosDeDireccion(direccion: string): Registro {\n const resultado: Registro = {};\n direccion.split('&').forEach(par => {\n const sinMarca = par.replace(/^[#?]/, '');\n const igual = sinMarca.indexOf('=');\n if (igual === -1) return;\n const nombre = sinMarca.slice(0, igual);\n try {\n const texto = decodeURIComponent(sinMarca.slice(igual + 1));\n resultado[nombre] = CODIFICADOS.includes(nombre) ? JSON4all.parse(texto) : texto;\n } catch (err) {\n console.warn(`No se pudo interpretar el parámetro '${nombre}' de la dirección:`, err);\n }\n });\n return resultado;\n}\n\n/**\n * Los fixedFields: backend-plus admite un arreglo [{fieldName, value, ...}] o un objeto {campo: valor}\n * (`wScreens.table` de my-menu.js). Lo que no tiene forma de fixedField se descarta.\n */\nexport function aFixedFields(valor: unknown): FixedField[] {\n if (Array.isArray(valor)) {\n return valor.flatMap((par: unknown): FixedField[] => {\n if (!esRegistro(par) || typeof par.fieldName !== 'string') return [];\n const fixedField: FixedField = { fieldName: par.fieldName, value: par.value };\n if (par.until !== undefined) fixedField.until = par.until;\n if (typeof par.range === 'boolean') fixedField.range = par.range;\n if (typeof par.show === 'boolean') fixedField.show = par.show;\n return [fixedField];\n });\n }\n if (esRegistro(valor)) return Object.entries(valor).map(([fieldName, value]) => ({ fieldName, value }));\n return [];\n}\n\n/**\n * Las filas de filtros de `filterColumns`: un arreglo de condiciones (una fila) o un arreglo de arreglos (varias filas, que\n * son alternativas), como `createRowFilter` de my-tables.js. Una condición sin columna o sin operador no filtra nada.\n * El cliente de backend-plus llama `column` a la columna (el .d.ts de MenuInfoTable dice `fieldName` para el `fc` del menú,\n * pero my-menu.js se lo pasa tal cual a `filterColumns`): se aceptan las dos.\n */\nexport function aFiltrosIniciales(valor: unknown): FiltroInicial[][] {\n if (!Array.isArray(valor) || valor.length === 0) return [];\n const filas: unknown[] = Array.isArray(valor[0]) ? valor : [valor];\n return filas\n .map((fila: unknown): FiltroInicial[] => Array.isArray(fila)\n ? fila.flatMap((condicion: unknown): FiltroInicial[] => {\n if (!esRegistro(condicion) || typeof condicion.operator !== 'string') return [];\n const columna = condicion.column ?? condicion.fieldName;\n return typeof columna === 'string' ? [{ column: columna, operator: condicion.operator, value: condicion.value }] : [];\n })\n : [])\n .filter(fila => fila.length > 0);\n}\n\n/** Los parámetros de una tabla a partir de lo que declara el ítem de menú y de la dirección (esta última manda) */\nexport function parametrosDeTabla(item: Registro | undefined, direccion: string): ParametrosDeTabla {\n const origen: Registro = { ...item, ...parametrosDeDireccion(direccion) };\n const parametros: ParametrosDeTabla = {};\n const fixedFields = aFixedFields(origen.ff ?? origen.fixedFields);\n if (fixedFields.length > 0) parametros.fixedFields = fixedFields;\n const filterColumns = aFiltrosIniciales(origen.fc);\n if (filterColumns.length > 0) parametros.filterColumns = filterColumns;\n if (esRegistro(origen.td)) parametros.tableDef = origen.td;\n if (esRegistro(origen.pf)) parametros.parameterFunctions = origen.pf;\n if (typeof origen.pick === 'string') parametros.pick = origen.pick;\n if (typeof origen.label === 'string') parametros.label = origen.label;\n return parametros;\n}\n\n/** Mezcla en profundidad (como `changing` de best-globals): los objetos se mezclan, todo lo demás (arreglos incluidos) se reemplaza */\nexport function fusionar<T extends object>(base: T, cambios: Registro): T {\n const resultado = { ...base };\n for (const [clave, valor] of Object.entries(cambios)) {\n const actual: unknown = Reflect.get(resultado, clave);\n Reflect.set(resultado, clave, esRegistro(actual) && esRegistro(valor) ? fusionar(actual, valor) : valor);\n }\n return resultado;\n}\n","// Prefijo de las rutas de la aplicación cuando no está en la raíz del sitio (por ejemplo `/ggs2026/react` cuando la sirve\n// el propio backend). Lo define el bundler de la aplicación (`base` de Vite); sin prefijo es una cadena vacía.\nconst baseDelBundler: string = (import.meta as ImportMeta & { env?: Record<string, string | undefined> }).env?.BASE_URL ?? '/';\n\nexport const rutaBase: string = baseDelBundler.replace(/\\/+$/, '');\n","// frontend-plus-react/src/App.tsx (modificado en tu librería)\n\nimport React, { useEffect, useMemo } from 'react';\nimport './App.css';\nimport { BrowserRouter, Routes, Route, useLocation, Navigate, Outlet } from 'react-router-dom';\nimport MainLayout from './components/MainLayout';\nimport SessionExpiredMessage from './components/SessionExpiredMessage';\nimport { AppProvider } from './contexts/AppContext';\nimport PantallaInicial from './pages/PantallaInicial';\nimport LogoutPage from './pages/LogoutPage';\nimport LoginPage from './pages/LoginPage';\nimport ProcedureForm from './components/ProcedureForm';\n\nimport { Provider } from 'react-redux';\nimport { PersistGate } from 'redux-persist/integration/react';\nimport { store, persistor } from './store';\n\nimport PrivateRoute from './components/PrivateRoute';\nimport InitialRedirectHandler from './components/InitialRedirectHandler';\nimport { useAppDispatch } from './store';\nimport { setCurrentPath } from './store/routerSlice';\n\nimport { SnackbarProvider } from './contexts/SnackbarContext';\nimport GenericDataGridPage from './pages/GenericDataGridPage';\nimport { PantallaDeRuta } from './pages/PantallaDeRuta';\nimport MenuTablePage from './pages/MenuTablePage';\n\nimport { wScreens, extendWScreens } from './pages/WScreens';\nimport { PantallaDeRespaldo, PaginaBP } from './puente/PantallaBP';\nimport { PaginaGrillaBP } from './puente/GrillaBP';\nimport { CapturaBP } from './puente/CapturaBP';\nimport { habilitarPuenteBP, puenteBPHabilitado } from './puente/habilitacion';\nimport { extendClientSides } from './components/grid/clientSides';\nimport { extendConditions } from './components/grid/condiciones';\nimport { extendResultsOk } from './pages/procedure-results/resultsOk';\nimport typeStore from 'type-store';\n\nimport {\n extractPathsFromRoutes,\n AppProps,\n WScreenMap,\n ClientSidesMap,\n ResultsOksMap,\n ClientSideProps,\n ResultOkProps\n} from './types';\n\n\nimport { envConfig } from './env';\nimport { rutaBase } from './rutaBase';\n\nconst LocationTracker: React.FC = () => {\n const location = useLocation();\n const dispatch = useAppDispatch();\n useEffect(() => {\n dispatch(setCurrentPath(location.pathname + location.search));\n }, [location, dispatch]);\n return null;\n};\n\ninterface RoutesProps {\n myRoutes?: React.ReactNode;\n myUnloggedRoutes?: React.ReactNode;\n layout?: React.ComponentType;\n unloggedLayout?: React.ComponentType;\n}\n\nexport function FrontendPlusReactRoutes({ myRoutes, myUnloggedRoutes, layout: Layout = MainLayout, unloggedLayout: UnloggedLayout }: RoutesProps) {\n return (\n <>\n {/* Fuera de las rutas privadas: con la sesión válida pero el estado todavía sin cargar, PrivateRoute manda\n a /login; desde acá se vuelve a la ruta guardada (o a la pantalla de inicio) apenas el estado termina de cargarse. */}\n <InitialRedirectHandler />\n {puenteBPHabilitado() && <CapturaBP />}\n <Routes>\n <Route element={UnloggedLayout ? <UnloggedLayout /> : <Outlet />}>\n <Route path=\"/login\" element={<LoginPage />} />\n <Route path=\"/logout\" element={<LogoutPage />} />\n {myUnloggedRoutes}\n </Route>\n\n <Route element={\n <PrivateRoute>\n <Layout />\n </PrivateRoute>\n }>\n {/* la pantalla de inicio la define el backend (selectedByDefault), como en backend-plus */}\n <Route path=\"/\" element={<PantallaInicial />} />\n <Route path=\"/table/:tableName\" element={<GenericDataGridPage />} />\n <Route path=\"/menu/:menuName\" element={<MenuTablePage />} />\n <Route path=\"/procedures/:procedureName\" element={<ProcedureForm />} />\n {Object.keys(wScreens).map((screenName) => (\n <Route\n key={screenName}\n path={`/wScreens/${screenName}`}\n element={<PantallaDeRuta screenName={screenName} />}\n />\n ))}\n <Route path=\"/wScreens-fallback/:screenName\" element={<PantallaDeRespaldo />} />\n {puenteBPHabilitado() && <Route path=\"/bp/menu\" element={<PaginaBP />} />}\n {puenteBPHabilitado() && <Route path=\"/bp/table/:tableName\" element={<PaginaGrillaBP />} />}\n\n {myRoutes}\n\n <Route path=\"*\" element={<div style={{ marginTop: '20px', marginLeft: \"10px\" }}>404 - Recurso No Encontrado</div>} />\n </Route>\n\n <Route path=\"*\" element={<Navigate to=\"/\" replace />} />\n </Routes>\n </>\n );\n}\n\n\n// Los tipos WScreenMap, ClientSidesMap, ResultsOksMap y AppProps ahora se importan de ./types\n\n\nexport const FrontendPlusProviders: React.FC<{ children: React.ReactNode, publicPaths: string[] }> = ({ children, publicPaths }) => {\n return (\n <Provider store={store}>\n <PersistGate loading={null} persistor={persistor}>\n <BrowserRouter basename={rutaBase}>\n <LocationTracker /> {/* El tracker necesita estar dentro del Router */}\n <AppProvider publicPaths={publicPaths}>\n <SnackbarProvider>\n {children}\n <SessionExpiredMessage />\n </SnackbarProvider>\n </AppProvider>\n </BrowserRouter>\n </PersistGate>\n </Provider>\n );\n};\n\nconst App = ({\n myRoutes,\n myUnloggedRoutes,\n unloggedLayout,\n myWScreens,\n myClientSides,\n myResultsOk,\n myConditions,\n puenteBP\n}: AppProps & {\n /** Las wScreens que la aplicación no registró se piden al cliente de backend-plus (mismo origen que el backend) */\n puenteBP?: boolean\n}) => {\n // Lo que aporta la aplicación se registra antes del primer render (y no en un efecto): las rutas de las wScreens\n // se arman a partir del registro al renderizar, y un detalle puede necesitar una condición o una wScreen enseguida.\n useMemo(() => {\n if (myClientSides) {\n extendClientSides(myClientSides);\n }\n if (myResultsOk) {\n extendResultsOk(myResultsOk);\n }\n if (myWScreens) {\n extendWScreens(myWScreens);\n }\n if (myConditions) {\n extendConditions(myConditions);\n }\n if (puenteBP) {\n habilitarPuenteBP();\n }\n }, [myClientSides, myResultsOk, myWScreens, myConditions, puenteBP]);\n\n useEffect(() => {\n // --- DINAMICO: Configuración de TypeStore en español ---\n typeStore.messages = typeStore.i18n.messages.es;\n typeStore.locale = typeStore.i18n.locale.es;\n\n // --- DINAMICO: Favicon desde el Backend ---\n const link = (document.querySelector(\"link[rel*='icon']\") || document.createElement('link')) as HTMLLinkElement;\n link.type = 'image/png';\n link.rel = 'shortcut icon';\n link.href = `${envConfig.backendUrl}/img/logo-128.png`;\n document.getElementsByTagName('head')[0].appendChild(link);\n }, []);\n\n const safePaths = React.useMemo(() => {\n const extracted = extractPathsFromRoutes(myUnloggedRoutes);\n // Siempre añadimos login y logout como rutas base seguras\n return ['/login', '/logout', ...extracted];\n }, [myUnloggedRoutes]);\n return (\n <FrontendPlusProviders publicPaths={safePaths}>\n <FrontendPlusReactRoutes\n myRoutes={myRoutes}\n myUnloggedRoutes={myUnloggedRoutes}\n unloggedLayout={unloggedLayout} // Pasamos el layout\n />\n </FrontendPlusProviders>\n );\n}\n\nexport default App;","// src/components/MainLayout.tsx\r\nimport React, { useEffect } from 'react';\r\nimport { Outlet } from 'react-router-dom'; // ¡IMPORTA Outlet AQUÍ!\r\nimport {\r\n Box, AppBar, Toolbar, Typography, Button, Drawer, CssBaseline,\r\n IconButton, useTheme, useMediaQuery\r\n} from '@mui/material';\r\nimport { useApp } from '../contexts/AppContext';\r\nimport { useNavigate } from 'react-router-dom'; // No estoy seguro si 'useNavigate' es necesario aquí si solo se usa en SideMenu o para Logout\r\nimport LogoutIcon from '@mui/icons-material/Logout';\r\nimport MenuIcon from '@mui/icons-material/Menu';\r\nimport ChevronLeftIcon from '@mui/icons-material/ChevronLeft';\r\nimport SideMenu from './SideMenu';\r\nimport useLogout from '../hooks/useLogout';\r\nimport { envConfig } from '../env';\r\n\r\n// Importamos los hooks de Redux y las acciones del nuevo slice\r\nimport { useAppDispatch, useIsDrawerOpen } from '../store';\r\nimport { setDrawerOpen, toggleDrawer } from '../store/menuUiSlice';\r\n\r\n\r\nconst drawerWidth = 240;\r\n\r\n// const MainLayout: React.FC<MainLayoutProps> = ({ children }) => { // Quitamos { children }\r\nconst MainLayout: React.FC = () => { // MainLayout ya no recibe 'children' como prop\r\n const { clientContext } = useApp();\r\n const logout = useLogout();\r\n\r\n const isDrawerOpen = useIsDrawerOpen();\r\n const dispatch = useAppDispatch();\r\n\r\n const theme = useTheme();\r\n const isMobile = useMediaQuery(theme.breakpoints.down('sm'));\r\n\r\n useEffect(() => {\r\n if (isMobile && isDrawerOpen) {\r\n dispatch(setDrawerOpen(false));\r\n }\r\n }, [isMobile, isDrawerOpen, dispatch]);\r\n\r\n const handleToggleDrawer = () => {\r\n dispatch(toggleDrawer());\r\n };\r\n\r\n const handleDrawerCloseMobile = () => {\r\n if (isMobile && isDrawerOpen) {\r\n dispatch(setDrawerOpen(false));\r\n }\r\n };\r\n\r\n // Si clientContext no existe (por ejemplo, al cargar o si la sesión falla),\r\n // muestra un mensaje de carga.\r\n // Esto es importante para evitar errores si los datos no están disponibles.\r\n if (!clientContext) {\r\n return (\r\n <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>\r\n <Typography>Cargando diseño de la aplicación...</Typography>\r\n </Box>\r\n );\r\n }\r\n\r\n return (\r\n <Box sx={{ display: 'flex', height: '100vh' }}>\r\n <CssBaseline />\r\n <AppBar\r\n position=\"fixed\"\r\n sx={{\r\n zIndex: (theme) => theme.zIndex.drawer + 1,\r\n transition: (theme) => theme.transitions.create(['width', 'margin'], {\r\n easing: theme.transitions.easing.sharp,\r\n duration: theme.transitions.duration.leavingScreen,\r\n }),\r\n ...(isDrawerOpen && {\r\n width: `calc(100% - ${drawerWidth}px)`,\r\n marginLeft: `${drawerWidth}px`,\r\n transition: (theme) => theme.transitions.create(['width', 'margin'], {\r\n easing: theme.transitions.easing.easeOut,\r\n duration: theme.transitions.duration.enteringScreen,\r\n }),\r\n }),\r\n }}\r\n >\r\n <Toolbar>\r\n <IconButton\r\n color=\"inherit\"\r\n aria-label=\"open drawer\"\r\n onClick={handleToggleDrawer}\r\n edge=\"start\"\r\n sx={{\r\n marginRight: 2,\r\n }}\r\n >\r\n {isDrawerOpen ? <ChevronLeftIcon /> : <MenuIcon />}\r\n </IconButton>\r\n {!isDrawerOpen && (\r\n <img\r\n src={`${envConfig.backendUrl}/img/logo-128.png`}\r\n alt=\"logo\"\r\n style={{ height: '32px', marginRight: '12px', objectFit: 'contain' }}\r\n />\r\n )}\r\n <Typography variant=\"h6\" noWrap component=\"div\" sx={{ flexGrow: 1 }}>\r\n {isDrawerOpen ? '' : clientContext.config.title}\r\n </Typography>\r\n <Typography variant=\"subtitle1\" component=\"div\" sx={{ mr: 2 }}>\r\n {clientContext.username}\r\n </Typography>\r\n <Button\r\n color=\"inherit\"\r\n onClick={logout}\r\n startIcon={<LogoutIcon />}\r\n >\r\n Salir\r\n </Button>\r\n </Toolbar>\r\n </AppBar>\r\n <Drawer\r\n variant=\"persistent\"\r\n anchor=\"left\"\r\n open={isDrawerOpen}\r\n sx={{\r\n width: drawerWidth,\r\n flexShrink: 0,\r\n '& .MuiDrawer-paper': {\r\n width: drawerWidth,\r\n boxSizing: 'border-box',\r\n overflowX: 'hidden',\r\n overflowY: 'auto',\r\n },\r\n }}\r\n >\r\n <Toolbar\r\n sx={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'flex-start',\r\n px: 1,\r\n fontSize: '1.3rem',\r\n minHeight: theme.mixins.toolbar.minHeight,\r\n gap: 1.5\r\n }}\r\n >\r\n <img\r\n src={`${envConfig.backendUrl}/img/logo-128.png`}\r\n alt=\"logo\"\r\n style={{ height: '32px', objectFit: 'contain' }}\r\n />\r\n <Typography variant=\"h6\" sx={{ fontWeight: 'bold' }}>\r\n {clientContext.config.title}\r\n </Typography>\r\n </Toolbar>\r\n {/* Pasamos la función para cerrar el drawer al SideMenu */}\r\n <SideMenu onMenuItemClick={handleDrawerCloseMobile} />\r\n </Drawer>\r\n <Box\r\n component=\"main\"\r\n sx={{\r\n flexGrow: 1,\r\n transition: (theme) => theme.transitions.create('margin', {\r\n easing: theme.transitions.easing.sharp,\r\n duration: theme.transitions.duration.leavingScreen,\r\n }),\r\n marginLeft: `-${drawerWidth}px`,\r\n ...(isDrawerOpen && {\r\n transition: (theme) => theme.transitions.create('margin', {\r\n easing: theme.transitions.easing.easeOut,\r\n duration: theme.transitions.duration.enteringScreen,\r\n }),\r\n marginLeft: 0,\r\n }),\r\n minWidth: 0,\r\n flexShrink: 0,\r\n height: '100%',\r\n boxSizing: 'border-box',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n width: isDrawerOpen ? `calc(100% - ${drawerWidth}px)` : '100%',\r\n }}\r\n >\r\n {/* ¡AQUÍ ES DONDE DEBE IR EL CONTENIDO DE LA RUTA ANIDADA! */}\r\n {/* Elimina {children} y reemplázalo por <Outlet /> */}\r\n {/* El lugar de la AppBar (mide lo mismo que ella en cualquier tamaño de pantalla) queda fuera del scroll:\r\n si el contenido scrolleara desde el borde de la ventana, la barra taparía el extremo de la barra de desplazamiento. */}\r\n <Toolbar />\r\n <Box sx={{ flex: 1, minHeight: 0, overflowY: 'auto', display: 'flex', flexDirection: 'column' }}>\r\n <Outlet />\r\n </Box>\r\n </Box>\r\n </Box>\r\n );\r\n};\r\n\r\nexport default MainLayout;","// src/contexts/AppContext.tsx\r\nimport React, { createContext, useContext, useState, useEffect, ReactNode, useCallback, useMemo, useRef } from 'react';\r\nimport { fetchApi } from '../utils/fetchApi';\r\nimport { useLocation, useNavigate } from 'react-router-dom';\r\n\r\n// Importa los hooks de Redux y las acciones de tu slice\r\nimport { useSelector, useDispatch } from 'react-redux';\r\nimport { RootState, AppDispatch } from '../store'; // Asegúrate de que la ruta sea correcta\r\nimport { fetchClientContext, clearClientContext } from '../store/clientContextSlice'; // Importa el thunk y la acción\r\nimport { AppContextType, AppProviderProps } from '../types';\r\n\r\n// Crea el contexto con un valor por defecto que se sobrescribirá\r\nconst AuthContext = createContext<AppContextType | undefined>(undefined);\r\n\r\n// Hook personalizado para usar el contexto de autenticación\r\nexport const useApp = () => {\r\n const context = useContext(AuthContext);\r\n if (!context) {\r\n throw new Error('useApp must be used within an AppProvider');\r\n }\r\n // Si quieres que 'useApp' siga devolviendo 'clientContext', lo obtendrías aquí de Redux:\r\n const clientContext = useSelector((state: RootState) => state.clientContext);\r\n return { ...context, clientContext };\r\n};\r\n\r\nexport const AppProvider: React.FC<AppProviderProps> = ({ children, publicPaths=[] }) => {\r\n console.log(\"AppProvider rendered/re-rendered\");\r\n\r\n const [isLoggedIn, setIsLoggedIn] = useState(false);\r\n const [showSessionExpiredMessage, setShowSessionExpiredMessage] = useState(false);\r\n\r\n const isRedirectingRef = useRef(false);\r\n // ¿Esta persona tuvo una sesión (en esta carga de la página o guardada de antes)? Solo entonces corresponde\r\n // avisar que \"la sesión expiró\"; quien entra por primera vez sin sesión va al login sin ese cartel.\r\n const huboSesionRef = useRef(false);\r\n \r\n \r\n \r\n const dispatch = useDispatch<AppDispatch>();\r\n const clientContext = useSelector((state: RootState) => state.clientContext);\r\n // si lo guardado de la visita anterior decía que había sesión, vencerse es un \"expiró\" y no una primera visita\r\n if (clientContext.status === 'succeeded') huboSesionRef.current = true;\r\n \r\n const navigate = useNavigate();\r\n\r\n const handleExpiredSessionRedirect = useCallback(() => {\r\n // A. Activamos el bloqueo inmediatamente\r\n isRedirectingRef.current = true;\r\n \r\n // B. Cerramos modal y navegamos\r\n setShowSessionExpiredMessage(false);\r\n setIsLoggedIn(false);\r\n navigate('/login', { replace: true });\r\n\r\n // C. (Opcional) Desbloqueamos después de un tiempo prudencial por si el usuario vuelve\r\n setTimeout(() => {\r\n isRedirectingRef.current = false;\r\n }, 2000);\r\n }, [navigate]);\r\n\r\n const location = useLocation();\r\n\r\n const isCurrentPathPublic = useCallback(() => {\r\n const current = location.pathname;\r\n // Verificamos si la ruta actual está en la lista de permitidas\r\n // Usamos startsWith para manejar sub-rutas si fuera necesario, o coincidencia exacta\r\n return publicPaths.some(path => current === path || current.startsWith(`${path}/`));\r\n }, [location.pathname, publicPaths]);\r\n\r\n // Memoizamos checkSession. Ahora despachará acciones de Redux.\r\n const checkSession = useCallback(async () => {\r\n if (isRedirectingRef.current) return;\r\n try {\r\n const response = await fetchApi('/keep-alive.json', { method: 'GET' });\r\n if (isRedirectingRef.current) return;\r\n if (response.ok) {\r\n huboSesionRef.current = true;\r\n setIsLoggedIn(true);\r\n setShowSessionExpiredMessage(false);\r\n\r\n // Si clientContext no está cargado o si hubo un error previo,\r\n // despachamos el thunk para cargarlo.\r\n // Con redux-persist, si ya estaba en localStorage, 'clientContext.status' será 'succeeded'.\r\n if (clientContext.status === 'idle' || clientContext.status === 'failed') {\r\n console.log(\"Client context needs loading/reloading, dispatching fetchClientContext thunk...\");\r\n try {\r\n // .unwrap() para que el 'catch' capture los errores del thunk\r\n await dispatch(fetchClientContext()).unwrap();\r\n console.log(\"Client context data loaded successfully by Redux.\");\r\n } catch (loadError) {\r\n console.error(\"Error loading client context data during session check:\", loadError);\r\n // No es necesario lanzar el error, el estado de Redux ya lo manejará\r\n }\r\n }\r\n } else {\r\n // Si la sesión no es válida, limpiamos el estado de autenticación y el clientContext en Redux\r\n setIsLoggedIn(false);\r\n dispatch(clearClientContext()); // Disparar la acción para limpiar el clientContext en Redux\r\n\r\n if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current) {\r\n console.warn(\"Sesión inválida en ruta protegida. Mostrando modal.\");\r\n setShowSessionExpiredMessage(true);\r\n } else {\r\n console.log(\"Sesión inválida, pero no hay nada que avisar (ruta pública o nunca hubo sesión).\");\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\"Error checking session:\", error);\r\n setIsLoggedIn(false);\r\n dispatch(clearClientContext()); // Limpiar clientContext en Redux\r\n const isPublic = publicPaths.some(p => location.pathname === p || location.pathname.startsWith(p + '/'));\r\n if (!showSessionExpiredMessage && !isCurrentPathPublic() && huboSesionRef.current) {\r\n setShowSessionExpiredMessage(true);\r\n }\r\n }\r\n }, [dispatch, clientContext.status, showSessionExpiredMessage, isCurrentPathPublic]); // Dependencias: dispatch (estable), y el estado de carga del clientContext\r\n\r\n useEffect(() => {\r\n checkSession();\r\n\r\n const intervalId = setInterval(checkSession, 15 * 1000);\r\n\r\n return () => {\r\n clearInterval(intervalId);\r\n };\r\n }, [checkSession]); // Se ejecuta una vez al montar, y se re-ejecuta si checkSession cambia (no debería si useCallback está bien)\r\n\r\n // ¡La CLAVE! Memoizamos el objeto de contexto.\r\n // clientContext y setClientContext ya NO van aquí, se acceden directamente vía Redux.\r\n const authContextValue = useMemo(() => ({\r\n isLoggedIn,\r\n setIsLoggedIn,\r\n checkSession,\r\n showSessionExpiredMessage,\r\n setShowSessionExpiredMessage,\r\n handleExpiredSessionRedirect\r\n }), [\r\n isLoggedIn,\r\n setIsLoggedIn,\r\n checkSession,\r\n showSessionExpiredMessage,\r\n setShowSessionExpiredMessage,\r\n handleExpiredSessionRedirect\r\n ]);\r\n\r\n return (\r\n <AuthContext.Provider value={authContextValue}>\r\n {children}\r\n </AuthContext.Provider>\r\n );\r\n};","import React, { useEffect } from 'react';\r\nimport { List, ListItem, ListItemButton, ListItemIcon, ListItemText, Divider, Box, Typography, Collapse } from '@mui/material';\r\nimport ExpandLess from '@mui/icons-material/ExpandLess';\r\nimport ExpandMore from '@mui/icons-material/ExpandMore';\r\nimport TableChartIcon from '@mui/icons-material/TableChart';\r\nimport FolderIcon from '@mui/icons-material/Folder';\r\nimport DnsIcon from '@mui/icons-material/Dns';\r\nimport WarningAmberIcon from '@mui/icons-material/WarningAmber';\r\nimport DesktopWindowsIcon from '@mui/icons-material/DesktopWindows';\r\nimport UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';\r\nimport UnfoldLessIcon from '@mui/icons-material/UnfoldLess';\r\n\r\nimport { useNavigate, useLocation } from 'react-router-dom';\r\nimport { useApp } from '../contexts/AppContext';\r\nimport { MenuInfoBase, MenuInfoTable, MenuInfoProc, MenuInfoMenu } from \"backend-plus\";\r\nimport { blue, orange, teal, red, grey } from '@mui/material/colors';\r\n\r\nimport { useSubMenuOpenState, useAppDispatch, useAppSelector } from '../store';\r\nimport { toggleSubMenu, setAllSubMenusOpen, setSubMenuOpen } from '../store/menuUiSlice';\r\nimport { MenuListItemProps, SideMenuProps } from '../types';\r\n\r\nimport { wScreens } from '../pages/WScreens';\r\nimport { rutaDeItemDeMenu } from '../utils/menuInicial';\r\n\r\nconst MenuListItem: React.FC<MenuListItemProps> = ({ item, level, onMenuItemClick }) => {\r\n const navigate = useNavigate();\r\n const location = useLocation();\r\n\r\n const open = useSubMenuOpenState(item.name);\r\n const dispatch = useAppDispatch();\r\n\r\n const handleClick = () => {\r\n if (item.menuType === \"menu\") {\r\n dispatch(toggleSubMenu(item.name));\r\n } else {\r\n navigate(rutaDeItemDeMenu(item));\r\n if (onMenuItemClick) {\r\n onMenuItemClick();\r\n }\r\n }\r\n };\r\n\r\n const getIcon = (item: MenuInfoBase) => {\r\n switch (item.menuType) {\r\n case \"table\":\r\n return <TableChartIcon sx={{ color: blue[700] }} />;\r\n case \"proc\":\r\n return <DnsIcon sx={{ color: teal[700] }} />;\r\n case \"menu\":\r\n return <FolderIcon sx={{ color: orange[800] }} />;\r\n default:\r\n const WScreenComponent = wScreens[item.menuType];\r\n if (WScreenComponent !== undefined) {\r\n return <DesktopWindowsIcon sx={{ color: grey[700] }} />;\r\n }\r\n return <WarningAmberIcon sx={{ color: red[500] }} />;\r\n }\r\n };\r\n\r\n const currentPath = location.pathname;\r\n let isSelected = false;\r\n\r\n // Obtener tableName de la URL si existe\r\n const pathParts = currentPath.split('/');\r\n const urlTableName = pathParts[1] === 'table' ? pathParts[2] : null;\r\n\r\n if (item.menuType === \"table\") {\r\n // Seleccionado si coincide la ruta de menú o la ruta directa de tabla\r\n isSelected = currentPath === `/menu/${item.name}` || (urlTableName === (item.table || item.name));\r\n } else if (item.menuType === \"proc\") {\r\n isSelected = currentPath === `/procedures/${item.name}`;\r\n } else if (item.menuType !== \"menu\") {\r\n isSelected = currentPath.startsWith(`/wScreens/${item.menuType}`) || currentPath.startsWith(`/wScreens-fallback/${item.menuType}`);\r\n }\r\n\r\n // SCROLL AUTOMÁTICO: Cuando se selecciona un ítem, lo traemos a la vista\r\n const itemRef = React.useRef<HTMLDivElement>(null);\r\n useEffect(() => {\r\n if (isSelected && itemRef.current) {\r\n itemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\r\n }\r\n }, [isSelected]);\r\n\r\n return (\r\n <div ref={itemRef}>\r\n <ListItemButton\r\n onClick={handleClick}\r\n sx={{ pl: level * 2 }}\r\n selected={isSelected}\r\n >\r\n <ListItemIcon sx={{ minWidth: '38px' }}>\r\n {getIcon(item)}\r\n </ListItemIcon>\r\n <ListItemText primary={item.label || item.name} />\r\n {item.menuType === \"menu\" ? (open ? <ExpandLess /> : <ExpandMore />) : null}\r\n </ListItemButton>\r\n {item.menuType === \"menu\" && (\r\n <Collapse in={open} timeout=\"auto\" unmountOnExit>\r\n <List component=\"div\" disablePadding>\r\n {(item as MenuInfoMenu).menuContent.map((subItem) => (\r\n <MenuListItem\r\n key={subItem.name}\r\n item={subItem}\r\n level={level + 1}\r\n onMenuItemClick={onMenuItemClick}\r\n />\r\n ))}\r\n </List>\r\n </Collapse>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\n\r\nconst SideMenu: React.FC<SideMenuProps> = ({ onMenuItemClick }) => {\r\n\r\n const { clientContext } = useApp();\r\n const navigate = useNavigate();\r\n const location = useLocation();\r\n const dispatch = useAppDispatch();\r\n\r\n const subMenuOpenStates = useAppSelector(state => state.menuUi.subMenuOpenStates);\r\n\r\n const allSubMenusKeys = Object.keys(subMenuOpenStates);\r\n const openSubMenusCount = allSubMenusKeys.filter(key => subMenuOpenStates[key]).length;\r\n const isMostlyOpen = allSubMenusKeys.length > 0 && openSubMenusCount / allSubMenusKeys.length > 0.5;\r\n\r\n const handleToggleAllSubMenus = () => {\r\n dispatch(setAllSubMenusOpen(!isMostlyOpen));\r\n };\r\n\r\n if (!clientContext || !clientContext.menu) {\r\n return (\r\n <Box sx={{ p: 2, textAlign: 'center' }}>\r\n <Typography variant=\"body2\" color=\"text.secondary\">Cargando menú...</Typography>\r\n </Box>\r\n );\r\n }\r\n\r\n // AUTO-EXPANSIÓN: Expandir padres según la ruta actual\r\n useEffect(() => {\r\n if (!clientContext?.menu) return;\r\n\r\n const expandParents = (items: MenuInfoBase[], targetPath: string, parentNames: string[] = []): boolean => {\r\n const pathParts = targetPath.split('/');\r\n const urlTableName = pathParts[1] === 'table' ? pathParts[2] : null;\r\n\r\n for (const item of items) {\r\n let matches = false;\r\n if (item.menuType === \"table\") {\r\n matches = targetPath === `/menu/${item.name}` || (urlTableName === (item.table || item.name));\r\n } else if (item.menuType === \"proc\") {\r\n matches = targetPath === `/procedures/${item.name}`;\r\n } else if (item.menuType !== \"menu\") {\r\n matches = targetPath.startsWith(`/wScreens/${item.menuType}`) || targetPath.startsWith(`/wScreens-fallback/${item.menuType}`);\r\n }\r\n\r\n if (matches) {\r\n // Encontramos el ítem, expandimos todos sus padres\r\n parentNames.forEach(name => {\r\n dispatch(setSubMenuOpen({ menuName: name, isOpen: true }));\r\n });\r\n return true;\r\n }\r\n\r\n if (item.menuType === \"menu\") {\r\n const found = expandParents((item as MenuInfoMenu).menuContent, targetPath, [...parentNames, item.name]);\r\n if (found) return true;\r\n }\r\n }\r\n return false;\r\n };\r\n\r\n expandParents(clientContext.menu, location.pathname);\r\n }, [location.pathname, clientContext, dispatch]);\r\n\r\n return (\r\n <Box sx={{ width: '100%', flexShrink: 0 }}>\r\n {clientContext.menu.some(item => item.menuType === \"menu\") && (\r\n <ListItem disablePadding>\r\n <ListItemButton onClick={handleToggleAllSubMenus}>\r\n <ListItemIcon sx={{ minWidth: '38px' }}>\r\n {isMostlyOpen ? <UnfoldLessIcon /> : <UnfoldMoreIcon />}\r\n </ListItemIcon>\r\n <ListItemText primary={isMostlyOpen ? \"colapsar \" : \"expandir\"} />\r\n </ListItemButton>\r\n </ListItem>\r\n )}\r\n <Divider />\r\n <List>\r\n {clientContext.menu.map((menuItem: MenuInfoBase) => (\r\n <MenuListItem\r\n key={menuItem.name}\r\n item={menuItem}\r\n level={1}\r\n onMenuItemClick={onMenuItemClick}\r\n />\r\n ))}\r\n </List>\r\n </Box>\r\n );\r\n};\r\n\r\nexport default SideMenu;","// La pantalla de inicio la define el backend, como en backend-plus: es el ítem de menú con `selectedByDefault`.\nimport { MenuInfoBase, MenuInfoMenu } from 'backend-plus';\nimport { wScreens } from '../pages/WScreens';\n\n/**\n * La ruta que muestra un ítem de menú: las tablas van a `/menu/:nombre` (con `td` y `fc` en la query string; los\n * `fixedFields` los toma la página del propio menú), los procedimientos a `/procedures/:nombre` y lo demás son wScreens.\n */\nexport function rutaDeItemDeMenu(item: MenuInfoBase): string {\n if (item.menuType === 'table') {\n const consulta = new URLSearchParams();\n if (item.td) consulta.append('td', JSON.stringify(item.td));\n if (item.fc) consulta.append('fc', JSON.stringify(item.fc));\n return `/menu/${item.name}${consulta.size ? `?${consulta.toString()}` : ''}`;\n }\n if (item.menuType === 'proc') return `/procedures/${item.name}`;\n if (wScreens[item.menuType] !== undefined) return `/wScreens/${item.menuType}`;\n console.warn(`Menu type '${item.menuType}' no reconocido o WScreen no mapeada.`);\n return `/wScreens-fallback/${item.menuType}`;\n}\n\n/**\n * El ítem que se muestra al entrar: como `my-menu.js`, el que declara `selectedByDefault` y, si es un grupo, el que\n * declare así adentro. Si el backend no declara ninguno (o el grupo no tiene uno por omisión) no hay pantalla de inicio.\n */\nexport function itemInicial(menu: MenuInfoBase[]): MenuInfoBase | undefined {\n const elegido = menu.find(item => item.selectedByDefault);\n if (!elegido) return undefined;\n return elegido.menuType === 'menu' ? itemInicial((elegido as MenuInfoMenu).menuContent) : elegido;\n}\n","// src/hooks/useLogout.ts\r\nimport { useCallback } from 'react';\r\nimport { useNavigate } from 'react-router-dom';\r\n\r\n// Importa los hooks de Redux y la acción clearClientContext\r\nimport { useDispatch } from 'react-redux';\r\nimport { AppDispatch } from '../store'; // Asegúrate de que la ruta sea correcta\r\nimport { clearClientContext } from '../store/clientContextSlice'; // Importa la acción para limpiar el contexto\r\n\r\nimport { useApp } from '../contexts/AppContext'; // Para acceder a setIsLoggedIn\r\nimport { fetchApi } from '../utils/fetchApi'; // Para la llamada a la API\r\n\r\n/**\r\n * Hook personalizado para manejar la lógica de cierre de sesión.\r\n * Limpia el estado del frontend y realiza la petición al backend.\r\n * @returns Una función 'logout' que, al ser llamada, cierra la sesión.\r\n */\r\nconst useLogout = () => {\r\n // Obtenemos las funciones del contexto de la aplicación\r\n const { setIsLoggedIn } = useApp(); // setClientContext ya no viene de useApp()\r\n\r\n // Obtenemos el dispatch de Redux\r\n const dispatch = useDispatch<AppDispatch>();\r\n\r\n // Obtenemos la función de navegación de React Router\r\n const navigate = useNavigate();\r\n\r\n // Utilizamos useCallback para memoizar la función 'logout'\r\n const logout = useCallback(async () => {\r\n try {\r\n const response = await fetchApi('/logout', { method: 'GET' }); // Asumiendo que tienes un endpoint /logout\r\n \r\n // Puedes ajustar esta lógica de verificación de respuesta\r\n // Si el backend siempre devuelve 401 para un logout exitoso (lo cual es inusual,\r\n // normalmente es un 200 OK y luego el backend invalida la sesión),\r\n // entonces tu lógica actual es válida.\r\n if (response.status === 401 || response.ok) { // Considera response.ok también si el backend da 200\r\n console.log('Sesión cerrada correctamente o ya deslogueado');\r\n } else {\r\n console.error('Error al cerrar sesión:', await response.text());\r\n // Si el logout falla en el backend, podrías decidir no limpiar el estado local\r\n // o mostrar un error al usuario. Por ahora, lo limpiamos igual.\r\n }\r\n } catch (error) {\r\n console.error('Error de red al intentar cerrar sesión:', error);\r\n } finally {\r\n // Siempre limpiamos el estado de login en el frontend,\r\n // independientemente del éxito o fracaso de la petición al backend.\r\n setIsLoggedIn(false); // Actualiza el estado de login en el contexto\r\n \r\n // --- ¡CAMBIO CLAVE AQUÍ! ---\r\n // Despacha la acción de Redux para limpiar el clientContext\r\n dispatch(clearClientContext()); \r\n\r\n navigate('/login', { replace: true }); // Redirige a /login\r\n }\r\n }, [setIsLoggedIn, dispatch, navigate]); // Dependencias: setIsLoggedIn, dispatch (estable), navigate (estable)\r\n\r\n return logout; // Devolvemos la función 'logout'\r\n};\r\n\r\nexport default useLogout;","import React from 'react';\r\nimport { Box, Typography, Button, Modal } from '@mui/material';\r\nimport { useApp } from '../contexts/AppContext'; // Importa useApp para el control del modal\r\nimport { useNavigate } from 'react-router-dom';\r\n\r\nconst style = {\r\n position: 'absolute' as 'absolute',\r\n top: '50%',\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n width: 400,\r\n bgcolor: 'background.paper',\r\n border: '2px solid #000',\r\n boxShadow: 24,\r\n p: 4,\r\n textAlign: 'center',\r\n borderRadius: 2,\r\n};\r\n\r\nconst SessionExpiredMessage: React.FC = () => {\r\n const { showSessionExpiredMessage, setShowSessionExpiredMessage, setIsLoggedIn, handleExpiredSessionRedirect } = useApp();\r\n const handleClose = () => {\r\n handleExpiredSessionRedirect(); \r\n };\r\n\r\n return (\r\n <Modal\r\n open={showSessionExpiredMessage}\r\n // Proporciona una función vacía para onClose. Esto satisface el tipo de prop\r\n // y evita que el modal se cierre por defecto al hacer clic en el backdrop.\r\n onClose={() => {}} \r\n aria-labelledby=\"session-expired-modal-title\"\r\n aria-describedby=\"session-expired-modal-description\"\r\n disableEscapeKeyDown={true} // Impide cerrar con la tecla Escape\r\n slotProps={{\r\n backdrop: {\r\n // Esto es crucial en MUI v5: detiene la propagación del evento de clic en el backdrop,\r\n // impidiendo que el modal se cierre al hacer clic fuera.\r\n onClick: (event) => event.stopPropagation(),\r\n sx: {\r\n backgroundColor: 'rgba(100, 100, 100, 0.95)', // Ajusta el color y la opacidad para un fondo más oscuro\r\n },\r\n },\r\n }}\r\n >\r\n <Box sx={style}>\r\n <Typography id=\"session-expired-modal-title\" variant=\"h6\" component=\"h2\" gutterBottom>\r\n ¡Sesión Expirada!\r\n </Typography>\r\n <Typography id=\"session-expired-modal-description\" sx={{ mt: 2, mb: 3 }}>\r\n Tu sesión ha expirado por inactividad o por seguridad. Por favor, inicia sesión de nuevo.\r\n </Typography>\r\n <Button variant=\"contained\" color=\"primary\" onClick={handleClose}>\r\n Ir al Login\r\n </Button>\r\n </Box>\r\n </Modal>\r\n );\r\n};\r\n\r\nexport default SessionExpiredMessage;\r\n","import React from 'react';\nimport { Navigate } from 'react-router-dom';\nimport { useApp } from '../contexts/AppContext';\nimport { itemInicial, rutaDeItemDeMenu } from '../utils/menuInicial';\n\n/** Ruta `/`: va a la pantalla de inicio que declara el backend (`selectedByDefault`). Si no declara ninguna, queda vacía, como en backend-plus. */\nconst PantallaInicial: React.FC = () => {\n const { clientContext } = useApp();\n const item = clientContext.menu ? itemInicial(clientContext.menu) : undefined;\n return item ? <Navigate to={rutaDeItemDeMenu(item)} replace /> : null;\n};\n\nexport default PantallaInicial;\n","// src/pages/LogoutPage.tsx\r\nimport React, { useEffect } from 'react';\r\nimport { Box, Typography, CircularProgress } from '@mui/material';\r\nimport useLogout from '../hooks/useLogout';\r\n\r\nconst LogoutPage: React.FC = () => {\r\n const logout = useLogout(); \r\n\r\n useEffect(() => {\r\n // Llama a la función de logout del hook cuando el componente se monta\r\n logout();\r\n }, [logout]); // Dependencia: la función 'logout' en sí misma\r\n\r\n return (\r\n <Box sx={{\r\n display: 'flex',\r\n flexDirection: 'column',\r\n justifyContent: 'center',\r\n alignItems: 'center',\r\n height: '100vh',\r\n textAlign: 'center'\r\n }}>\r\n <CircularProgress size={60} />\r\n <Typography variant=\"h6\" sx={{ mt: 2 }}>Cerrando sesión...</Typography>\r\n </Box>\r\n );\r\n};\r\n\r\nexport default LogoutPage;","import React, { useState } from 'react';\r\nimport { Button, TextField, Typography, Alert, AlertTitle, Box, CircularProgress } from '@mui/material'; // Importaciones de MUI\r\nimport { useForm, Controller } from 'react-hook-form';\r\nimport { zodResolver } from '@hookform/resolvers/zod';\r\nimport * as z from 'zod';\r\n\r\n// --- ¡NUEVAS IMPORTACIONES DE REDUX! ---\r\nimport { useDispatch } from 'react-redux';\r\nimport { AppDispatch } from '../store'; // Asegúrate de que la ruta sea correcta\r\nimport { fetchClientContext } from '../store/clientContextSlice'; // Importa el thunk para cargar el contexto\r\n\r\nimport { useApp } from '../contexts/AppContext'; // Sigue importando useApp para setIsLoggedIn y checkSession\r\nimport { fetchApi } from '../utils/fetchApi'; // Tu utilidad fetchApi\r\nimport { envConfig } from '../env';\r\nimport { mensajeDeError } from '../utils/errores';\r\n\r\n// Define el esquema de validación con Zod\r\nconst loginSchema = z.object({\r\n username: z.string().min(1, \"El usuario es requerido\"), // Añadir validación básica\r\n password: z.string().min(1, \"La contraseña es requerida\"), // Añadir validación básica\r\n});\r\n\r\n// Define el tipo para los datos del formulario\r\ntype LoginFormValues = z.infer<typeof loginSchema>;\r\n\r\nconst LoginPage = () => {\r\n const [loginError, setLoginError] = useState<string | null>(null);\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n\r\n // Obtiene setIsLoggedIn y checkSession del contexto de la aplicación\r\n const { setIsLoggedIn, checkSession } = useApp();\r\n\r\n // Obtiene el dispatch de Redux\r\n const dispatch = useDispatch<AppDispatch>();\r\n\r\n const {\r\n control,\r\n handleSubmit,\r\n formState: { errors },\r\n reset\r\n } = useForm<LoginFormValues>({\r\n resolver: zodResolver(loginSchema),\r\n defaultValues: {\r\n username: '',\r\n password: '',\r\n },\r\n });\r\n\r\n const onSubmit = async (data: LoginFormValues) => {\r\n setIsSubmitting(true);\r\n setLoginError(null); // Limpia cualquier error anterior\r\n\r\n try {\r\n const body = new URLSearchParams({\r\n username: data.username,\r\n password: data.password\r\n });\r\n\r\n const loginResponse = await fetchApi('/login', {\r\n method: 'POST',\r\n body, // URLSearchParams se envía directamente como body para form-urlencoded\r\n // Asegúrate de que el Content-Type en el backend maneje 'application/x-www-form-urlencoded'\r\n });\r\n\r\n // Si el backend redirige a /menu, significa que el login fue exitoso.\r\n // Si la respuesta no es OK, o si la URL no termina en /menu, asumimos un fallo.\r\n if (!loginResponse.ok || !loginResponse.url.endsWith('/menu')) {\r\n //const errorText = await loginResponse.text();\r\n const errorText = 'Credenciales incorrectas o error al iniciar sesión';\r\n throw new Error(errorText);\r\n }\r\n\r\n // Si el login fue exitoso, actualiza el estado de la sesión y carga el contexto del cliente\r\n setIsLoggedIn(true);\r\n\r\n // --- ¡CAMBIO CLAVE AQUÍ! ---\r\n // Despacha el thunk de Redux para cargar el clientContext.\r\n // El .unwrap() es importante para poder capturar errores directamente con try/catch.\r\n await dispatch(fetchClientContext()).unwrap();\r\n\r\n console.log(\"Login exitoso y clientContext cargado.\");\r\n // el destino lo decide InitialRedirectHandler (la ruta que se pidió antes del login o, si no hubo, el inicio): navegar acá\r\n // también pisaría esa ruta con la pantalla de inicio\r\n reset(); // Limpia el formulario\r\n } catch (error) {\r\n console.error(\"Error al iniciar sesión:\", error);\r\n setIsLoggedIn(false); // Marca como no logueado en caso de error\r\n // setClientContext(null); // No necesitas llamar a setClientContext aquí, Redux lo maneja\r\n setLoginError(mensajeDeError(error, 'Ocurrió un error al iniciar sesión. Inténtalo de nuevo.'));\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n };\r\n\r\n return (\r\n <Box\r\n display=\"flex\"\r\n alignItems=\"center\"\r\n justifyContent=\"center\"\r\n minHeight=\"100vh\"\r\n bgcolor=\"#f5f5f5\"\r\n >\r\n <Box\r\n width=\"100%\"\r\n maxWidth=\"400px\"\r\n padding={4}\r\n bgcolor=\"white\"\r\n borderRadius={8}\r\n boxShadow={3}\r\n >\r\n <Box sx={{ display: 'flex', justifyContent: 'center', mb: 3 }}>\r\n <img\r\n src={`${envConfig.backendUrl}/img/login-logo-icon.png`}\r\n alt=\"Logo\"\r\n style={{ height: '192px', maxWidth: '100%', objectFit: 'contain' }}\r\n />\r\n </Box>\r\n <Typography variant=\"h5\" align=\"center\" gutterBottom sx={{ fontWeight: 'bold', color: 'primary.main' }}>\r\n Iniciar Sesión\r\n </Typography>\r\n <form onSubmit={handleSubmit(onSubmit)} className=\"space-y-4\">\r\n <div>\r\n <label htmlFor=\"username\">\r\n <Typography variant=\"subtitle1\" component=\"div\" sx={{ fontWeight: 'medium' }}>\r\n Usuario\r\n </Typography>\r\n </label>\r\n <Controller\r\n name=\"username\"\r\n control={control}\r\n render={({ field }) => (\r\n <TextField\r\n {...field}\r\n type=\"text\" // Cambiado de \"username\" a \"text\" que es más estándar para input type\r\n placeholder=\"Ingresa tu usuario\"\r\n fullWidth\r\n margin=\"normal\"\r\n error={!!errors.username}\r\n helperText={errors.username?.message}\r\n disabled={isSubmitting}\r\n />\r\n )}\r\n />\r\n </div>\r\n <div>\r\n <label htmlFor=\"password\">\r\n <Typography variant=\"subtitle1\" component=\"div\" sx={{ fontWeight: 'medium' }}>\r\n Contraseña\r\n </Typography>\r\n </label>\r\n <Controller\r\n name=\"password\"\r\n control={control}\r\n render={({ field }) => (\r\n <TextField\r\n {...field}\r\n type=\"password\"\r\n placeholder=\"Ingresa tu contraseña\"\r\n fullWidth\r\n margin=\"normal\"\r\n error={!!errors.password}\r\n helperText={errors.password?.message}\r\n disabled={isSubmitting}\r\n />\r\n )}\r\n />\r\n </div>\r\n <Button type=\"submit\" fullWidth variant=\"contained\" color=\"primary\" disabled={isSubmitting} sx={{ mt: 2 }}>\r\n {isSubmitting ? (\r\n <>\r\n <CircularProgress size={20} sx={{ mr: 1 }} />\r\n Iniciando sesión...\r\n </>\r\n ) : (\r\n 'Iniciar Sesión'\r\n )}\r\n </Button>\r\n </form>\r\n {loginError && (\r\n <Alert severity=\"error\" sx={{ mt: 2, maxHeight: '200px' }}> {/* Ya no necesitas alertProps */}\r\n <AlertTitle>Error</AlertTitle>\r\n {loginError}\r\n </Alert>\r\n )}\r\n </Box>\r\n </Box>\r\n );\r\n};\r\n\r\nexport default LoginPage;","import React, { useState, FormEvent, ChangeEvent, useCallback, useMemo } from 'react';\r\nimport { ProcedureDef, ProcedureParameter } from \"backend-plus\";\r\nimport { useParams } from 'react-router-dom';\r\nimport { useApp } from '../contexts/AppContext';\r\nimport { mensajeDeError } from '../utils/errores';\r\n\r\nimport { useApiCall } from '../hooks/useApiCall';\r\n\r\nimport {\r\n TextField,\r\n Button,\r\n FormControl,\r\n InputLabel,\r\n Select,\r\n MenuItem,\r\n Checkbox,\r\n FormControlLabel,\r\n FormHelperText,\r\n Typography,\r\n Box,\r\n CircularProgress,\r\n Alert,\r\n Divider,\r\n Paper\r\n} from '@mui/material';\r\nimport { SelectChangeEvent } from '@mui/material/Select';\r\nimport { cambiarGuionesBajosPorEspacios } from '../utils/functions';\r\n\r\n// Importamos el mapa de componentes de resultado\r\nimport { resultsOk } from '../pages/procedure-results/resultsOk';\r\n\r\nconst ProcedureForm: React.FC = () => {\r\n const { procedureName } = useParams<{ procedureName: string }>();\r\n const { clientContext } = useApp();\r\n\r\n const { callApi, loading: apiLoading, error: apiError } = useApiCall();\r\n\r\n const [validationErrors, setValidationErrors] = useState<Record<string, string | null>>({});\r\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\r\n const [executionLog, setExecutionLog] = useState<string[]>([]);\r\n const [procedureResultData, setProcedureResultData] = useState<unknown>(null);\r\n\r\n const procedure: ProcedureDef | undefined = useMemo(() => {\r\n if (procedureName && clientContext?.procedure) {\r\n return clientContext.procedure[procedureName];\r\n }\r\n return undefined;\r\n }, [procedureName, clientContext]);\r\n\r\n const [formValues, setFormValues] = useState<Record<string, unknown>>(() => {\r\n if (!procedure) return {};\r\n const initialValues: Record<string, unknown> = {};\r\n procedure.parameters.forEach(param => {\r\n if (param.defaultValue !== undefined) {\r\n initialValues[param.name] = param.defaultValue;\r\n } else if (param.specialDefaultValue) {\r\n switch (param.specialDefaultValue) {\r\n case 'current_date':\r\n initialValues[param.name] = new Date().toISOString().split('T')[0];\r\n break;\r\n case 'current_datetime':\r\n const now = new Date();\r\n initialValues[param.name] = now.toISOString().substring(0, 16);\r\n break;\r\n default:\r\n initialValues[param.name] = param.specialDefaultValue;\r\n break;\r\n }\r\n } else {\r\n initialValues[param.name] = '';\r\n }\r\n });\r\n return initialValues;\r\n });\r\n\r\n React.useEffect(() => {\r\n if (procedure) {\r\n setValidationErrors({});\r\n setSuccessMessage(null);\r\n setProcedureResultData(null);\r\n const newInitialValues: Record<string, unknown> = {};\r\n procedure.parameters.forEach(param => {\r\n if (param.defaultValue !== undefined) {\r\n newInitialValues[param.name] = param.defaultValue;\r\n } else if (param.specialDefaultValue) {\r\n switch (param.specialDefaultValue) {\r\n case 'current_date':\r\n newInitialValues[param.name] = new Date().toISOString().split('T')[0];\r\n break;\r\n case 'current_datetime':\r\n const now = new Date();\r\n newInitialValues[param.name] = now.toISOString().substring(0, 16);\r\n break;\r\n default:\r\n newInitialValues[param.name] = param.specialDefaultValue;\r\n break;\r\n }\r\n } else {\r\n newInitialValues[param.name] = '';\r\n }\r\n });\r\n setFormValues(newInitialValues);\r\n setExecutionLog([]);\r\n }\r\n }, [procedure]);\r\n\r\n const addLogMessage = useCallback((message: string, isError: boolean = false) => {\r\n const timestamp = new Date().toLocaleTimeString('es-AR');\r\n setExecutionLog(prev => [...prev, `[${timestamp}] ${isError ? 'ERROR: ' : ''}${message}`]);\r\n }, []);\r\n\r\n const handleChange = useCallback((e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | SelectChangeEvent<unknown>, child?: React.ReactNode) => {\r\n const { name, value } = e.target;\r\n let newValue: unknown = value;\r\n\r\n if ((e.target as HTMLInputElement).type === 'checkbox') {\r\n newValue = (e.target as HTMLInputElement).checked;\r\n }\r\n\r\n setFormValues(prevValues => ({\r\n ...prevValues,\r\n [name]: newValue,\r\n }));\r\n setValidationErrors(prev => ({ ...prev, [name]: null }));\r\n setSuccessMessage(null);\r\n setProcedureResultData(null);\r\n }, []);\r\n\r\n const handleSubmit = async (e: FormEvent) => {\r\n e.preventDefault();\r\n setValidationErrors({});\r\n setSuccessMessage(null);\r\n setExecutionLog([]);\r\n setProcedureResultData(null);\r\n\r\n if (!procedure) {\r\n addLogMessage('Error: La definición del procedimiento no está disponible.', true);\r\n return;\r\n }\r\n\r\n addLogMessage(`Iniciando ejecución del procedimiento: ${procedure.action}...`);\r\n\r\n const paramsToSend: Record<string, unknown> = {};\r\n let currentValidationErrors: Record<string, string | null> = {};\r\n let hasOverallError = false;\r\n\r\n for (const param of procedure.parameters) {\r\n let value = formValues[param.name];\r\n let processedValue: unknown = value;\r\n let paramError: string | null = null;\r\n\r\n switch (param.typeName) {\r\n case 'decimal':\r\n case 'double':\r\n if (value === '' || value === null) {\r\n processedValue = null;\r\n } else {\r\n processedValue = parseFloat(String(value));\r\n if (typeof processedValue === 'number' && isNaN(processedValue)) {\r\n paramError = `El valor para \"${param.label || param.name}\" no es un número válido.`;\r\n }\r\n }\r\n break;\r\n case 'integer':\r\n case 'bigint':\r\n if (value === '' || value === null) {\r\n processedValue = null;\r\n } else {\r\n processedValue = parseInt(String(value), 10);\r\n if (typeof processedValue === 'number' && isNaN(processedValue)) {\r\n paramError = `El valor para \"${param.label || param.name}\" no es un entero válido.`;\r\n }\r\n }\r\n break;\r\n case 'boolean':\r\n processedValue = !!value;\r\n break;\r\n case 'jsonb':\r\n case 'jsona':\r\n if (value === '' || value === null) {\r\n processedValue = null;\r\n } else {\r\n try {\r\n processedValue = JSON.parse(String(value));\r\n } catch (jsonError) {\r\n paramError = `Error en el formato JSON para \"${param.label || param.name}\": ${mensajeDeError(jsonError)}`;\r\n }\r\n }\r\n break;\r\n case 'bytea':\r\n processedValue = value instanceof File ? value : (value === '' ? null : value);\r\n break;\r\n case 'text':\r\n case 'date':\r\n case 'timestamp':\r\n case 'time':\r\n case 'interval':\r\n case 'tsrange':\r\n case 'time_range':\r\n case 'daterange':\r\n default:\r\n processedValue = value === '' ? null : value;\r\n break;\r\n }\r\n\r\n const hasDefaultValue = param.defaultValue !== undefined || param.specialDefaultValue !== undefined;\r\n const isEmpty = processedValue === null || processedValue === undefined || (typeof processedValue === 'string' && processedValue.trim() === '');\r\n \r\n if (!hasDefaultValue && isEmpty) {\r\n paramError = `El campo \"${param.label || param.name}\" es obligatorio y no puede estar vacío.`;\r\n }\r\n\r\n if (paramError) {\r\n currentValidationErrors[param.name] = paramError;\r\n hasOverallError = true;\r\n addLogMessage(`Error de validación para \"${param.label || param.name}\": ${paramError}`, true);\r\n }\r\n\r\n paramsToSend[param.name] = processedValue;\r\n }\r\n\r\n if (hasOverallError) {\r\n setValidationErrors(currentValidationErrors);\r\n addLogMessage('No se puede ejecutar el procedimiento debido a errores de validación en el formulario.', true);\r\n return;\r\n }\r\n\r\n addLogMessage(`Parámetros a enviar: ${JSON.stringify(paramsToSend, null, 2)}`);\r\n\r\n try {\r\n addLogMessage('Invocando procedimiento a través de useApiCall...');\r\n const result = await callApi(procedure.action, paramsToSend);\r\n \r\n if (result !== undefined) {\r\n setSuccessMessage(procedure.resultOk || 'Procedimiento ejecutado con éxito.');\r\n addLogMessage('Procedimiento ejecutado con éxito.');\r\n addLogMessage(`Resultado: ${JSON.stringify(result, null, 2)}`);\r\n setProcedureResultData(result);\r\n console.log('Resultado del procedimiento:', result);\r\n } else {\r\n addLogMessage('Procedimiento finalizado con errores.', true);\r\n }\r\n } catch (err) {\r\n console.error('Error al ejecutar el procedimiento (capturado en ProcedureForm):', err);\r\n addLogMessage(`Error en la ejecución: ${mensajeDeError(err, 'Error desconocido.')}`, true);\r\n } finally {\r\n addLogMessage('Ejecución finalizada.');\r\n }\r\n };\r\n\r\n const renderInputField = useCallback((param: ProcedureParameter) => {\r\n const hasError = !!validationErrors[param.name];\r\n const errorMessage = validationErrors[param.name];\r\n\r\n const commonProps = {\r\n id: param.name,\r\n name: param.name,\r\n label: param.label || param.name,\r\n variant: \"outlined\" as \"outlined\",\r\n fullWidth: true,\r\n margin: \"dense\" as \"dense\",\r\n required: !(param.defaultValue !== undefined || param.specialDefaultValue !== undefined),\r\n size: \"small\" as \"small\",\r\n helperText: errorMessage || param.description,\r\n error: hasError,\r\n disabled: apiLoading,\r\n onChange: handleChange,\r\n sx: { fontSize: '0.8rem' }\r\n };\r\n\r\n if (param.options && param.options.length > 0) {\r\n return (\r\n <FormControl fullWidth margin=\"dense\" size=\"small\" error={hasError} required={commonProps.required}>\r\n <InputLabel id={`${param.name}-label`} sx={{ fontSize: '0.8rem' }}>{param.label || param.name}</InputLabel>\r\n <Select\r\n labelId={`${param.name}-label`}\r\n value={formValues[param.name] === undefined ? '' : formValues[param.name]}\r\n onChange={handleChange}\r\n name={param.name}\r\n id={param.name}\r\n label={param.label || param.name}\r\n disabled={apiLoading}\r\n sx={{ fontSize: '0.8rem' }}\r\n renderValue={(selected) => (\r\n <Typography sx={{ fontSize: '0.8rem' }}>{selected as string}</Typography>\r\n )}\r\n >\r\n {(!commonProps.required || formValues[param.name] === '') && <MenuItem value=\"\" sx={{ fontSize: '0.8rem' }}><em>Seleccione...</em></MenuItem>}\r\n {param.options.map(option => (\r\n <MenuItem key={option} value={option} sx={{ fontSize: '0.8rem' }}>\r\n {option}\r\n </MenuItem>\r\n ))}\r\n </Select>\r\n {(param.description || errorMessage) && <FormHelperText sx={{ fontSize: '0.7rem' }}>{errorMessage || param.description}</FormHelperText>}\r\n </FormControl>\r\n );\r\n }\r\n\r\n switch (param.typeName) {\r\n case 'text':\r\n case 'interval':\r\n case 'tsrange':\r\n case 'time_range':\r\n case 'daterange':\r\n return <TextField type=\"text\" value={formValues[param.name] || ''} {...commonProps} />;\r\n case 'decimal':\r\n case 'double':\r\n return <TextField type=\"number\" inputProps={{ step: \"any\", style: { fontSize: '0.8rem' } }} value={formValues[param.name] || ''} {...commonProps} />;\r\n case 'integer':\r\n case 'bigint':\r\n return <TextField type=\"number\" inputProps={{ step: \"1\", style: { fontSize: '0.8rem' } }} value={formValues[param.name] || ''} {...commonProps} />;\r\n case 'boolean':\r\n return (\r\n <FormControlLabel\r\n control={\r\n <Checkbox\r\n checked={!!formValues[param.name]}\r\n onChange={handleChange}\r\n name={param.name}\r\n id={param.name}\r\n disabled={apiLoading}\r\n size=\"small\"\r\n />\r\n }\r\n label={<Typography sx={{ fontSize: '0.8rem', color: hasError ? 'error.main' : 'inherit' }}>{param.label || param.name}</Typography>}\r\n sx={{ mt: 1, mb: 0 }}\r\n />\r\n );\r\n case 'date':\r\n case 'timestamp':\r\n case 'time':\r\n return <TextField type={param.typeName === 'date' ? 'date' : param.typeName === 'timestamp' ? 'datetime-local' : 'time'} value={formValues[param.name] || ''} {...commonProps} InputLabelProps={{ shrink: true, sx: { fontSize: '0.8rem' } }} />;\r\n case 'jsonb':\r\n case 'jsona':\r\n return <TextField multiline rows={5} value={formValues[param.name] || ''} {...commonProps} InputProps={{ sx: { fontSize: '0.8rem' } }} />;\r\n case 'bytea':\r\n return (\r\n <FormControl fullWidth margin=\"dense\" error={hasError} required={commonProps.required}>\r\n <Typography variant=\"body2\" color=\"textSecondary\" component=\"label\" htmlFor={param.name} sx={{ fontSize: '0.8rem', color: hasError ? 'error.main' : 'text.secondary' }}>\r\n {param.label || param.name}\r\n </Typography>\r\n <input\r\n type=\"file\"\r\n id={param.name}\r\n name={param.name}\r\n onChange={(e: ChangeEvent<HTMLInputElement>) => {\r\n const files = e.target.files;\r\n setFormValues(prev => ({ ...prev, [param.name]: files && files.length > 0 ? files[0] : undefined }));\r\n }}\r\n disabled={apiLoading}\r\n style={{ padding: '6px 0', fontSize: '0.8rem' }}\r\n />\r\n {(param.description || errorMessage) && <FormHelperText sx={{ fontSize: '0.7rem' }}>{errorMessage || param.description}</FormHelperText>}\r\n </FormControl>\r\n );\r\n default:\r\n return <TextField type=\"text\" value={formValues[param.name] || ''} {...commonProps} />;\r\n }\r\n }, [formValues, handleChange, apiLoading, validationErrors]);\r\n\r\n // Contenido a renderizar dentro de la sección de resultado\r\n const ResultDisplayContent = useMemo(() => {\r\n if (!procedureResultData || !procedure?.resultOk) return null;\r\n\r\n const ComponentToRender = resultsOk[procedure.resultOk];\r\n\r\n if (ComponentToRender) {\r\n return <ComponentToRender data={procedureResultData} />;\r\n } else {\r\n return (\r\n <Paper elevation={2} sx={{ p: 2, mt: 0, bgcolor: 'warning.light' }}>\r\n <Typography variant=\"h6\" sx={{ fontSize: '1rem', mb: 1 }}>\r\n el componente para resolver el resultOk: '{procedure.resultOk}' definido en el procedure no existe o no está mapeado.\r\n </Typography>\r\n <Box sx={{ \r\n bgcolor: 'warning.dark', \r\n p: 1, \r\n borderRadius: 1, \r\n }}>\r\n <pre style={{ margin: 0, fontSize: '0.75rem', color: 'white' }}>\r\n {JSON.stringify(procedureResultData, null, 2)}\r\n </pre>\r\n </Box>\r\n </Paper>\r\n );\r\n }\r\n }, [procedureResultData, procedure]);\r\n\r\n\r\n if (!procedure) {\r\n return (\r\n <Box sx={{\r\n p: 2,\r\n width: '100%',\r\n boxSizing: 'border-box',\r\n mt: 0,\r\n bgcolor: 'background.paper',\r\n borderRadius: 2,\r\n boxShadow: 3,\r\n textAlign: 'center'\r\n }}>\r\n <CircularProgress sx={{ mb: 2 }} />\r\n <Typography variant=\"h6\" color=\"text.secondary\">\r\n Cargando procedimiento o no encontrado...\r\n </Typography>\r\n </Box>\r\n );\r\n }\r\n\r\n return (\r\n // Esta es la Box más externa que ahora controlará el scroll\r\n <Box sx={{\r\n p: 2,\r\n ml: 1,\r\n mt: 2,\r\n mr: 1,\r\n boxSizing: 'border-box',\r\n bgcolor: 'background.paper',\r\n borderRadius: 2,\r\n boxShadow: 3,\r\n fontSize: '0.8rem',\r\n maxHeight: 'calc(100vh - 100px)', // Ajusta esta altura máxima según el espacio disponible en tu layout\r\n overflowY: 'auto', // Habilita el scroll vertical para todo el contenido de esta Box\r\n overflowX: 'hidden' // Oculta el scroll horizontal si no es necesario para el contenedor principal\r\n }}>\r\n <Typography variant=\"h5\" component=\"h2\" gutterBottom align=\"left\">\r\n {cambiarGuionesBajosPorEspacios(procedure.proceedLabel || procedure.action)}\r\n </Typography>\r\n\r\n {Object.values(validationErrors).some(err => err !== null) && (\r\n <Alert severity=\"error\" sx={{ mb: 2, fontSize: '0.8rem' }}>\r\n Por favor, corrige los errores en el formulario antes de enviar.\r\n <ul>\r\n {Object.values(validationErrors).map((err, idx) => err && <li key={idx}>{err}</li>)}\r\n </ul>\r\n </Alert>\r\n )}\r\n {!Object.values(validationErrors).some(err => err !== null) && apiError && <Alert severity=\"error\" sx={{ mb: 2, fontSize: '0.8rem' }}>{apiError.message}</Alert>}\r\n {!Object.values(validationErrors).some(err => err !== null) && !apiError && successMessage && <Alert severity=\"success\" sx={{ mb: 2, fontSize: '0.8rem' }}>{successMessage}</Alert>}\r\n\r\n <form onSubmit={handleSubmit}>\r\n {procedure.parameters.map(param => (\r\n <div key={param.name}>\r\n {renderInputField(param)}\r\n </div>\r\n ))}\r\n\r\n <Button\r\n type=\"submit\"\r\n variant=\"contained\"\r\n color=\"primary\"\r\n disabled={apiLoading || Object.values(validationErrors).some(err => err !== null)}\r\n sx={{ mt: 2 }}\r\n size=\"medium\"\r\n startIcon={apiLoading ? <CircularProgress size={20} color=\"inherit\" /> : null}\r\n >\r\n {apiLoading ? 'Ejecutando...' : (procedure.proceedLabel || 'Ejecutar')}\r\n </Button>\r\n </form>\r\n\r\n {/* Renderizamos el contenido del resultado, sin scroll propio aquí */}\r\n {ResultDisplayContent && (\r\n <Box sx={{ mt: 2 }}> {/* Margen superior para separar del botón */}\r\n {ResultDisplayContent}\r\n </Box>\r\n )}\r\n \r\n {/* Área para el registro de ejecución del procedimiento */}\r\n {executionLog.length > 0 && (\r\n <Box sx={{ mt: 3 }}>\r\n <Divider sx={{ mb: 2 }} />\r\n <Typography variant=\"h6\" gutterBottom sx={{ fontSize: '0.9rem' }}>\r\n Registro de Ejecución\r\n </Typography>\r\n <Box\r\n sx={{\r\n bgcolor: 'grey.100',\r\n p: 2,\r\n borderRadius: 1,\r\n maxHeight: '200px', // Este log puede tener su propio scroll interno si es muy largo\r\n overflowY: 'auto',\r\n fontFamily: 'monospace',\r\n fontSize: '0.75rem',\r\n whiteSpace: 'pre-wrap',\r\n }}\r\n >\r\n {executionLog.map((log, index) => (\r\n <Typography\r\n key={index}\r\n component=\"pre\"\r\n sx={{\r\n margin: 0,\r\n color: log.includes('ERROR:') ? 'error.main' : 'text.primary',\r\n fontSize: 'inherit'\r\n }}\r\n >\r\n {log}\r\n </Typography>\r\n ))}\r\n </Box>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n};\r\n\r\nexport default ProcedureForm;","import React from 'react';\r\nimport { Box, Typography, Paper } from '@mui/material';\r\nimport { ResultOkProps } from '../../types';\r\n\r\nconst SuccessDisplay: React.FC<ResultOkProps> = ({ data }) => {\r\n return (\r\n <Paper elevation={2} sx={{ p: 2, mt: 2, bgcolor: 'success.light', color: 'success.contrastText' }}>\r\n <Typography variant=\"h6\" sx={{ fontSize: '1rem', mb: 1 }}>\r\n ¡Operación Exitosa!\r\n </Typography>\r\n <Typography variant=\"body2\" sx={{ fontSize: '0.85rem' }}>\r\n Detalles del resultado:\r\n </Typography>\r\n <Box sx={{ bgcolor: 'success.dark', p: 1, borderRadius: 1, mt: 1, overflowX: 'auto' }}>\r\n <pre style={{ margin: 0, fontSize: '0.75rem', color: 'white' }}>\r\n {JSON.stringify(data, null, 2)}\r\n </pre>\r\n </Box>\r\n <Typography variant=\"body2\" sx={{ fontSize: '0.8rem', mt: 1 }}>\r\n Puedes revisar los detalles anteriores o cerrar este mensaje.\r\n </Typography>\r\n </Paper>\r\n );\r\n};\r\n\r\nexport default SuccessDisplay;","import React from 'react';\r\n\r\nimport SuccessDisplay from './SuccessDisplay';\r\n\r\nimport { ResultOkProps } from '../../types';\r\n\r\n// La definición de ResultOkProps se movió a ../../types\r\n\r\n\r\ntype ResultComponentMap = {\r\n [key: string]: React.FC<ResultOkProps>; \r\n};\r\n\r\nexport let resultsOk: ResultComponentMap = {\r\n 'successful_operation': SuccessDisplay as React.FC<ResultOkProps>,\r\n};\r\n\r\nexport const extendResultsOk = (newComponents: ResultComponentMap) => {\r\n resultsOk = { ...resultsOk, ...newComponents };\r\n};","// src/components/PrivateRoute.tsx\r\nimport React, { useEffect, ReactNode } from 'react';\r\nimport { Navigate, useLocation } from 'react-router-dom';\r\nimport { useAppDispatch, useClientContextStatus } from '../store'; // Importa useDispatch, useClientContextStatus\r\nimport { setRedirectPath } from '../store/routerSlice'; // Importa la acción para guardar la ruta de redirección\r\nimport { PrivateRouteProps } from '../types';\r\n\r\nconst PrivateRoute: React.FC<PrivateRouteProps> = ({ children }) => {\r\n const location = useLocation();\r\n const dispatch = useAppDispatch();\r\n const clientContextStatus = useClientContextStatus(); // Obtiene el estado de Redux\r\n\r\n const isAuthenticated = clientContextStatus === 'succeeded';\r\n const isLoadingAuth = clientContextStatus === 'loading';\r\n\r\n console.log(`[PrivateRoute] Render: Path=${location.pathname}, AuthStatus=${clientContextStatus}, IsAuthenticated=${isAuthenticated}, IsLoadingAuth=${isLoadingAuth}`);\r\n\r\n useEffect(() => {\r\n console.log(`[PrivateRoute] Effect: Path=${location.pathname}, AuthStatus=${clientContextStatus}, IsAuthenticated=${isAuthenticated}, IsLoadingAuth=${isLoadingAuth}`);\r\n // Condición para guardar la URL:\r\n // 1. El usuario NO está autenticado.\r\n // 2. NO estamos en el proceso de carga de autenticación (ya sabemos que no está autenticado).\r\n // 3. NO estamos ya en la página de login (para evitar guardar /login).\r\n if (!isAuthenticated && !isLoadingAuth && location.pathname !== '/login') {\r\n const path = location.pathname + location.search + location.hash;\r\n console.log(`[PrivateRoute] ACTION: Dispatching setRedirectPath(${path})`);\r\n dispatch(setRedirectPath(path));\r\n } else if (isAuthenticated) {\r\n console.log(`[PrivateRoute] User is authenticated, no redirectPath set.`);\r\n } else if (isLoadingAuth) {\r\n console.log(`[PrivateRoute] Still loading authentication.`);\r\n } else if (location.pathname === '/login') {\r\n console.log(`[PrivateRoute] Currently on /login, not setting redirectPath.`);\r\n }\r\n }, [isAuthenticated, isLoadingAuth, location, dispatch, clientContextStatus]); // Añadir clientContextStatus a las dependencias del efecto\r\n\r\n if (isLoadingAuth) {\r\n console.log(`[PrivateRoute] Returning loading state...`);\r\n return <p>Verificando autenticación...</p>;\r\n }\r\n\r\n if (!isAuthenticated) {\r\n console.log(`[PrivateRoute] User not authenticated, navigating to /login`);\r\n return <Navigate to=\"/login\" replace />;\r\n }\r\n\r\n console.log(`[PrivateRoute] User authenticated, rendering children.`);\r\n return <>{children}</>;\r\n};\r\n\r\nexport default PrivateRoute;","// src/components/InitialRedirectHandler.tsx\r\nimport React, { useEffect, useRef } from 'react';\r\nimport { useLocation, useNavigate } from 'react-router-dom';\r\nimport { useAppDispatch, useRedirectPath, useClientContextStatus } from '../store'; // <--- ¡Asegúrate de importar useClientContextStatus de aquí!\r\nimport { setCurrentPath, setRedirectPath } from '../store/routerSlice';\r\n\r\nconst InitialRedirectHandler: React.FC = () => {\r\n const location = useLocation();\r\n const navigate = useNavigate();\r\n const dispatch = useAppDispatch();\r\n const redirectPath = useRedirectPath();\r\n const clientContextStatus = useClientContextStatus(); // <--- ¡Ahora lo obtienes de Redux!\r\n\r\n // Efecto para despachar la ruta actual al cambiar\r\n useEffect(() => {\r\n dispatch(setCurrentPath(location.pathname + location.search));\r\n }, [location, dispatch]);\r\n\r\n // Después de navegar a la ruta guardada, el efecto de abajo se vuelve a ejecutar (redirectPath pasó a null) mientras\r\n // la ruta todavía figura como /login: sin esta marca caería en \"ir a la pantalla de inicio por defecto\" y se perdería el destino.\r\n const navegandoRef = useRef(false);\r\n useEffect(() => {\r\n navegandoRef.current = false;\r\n }, [location.pathname]);\r\n\r\n // Efecto para manejar la redirección inicial (después de login o recarga si ya estaba logueado)\r\n useEffect(() => {\r\n // Asegúrate de que el clientContext esté cargado/autenticado antes de intentar redirigir\r\n if (clientContextStatus === 'succeeded') {\r\n if (redirectPath && redirectPath !== '/login') { // Evita redirigir a /login\r\n console.log('Redirigiendo a la ruta guardada:', redirectPath);\r\n navegandoRef.current = true;\r\n navigate(redirectPath);\r\n dispatch(setRedirectPath(null)); // Limpia la ruta guardada después de usarla\r\n } else if (!navegandoRef.current && location.pathname === '/login') {\r\n // Si el usuario está en /login y ya está autenticado y no hay ruta de redirección\r\n // lo mandamos a / (PantallaInicial lo lleva a la pantalla de inicio que declara el backend).\r\n console.log('Usuario autenticado, redirigiendo a la pantalla de inicio.');\r\n navigate('/');\r\n }\r\n }\r\n }, [clientContextStatus, redirectPath, location.pathname, navigate, dispatch]);\r\n\r\n return null; // No renderiza nada\r\n};\r\n\r\nexport default InitialRedirectHandler;","// src/pages/GenericDataGridPage.tsx\r\nimport React, { useState, useCallback, useEffect, useMemo } from 'react';\r\nimport { useParams, useLocation } from 'react-router-dom';\r\nimport { Box, Alert, Typography, Tabs, Tab, IconButton } from '@mui/material';\r\nimport CloseIcon from '@mui/icons-material/Close';\r\nimport GenericDataGrid from '../components/grid/GenericDataGrid';\r\nimport { FixedField, Ancestor } from '../types';\r\nimport { cambiarGuionesBajosPorEspacios } from '../utils/functions';\r\nimport { ParametrosDeTabla, parametrosDeTabla } from '../utils/parametrosDeTabla';\r\n\r\ninterface TabItem {\r\n id: string;\r\n label: string;\r\n tableName: string;\r\n fixedFields: FixedField[];\r\n /** Lo que declara la dirección o el ítem de menú de la tabla principal (filtros iniciales, `td`, `pf`, `pick`) */\r\n parametros?: ParametrosDeTabla;\r\n ancestors?: Ancestor[];\r\n}\r\n\r\ninterface GenericDataGridPageProps {\r\n initialTableName?: string;\r\n initialFixedFields?: FixedField[];\r\n /** Los parámetros de la tabla que ya resolvió quien la llama (el menú); sin ellos se leen de la dirección */\r\n parametros?: ParametrosDeTabla;\r\n}\r\n\r\nconst GenericDataGridPage: React.FC<GenericDataGridPageProps> = ({ \r\n initialTableName, \r\n initialFixedFields = [],\r\n parametros: parametrosRecibidos\r\n}) => {\r\n const { tableName: urlTableName } = useParams<{ tableName?: string }>();\r\n const effectiveTableName = initialTableName || urlTableName;\r\n \r\n const [tabs, setTabs] = useState<TabItem[]>([]);\r\n const [activeTabIdx, setActiveTabIdx] = useState(0);\r\n\r\n // Evitar loop infinito: solo reinicializar si el nombre de tabla cambia realmente\r\n // o si los parámetros de filtrado base cambian.\r\n const ffKey = JSON.stringify(initialFixedFields);\r\n\r\n const location = useLocation();\r\n // como backend-plus: lo de la dirección (`ff`, `fc`, `td`, `pf`, `pick`, `label`) pisa a lo del menú\r\n const parametros = useMemo(() => parametrosRecibidos ?? parametrosDeTabla(undefined, location.search), [parametrosRecibidos, location.search]);\r\n const fixedFieldsIniciales = initialFixedFields.length > 0 ? initialFixedFields : (parametros.fixedFields ?? []);\r\n const parametrosKey = JSON.stringify(parametros);\r\n React.useEffect(() => {\r\n if (effectiveTableName) {\r\n // Cada vez que cambia la tabla base (por menú o URL), reseteamos las pestañas\r\n setTabs([{\r\n id: `main-${effectiveTableName}-${ffKey}-${parametrosKey}`, // ID único que incluye filtros\r\n label: parametros.label ?? effectiveTableName.replace(/_/g, ' ').toUpperCase(),\r\n tableName: effectiveTableName,\r\n fixedFields: fixedFieldsIniciales,\r\n parametros,\r\n ancestors: []\r\n }]);\r\n setActiveTabIdx(0);\r\n }\r\n }, [effectiveTableName, ffKey, parametrosKey, location.key]);\r\n\r\n const handleOpenDetail = useCallback((tableName: string, fixedFields: FixedField[], label: string, ancestors: Ancestor[]) => {\r\n const tabId = `${tableName}-${JSON.stringify(fixedFields)}`;\r\n \r\n setTabs(prevTabs => {\r\n const existingTabIdx = prevTabs.findIndex(t => t.id === tabId);\r\n if (existingTabIdx !== -1) {\r\n setActiveTabIdx(existingTabIdx);\r\n return prevTabs;\r\n }\r\n const newTabs = [...prevTabs, { id: tabId, label, tableName, fixedFields, ancestors }];\r\n setActiveTabIdx(newTabs.length - 1);\r\n return newTabs;\r\n });\r\n }, []);\r\n\r\n const handleCloseTab = (index: number, event: React.MouseEvent) => {\r\n event.stopPropagation();\r\n if (index === 0) return; \r\n\r\n const newTabs = tabs.filter((_, i) => i !== index);\r\n setTabs(newTabs);\r\n if (activeTabIdx >= newTabs.length) setActiveTabIdx(newTabs.length - 1);\r\n else if (activeTabIdx === index) setActiveTabIdx(index - 1);\r\n };\r\n\r\n if (!effectiveTableName) {\r\n return (\r\n <Box p={3}><Alert severity=\"error\">No se especificó una tabla.</Alert></Box>\r\n );\r\n }\r\n\r\n return (\r\n <Box sx={{ width: '100%', height: 'calc(100vh - 64px)', display: 'flex', flexDirection: 'column' }}>\r\n <Box sx={{ borderBottom: 1, borderColor: 'divider', bgcolor: 'background.paper' }}>\r\n <Tabs value={activeTabIdx} onChange={(_, v) => setActiveTabIdx(v)} variant=\"scrollable\">\r\n {tabs.map((tab, index) => (\r\n <Tab \r\n key={tab.id} \r\n label={\r\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>\r\n <Typography variant=\"caption\" sx={{ fontWeight: 'bold' }}>{tab.label}</Typography>\r\n {index !== 0 && (\r\n <IconButton size=\"small\" onClick={(e) => handleCloseTab(index, e)}>\r\n <CloseIcon fontSize=\"inherit\" />\r\n </IconButton>\r\n )}\r\n </Box>\r\n } \r\n />\r\n ))}\r\n </Tabs>\r\n </Box>\r\n\r\n {tabs.map((tab, index) => (\r\n <Box\r\n key={tab.id}\r\n role=\"tabpanel\"\r\n hidden={activeTabIdx !== index}\r\n sx={{ flexGrow: 1, display: activeTabIdx === index ? 'flex' : 'none', flexDirection: 'column', overflow: 'hidden' }}\r\n >\r\n <GenericDataGrid \r\n tableName={tab.tableName} \r\n fixedFields={tab.fixedFields} \r\n filterColumns={tab.parametros?.filterColumns}\r\n tableDef={tab.parametros?.tableDef}\r\n parameterFunctions={tab.parametros?.parameterFunctions}\r\n pick={tab.parametros?.pick}\r\n onOpenDetail={handleOpenDetail} \r\n ancestors={tab.ancestors}\r\n />\r\n </Box>\r\n ))}\r\n </Box>\r\n );\r\n};\r\n\r\nexport default GenericDataGridPage;","// src/pages/PantallaDeRuta.tsx\nimport React, { useMemo } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { Box } from '@mui/material';\nimport { FallbackWScreen, wScreens } from './WScreens';\n\n/** Una wScreen como página: los parámetros vienen de la query string (es como se abre un detalle wScreen en otra pestaña) */\nexport const PantallaDeRuta: React.FC<{ screenName: string }> = ({ screenName }) => {\n const [busqueda] = useSearchParams();\n const params = useMemo(() => Object.fromEntries(busqueda.entries()), [busqueda]);\n const Pantalla = wScreens[screenName];\n return (\n <Box sx={{ pt: 3, pl: 4 }}>\n {Pantalla\n ? <Pantalla screenName={screenName} params={params} />\n : <FallbackWScreen screenName={screenName} />}\n </Box>\n );\n};\n","// src/pages/MenuTablePage.tsx\r\n\r\nimport React, { useMemo } from 'react';\r\nimport { useLocation, useParams } from 'react-router-dom';\r\nimport { Box, Alert, Typography } from '@mui/material';\r\nimport GenericDataGridPage from './GenericDataGridPage'; // Importamos el contenedor de tabs\r\nimport { useApp } from '../contexts/AppContext'; // Necesitamos el contexto para acceder al menú\r\nimport { MenuInfoBase, MenuInfoMenu, MenuInfoTable } from 'backend-plus'; // Asegúrate de importar tus tipos de menú\r\nimport { FixedField } from '../types';\r\nimport { aFixedFields, ParametrosDeTabla, parametrosDeTabla } from '../utils/parametrosDeTabla';\r\n\r\n/**\r\n * Los fixedFields que declara un ítem de menú (`ff`). backend-plus admite dos formas, igual que su cliente (my-menu.js):\r\n * un arreglo [{fieldName, value}] o un objeto {campo: valor}.\r\n */\r\nexport function fixedFieldsDeMenu(ff: MenuInfoTable['ff'] | Record<string, unknown> | undefined): FixedField[] {\r\n return aFixedFields(ff);\r\n}\r\n\r\nconst MenuTablePage: React.FC = () => {\r\n // Obtiene el `menuName` de la URL para buscar en clientContext.menu\r\n const { menuName } = useParams<{ menuName?: string }>();\r\n const { clientContext } = useApp();\r\n const { search } = useLocation();\r\n\r\n // Función auxiliar para encontrar un ítem de menú recursivamente\r\n const findMenuItem = (items: MenuInfoBase[], nameToFind: string): MenuInfoTable | undefined => {\r\n for (const item of items) {\r\n if (item.name === nameToFind && item.menuType === \"table\") {\r\n return item as MenuInfoTable;\r\n }\r\n if (item.menuType === \"menu\" && (item as MenuInfoMenu).menuContent) {\r\n const found = findMenuItem((item as MenuInfoMenu).menuContent, nameToFind);\r\n if (found) return found;\r\n }\r\n }\r\n return undefined;\r\n };\r\n\r\n // Usa useMemo para determinar el tableName y los fixedFields desde el menú\r\n const { currentTableName, currentFixedFields, parametros } = useMemo(() => {\r\n let tableName: string | undefined = undefined;\r\n let fixedFields: FixedField[] = [];\r\n let parametros: ParametrosDeTabla = {};\r\n \r\n if (menuName && clientContext && clientContext.menu) {\r\n const menuItem = findMenuItem(clientContext.menu, menuName);\r\n if (menuItem) {\r\n tableName = menuItem.table || menuItem.name;\r\n // como backend-plus (wScreens.table de my-menu.js): lo que declara el ítem (ff, fc, td, pf, label...) y, encima,\r\n // lo de la dirección\r\n parametros = parametrosDeTabla(menuItem, search);\r\n fixedFields = parametros.fixedFields ?? [];\r\n } else {\r\n console.warn(`[${menuName}] Ítem de menú '${menuName}' no encontrado o no es de tipo 'table' en clientContext.menu.`);\r\n // Opcional: podrías redirigir o mostrar un error más amigable\r\n }\r\n }\r\n\r\n return {\r\n currentTableName: tableName,\r\n currentFixedFields: fixedFields,\r\n parametros,\r\n };\r\n }, [menuName, clientContext, search]);\r\n\r\n if (!currentTableName) {\r\n // Muestra un mensaje de error si el nombre de la tabla no se pudo determinar\r\n return (\r\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"200px\">\r\n <Alert severity=\"error\">\r\n <Typography variant=\"h6\">Error: No se pudo cargar la información de la tabla.</Typography>\r\n <Typography variant=\"body1\">Verifique la configuración del menú o el nombre de la tabla en la URL.</Typography>\r\n </Alert>\r\n </Box>\r\n );\r\n }\r\n\r\n // Renderiza GenericDataGrid con los datos obtenidos del menú\r\n return (\r\n <GenericDataGridPage \r\n initialTableName={currentTableName} \r\n initialFixedFields={currentFixedFields} \r\n parametros={parametros}\r\n />\r\n );\r\n};\r\n\r\nexport default MenuTablePage;","import React, { useEffect, useRef, useState } from 'react';\nimport { Navigate, useLocation, useParams } from 'react-router-dom';\nimport { Alert, Box, CircularProgress } from '@mui/material';\nimport { MenuInfoBase, MenuInfoMenu } from 'backend-plus';\nimport { envConfig } from '../env';\nimport { useApp } from '../contexts/AppContext';\nimport { FallbackWScreen } from '../pages/WScreens';\nimport { mensajeDeError } from '../utils/errores';\nimport { cargarClienteBP } from './clienteBP';\nimport { ConsolaBP } from './ConsolaBP';\nimport { resolverPantallaBP, rutaDePantallaBP } from './pantallaDeBP';\nimport { puenteBPHabilitado } from './habilitacion';\n\nfunction buscarItemDeMenu(items: MenuInfoBase[], tipo: string): MenuInfoBase | undefined {\n for (const item of items) {\n if (item.menuType === tipo) return item;\n if (item.menuType === 'menu') {\n const encontrado = buscarItemDeMenu((item as MenuInfoMenu).menuContent, tipo);\n if (encontrado) return encontrado;\n }\n }\n return undefined;\n}\n\ninterface PantallaBPProps {\n nombre: string;\n /** Los parámetros en el formato de dirección de backend-plus: `#w=...&up=...` (o con `?`) */\n direccion: string;\n}\n\n/** Una wScreen de backend-plus (la de la propia aplicación o una de las del framework) dibujada en su `#main_layout` */\nexport const PantallaBP: React.FC<PantallaBPProps> = ({ nombre, direccion }) => {\n const { clientContext } = useApp();\n const [cargando, setCargando] = useState<boolean>(true);\n const [error, setError] = useState<string | null>(null);\n const [noExiste, setNoExiste] = useState<boolean>(false);\n const contenedor = useRef<HTMLDivElement>(null);\n const menu = clientContext.menu;\n\n useEffect(() => {\n const elemento = contenedor.current;\n if (!elemento) return;\n let vigente = true;\n setError(null);\n setNoExiste(false);\n setCargando(true);\n cargarClienteBP(envConfig.backendUrl)\n .then(myOwn => {\n if (!vigente) return;\n setCargando(false);\n const pantalla = resolverPantallaBP(myOwn, nombre, elemento);\n if (!pantalla) {\n setNoExiste(true);\n return;\n }\n elemento.replaceChildren();\n // como en backend-plus: lo que declara el ítem de menú, pisado por la dirección (leída con su propio decodificador:\n // `up`, `ff`, `detailing` llegan como objetos), y la pantalla se ejecuta con myOwn como `this`\n const item = menu ? buscarItemDeMenu(menu, nombre) : undefined;\n const desdeLaDireccion = myOwn.UriSearchToObject?.(direccion) ?? Object.fromEntries(new URLSearchParams(direccion.replace(/^[#?]/, '')).entries());\n const parametros: Record<string, unknown> = { ...item, ...desdeLaDireccion };\n myOwn.preDisplayPage?.(parametros, pantalla.definicion, nombre);\n pantalla.ejecutar.call(myOwn, parametros);\n })\n .catch((err: unknown) => {\n if (!vigente) return;\n setCargando(false);\n setError(mensajeDeError(err));\n });\n return () => {\n vigente = false;\n elemento.replaceChildren();\n };\n }, [nombre, direccion, menu]);\n\n if (noExiste) return <FallbackWScreen screenName={nombre} />;\n return (\n // el indicador de carga flota: si ocupara lugar, el contenido de backend-plus empezaría más abajo mientras carga y subiría al terminar\n <Box sx={{ position: 'relative' }}>\n {cargando && <CircularProgress size={24} sx={{ position: 'absolute', top: 16, left: 16 }} />}\n {error && <Alert severity=\"error\" sx={{ m: 2 }}>{error}</Alert>}\n {/* backend-plus dibuja sus pantallas en el elemento con este id */}\n <div id=\"main_layout\" ref={contenedor} />\n <ConsolaBP />\n </Box>\n );\n};\n\n/** Ruta de las wScreens que la aplicación no registró: con el puente encendido se pasan a la ruta de las pantallas de backend-plus */\nexport const PantallaDeRespaldo: React.FC = () => {\n const { screenName = '' } = useParams<{ screenName: string }>();\n const { search } = useLocation();\n if (!puenteBPHabilitado()) {\n return <Box sx={{ p: 3 }}><FallbackWScreen screenName={screenName} /></Box>;\n }\n return <Navigate to={rutaDePantallaBP(screenName, search)} replace />;\n};\n\n/**\n * La página de backend-plus dentro de la aplicación: `/bp/menu#w=<pantalla>&...`. Se parece a `<base-url>/menu` a propósito:\n * el código de backend-plus y de las aplicaciones arma direcciones a partir de la ubicación (`location.pathname + '/../menu#...'`,\n * `location.pathname + '#w=...'` para abrir en otra pestaña) y así caen en esta misma ruta, que lee los parámetros como BP\n * (del hash, o de la query).\n */\nexport const PaginaBP: React.FC = () => {\n const { hash, search } = useLocation();\n const direccion = hash || search;\n const nombre = new URLSearchParams(direccion.replace(/^[#?]/, '')).get('w');\n if (!puenteBPHabilitado()) return <Navigate to=\"/\" replace />;\n if (!nombre) return <Box sx={{ p: 3 }}><Alert severity=\"warning\">Falta la pantalla de backend-plus (<code>#w=...</code>).</Alert></Box>;\n return <PantallaBP key={nombre} nombre={nombre} direccion={direccion} />;\n};\n","// Puente con el cliente de backend-plus: carga sus scripts en la página (los mismos que arma el servidor con\n// `clientIncludes`) para poder usar su grilla y sus wScreens donde esta librería todavía no tiene una versión propia.\n// Solo funciona en el mismo origen que el backend (comparte la sesión), por ejemplo bajo `<base-url>/react/`.\nimport { FixedField } from '../types';\n\nexport interface OpcionesGrillaBP {\n fixedFields?: FixedField[];\n tableDef?: Record<string, unknown>;\n}\n\n/** Una wScreen de backend-plus: dibuja en `#main_layout` a partir de los parámetros de la dirección */\nexport type PantallaBP = (parametros: Record<string, unknown>) => void;\n\n/**\n * Las formas de wScreen que entiende `showPage` de backend-plus: una función, o un objeto con `mainFunction`, o un\n * objeto con `parameters` y `mainAction` (muestra el formulario de parámetros y después ejecuta `mainAction`).\n * `procAux` también vive en `wScreens`: es un objeto con `showParams`.\n */\nexport interface DefinicionPantallaBP {\n mainFunction?: PantallaBP;\n parameters?: unknown[];\n mainAction?: unknown;\n resultClass?: string;\n proceedLabel?: string;\n pageTitle?: string;\n showParams?: (definicion: Record<string, unknown>, contenedor: HTMLElement, parametros: Record<string, unknown>, accion: unknown) => void;\n}\n\nexport interface MyOwnBP {\n autoSetup(): Promise<unknown>;\n wScreens: Record<string, PantallaBP | DefinicionPantallaBP | undefined>;\n /** Lee una dirección de backend-plus (`#w=...&up=...` o `?...`) con los decodificadores de cada parámetro */\n UriSearchToObject?(direccion: string): Record<string, unknown>;\n preDisplayPage?(parametros: Record<string, unknown>, pantalla: DefinicionPantallaBP, nombre: string): void;\n tableGrid(nombreTabla: string, elemento: HTMLElement, opciones: OpcionesGrillaBP): unknown;\n}\n\ndeclare global {\n interface Window {\n myOwn?: MyOwnBP;\n my?: MyOwnBP;\n }\n}\n\nexport interface RecursosBP {\n hojasDeEstilo: string[];\n scripts: string[];\n}\n\n/** Las hojas de estilo y los scripts de la página que sirve backend-plus, en el orden en que los carga */\nexport function extraerRecursos(html: string): RecursosBP {\n const documento = new DOMParser().parseFromString(html, 'text/html');\n const referencias = (selector: string, atributo: string): string[] =>\n Array.from(documento.querySelectorAll(selector))\n .map(elemento => elemento.getAttribute(atributo))\n .filter((ruta): ruta is string => !!ruta);\n return {\n hojasDeEstilo: referencias('link[rel=stylesheet]', 'href'),\n scripts: referencias('script[src]', 'src'),\n };\n}\n\n/**\n * El cliente de backend-plus arma sus direcciones (`lib/...`, `img/...`, los procedimientos) relativas a la página:\n * con un `<base>` apuntando a la base-url se comporta como si estuviera en su propia página.\n */\nfunction fijarBase(urlBackend: string): void {\n const href = new URL(urlBackend.replace(/\\/*$/, '/'), window.location.href).href;\n let base = document.querySelector('base');\n if (!base) {\n base = document.createElement('base');\n document.head.prepend(base);\n }\n base.href = href;\n}\n\nfunction cargarScript(ruta: string): Promise<void> {\n return new Promise((resolver, rechazar) => {\n const script = document.createElement('script');\n script.async = false;\n script.src = ruta;\n script.onload = () => resolver();\n script.onerror = () => rechazar(new Error(`No se pudo cargar ${ruta}`));\n document.head.appendChild(script);\n });\n}\n\n/**\n * El cliente de backend-plus se engancha al evento `load` de la ventana para mostrar su página (`showPage`), que acá\n * no tiene dónde dibujar (`#total-layout`) y falla; su manejador de errores deja un cartel como primer hijo del `body`\n * que corre toda la aplicación hacia abajo. Por eso se lo carga recién cuando la ventana terminó de cargar.\n */\nexport function esperarCargaDeVentana(): Promise<void> {\n if (document.readyState === 'complete') return Promise.resolve();\n return new Promise(resolver => window.addEventListener('load', () => resolver(), { once: true }));\n}\n\n/**\n * Mientras corre `inyectar`, ignora que lo que se está cargando registre escuchas de esos eventos de la ventana.\n * backend-plus (my-menu.js) escucha `popstate` para mostrar su página, salvo que la dirección termine en `/react`: sin esto\n * la aplicación quedaría atada a ese nombre. Con la navegación en manos del router de la aplicación, esa escucha sobra.\n */\nexport async function sinEscuchasDeVentana<R>(tipos: string[], inyectar: () => Promise<R>): Promise<R> {\n const original = window.addEventListener;\n const filtrada = function (this: Window, tipo: string, escucha: EventListenerOrEventListenerObject, opciones?: boolean | AddEventListenerOptions): void {\n if (tipos.includes(tipo)) return;\n original.call(this, tipo, escucha, opciones);\n };\n window.addEventListener = filtrada;\n try {\n return await inyectar();\n } finally {\n if (window.addEventListener === filtrada) window.addEventListener = original;\n }\n}\n\nasync function cargarScripts(rutas: string[]): Promise<void> {\n for (const ruta of rutas) {\n await cargarScript(ruta);\n }\n}\n\nlet carga: Promise<MyOwnBP> | null = null;\n\n/** Carga (una sola vez) el cliente de backend-plus y lo deja listo para usar */\nexport function cargarClienteBP(urlBackend: string): Promise<MyOwnBP> {\n if (!carga) {\n carga = cargarSinMemoria(urlBackend).catch((error: unknown) => {\n carga = null;\n throw error;\n });\n }\n return carga;\n}\n\nasync function cargarSinMemoria(urlBackend: string): Promise<MyOwnBP> {\n const destino = new URL(urlBackend, window.location.href);\n if (destino.origin !== window.location.origin) {\n throw new Error(`El cliente de backend-plus solo se puede usar desde el mismo origen que el backend (${destino.origin} no es ${window.location.origin}).`);\n }\n fijarBase(urlBackend);\n const respuesta = await fetch(`${destino.pathname.replace(/\\/*$/, '')}/menu`, { credentials: 'include' });\n if (!respuesta.ok || /\\/login\\/?$/.test(new URL(respuesta.url).pathname)) {\n throw new Error('No hay una sesión iniciada en el backend.');\n }\n const recursos = extraerRecursos(await respuesta.text());\n recursos.hojasDeEstilo.forEach(ruta => {\n const enlace = document.createElement('link');\n enlace.rel = 'stylesheet';\n enlace.href = ruta;\n document.head.appendChild(enlace);\n });\n await esperarCargaDeVentana();\n await sinEscuchasDeVentana(['popstate'], () => cargarScripts(recursos.scripts));\n const myOwn = window.myOwn;\n if (!myOwn) throw new Error('El backend no definió myOwn.');\n // backend-plus lo hace al cargar la ventana, y esta ya está cargada; parte de su código usa el global `my`\n window.my = myOwn;\n await myOwn.autoSetup();\n return myOwn;\n}\n","import React, { useEffect } from 'react';\nimport { Box } from '@mui/material';\n\n/** Lo que tardan en llegar las dos llamadas de backend-plus por un mismo error (en la misma tarea): más que eso es otro error igual */\nconst MARGEN_DE_REPETIDO_MS = 1000;\n\n/**\n * backend-plus registra algunos errores dos veces: el de una llamada ajax lo registra `my-ajax.js` y el `catch` de «proceder»\n * (`my-menu.js`) lo vuelve a registrar. Si ya hay un aviso con ese texto que llegó recién, el repetido se descarta.\n */\nexport function descartarRepetidos(consola: HTMLElement, agregados: Node[], ahora: number): void {\n for (const nodo of agregados) {\n if (!(nodo instanceof HTMLElement)) continue;\n const repetido = Array.from(consola.children).find(\n (otro): otro is HTMLElement => otro instanceof HTMLElement && otro !== nodo && otro.className === nodo.className && otro.textContent === nodo.textContent\n && ahora - Number(otro.dataset.instante) < MARGEN_DE_REPETIDO_MS\n );\n if (repetido) nodo.remove();\n else nodo.dataset.instante = String(ahora);\n }\n}\n\n/**\n * Los mensajes y errores de backend-plus (`myOwn.log`): los escribe en todos los `#mini-console` que haya y, si no hay ninguno,\n * al principio del `body`, donde empujan toda la aplicación hacia abajo. Acá flotan en la esquina de arriba a la derecha,\n * debajo de la barra, como avisos, y él mismo los retira (los errores a los 30 segundos, los mensajes a los 3).\n * Las hojas de estilo de backend-plus y de las aplicaciones también le dan estilo a `#mini-console` (por id y con\n * `width`, `max-height`, `top`...): `&#mini-console` (clase + id) les gana y se reponen esos valores.\n */\nexport const ConsolaBP: React.FC = () => {\n useEffect(() => {\n const consola = document.getElementById('mini-console');\n if (!consola) return;\n const observador = new MutationObserver(registros =>\n descartarRepetidos(consola, registros.flatMap(registro => Array.from(registro.addedNodes)), Date.now())\n );\n observador.observe(consola, { childList: true });\n return () => observador.disconnect();\n }, []);\n return (\n <Box\n id=\"mini-console\"\n sx={(tema) => ({\n '&#mini-console': {\n position: 'fixed',\n top: { xs: 64, sm: 72 },\n left: 'auto',\n right: 16,\n bottom: 'auto',\n float: 'none',\n zIndex: tema.zIndex.snackbar,\n width: 'auto',\n minWidth: 0,\n maxWidth: 'min(480px, calc(100vw - 32px))',\n minHeight: 0,\n maxHeight: 'none',\n overflow: 'visible',\n padding: 0,\n border: 'none',\n borderRadius: 0,\n boxShadow: 'none',\n backgroundColor: 'transparent',\n fontSize: 'inherit',\n display: 'flex',\n flexDirection: 'column',\n gap: 1,\n },\n '&#mini-console:not(:empty)': { display: 'flex' },\n '&#mini-console:empty': { display: 'none' },\n '& > div': {\n p: '6px 12px',\n borderRadius: 1,\n boxShadow: tema.shadows[3],\n fontSize: '0.875rem',\n whiteSpace: 'pre-wrap',\n overflowWrap: 'anywhere',\n backgroundColor: tema.palette.grey[800],\n color: tema.palette.common.white,\n },\n '& > .status-error': {\n backgroundColor: tema.palette.error.main,\n color: tema.palette.error.contrastText,\n },\n })}\n />\n );\n};\n","// Cómo se ejecuta una wScreen de backend-plus, igual que `showPage` de my-menu.js\nimport type { DefinicionPantallaBP, MyOwnBP, PantallaBP } from './clienteBP';\n\nexport interface PantallaResuelta {\n definicion: DefinicionPantallaBP;\n ejecutar: PantallaBP;\n}\n\n/** Ruta de las pantallas de backend-plus dentro de la aplicación: se parece a la página de BP (`<base-url>/menu#w=...`) */\nexport const RUTA_PANTALLA_BP = '/bp/menu';\n\n/** La ruta de una pantalla de backend-plus con sus parámetros, en el formato de dirección de BP (`#w=<pantalla>&...`) */\nexport function rutaDePantallaBP(nombre: string, parametros: string = ''): string {\n const resto = parametros.replace(/^[#?]/, '').replace(/(^|&)w=[^&]*/, '').replace(/^&/, '');\n return `${RUTA_PANTALLA_BP}#w=${encodeURIComponent(nombre)}${resto ? `&${resto}` : ''}`;\n}\n\n/**\n * Las direcciones de backend-plus a las que apunta lo que él arma (el botón «proceder» de una wScreen con parámetros, los\n * vínculos de la ayuda, `location.href = 'login'` al vencer la sesión): son páginas del backend y recargan todo. Dentro de\n * esta aplicación hay que quedarse en ella: devuelve la ruta equivalente, o null si la dirección es de otra cosa.\n */\nexport function rutaDeEnlaceBP(href: string, baseDelDocumento: string, urlBackend: string): string | null {\n const enlace = new URL(href, baseDelDocumento);\n const base = `${urlBackend.replace(/\\/*$/, '/')}`;\n const pagina = (nombre: string) => new URL(`${base}${nombre}`, baseDelDocumento);\n if (enlace.origin !== pagina('menu').origin) return null;\n if (enlace.pathname === pagina('login').pathname) return '/login';\n if (enlace.pathname === pagina('logout').pathname) return '/logout';\n if (enlace.pathname !== pagina('menu').pathname) return null;\n const texto = (enlace.hash || enlace.search).slice(1);\n const nombre = new URLSearchParams(texto).get('w');\n return nombre ? rutaDePantallaBP(nombre, texto) : null;\n}\n\n/** La función que dibuja la pantalla, o null si backend-plus no la tiene (o no tiene cómo dibujarla) */\nexport function resolverPantallaBP(myOwn: MyOwnBP, nombre: string, contenedor: HTMLElement): PantallaResuelta | null {\n const registrada = myOwn.wScreens[nombre];\n if (!registrada) return null;\n if (typeof registrada === 'function') {\n return { definicion: { mainFunction: registrada }, ejecutar: registrada };\n }\n if (registrada.mainFunction) {\n return { definicion: registrada, ejecutar: registrada.mainFunction };\n }\n const { parameters, mainAction, resultClass, proceedLabel } = registrada;\n const procAux = myOwn.wScreens.procAux;\n const mostrarParametros = typeof procAux === 'object' ? procAux.showParams : undefined;\n if (parameters && mostrarParametros) {\n return {\n definicion: registrada,\n ejecutar: parametros => mostrarParametros({ parameters, resultClass, proceedLabel }, contenedor, parametros, mainAction),\n };\n }\n return null;\n}\n","// El puente con backend-plus es opcional: la aplicación lo enciende (prop `puenteBP` de `App`) y recién entonces las\n// wScreens que no están en `myWScreens` se piden al cliente de backend-plus en lugar de mostrar «no implementada».\nlet habilitado = false;\n\nexport const habilitarPuenteBP = (): void => {\n habilitado = true;\n};\n\nexport const puenteBPHabilitado = (): boolean => habilitado;\n","import React, { useEffect, useRef, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Alert, Box, CircularProgress } from '@mui/material';\nimport { envConfig } from '../env';\nimport { FixedField } from '../types';\nimport { mensajeDeError } from '../utils/errores';\nimport { cargarClienteBP } from './clienteBP';\nimport { ConsolaBP } from './ConsolaBP';\n\ninterface GrillaBPProps {\n tabla: string;\n fixedFields?: FixedField[];\n}\n\n/** La grilla de backend-plus (`myOwn.tableGrid`) dentro de una página React: conserva todo lo que ya hace esa grilla */\nexport const GrillaBP: React.FC<GrillaBPProps> = ({ tabla, fixedFields }) => {\n const contenedor = useRef<HTMLDivElement>(null);\n const [cargando, setCargando] = useState<boolean>(true);\n const [error, setError] = useState<string | null>(null);\n // los fixedFields llegan como arreglo nuevo en cada render: la grilla solo se rearma si cambia su contenido\n const clave = JSON.stringify(fixedFields ?? []);\n\n useEffect(() => {\n const elemento = contenedor.current;\n if (!elemento) return;\n let vigente = true;\n setError(null);\n setCargando(true);\n cargarClienteBP(envConfig.backendUrl)\n .then(myOwn => {\n if (!vigente) return;\n setCargando(false);\n elemento.replaceChildren();\n myOwn.tableGrid(tabla, elemento, { fixedFields: JSON.parse(clave) as FixedField[] });\n })\n .catch((err: unknown) => {\n if (!vigente) return;\n setCargando(false);\n setError(mensajeDeError(err));\n });\n return () => {\n vigente = false;\n elemento.replaceChildren();\n };\n }, [tabla, clave]);\n\n return (\n // el indicador de carga flota: si ocupara lugar, el contenido de backend-plus empezaría más abajo mientras carga y subiría al terminar\n <Box sx={{ position: 'relative' }}>\n {cargando && <CircularProgress size={24} sx={{ position: 'absolute', top: 16, left: 16 }} />}\n {error && <Alert severity=\"error\" sx={{ m: 2 }}>{error}</Alert>}\n <div ref={contenedor} />\n <ConsolaBP />\n </Box>\n );\n};\n\n/** Ruta `/bp/table/:tableName`: una tabla con la grilla de backend-plus, para compararla con la de esta librería */\nexport const PaginaGrillaBP: React.FC = () => {\n const { tableName = '' } = useParams<{ tableName: string }>();\n return <GrillaBP key={tableName} tabla={tableName} />;\n};\n","import React, { useEffect } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { envConfig } from '../env';\nimport { capturarNavegacionBP } from './capturaDeNavegacion';\n\n/**\n * «proceder», los vínculos, `location.href = ...` y los avisos de backend-plus (el de «Iniciar sesión», por ejemplo) apuntan\n * a páginas del backend: se resuelven dentro de la aplicación. Se monta una sola vez y no depende de que haya una pantalla de\n * backend-plus en la página: su cliente sigue vivo (y sus avisos en el `body`) aunque el usuario ya esté en una pantalla de React.\n */\nexport const CapturaBP: React.FC = () => {\n const navigate = useNavigate();\n useEffect(() => capturarNavegacionBP(envConfig.backendUrl, navigate), [navigate]);\n return null;\n};\n","// Mientras hay una pantalla de backend-plus en la página, lo que ella (o el código de la aplicación sobre backend-plus)\n// manda a las páginas del backend se resuelve dentro de la aplicación: clics en enlaces, asignaciones a `location` y\n// `history.replaceState`.\nimport { rutaBase } from '../rutaBase';\nimport { rutaDeEnlaceBP } from './pantallaDeBP';\n\ninterface EventoNavegacion extends Event {\n canIntercept: boolean;\n destination: { url: string; sameDocument: boolean };\n downloadRequest: string | null;\n}\n\ninterface ApiNavegacion {\n addEventListener(tipo: 'navigate', escucha: (evento: EventoNavegacion) => void): void;\n removeEventListener(tipo: 'navigate', escucha: (evento: EventoNavegacion) => void): void;\n}\n\n// la Navigation API no está en todos los navegadores (ni en los tipos del DOM de esta versión de TypeScript)\nfunction apiDeNavegacion(): ApiNavegacion | null {\n const api: unknown = Reflect.get(window, 'navigation');\n return typeof api === 'object' && api !== null && 'addEventListener' in api && typeof api.addEventListener === 'function'\n ? api as ApiNavegacion\n : null;\n}\n\n/** Empieza a capturar; devuelve la función que deja de hacerlo */\nexport function capturarNavegacionBP(urlBackend: string, navegar: (ruta: string) => void): () => void {\n const alHacerClick = (evento: MouseEvent) => {\n if (evento.defaultPrevented || evento.button !== 0 || evento.ctrlKey || evento.metaKey || evento.shiftKey) return;\n const enlace = evento.target instanceof Element ? evento.target.closest('a[href]') : null;\n if (!enlace || enlace.hasAttribute('download') || (enlace.getAttribute('target') ?? '_self') !== '_self') return;\n const ruta = rutaDeEnlaceBP(enlace.getAttribute('href') ?? '', document.baseURI, urlBackend);\n if (ruta) {\n // el botón «proceder» de backend-plus además tiene su propio onclick (history.pushState a `<base-url>/menu#...` y\n // showPage): si corriera, la dirección quedaría fuera de la aplicación\n evento.preventDefault();\n evento.stopPropagation();\n navegar(ruta);\n }\n };\n // cubre los enlaces de los diálogos y de todo lo que backend-plus cuelga fuera de su contenedor\n document.addEventListener('click', alHacerClick, true);\n\n // `location.href = ...` no pasa por ningún clic: donde existe, la Navigation API avisa de cualquier navegación\n const navegacion = apiDeNavegacion();\n const alNavegar = (evento: EventoNavegacion) => {\n if (!evento.cancelable || evento.destination.sameDocument || evento.downloadRequest !== null) return;\n const ruta = rutaDeEnlaceBP(evento.destination.url, document.baseURI, urlBackend);\n if (ruta) {\n evento.preventDefault();\n navegar(ruta);\n }\n };\n navegacion?.addEventListener('navigate', alNavegar);\n\n // al editar un parámetro, backend-plus reescribe la dirección (`myOwn.replaceAddrParams`: `history.replaceState(null, null, 'menu#...')`)\n // y, con el `<base>` en la base-url del backend, eso deja la aplicación; `replaceState` no pasa por la Navigation API\n const reemplazarOriginal = history.replaceState;\n const reemplazar: History['replaceState'] = (estado, titulo, url) => {\n const ruta = url === undefined || url === null ? null : rutaDeEnlaceBP(String(url), document.baseURI, urlBackend);\n reemplazarOriginal.call(history, estado, titulo, ruta === null ? url : rutaBase + ruta);\n };\n history.replaceState = reemplazar;\n\n return () => {\n if (history.replaceState === reemplazar) history.replaceState = reemplazarOriginal;\n document.removeEventListener('click', alHacerClick, true);\n navegacion?.removeEventListener('navigate', alNavegar);\n };\n}\n"],"mappings":"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","names":["import_react","import_react","React","SnackbarAlert","MuiAlert","Snackbar","JSON4all","import_material","import_toolkit","import_toolkit","initialState","import_toolkit","initialState","storage","import_jsx_runtime","Dialog","DialogTitle","DialogContent","DialogContentText","DialogActions","Button","import_react","import_jsx_runtime","import_react","typeStore","import_react","import_react","import_type_store","condiciones","typeStore","import_react","import_react","import_material","import_material","import_jsx_runtime","ConstructionIcon","WarningAmberIcon","import_jsx_runtime","KeyboardArrowDownIcon","KeyboardArrowRightIcon","tableDefinition","filteredRows","import_react","filterHeight","import_react","React","RefreshIcon","ViewColumnIcon","VisibilityOffIcon","FileDownloadIcon","FileUploadIcon","DeleteSweepIcon","FilterAltIcon","import_react","JSON4all","import_material","import_react","import_jsx_runtime","AddIcon","SearchOffIcon","SearchIcon","MoreVertIcon","import_material","import_react","import_react","import_material","import_type_store","import_react","import_react","import_Search","import_react","import_material","import_jsx_runtime","import_react","import_material","import_jsx_runtime","import_react","import_material","import_jsx_runtime","import_jsx_runtime","React","IconButton","CalendarMonthIcon","SearchIcon","ArrowDropDownIcon","import_jsx_runtime","typeStore","cellBackgroundColor","transitionStyle","import_jsx_runtime","React","import_material","import_jsx_runtime","import_react","import_material","import_Add","import_material","import_jsx_runtime","import_material","import_WarningAmber","import_jsx_runtime","WarningAmberIcon","import_jsx_runtime","React","CeldaDato","AddIcon","DeleteIcon","ViewHeadlineIcon","import_react","import_react","import_material","import_type_store","import_jsx_runtime","typeStore","React","isMandatory","InputBase","import_jsx_runtime","React","import_react","import_material","import_FileUpload","import_jsx_runtime","FileUploadIcon","import_react","import_material","import_type_store","import_jsx_runtime","typeStore","import_react","import_material","import_jsx_runtime","CloseIcon","import_json4all","JSON4all","import_meta","import_jsx_runtime","definition","data","parametros","altoDeseado","import_react","import_react_router_dom","import_react","import_react_router_dom","import_material","import_react","import_react_redux","import_jsx_runtime","import_react","import_material","import_WarningAmber","import_react_router_dom","import_jsx_runtime","item","TableChartIcon","DnsIcon","FolderIcon","DesktopWindowsIcon","WarningAmberIcon","React","ExpandLess","ExpandMore","UnfoldLessIcon","UnfoldMoreIcon","import_react","import_react_router_dom","import_react_redux","import_jsx_runtime","theme","ChevronLeftIcon","MenuIcon","LogoutIcon","import_material","import_jsx_runtime","import_react_router_dom","import_jsx_runtime","import_react","import_material","import_jsx_runtime","import_react","import_material","import_react_redux","import_jsx_runtime","import_react","import_react_router_dom","import_material","import_material","import_jsx_runtime","import_jsx_runtime","React","import_react_redux","import_react","import_react","import_react_router_dom","import_jsx_runtime","import_react","import_react_router_dom","import_react","import_react_router_dom","import_material","import_Close","import_jsx_runtime","React","CloseIcon","import_react","import_react_router_dom","import_material","import_jsx_runtime","import_react","import_react_router_dom","import_material","import_jsx_runtime","parametros","import_react","import_react_router_dom","import_material","referencias","import_react","import_material","import_jsx_runtime","nombre","import_jsx_runtime","import_react","import_react_router_dom","import_material","import_jsx_runtime","import_react","import_react_router_dom","import_type_store","import_jsx_runtime","typeStore","React"]}
|