metis-desk/docs/contexto-listados-l03-1.md

24 KiB
Raw Blame History

L03.1 — Persistencia y restauración del contexto de listados (V05)

Fecha: 22 de octubre de 2026. Pilotos: Activos, Recursos y Personas. Referencias: L01, L02.2, L02.3, rutas y listados.

1. Resumen ejecutivo

Los pilotos añaden ordenación y página a su consulta navegable. Personas incorpora también búsqueda y Solo contactos a la URL, conservando su fallback local cuando está embebida fuera de una ruta propia. El tamaño de página y la visibilidad mantienen sus preferencias existentes. El retorno utiliza coordenadas e identidad de foco en la entrada de history.state, sin DTO, referencias DOM persistidas ni otra caché remota.

Activos conserva su ficha completa; Recursos conserva selección URL y panel/diálogo contextual. No se añaden rutas, endpoints, dependencias, cambios de backend o modificaciones vendor. Los archivos previos del usuario en docs/auditorias/ se conservan sin modificación.

2. Auditoría del estado anterior

Consumidor inspeccionado URL antes de L03.1 Estado local / persistido Desmontaje, retorno y acceso directo anteriores
Activos q, template; ficha con esos parámetros Sort/página en useEntityTable; selección contextual local; tamaño global y columnas en localStorage Ficha completa desmonta listado. Atrás recuperaba filtros, pero sort ascendente/página 1/foco BODY; horizontal se perdía.
Recursos q, template, selección en pathname Sort/página locales; apertura de panel y diálogo explícito temporales; tamaño persistido Seleccionar no desmonta tabla. Cierre compacto usa historial; cierre inline oculta panel conservando selección/ruta. Recarga mantenía filtros, pero no sort/página.
Personas Selección en pathname Búsqueda y Solo contactos en componente padre; sort/página en tabla; tamaño persistido Detalle sustituye tabla. Padre podía conservar filtros, pero se perdían al reconstruirse; sort/página se reiniciaban.
Puestos Ficha en pathname Búsqueda y editor locales; sort/página locales; tamaño persistido Hay ficha completa; la reconstrucción pierde el estado local. Pendiente L04.
Perfiles q, archived, perfil/configuración en pathname Sort/página locales; diálogos locales; tamaño persistido Entorno/configuración sustituye vista. URL reproduce filtros/destino, no todo el contexto. Pendiente L04.
Compañías q, activeOnly, companyId Tabla/controlador y selección local según consumidor; tamaño persistido Cambiar espacio reconstruye directorio. Filtros URL sobreviven; sort/página/scroll no tienen contrato completo.
Catálogo de activos q, tab Sort/página y editor/detalle/historial locales Los diálogos no cambian ruta; cambiar catálogo/sección puede desmontar. URL no representa sort/página.
Sublistado de puestos de una persona Ninguna consulta propia navegable Datos de queries compartidas; controles locales del sublistado Se desmonta con la ficha. No debe heredar la página/orden del listado de Personas.

Los datos remotos proceden de TanStack Query: inventarios/catálogos de Activos y Recursos, compañía y referencias de puestos/perfiles. No son preferencias ni contexto de navegación. Un panel/diálogo no equivale a abandonar la página; no se cambia ese contrato para uniformar. Esta matriz combina inspección del código y L01. Los consumidores ajenos a los tres pilotos no se presentan como una nueva validación visual con listados poblados.

La nueva reproducción previa, con 15 compañías y 50 personas/51 activos/66 recursos, conserva las medidas históricas de L01. En esta instalación hay 40 portátiles con q=AUDIT-ASSET:

Dimensión Antes de abrir ficha Atrás anterior, 1440×900 Atrás anterior, 390×844
Orden / página Descendente / 3 Ascendente / 1 Ascendente / 1
Búsqueda / plantilla AUDIT-ASSET / Portátil Conservadas Conservadas
Scroll X 0 escritorio, 40 móvil 0 0: pérdida de los 40 px
Scroll Y 100 100 en esta muestra 100 en esta muestra
Foco Enlace AUDIT-ASSET-24 BODY BODY

