# Recursos responsive y cierre de V01 — V01.4 ## Resumen y alcance Recursos utiliza capacidad real del contenedor exterior, con un presupuesto propio de tabla de 640 px, separación de 24 px y panel de 320–400 px: umbral W ≥ 984 px. Activos conserva 800 + 24 + 320 = 1144 px. Ambos consumen la misma coordinación de capacidad y foco en `@repo/rdx-ui`, manteniendo selección, rutas y operaciones en sus módulos. Esta entrega conserva los cambios previos de V01.3. No cambia el shell V01.1/V01.2, rutas, contratos HTTP, seguridad, datos persistentes, identidad visual, vendor ni dependencias. V04, V05, V07, V08, V10, V11 y V12 permanecen fuera de alcance. El estado formal se determina con los resultados de la regresión al final del informe; no se deduce del build ni de una inspección de código. ## Auditoría funcional previa y contratos conservados La revisión incluye ResourcesPage, su controlador, useEntityNavigation/closeRoute, ResourceSelectionPanel, ResourceDetailDialog, ResourceEditorDialog, DeleteResourceDialog, EntityTable, CompanyDetail y el coordinador del shell. No se presupone una ficha completa de recursos equivalente a la de activos. | Comportamiento | Activos | Recursos | Responsabilidad | | --- | --- | --- | --- | | Selección por zona no interactiva | Local en listado, sin cambiar URL | Selecciona la ruta `/companies/:id/resources/:resourceId`; embebido usa selección local | Cada módulo | | Nombre | Enlace a página completa Información/Historial | Enlace a la misma selección de recursos, conservando q/template | Cada módulo; BrowserLink conserva navegación nativa | | Panel | Datos, acordeones y relaciones propios | Características, integración, estado y contenido relacionado aportado por compañía | Composición específica | | Ficha e historial | Página completa | Diálogo explícito existente, también en escritorio; puede consultar otro recurso sin cambiar la selección previa | Cada módulo | | Cierre inline | Oculta sin perder selección | Oculta sin borrar selección ni cambiar URL | Visibilidad compartida, selección propia | | Cierre compacto voluntario | Conserva selección local | En ruta, utiliza selectEntity/closeRoute: vuelve a la ruta anterior/listado; embebido oculta detalle | Recursos conserva su semántica | | Detalle explícito de escritorio | No aplica | Suspende el panel y lo repone al cerrar si su contexto sigue visible; no cambia URL | Recursos | | Filtros/página | No borran selección si el elemento sigue en inventario | Igual; búsqueda por nombre y plantilla, filtro por identidad de plantilla | Módulos y EntityTable | | Desaparición del seleccionado | Aviso EntityNotFound | Mismo aviso previo; conserva nodo exterior y ofrece foco alternativo | Módulo | | Desaparición del consultado explícitamente | No aplica | Desaparece el diálogo y puede recuperarse el contexto seleccionado anterior | Recursos | | Edición | Diálogo propio y retorno V06 | Diálogo propio, borrador y selectores conservados; finalFocus sobre disparador o alternativa visible | Formularios de cada módulo | | Activación y borrado | Contratos de activos | setActive e invalidación de claves relacionadas; borrado usa operación existente y limpia selección/ruta solo tras éxito | Recursos, sin trasladarlo a rdx-ui | | Permisos | Sesión, pertenencia y contratos existentes | Mismos controles de servidor; editar se habilita con catálogo disponible, estado pendiente bloquea acciones según contrato previo | Servidor/módulos; sin permisos nuevos | | Medida y transiciones | Mismo mecanismo, presupuesto 800 | Mismo mecanismo, presupuesto 640 | Reutilización real en rdx-ui | Las acciones ancladas y su menú permanecen. No se ejecutan activaciones ni borrados en la auditoría: se comprueban opciones y cancelación. La restauración al abandonar una ficha y volver al listado sigue siendo V05. ## Arquitectura compartida `packages/rdx-ui/src/lib/contextual-detail-layout.ts` contiene el contrato `ContextualDetailBudget`, cálculo de modo/columnas y reducer de visibilidad. No importa módulos, DTO, identificadores, rutas ni consultas. Los presupuestos estables permanecen junto a cada listado: `INVENTORY_DETAIL_BUDGET` y `RESOURCE_DETAIL_BUDGET`. `useContextualDetail({ budget, initialVisible })` sustituye el hook específico de activos. Observa un único `containerRef` y publica modo discreto, inicialmente overlay. No guarda cada píxel ni usa innerWidth. Evita publicaciones repetidas y desconecta el observer al desmontar. El hook coordina reapertura, disparador, foco final y frames cancelables; no decide qué entidad está seleccionada. El contenedor publica `data-detail-mode` y `data-detail-open` con la presentación real. SelectionPanel publica `data-slot="selection-panel"`; su cabecera aporta el cierre equivalente. Ambos inventarios pasan `layout="inline"`, por lo que su visibilidad depende del coordinador y no del breakpoint inventory. Se conserva sin cambios la API predeterminada responsive de SelectionPanel y el hook exportado useCompactDetail; ya no deciden la presentación de activos ni recursos. No se inventan consumidores adicionales ni se elimina una API pública incidentalmente. La tabla permanece en la misma posición, sin keys por modo ni segunda instancia. El historial, las integraciones de correo/acceso, las referencias y CompanyResourceRelatedContent permanecen en recursos/compañías. En el diálogo explícito se retira el panel, evitando una copia interactiva detrás; la tabla sigue montada. ## Presupuesto propio y evidencia Se compararon en Chromium 600, 640 y 720 px de tabla, con nombre de carpeta de 83 caracteres, resumen técnico y las cuatro columnas iniciales, en español e inglés. A 640 px (638 interiores por los dos bordes de EntityTable), el nombre conserva unos 299–300 px, plantilla 163–164, estado unos 80 y acciones 96. No hay scroll horizontal en esa muestra. A 600, el nombre queda en unos 260–262 px; 720 no es necesario para esas cuatro columnas. Se adopta 640 para mejorar lectura sin imponer el mínimo de ocho columnas de activos. Panel = clamp(320 px, W − 664 px, 400 px). La única fuente del presupuesto alimenta tanto el umbral como las columnas. Los 640 px son una referencia del listado actual, no una constante universal ni la suma de columnas opcionales. El scroll horizontal interno continúa permitido; las columnas de recursos se conservan localmente durante la sesión del componente, sin añadir persistencia que antes no existía. Las pruebas del presupuesto están en `budget/measurements.json`, con capturas es/en abiertas e inspeccionadas. No se confunde ese prototipo temporal del navegador con la implementación posterior. ## Selección, transiciones y foco - Inline → overlay: conserva selección y URL, oculta panel y muestra reapertura explícita; nunca abre un modal por resize. Si el foco estaba en el panel va a reapertura; si estaba en la tabla permanece. - Overlay contextual abierto → inline: conserva contexto, retira modal, libera modalidad y enfoca el cierre del panel. Cerrado permanece cerrado. - Mismo modo: no reinicializa visibilidad, tabla, orden ni selección. Se verifican variaciones sin cruzar el umbral. - Ficha explícita: conserva su intención de diálogo aunque cambie capacidad. Al cerrarla se recupera la presentación contextual que siga disponible; no se transforma artificialmente en una página inexistente. - FinalFocus se delega a Base UI. La coordinación posterior al commit resuelve el disparador que vuelve a ser visible o el panel que se repone. Los frames se cancelan al desmontar. No se añade focus trap ni listener global a la aplicación. - El panel es complementario y su nombre accesible incluye el recurso. El diálogo conserva título, Escape, cierre y pie; campos y notas desplazan internamente. En edición el borrador persiste durante resize. Base UI puede cerrar un desplegable al cambiar su geometría, sin cerrar el editor. La página coordina también la retirada del diálogo por una actualización externa: repone el panel anterior y restaura el foco tras el commit, sin alterar la consulta ni eliminar datos. Se comprueba por separado la desaparición del recurso seleccionado y su aviso existente. El scroll se conserva cuando el nuevo contenido tiene recorrido suficiente. No se promete conservar una posición superior al máximo físico al desaparecer un panel largo. La prueba usa un desplazamiento conservable y comprueba por separado scroll horizontal en móvil. ## Entorno sintético y reproducibilidad Se amplía exclusivamente `scripts/visual-audit.py`. `prepare_resources` usa plantillas y campos identificados por builtinKey, crea mediante HTTP normal y reutiliza fixtures por nombre si ya existen en esa instalación. No cambia usuarios ni datos reales. Nueve pruebas Python cubren aislamiento de sesión, idempotencia, catálogo incompleto, estados y conservación de cero/false. Se preparan 16 recursos: doce aplicaciones para paginación, carpeta compartida, licencia de software, certificado inactivo y dominio Windows. Hay nombres cortos/largos, campos ausentes, fechas, número cero, booleano false y notas extensas. La carpeta y el dominio utilizan las integraciones automáticas reales. No se inventan publicaciones físicas, permisos SMB o cuentas de dominio. Las relaciones pobladas complejas no se simulan como si existieran. No existe plantilla estándar de recurso impresora en el catálogo; las impresoras físicas son activos. No se introduce un tipo artificial para esta auditoría. Tampoco se añaden usuarios permanentes: el administrador de preparación y el ordinario solo existen en la BD desechable. Se reutiliza la sesión ordinaria obtenida por login real. Se verifican lectura de los 16 recursos con 200, 401 sin sesión y 403 en gestión de usuarios. Las pruebas bloquean escrituras salvo la fixture ya existente de segunda compañía. No se instalan navegadores en WSL ni se altera la configuración global de Codex. Para repetir desde WSL: ```bash python3 scripts/visual-audit.py ``` Conservar el preparador abierto y usar la ruta privada que imprime: ```bash METIS_PLAYWRIGHT_STORAGE_STATE=.artifacts/playwright/authenticated-XXXX/session.json \ METIS_UI_AUDIT_DIRECTORY=.artifacts/playwright/v01-4/regression \ python3 scripts/visual-audit-ui.test.py python3 scripts/visual-audit.test.py pnpm --filter @metis/resources test pnpm --filter @metis/assets test pnpm --filter @repo/rdx-ui test pnpm --filter @metis/web test ``` La suite global requiere una BD de pruebas desechable separada; no conectar sus fixtures a una instalación existente. Ctrl+C retira exclusivamente el preparador propio y su sesión privada. ## Matriz antes/después Misma compañía sintética, preferencia global inicial y navegación de compañía fija cuando cabe. W es la caja exterior; tabla es el viewport interior. Se esperan carga, fuentes y frames. Cada fase de recursos contiene 22 estados: cerrado y seleccionado en once resoluciones. | Viewport | W | Tabla antes | Panel antes | Tabla después | Presentación después | | --- | ---: | ---: | ---: | ---: | --- | | 1920×1080 | 1403 | 977 | 400 | 977 | Inline, panel 400 | | 1660×900 | 1143 | 719 | 398,39 | 717 | Inline, panel 400 | | 1440×900 | 1079 | 707 | 345,59 | 653 | Inline, panel 400 | | 1366×768 | 1005 | 651 | 327,83 | 638 | Inline, panel 341 | | 1160×800 | 799 | 453 | 320 | 797 | Overlay | | 1100×800 | 980 | 634 | 320 | 978 | Overlay | | 1024×768 | 904 | 902 | Oculto por CSS | 902 | Overlay | | 920×768 | 800 | 798 | Oculto por CSS | 798 | Overlay | | 768×1024 | 720 | 718 | Oculto por CSS | 718 | Overlay | | 390×844 | 358 | 356 | Oculto por CSS | 356 | Overlay | | 320×640 | 288 | 286 | Oculto por CSS | 286 | Overlay | A 1440/1366 la tabla se ajusta a su presupuesto operativo propio mientras mejora el ancho del panel; no se afirma que todas las tablas crezcan. A 1100 y 1160 se elimina la compresión por un panel que no cabe. El panel oculto previo no permanece montado en overlay después de migrar. También se repite la matriz de activos tras la extracción, sin modificar su mínimo de 1144. El shell sigue decidiendo global/compañía con 920/1160/1660; abrir detalle no cambia su capacidad. ## Pruebas y evidencias Evidencias locales ignoradas por Git: `.artifacts/playwright/v01-4/`. - `before/`, `after/`: matriz de recursos, geometrías, capturas y consola. - `budget/`: comparación 600/640/720 con datos largos es/en. - `assets-after/`: repetición de las once resoluciones de activos. - Pruebas de recursos: límite 983/984/985/1064, selección por fila/nombre, URL, cierre inline/compacto, historial explícito, Atrás/Adelante, foco, edición, selector anidado, cancelación de borrado, estado/página/columnas/scroll, inglés y relaciones existentes. - Instrumentación de observer solo en test: uno activo por inventario, ninguna publicación de modo dentro del mismo tramo y disconnect al abandonar la sección. No entra en código funcional. - Regresión de V01.1/V01.2: franjas, cabecera, los 21 destinos, modos de shell, preferencia, ocultar/fijar, compañía, foco y scroll. - Regresión V01.3 y V02/V03/V06/V09: presupuesto 1144, tabla/contexto de activos, selección/contraste ya corregidos, asistentes, foco y cabeceras. ### Resultados técnicos | Comprobación ejecutada | Resultado final | | --- | --- | | Playwright MCP, V01.1–V01.4 y V02/V03/V06/V09 | **28/28 métodos correctos**, en cuatro grupos 12+8+7+1, repetidos tras la última corrección funcional. Ningún caso omitido. | | Matriz de recursos | 22 estados antes + 22 después, once resoluciones; sin overflow horizontal global. | | Matriz de activos tras extracción | 22 estados; geometrías de inventario/tabla/panel/diálogo idénticas a V01.3. | | Consola MCP de las cuatro suites finales y las matrices | Cero errores y warnings registrados. No equivale a una auditoría de accesibilidad completa. | | `python3 scripts/visual-audit.test.py` | **9/9** correctos. | | Tests dirigidos de `@repo/rdx-ui`, `@metis/assets`, `@metis/resources`, `@metis/web` | **56/56, 138/138, 80/80 y 16/16**, respectivamente. | | Contratos de navegación dirigidos | **16/16**, incluidos History, rutas y preferencia responsive. | | Ejecución directa de contratos de selección/activos/recursos | **8/8, 24/24 y 12/12**; subconjuntos de las suites anteriores, no casos adicionales que sumar a ellas. | | Typecheck dirigido de rdx-ui, assets, resources y web | Correcto en los cuatro paquetes. | | Biome y ESLint de espaciado sobre los archivos afectados | Correctos. Se corrigió una línea vacía entre funciones detectada por ESLint; diagnóstico inicial conservado. | | `pnpm typecheck` | **Falla, exit 2**: mismo TS2375 de `packages/shadcn-ui/src/components/calendar.tsx:165`, por locale opcional y exactOptionalPropertyTypes. No se toca vendor. | | `pnpm lint` | **Falla, exit 1**: mismos 15 errores, 1 warning y 4 infos previos, sin diagnósticos en los archivos afectados. | | `pnpm check` | **Falla, exit 1**: mismos 168 errores, 1 warning y 4 infos previos. No se reformatean archivos ajenos. | | `pnpm test`, MariaDB temporal independiente | **Falla, exit 1**: mismas dos expectativas SSR de compañías: «Personas (1)» y «Esta persona no tiene acceso a Metis Desk». 7 tareas correctas de 8 ejecutadas; la suite global no se completa. BD retirada. | | `pnpm build` | Correcto, exit 0; conserva el aviso de chunk mayor de 500 kB. | La comparación con V01.3 queda en `technical/comparison.json`: mismo error TypeScript, mismos totales y conjunto de diagnósticos visibles de lint/check, ninguna incidencia en archivos cambiados y mismas expectativas fallidas de compañías. Biome dirigido comprueba íntegramente los archivos afectados, además del límite de diagnósticos de la ejecución global. No se presenta el monorepo como totalmente verde. Se comprobaron 206 destinos de enlaces locales en los documentos afectados y git diff --check, sin incidencias. El diff no incorpora archivos privados, capturas ni cambios de vendor. `verification.json` enumera los 28 métodos únicos y su cobertura real. `screenshots.json` indexa los PNG con dimensiones y SHA-256; `README.md` organiza las evidencias. `technical/visual-*.log` conserva las pasadas finales y `technical/intermediate-*` los diagnósticos previos. Se abrieron capturas representativas de `after/selected-1366.png`, `after/selected-1100.png`, `after/selected-320.png`, presupuesto largo es/en, transferencias de foco y desaparición externa. Las dos esperas de navegación intermedias no se reprodujeron en la pasada final. La corrección del caso adicional de desaparición externa se verificó primero de forma aislada y después dentro de los cuatro grupos finales. Las ejecuciones fallidas permanecen identificadas como tales. ## Regresiones y límites Se detectó foco en BODY al cerrar el diálogo explícito y reponer el panel: el disparador anterior se había desmontado. Se añadió coordinación posterior al commit con alternativa visible. La regresión integral detectó además una lectura prematura de visibilidad del disparador al cerrar el detalle de activos; se coordina la restauración una vez actualizado el DOM. Una prueba adicional detectó el mismo riesgo al desaparecer externamente un recurso consultado en diálogo: al no pasar por onClose, faltaba la coordinación del foco. La página observa esa retirada real del DOM y restaura el destino tras reponer el contexto. Se verifican tanto el consultado como el seleccionado mediante transformación de respuestas HTTP, sin borrar recursos. No se introducen excepciones de autenticación ni supresiones de tipos/lint para resolverlo. Las primeras pruebas también corrigieron supuestos del guion: el máximo de scroll se reduce físicamente al retirar el panel, y el selector de enlace por sufijo coincidía con más de un destino. Se usa desplazamiento conservable y href exacto. Estos errores de prueba no se atribuyen a reglas de negocio. Dos esperas de navegación de una pasada intermedia agotaron su timeout, sin errores de consola registrados. Se conservan sus logs y se repiten los escenarios; no se atribuye una causa de infraestructura no demostrada ni se ocultan como aprobados. No se afirma cumplimiento WCAG completo, validación con lector de pantalla, zoom nativo ni otros motores. La selección visual de recursos conserva sus colores previos; V02 se verifica en el alcance corregido del inventario de activos, sin extender su implementación a otros hallazgos. ## Archivos y skills | Área | Ficheros principales y finalidad | | --- | --- | | Biblioteca propia | `packages/rdx-ui/src/hooks/use-contextual-detail.ts`, `lib/contextual-detail-layout.ts`: capacidad, transiciones y foco. `components/selection-panel.tsx`: marcador del panel; conserva layout predeterminado. | | Activos, continuidad de V01.3 | `list/helpers/inventory-detail-layout.ts`, `list/controllers/use-inventory-page-controller.ts`, `list/ui/inventory-page.tsx`: consumen la extracción sin alterar el presupuesto. `detail/ui/asset-selection-panel.tsx` y `asset-detail-dialog.tsx`: presentación y finalFocus de V01.3 conservados. | | Recursos | `list/helpers/resource-detail-budget.ts`, `list/controllers/use-resources-page-controller.ts`, `list/ui/resources-page.tsx`: presupuesto y composición específicos. `detail/ui/resource-selection-panel.tsx`, `resource-detail-dialog.tsx`, `shared/ui/resource-editor-dialog.tsx`: nombre accesible y destinos de foco. | | Traducciones | `modules/{assets,resources}/src/common/locales/{es,en}.json`: reapertura del detalle seleccionado; activos conserva el cambio previo. | | Contratos unitarios | `packages/rdx-ui/src/selection-components.test.tsx`, pruebas existentes `assets/shared/inventory.test.tsx` y `resources/shared/resources.test.tsx`. | | Preparación y navegador | `scripts/visual-audit.py`, `visual-audit.test.py`, `visual-audit-ui.test.py`: recursos sintéticos y siete escenarios nuevos, junto con los 21 existentes. | | Documentación | Este informe, DESIGN, componentes, patrón de listados, propuesta V01, auditoría, rutas, procedimiento MCP y seguimiento histórico V01.3. | Las rutas abreviadas de módulos parten de `modules//src/web//`. El hook específico de activos se extrae a rdx-ui; no se conserva una copia paralela. Se consultaron las skills ya leídas en la sesión: ux-tables como principal (comparación y densidad), ux-navigation (contratos de URL/foco), shadcn con referencias Base UI/composición (modalidad existente), vercel-composition-patterns (frontera módulo/biblioteca) y vercel-react-best-practices (modo discreto, efectos y cleanup). Se leyó además ux-inputs-and-forms para verificar borrador, cancelación y selectores; no se aplican recetas de campos/layout que contradigan el patrón Metis. No se requiere Pro Max para una decisión estética adicional. Los auditores especializados ausentes de la matriz no se instalan ni se presentan como ejecutados. ## Estado formal de V01 **V01 resuelto en los escenarios verificados de V01.1–V01.4, a 22 de octubre de 2026.** | Criterio de cierre | Evidencia | | --- | --- | | Móvil y orientación de compañía/sección | Cabecera, franja, 21 destinos, Sheet, teclado y foco; regresión de navegación correcta. | | Portátil/tablet/escritorio | Once resoluciones y límites de shell 920/1160/1660; preferencia e intención conservadas. | | Ambos inventarios | Umbrales reales 1144/984; tabla operativa, un solo detalle, sin modal automático por contracción. | | Contratos y continuidad | Selección, URL, cierre, historial, edición, estado del listado y scroll comprobados según cada entidad. | | Sin regresiones propias pendientes | 28/28 métodos visuales, checks dirigidos y build correctos; tres escenarios de foco detectados y corregidos, incluida desaparición externa. | | Documentación y evidencia | Matrices, logs, consola, índice PNG y seguimiento sin borrar la línea base. | Se envió SIGINT únicamente al preparador identificado por sus logs. Terminó con exit 0, retiró su contenedor y borró session.json y database.env. Los cuatro contenedores ajenos permanecieron activos; la BD temporal de la suite global también se retiró. Comprobación en `cleanup.json`. Las capturas y metadatos sintéticos siguen disponibles localmente, excluidos de Git. Este cierre no resuelve los fallos globales preexistentes ni extiende el alcance a V04, V05, V07, V08, V10, V11 o V12. V02/V03/V06/V09 conservan su estado en los escenarios sometidos a regresión; no se declara accesibilidad WCAG completa.