docs: actualizar la documentación sobre la estructura web y organización de módulos

This commit is contained in:
David Arranz 2026-10-08 19:38:41 +02:00
parent d116fef653
commit f5dd8fdf31
8 changed files with 159 additions and 54 deletions

View File

@ -57,6 +57,7 @@ Las fuentes normativas se consultan, no se duplican:
- [Contratos HTTP](docs/arquitectura/contratos-http.md)
- [Pruebas](docs/arquitectura/pruebas.md)
- [Modelo DDD e identificadores](docs/arquitectura/modelo-ddd-e-identificadores.md)
- [Estructura web por entidades y operaciones](docs/estructura-web-por-entidades-y-operaciones.md)
## Seguridad y persistencia
@ -77,6 +78,8 @@ Las fuentes normativas se consultan, no se duplican:
## Frontend
Todo código web nuevo de módulos, entidades y operaciones debe seguir [la estructura web por entidades y operaciones](docs/estructura-web-por-entidades-y-operaciones.md): `modules/<module>/src/web/<entity>/<operation>/`, con ámbitos `shared` según sus consumidores e índices únicamente donde haya contenido exportable para otras unidades. Esta guía es la fuente normativa de la organización interna del frontend; no cambia las capas del servidor ni la implementación vendor.
React, TanStack Query, Tailwind y shadcn/ui (Base UI, estilo Vega). Los componentes compartidos viven en @repo/shadcn-ui. Incorporar TanStack Table y React Hook Form cuando haya consumidores.
Estado remoto en TanStack Query; claves centralizadas e invalidación explícita después de mutaciones.
No duplicar estado remoto ni usar efectos para derivar valores calculables durante render.
@ -317,4 +320,4 @@ Al finalizar:
- Revisar el diff completo, no únicamente los ficheros editados individualmente.
- Comprobar que no se hayan introducido duplicaciones, componentes excesivamente grandes, varios componentes relevantes por fichero, imports no usados, casts innecesarios ni efectos React evitables.
- Ejecutar las validaciones apropiadas para el alcance real del cambio.
- Ejecutar las validaciones apropiadas para el alcance real del cambio.

View File

@ -5,6 +5,7 @@ Aplicación interna para soporte técnico a pequeñas empresas.
- [Preparación y ejecución](docs/puesta-en-marcha.md)
- [Modelo DDD e identificadores](docs/arquitectura/modelo-ddd-e-identificadores.md)
- [Organización del código](docs/arquitectura/capas-del-servidor.md)
- [Organización web y guía para nuevas entidades y operaciones](docs/estructura-web-por-entidades-y-operaciones.md)
- [Usuarios y acceso](docs/usuarios-y-acceso.md)
- [Compañías, contactos y sedes](docs/companias-contactos-y-sedes.md)
- [Personas y departamentos](docs/personas-y-departamentos.md)

View File

@ -20,8 +20,8 @@ packages/
Los módulos funcionales seguirán esta estructura. Dentro de api, crear domain, application e infrastructure cuando existan responsabilidades que lo requieran.
Dentro de `web`, el código se organiza por entidad o área funcional y después por operación,
con ámbitos compartidos e índices explícitos según la
En módulos existentes y nuevos, el código de `web` debe organizarse por entidad o área
funcional y después por operación, con ámbitos compartidos e índices explícitos según la
[estructura web por entidades y operaciones](../estructura-web-por-entidades-y-operaciones.md).
La primera entrega solo comprueba la vida del proceso. No hay reglas de negocio, agregados, repositorios ni casos de uso artificiales para esta comprobación técnica.

View File