La muestra nueva no reproduce la pérdida vertical concreta de L01; las pruebas posteriores desplazan deliberadamente la ficha a otra coordenada para comprobar una restauración real.

3. Arquitectura del contexto

Responsabilidad Fuente única
Búsqueda/filtros/plantilla/sort/página URL, únicamente en consumidores habilitados
Tamaño / columnas Preferencias ya existentes
Scroll y origen del foco history.state.metisListReturn, con versión y ámbito
Registros y referencias TanStack Query y sus query keys existentes
Modalidad, selección y paneles Contratos V01/V06 del módulo y Base UI

@metis/core/client conserva su History API. useListLocation enlaza los nombres de parámetros que aporta el consumidor; no conoce TanStack Table ni columnas. rdx-ui recibe un EntityTableNavigation presentacional (values, onChange, ready), valida metadatos y utiliza los modelos/callbacks nativos de TanStack Table. No se añade dependencia core→rdx-ui.

4. Contrato URL

Parámetro de los pilotos Validación / valor inicial
q Búsqueda existente; límite de ruta 200 caracteres y semántica propia intacta.
template Activos/Recursos: identidad conocida en el catálogo. Se conserva pendiente de metadatos; después se normaliza a Todo si no existe.
contactsOnly Personas: true; ausente significa false. False/malformados se normalizan; no se añade a otros consumidores.
sort ID existente y ordenable; admite characteristic-<UUID> estable. Acciones/columnas desconocidas vuelven al orden inicial.
direction asc o desc, con columna válida. Una pareja inválida usa el orden inicial.
page Entero decimal positivo, índice compartible desde 1, seguro en JavaScript; se acota al conjunto disponible.

El orden inicial sigue las reglas existentes: declaración explícita, Nombre, primera columna ordenable o ausencia de orden. Se omiten sort/direction predeterminados y página 1. Se normalizan repeticiones de los parámetros de contexto; los parámetros ajenos no se sobrescriben. El parser del workspace sigue rechazando claves desconocidas y las rutas/identidades mal formadas.

La validación semántica de plantilla y contexto se realiza en el consumidor, una vez disponibles sus definiciones. El parser permite que esos valores lleguen a esa frontera sin convertir una página inválida o una columna retirada en una ruta de negocio nueva. Los demás contratos de validación HTTP y autorización no cambian.

Política aplicada: replace para búsqueda, filtros discretos, sort, página y normalizaciones. Son variaciones del mismo espacio operativo; Atrás debe volver de ficha a listado, sin recorrer cada pulsación/página de trabajo. Abrir otra sección o ficha mantiene push. La URL sigue siendo compartible y reproducible aunque no se cree una entrada por cada ajuste. Cambiar un filtro desde su control reinicia página; normalizar contactsOnly=false a ausencia conserva una página solicitada válida, porque no cambia el conjunto filtrado.

5. Preferencias

Se conservan entity-page-size y asset-inventory-columns, sus formatos, límites, defaults y tolerancia al almacenamiento bloqueado. No se trasladan a parámetros URL ni se borran IDs antiguos. El enlace compartido utiliza el tamaño preferido del receptor y acota la página si es necesario; no promete idéntica partición entre usuarios con tamaños distintos.

6. Contexto temporal de retorno

metisListReturn contiene versión 1, scope, URL de referencia, X/Y e identidad opcional de fila y href. Los números se validan como finitos/no negativos y acotados; los destinos son internos. Se conserva el resto de history.state, incluidos previous, create y propiedades ajenas. URL y href admiten hasta 8192 caracteres serializados: una búsqueda válida de 200 caracteres Unicode puede superar 2000 al codificarse. Este límite no amplía el contrato de búsqueda.

