Compare commits
2 Commits
e0947dcaaa
...
1f21a605cc
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f21a605cc | |||
| f37cec6d50 |
@ -12,6 +12,7 @@ body {
|
|||||||
:root {
|
:root {
|
||||||
--desk-app-background: #000;
|
--desk-app-background: #000;
|
||||||
--desk-content-background: #f7f9fb;
|
--desk-content-background: #f7f9fb;
|
||||||
|
--desk-header-background: #fff;
|
||||||
--desk-sidebar-background: #0e282c;
|
--desk-sidebar-background: #0e282c;
|
||||||
--desk-sidebar-top: #0c252a;
|
--desk-sidebar-top: #0c252a;
|
||||||
--desk-sidebar-bottom: #102c30;
|
--desk-sidebar-bottom: #102c30;
|
||||||
@ -27,6 +28,8 @@ body {
|
|||||||
--desk-radius-md: 10px;
|
--desk-radius-md: 10px;
|
||||||
--desk-radius-shell: 20px;
|
--desk-radius-shell: 20px;
|
||||||
--desk-sidebar-width: 228px;
|
--desk-sidebar-width: 228px;
|
||||||
|
--desk-sidebar-inset: 20px;
|
||||||
|
--desk-sidebar-collapsed-width: 72px;
|
||||||
--desk-header-height: 88px;
|
--desk-header-height: 88px;
|
||||||
--desk-search-height: 41px;
|
--desk-search-height: 41px;
|
||||||
--desk-nav-height: 48px;
|
--desk-nav-height: 48px;
|
||||||
@ -59,9 +62,12 @@ body:has(.desk-workspace) {
|
|||||||
background: var(--desk-app-background);
|
background: var(--desk-app-background);
|
||||||
}
|
}
|
||||||
.desk-sidebar {
|
.desk-sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
width: var(--desk-sidebar-width);
|
width: var(--desk-sidebar-width);
|
||||||
flex: 0 0 var(--desk-sidebar-width);
|
flex: 0 0 var(--desk-sidebar-width);
|
||||||
overflow-y: auto;
|
overflow: hidden;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
var(--desk-sidebar-top),
|
var(--desk-sidebar-top),
|
||||||
@ -71,6 +77,7 @@ body:has(.desk-workspace) {
|
|||||||
color: var(--desk-sidebar-text);
|
color: var(--desk-sidebar-text);
|
||||||
}
|
}
|
||||||
.desk-brand {
|
.desk-brand {
|
||||||
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 13px;
|
gap: 13px;
|
||||||
@ -88,10 +95,13 @@ body:has(.desk-workspace) {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.desk-company-rail {
|
.desk-company-rail {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
padding: 28px 13px 24px 26px;
|
padding: 28px var(--desk-sidebar-inset) 24px;
|
||||||
}
|
}
|
||||||
.desk-space-button {
|
.desk-space-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -137,7 +147,7 @@ body:has(.desk-workspace) {
|
|||||||
.desk-rail-separator {
|
.desk-rail-separator {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: rgb(255 255 255 / 12%);
|
background: rgb(255 255 255 / 12%);
|
||||||
margin: 26px -3px 25px -5px;
|
margin: 26px 0 25px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.desk-catalog-navigation {
|
.desk-catalog-navigation {
|
||||||
@ -156,6 +166,58 @@ body:has(.desk-workspace) {
|
|||||||
background: rgb(255 255 255 / 8%);
|
background: rgb(255 255 255 / 8%);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
.desk-sidebar-footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: grid;
|
||||||
|
gap: 2px;
|
||||||
|
padding: 12px var(--desk-sidebar-inset) 20px;
|
||||||
|
border-top: 1px solid rgb(255 255 255 / 12%);
|
||||||
|
}
|
||||||
|
.desk-sidebar-toggle[aria-expanded="true"] > svg {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
.desk-catalog-navigation > button {
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.desk-catalog-navigation > button > svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.desk-sidebar[data-collapsed="true"] {
|
||||||
|
width: var(--desk-sidebar-collapsed-width);
|
||||||
|
flex-basis: var(--desk-sidebar-collapsed-width);
|
||||||
|
}
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-brand {
|
||||||
|
justify-content: center;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-brand > span,
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-space-button > span,
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-nav-chevron,
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-catalog-navigation > button > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-company-rail {
|
||||||
|
padding: 28px 12px 12px;
|
||||||
|
}
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-sidebar-footer {
|
||||||
|
padding: 12px 12px 20px;
|
||||||
|
}
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-space-button {
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-catalog-navigation {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.desk-sidebar[data-collapsed="true"] .desk-catalog-navigation > button {
|
||||||
|
justify-content: center;
|
||||||
|
padding-inline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
.desk-workspace-content {
|
.desk-workspace-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@ -167,7 +229,9 @@ body:has(.desk-workspace) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 28px;
|
gap: 28px;
|
||||||
min-height: var(--desk-header-height);
|
min-height: var(--desk-header-height);
|
||||||
padding: 31px 30px 16px 17px;
|
padding: 15px 30px 15px 17px;
|
||||||
|
background: var(--desk-header-background);
|
||||||
|
border-bottom: 1px solid var(--desk-border);
|
||||||
color: var(--desk-navy);
|
color: var(--desk-navy);
|
||||||
}
|
}
|
||||||
.desk-search {
|
.desk-search {
|
||||||
@ -561,7 +625,7 @@ body:has(.desk-workspace) {
|
|||||||
flex-basis: auto;
|
flex-basis: auto;
|
||||||
}
|
}
|
||||||
.desk-brand {
|
.desk-brand {
|
||||||
padding: 20px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
.desk-company-rail {
|
.desk-company-rail {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -645,3 +709,16 @@ body:has(.desk-workspace) {
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.desk-company-rail {
|
||||||
|
flex: none;
|
||||||
|
overflow-y: visible;
|
||||||
|
}
|
||||||
|
.desk-sidebar-footer {
|
||||||
|
padding: 10px 16px;
|
||||||
|
}
|
||||||
|
.desk-sidebar-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -155,3 +155,13 @@ 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
|
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
|
ni DropdownMenu instalados; el avatar muestra iniciales del usuario real, sin
|
||||||
inventar foto ni cargo. Configuración abre un FormDialog con las acciones existentes.
|
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.
|
||||||
|
|
||||||
|
El menú expandido y su pie comparten márgenes horizontales de 20px mediante
|
||||||
|
`--desk-sidebar-inset`. El header global usa `--desk-header-background` (#FFFFFF),
|
||||||
|
con un borde inferior de 1px en `--desk-border`, sobre el contenido #F7F9FB.
|
||||||
|
|||||||
719
docs/prompts/prompt-refactorizacion-general.md
Normal file
719
docs/prompts/prompt-refactorizacion-general.md
Normal file
@ -0,0 +1,719 @@
|
|||||||
|
# Refactorización general del repositorio
|
||||||
|
|
||||||
|
Revisa y refactoriza de forma sistemática todo el código propio del repositorio para que cumpla las buenas prácticas, convenciones, arquitectura y reglas definidas en `AGENTS.md`.
|
||||||
|
|
||||||
|
`AGENTS.md` es la referencia normativa principal para esta tarea.
|
||||||
|
|
||||||
|
Antes de modificar código:
|
||||||
|
|
||||||
|
1. Lee `AGENTS.md` completo.
|
||||||
|
2. Lee la documentación de arquitectura y estándares a la que haga referencia.
|
||||||
|
3. Comprueba el entorno y estado del repositorio conforme a las instrucciones de `AGENTS.md`.
|
||||||
|
4. Realiza una inspección suficientemente amplia del repositorio antes de empezar a modificar archivos.
|
||||||
|
5. Identifica la estructura general, paquetes, módulos, dependencias internas, patrones existentes y áreas principales.
|
||||||
|
6. Establece un orden razonable de revisión y refactorización.
|
||||||
|
|
||||||
|
No empieces a corregir el primer problema que encuentres sin conocer antes suficientemente el contexto general del repositorio.
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
El objetivo no es reescribir el proyecto desde cero.
|
||||||
|
|
||||||
|
Debes:
|
||||||
|
|
||||||
|
- identificar incumplimientos reales de `AGENTS.md`;
|
||||||
|
- corregirlos cuando pueda hacerse de forma segura;
|
||||||
|
- mejorar claridad, mantenibilidad, tipado, separación de responsabilidades y coherencia;
|
||||||
|
- mantener la arquitectura existente;
|
||||||
|
- conservar el comportamiento funcional salvo que detectes claramente un bug;
|
||||||
|
- evitar cambios puramente cosméticos sin beneficio real;
|
||||||
|
- evitar introducir nuevas abstracciones innecesarias.
|
||||||
|
|
||||||
|
La tarea consiste en revisar TODO el código propio relevante del repositorio, no únicamente los archivos modificados recientemente.
|
||||||
|
|
||||||
|
No te limites a generar un informe de problemas: corrige los problemas que puedan resolverse de forma segura.
|
||||||
|
|
||||||
|
No te detengas después de revisar o corregir una única área. Continúa de forma progresiva hasta completar la revisión del código propio relevante del repositorio, salvo que exista un bloqueo real que requiera una decisión del usuario.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Código excluido
|
||||||
|
|
||||||
|
No revisar, refactorizar, reformatear, reorganizar ni modificar código perteneciente a terceros, código vendor o paquetes que hayan sido incorporados al repositorio como biblioteca externa.
|
||||||
|
|
||||||
|
Queda expresamente excluido:
|
||||||
|
|
||||||
|
- `packages/shadcn-ui`
|
||||||
|
|
||||||
|
Considera `packages/shadcn-ui` código de terceros/vendor aunque forme parte físicamente del monorepo.
|
||||||
|
|
||||||
|
No apliques sobre este paquete las reglas de estilo, estructura, TypeScript, React, nombres de fichero, separación de componentes ni ninguna otra refactorización definida en `AGENTS.md`.
|
||||||
|
|
||||||
|
No modifiques archivos dentro de `packages/shadcn-ui`, ni siquiera para:
|
||||||
|
|
||||||
|
- corregir lint;
|
||||||
|
- corregir formato;
|
||||||
|
- reducir warnings;
|
||||||
|
- cambiar tipos;
|
||||||
|
- separar componentes;
|
||||||
|
- reorganizar archivos;
|
||||||
|
- cambiar imports;
|
||||||
|
- eliminar código aparentemente no utilizado;
|
||||||
|
- homogeneizarlo con el resto del proyecto.
|
||||||
|
|
||||||
|
Puedes consultar únicamente su API pública, exports, componentes y contratos cuando sea necesario para utilizarlo correctamente desde código propio.
|
||||||
|
|
||||||
|
Si una validación global falla exclusivamente por código existente dentro de `packages/shadcn-ui`, indícalo claramente en el informe final y no modifiques ese paquete para hacer pasar la validación.
|
||||||
|
|
||||||
|
Si durante la revisión aparecen otros directorios claramente correspondientes a código generado, dependencias externas o vendor, no los refactorices salvo que `AGENTS.md` indique expresamente lo contrario.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Estrategia de trabajo
|
||||||
|
|
||||||
|
Trabaja progresivamente por paquetes, módulos o áreas funcionales coherentes.
|
||||||
|
|
||||||
|
Antes de modificar cada área:
|
||||||
|
|
||||||
|
1. Examina su estructura.
|
||||||
|
2. Identifica sus dependencias y consumidores.
|
||||||
|
3. Busca implementaciones equivalentes dentro del repositorio.
|
||||||
|
4. Consulta la documentación normativa aplicable.
|
||||||
|
5. Identifica los incumplimientos de `AGENTS.md`.
|
||||||
|
6. Distingue entre problemas reales y simples preferencias estilísticas.
|
||||||
|
7. Determina la solución más pequeña y segura que respete la arquitectura existente.
|
||||||
|
|
||||||
|
Después realiza las refactorizaciones necesarias.
|
||||||
|
|
||||||
|
No hagas una refactorización masiva sin control.
|
||||||
|
|
||||||
|
Después de cada conjunto coherente de cambios:
|
||||||
|
|
||||||
|
- revisa el diff;
|
||||||
|
- comprueba imports y referencias;
|
||||||
|
- comprueba que no has ampliado innecesariamente el alcance;
|
||||||
|
- ejecuta las validaciones dirigidas apropiadas;
|
||||||
|
- corrige cualquier regresión introducida antes de continuar.
|
||||||
|
|
||||||
|
Cuando completes un módulo o área importante, ejecuta las pruebas, typecheck y lint correspondientes cuando resulte razonable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
|
||||||
|
Revisa especialmente:
|
||||||
|
|
||||||
|
- usos de `any`;
|
||||||
|
- assertions innecesarias;
|
||||||
|
- non-null assertions;
|
||||||
|
- tipos excesivamente débiles;
|
||||||
|
- duplicación de tipos;
|
||||||
|
- tipos que puedan derivarse de schemas existentes;
|
||||||
|
- uso incorrecto de `Partial`;
|
||||||
|
- opcionalidad mal representada;
|
||||||
|
- discriminated unions incompletas;
|
||||||
|
- `switch` no exhaustivos cuando deberían serlo;
|
||||||
|
- casts usados como sustituto de validación;
|
||||||
|
- datos externos tratados como confiables sin validación;
|
||||||
|
- imports de tipos incorrectos;
|
||||||
|
- genéricos innecesarios;
|
||||||
|
- aliases sin semántica;
|
||||||
|
- estructuras mutables cuando no deberían serlo.
|
||||||
|
|
||||||
|
Prioriza:
|
||||||
|
|
||||||
|
- `unknown` frente a `any` para datos todavía no validados;
|
||||||
|
- narrowing frente a casts;
|
||||||
|
- `import type`;
|
||||||
|
- discriminated unions;
|
||||||
|
- exhaustividad;
|
||||||
|
- inferencia cuando sea clara;
|
||||||
|
- contratos explícitos cuando formen parte de APIs internas o públicas;
|
||||||
|
- schemas como fuente normativa cuando corresponda.
|
||||||
|
|
||||||
|
No debilites TypeScript para conseguir que el código compile.
|
||||||
|
|
||||||
|
Corrige el origen del problema de tipos siempre que sea posible.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Asincronía
|
||||||
|
|
||||||
|
Revisa:
|
||||||
|
|
||||||
|
- promesas flotantes;
|
||||||
|
- `void` usado para descartar promesas;
|
||||||
|
- `catch` que ocultan errores;
|
||||||
|
- callbacks convertidos incorrectamente a `async`;
|
||||||
|
- operaciones asíncronas no esperadas;
|
||||||
|
- errores asíncronos ignorados;
|
||||||
|
- flujos donde una operación debería devolverse, esperarse o manejar explícitamente su error.
|
||||||
|
|
||||||
|
Toda promesa debe:
|
||||||
|
|
||||||
|
- esperarse;
|
||||||
|
- devolverse;
|
||||||
|
- o tener su error gestionado explícitamente.
|
||||||
|
|
||||||
|
No ocultes errores para conseguir que el flujo continúe artificialmente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
|
||||||
|
Revisa especialmente:
|
||||||
|
|
||||||
|
- I/O síncrono dentro del flujo normal del servidor;
|
||||||
|
- accesos dispersos a `process.env`;
|
||||||
|
- configuración sin validar;
|
||||||
|
- recursos externos sin timeout;
|
||||||
|
- operaciones cancelables sin mecanismo de cancelación cuando corresponda;
|
||||||
|
- estado mutable global dependiente de peticiones;
|
||||||
|
- errores de infraestructura silenciados;
|
||||||
|
- cierre incorrecto de recursos;
|
||||||
|
- responsabilidades mezcladas.
|
||||||
|
|
||||||
|
La configuración debe cargarse, validarse y normalizarse en los puntos definidos por la arquitectura.
|
||||||
|
|
||||||
|
No disperses lecturas directas de `process.env` por la aplicación.
|
||||||
|
|
||||||
|
No bloquees el event loop innecesariamente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Express
|
||||||
|
|
||||||
|
Revisa especialmente:
|
||||||
|
|
||||||
|
- routers con demasiada lógica;
|
||||||
|
- controladores demasiado grandes;
|
||||||
|
- lógica de negocio dentro de controladores;
|
||||||
|
- acceso directo al ORM desde capas HTTP cuando contradiga la arquitectura;
|
||||||
|
- validación insuficiente de `params`, `query`, `headers` y `body`;
|
||||||
|
- manejo repetitivo o incorrecto de errores;
|
||||||
|
- exposición de información interna;
|
||||||
|
- middleware demasiado genérico;
|
||||||
|
- middleware con varias responsabilidades;
|
||||||
|
- ramas que puedan producir más de una respuesta HTTP;
|
||||||
|
- código HTTP mezclado con dominio o persistencia.
|
||||||
|
|
||||||
|
Los controladores deben actuar principalmente como adaptadores entre HTTP y la aplicación.
|
||||||
|
|
||||||
|
La lógica de negocio debe permanecer en las capas definidas por la arquitectura.
|
||||||
|
|
||||||
|
Centraliza el tratamiento de errores según los patrones existentes.
|
||||||
|
|
||||||
|
No expongas:
|
||||||
|
|
||||||
|
- stack traces;
|
||||||
|
- SQL;
|
||||||
|
- secretos;
|
||||||
|
- credenciales;
|
||||||
|
- detalles internos innecesarios.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# React 19
|
||||||
|
|
||||||
|
Revisa todo el código React propio.
|
||||||
|
|
||||||
|
Comprueba especialmente:
|
||||||
|
|
||||||
|
- componentes demasiado grandes;
|
||||||
|
- varios componentes relevantes agrupados en un único fichero;
|
||||||
|
- responsabilidades mezcladas;
|
||||||
|
- efectos innecesarios;
|
||||||
|
- estado redundante;
|
||||||
|
- valores derivados almacenados en estado;
|
||||||
|
- props copiadas a estado sin necesidad;
|
||||||
|
- acceso a datos mezclado con presentación;
|
||||||
|
- custom hooks innecesarios;
|
||||||
|
- hooks con responsabilidades ambiguas;
|
||||||
|
- `useMemo` o `useCallback` utilizados preventivamente;
|
||||||
|
- incumplimientos de las Rules of Hooks;
|
||||||
|
- efectos secundarios durante render;
|
||||||
|
- mutación de props o estado;
|
||||||
|
- duplicación de estado remoto gestionado por TanStack Query;
|
||||||
|
- carga remota implementada mediante `useEffect`;
|
||||||
|
- patrones visuales repetidos que deberían reutilizar componentes existentes.
|
||||||
|
|
||||||
|
## Organización de componentes React
|
||||||
|
|
||||||
|
Como norma general:
|
||||||
|
|
||||||
|
- un fichero `.tsx` debe contener un único componente React principal;
|
||||||
|
- no agrupes varios componentes React relevantes en un mismo fichero por comodidad.
|
||||||
|
|
||||||
|
Extrae un componente a su propio fichero cuando tenga alguna de estas características:
|
||||||
|
|
||||||
|
- props propias;
|
||||||
|
- estado;
|
||||||
|
- hooks;
|
||||||
|
- lógica de interacción;
|
||||||
|
- lógica de renderizado significativa;
|
||||||
|
- responsabilidad semántica identificable;
|
||||||
|
- posibilidad razonable de reutilización;
|
||||||
|
- tamaño suficiente para dificultar la lectura del componente principal.
|
||||||
|
|
||||||
|
Un subcomponente puede permanecer en el mismo fichero únicamente cuando sea:
|
||||||
|
|
||||||
|
- pequeño;
|
||||||
|
- puramente presentacional;
|
||||||
|
- estrechamente ligado al componente principal;
|
||||||
|
- sin lógica relevante;
|
||||||
|
- sin estado;
|
||||||
|
- sin hooks significativos;
|
||||||
|
- y su extracción no mejore realmente la comprensión.
|
||||||
|
|
||||||
|
Si un fichero contiene varios subcomponentes privados con responsabilidad propia, reconsidera la estructura y extráelos.
|
||||||
|
|
||||||
|
No fragmentes JSX trivial en componentes artificiales simplemente para reducir el número de líneas.
|
||||||
|
|
||||||
|
La separación debe basarse en responsabilidades, semántica y mantenibilidad.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Diálogos, modales y ventanas emergentes
|
||||||
|
|
||||||
|
Durante la revisión identifica todas las ventanas modales, cuadros de diálogo, formularios modales, paneles emergentes y componentes equivalentes.
|
||||||
|
|
||||||
|
No te limites a buscar componentes llamados `Dialog` o `FormDialog`.
|
||||||
|
|
||||||
|
Revisa especialmente implementaciones construidas mediante:
|
||||||
|
|
||||||
|
- `Dialog`;
|
||||||
|
- `AlertDialog`;
|
||||||
|
- `Sheet`;
|
||||||
|
- `Drawer`;
|
||||||
|
- `FormDialog`;
|
||||||
|
- wrappers propios;
|
||||||
|
- cualquier otra primitiva que represente una interacción modal o superpuesta.
|
||||||
|
|
||||||
|
Toda ventana modal o cuadro de diálogo con contenido y responsabilidad propios debe extraerse a un componente React independiente y vivir en su propio fichero `.tsx`.
|
||||||
|
|
||||||
|
Considera especialmente como componentes independientes los diálogos de:
|
||||||
|
|
||||||
|
- alta;
|
||||||
|
- edición;
|
||||||
|
- eliminación;
|
||||||
|
- confirmación;
|
||||||
|
- selección;
|
||||||
|
- cambio de estado;
|
||||||
|
- detalle;
|
||||||
|
- ayuda;
|
||||||
|
- configuración;
|
||||||
|
- asignación;
|
||||||
|
- acciones que requieran interacción adicional.
|
||||||
|
|
||||||
|
Como norma general, una página o listado no debe contener internamente grandes bloques de JSX correspondientes a diálogos funcionales.
|
||||||
|
|
||||||
|
El componente padre debe coordinar:
|
||||||
|
|
||||||
|
- apertura;
|
||||||
|
- cierre;
|
||||||
|
- datos necesarios;
|
||||||
|
- callbacks;
|
||||||
|
- acciones posteriores.
|
||||||
|
|
||||||
|
El componente del diálogo debe encapsular:
|
||||||
|
|
||||||
|
- su estructura visual;
|
||||||
|
- contenido;
|
||||||
|
- formulario cuando corresponda;
|
||||||
|
- lógica de interacción propia;
|
||||||
|
- estados locales propios.
|
||||||
|
|
||||||
|
No mantengas formularios completos ni bloques grandes de `DialogContent` dentro de páginas o listados cuando representen una unidad funcional reconocible.
|
||||||
|
|
||||||
|
Un formulario utilizado dentro de un diálogo puede separarse además en otro componente cuando tenga suficiente complejidad, reutilización o responsabilidad propia.
|
||||||
|
|
||||||
|
No extraigas mecánicamente un diálogo trivial compuesto únicamente por unas pocas líneas de contenido estático sin lógica, estado, formulario ni responsabilidad propia.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Estado y efectos React
|
||||||
|
|
||||||
|
Mantén el estado mínimo necesario.
|
||||||
|
|
||||||
|
No almacenes en estado valores que puedan calcularse correctamente durante render a partir de:
|
||||||
|
|
||||||
|
- props;
|
||||||
|
- otro estado;
|
||||||
|
- resultados de consultas;
|
||||||
|
- constantes;
|
||||||
|
- datos derivados.
|
||||||
|
|
||||||
|
No utilices `useEffect` para sincronizar estado React con otro estado React cuando el valor pueda derivarse.
|
||||||
|
|
||||||
|
No utilices `useEffect` para reaccionar a una acción directa del usuario cuando esa lógica deba ejecutarse desde el event handler correspondiente.
|
||||||
|
|
||||||
|
Utiliza efectos para sincronización real con:
|
||||||
|
|
||||||
|
- sistemas externos;
|
||||||
|
- APIs imperativas;
|
||||||
|
- suscripciones;
|
||||||
|
- listeners;
|
||||||
|
- timers;
|
||||||
|
- recursos externos.
|
||||||
|
|
||||||
|
Todo efecto que cree recursos debe realizar el cleanup correspondiente.
|
||||||
|
|
||||||
|
No introduzcas `useMemo` ni `useCallback` preventivamente.
|
||||||
|
|
||||||
|
Úsalos solo cuando exista una necesidad concreta:
|
||||||
|
|
||||||
|
- rendimiento demostrado;
|
||||||
|
- profiling;
|
||||||
|
- estabilidad referencial exigida por un contrato;
|
||||||
|
- comportamiento de una dependencia que lo requiera.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# TanStack Query y acceso a datos
|
||||||
|
|
||||||
|
Revisa:
|
||||||
|
|
||||||
|
- query keys dispersas;
|
||||||
|
- duplicación de estado remoto;
|
||||||
|
- invalidaciones excesivamente amplias;
|
||||||
|
- `fetch` directo desde componentes cuando exista una capa apropiada;
|
||||||
|
- carga remota mediante `useEffect`;
|
||||||
|
- funciones de API dependientes de React;
|
||||||
|
- mutaciones que no actualicen correctamente la caché;
|
||||||
|
- errores de carga no representados;
|
||||||
|
- ausencia de estados de carga o vacío.
|
||||||
|
|
||||||
|
Mantén las query keys centralizadas conforme a los patrones del proyecto.
|
||||||
|
|
||||||
|
Después de una mutación, invalida o actualiza únicamente aquello que realmente se vea afectado.
|
||||||
|
|
||||||
|
No invalide indiscriminadamente toda la caché para evitar razonar sobre dependencias.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Formularios
|
||||||
|
|
||||||
|
Revisa:
|
||||||
|
|
||||||
|
- duplicación de validación;
|
||||||
|
- estado duplicado;
|
||||||
|
- formularios demasiado acoplados a páginas;
|
||||||
|
- formularios modales incluidos directamente dentro de grandes componentes;
|
||||||
|
- errores de campos mal representados;
|
||||||
|
- errores globales mezclados con errores de validación;
|
||||||
|
- envíos duplicados;
|
||||||
|
- cierres del diálogo antes de confirmar éxito.
|
||||||
|
|
||||||
|
Utiliza React Hook Form y Zod conforme a los patrones definidos en el proyecto.
|
||||||
|
|
||||||
|
Mantén una única fuente normativa de validación cuando sea posible.
|
||||||
|
|
||||||
|
No cierres ni limpies un formulario hasta confirmar correctamente la operación que lo requiera.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Componentes visuales y reutilización
|
||||||
|
|
||||||
|
Antes de crear un componente nuevo:
|
||||||
|
|
||||||
|
1. Busca si ya existe uno equivalente.
|
||||||
|
2. Comprueba `@repo/shadcn-ui`.
|
||||||
|
3. Revisa componentes compartidos del proyecto.
|
||||||
|
4. Busca patrones equivalentes en otras pantallas.
|
||||||
|
|
||||||
|
No dupliques estructura, comportamiento ni patrones visuales existentes.
|
||||||
|
|
||||||
|
Extrae componentes comunes únicamente cuando exista repetición real o una responsabilidad compartida clara.
|
||||||
|
|
||||||
|
Mantén los componentes genéricos independientes de:
|
||||||
|
|
||||||
|
- DTO específicos;
|
||||||
|
- endpoints;
|
||||||
|
- Sequelize;
|
||||||
|
- lógica de dominio concreta;
|
||||||
|
- detalles internos de Metis Desk,
|
||||||
|
|
||||||
|
si esa dependencia no es necesaria para cumplir su función.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Estructura de archivos
|
||||||
|
|
||||||
|
Revisa:
|
||||||
|
|
||||||
|
- archivos excesivamente grandes;
|
||||||
|
- archivos con varias responsabilidades;
|
||||||
|
- `utils.ts`;
|
||||||
|
- `helpers.ts`;
|
||||||
|
- `types.ts`;
|
||||||
|
- `common.ts`;
|
||||||
|
- `constants.ts`;
|
||||||
|
|
||||||
|
cuando se hayan convertido en cajones de sastre.
|
||||||
|
|
||||||
|
Mantén tipos, constantes y utilidades cerca del dominio o funcionalidad a la que pertenecen.
|
||||||
|
|
||||||
|
Muévelos a ubicaciones compartidas únicamente cuando existan consumidores reales en diferentes áreas.
|
||||||
|
|
||||||
|
No crees barrels `index.ts` indiscriminadamente.
|
||||||
|
|
||||||
|
Evita barrels que:
|
||||||
|
|
||||||
|
- oculten dependencias;
|
||||||
|
- provoquen ciclos;
|
||||||
|
- hagan difícil localizar el origen de un símbolo.
|
||||||
|
|
||||||
|
No reorganices directorios únicamente por preferencias personales.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Abstracciones
|
||||||
|
|
||||||
|
Revisa y evita abstracciones innecesarias como:
|
||||||
|
|
||||||
|
- `BaseService`;
|
||||||
|
- `BaseRepository`;
|
||||||
|
- repositorios genéricos;
|
||||||
|
- factories genéricas;
|
||||||
|
- wrappers sin valor semántico;
|
||||||
|
- clases abstractas sin necesidad;
|
||||||
|
- eventos internos sin consumidores reales;
|
||||||
|
- contenedores DI innecesarios;
|
||||||
|
- genéricos anticipatorios;
|
||||||
|
- configuraciones altamente parametrizables sin consumidores reales.
|
||||||
|
|
||||||
|
No introduzcas extensibilidad hipotética.
|
||||||
|
|
||||||
|
Aplica YAGNI.
|
||||||
|
|
||||||
|
Las abstracciones deben aparecer porque exista una necesidad real, no porque puedan resultar útiles en el futuro.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Duplicación
|
||||||
|
|
||||||
|
Busca duplicación real de:
|
||||||
|
|
||||||
|
- lógica;
|
||||||
|
- validaciones;
|
||||||
|
- transformaciones;
|
||||||
|
- componentes;
|
||||||
|
- estructuras de formularios;
|
||||||
|
- patrones de interacción;
|
||||||
|
- consultas;
|
||||||
|
- tratamiento de errores.
|
||||||
|
|
||||||
|
No elimines duplicación de manera mecánica.
|
||||||
|
|
||||||
|
Dos bloques similares no requieren necesariamente una abstracción común si representan conceptos diferentes o evolucionarán de manera independiente.
|
||||||
|
|
||||||
|
Extrae únicamente cuando exista una responsabilidad compartida real.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Código muerto
|
||||||
|
|
||||||
|
Identifica:
|
||||||
|
|
||||||
|
- funciones sin uso;
|
||||||
|
- componentes sin consumidores;
|
||||||
|
- exports innecesarios;
|
||||||
|
- tipos obsoletos;
|
||||||
|
- código comentado;
|
||||||
|
- compatibilidad interna que ya no tenga consumidores;
|
||||||
|
- implementaciones duplicadas antiguas.
|
||||||
|
|
||||||
|
Antes de eliminar código:
|
||||||
|
|
||||||
|
1. busca todas sus referencias en el monorepo;
|
||||||
|
2. identifica posibles usos dinámicos;
|
||||||
|
3. comprueba tests y documentación;
|
||||||
|
4. confirma que no forme parte de un contrato público.
|
||||||
|
|
||||||
|
No elimines legado basándote únicamente en una búsqueda superficial.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dependencias
|
||||||
|
|
||||||
|
No añadas una dependencia nueva salvo que exista una necesidad clara.
|
||||||
|
|
||||||
|
Antes de hacerlo:
|
||||||
|
|
||||||
|
1. comprueba APIs nativas;
|
||||||
|
2. comprueba dependencias ya instaladas;
|
||||||
|
3. comprueba utilidades existentes;
|
||||||
|
4. justifica la incorporación.
|
||||||
|
|
||||||
|
No reemplaces una dependencia establecida por otra equivalente únicamente por preferencia.
|
||||||
|
|
||||||
|
No actualices versiones de dependencias salvo que sea necesario para esta tarea o exista instrucción expresa.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Seguridad
|
||||||
|
|
||||||
|
Durante la refactorización conserva y mejora cuando corresponda:
|
||||||
|
|
||||||
|
- validación de entrada;
|
||||||
|
- autenticación;
|
||||||
|
- autorización;
|
||||||
|
- aislamiento de clientes;
|
||||||
|
- consultas parametrizadas;
|
||||||
|
- transacciones;
|
||||||
|
- restricciones de base de datos;
|
||||||
|
- protección de secretos;
|
||||||
|
- privacidad de logs;
|
||||||
|
- idempotencia.
|
||||||
|
|
||||||
|
No confíes en identificadores enviados por el navegador para determinar permisos o pertenencia.
|
||||||
|
|
||||||
|
No introduzcas regresiones de seguridad en nombre de simplificar código.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
|
||||||
|
Revisa los tests relacionados con el código refactorizado.
|
||||||
|
|
||||||
|
Prioriza tests de comportamiento observable.
|
||||||
|
|
||||||
|
No escribas tests únicamente para aumentar cobertura.
|
||||||
|
|
||||||
|
Evita tests excesivamente acoplados a detalles internos.
|
||||||
|
|
||||||
|
Cuando corrijas un bug reproducible, considera añadir una prueba que:
|
||||||
|
|
||||||
|
1. falle con el comportamiento anterior;
|
||||||
|
2. pase con la corrección.
|
||||||
|
|
||||||
|
Los tests deben ser deterministas.
|
||||||
|
|
||||||
|
No introduzcas:
|
||||||
|
|
||||||
|
- esperas arbitrarias;
|
||||||
|
- sleeps;
|
||||||
|
- retries para ocultar inestabilidad;
|
||||||
|
- desactivaciones de tests para conseguir que la suite pase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Cambios fuera de alcance
|
||||||
|
|
||||||
|
No amplíes una refactorización para corregir indiscriminadamente cualquier problema que encuentres.
|
||||||
|
|
||||||
|
Si detectas un problema no relacionado con el área que estás trabajando:
|
||||||
|
|
||||||
|
- corrígelo únicamente si es trivial, seguro y necesario para completar correctamente el cambio actual;
|
||||||
|
- en caso contrario, déjalo documentado como pendiente.
|
||||||
|
|
||||||
|
No hagas reformateos globales ni renombres masivos sin necesidad.
|
||||||
|
|
||||||
|
No cambies APIs públicas, contratos HTTP, schemas, persistencia o comportamiento funcional salvo que sea necesario para resolver un problema real.
|
||||||
|
|
||||||
|
Si un cambio afecta contratos compartidos:
|
||||||
|
|
||||||
|
1. identifica todos los consumidores;
|
||||||
|
2. actualízalos conjuntamente;
|
||||||
|
3. valida el comportamiento completo.
|
||||||
|
|
||||||
|
No mantengas compatibilidad artificial con APIs internas antiguas cuando todos los consumidores puedan actualizarse de forma segura dentro del mismo cambio.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Criterios para detener una refactorización concreta
|
||||||
|
|
||||||
|
No improvises cuando la corrección requiera:
|
||||||
|
|
||||||
|
- una decisión funcional;
|
||||||
|
- una decisión arquitectónica significativa;
|
||||||
|
- cambios de negocio no documentados;
|
||||||
|
- una migración de datos con implicaciones desconocidas;
|
||||||
|
- modificar contratos públicos sin poder determinar los consumidores;
|
||||||
|
- asumir comportamiento no verificable.
|
||||||
|
|
||||||
|
En estos casos deja el problema claramente identificado como pendiente indicando:
|
||||||
|
|
||||||
|
- fichero o área;
|
||||||
|
- problema encontrado;
|
||||||
|
- impacto;
|
||||||
|
- motivo por el que no debe corregirse automáticamente;
|
||||||
|
- solución recomendada.
|
||||||
|
|
||||||
|
Esto no implica detener la revisión completa del repositorio si puedes continuar con otras áreas independientes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Validación
|
||||||
|
|
||||||
|
Después de cada conjunto coherente de cambios ejecuta las comprobaciones dirigidas apropiadas.
|
||||||
|
|
||||||
|
Al finalizar la refactorización completa ejecuta, como mínimo, las validaciones globales definidas en `AGENTS.md`.
|
||||||
|
|
||||||
|
Salvo que `AGENTS.md` indique otra cosa, incluye:
|
||||||
|
|
||||||
|
- `pnpm typecheck`
|
||||||
|
- `pnpm lint`
|
||||||
|
- `pnpm test`
|
||||||
|
- `pnpm check`
|
||||||
|
- `pnpm build`
|
||||||
|
|
||||||
|
No afirmes que una validación ha pasado si no la has ejecutado.
|
||||||
|
|
||||||
|
Si una comprobación falla:
|
||||||
|
|
||||||
|
1. determina si el fallo ha sido introducido por la refactorización;
|
||||||
|
2. corrígelo si es responsabilidad del código propio;
|
||||||
|
3. vuelve a ejecutar la comprobación;
|
||||||
|
4. si corresponde a código excluido o a un problema previo no relacionado, documenta claramente la causa.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Revisión final
|
||||||
|
|
||||||
|
Antes de dar la tarea por terminada:
|
||||||
|
|
||||||
|
1. Revisa el diff completo.
|
||||||
|
2. Comprueba que no se hayan modificado áreas excluidas.
|
||||||
|
3. Busca imports no utilizados.
|
||||||
|
4. Busca casts añadidos innecesariamente.
|
||||||
|
5. Busca supresiones nuevas de TypeScript, lint o Biome.
|
||||||
|
6. Comprueba que no haya promesas flotantes.
|
||||||
|
7. Comprueba que no hayas añadido `useEffect`, `useMemo` o `useCallback` innecesarios.
|
||||||
|
8. Revisa los archivos `.tsx` modificados para detectar varios componentes relevantes en el mismo fichero.
|
||||||
|
9. Comprueba que los diálogos funcionales hayan sido extraídos cuando corresponda.
|
||||||
|
10. Comprueba que no hayas creado abstracciones sin consumidores reales.
|
||||||
|
11. Comprueba que no hayas duplicado tipos, schemas o lógica.
|
||||||
|
12. Comprueba que no quede código temporal, comentado o de depuración.
|
||||||
|
13. Ejecuta las validaciones finales.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Informe final
|
||||||
|
|
||||||
|
Al finalizar proporciona un resumen estructurado y concreto indicando:
|
||||||
|
|
||||||
|
- áreas y módulos revisados;
|
||||||
|
- áreas expresamente excluidas;
|
||||||
|
- principales incumplimientos encontrados;
|
||||||
|
- principales refactorizaciones realizadas;
|
||||||
|
- componentes React extraídos;
|
||||||
|
- diálogos o formularios modales extraídos;
|
||||||
|
- mejoras relevantes de TypeScript;
|
||||||
|
- mejoras de Node.js/Express;
|
||||||
|
- duplicaciones eliminadas;
|
||||||
|
- código muerto eliminado;
|
||||||
|
- archivos o módulos especialmente modificados;
|
||||||
|
- tests añadidos o modificados;
|
||||||
|
- comandos ejecutados;
|
||||||
|
- resultado de cada validación;
|
||||||
|
- problemas detectados que hayan quedado pendientes;
|
||||||
|
- decisiones que requieran revisión humana.
|
||||||
|
|
||||||
|
No afirmes:
|
||||||
|
|
||||||
|
- haber revisado un área que no hayas inspeccionado;
|
||||||
|
- haber corregido un problema que siga presente;
|
||||||
|
- haber ejecutado una comprobación que no hayas ejecutado.
|
||||||
|
|
||||||
|
El resultado debe dejar el código propio del repositorio progresivamente alineado con `AGENTS.md`, manteniendo el comportamiento existente y evitando refactorizaciones especulativas.
|
||||||
@ -1,14 +1,19 @@
|
|||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import {
|
import {
|
||||||
|
BoxIcon,
|
||||||
Building2Icon,
|
Building2Icon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
DatabaseIcon,
|
DatabaseIcon,
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
|
KeyRoundIcon,
|
||||||
|
LayersIcon,
|
||||||
|
LayoutTemplateIcon,
|
||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
TicketIcon,
|
TicketIcon,
|
||||||
WrenchIcon,
|
WrenchIcon,
|
||||||
} from "@repo/shadcn-ui/lib/icons";
|
} from "@repo/shadcn-ui/lib/icons";
|
||||||
import type { ComponentProps } from "react";
|
import type { ComponentProps } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { WorkspaceContent } from "./workspace-content";
|
import type { WorkspaceContent } from "./workspace-content";
|
||||||
@ -35,9 +40,10 @@ export function WorkspaceSidebar({
|
|||||||
const { t: resourceTranslation } = useTranslation("resources");
|
const { t: resourceTranslation } = useTranslation("resources");
|
||||||
const { t: assetTranslation } = useTranslation("assets");
|
const { t: assetTranslation } = useTranslation("assets");
|
||||||
const { t: accessTranslation } = useTranslation("access");
|
const { t: accessTranslation } = useTranslation("access");
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
return (
|
return (
|
||||||
<aside className="desk-sidebar">
|
<aside className="desk-sidebar" data-collapsed={collapsed}>
|
||||||
<div className="desk-brand">
|
<div aria-label="Metis Desk" className="desk-brand" role="img">
|
||||||
<svg aria-hidden="true" viewBox="0 0 36 30">
|
<svg aria-hidden="true" viewBox="0 0 36 30">
|
||||||
<path
|
<path
|
||||||
d="M5 24 13 7l9 17 9-18"
|
d="M5 24 13 7l9 17 9-18"
|
||||||
@ -66,6 +72,7 @@ export function WorkspaceSidebar({
|
|||||||
className="desk-space-button"
|
className="desk-space-button"
|
||||||
key={area}
|
key={area}
|
||||||
onClick={() => onNavigate({ area })}
|
onClick={() => onNavigate({ area })}
|
||||||
|
title={t(label)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
<Icon aria-hidden="true" />
|
<Icon aria-hidden="true" />
|
||||||
@ -79,47 +86,58 @@ export function WorkspaceSidebar({
|
|||||||
<nav aria-label={t("globalCatalogs")} className="desk-catalog-navigation">
|
<nav aria-label={t("globalCatalogs")} className="desk-catalog-navigation">
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
aria-label={accessTranslation("catalogTitle")}
|
||||||
aria-pressed={catalog === "access"}
|
aria-pressed={catalog === "access"}
|
||||||
onClick={() => onSelectCatalog("access")}
|
onClick={() => onSelectCatalog("access")}
|
||||||
|
title={accessTranslation("catalogTitle")}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
{accessTranslation("catalogTitle")}
|
<KeyRoundIcon aria-hidden="true" />
|
||||||
|
<span>{accessTranslation("catalogTitle")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
aria-label={assetTranslation("title")}
|
||||||
aria-pressed={catalog === "assets"}
|
aria-pressed={catalog === "assets"}
|
||||||
onClick={() => onSelectCatalog("assets")}
|
onClick={() => onSelectCatalog("assets")}
|
||||||
|
title={assetTranslation("title")}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
{assetTranslation("title")}
|
<BoxIcon aria-hidden="true" />
|
||||||
|
<span>{assetTranslation("title")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
aria-label={t("interventionTemplates.title")}
|
||||||
aria-pressed={catalog === "intervention-templates"}
|
aria-pressed={catalog === "intervention-templates"}
|
||||||
onClick={() => onSelectCatalog("intervention-templates")}
|
onClick={() => onSelectCatalog("intervention-templates")}
|
||||||
|
title={t("interventionTemplates.title")}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
{t("interventionTemplates.title")}
|
<LayoutTemplateIcon aria-hidden="true" />
|
||||||
|
<span>{t("interventionTemplates.title")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
aria-label={assetTranslation("brands.title")}
|
||||||
aria-pressed={catalog === "brands"}
|
aria-pressed={catalog === "brands"}
|
||||||
onClick={() => onSelectCatalog("brands")}
|
onClick={() => onSelectCatalog("brands")}
|
||||||
|
title={assetTranslation("brands.title")}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
{assetTranslation("brands.title")}
|
<LayersIcon aria-hidden="true" />
|
||||||
|
<span>{assetTranslation("brands.title")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
aria-label={resourceTranslation("title")}
|
||||||
aria-pressed={catalog === "resources"}
|
aria-pressed={catalog === "resources"}
|
||||||
onClick={() => onSelectCatalog("resources")}
|
onClick={() => onSelectCatalog("resources")}
|
||||||
|
title={resourceTranslation("title")}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
{resourceTranslation("title")}
|
<DatabaseIcon aria-hidden="true" />
|
||||||
|
<span>{resourceTranslation("title")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
</nav>
|
</nav>
|
||||||
)}
|
)}
|
||||||
<Button className="desk-space-button" onClick={onSettings} variant="ghost">
|
|
||||||
<SettingsIcon aria-hidden="true" />
|
|
||||||
<span>{coreTranslation("shell.settings")}</span>
|
|
||||||
</Button>
|
|
||||||
<div className="desk-rail-separator" />
|
<div className="desk-rail-separator" />
|
||||||
{companies.map((company) => (
|
{companies.map((company) => (
|
||||||
<Button
|
<Button
|
||||||
@ -136,6 +154,29 @@ export function WorkspaceSidebar({
|
|||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
<footer className="desk-sidebar-footer">
|
||||||
|
<Button
|
||||||
|
aria-label={coreTranslation("shell.settings")}
|
||||||
|
className="desk-space-button"
|
||||||
|
onClick={onSettings}
|
||||||
|
title={coreTranslation("shell.settings")}
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
<SettingsIcon aria-hidden="true" />
|
||||||
|
<span>{coreTranslation("shell.settings")}</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
aria-expanded={!collapsed}
|
||||||
|
aria-label={coreTranslation(collapsed ? "shell.expandSidebar" : "shell.collapseSidebar")}
|
||||||
|
className="desk-space-button desk-sidebar-toggle"
|
||||||
|
onClick={() => setCollapsed((value) => !value)}
|
||||||
|
title={coreTranslation(collapsed ? "shell.expandSidebar" : "shell.collapseSidebar")}
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
<ChevronRightIcon aria-hidden="true" />
|
||||||
|
<span>{coreTranslation("shell.collapseSidebar")}</span>
|
||||||
|
</Button>
|
||||||
|
</footer>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,8 @@
|
|||||||
"searchScope": "Search available companies and sections.",
|
"searchScope": "Search available companies and sections.",
|
||||||
"notificationsUnavailable": "Notifications: service not yet available",
|
"notificationsUnavailable": "Notifications: service not yet available",
|
||||||
"profile": "User menu",
|
"profile": "User menu",
|
||||||
"supportWorkspace": "Support workspace"
|
"supportWorkspace": "Support workspace",
|
||||||
|
"collapseSidebar": "Collapse menu",
|
||||||
|
"expandSidebar": "Expand menu"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,8 @@
|
|||||||
"searchScope": "Buscar en compañías y secciones disponibles.",
|
"searchScope": "Buscar en compañías y secciones disponibles.",
|
||||||
"notificationsUnavailable": "Notificaciones: servicio todavía no disponible",
|
"notificationsUnavailable": "Notificaciones: servicio todavía no disponible",
|
||||||
"profile": "Menú de usuario",
|
"profile": "Menú de usuario",
|
||||||
"supportWorkspace": "Espacio de soporte"
|
"supportWorkspace": "Espacio de soporte",
|
||||||
|
"collapseSidebar": "Contraer menú",
|
||||||
|
"expandSidebar": "Expandir menú"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user