@ -14,5 +14,14 @@ Cuando sea necesario distinguir superficies:
No exportar repositorios ni modelos ORM a otros módulos. No crear contratos públicos sin consumidores.
## Superficie web de los módulos
Al crear un módulo con código de navegador o ampliar uno existente, aplicar la
[estructura web por entidades y operaciones](../estructura-web-por-entidades-y-operaciones.md).
`./client` apunta a `src/web/index.ts` cuando exista una API web pública con consumidores.
Ese índice compone contratos internos explícitos; no expone automáticamente todas las
entidades, operaciones o componentes del módulo. `./common` y `./module` mantienen sus
fronteras de contratos compartidos y composición del servidor.
En esta entrega core expone registerCoreRoutes mediante ./module, una frontera de composición del servidor.
./common expone contratos y ./client expone la página y recursos de traducción. No hay dependencias locales del ERP.

View File

@ -33,6 +33,15 @@ Usar tokens semánticos como bg-background, text-foreground y border-border en v
## Añadir componentes
Para componentes propios de un módulo, consultar primero la
[estructura web por entidades y operaciones](estructura-web-por-entidades-y-operaciones.md)
y los componentes equivalentes existentes. Ubicar cada componente en la entidad y operación
responsables, o en su ámbito compartido cuando tenga varios consumidores reales. Los
componentes específicos de Metis Desk no pertenecen al paquete visual genérico.
La incorporación de primitivas al paquete vendor requiere una instrucción explícita del
usuario según `AGENTS.md`. En ese caso, utilizar el procedimiento siguiente.
Desde la raíz del proyecto, dentro de WSL:
```bash

View File

@ -1,8 +1,27 @@
# Estructura web por entidades y operaciones
El código de navegador de cada módulo se organiza primero por entidad o área funcional y,
dentro de ella, por las operaciones existentes. Esta estructura se aplica a `core`, `identity`,
`assets`, `resources` y `companies`. No modifica la composición del servidor ni los contratos HTTP.
Fuente normativa de la organización interna del código web de Metis Desk, referenciada
desde `AGENTS.md`. Todo módulo nuevo y toda entidad, operación o componente que se añada
a un módulo existente debe seguir esta estructura. Los módulos `core`, `identity`, `assets`,
`resources` y `companies` proporcionan ejemplos de su aplicación.
## Regla de organización
El orden obligatorio es módulo → entidad o área funcional → operación → responsabilidad:
```text
modules/<module>/src/web/<entity>/<operation>/<responsibility>/
```
Por ejemplo, `web/tickets/create/ui/`, no `web/create/tickets/` ni un directorio global
`web/components/` para componentes de entidades distintas. Utilizar nombres en inglés y
kebab-case. Las áreas sin una entidad de negocio propia, como `dashboard` o `workspace`,
siguen el mismo orden con operaciones descriptivas como `view`.
Esta regla organiza el código del navegador, no prescribe nuevas entidades DDD ni cambia
la estructura de `api` y `common`, la composición del servidor o los contratos HTTP.
No obliga a crear carpetas para todas las operaciones posibles ni todas las responsabilidades:
cada directorio debe contener una responsabilidad real de la implementación actual.
## Ubicación de las responsabilidades
@ -51,22 +70,30 @@ El árbol muestra las ubicaciones, no implica exportar todos los ficheros. Los
propia no reciben directorios vacíos: por ejemplo, el editor común de alta y edición permanece
en `tickets/shared/ui/`.
- `ui`: componentes y formularios existentes. Un componente principal por fichero, sin nuevas
- `ui`: componentes y formularios. Un componente principal por fichero, sin nuevas
subdivisiones de JSX trivial.
- `api`: funciones de acceso HTTP y claves de consultas existentes.
- `controllers` y `hooks`: responsabilidades React ya separadas en el código original, como
`catalog/shared/controllers/use-catalog-save.ts` y
`work-environments/detail/hooks/use-environment-queries.ts`.
- `types` y `helpers`: contratos y cálculos existentes, próximos a sus consumidores.
- `api`: funciones de acceso HTTP y claves de consultas de su ámbito, sin dependencias de UI.
- `queries`: claves y configuración de TanStack Query cuando ya tengan una responsabilidad
separada, como `health/shared/queries/core.query-keys.ts`; no duplicarlas también en `api`.
- `controllers`: coordinación React de una operación cuando necesite una responsabilidad
propia, como `catalog/shared/controllers/use-catalog-save.ts`.
- `hooks`: comportamiento React reutilizable o una responsabilidad React identificable,
como `work-environments/detail/hooks/use-environment-queries.ts`.
- `types` y `helpers`: contratos locales y cálculos, próximos a sus consumidores.
- `schemas`, `adapters`, `entities` o `utils`: solo cuando existan validaciones de formulario,
adaptaciones, modelos de UI o utilidades concretas que requieran esos ficheros. No duplicar
DTOs ni schemas HTTP de `common`, trasladar entidades de dominio a `web` o crear cajones
de sastre genéricos.
- `<entity>/shared`: código utilizado por varias operaciones de la misma entidad y pruebas
que cubren conjuntamente esas operaciones.
- `web/shared`: recursos de varias entidades del módulo, incluidas traducciones y componentes
comunes. No sustituye a `common`, que sigue conteniendo contratos y schemas compartidos con
el servidor.
No se han creado controllers, hooks, schemas ni adapters para trasladar lógica desde los
componentes. Esa auditoría queda para una tarea posterior. Las props y los schemas que ya
pertenecían a sus ficheros mantienen su contrato y ubicación relativa a la responsabilidad.
Crear únicamente los subdirectorios que la operación necesite. Una pantalla sencilla no
requiere un controller, hook o adapter artificial. Los elementos privados inseparables del
componente pueden permanecer junto a él; separar por responsabilidad, no por tamaño ni
para completar el árbol de un ejemplo.
## Entidades y ámbitos compartidos
@ -79,20 +106,34 @@ y `history`. Las operaciones particulares conservan nombres explícitos, como `c
`processes` y `process-templates`; `interventions` e `intervention-templates`.
Los componentes de dashboard y workspace siguen el mismo criterio funcional.
Un nuevo módulo conserva los niveles de entidad y operación aunque inicialmente contenga
una sola entidad. Para una entidad nueva, comprobar antes si pertenece a un módulo existente:
la estructura web no es un motivo para crear un paquete independiente. Para una operación
nueva, utilizar un nombre que describa su intención y no duplicar operaciones que ya cubran
el mismo comportamiento.
Ubicar código privado de una operación dentro de ella. Promoverlo a `<entity>/shared` cuando
lo consuman varias operaciones de esa entidad, o a `web/shared` cuando lo consuman varias
entidades del módulo. No usar `shared` como ubicación por defecto ni anticipar reutilización.
Las traducciones siguen [su estándar](traducciones.md); los componentes visuales genéricos
compartidos siguen [componentes de interfaz](componentes-de-interfaz.md).
Los formularios y diálogos que sirven tanto para crear como para editar se mantienen en el
`shared/ui` de su entidad. No se duplican ni se introducen wrappers de alta y edición sin
comportamiento propio. Un diálogo de alta de tickets pertenece a `tickets/create`, aunque
se abra desde una intervención; el consumidor no determina la identidad del componente.
Los clientes HTTP existentes que abarcan varias entidades permanecen en el ámbito compartido
adecuado: por ejemplo, `identity/shared/api/identity-api.ts` y
`assets/shared/api/inventory-api.ts`. Separar sus métodos o auditar su lógica excede esta
reorganización. Las funciones, firmas y claves de consultas no cambian.
Situar el acceso HTTP en el ámbito más específico que corresponda a sus consumidores:
`<entity>/<operation>/api`, `<entity>/shared/api` o `web/shared/api`. Los clientes existentes
que abarcan varias entidades, como `identity/shared/api/identity-api.ts` y
`assets/shared/api/inventory-api.ts`, pueden permanecer compartidos. No dividir un cliente
ni alterar sus firmas, queries o contratos únicamente para reproducir el árbol de ejemplo.
## Índices y dependencias
Cada nivel con un contrato consumido desde otra unidad dispone de un `index.ts` que enumera
sus exportaciones. No se crean índices vacíos, exportaciones `*`, exportaciones de tests ni
Cada nivel debe tener un `index.ts` solo cuando contenga símbolos exportables para otras
unidades con consumidores reales. El índice enumera ese contrato, no todos los exports
de los ficheros internos. No se crean índices vacíos, exportaciones `*`, exportaciones de tests ni
índices para detalles privados utilizados exclusivamente dentro de su propio directorio.
Los tipos se reexportan mediante `export type`.
@ -102,9 +143,12 @@ imports directos específicos cuando un índice agregado añadiría un ciclo de
por ejemplo en flujos de altas anidadas. No se amplía la API pública para resolver esas
dependencias internas.
`web/index.ts` mantiene los símbolos públicos originales de `@metis/<module>/client` y
compone los índices internos. Las aplicaciones y otros módulos continúan utilizando ese
contrato público. Los antiguos caminos internos no tienen reexports de compatibilidad.
`web/index.ts` compone los índices internos y define los símbolos públicos de
`@metis/<module>/client` cuando haya consumidores externos. Las aplicaciones y otros módulos
utilizan ese contrato, no caminos internos del módulo. Al ampliar un módulo existente,
mantener sus contratos públicos salvo un cambio deliberado con revisión de consumidores.
No añadir reexports de compatibilidad para caminos internos antiguos cuando todos sus
consumidores puedan actualizarse de forma segura.
## Pruebas y mantenimiento
@ -112,6 +156,33 @@ Las pruebas existentes acompañan al ámbito que comprueban. Los scripts de cada
referencian sus nuevas ubicaciones; los imports, traducciones y enlaces de documentación
se actualizan junto con los movimientos.
Las pruebas nuevas específicas de una operación viven junto a la responsabilidad que
comprueban. Las que cubren varias operaciones se sitúan en el `shared` correspondiente.
No exportar tests desde los índices. Registrar los nuevos ficheros en el script del paquete
cuando utilice una enumeración explícita.
## Lista de comprobación para nuevos desarrollos
1. Consultar esta guía, las capas aplicables y las implementaciones equivalentes existentes.
2. Identificar módulo, entidad o área funcional y operación antes de crear ficheros.
3. Crear solo responsabilidades necesarias y elegir el ámbito compartido por sus consumidores.
4. Reutilizar formularios de alta/edición y primitivas visuales; separar modales significativos
en su propio `.tsx` sin trasladar todo su contenido al listado.
5. Definir índices explícitos únicamente donde haya contratos exportables a otras unidades;
comprobar imports de tipos, consumidores y ausencia de nuevos ciclos.
6. Mantener DTOs y schemas compartidos en `common`, traducciones es/en y acceso HTTP fuera
de la presentación; no introducir capas o dependencias para completar una estructura.
7. Añadir pruebas observables, actualizar scripts y enlaces, y revisar el diff completo.
8. Ejecutar comprobaciones dirigidas al paquete o las cinco validaciones globales cuando
el cambio sea transversal, según [pruebas](arquitectura/pruebas.md).
## Alcance de la migración realizada
La reorganización inicial trasladó ficheros y ajustó sus imports, sin cambiar comportamiento
ni contratos. No extrajo lógica nueva a controllers, hooks, schemas o adapters; esa auditoría
se aplazó por decisión del usuario. Esa limitación describe la migración, no exime al código
nuevo de las reglas de separación de responsabilidades de `AGENTS.md`.
Para futuros cambios, consultar también [componentes de interfaz](componentes-de-interfaz.md),
[inventario de modales](inventario-de-modales.md), [patrón de listados](patron-de-listados-de-entidades.md)
y [pruebas](arquitectura/pruebas.md). No trasladar una responsabilidad compartida a una