La captura se integra antes del push de la infraestructura existente y mediante focusin / scrollend de los contenedores del listado. Así también se conserva el contexto antes de recorridos nativos del historial. No se añade otro listener global independiente de popstate, ni temporizadores de sondeo, ni almacenamiento de DTO o nodos DOM.

El ámbito incluye compañía y listado. Cambiar filtros invalida una posición incompatible; una corrección automática de sort/página no impide recuperar y limitar coordenadas del mismo conjunto filtrado. Los snapshots no se transfieren a otra compañía.

7. Evolución de useEntityTable

navigation es opcional; sin ella continúa el estado no controlado de los consumidores existentes. Con ella, sorting y pageIndex efectivos se derivan de los valores externos y sus callbacks escriben la consulta. El tamaño sigue siendo preferencia local persistida. También se admiten initialSorting / initialPagination en el hook para inicialización explícita. En modo controlado, actualizar paginación solo guarda localmente cambios de tamaño; no mantiene una copia del índice externo.

Se conservan useReactTable, modelos de filtro/orden/paginación, selección, protección y visibilidad de columnas. No hay otro algoritmo de ordenación ni paginación. Los controles conservan aria-sort y aria-current. Cambiar sort reinicia la página desde su handler; cambiar criterios la reinicia desde el controlador del consumidor. No hay dos fuentes activas de verdad.

La normalización de URL es una sincronización con una frontera externa y espera ready: no acota página 3 a 1 por el conjunto vacío provisional del skeleton, ni elimina un campo dinámico mientras falta el catálogo. La reducción confirmada del conjunto limita la página.

8. Consumidores piloto

  • Activos: controlador conserva q/template; AssetList recibe navegación opcional y transmite el contrato a EntityTable. Los enlaces mantienen la consulta en la ficha completa.
  • Recursos: conserva q/template, selección en pathname y una sola tabla. Cierre inline sigue ocultando panel sin borrar ruta/selección; cierre compacto vuelve por History y su fallback directo conserva la consulta. No se introduce una ficha completa nueva.
  • Personas: usePeopleListContext encapsula URL y fallback embebido. Los enlaces y acciones de apertura conservan consulta; el botón Volver recupera la entrada del listado. El padre transmite frescura de compañía para no confirmar registros de una copia fallida.

9. Restauración de scroll y foco

El scroll vertical real es [data-slot=workspace-scroll]; el horizontal es el contenedor de la tabla. Se espera disponibilidad de datos y composición relevante, fuentes listas y el siguiente frame de layout. No se espera una cantidad fija de milisegundos.

Al volver se limita X/Y al máximo real. El foco vuelve al enlace del mismo data-entity-id; si cambió su href por una normalización, se usa un control de esa misma fila. Si desapareció o los datos no están verificados, se usa la búsqueda; no se selecciona otra fila arbitraria. En navegación directa sin contexto se orienta al título, respetando interacción ya iniciada. El enfoque usa preventScroll después de restaurar coordenadas.

BrowserLink navigation="return" reutiliza la entrada anterior solo si coincide con su destino; sin historial apropiado, reemplaza por el listado. Conserva enlaces reales, clic modificado, descarga y nueva pestaña. La ficha ausente conserva también acceso a Volver.

El contexto ya aplicado no se repite por un refetch. Si el usuario escribe durante la carga no se le roba el foco. Los diálogos siguen sus finalFocus; la restauración genérica no los enfoca ni abre. Recursos mantiene su instancia y Base UI conserva la orientación modal.

10. Datos y parámetros inválidos

Página inválida vuelve a 1; fuera de rango se limita a la última válida. Orden desconocido, no ordenable o dirección inválida vuelve al inicial. Una columna dinámica retirada solo deja de ser válida si tampoco existe en las instantáneas conservadas: no se elimina legado.

El vacío confirmado y los filtros sin coincidencias conservan L02.3. Error inicial y posterior no se confunden con desaparición. Un 503 posterior mantiene filas anteriores y contexto, pero no autoriza a usar esa caché como prueba para devolver el foco al registro. Personas recibe pending/ready/unverified del propietario de su consulta, sin una petición adicional.

