From 73655f86c09538b0eaabd29ba412525594d92f3c Mon Sep 17 00:00:00 2001 From: David Date: Wed, 21 Oct 2026 22:55:13 +0200 Subject: [PATCH] feat(companies): implement company navigation and context components - Added `useCompanyNavigationEntries` for defining navigation entries. - Created `CompanyContextBar` for displaying company information and navigation. - Developed `CompanyHeader` for showing company identity and status. - Introduced `CompanyNavigationSheet` for mobile navigation options. - Implemented `CompanySectionNavigation` for section-specific navigation. - Added `workspaceContentFocusTarget` to manage focus behavior. - Created `WorkspaceMobileNavigationProvider` for managing mobile navigation state. - Developed `WorkspaceMobileSheet` for consistent mobile sheet UI. - Added `WorkspaceSkipLink` for accessibility improvements. - Established `WorkspaceMobileNavigationContext` for navigation state management. - Created `WorkspaceNavigationContent` for displaying workspace navigation. - Implemented `WorkspaceSearchContent` and `WorkspaceSearch` for search functionality. --- AGENTS.md | 21 +- DESIGN.md | 10 +- apps/web/src/app.test.tsx | 25 ++ apps/web/src/global.css | 12 + docs/componentes-de-interfaz.md | 11 +- docs/navegacion-movil-v01-1.md | 96 ++++++ docs/propuesta-navegacion-responsive-v01.md | 4 +- docs/skills-frontend-ui-ux.md | 310 ++++++++++++++++++ modules/companies/src/common/locales/en.json | 9 + modules/companies/src/common/locales/es.json | 9 + .../helpers/use-company-navigation-entries.ts | 51 +++ .../detail/ui/company-context-bar.tsx | 66 ++++ .../companies/detail/ui/company-detail.tsx | 224 ++++--------- .../companies/detail/ui/company-header.tsx | 57 ++++ .../detail/ui/company-navigation-sheet.tsx | 48 +++ .../detail/ui/company-section-navigation.tsx | 100 ++++++ .../companies/shared/companies-page.test.tsx | 41 +++ .../shared/helpers/workspace-content-focus.ts | 11 + .../workspace-mobile-navigation-provider.tsx | 93 ++++++ .../shared/ui/workspace-mobile-sheet.tsx | 71 ++++ .../shared/ui/workspace-navigation-button.tsx | 45 ++- .../shared/ui/workspace-skip-link.tsx | 14 + .../workspace-mobile-navigation-context.ts | 18 + .../workspace/view/ui/companies-workspace.tsx | 158 +++++---- .../workspace/view/ui/workspace-header.tsx | 121 +++---- .../view/ui/workspace-navigation-content.tsx | 220 +++++++++++++ .../view/ui/workspace-search-content.tsx | 106 ++++++ .../workspace/view/ui/workspace-search.tsx | 38 +++ .../workspace/view/ui/workspace-sidebar.tsx | 222 ++----------- packages/rdx-ui/src/lib/icons.ts | 1 + scripts/visual-audit-ui.test.py | 180 +++++++++- 31 files changed, 1841 insertions(+), 551 deletions(-) create mode 100644 docs/navegacion-movil-v01-1.md create mode 100644 docs/skills-frontend-ui-ux.md create mode 100644 modules/companies/src/web/companies/detail/helpers/use-company-navigation-entries.ts create mode 100644 modules/companies/src/web/companies/detail/ui/company-context-bar.tsx create mode 100644 modules/companies/src/web/companies/detail/ui/company-header.tsx create mode 100644 modules/companies/src/web/companies/detail/ui/company-navigation-sheet.tsx create mode 100644 modules/companies/src/web/companies/detail/ui/company-section-navigation.tsx create mode 100644 modules/companies/src/web/workspace/shared/helpers/workspace-content-focus.ts create mode 100644 modules/companies/src/web/workspace/shared/ui/workspace-mobile-navigation-provider.tsx create mode 100644 modules/companies/src/web/workspace/shared/ui/workspace-mobile-sheet.tsx create mode 100644 modules/companies/src/web/workspace/shared/ui/workspace-skip-link.tsx create mode 100644 modules/companies/src/web/workspace/shared/workspace-mobile-navigation-context.ts create mode 100644 modules/companies/src/web/workspace/view/ui/workspace-navigation-content.tsx create mode 100644 modules/companies/src/web/workspace/view/ui/workspace-search-content.tsx create mode 100644 modules/companies/src/web/workspace/view/ui/workspace-search.tsx diff --git a/AGENTS.md b/AGENTS.md index 72b58d6..cbc1a13 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -218,22 +218,13 @@ Estas reglas complementan las convenciones y documentos de arquitectura existent ## UI/UX — Instrucciones obligatorias -Antes de implementar o modificar interfaces de usuario: +Antes de tareas relevantes de frontend o UI/UX, consultar la [matriz de skills](docs/skills-frontend-ui-ux.md#selección-por-tarea): -1. Identificar las skills de UI/UX, diseño visual, accesibilidad y componentes frontend disponibles en el entorno de Codex. -2. Leer el `SKILL.md` de cada skill relevante y sus referencias aplicables. -3. Analizar las recomendaciones en relación con la arquitectura frontend existente de Metis Desk. -4. Reutilizar los componentes de shadcn/ui y `@repo/rdx-ui`, así como los patrones visuales establecidos en el proyecto. -5. Evitar incorporar nuevos sistemas de diseño, librerías de componentes o estilos incompatibles. -6. Aplicar los criterios de las skills durante el diseño, la implementación y la revisión. -7. Verificar accesibilidad, usabilidad, estados de carga, errores, responsive y coherencia visual. -8. Indicar en el informe final qué skills se han consultado y cómo han influido en la implementación. - -No afirmar que una skill ha sido aplicada si no se ha leído realmente. - -Si alguna skill no está disponible, identificarla y comunicar la limitación. - -Prioridad de diseño: convenciones específicas de Metis Desk y requisitos funcionales acordados, complementados por los criterios pertinentes de las skills. +- Seleccionar una skill principal según el objetivo y complementarias solo cuando aporten valor. Comprobar su disponibilidad y leer sus `SKILL.md` y referencias aplicables antes de utilizar sus criterios en diseño, implementación y revisión. +- Respetar AGENTS.md, DESIGN.md y los patrones especializados; comprobar las APIs reales instaladas. Las skills complementan estas fuentes, no las sustituyen; una desviación del código respecto al patrón se señala, no se convierte en norma. +- Reutilizar shadcn/ui y `@repo/rdx-ui`. Las recomendaciones genéricas de una skill no autorizan regenerar o modificar vendor, introducir dependencias ni crear un sistema visual alternativo. +- Comprobar comportamiento visual, accesibilidad, teclado, foco, usabilidad, responsive y estados de carga/error/vacío mediante [Playwright MCP](docs/playwright-mcp.md) cuando corresponda y el entorno lo permita. Un checklist no sustituye las pruebas; seguir el procedimiento de evidencias y datos sintéticos existente. +- Informar qué skills se han consultado y cómo han influido, cuáles faltan y qué comprobaciones no se han ejecutado. No afirmar una aplicación o validación no realizada. La ausencia de auditores especializados no impide las verificaciones disponibles con Playwright MCP ni autoriza instalarlos automáticamente. #### Componentes visuales diff --git a/DESIGN.md b/DESIGN.md index 1c2c5b0..ae9f4b4 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -32,7 +32,7 @@ La precedencia de instrucciones es la de AGENTS: petición actual del usuario, A | [Componentes](docs/componentes-de-interfaz.md), [rutas](docs/rutas-del-cliente.md) y [estructura web](docs/estructura-web-por-entidades-y-operaciones.md) | Contratos de composición, navegación y organización | | Tokens y componentes implementados | Valores efectivos y APIs reales; su existencia no convierte una desviación en pauta correcta | | [Auditoría visual](docs/auditoria-ui-ux-visual.md) | Evidencias, escenarios comprobados y limitaciones; no prescribe por sí sola un nuevo diseño | -| Skills instaladas | Criterios complementarios, subordinados a las decisiones específicas de Metis | +| [Matriz de skills frontend y UI/UX](docs/skills-frontend-ui-ux.md) | Selección por tarea y criterios complementarios, subordinados a las decisiones específicas de Metis | Ante una contradicción, comprobar normativa especializada, consumidor y evidencia; identificar qué está obsoleto y corregir documentación dentro del alcance autorizado. No copiar una inconsistencia del código ni presentar una propuesta como implementada. Por ejemplo, el patrón exige altura estable aunque ciertos asistentes todavía no la aplican. @@ -142,7 +142,9 @@ Implementación representativa: [CompaniesWorkspace](modules/companies/src/web/w | Panel de inventario `clamp(320px,24vw,400px)` con gap-6 | [AssetInventoryPage](modules/assets/src/web/assets/list/ui/inventory-page.tsx) | | Detalle compacto hasta 1099 px | [useCompactDetail](packages/rdx-ui/src/hooks/use-compact-detail.ts) | -Estos son umbrales existentes, **no mínimos operativos validados para layouts futuros**. En móvil el shell se apila y la navegación de compañía precede al contenido; V01 demuestra pérdida importante de espacio útil. Queda pendiente diseñar y validar su compactación y la decisión tabla/panel por contenedor. No se fijan aquí nuevos breakpoints, drawers o anchos mínimos. +Estos son umbrales existentes, **no mínimos operativos validados para layouts futuros**. V01.1 implementa la composición móvil por debajo de `shell` (768 px): cabecera compacta, franja de compañía/sección y navegación global/contextual mediante un único Sheet modal abierto a la vez. La búsqueda móvil reutiliza la lógica existente y el salto al contenido conserva foco accesible. En los datos sintéticos verificados, `main` comienza en Y=106 px a 320 y 390 px; la ampliación de texto puede aumentar ese alto. Desde 768 px se conserva la composición anterior. Véase [entrega y validación V01.1](docs/navegacion-movil-v01-1.md). + +V01 sigue parcialmente pendiente: no se implementan aún colapso automático en portátil/tablet, nuevos presupuestos ni tabla/panel por capacidad. `inventory`, `dashboard` y `useCompactDetail` mantienen sus contratos. Usar [BrowserLink y rutas existentes](docs/rutas-del-cliente.md) para destinos reales; botones para acciones, selección local y modales. Conservar acceso directo, recarga, Atrás/Adelante, clic modificado y sección activa accesible. No inventar rutas de edición o fichas para entidades que no las tienen. Si el esquema narrativo de rutas queda desactualizado, contrastarlo con `workspace/shared/helpers/workspace-routes.ts`; no cambiar el modelo de negocio para adaptar una URL. @@ -233,7 +235,7 @@ Vigente: distribución por contenedor para grupos de formulario, orden DOM conse - Escritorio amplio: mostrar datos comparables y paneles cuando quepan; evitar márgenes ornamentales y tarjetas sin función. - Portátil/tablet: evaluar el ancho restante después de las navegaciones y el panel. Los 768/1100 px actuales no garantizan espacio operativo, V01. -- Móvil: contenido y acciones alcanzables sin desbordamiento global horizontal; la tabla puede tener scroll propio. La navegación compacta de compañía sigue por diseñar, sin imponer aquí una solución. +- Móvil: contenido y acciones alcanzables sin desbordamiento global horizontal; la tabla puede tener scroll propio. V01.1 mantiene compañía y sección en una franja sticky, y sus destinos en Sheet; la adaptación por capacidad de portátil/tablet sigue pendiente. - Ventanas bajas: altura dinámica, cuerpo desplazable y pie visible; no reducir campos para hacerlos caber. El presupuesto de ayuda y la versión compacta de progreso siguen pendientes, V04. - Formularios y tablas extensas: usar grupos/pestañas/componentes existentes; truncar solo con acceso al valor completo cuando sea necesario. La búsqueda del selector de columnas, V08, y el resumen de ficha, V10, requieren implementación o diseño adicional. @@ -253,7 +255,7 @@ V02, V03, V06 y V09 están **resueltos en los escenarios verificados del sprint | ID | Tipo y alcance | Próxima decisión o comprobación | | --- | --- | --- | -| V01 | Diseño de layout/navegación + implementación | Diseñar por capacidad real; validar móvil, tablet y tabla/panel antes de fijar nuevos umbrales | +| V01 | Implementación parcial: V01.1 móvil | Móvil verificado; pendientes política de portátil/tablet, tabla/panel por capacidad y regresión final de V01 | | V02 | Resuelto: combinación de tokens/estados del inventario claro | Contraste y foco comprobados; no equivale a validar todos los temas o tablas | | V03 | Resuelto: adopción de patrón y pie estable | Tres pasos de activos/recursos verificados en los cuatro tamaños del sprint | | V04 | Diseño de ayuda/progreso compacto + componentes | Acordar presupuesto de altura sin perder ayuda junto al pie; verificar 320×640 | diff --git a/apps/web/src/app.test.tsx b/apps/web/src/app.test.tsx index 017795d..51386bd 100644 --- a/apps/web/src/app.test.tsx +++ b/apps/web/src/app.test.tsx @@ -394,3 +394,28 @@ test("usuarios: pantalla integrada es/en, solo para el administrador, con altas } } }); + +test("el shell ofrece menú, búsqueda y salto al contenido es/en sin compañía ni modales abiertos", async () => { + for (const language of ["es", "en"]) { + const html = await renderPage( + language, + { user, csrfToken: "a".repeat(64), setupRequired: false }, + (client) => client.setQueryData(companyKeys.list, { companies: [] }) + ); + + assert.ok( + html.includes(language === "es" ? 'aria-label="Menú global"' : 'aria-label="Global menu"') + ); + assert.ok( + html.includes( + language === "es" ? 'aria-label="Buscar en Metis Desk"' : 'aria-label="Search Metis Desk"' + ) + ); + assert.ok(html.includes(language === "es" ? "Saltar al contenido" : "Skip to content")); + assert.ok(html.includes('href="#workspace-main"')); + assert.ok(html.includes('id="workspace-main"')); + assert.equal(html.includes('role="dialog"'), false); + assert.equal(html.includes('data-slot="company-context-bar"'), false); + assert.equal(html.includes('href="/settings/users"'), false); + } +}); diff --git a/apps/web/src/global.css b/apps/web/src/global.css index cd10bf8..885a064 100644 --- a/apps/web/src/global.css +++ b/apps/web/src/global.css @@ -142,3 +142,15 @@ [data-slot="dialog-content"] [data-slot="dialog-header"] { background-color: var(--popover); } + +/* La navegación móvil comparte un único scroll y reserva el bloque sticky al enfocar. */ +@media (width < 768px) { + [data-slot="workspace-scroll"] { + scroll-padding-block-start: 128px; + } + + [data-workspace-main], + [data-workspace-main] :is(h1, h2, input, button, a, select, textarea) { + scroll-margin-block-start: 128px; + } +} diff --git a/docs/componentes-de-interfaz.md b/docs/componentes-de-interfaz.md index ea4690e..0e4526f 100644 --- a/docs/componentes-de-interfaz.md +++ b/docs/componentes-de-interfaz.md @@ -268,21 +268,22 @@ Los tokens Tailwind 4 `--color-shell-*` de `apps/web/src/global.css` aíslan la colores semánticos del resto del producto: superficie negra exterior, contenido #F7F9FB, sidebar #0E282C, acento turquesa, texto y borde azul, radios 8/10/20px, sidebar 228px, header 88px, búsqueda 41px, fila de navegación 48px y avatar 35px. -En móvil se conserva la navegación horizontal y envuelta, sin introducir drawer. +Por debajo de 768 px, V01.1 sustituye la navegación apilada por cabecera compacta y Sheet modal. La cabecera móvil y la franja de compañía son sticky dentro del scroll principal. La búsqueda usa un botón y el mismo estado/lógica de compañías y destinos en una presentación compacta; la cuenta mantiene su disclosure y acciones. No cambia la búsqueda funcional ni añade persistencia de menús. El buscador usa Input de shadcn/ui y filtra compañías cargadas y destinos globales; Escape limpia y los resultados se recorren mediante Tab. No existe un servicio de búsqueda transversal de entidades. La campana no muestra avisos ficticios: no existe servicio de notificaciones. El perfil usa disclosure nativo con foco visible y -conserva las acciones de identidad y selección de idioma. No hay primitives Avatar -ni DropdownMenu instalados; el avatar muestra iniciales del usuario real, sin -inventar foto ni cargo. Configuración abre un FormDialog con las acciones existentes. +conserva las acciones de identidad y selección de idioma. El avatar muestra iniciales +del usuario real, sin inventar foto ni cargo. Configuración abre un FormDialog con las acciones existentes. La sidebar global permite contraerse en escritorio de 228px a 72px mediante «Contraer menú» / «Expandir menú». Conserva iconos, nombres accesibles y títulos de ayuda también para compañías y subcatálogos. La navegación se desplaza por separado, manteniendo Configuración y el control de anchura en el pie visible. -En móvil se conserva la navegación horizontal existente. +En móvil, `WorkspaceNavigationContent` reutiliza esos destinos dentro de `WorkspaceMobileSheet`. `WorkspaceMobileNavigationProvider` controla una única raíz Sheet, con estado efímero de panel/apertura y retorno de foco. Cambiar compañía desde el Sheet contextual cambia al global sin apilar dos modales; Escape/cerrar restaura el disparador y navegar enfoca el contenido. Cruzar a 768 px cierra la modalidad y devuelve foco a un destino visible. No se persiste apertura ni se modifica el colapso desktop. + +`useCompanyNavigationEntries` concentra los 21 destinos y traducciones; `CompanySectionNavigation` los presenta como enlaces reales cuando existe navegación por URL y botones en el modo local. `CompanyContextBar` muestra nombre completo accesible, sección y activador contextual, con tooltip al foco/hover para nombres truncados. `CompanyNavigationSheet` conserva contadores y edición con las mismas restricciones previas. `WorkspaceSkipLink` lleva al `main` activo. La tabla y `useCompactDetail` no cambian. Evidencias y límites: [V01.1](navegacion-movil-v01-1.md). El plegado se conserva entre recargas y sesiones mediante la clave `metis-desk:sidebar-collapsed` de `localStorage`, compartida por las sesiones de diff --git a/docs/navegacion-movil-v01-1.md b/docs/navegacion-movil-v01-1.md new file mode 100644 index 0000000..758d067 --- /dev/null +++ b/docs/navegacion-movil-v01-1.md @@ -0,0 +1,96 @@ +# V01.1 — Navegación móvil y franja contextual + +Primera entrega de la [arquitectura responsive V01](propuesta-navegacion-responsive-v01.md), 21 de octubre de 2026. V01 sigue parcialmente pendiente: esta entrega solo modifica la composición por debajo de 768 px. + +## Comportamiento implementado + +- Cabecera móvil compacta: menú global, marca, búsqueda y cuenta. La búsqueda reutiliza estado, filtrado y destinos actuales, sin consultar activos/personas. +- Franja sticky con compañía, sección actual y botón Secciones; nombre completo accesible y tooltip para texto truncado. No hay 21 enlaces apilados antes del contenido. +- Sheet global con destinos, compañías y configuración; Sheet contextual con los 21 destinos, contadores, cambio de compañía y edición existentes. Una raíz modal y una presentación activa; el cambio contextual → global conserva modalidad sin apilar Sheet. +- Destinos con BrowserLink nativo, `aria-current` y estilos de Button; botones para acciones. Enter navega, Espacio conserva la interacción nativa de enlaces. Se verificaron Ctrl+clic y clic central abriendo otra pestaña, sin cerrar el menú original ni cambiar su URL. +- Escape/cierre devuelve foco al disparador; navegación ordinaria lo traslada al contenido. Cancelar edición de compañía desde el menú devuelve foco a Secciones. Base UI conserva el trap y el bloqueo de fondo; no se implementan manualmente. +- Enlace Saltar al contenido al inicio del teclado, con destino `main` real y separación del bloque sticky. Scroll vertical principal único; scroll propio en menús extensos y tabla horizontal cuando lo requiere. +- Cruzar a 768 px cierra el menú y devuelve foco visible; volver a móvil no reabre menús. Apertura efímera, sin nuevas preferencias. + +Las barras de escritorio, el colapso manual 228/72, la secundaria de 240, los filtros, paginación, rutas y permisos permanecen con sus contratos anteriores. La edición usa la acción ya disponible en la ficha: no se inventa un permiso ni se amplía acceso. No cambian `inventory=1100`, `dashboard=1200` ni `useCompactDetail`; V01.2–V01.4 quedan pendientes. + +## Componentes y decisiones + +`CompaniesWorkspace` compone `WorkspaceMobileNavigationProvider`, salto al contenido, sidebar y cabecera sobre el scroll existente. `WorkspaceMobileSheet` encapsula popup, título, cierre traducido y foco; el consumidor aporta contenido. Los controles de modalidad pertenecen a Base UI. + +`WorkspaceNavigationContent` comparte destinos globales entre escritorio y móvil. `useCompanyNavigationEntries` concentra los 21 destinos contextuales; `CompanySectionNavigation` comparte presentación y contadores. `CompanyContextBar` y `CompanyNavigationSheet` contienen las responsabilidades móviles; `CompanyHeader` conserva el encabezado desktop extraído del componente anterior. + +`WorkspaceSearch` conserva una sola consulta local; `WorkspaceSearchContent` comparte input/resultados entre las presentaciones. No duplica consultas remotas ni crea otro motor. El catálogo semántico propio incorpora MenuIcon de Tabler. No se modifica vendor ni se añaden dependencias. + +Los estilos nuevos se limitan al shell móvil, sticky y separación de foco. El breakpoint de 768 es el existente; no se introducen 920/1160/1660 ni observadores de capacidad. Los enlaces de navegación usan las variantes públicas de Button sin el comportamiento de teclado de un botón no nativo. + +## Geometría medida + +Mismo entorno sintético, cuatro pantallas y siete resoluciones antes/después: 28 geometrías por fase. Se esperó a carga, fuentes y transiciones. No son wireframes. + +| Resolución | Activos: Y de main antes → después | Ancho main antes/después | Tabla útil antes/después | +| --- | --- | --- | --- | +| 320×640 | 1496,31 → 106 | 320 | 286 | +| 390×844 | 1429,66 → 106 | 390 | 356 | +| 768×1024 | 88 → 88 | 300 | 249 | +| 1024×768 | 88 → 88 | 556 | 505 | +| 1366×768 | 88 → 88 | 898 | 847 | +| 1440×900 | 88 → 88 | 972 | 921 | +| 1920×1080 | 88 → 88 | 1452 | 1401 | + +A tamaño normal, cabecera/franja miden 57/49 px efectivos incluidos bordes. Ambos móviles cumplen `main.y <= 128` y muestran título sin scroll. No hay overflow horizontal del documento en la matriz; la tabla conserva su scroll propio. Dashboard/directorio comienzan tras la cabecera compacta, sin franja de compañía. La escasa capacidad de tablet a 768 continúa como deuda de V01.2, no se presenta como corregida. + +## Validación ejecutada + +Se preparó una única instancia desechable con `scripts/visual-audit.py`: no había archivo de sesión reutilizable. Login real de usuario ordinario, compañía y activo sintéticos, servicios/puertos propios. Las pruebas visuales bloquean escrituras HTTP; altas y ediciones se cancelan. No se accede a datos reales ni se retiran contenedores anteriores ajenos. Al terminar, el preparador retiró su entorno y eliminó la sesión privada; las evidencias permanecen sin credenciales. + +Pruebas de componentes: nuevos casos es/en en `apps/web/src/app.test.tsx` y `companies-page.test.tsx`, con 21 href únicos, estado activo, nombres accesibles y menús cerrados sin compañía. Ambos casos pasan. No dependen de clases para simular interacciones; teclado/modalidad se comprueban en Chromium. + +Se amplía `scripts/visual-audit-ui.test.py`, usando el mismo cliente MCP y preparador, sin otro framework. El directorio de salida es configurable y restringido a `.artifacts/playwright/`: + +```bash +METIS_PLAYWRIGHT_STORAGE_STATE=.artifacts/playwright/authenticated-ID/session.json \ +METIS_UI_AUDIT_DIRECTORY=.artifacts/playwright/v01-1/after \ +python3 scripts/visual-audit-ui.test.py +``` + +Sustituir ID por la sesión activa, manteniendo el preparador abierto. La suite comprueba geometría, 21 destinos y último enlace por teclado, Tab/Shift+Tab/Espacio/Enter/Escape, retorno de foco, rutas y Atrás/Adelante, clics modificados, transferencia entre menús, búsqueda, sticky, resize, cuenta/idioma y edición cancelada. Incluye regresiones V02, V03, V06 y V09. Se espera al foco final de la primitiva tras atravesar sus guardas, no a una espera fija. + +**Resultado final:** nueve pruebas MCP pasan en 288,506 s. Después se amplió el caso de teclado con un ciclo inverso de 26 pulsaciones Shift+Tab y se repitió ese caso completo: pasa en 25,263 s. En ambos sentidos se mantiene el foco en el Sheet y se alcanza el último destino. Logs: `technical/visual-ui.log` y `technical/visual-keyboard.log`. + +Se simula un nombre largo mediante respuestas GET interceptadas, sin guardarlo. A 320 px conserva el presupuesto de 106 y no desborda. En 320×360 se alcanza el último enlace y el cierre permanece visible. La ampliación de fuente raíz al 200 % mantiene el ancho sin overflow y aumenta main.y a 210 px: excepción justificada al presupuesto nominal por ampliación de texto. Se conserva contenido y scroll; no se fuerza un alto fijo recortando textos. + +El intento de zoom real con atajos de Chromium headless no cambió devicePixelRatio. No se declara comprobado zoom real 200/400 %: ampliar fuente y probar viewport de 320 son comprobaciones distintas. También quedan pendientes Cmd+clic en macOS, lector de pantalla, modo oscuro completo y combinaciones de muchos roles/compañías. El usuario ordinario no recibe enlace de administración de usuarios; el comportamiento administrativo conserva su condición existente. + +## Comprobaciones técnicas + +| Comprobación | Resultado | +| --- | --- | +| Typecheck dirigido compañías y web | Pasa | +| Biome de los archivos TS/TSX/JSON modificados | Pasa | +| Espaciado ESLint dirigido | Pasa tras corregir cuatro separaciones | +| Nuevos tests de componentes es/en | 2 pasan | +| `pnpm build` | Pasa; conserva aviso de chunk grande | +| `pnpm typecheck` | Falla en vendor `calendar.tsx:165`, contrato locale de react-day-picker; sin modificar vendor | +| `pnpm lint` | Falla con 15 errores en código ajeno de fotos/procesos/migraciones; además avisos de assets de skills | +| `pnpm check` | Falla global; archivos modificados comprobados separadamente | +| `pnpm test` con BD aislada | Falla en dos expectativas antiguas de `companies-page.test.tsx`: «Personas (1)» y texto antiguo sobre acceso de persona. No se corrigen fuera de alcance; la suite global no se declara aprobada | + +La BD de tests se crea en otro contenedor y se elimina al terminar. Se conserva el log de Turbo para distinguir paquetes ejecutados/cacheados y no atribuir validación a suites que su fallo haya interrumpido. Los fallos citados estaban documentados antes de V01.1; se contrastan con los archivos implicados, no se supone que todo error nuevo sea previo. + +La consola de la suite final no registra errores ni warnings. En la matriz previa apareció un 404 accesorio de favicon; no bloquea navegación. + +Durante desarrollo se corrigieron una expansión indebida del separador móvil y expectativas/esperas de las propias pruebas. Los intentos parciales no sustituyen el resultado final. No se ignora un fallo de la aplicación para hacer pasar una prueba. + +## Evidencias y documentación + +Evidencias locales, excluidas de Git: + +- [Baseline](../.artifacts/playwright/v01-1/before/measurements.json) y [matriz posterior](../.artifacts/playwright/v01-1/after-layout/measurements.json). +- [Últimos resultados por escenario](../.artifacts/playwright/v01-1/after/summary-latest.json), [historial de mediciones interactivas](../.artifacts/playwright/v01-1/after/measurements.json), [ciclo de teclado ampliado](../.artifacts/playwright/v01-1/keyboard/measurements.json), [consola](../.artifacts/playwright/v01-1/after/console.json) e [índice de capturas con dimensiones y SHA-256](../.artifacts/playwright/v01-1/evidence-index.json). +- [Activos 320 antes](../.artifacts/playwright/v01-1/before/assets-320.png), [después](../.artifacts/playwright/v01-1/after-layout/assets-320.png), [Sheet contextual](../.artifacts/playwright/v01-1/after/navigation-company-menu-390.png), [global](../.artifacts/playwright/v01-1/after/navigation-global-menu-390.png) y [búsqueda](../.artifacts/playwright/v01-1/after/navigation-search-320.png). +- Logs de comandos en `.artifacts/playwright/v01-1/technical/`. Las capturas se abrieron y examinaron; geometría, snapshot y comportamiento son pruebas distintas. + +Para compartir evidencias, entregar solo `v01-1/`; no incluir `authenticated-*`, sesiones ni credenciales. Se actualizan DESIGN.md y componentes de interfaz para esta entrega, conservando los estados de los otros hallazgos. + +Skills consultadas: `ux-navigation` dirige orientación y separación de ámbitos; `shadcn` aporta contratos Sheet/Base UI y primitivas existentes; `frontend-design` conserva identidad y jerarquía; composición y reglas React justifican modelos compartidos, estado efímero y suscripción responsive acotada. La CLI informativa shadcn falló por DNS; se consultaron sus [docs oficiales de Sheet](https://ui.shadcn.com/docs/components/base/sheet) y [Dialog de Base UI](https://base-ui.com/react/components/dialog), contrastadas con el código instalado. No se aplicaron auditores no instalados ni se instalaron herramientas. diff --git a/docs/propuesta-navegacion-responsive-v01.md b/docs/propuesta-navegacion-responsive-v01.md index 86abaf3..6a44595 100644 --- a/docs/propuesta-navegacion-responsive-v01.md +++ b/docs/propuesta-navegacion-responsive-v01.md @@ -1,6 +1,8 @@ # V01 — Propuesta de navegación y layout responsive -Fecha: 21 de octubre de 2026. **Propuesta para aprobación; sin implementar. V01 continúa pendiente.** +Fecha: 21 de octubre de 2026. **Arquitectura B híbrida aprobada para entregas incrementales. V01 continúa parcialmente pendiente.** + +Seguimiento: [V01.1 — navegación móvil y franja contextual](navegacion-movil-v01-1.md) implementa la primera entrega, conservando escritorio y umbrales existentes. El resto del documento conserva las alternativas, mediciones originales y arquitectura objetivo; los presupuestos de capacidad y las entregas V01.2–V01.4 no están implementados. ## 1. Resumen ejecutivo diff --git a/docs/skills-frontend-ui-ux.md b/docs/skills-frontend-ui-ux.md new file mode 100644 index 0000000..e5f241e --- /dev/null +++ b/docs/skills-frontend-ui-ux.md @@ -0,0 +1,310 @@ +# Auditoría e integración de skills frontend y UI/UX + +Fecha: 21 de octubre de 2026. Alcance: instalación local de Metis Desk y capacidades expuestas en esta sesión de Codex. **Selección de skills formalizada en AGENTS.md; las mejoras propuestas de DESIGN.md y V01 siguen pendientes.** + +## 1. Diagnóstico y recomendación + +Hay **15 skills pertinentes instaladas en `.agents/skills/`**, coincidentes con las 15 entradas de [skills-lock.json](../skills-lock.json). Las cuatro anteriores cubrían dirección visual, shadcn/ui, composición y rendimiento React. Las once incorporaciones aportan diez guías UX especializadas —incluido un orquestador de revisión— y una base consultable con herramientas Python, `ui-ux-pro-max`. + +Recomiendo seleccionar **una skill principal por tarea**, añadir `shadcn` cuando se compongan primitivas y consultar las reglas React/composición solo cuando exista una decisión técnica correspondiente. Utilizar las nuevas guías para concretar tareas, alternativas y comprobaciones; no para sustituir los patrones de Metis ni para ejecutar todas las revisiones en cada cambio. + +Dos límites relevantes: + +- `ux-design-review` está instalado, pero sus auditores dependientes `*-audit` no. Tampoco se ha localizado su política `docs/orchestration-policy.md`. Ofrece una estructura útil de revisión, pero su orquestación completa no está disponible. +- Las recomendaciones de `ui-ux-pro-max` y `shadcn` necesitan filtrado: hay generación de sistemas visuales, ejemplos abreviados de APIs, herramientas de instalación y consejos de otras plataformas. Su presencia no autoriza regenerar vendor, añadir dependencias ni reemplazar DESIGN.md. + +No se necesita instalar más herramientas para aplicar las recomendaciones básicas. La ausencia de auditores especializados debe declararse, no ocultarse ni resolverse instalándolos automáticamente. + +## 2. Fuentes, localización y método + +Se consultaron los 15 `SKILL.md`, los recursos aplicables descritos más adelante, el lockfile, [AGENTS.md](../AGENTS.md), [DESIGN.md](../DESIGN.md), los patrones de [listados](patron-de-listados-de-entidades.md) y [diálogos](patron-de-dialogos.md), la [estructura frontend](estructura-web-por-entidades-y-operaciones.md), la [auditoría visual](auditoria-ui-ux-visual.md), la [propuesta V01](propuesta-navegacion-responsive-v01.md) y el procedimiento de [Playwright MCP](playwright-mcp.md). + +Se contrastaron también `apps/web/components.json`, los manifests del proyecto/vendor, la API pública de Button/Sheet y la configuración MCP de proyecto. La app es React 19.2.5, TypeScript 6.0.2, Tailwind 4.2.2 y Vite; vendor usa Base UI 1.8.0 y shadcn CLI 4.21.1. Son versiones declaradas en los manifests inspeccionados, no versiones inferidas de las skills. `rsc:false`, estilo `base-vega` e iconografía propia Tabler delimitan la aplicación de ejemplos externos. + +Ubicaciones inspeccionadas: + +| Ubicación | Resultado y alcance | +| --- | --- | +| `.agents/skills/` y `skills-lock.json` | Núcleo completo de 15 skills; inventario detallado en §3. | +| `.codex/config.toml` | Configura Playwright MCP; no constituye un registro adicional de skills UI. | +| `/home/david/.agents/skills/` | `find-skills`: descubrimiento/instalación, no guía de diseño. No se usa para instalar nada en esta tarea. | +| `/home/david/.codex/skills/.system/` | `imagegen` como capacidad visual auxiliar; herramientas de administración/documentación y `review-agent`, ajenas al núcleo UI. Un fichero presente no implica que sea invocable en esta sesión. | +| `/home/david/.codex/config.toml` | Se inspeccionó únicamente la estructura de registro de skills: no hay clave `skills` con rutas adicionales. No se reproducen otros ajustes ni secretos. | +| Caché de plugins de Codex | Skills de Sites y plantillas de documentos/artefactos; se separan en §4 de las guías para desarrollar Metis. Caché no equivale a plugin activo. | + +No se consultó upstream para deducir releases ni se instalaron/actualizaron skills. Se inspeccionaron estáticamente los scripts antes de ejecutar dos búsquedas locales sin persistencia (§11). No se ejecutaron scripts de generación, instalación, actualización de catálogos ni publicación. + +## 3. Inventario de skills del repositorio + +Todos los enlaces de esta sección resuelven al `SKILL.md` local. Las rutas son relativas a la raíz del repositorio. El lockfile registra procedencia GitHub, ruta upstream y `computedHash`; **no registra commit, tag ni release**. Las versiones `1.0.0` proceden del frontmatter y no identifican por sí solas una revisión inmutable. Las huellas siguientes están abreviadas; el valor completo está en el lockfile. No se confunden con un SHA de commit ni con el SHA-256 del fichero individual. + +### 3.1. Procedencia y referencia instalada + +| Nombre / ubicación | Procedencia y ruta upstream declaradas en lock | Versión declarada | `computedHash`, prefijo | +| --- | --- | --- | --- | +| [frontend-design](../.agents/skills/frontend-design/SKILL.md) | `anthropics/skills` · `skills/frontend-design/SKILL.md` | No declarada | `89496d5dd00f` | +| [shadcn](../.agents/skills/shadcn/SKILL.md) | `shadcn-ui/ui` · `skills/shadcn/SKILL.md` | No declarada | `b864a9587655` | +| [ui-ux-pro-max](../.agents/skills/ui-ux-pro-max/SKILL.md) | `nextlevelbuilder/ui-ux-pro-max-skill` · `.claude/skills/ui-ux-pro-max/SKILL.md` | No declarada | `27b1e8c953e2` | +| [ux-dashboard](../.agents/skills/ux-dashboard/SKILL.md) | `uxcel-lab/product-skills` · `ux/screens/dashboard/SKILL.md` | `1.0.0` | `4bf93a57b230` | +| [ux-design-review](../.agents/skills/ux-design-review/SKILL.md) | `uxcel-lab/product-skills` · `ux/workflows/design-review/SKILL.md` | `1.0.0` | `16b69fd0d31d` | +| [ux-empty-states](../.agents/skills/ux-empty-states/SKILL.md) | `uxcel-lab/product-skills` · `ux/screens/empty-states/SKILL.md` | `1.0.0` | `82c55ab7d5d7` | +| [ux-error-recovery](../.agents/skills/ux-error-recovery/SKILL.md) | `uxcel-lab/product-skills` · `ux/flows/error-recovery/SKILL.md` | `1.0.0` | `9f77a4a62216` | +| [ux-inputs-and-forms](../.agents/skills/ux-inputs-and-forms/SKILL.md) | `uxcel-lab/product-skills` · `ux/components/inputs-and-forms/SKILL.md` | `1.0.0` | `af498000a05f` | +| [ux-microcopy](../.agents/skills/ux-microcopy/SKILL.md) | `uxcel-lab/product-skills` · `ux/foundations/microcopy/SKILL.md` | `1.0.0` | `980c00cfa403` | +| [ux-navigation](../.agents/skills/ux-navigation/SKILL.md) | `uxcel-lab/product-skills` · `ux/components/navigation/SKILL.md` | `1.0.0` | `3d3442366144` | +| [ux-notifications-and-toasts](../.agents/skills/ux-notifications-and-toasts/SKILL.md) | `uxcel-lab/product-skills` · `ux/components/notifications-and-toasts/SKILL.md` | `1.0.0` | `86574eaa1d14` | +| [ux-search](../.agents/skills/ux-search/SKILL.md) | `uxcel-lab/product-skills` · `ux/components/search/SKILL.md` | `1.0.0` | `9918d81eaa3d` | +| [ux-tables](../.agents/skills/ux-tables/SKILL.md) | `uxcel-lab/product-skills` · `ux/components/tables/SKILL.md` | `1.0.0` | `92d4de539c6a` | +| [vercel-composition-patterns](../.agents/skills/vercel-composition-patterns/SKILL.md) | `vercel-labs/agent-skills` · `skills/composition-patterns/SKILL.md` | `1.0.0` | `f98931159fa9` | +| [vercel-react-best-practices](../.agents/skills/vercel-react-best-practices/SKILL.md) | `vercel-labs/agent-skills` · `skills/react-best-practices/SKILL.md` | `1.0.0` | `3219a1944e40` | + +### 3.2. Propósito, instrucciones y activadores + +| Skill | Propósito e instrucciones principales | Activador recomendado en Metis | Compatibilidad / adaptación | +| --- | --- | --- | --- | +| frontend-design | Dirección visual intencionada; plan/wireframe, crítica y revisión; jerarquía y contenido específicos del producto. | Pantalla nueva o mejora visual con decisiones de composición. | Compatible como criterio. Conservar paleta, Inter, densidad y recetas; sus ejemplos de hero, tipografías nuevas y aversión general a mayúsculas no desplazan el patrón empresarial. | +| shadcn | Reutilizar componentes, inspeccionar configuración y docs, componer semánticamente, diferenciar Base UI/Radix y revisar resultado. | Uso de primitivas, formularios/modales o componentes compartidos. | Compatible con límites: `render`, APIs instaladas y wrappers rdx-ui. Prohibidos init/preset/add/vendor sin autorización específica. | +| ui-ux-pro-max | Buscar una intención observable y stack explícito; verificar pertinencia, reintentar una vez si no hay coincidencia; no presentar fallbacks como resultados. | Duda concreta de accesibilidad, interacción, responsive o patrón; consulta transversal de apoyo. | Compatible con adaptación de rutas, plataforma y sistema visual. No generar/persistir otro diseño para cada página de Metis. | +| ux-dashboard | Elegir métricas según la decisión del usuario, jerarquizar, agrupar y mantener comparaciones visibles; gráficos según datos. | Dashboard/resumen operativo, revisión de sobrecarga. | Compatible como guía, sin añadir métricas, tiempo real o gráficos sin consumidor/datos. No convertir «todos los ejes a cero» en regla universal para cualquier gráfico. | +| ux-design-review | Establecer contexto una vez, seleccionar lentes, suprimir rediseño de tokens con sistema existente y consolidar hallazgos por gravedad. | Auditoría integral de una pantalla/flujo, no revisión de un único aspecto. | **Parcial:** faltan auditores y política; usar estructura de informe y declarar qué comprobaciones se realizan directamente. | +| ux-empty-states | Distinguir primer uso, completado, sin resultados y error/404; explicar y ofrecer siguiente acción, con una primaria. | Estados vacíos de listas/asistentes y recuperación sin resultados. | Compatible con `EmptyState`/EntityTable existentes; no inventar datos de demostración en inventarios reales ni multiplicar CTA sin permisos. | +| ux-error-recovery | Prevenir fallos, validación contextual, conservar trabajo, dar salida y ajustar confirmación/undo a consecuencias. | Formularios fallidos, errores de red, borrados y rutas sin entidad. | Compatible como UX. Undo/autosave y explicaciones de seguridad dependen de contratos de negocio, no los crea la skill. | +| ux-inputs-and-forms | Justificar campos, etiquetas persistentes, agrupar, seleccionar control adecuado, validar y comunicar envío. | Formularios, asistentes y selectores. | Adaptar una columna obligatoria, «(opcional)», asterisco previo, defaults y validación al patrón de diálogos, FormLabel y schemas. | +| ux-microcopy | Tono según tarea, texto conciso y específico, verbos de acción, consistencia e inclusión. | Etiquetas, ayuda, tooltips, confirmaciones, errores y traducciones. | Compatible mediante i18n es/en y vocabulario de entidades. Fechas relativas no sustituyen fechas exactas necesarias para soporte/historial. | +| ux-navigation | Elegir patrón según amplitud/profundidad, contexto y rutas de entrada; orientación, estado activo y acceso a destinos. | Shell, compañía, navegación responsive y evaluación de V01. | Compatible con la justificación de V01; adaptar tamaños y prohibiciones absolutas de hamburguesa/iconos a capacidad real. | +| ux-notifications-and-toasts | Relacionar urgencia y canal; evitar interrupciones/repeticiones; mensajes comprensibles y control del usuario. | Feedback de mutaciones, avisos, banners o futura estrategia de notificaciones. | Compatible como criterio; no implementar un servicio de avisos por disponer de la skill ni confundir CountBadge con no leídos. | +| ux-search | Distinguir búsqueda conocida/exploratoria; consulta visible, recuperación, filtrado y orden separados. | Búsqueda de inventarios/pickers o global; problemas sin resultados. | Compatible sin introducir tendencias, historial, buscador transversal ni botón de enviar donde ya hay filtrado inmediato. | +| ux-tables | Escanear/comparar/actuar; alineación, formatos, orden, personalización y densidad ajustada a tareas. | Listados, tablas, sublistados y pickers. | Compatible con EntityTable. Acciones visibles y selección primera según Metis prevalecen sobre hover-only o controles a la derecha. | +| vercel-composition-patterns | Composición, variantes explícitas y separación de estado/presentación; evitar proliferación de flags. | APIs reutilizables, responsabilidades compartidas y refactors con consumidores reales. | Compatible con React 19. No exigir Context/compound components para componentes sencillos ni migraciones mecánicas de hooks. | +| vercel-react-best-practices | Evitar trabajo redundante, waterfalls, bundles innecesarios y estado derivado por efectos; optimizar con evidencia. | React con lógica, acceso remoto o problema de rendimiento medido. | Aplicar parte React/Vite; no introducir `next/dynamic`, RSC, `next/server`, SWR o cachés globales por copiar ejemplos. | + +### 3.3. Recursos, herramientas y dependencias + +**Diez skills Uxcel `ux-*`.** Cada directorio instalado contiene únicamente `SKILL.md`, con licencia MIT y versión declaradas en frontmatter. Son guías de razonamiento/procedimiento, no ejecutables ni bibliotecas React. Incorporan tablas de decisiones, comprobaciones y enlaces a lecciones externas; esos enlaces no son contenido instalado ni han sido auditados aquí. No requieren un runtime para leerlas. Las referencias a otras skills son dependencias de orquestación, no paquetes npm. + +**frontend-design.** `SKILL.md` y `LICENSE.txt`; sin scripts ni dependencias de ejecución. No tiene herramientas propias para renderizar, medir contraste o producir evidencias de navegador. + +**shadcn.** Incluye `agents/openai.yml`, documentación CLI/MCP, registry/customization, reglas de forms/composition/icons/styling/Base-vs-Radix/chat, guías de design system/showcase, ejemplos TSX/CSS, iconos PNG, `evals/evals.json` y `scripts/showcase-coverage.mjs`. Se consultaron SKILL, Base-vs-Radix, las guías de composición/estilos pertinentes, la ruta de design system y el script de cobertura. El `.mjs` usa Node y filesystem, compara imports de un showcase con nombres de componentes; **no mide accesibilidad, cobertura funcional ni calidad visual**. No se ejecutó. CLI requiere Node/PNPM y puede necesitar red; búsquedas/docs no equivalen a autorización para instalar. `mcp.md` describe un servidor shadcn, pero no se encontró configurado en `.codex/config.toml`; no inventar su disponibilidad. + +**vercel-composition-patterns.** README, AGENTS compilado y `rules/*.md`; sin runtime ni ejecutables instalados. Se consultaron las reglas de evitar flags, interfaces de estado y cambios React 19. El README menciona metadata/generación upstream, pero eso no significa que estén los generadores o `metadata.json` en esta copia. + +**vercel-react-best-practices.** README, AGENTS compilado y reglas individuales; sin scripts de build/validación instalados. Las instrucciones `pnpm build/validate/extract-tests` del README son del paquete upstream, no comandos que deban ejecutarse desde la raíz de Metis para preparar una skill. Se contrastaron estado derivado, importaciones dinámicas y deduplicación SWR. + +**ui-ux-pro-max.** Tiene `references/quick-reference.md`, `references/pro-rules.md`, datos CSV/JSON y cinco módulos Python principales: `search.py`, `core.py`, `design_system.py`, `reasoning_contract.py` y `validate_data.py`, además de tests/fixtures. Se inspeccionaron búsqueda, imports, lectura de CSV y ramas de escritura. Python 3 y biblioteca estándar bastan para la búsqueda inspeccionada; no requiere pip ni dependencias de producción. `--persist` crea `design-system//MASTER.md` y overrides; `--force` puede sobrescribirlos. No usar esas opciones en Metis para competir con DESIGN.md. + +El catálogo local declara verificación a 26-08-2026; se contaron realmente 88 filas de estilos, 192 productos/paletas/perfiles de razonamiento, 74 pares tipográficos, 119 guías UX, 105 iconos curados, 17 presets de movimiento, 25 tipos de gráfico y 22 CSV de stacks. Los 79 estilos consultables y 50 activos están declarados en `catalog-summary.json`; no equivalen a 79 temas implementados en Metis. Las fechas/procedencias del dataset no son una versión del motor ni garantía de actualidad de cada API. Los presets GSAP, recomendaciones Phosphor/otras fuentes y ejemplos de librerías no constituyen nuevas dependencias autorizadas. + +### 3.4. Compatibilidad con Codex y límites reales + +Las 15 skills aparecen en el catálogo de la sesión y existen en disco. Sus instrucciones Markdown son legibles por Codex. `shadcn` incorpora descriptor `agents/openai.yml`; las demás no necesitan ese descriptor para estar expuestas aquí. La sintaxis `!` de shell interpolado y `allowed-tools: Bash(...)` de shadcn no debe considerarse una ejecución ya realizada ni una herramienta Codex disponible por sí misma. + +El ejemplo de Pro Max con `${CLAUDE_PLUGIN_ROOT}/.claude/...` no corresponde a esta instalación. Resolver el script desde `.agents/skills/ui-ux-pro-max/scripts/`, con `python3` en WSL y sin rutas personales embebidas en código del proyecto. Las menciones a «Claude» en el texto no impiden usar las guías; tampoco garantizan compatibilidad ejecutable sin esta adaptación. + +La orquestación Uxcel remite a estas skills de auditoría **no instaladas**: `ux-heuristics-audit`, `ux-microcopy-audit`, `ux-dark-patterns-audit`, `ux-accessibility-audit`, `ux-aesthetics-audit`, `ux-information-architecture-audit` y `ux-mobile-responsiveness-audit`. También hay referencias a `ux-color`, `ux-typography`, `ux-layout-spacing-grids`, `ux-contact-support`, `ux-login-signup`, `ux-menus`, `ux-modals-and-dialogs` y `ux-settings`, ausentes. No son aliases de las guías generativas disponibles; por ejemplo, `ux-microcopy` no es `ux-microcopy-audit`. + +No existe el `docs/orchestration-policy.md` referenciado ni en esos directorios de skill ni en la documentación del proyecto. No inventar su contenido ni escribirlo dentro de la distribución ajena. El orquestador dice que las ausencias no bloquean: continuar con comprobaciones propias y declararlas. Su sugerencia de listarlas como «available but not run» se corrige en el informe de Metis por **«no instalada/no ejecutada»**, para no comunicar una capacidad inexistente. + +No hay una skill dedicada de accesibilidad/auditoría WCAG en este núcleo, ni un runner de Playwright suministrado por estas skills. Hay criterios de accesibilidad repartidos entre ellas; la ejecución visual/interactiva corresponde al MCP y scripts propios existentes. No se certifica accesibilidad por leer checklists ni se presentan los tests del catálogo como pruebas de la aplicación. + +## 4. Otras capacidades del entorno: fuera del núcleo Metis + +Se revisaron metadatos y alcance inicial, sin invocarlas ni auditar todos sus recursos. Las rutas siguientes son del equipo de esta sesión, no dependencias portables del repositorio. + +| Skill / ubicación `SKILL.md` | Procedencia/referencia visible | Propósito y decisión de uso | +| --- | --- | --- | +| `imagegen` · `/home/david/.codex/skills/.system/imagegen/SKILL.md` | Skill de sistema; sin versión declarada en frontmatter | Recursos bitmap, edición y mockups. Puede apoyar una tarea explícita de imágenes; no sustituye iconos Tabler, wireframes de estructura ni navegador. Herramienta de imagen por defecto; fallback CLI tiene otros requisitos y autorización, no ejecutado. | +| `sites:sites` · `/home/david/.codex/plugins/cache/openai-curated-remote/sites/1.1.0/skills/sites/SKILL.md` | Paquete de caché Sites `1.1.0`, no versión declarada de SKILL | Construcción/publicación alojada mediante Sites. Fuera del flujo de mantenimiento de esta SPA local; no exportar ni publicar Metis por esta auditoría. Requiere herramientas/servicio del plugin, no comprobados aquí. | +| `sites:sites-remix` · misma raíz, `skills/sites-remix/SKILL.md` | Paquete Sites `1.1.0` | Remix de un Site identificado. Sin consumidor para el repositorio Metis; no usar para rediseñar su shell. | + +La caché `openai-templates/0.1.1/skills/` contiene `artifact-template-simple-light-mode`, `artifact-template-simple-dark-mode`, `artifact-template-design-report`, `artifact-template-analytics-dashboard` y `artifact-template-system-design`, cada una con su `SKILL.md`. Se inspeccionaron sus frontmatters: son plantillas de presentaciones, documentos o una hoja de cálculo, **no skills de componentes frontend**. No aparecen como skills disponibles en esta sesión; no atribuirles capacidad activa ni aplicar sus temas claro/oscuro a Metis. El resto de plantillas de esa caché son artefactos de negocio, fuera del alcance UI de la aplicación. + +`find-skills`, `skill-installer` y `skill-creator` administran descubrimiento/instalación/autoría; `openai-docs` documenta productos OpenAI. `review-agent` está presente en la carpeta de sistema pero no expuesto en el catálogo de skills de esta sesión; no se ha delegado ninguna revisión. Skills de Pages, mascotas y administración de plugins tampoco aportan reglas al frontend de Metis. No se cuentan dentro de las 15 especializadas. + +## 5. Matriz de responsabilidades y utilización + +**R** = reglas/criterios; **P** = procedimiento; **H** = herramienta ejecutable; **B** = base de conocimiento. Una skill puede cubrir varias funciones, pero no todas ejecutan pruebas. + +| Área | Skills y naturaleza | Límite / responsable efectivo | +| --- | --- | --- | +| Diseño visual | frontend-design R/P; Pro Max R/B/H | DESIGN.md decide tokens/identidad; consumidor y rdx-ui componen. | +| UX | ux-* R/P; Pro Max R/B/H | Requisitos/tareas del usuario y patrones especializados prevalecen. | +| Accesibilidad | Criterios de Pro Max, shadcn y ux-* R/B | Base UI aporta primitivas; integrador verifica uso y foco. Sin auditor dedicado instalado. | +| Arquitectura frontend | vercel-composition-patterns R; shadcn R/P | AGENTS/estructura web y fronteras módulo/rdx-ui/vendor. | +| React/rendimiento | vercel-react-best-practices R; Pro Max R/B/H | Evidencia de profiling/bundle y stack instalado; no nuevas librerías por receta. | +| Componentes/composición | shadcn R/P/H; vercel-composition-patterns R | API pública real, composición propia y responsabilidades acotadas. | +| Auditoría visual | ux-design-review P parcial; otras guías R | Capturas/navegación reales con Playwright MCP; skill no genera evidencia por sí sola. | +| Automatización/pruebas | Pro Max H sobre catálogo; shadcn H sobre showcase | No son tests UI. Infraestructura de aplicación: scripts propios, MCP y pruebas del proyecto. | + +### Selección por tarea + +| Tarea | Skill principal | Complementarias cuando aporten valor | Resultado esperado | +| --- | --- | --- | --- | +| 1. Diseñar pantalla nueva | frontend-design | Skill UX del tipo de tarea; Pro Max ante duda concreta; shadcn al componer | Esquema/jerarquía basado en DESIGN, estados y patrón reutilizable, sin generar otro sistema. | +| 2. Mejorar visualmente pantalla | frontend-design | ux-tables/navigation/forms según problema; Pro Max para criterio observable | Cambio acotado y comparación visual, sin refactor ajeno. | +| 3. Crear componente compartido | vercel-composition-patterns | shadcn; React best practices si hay hooks/estado | API semántica, consumidores reales, composición accesible y sin DTO en rdx-ui. | +| 4. Formularios/diálogos | ux-inputs-and-forms | shadcn; ux-error-recovery o microcopy para el aspecto afectado | Patrón de diálogos, schemas/RHF existentes, estabilidad, ayuda y foco verificados. | +| 5. Listados/tablas | ux-tables | shadcn; ux-search si cambian búsquedas/filtros | EntityTable y acciones/paginación de Metis, con datos representativos. | +| 6. Navegación responsive | ux-navigation | shadcn para Sheet; Pro Max para foco; composición si cambia API | Contratos de V01 y prueba por capacidad; no nueva arquitectura por hábito. | +| 7. Accesibilidad | ui-ux-pro-max, consulta puntual | shadcn y guía UX del componente | Criterio observable → API → prueba real. Declarar que no existe skill WCAG dedicada. | +| 8. Refactorizar React | vercel-composition-patterns | vercel-react-best-practices si hay estado/efectos; shadcn si afecta composición | Simplificación justificada, sin mover código por preferencia. | +| 9. Rendimiento frontend | vercel-react-best-practices | Pro Max solo para UX percibida, listas o layout | Medición antes/después; aplicar React/Vite, no recetas Next/SWR. | +| 10. Auditoría UI/UX | ux-design-review, con alcance parcial declarado | Guías especializadas del flujo y Pro Max; navegador según AGENTS | Informe único priorizado; separar código, visual, funcional, no reproducido y pendiente. | + +Dashboard, vacíos, copy, recuperación y notificaciones tienen sus guías principales propias cuando ese sea el objeto de la tarea. No añadirlas por el simple hecho de que una pantalla contenga un texto o un contador. + +## 6. Compatibilidad, solapamientos y resoluciones + +### Reglas de precedencia propuestas + +Bajo las instrucciones vigentes del usuario y del entorno, aplicar el orden solicitado: + +1. **Restricciones obligatorias del proyecto y AGENTS aplicable:** alcance, seguridad, vendor, i18n y arquitectura. +2. **DESIGN.md y patrones especializados:** en su ámbito, listados/diálogos prevalecen sobre el resumen general. +3. **Contratos reales de componentes instalados:** determinan cómo componer una pauta; una desviación de la implementación no deroga la norma y debe señalarse. +4. **Skills pertinentes:** criterios complementarios; recomendaciones no son autorización ni requisitos nuevos. + +Ante conflicto: citar recomendación y norma, justificar adaptación, escoger la solución menor y registrar solo lo relevante en la entrega. No modificar una skill upstream para silenciar un conflicto ni bloquear una tarea ya autorizada para pedir de nuevo permiso que no necesita. + +### Conflictos y riesgos concretos + +| Recomendación localizada | Conflicto o riesgo en Metis | Resolución propuesta | +| --- | --- | --- | +| shadcn SKILL/design-system: `init`, preset, `add --all`, editar variantes/vendor y `tailwindCssFile` | El alias apunta al CSS vendor, protegido por AGENTS; la app ya existe. | Consultar docs/API; extender desde rdx-ui/módulos y CSS propio autorizado. No ejecutar el bootstrap ni regenerar vendor. | +| shadcn: consultar registries/añadir componentes y providers | Tener una receta no demuestra que falte componente ni autoriza nuevas dependencias. | Buscar primero en rdx-ui/vendor; CLI informativa cuando corresponda; instalación solo con alcance y autorización aplicables. | +| shadcn: FieldGroup/Field, Empty y toast como recetas universales | Metis tiene FormDialog/FormLabel/FormSection/EmptyState y feedback propios. | Reutilizar wrappers; no migrar formularios o notificaciones completos solo para coincidir con la skill. | +| Pro Max: generar sistema para páginas nuevas y persistir MASTER/overrides | Crearía una segunda fuente normativa de color, tipo y espaciado. | Consulta dirigida `--domain`/`--stack`; sin `--persist`, `--force` ni redefinición de tokens por página. | +| Pro Max: 16 px base, max-width global, cero scroll horizontal y breakpoints de ejemplo | Metis usa texto base de 14 px, tablas anchas con scroll propio y distribución por contenedor. | No ensanchar ni restringir todo por receta. Distinguir documento de tabla; validar legibilidad/zoom y capacidad real. | +| Pro Max: virtualizar 50+ filas; stack React: listas de más de 100 | Umbrales internos distintos, sin medición de consumidores; los listados ya paginan. | Medir volumen realmente renderizado/rendimiento antes de introducir virtualización. | +| Pro Max/ux-navigation: icono siempre con texto, no navegación modal | Choca literalmente con raíl y Sheet propuestos en V01. | Overlay es contenedor temporal de enlaces reales, no un formulario que sustituye una ruta. Raíl solo con etiquetas accesibles, ayuda y expansión; validar descubribilidad. | +| ux-navigation: 44×44, texto 16; forms: objetivos 38; Pro Max: 24 CSS px web frente a unidades nativas | Inconsistencia entre guías, densidad empresarial y plataformas. | Distinguir objetivo táctil, tamaño visual y criterio de accesibilidad. No redimensionar vendor globalmente; aprobar objetivo por contexto y verificar espacio/zoom. | +| ux-inputs-and-forms: una columna siempre, opcionales rotulados y asterisco antes | Patrón de Metis admite columnas por contenedor, opcionales sin sufijo y asterisco final. | Aplicar patrón de diálogos y FormLabel existentes; conservar orden DOM y agrupación accesibles. | +| forms/error/Pro Max: validación siempre onBlur, máscaras y autofill; autosave/confirmar todo cierre con cambios | Puede alterar validación Zod/RHF, cancelar y persistencia, además de campos legítimos. | Evaluar por campo/operación; no quitar campos funcionales ni cambiar contratos automáticamente. Preservar borrador ante fallo sin inventar autosave. | +| ux-tables: acciones ocultas hasta hover, controles de lista a la derecha | Acción principal visible y selection primera son normas de Metis. | Conservar icono principal, tres puntos y selección al inicio; controles siempre accesibles con teclado/táctil. | +| ux-search: lupa como submit y ~10 sugerencias/recentes, múltiples filtros | Búsqueda actual filtra en vivo; global solo busca compañías/destinos. | No crear botón inútil, historial ni servicio transversal. Mantener query/contexto; mejorar solo el problema real. | +| notifications: badges solo para no leídos, toast no descartable, éxito siempre verde | CountBadge cuenta entidades; UI y feedback no son una taxonomía móvil universal. | Contador de inventario no es notificación. Comprobar primitive/API y tokens antes de introducir colores, timers o controles. | +| ux-dashboard: todo eje parte de cero; microcopy: mañana mejor que fecha exacta | No todo gráfico o dato de historial tiene la misma semántica. | Evaluar comparación/trend y necesidad de fecha exacta; no convertir preferencias en mandatos universales. | +| Vercel React: Next/RSC, `next/dynamic`, SWR | SPA Vite y TanStack Query ya establecidos. | Aplicar principio de diferir/cachar sin copiar APIs ajenas. Importación dinámica/React.lazy si procede y se justifica. | +| Vercel composición: todos los estados en providers, `use()` reemplaza useContext | Puede añadir capas a componentes sencillos y provocar migraciones sin valor. | Estado local por defecto, elevar con necesidad real. No sustituir hooks válidos ni alterar vendor por una convención de la skill. | +| shadcn/Pro Max ejemplos Base UI abreviados | Ejemplo Pro Max de Button con enlace omite `nativeButton={false}`; ejemplo Sheet coloca `side` en Root, aunque vive en SheetContent. | Comprobar tipos y consumidor instalado. `render` frente a `asChild`, `multiple` y contratos Base UI según API concreta. No copiar snippets como contratos. | +| Uxcel: lentes opcionales ofrecidas antes de accesibilidad/mobile | AGENTS ya exige verificar accesibilidad y responsive cuando se modifica UI. | No usar la oferta como permiso para omitir controles obligatorios; no instalar auditores ni pedir aprobaciones repetidas para verificaciones autorizadas. | +| Skills: checklist de entrega o consejo «shadcn es accesible» | Lectura/uso de primitiva no prueba integración, contraste ni foco. | Navegador, mediciones y teclado; lector/zoom/tema según alcance y límites expresos. | + +Las guías coinciden en jerarquía, lenguaje claro, etiquetas, reutilización, estados y foco. Se deduplica el análisis: una incidencia puede tener apoyo de varias skills, pero se informa una sola vez. `ux-microcopy` concreta textos; `ux-error-recovery` decide recuperación; `ux-empty-states` diferencia causas. No repetir tres informes para el mismo «sin resultados». + +## 7. Integración en el trabajo habitual + +### Antes de diseñar + +Leer el requisito, AGENTS, DESIGN y patrón pertinente; reconocer consumidores equivalentes. Elegir una guía principal de §5 y declarar contexto conocido: soporte empresarial, web responsive, Base UI/Vega, sistema visual existente y tarea operativa. No volver a preguntar identidad/stack ya documentados. + +Consultar frontend-design para composición visual nueva; para V01 comenzar con ux-navigation. Pro Max se utiliza para una duda verificable —por ejemplo foco oculto por sticky—, no para solicitar una paleta SaaS de sustitución. Registrar alternativas solo cuando exista una decisión real. + +### Durante implementación + +Consultar shadcn para contratos/primitivas y tipos instalados, manteniendo wrappers existentes. Si el cambio tiene estado/hook, consultar las reglas React pertinentes; composición solo cuando se diseñe una API o cambien responsabilidades reales. La skill UX especializada dirige contenido/interacción; ninguna decide DTO, permisos, rutas o persistencia. + +Convertir cada criterio aplicado en comportamiento observable: acción visible, foco al cerrar, error conservado, contador anunciado con contexto o ancho operativo. Evitar instalar herramientas de revisión, servidores MCP o librerías porque aparezcan en el texto de una skill. + +### Al auditar la interfaz terminada + +Para cambio puntual, revisar el flujo afectado con la guía principal y MCP; para auditoría integral, usar la estructura de ux-design-review y declarar sus lentes ausentes. Mantener accesibilidad, responsive y estados exigidos por el proyecto aunque el orquestador los considere opcionales. + +Reutilizar entorno sintético autenticado activo, conservar capturas fuera de Git y abrirlas antes de concluir. Separar evidencia visual, geométrica, funcional y de código; no afirmar WCAG, dark mode o zoom sin pruebas. Informar skills consultadas y efecto concreto, no una lista decorativa de nombres. + +### Integración formalizada en AGENTS.md + +La sección [UI/UX — Instrucciones obligatorias](../AGENTS.md#uiux--instrucciones-obligatorias) remite a esta matriz para seleccionar una skill principal y complementarias pertinentes. Establece precedencia, reutilización, límites sobre vendor/dependencias, verificación con Playwright y declaración de ausencias y pruebas pendientes. DESIGN.md incorpora únicamente una referencia a esta guía; no se han adoptado sus propuestas de tokens, layout o arquitectura V01. La matriz se mantiene aquí, sin duplicarla en AGENTS.md. + +## 8. Posibles mejoras de DESIGN.md + +No hace falta modificar paleta, fuentes o tokens para adoptar las skills. Las mejoras siguientes son propuestas editoriales/de proceso; requieren aprobación o una tarea posterior, sin convertirse aquí en normas vigentes. + +| Sección de DESIGN.md | Mejora concreta | Aportación / límite | +| --- | --- | --- | +| §2 Fuentes y capas | Enlazar esta matriz y distinguir instrucción, API, ejemplo y evidencia. | Aclara conflictos; ya existe precedencia, no crear otra autoridad. | +| §3 Fundamentos | Documentar rol de pares semánticos y cómo verificar estados/temas; inventariar tokens de éxito/aviso solo si hay necesidad real. | Pro Max sistematiza búsquedas/checks; no adoptar paletas generadas ni editar vendor. | +| §4/§10 Navegación y responsive | Añadir criterios de orientación y pruebas de disponibilidad del contenido, remitiendo a V01 como propuesta pendiente. | ux-navigation aporta taxonomía/entrada profunda; no fija nuevos breakpoints. | +| §5 Listados | Aclarar alineación de cifras/formatos y visibilidad de acciones como criterios revisables por tarea. | ux-tables concreta escaneabilidad; no sustituye EntityTable ni fija 40/48 px para todas las filas. | +| §7 Formularios | Registrar momento de validación por contrato y criterios para resumen de errores si se aprueba. | forms/error-recovery, sin eliminar campos, automatizar defaults ni cambiar cancelación. | +| §8 Estados | Matriz primer uso/sin coincidencias/completado/error/permisos y acción permitida; distinguir contador, estado y notificación. | Mayor detalle de empty/error/notifications, buena parte ya cubierta por Metis. | +| §9 Accesibilidad | Distinguir criterio mínimo y objetivo más estricto, tamaño CSS/táctil, foco visible/no oculto y pruebas por componente. | Pro Max aporta índice consultable; no declarar cumplimiento por catálogo. | +| §11 Verificación | Elegir skills por tarea, declarar auditores ausentes y documentar evidencia/limitaciones sin duplicar checklists. | ux-design-review aporta estructura parcial; Playwright ya existía. | +| §12 Gobernanza | Referencias al lock, procedencia y revisión de scripts/actualizaciones. | Nueva disciplina de mantenimiento del conocimiento; no una dependencia runtime. | + +El catálogo de componentes puede incorporar, en futuras tareas, ejemplos de estados de uso y decisiones de foco por consumidor. No es necesario construir ahora un showcase completo para cumplir una receta de shadcn ni afirmar cobertura porque todos los componentes estén importados. + +## 9. Revisión de impacto sobre V01 + +La propuesta sigue fundamentada por las geometrías observadas: menús apilados en móvil y competencia de tabla/panel en ventanas intermedias. Las skills nuevas **no aportan mediciones nuevas ni justifican cambiar la arquitectura B híbrida**, los 21 destinos o el modelo por capacidad. V01 permanece pendiente; no se repite su auditoría ni se actualiza su documento en esta tarea. + +| Criterio nuevo/revisado | Qué cubre ya V01 | Mejora concreta justificada para su futura implementación | +| --- | --- | --- | +| ux-navigation: visibles cuando quepan; orientación al entrar por enlace | Colapso/overlay por capacidad, compañía/sección persistentes, URLs y activo | Añadir a pruebas la identificación del destino sin abrir menú y acceso al último de los 21 destinos con teclado. Mantener el coste de dos activaciones declarado, sin inventar mejora de tiempos. | +| ux-navigation/Pro Max: iconos no deben sustituir reconocimiento | Raíl global con nombre accesible, tooltip/expansión; secundaria con etiquetas | Comprobar que la ayuda aparece también al foco y es alcanzable al tacto mediante expansión. No eliminar el raíl por una máxima genérica ni añadir una bottom nav sin necesidad demostrada. | +| Pro Max: foco no oculto, mínimo frente a enhanced | Sticky, scroll-padding y prueba de foco visible previstos | Separar en aceptación «foco al menos parcialmente visible» y objetivo de foco completamente visible, sin llamar AA al segundo. El motor devolvió ambas reglas por separado. | +| ux-navigation: títulos de documento describen ubicación | Franja y título en contenido; no contrato específico de `document.title` | Proponer como decisión menor un título `Activos · Compañía · Metis Desk` al entrar por URL. Requiere definir responsable de navegación; no modifica rutas ni es requisito aprobado. | +| Pro Max: etiquetas compactas y cadenas largas | V01 pide truncamiento con nombre accesible completo | Concretar una prueba de compañía con nombre largo/ID/URL: ningún overflow global y acceso al texto completo por teclado y puntero, no solo un atributo title o lector. | +| ux-tables: comparar sin perder acción | V01 propone tabla 800, panel mínimo 1144 y scroll interno | Validar ese presupuesto con varias filas, columnas opcionales y etiquetas inglesas. La skill no demuestra que 800 sea el mínimo correcto ni aporta otro umbral. | +| ux-search: búsqueda secundaria móvil expandible | Botón móvil propuesto con ámbito compañías/destinos preservado | Mantener activador fuera del Sheet y retorno al botón; no introducir resultados de activos o historial por usar ux-search. | +| Pro Max: no bloquear interacción por animaciones | Resize/foco/modalidad y una presentación de detalle previstos | Probar aperturas/cierres rápidos y resize durante transición, con movimiento reducido; el estado final no depende de animation-end. Complementa, no sustituye, las esperas estables de medición. | + +Las guías llaman «drawer» a un patrón, no a una obligación de importar otro componente o biblioteca. V01 puede usar el Sheet ya instalado basado en Dialog de Base UI. Su condición por contenedor sigue requiriendo coordinación semántica, no solo CSS que oculte un panel modal. El observer único y el modo discreto ya están justificados; no añadir un framework responsive ni observadores por enlace. + +Hay criterios genéricos en tensión con V01 —«no ocultar navegación primaria», «sin modales para navegación», «siempre icono + texto»— que no constituyen evidencia técnica contra el diseño: el overlay presenta enlaces normales conservando URL/historial y evita la pérdida de contenido medida. Mantener sus ventajas en escritorio amplio y verificar descubribilidad en los tamaños donde se oculta. Si las pruebas con usuarios muestran dificultad, revisar acceso a destinos frecuentes; no anticipar otra arquitectura sin esa evidencia. + +## 10. Gobernanza y mantenimiento propuestos + +### Incorporación de una skill + +1. Identificar una carencia concreta y comprobar que normas, componentes o skills existentes no la resuelven. Preferir no añadir otra guía que repita cinco checklists. +2. Revisar procedencia, licencia declarada, revisión upstream, instrucciones, referencias y dependencias. Separar consejo, comando, ejemplo y acción externa. No ejecutar lo descargado antes de leerlo. +3. Revisar scripts e imports transitivos: red, lectura de archivos, ejecución de procesos, instalación, escrituras y flags de sobrescritura. No pasar datos de clientes, credenciales o fragmentos privados a búsquedas/servicios externos. +4. Comprobar compatibilidad React 19/TypeScript/Tailwind 4/Vite/Base UI y APIs reales. Registrar conflictos y referencias faltantes. +5. Tras autorización aplicable, instalar en `.agents/skills/`, actualizar lock y documentar su referencia efectiva y responsabilidades. No registrar un commit/tag que el instalador no proporcione. Si falta referencia inmutable, declarar esa limitación junto a la huella disponible. +6. Validar una tarea representativa acotada. Para guía textual, comprobar instrucciones; para herramienta, prueba reproducible sin persistencia y, cuando corresponda, tests propios inspeccionados. Ninguna prueba del catálogo certifica la aplicación. + +### Actualizaciones + +Actualizar de forma explícita por necesidad, no descargar `latest` durante cada tarea de UI. Revisar diff de SKILL, referencias, scripts y datos; una misma versión `1.0.0` puede contener revisiones distintas. Registrar fecha, origen, huella anterior/nueva, comandos realmente ejecutados y cambios de compatibilidad. No modificar manualmente `computedHash` para hacer coincidir una copia ni asumir un algoritmo de hash que no se ha comprobado. + +Si se necesita reproducibilidad más fuerte que el lock actual, conservar el commit/tag resuelto y el método de instalación en la revisión que lo introduzca. La huella no demuestra autenticidad remota y no se validó aquí su algoritmo. No ejecutar los comandos de build de README upstream como si fueran scripts del monorepo. + +Pruebas mínimas tras cambios de herramientas: consultas de dominio/stack conocidas, salida vacía/fallback, ausencia de efectos de escritura en modo lectura y comprobación de rutas. Cambios en generación/persistencia se prueban únicamente en una salida desechable explícita. No ejecutar un `--force` contra el proyecto para validar. + +### Responsabilidades y registro + +| Responsable | Responsabilidad propuesta | +| --- | --- | +| Quien mantiene frontend/sistema visual | Selección por tarea, conflictos, tokens/patrones y revisión de cambios de API. | +| Quien ejecuta la tarea con Codex | Leer recursos, adaptar ejemplos, respetar alcance y declarar evidencia/limitaciones. | +| Revisión técnica del cambio | Comprobar fuente/huella/scripts y que no se han aplicado recetas incompatibles o añadido dependencias sin motivo. | +| Responsable funcional de Metis | Aprobar decisiones de producto y cambios de arquitectura visual, como V01; no las decide la skill. | + +En la entrega registrar únicamente las skills consultadas con influencia concreta y las comprobaciones realizadas. Mantener esta matriz como mapa, sin copiar todos los SKILL a AGENTS o DESIGN. Registrar «no instalada», «no pertinente» y «no ejecutada» por separado. Si falta una referencia, continuar con alcance declarado cuando sea posible; no afirmar haber leído un documento ausente. + +## 11. Comprobaciones realmente ejecutadas y limitaciones + +Entorno comprobado: bash/WSL en la raíz del proyecto, Node `v24.20.0`, PNPM `11.25.0` y Python `3.12.3`. + +Después de inspeccionar los módulos Python y sus ramas de lectura/escritura, se ejecutaron desde la raíz: + +```bash +PYTHONDONTWRITEBYTECODE=1 python3 .agents/skills/ui-ux-pro-max/scripts/search.py \ + 'focus not obscured' --domain ux -n 2 --json +PYTHONDONTWRITEBYTECODE=1 python3 .agents/skills/ui-ux-pro-max/scripts/search.py \ + 'dialog render base' --stack shadcn -n 2 --json +``` + +Ambas terminaron con código 0 y devolvieron dos resultados. La primera distinguió Focus Not Obscured Enhanced/Minimum; la segunda devolvió composición `render` para Base UI y selección explícita de base. Se verificó pertinencia y se detectó que el ejemplo abreviado del enlace necesita completar el contrato real de Button. No fue necesario reintentar. No se generaron MASTER, overrides, paquetes ni cachés Python en la instalación de skills. + +Se contaron archivos de catálogo con CSV/JSON de la biblioteca estándar y se inspeccionaron registros/referencias. No se ejecutó `validate_data.py`, la suite de tests de la skill, el showcase, shadcn CLI, herramientas de instalación ni scripts de generación. Es una prueba de búsqueda, **no una validación exhaustiva del catálogo ni de la interfaz**. + +En la auditoría original no se abrió otra auditoría de navegador ni se iniciaron entornos. Las pruebas visuales de V01 citadas pertenecen a su documento y no se presentan como repetidas aquí. No se ejecutaron build/typecheck/lint/tests globales: los cambios de auditoría y formalización son documentales. Se comprobaron enlaces locales, concordancia de nombres/lock y diff; los archivos de skills/lock se conservan. + +### Capacidades nuevas frente a las ya cubiertas + +**Nuevas realmente disponibles:** diez guías UX con decisiones por tipo de tarea, estructura de auditoría parcial y recuperación/microcopy más detalladas; buscador local de Pro Max con datos, criterios por stack y dos consultas comprobadas. **Ya cubiertas:** identidad visual, reutilización, contratos Base UI, composición React, estado mínimo, teclado/foco, responsive, estados y verificación de navegador, presentes en las cuatro skills anteriores y en normas/infraestructura de Metis. + +**No incorporadas por estas instalaciones:** auditores `*-audit`, política de orquestación ausente, certificación WCAG, tests E2E, servidor shadcn MCP, nueva búsqueda global, sistema de notificaciones, bibliotecas de UI/animación o implementación V01. Tampoco implican que un script, snippet o recomendación esté validado para cada consumidor. + +La adopción propuesta mejora la selección y profundidad de criterios; no cambia automáticamente ninguna pantalla ni convierte máximas generales en decisiones normativas. diff --git a/modules/companies/src/common/locales/en.json b/modules/companies/src/common/locales/en.json index c0916d1..429ec09 100644 --- a/modules/companies/src/common/locales/en.json +++ b/modules/companies/src/common/locales/en.json @@ -1,5 +1,14 @@ { "companies": { + "mobileNavigation": { + "global": "Global menu", + "search": "Search Metis Desk", + "sections": "Sections", + "company": "Sections for {{company}}", + "changeCompany": "Change company", + "close": "Close navigation", + "skip": "Skip to content" + }, "navigationCounts": { "loading": "Loading total…", "error": "The total could not be refreshed." diff --git a/modules/companies/src/common/locales/es.json b/modules/companies/src/common/locales/es.json index a7c99df..b01b8b9 100644 --- a/modules/companies/src/common/locales/es.json +++ b/modules/companies/src/common/locales/es.json @@ -1,5 +1,14 @@ { "companies": { + "mobileNavigation": { + "global": "Menú global", + "search": "Buscar en Metis Desk", + "sections": "Secciones", + "company": "Secciones de {{company}}", + "changeCompany": "Cambiar compañía", + "close": "Cerrar navegación", + "skip": "Saltar al contenido" + }, "navigationCounts": { "loading": "Cargando total…", "error": "No se ha podido actualizar el total." diff --git a/modules/companies/src/web/companies/detail/helpers/use-company-navigation-entries.ts b/modules/companies/src/web/companies/detail/helpers/use-company-navigation-entries.ts new file mode 100644 index 0000000..b82bf1b --- /dev/null +++ b/modules/companies/src/web/companies/detail/helpers/use-company-navigation-entries.ts @@ -0,0 +1,51 @@ +import { + BoxIcon, + BriefcaseIcon, + DatabaseIcon, + FileKeyIcon, + FolderIcon, + HomeIcon, + KeyRoundIcon, + LayersIcon, + LayoutTemplateIcon, + MailIcon, + MapPinIcon, + MonitorIcon, + NetworkIcon, + PersonIcon, + PrinterIcon, + WorkProfileIcon, +} from "@repo/rdx-ui/lib/icons"; +import { useTranslation } from "react-i18next"; + +/** Destinos únicos de compañía, compartidos por navegación fija, franja y Sheet. */ +export function useCompanyNavigationEntries() { + const { t } = useTranslation("companies"); + const { t: resourceTranslation } = useTranslation("resources"); + const { t: assetTranslation } = useTranslation("assets"); + const { t: accessTranslation } = useTranslation("access"); + + return [ + ["overview", t("companyOverview.navigation"), HomeIcon], + ["people", t("people"), PersonIcon], + ["workstations", assetTranslation("workstations"), MonitorIcon], + ["profiles", t("profiles.title"), WorkProfileIcon], + ["inventory", assetTranslation("inventory"), BoxIcon], + ["infrastructure", assetTranslation("infrastructure.title"), NetworkIcon], + ["resources", resourceTranslation("inventory"), LayersIcon], + ["departments", t("departments"), BriefcaseIcon], + ["sites", t("sites"), MapPinIcon], + ["telework", t("telework.title"), KeyRoundIcon], + ["remote", t("remote.title"), KeyRoundIcon], + ["processes", t("processes.title"), LayoutTemplateIcon], + ["access", accessTranslation("title"), KeyRoundIcon], + ["folders", t("folders.title"), FolderIcon], + ["certificates", t("certificates.title"), FileKeyIcon], + ["applications", t("applications.title"), LayoutTemplateIcon], + ["printers", t("printers.title"), PrinterIcon], + ["mail", t("mail.title"), MailIcon], + ["catalogs", t("catalogOverview.companyTitle"), DatabaseIcon], + ["assets", assetTranslation("companyTitle"), LayoutTemplateIcon], + ["resource-catalog", resourceTranslation("companyTitle"), LayoutTemplateIcon], + ] as const; +} diff --git a/modules/companies/src/web/companies/detail/ui/company-context-bar.tsx b/modules/companies/src/web/companies/detail/ui/company-context-bar.tsx new file mode 100644 index 0000000..ccf5abd --- /dev/null +++ b/modules/companies/src/web/companies/detail/ui/company-context-bar.tsx @@ -0,0 +1,66 @@ +import { BrowserLink } from "@metis/core/client"; +import { ChevronDownIcon } from "@repo/rdx-ui/lib/icons"; +import { Button } from "@repo/shadcn-ui/components/button"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@repo/shadcn-ui/components/tooltip"; +import { useContext } from "react"; +import { useTranslation } from "react-i18next"; + +import { companySectionHref } from "../../../workspace/shared/helpers"; +import { WorkspaceMobileNavigationContext } from "../../../workspace/shared/workspace-mobile-navigation-context"; +import { CompanyAvatar } from "../../shared"; + +export function CompanyContextBar({ + companyId, + companyName, + logoVersion, + sectionLabel, +}: { + companyId: string; + companyName: string; + logoVersion?: string | null | undefined; + sectionLabel: string; +}) { + const { t } = useTranslation("companies"); + const navigation = useContext(WorkspaceMobileNavigationContext); + + return ( +
+
+ ); +} diff --git a/modules/companies/src/web/companies/detail/ui/company-detail.tsx b/modules/companies/src/web/companies/detail/ui/company-detail.tsx index 556268e..463eb68 100644 --- a/modules/companies/src/web/companies/detail/ui/company-detail.tsx +++ b/modules/companies/src/web/companies/detail/ui/company-detail.tsx @@ -1,35 +1,19 @@ -import { BrowserLink } from "@metis/core/client"; -import { CountBadge } from "@repo/rdx-ui/components/count-badge"; -import { - BoxIcon, - BriefcaseIcon, - ChevronLeftIcon, - DatabaseIcon, - FileKeyIcon, - FolderIcon, - HomeIcon, - KeyRoundIcon, - LayersIcon, - LayoutTemplateIcon, - MailIcon, - MapPinIcon, - MonitorIcon, - NetworkIcon, - PersonIcon, - PrinterIcon, - WorkProfileIcon, -} from "@repo/rdx-ui/lib/icons"; -import { Avatar, AvatarBadge } from "@repo/shadcn-ui/components/avatar"; +import { ChevronLeftIcon } from "@repo/rdx-ui/lib/icons"; import { Button } from "@repo/shadcn-ui/components/button"; +import { useContext } from "react"; import { useTranslation } from "react-i18next"; -import type { CompanyDTO } from "../../../../common"; -import { companySectionHref } from "../../../workspace/shared/helpers"; -import { CompanyAvatar, CompanyEditorDialog, CompanyFeedback, CompanyIdentity } from "../../shared"; +import { WorkspaceMobileNavigationContext } from "../../../workspace/shared/workspace-mobile-navigation-context"; +import { CompanyEditorDialog, CompanyFeedback, CompanyIdentity } from "../../shared"; import { useCompanyDetailController } from "../controllers"; +import { useCompanyNavigationEntries } from "../helpers/use-company-navigation-entries"; import type { CompanyDetailProps } from "../types"; +import { CompanyContextBar } from "./company-context-bar"; +import { CompanyHeader } from "./company-header"; +import { CompanyNavigationSheet } from "./company-navigation-sheet"; import { CompanySection } from "./company-section"; +import { CompanySectionNavigation } from "./company-section-navigation"; export function CompanyDetail(props: CompanyDetailProps) { const { @@ -42,23 +26,23 @@ export function CompanyDetail(props: CompanyDetailProps) { } = props; const { query, editOpen, setEditOpen, tab, setTab, company, counts } = useCompanyDetailController(props); - const { t, i18n } = useTranslation("companies"); - const { t: resourceTranslation } = useTranslation("resources"); - const { t: assetTranslation } = useTranslation("assets"); - const { t: accessTranslation } = useTranslation("access"); + const { t } = useTranslation("companies"); + const navigation = useContext(WorkspaceMobileNavigationContext); + const entries = useCompanyNavigationEntries(); + const sectionLabel = entries.find(([section]) => section === tab)?.[1] ?? ""; + const initialContext = Boolean(props.section) || tab === initialSection; const layout = embedded ? { - root: "grid min-h-[calc(100dvh-140px)] grid-cols-[minmax(0,1fr)] grid-rows-[auto_auto_1fr] [grid-template-areas:'heading'_'navigation'_'main'] shell:min-h-[calc(100dvh-var(--spacing-shell-header))] shell:grid-cols-[240px_minmax(0,1fr)] shell:grid-rows-[auto_1fr] shell:[grid-template-areas:'heading_main'_'navigation_main']", + root: "grid min-h-[calc(100dvh-140px)] grid-cols-[minmax(0,1fr)] grid-rows-[auto_1fr] [grid-template-areas:'context'_'main'] shell:min-h-[calc(100dvh-var(--spacing-shell-header))] shell:grid-cols-[240px_minmax(0,1fr)] shell:grid-rows-[auto_1fr] shell:[grid-template-areas:'heading_main'_'navigation_main']", heading: - "flex items-center justify-between gap-3 border-b border-border p-3 [grid-area:heading] shell:block shell:px-4 shell:py-5", - navigation: - "flex flex-row flex-wrap gap-1.5 p-2.5 [grid-area:navigation] shell:flex-col shell:flex-nowrap shell:gap-1 shell:py-4", + "hidden border-b border-border [grid-area:heading] shell:block shell:px-4 shell:py-5", + navigation: "hidden [grid-area:navigation] shell:block", main: "min-w-0 border-t border-border bg-background p-4 [grid-area:main] shell:border-l shell:border-t-0 shell:p-6", } : { root: "space-y-6", heading: "flex flex-wrap items-start justify-between gap-4", - navigation: "flex flex-wrap gap-2", + navigation: "contents", main: undefined, }; @@ -109,82 +93,45 @@ export function CompanyDetail(props: CompanyDetailProps) { onSaved={() => setEditOpen(false)} open={editOpen} /> - - -
+
{!embedded && (
@@ -210,56 +155,3 @@ export function CompanyDetail(props: CompanyDetailProps) { ); } - -function CompanyHeader({ - activeLabel, - company, - embedded, - inactiveLabel, -}: { - activeLabel: string; - company: CompanyDTO; - embedded: boolean; - inactiveLabel: string; -}) { - const statusLabel = company.active ? activeLabel : inactiveLabel; - - if (embedded) { - return ( -
- - -

- {company.name} -

-
- ); - } - - return ( -
-

- -

-

{statusLabel}

-
- ); -} diff --git a/modules/companies/src/web/companies/detail/ui/company-header.tsx b/modules/companies/src/web/companies/detail/ui/company-header.tsx new file mode 100644 index 0000000..534ddfc --- /dev/null +++ b/modules/companies/src/web/companies/detail/ui/company-header.tsx @@ -0,0 +1,57 @@ +import { Avatar, AvatarBadge } from "@repo/shadcn-ui/components/avatar"; + +import type { CompanyDTO } from "../../../../common"; +import { CompanyAvatar, CompanyIdentity } from "../../shared"; + +export function CompanyHeader({ + activeLabel, + company, + embedded, + inactiveLabel, +}: { + activeLabel: string; + company: CompanyDTO; + embedded: boolean; + inactiveLabel: string; +}) { + const statusLabel = company.active ? activeLabel : inactiveLabel; + + if (embedded) { + return ( +
+ + +

+ {company.name} +

+
+ ); + } + + return ( +
+

+ +

+

{statusLabel}

+
+ ); +} diff --git a/modules/companies/src/web/companies/detail/ui/company-navigation-sheet.tsx b/modules/companies/src/web/companies/detail/ui/company-navigation-sheet.tsx new file mode 100644 index 0000000..60ac1e4 --- /dev/null +++ b/modules/companies/src/web/companies/detail/ui/company-navigation-sheet.tsx @@ -0,0 +1,48 @@ +import { Button } from "@repo/shadcn-ui/components/button"; +import type { ReactNode } from "react"; +import { useContext } from "react"; +import { useTranslation } from "react-i18next"; + +import { WorkspaceMobileSheet } from "../../../workspace/shared/ui/workspace-mobile-sheet"; +import { WorkspaceMobileNavigationContext } from "../../../workspace/shared/workspace-mobile-navigation-context"; + +export function CompanyNavigationSheet({ + companyName, + sectionLabel, + onEdit, + children, +}: { + companyName: string; + sectionLabel: string; + onEdit: () => void; + children: ReactNode; +}) { + const { t } = useTranslation("companies"); + const navigation = useContext(WorkspaceMobileNavigationContext); + + return ( + +
+

{sectionLabel}

+ +
+ {children} +
+ +
+
+ ); +} diff --git a/modules/companies/src/web/companies/detail/ui/company-section-navigation.tsx b/modules/companies/src/web/companies/detail/ui/company-section-navigation.tsx new file mode 100644 index 0000000..42dd05c --- /dev/null +++ b/modules/companies/src/web/companies/detail/ui/company-section-navigation.tsx @@ -0,0 +1,100 @@ +import { BrowserLink } from "@metis/core/client"; +import { CountBadge } from "@repo/rdx-ui/components/count-badge"; +import { Button, buttonVariants } from "@repo/shadcn-ui/components/button"; +import type { MouseEventHandler } from "react"; +import { useTranslation } from "react-i18next"; + +import { companySectionHref } from "../../../workspace/shared/helpers"; +import type { CompanyTab } from "../../shared/types"; +import type { useCompanyNavigationEntries } from "../helpers/use-company-navigation-entries"; + +export function CompanySectionNavigation({ + companyId, + activeSection, + entries, + counts, + layout = "sidebar", + onNavigate, + onSectionChange, +}: { + companyId: string; + activeSection: CompanyTab; + entries: ReturnType; + counts: Readonly>>; + layout?: "sidebar" | "inline"; + onNavigate?: MouseEventHandler | undefined; + onSectionChange?: (section: CompanyTab) => void; +}) { + const { t, i18n } = useTranslation("companies"); + + return ( + + ); +} diff --git a/modules/companies/src/web/companies/shared/companies-page.test.tsx b/modules/companies/src/web/companies/shared/companies-page.test.tsx index 1597efb..c7195da 100644 --- a/modules/companies/src/web/companies/shared/companies-page.test.tsx +++ b/modules/companies/src/web/companies/shared/companies-page.test.tsx @@ -402,3 +402,44 @@ test("sedes: listado traducido con filtro de archivadas y solo sedes activas por assert.ok(form.includes('aria-checked="false"')); } }); + +test("la navegación de compañía conserva 21 enlaces reales, sección activa y contexto es/en", async () => { + for (const language of ["es", "en"]) { + const name = "Empresa de soporte con un nombre largo"; + const markup = await renderPage( + language, + undefined} + onSectionChange={() => undefined} + section="inventory" + />, + (client) => + client.setQueryData(companyKeys.detail(companyId), { + company: { + id: companyId, + name, + active: true, + people: [], + contacts: [], + sites: [], + departments: [], + assignments: [], + }, + }) + ); + const navigation = markup.match(/]*>[\s\S]*?<\/nav>/)?.[0]; + + assert.ok(navigation); + const destinations = Array.from(navigation.matchAll(/href="([^"]+)"/g), (match) => match[1]); + + assert.equal(destinations.length, 21); + assert.equal(new Set(destinations).size, 21); + assert.equal((navigation.match(/aria-current="page"/g) ?? []).length, 1); + assert.equal(navigation.includes('role="button"'), false); + assert.ok(destinations.includes(`/companies/${companyId}/catalogs/resources`)); + assert.ok(markup.includes(language === "es" ? `Secciones de ${name}` : `Sections for ${name}`)); + assert.equal(markup.includes('role="dialog"'), false); + } +}); diff --git a/modules/companies/src/web/workspace/shared/helpers/workspace-content-focus.ts b/modules/companies/src/web/workspace/shared/helpers/workspace-content-focus.ts new file mode 100644 index 0000000..65864f7 --- /dev/null +++ b/modules/companies/src/web/workspace/shared/helpers/workspace-content-focus.ts @@ -0,0 +1,11 @@ +/** Resuelve el contenido activo; las cabeceras de las navegaciones no son destinos de foco. */ +export function workspaceContentFocusTarget(): HTMLElement | null { + const main = document.getElementById("workspace-main"); + const target = main?.querySelector("h1, h2") ?? main; + + if (target) { + target.tabIndex = -1; + } + + return target; +} diff --git a/modules/companies/src/web/workspace/shared/ui/workspace-mobile-navigation-provider.tsx b/modules/companies/src/web/workspace/shared/ui/workspace-mobile-navigation-provider.tsx new file mode 100644 index 0000000..2f39b69 --- /dev/null +++ b/modules/companies/src/web/workspace/shared/ui/workspace-mobile-navigation-provider.tsx @@ -0,0 +1,93 @@ +import { Sheet } from "@repo/shadcn-ui/components/sheet"; +import type { MouseEvent, ReactNode } from "react"; +import { useEffect, useId, useRef, useState } from "react"; + +import { workspaceContentFocusTarget } from "../helpers/workspace-content-focus"; +import type { WorkspaceMobilePanel } from "../workspace-mobile-navigation-context"; +import { WorkspaceMobileNavigationContext } from "../workspace-mobile-navigation-context"; + +/** Una única raíz modal permite cambiar de menú sin apilar Sheet ni devolver foco al fondo. */ +export function WorkspaceMobileNavigationProvider({ children }: { children: ReactNode }) { + const [state, setState] = useState<{ panel: WorkspaceMobilePanel; open: boolean }>({ + panel: "global", + open: false, + }); + const triggerRef = useRef(undefined); + const focusRef = useRef<"trigger" | "content" | "none">("trigger"); + const contentId = useId(); + + function close(focus: "trigger" | "content" | "none" = "trigger") { + focusRef.current = focus; + setState((previous) => ({ ...previous, open: false })); + } + + function show(panel: WorkspaceMobilePanel, trigger?: HTMLElement) { + if (!state.open) { + const activeElement = document.activeElement; + + triggerRef.current = + trigger ?? (activeElement instanceof HTMLElement ? activeElement : undefined); + } + focusRef.current = "trigger"; + setState({ panel, open: true }); + } + + function onNavigate(event: MouseEvent) { + if ( + !event.defaultPrevented && + event.button === 0 && + !event.metaKey && + !event.ctrlKey && + !event.shiftKey && + !event.altKey + ) { + close("content"); + } + } + + function finalFocus() { + if (focusRef.current === "none") { + return false; + } + if (focusRef.current === "content") { + return workspaceContentFocusTarget() ?? false; + } + const trigger = triggerRef.current; + + return trigger?.isConnected && trigger.getClientRects().length > 0 + ? trigger + : (document.querySelector( + "[data-workspace-desktop-navigation] [aria-current=page]" + ) ?? + workspaceContentFocusTarget() ?? + false); + } + + useEffect(() => { + const media = window.matchMedia("(min-width: 768px)"); + + const onChange = () => { + if (media.matches) { + focusRef.current = "trigger"; + setState((previous) => ({ ...previous, open: false })); + } + }; + + media.addEventListener("change", onChange); + + return () => media.removeEventListener("change", onChange); + }, []); + + return ( + + setState((previous) => ({ ...previous, open }))} + open={state.open} + > + {children} + + + ); +} diff --git a/modules/companies/src/web/workspace/shared/ui/workspace-mobile-sheet.tsx b/modules/companies/src/web/workspace/shared/ui/workspace-mobile-sheet.tsx new file mode 100644 index 0000000..5092700 --- /dev/null +++ b/modules/companies/src/web/workspace/shared/ui/workspace-mobile-sheet.tsx @@ -0,0 +1,71 @@ +import { XIcon } from "@repo/rdx-ui/lib/icons"; +import { Button } from "@repo/shadcn-ui/components/button"; +import { + SheetClose, + SheetContent, + SheetHeader, + SheetTitle, +} from "@repo/shadcn-ui/components/sheet"; +import type { ReactNode } from "react"; +import { useContext } from "react"; +import { useTranslation } from "react-i18next"; + +import type { WorkspaceMobilePanel } from "../workspace-mobile-navigation-context"; +import { WorkspaceMobileNavigationContext } from "../workspace-mobile-navigation-context"; + +export function WorkspaceMobileSheet({ + panel, + title, + children, +}: { + panel: WorkspaceMobilePanel; + title: string; + children: ReactNode; +}) { + const navigation = useContext(WorkspaceMobileNavigationContext); + const { t } = useTranslation("companies"); + + if (!navigation || navigation.panel !== panel) { + return null; + } + + return ( + { + const content = document.getElementById(navigation.contentId); + + return ( + content?.querySelector( + panel === "search" ? "input" : "[aria-current=page]" + ) ?? + content?.querySelector("a, button") ?? + interaction !== "touch" + ); + }} + showCloseButton={false} + side="left" + > + + + {title} + + + } + > + + +
{children}
+
+ ); +} diff --git a/modules/companies/src/web/workspace/shared/ui/workspace-navigation-button.tsx b/modules/companies/src/web/workspace/shared/ui/workspace-navigation-button.tsx index d159c4e..bfd9cb6 100644 --- a/modules/companies/src/web/workspace/shared/ui/workspace-navigation-button.tsx +++ b/modules/companies/src/web/workspace/shared/ui/workspace-navigation-button.tsx @@ -1,31 +1,42 @@ import { BrowserLink } from "@metis/core/client"; -import { Button } from "@repo/shadcn-ui/components/button"; +import { Button, buttonVariants } from "@repo/shadcn-ui/components/button"; import { cn } from "@repo/shadcn-ui/lib/utils"; -import type { ComponentProps } from "react"; +import type { HTMLAttributes } from "react"; export function WorkspaceNavigationButton({ level = "primary", className, href, ...props -}: ComponentProps & { level?: "primary" | "catalog"; href?: string }) { +}: HTMLAttributes & { level?: "primary" | "catalog"; href?: string }) { + const navigationClassName = cn( + "shell:group-data-[collapsed=true]/sidebar:justify-center shell:group-data-[collapsed=true]/sidebar:px-0 shell:group-data-[collapsed=true]/sidebar:[&>span:not([data-slot$=avatar])]:hidden", + level === "catalog" + ? "h-auto min-h-[38px] justify-start gap-2.5 whitespace-normal text-left text-shell-sidebar-muted data-[active=true]:bg-white/[0.08] data-[active=true]:hover:bg-white/[0.08] dark:data-[active=true]:hover:bg-white/[0.08] data-[active=true]:text-white" + : "h-[42px] min-h-[42px] w-auto justify-start gap-2 border-0 border-l-2 border-l-transparent rounded-shell-control bg-transparent px-2.5 py-0 text-sm font-normal text-shell-sidebar-foreground shadow-none hover:bg-white/[0.06] hover:text-white dark:hover:bg-white/[0.06] aria-expanded:bg-transparent aria-expanded:hover:bg-white/[0.06] dark:aria-expanded:hover:bg-white/[0.06] aria-expanded:text-shell-sidebar-foreground aria-expanded:hover:text-white data-[active=true]:border-l-shell-accent data-[active=true]:bg-[linear-gradient(90deg,var(--color-shell-active-start),var(--color-shell-active-end))] data-[active=true]:text-white data-[active=true]:hover:bg-transparent dark:data-[active=true]:hover:bg-transparent focus-visible:border-transparent focus-visible:ring-0 focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-3 focus-visible:outline-shell-accent data-[active=true]:focus-visible:border-l-shell-accent shell:h-shell-navigation shell:min-h-shell-navigation shell:w-full shell:gap-[19px] shell:pl-3.5 shell:pr-3 shell:text-base shell:leading-[1.4285714286] [&>svg]:stroke-[1.6] shell:group-data-[collapsed=true]/sidebar:gap-0 [&>span:not([data-slot$=avatar])]:truncate", + className + ); + + if (href) { + const { "aria-pressed": pressed, ...linkProps } = props; + + return ( + + ); + } + return ( - - )} - {administrationContent ? ( -
{administrationContent}
- ) : current.area === "company" ? ( - setLocation({ area: "directory" })} - onSectionChange={(section) => setLocation({ area: "company", id: current.id, section })} - section={current.section} +
+ - ) : ( -
- + {coreTranslation("workspace")} + {!administrationContent && query.isPending && ( +

+ {t("loading")} +

+ )} + {!administrationContent && query.isError && ( +
+ + +
+ )} + {administrationContent ? ( +
+ {administrationContent} +
+ ) : current.area === "company" ? ( + setLocation({ area: "directory" })} + onSectionChange={(section) => + setLocation({ area: "company", id: current.id, section }) + } + section={current.section} /> -
- )} + ) : ( +
+ +
+ )} +
- + ); } diff --git a/modules/companies/src/web/workspace/view/ui/workspace-header.tsx b/modules/companies/src/web/workspace/view/ui/workspace-header.tsx index 187e2a7..78ea110 100644 --- a/modules/companies/src/web/workspace/view/ui/workspace-header.tsx +++ b/modules/companies/src/web/workspace/view/ui/workspace-header.tsx @@ -1,14 +1,14 @@ -import { BrowserLink } from "@metis/core/client"; -import { BellIcon, ChevronDownIcon, SearchIcon } from "@repo/rdx-ui/lib/icons"; +import { BrowserLink, MetisLogo } from "@metis/core/client"; +import { BellIcon, ChevronDownIcon, MenuIcon, SearchIcon } from "@repo/rdx-ui/lib/icons"; import { Button } from "@repo/shadcn-ui/components/button"; -import { Input } from "@repo/shadcn-ui/components/input"; import type { ReactNode } from "react"; -import { useState } from "react"; +import { useContext } from "react"; import { useTranslation } from "react-i18next"; -import { CompanyIdentity } from "../../../companies"; import { InitialsAvatar } from "../../../shared"; -import { companySectionHref, workspaceHref } from "../../shared/helpers"; +import { WorkspaceMobileNavigationContext } from "../../shared/workspace-mobile-navigation-context"; + +import { WorkspaceSearch } from "./workspace-search"; export function WorkspaceHeader({ companies, @@ -22,84 +22,47 @@ export function WorkspaceHeader({ languageSwitcher: ReactNode; }) { const { t } = useTranslation("companies"); + const navigation = useContext(WorkspaceMobileNavigationContext); const { t: coreTranslation } = useTranslation("core"); - const [search, setSearch] = useState(""); return ( -
-
-