View File

@ -2,6 +2,12 @@
Estándar de Metis Desk. Fuente funcional: patrón acordado con el usuario en esta conversación. La referencia de Activos conserva el estilo Vega, los tokens semánticos y la ficha lateral real de la aplicación.
Los listados nuevos y sus operaciones deben aplicar la
[estructura web por entidades y operaciones](estructura-web-por-entidades-y-operaciones.md).
El listado pertenece a `<entity>/list/ui`; sus altas, detalles e historiales a sus operaciones
respectivas. Los formularios y diálogos comunes de alta y edición pertenecen a
`<entity>/shared/ui`, no al fichero del listado ni a un directorio global de componentes.
## Estructura obligatoria
La pantalla se divide en cabecera, contenido y pie opcional. La cabecera muestra título y subtítulo breve a la izquierda y acciones globales a la derecha. El alta es la acción primaria, normalmente «+ Añadir [entidad]», y abre un modal; se conservan las etiquetas actuales cuando ya describen una operación concreta. Nunca mostrar el formulario junto al listado.
@ -42,41 +48,43 @@ Usar tabla semántica con nombre accesible, th con scope, controles etiquetados,
Carga inicial y errores se presentan dentro del contenido sin afirmar que no hay registros. El vacío distingue ausencia de datos y búsqueda sin resultados. Mantener el mensaje y la recuperación de errores de cada consulta; no inventar resultados ni endpoints. Las recargas conservan datos y controles cuando la integración existente lo permite.
## Inventario previo a la migración
## Inventario de listados y ubicaciones actuales
La aplicación navega por espacios y secciones en memoria, no mediante rutas URL independientes. Los componentes siguientes se alcanzan desde CompaniesWorkspace, CompanyDetail o IdentityWorkspace.
| Listado / ámbito | Componente actual | Aplicación |
| Listado / ámbito | Componente y ubicación actual | Aplicación |
| --- | --- | --- |
| Compañías | companies/components/companies-page.tsx | Sí |
| Personas/contactos | companies/components/people-section.tsx, PeopleSection | Sí |
| Departamentos | companies/components/department-section.tsx | Sí |
| Sedes | companies/components/company-detail.tsx, SiteSection | Sí |
| Usuarios internos | identity/components/user-management.tsx | Sí |
| Activos | assets/inventory-page.tsx | Sí; conserva panel lateral |
| Puestos de trabajo | assets/workstations-page.tsx | Sí |
| Recursos, incluidas aplicaciones y licencias | resources/resources-page.tsx | Sí |
| Campos y plantillas de recursos, global/compañía | resources/catalog-page.tsx | Sí; dos secciones |
| Campos, plantillas y relaciones de activos, global/compañía | assets/catalog-page.tsx | Sí; tres secciones |
| Cuentas y accesos / catálogos informáticos | companies/access/access-workspace.tsx | Sí; cuenta, titularidad, grupo, pertenencia, autorización, necesidad, cobertura, entorno, sistema, tipo, permiso y plantilla |
| Perfiles de usuario | companies/profiles/work-profiles-page.tsx | Sí |
| Correo por perfil | companies/mail/mail-configurations-page.tsx | Sí, también entrada desde perfil |
| Carpetas por perfil | companies/folders/folder-mappings-page.tsx | Sí, también entrada desde perfil |
| Impresoras por perfil | companies/printers/printer-configurations-page.tsx | Sí, también entrada desde perfil |
| Aplicaciones por perfil | companies/applications/application-configurations-page.tsx | Sí, también entrada desde perfil |
| Certificados por perfil | companies/certificates/certificate-configurations-page.tsx | Sí, también entrada desde perfil |
| Teletrabajo | companies/telework/telework-authorizations-page.tsx | Sí, conserva ámbito de persona |
| Accesos remotos | companies/remote/remote-connections-page.tsx | Sí, conserva ámbito de persona |
| Procesos de personas | companies/processes/processes-page.tsx | Sí; su detalle es excepción |
| Plantillas de procesos | companies/processes/process-templates-page.tsx | Sí |
| Plantillas de intervención, global/compañía | companies/intervention-templates/templates-page.tsx | Sí |
| Tickets, global/compañía | companies/tickets/tickets-page.tsx | Sí |
| Intervenciones, global/compañía | companies/interventions/interventions-page.tsx | Sí |
| Marcas globales | assets/brands-page.tsx | Sí; EntityListHeader y EntityTable, filtro de estado y modales |
| Herramientas de asistencia | assets/support-tools.tsx | Sí; catálogo dentro de un modal |
| Compañías | [CompaniesPage](../modules/companies/src/web/companies/list/ui/companies-page.tsx) | Sí |
| Personas/contactos | [PeopleSection](../modules/companies/src/web/people/list/ui/people-section.tsx) | Sí |
| Departamentos | [DepartmentSection](../modules/companies/src/web/departments/list/ui/department-section.tsx) | Sí |
| Sedes | [SiteSection](../modules/companies/src/web/sites/list/ui/site-section.tsx) | Sí |
| Usuarios internos | [UserManagement](../modules/identity/src/web/users/list/ui/user-management.tsx) | Sí |
| Activos | [AssetInventoryPage](../modules/assets/src/web/assets/list/ui/inventory-page.tsx) | Sí; conserva panel lateral |
| Puestos de trabajo | [WorkstationsPage](../modules/assets/src/web/workstations/list/ui/workstations-page.tsx) | Sí |
| Recursos, incluidas aplicaciones y licencias | [ResourcesPage](../modules/resources/src/web/resources/list/ui/resources-page.tsx) | Sí |
| Campos y plantillas de recursos, global/compañía | [ResourceCatalogPage](../modules/resources/src/web/catalog/list/ui/catalog-page.tsx) | Sí; dos secciones |
| Campos, plantillas y relaciones de activos, global/compañía | [AssetCatalogPage](../modules/assets/src/web/catalog/list/ui/catalog-page.tsx) | Sí; tres secciones |
| Cuentas y accesos / catálogos informáticos | [AccessWorkspacePage](../modules/companies/src/web/access/list/ui/access-workspace.tsx) | Sí; cuenta, titularidad, grupo, pertenencia, autorización, necesidad, cobertura, entorno, sistema, tipo, permiso y plantilla |
| Perfiles de usuario | [WorkProfilesPage](../modules/companies/src/web/work-profiles/list/ui/work-profiles-page.tsx) | Sí |
| Correo por perfil | [MailConfigurationsPage](../modules/companies/src/web/mail-configurations/list/ui/mail-configurations-page.tsx) | Sí, también entrada desde perfil |
| Carpetas por perfil | [FolderMappingsPage](../modules/companies/src/web/folder-mappings/list/ui/folder-mappings-page.tsx) | Sí, también entrada desde perfil |
| Impresoras por perfil | [PrinterConfigurationsPage](../modules/companies/src/web/printer-configurations/list/ui/printer-configurations-page.tsx) | Sí, también entrada desde perfil |
| Aplicaciones por perfil | [ApplicationConfigurationsPage](../modules/companies/src/web/application-configurations/list/ui/application-configurations-page.tsx) | Sí, también entrada desde perfil |
| Certificados por perfil | [CertificateConfigurationsPage](../modules/companies/src/web/certificate-configurations/list/ui/certificate-configurations-page.tsx) | Sí, también entrada desde perfil |
| Teletrabajo | [TeleworkAuthorizationsPage](../modules/companies/src/web/telework-authorizations/list/ui/telework-authorizations-page.tsx) | Sí, conserva ámbito de persona |
| Accesos remotos | [RemoteConnectionsPage](../modules/companies/src/web/remote-connections/list/ui/remote-connections-page.tsx) | Sí, conserva ámbito de persona |
| Procesos de personas | [ProcessesPage](../modules/companies/src/web/processes/list/ui/processes-page.tsx) | Sí; su detalle es excepción |
| Plantillas de procesos | [ProcessTemplatesPage](../modules/companies/src/web/process-templates/list/ui/process-templates-page.tsx) | Sí |
| Plantillas de intervención, global/compañía | [InterventionTemplatesPage](../modules/companies/src/web/intervention-templates/list/ui/templates-page.tsx) | Sí |
| Tickets, global/compañía | [TicketsPage](../modules/companies/src/web/tickets/list/ui/tickets-page.tsx) | Sí |
| Intervenciones, global/compañía | [InterventionsPage](../modules/companies/src/web/interventions/list/ui/interventions-page.tsx) | Sí |
| Marcas globales | [BrandCatalogPage](../modules/assets/src/web/brands/list/ui/brands-page.tsx) | Sí; EntityListHeader y EntityTable, filtro de estado y modales |
| Herramientas de asistencia | [SupportTools](../modules/assets/src/web/support-tools/list/ui/support-tools.tsx) | Sí; catálogo dentro de un modal |
| Asignaciones departamentales | PeopleSection → PersonDetails | Sublistado de ficha; conserva sus operaciones y adopta tabla común |
Rutas relativas anteriores bajo modules/[módulo]/src/web. No hay listados separados de equipos, software, incidencias o ubicaciones adicionales: corresponden a Activos, Recursos, Tickets o Sedes.
Los enlaces apuntan a las ubicaciones actuales bajo `modules/<module>/src/web/<entity>/`.
No hay listados separados de equipos, software, incidencias o ubicaciones adicionales:
corresponden a Activos, Recursos, Tickets o Sedes.
## Excepciones justificadas