11. Compatibilidad con Recursos y V01

Activos conserva el mínimo de contenedor 1144 px; Recursos 984 px. No cambian presupuestos, shell, rutas, intención de apertura, modalidad, observers ni reglas de resize. Un sublistado o picker no adopta la consulta del padre por usar EntityTable: debe habilitar navegación explícitamente. No se altera la selección provisional de los pickers ni V06.

12. Pruebas automatizadas

Las pruebas nuevas verifican parser/serializer, límites numéricos, IDs dinámicos, sort no ordenable, fallback, tabla controlada con filas/página efectivas, consumidor no controlado, History anterior/Adelante/retorno directo, propiedades ajenas y validación/aislamiento de scope. Las pruebas de rutas se ajustan al nuevo límite entre validación sintáctica y semántica.

scripts/list-context-ui.test.py utiliza Playwright MCP Docker/Chromium con sesión ordinaria, login real y datos sintéticos. Cubre matriz de retorno, recarga/enlace compartido, parámetros inválidos, resultados reducidos, Personas/contactos, Recursos contextual/directo, desaparición, refetch retenido, carga inicial retenida al recargar, compañía distinta, Escape sin navegación, campo dinámico retirado y errores simulados de Activos/Compañía. No escribe entidades.

Los comandos y resultados reales finales se recogen junto a las evidencias; no se equiparan las comprobaciones dirigidas correctas con las suites globales que mantienen deuda previa.

Ejecuciones y resultados

Comprobación ejecutada Resultado real
Typecheck dirigido de @repo/rdx-ui, @metis/core, @metis/assets, @metis/resources, @metis/companies Los cinco correctos; Compañías repetido tras el ajuste final de canonicalización.
pnpm --filter @repo/rdx-ui test 11/11 archivos correctos, incluido contrato controlado/no controlado.
pnpm --filter @metis/core test 3/3 archivos correctos; History, aislamiento y URL Unicode incluidos.
pnpm --filter @metis/assets test 27/27 archivos correctos.
pnpm --filter @metis/resources test 11/11 archivos correctos.
pnpm --filter @metis/companies test 40/41 archivos correctos. Falla companies-page.test.tsx, con las dos expectativas SSR ya documentadas en L02.3: Personas (1) y mensaje de acceso de persona. Rutas del workspace pasa.
Biome y ESLint dirigidos a los 28 archivos TypeScript modificados/nuevos Correctos, sin fixes ni supresiones; repetidos tras el último ajuste.
pnpm typecheck Falla en vendor calendar.tsx:165, TS2375 sobre locale y exactOptionalPropertyTypes, ya documentado. No se modifica vendor.
pnpm lint Falla: 15 errores, un warning y cuatro infos, como L02.3.
pnpm check Falla: 167 errores, un warning y cuatro infos; L02.3 registraba 168 errores. Los archivos propios del cambio pasan la comprobación dirigida.
pnpm test 7/8 tareas correctas; falla únicamente la tarea de Compañías anterior. Se ejecuta contra otra MariaDB temporal metis_desk_test, aislada de la instalación del navegador.
pnpm build 2/2 tareas correctas; permanece el aviso de chunks superiores a 500 kB.
Playwright L03.1 14 métodos distintos comprobados: ejecución principal 12/12, revisión 4/4 (incluye el nuevo caso 13) y revisión de Personas 3/3 (incluye el nuevo caso 14). Los métodos repetidos no se cuentan dos veces.
Regresiones Playwright L02.1 / L02.2 / L02.3 8/8, 8/8 y 8/8 métodos correctos, respectivamente.
Otras regresiones Playwright 9/9: navegación, capacidad de inventarios, contraste seleccionado, asistentes y políticas de foco.

Son 47 métodos distintos de navegador verificados, contando pilotos y regresiones. No se afirma una ejecución única de los 14 métodos nuevos: se conservan los logs de cada lote. Las pruebas comprueban filas reales, aria-sort/current, filtros, coordenadas y elemento activo; no se limitan a comparar la URL.

Para repetirlos, preparar la instalación y el volumen según Playwright MCP, mantener vivo su preparador y utilizar el archivo privado de sesión que este proporciona:

METIS_PLAYWRIGHT_STORAGE_STATE=.artifacts/playwright/authenticated-<id>/session.json \
  python3 scripts/list-context-ui.test.py

El script reutiliza los helpers MCP existentes, no inicia servicios y bloquea escrituras HTTP de negocio durante la inspección. Requiere acceso al Docker configurado y sesión ordinaria sintética activa; no sirve una sesión de producción. No se versionan archivos de sesión.

13. Evidencias Playwright

Directorio local ignorado: .artifacts/playwright/l03-1/, con baseline before/, capturas y mediciones nuevas en after/, resultados URL/DOM/X/Y/foco, consola, logs e índice. Las imágenes locales no son evidencia portable por sí solas: compartir expresamente PNG/mediciones sin sesiones. Se abren capturas representativas antes de concluir la revisión visual.

La matriz incluye 1920×1080, 1440×900, 1366×768, 1024×768, 768×1024, 390×844 y 320×640. El recorrido inicial corregido a 1440 y 390 conserva Nombre descendente, página 3 y el enlace AUDIT-ASSET-24. Se restauran 100 px verticales y, en móvil, 40 px horizontales; la suite ampliada usa 150 px y desplaza la ficha a otra coordenada para comprobar la recuperación.

Resolución Sort / página al regresar Scroll X/Y antes y después Foco al regresar
1920×1080 Nombre descendente / 3 0 / 0: la tabla cabe en altura Mismo enlace de registro
1440×900 Nombre descendente / 3 0 / 150 Mismo enlace de registro
1366×768 Nombre descendente / 3 0 / 150 Mismo enlace de registro
1024×768 Nombre descendente / 3 0 / 150 Mismo enlace de registro
768×1024 Nombre descendente / 3 40 / 150 Mismo enlace de registro
390×844 Nombre descendente / 3 40 / 150 Mismo enlace de registro
320×640 Nombre descendente / 3 40 / 150 Mismo enlace de registro

En las siete medidas coinciden consulta, filas e identidad del foco, sin overflow horizontal global. Se alterna Atrás y «Volver a activos»; a 1440 también se comprueba Adelante y otro Atrás. No se exige scroll vertical inexistente en 1920. Personas recupera contactos, descendente, página 2, X=40/Y=27 y enlace de origen en 390×844. Recursos se verifica a 1920 inline y a 390 compacto, con misma tabla, consulta y orden/página después del cierre.

La recarga con GET retenido en 320 mantiene page=3 y el orden descendente durante skeleton: no se fuerza una página efectiva antes de que existan filas. Al liberar los datos vuelve a página 3, X=40/Y=150 y al enlace original. Tras un 503 posterior, Activos y Personas conservan la tabla/contexto, pero enfocan búsqueda, porque la existencia del registro no está verificada.

Índice local: evidencias y logs. Mediciones de referencia: after/measurements-2776681.json (12 métodos), after/measurements-2784654.json (revisión y tamaño persistido) y after/measurements-2797155.json (Personas y canonicalización de false). Capturas abiertas e inspeccionadas: baseline y retorno móvil, Personas, carga retenida a 320, retorno de Personas sin frescura y cierre contextual de Recursos a 390.

Se utiliza una instalación propia desechable metis_desk_test, con 15 compañías, 50 personas, 51 activos y 66 recursos. No se precisan nuevos puestos/perfiles para estos pilotos; sus escenarios previos se mantienen como regresión. Las retiradas de registros/campos y los 503 son interceptaciones GET explícitas, no borrados ni incidencias espontáneas del servidor.

14. Regresiones