View File

@ -77,9 +77,9 @@ export function WorkspaceSidebar({
[
["home", "dashboard.title", HomeIcon],
["directory", "title", Building2Icon],
["catalogs", "shell.catalogs", DatabaseIcon],
["tickets", "shell.tickets", TicketIcon],
["interventions", "shell.interventions", WrenchIcon],
["catalogs", "shell.catalogs", DatabaseIcon],
] as const
).map(([area, label, Icon]) => (
<WorkspaceNavigationButton
@ -115,6 +115,7 @@ export function WorkspaceSidebar({
<KeyRoundIcon aria-hidden="true" className="size-[18px]" />
<span>{accessTranslation("catalogTitle")}</span>
</WorkspaceNavigationButton>
<WorkspaceNavigationButton
aria-label={assetTranslation("title")}
aria-pressed={catalog === "assets"}
@ -125,6 +126,7 @@ export function WorkspaceSidebar({
<BoxIcon aria-hidden="true" className="size-[18px]" />
<span>{assetTranslation("title")}</span>
</WorkspaceNavigationButton>
<WorkspaceNavigationButton
aria-label={t("interventionTemplates.title")}
aria-pressed={catalog === "intervention-templates"}
@ -135,6 +137,7 @@ export function WorkspaceSidebar({
<LayoutTemplateIcon aria-hidden="true" className="size-[18px]" />
<span>{t("interventionTemplates.title")}</span>
</WorkspaceNavigationButton>
<WorkspaceNavigationButton
aria-label={assetTranslation("brands.title")}
aria-pressed={catalog === "brands"}
@ -145,6 +148,7 @@ export function WorkspaceSidebar({
<LayersIcon aria-hidden="true" className="size-[18px]" />
<span>{assetTranslation("brands.title")}</span>
</WorkspaceNavigationButton>
<WorkspaceNavigationButton
aria-label={resourceTranslation("title")}
aria-pressed={catalog === "resources"}