Se ejecutan suites dirigidas y regresiones de L02.1, L02.2, V12, navegación responsive, capacidad de inventarios, contraste seleccionado, asistentes, acciones y V06. Se conserva la carga estable y la tabla durante refetch. Se registran por separado intentos intermedios con errores de instrumentación/contexto Node-DOM o timeouts y las ejecuciones finales. Se corrigieron durante la entrega el reinicio prematuro al cargar, la frescura de Personas aportada por su consulta propietaria y la pérdida de página al canonicalizar un filtro false. El límite de snapshots se ajustó para URL Unicode válidas y se eliminó la copia local del índice controlado. Cada ajuste tiene comprobación dirigida o de navegador correspondiente. Los primeros errores de instrumentación MCP (DOM fuera de page.evaluate) y timeouts de lotes concurrentes/HMR no se ocultan ni se contabilizan como pruebas superadas; las ejecuciones finales se realizaron con menor concurrencia y pasaron.

15. Limitaciones

V05 se delimita a los tres pilotos y escenarios ejecutados; no se declara una restauración universal. No se acredita rendimiento de producción, lectores de pantalla, todos los motores o todas las variantes de historial externo. scrollend se verifica en Chromium de la imagen fijada. Los datos remotos pueden cambiar y las coordenadas se limitan a la geometría vigente.

Skills consultadas: ux-navigation principal; ux-tables, ux-search, vercel-composition-patterns, vercel-react-best-practices, shadcn y ux-error-recovery. Se aplican a orientación, continuidad operativa, fronteras de estado, composición y recuperación; no se introducen tendencias de búsqueda, rediseño ni bibliotecas. Prevalecen AGENTS/DESIGN y APIs reales. Los auditores especializados referidos por las skills no están disponibles; no se instalan ni se afirma haberlos ejecutado.

16. Consumidores pendientes para L04

Puestos, Perfiles, Compañías, catálogos, procesos/configuraciones y sublistados deben decidir su contrato navegable antes de adoptar el mecanismo. Un selector remoto conserva su cursor y búsqueda del proveedor: no debe serializarse como paginación local para uniformar.

V07 permanece pendiente. V08 y V12 conservan su alcance ya documentado. No se implementa L04 ni se modifica la normalización de las búsquedas de negocio.

L03.2 requiere otra instrucción: no se adelantan sus funcionalidades. L04 podrá reutilizar el contrato tras analizar desmontaje, semántica de selección y consultas de cada consumidor.

17. Archivos principales y cierre

  • packages/rdx-ui/src/lib/entity-table-navigation.ts, hooks/use-entity-table.ts, components/entity-list.tsx y components/entity-table-content.tsx: contrato opcional, callbacks nativos de tabla e identidad estable de filas; entity-list.test.tsx lo verifica.
  • modules/core/src/web/navigation/shared/: use-list-location.ts, use-list-return.ts, list-return-context.ts, History/BrowserLink y pruebas; exports públicos de navegación/core.
  • Activos: controlador de inventario, inventory-page.tsx, asset-list.tsx y ficha completa.
  • Recursos: controlador y resources-page.tsx, preservando panel y fallback de retorno.
  • Compañías: use-people-list-context.ts, people-section.tsx, composición de detalle de compañía, exports y parser/pruebas de rutas existentes.
  • scripts/list-context-ui.test.py: pruebas autenticadas nuevas con los helpers MCP existentes.
  • DESIGN.md, patrón de listados, componentes, rutas, seguimientos de ambas auditorías y este informe: responsabilidades, alcance verificado y consumidores pendientes.

No se modifica packages/shadcn-ui, shell responsive, estilos, contratos HTTP, base de datos, autenticación, dependencias ni docs/auditorias/. Se revisa el diff completo y enlaces locales. El preparador propio termina con código 0 y confirma la retirada del entorno temporal; se comprueba que su archivo privado de sesión ya no existe. Capturas y mediciones permanecen ignoradas en .artifacts/playwright/l03-1/, sin incorporar credenciales al repositorio.