- Implemented CompanyLogoUseCases for retrieving, saving, and removing company logos. - Created CompanyLogoError class for error handling related to logo operations. - Developed CompanyLogoProcessor to optimize and validate logo images. - Added HTTP routes for logo management, including GET and POST endpoints. - Created Sequelize-based repository for logo persistence. - Implemented migration for adding logoVersion to companies and creating company_logos table. - Added frontend components for displaying and managing company logos, including avatar and logo upload fields. - Wrote tests for logo processing and avatar components to ensure functionality and correctness.
213 lines
17 KiB
Markdown
213 lines
17 KiB
Markdown
# Componentes de interfaz
|
||
|
||
Metis Desk usa shadcn/ui, con Base UI y el estilo base-vega, siguiendo la organización de factuges-erp.
|
||
Los componentes viven en packages/shadcn-ui y se consumen mediante el paquete @repo/shadcn-ui.
|
||
El código se genera desde el registro oficial y queda versionado en este repositorio, sin depender del ERP.
|
||
|
||
## Componentes actuales
|
||
|
||
- Button: volver a comprobar la conexión.
|
||
- Card: bloques de información y estado.
|
||
- Select: idioma y selección de sistemas, entornos, cuentas, personas y demás relaciones, con navegación por teclado.
|
||
- Input y Label: credenciales y datos de usuario.
|
||
- Checkbox: estado activo de usuarios, compañías, personas y departamentos.
|
||
- Table: listados de usuarios, compañías, personas y departamentos.
|
||
- Textarea: dirección opcional de las sedes, con varias líneas.
|
||
- Dialog: formularios de alta y edición, con foco y cierre por teclado. FormDialog comparte el título y la cancelación traducidos.
|
||
|
||
## Convención de navegación
|
||
|
||
Los listados presentan datos, búsqueda y acciones. El botón de alta se coloca en la cabecera, a la derecha. Los formularios se abren en modal como norma general, con anchura adaptable y desplazamiento si el contenido lo requiere. No mostrar formularios junto al listado. Las ediciones se abren mediante una acción explícita. Compañías, personas, departamentos, asignaciones, sedes y usuarios siguen este criterio. Las personas, los departamentos y las asignaciones se crean y editan en modales. El historial se consulta en un diálogo. Catálogos, cuentas, grupos, recursos, permisos y necesidades siguen la misma convención. Los permisos de una necesidad se eligen mediante casillas shadcn/ui.
|
||
|
||
TanStack Table filtra el listado de compañías por nombre; React Hook Form gestiona los formularios con los contratos Zod compartidos.
|
||
|
||
El paquete exporta componentes individuales. Importar solo el componente necesario:
|
||
|
||
```tsx
|
||
import { Button } from "@repo/shadcn-ui/components/button";
|
||
```
|
||
|
||
El tema se define una sola vez en packages/shadcn-ui/src/styles/globals.css.
|
||
La aplicación importa ese CSS y declara las fuentes de Tailwind de los módulos y del paquete compartido.
|
||
Usar tokens semánticos como bg-background, text-foreground y border-border en vez de repetir colores por pantalla.
|
||
|
||
## Añadir componentes
|
||
|
||
Desde la raíz del proyecto, dentro de WSL:
|
||
|
||
```bash
|
||
pnpm --filter @repo/shadcn-ui ui:add input
|
||
```
|
||
|
||
El CLI está fijado en package.json. Revisar los archivos y dependencias añadidos antes de validar.
|
||
No usar --overwrite sin revisar las adaptaciones locales.
|
||
apps/web/components.json también permite al CLI localizar el paquete compartido desde la aplicación.
|
||
|
||
Después de generar código, separar los imports de tipos con import type y ejecutar las comprobaciones del proyecto.
|
||
No añadir todo el catálogo ni crear envoltorios sin una necesidad concreta.
|
||
|
||
## Dependencias
|
||
|
||
Base UI proporciona interacción accesible y gestión del foco.
|
||
class-variance-authority define variantes del botón; cn combina clases de Tailwind.
|
||
lucide-react aporta los iconos del selector y tw-animate-css sus transiciones.
|
||
shadcn es una herramienta de desarrollo; su hoja de estilos común se incorpora al compilar.
|
||
|
||
Guías oficiales: [monorepos](https://ui.shadcn.com/docs/monorepo) e [instalación manual](https://ui.shadcn.com/docs/installation/manual).
|
||
|
||
## Ayuda contextual
|
||
|
||
ContextHelp muestra una explicación breve y permite desplegar ejemplos y recomendaciones mediante teclado. EntityHelp aporta contenido específico en español e inglés para compañías, personas, departamentos, sedes, asignaciones y todas las secciones de cuentas y accesos. Los catálogos explican si la definición es global o exclusiva de la compañía. Los ejemplos son orientativos: nunca se convierten en valores del formulario.
|
||
|
||
El botón Ayuda de Cuentas y accesos y Catálogos informáticos permite consultar la explicación de la sección sin abrir un alta. La misma ayuda aparece dentro de los modales de alta y edición. FormDialog utiliza una variante amplia para personas y formularios con más campos; el número de campos no cambia la navegación a una página propia.
|
||
|
||
La ayuda de los formularios se coloca en su parte inferior, inmediatamente antes de FormActions. Este pie compartido reúne Guardar y Cancelar dentro del formulario, conservando el envío y el cierre accesible del diálogo. Los diálogos de consulta mantienen únicamente Cerrar.
|
||
|
||
## SelectWithAction: desplegable con acción integrada
|
||
|
||
El componente compartido vive en `@repo/shadcn-ui/components/select-with-action`. Compone Select, Button y ButtonGroup de shadcn/ui (Base UI, Vega). Presenta selección y botón «+» como un único grupo visual, manteniendo controles separados para teclado y lectores de pantalla. ButtonGroup utiliza fieldset para expresar semánticamente la agrupación.
|
||
|
||
Su contrato recibe id, etiqueta, opciones, placeholder, valor controlado y callback de selección. La acción es opcional y contiene etiqueta accesible, callback y estado deshabilitado. Admite opciones deshabilitadas, estado inválido, descripción accesible y obligatoriedad. Deshabilitar el selector también deshabilita la acción. El botón usa type=button y no envía el formulario.
|
||
|
||
No conoce compañías, HTTP, formularios ni modales: esas responsabilidades pertenecen al consumidor. No contiene textos propios que deban traducirse.
|
||
|
||
### Formularios que permiten alta rápida
|
||
|
||
Sistema permite crear un sistema propio de la compañía mediante un modal adicional. Entorno permite crear un entorno para el sistema activo seleccionado; su acción no aparece antes de elegir un sistema. Los campos fijos en edición no ofrecen alta rápida. Uso y otros valores cerrados tampoco ofrecen altas.
|
||
|
||
QuickCreateDialog conserva los valores del formulario principal, actualiza el catálogo y selecciona el nuevo UUID después de guardar. Usa las rutas, protección CSRF y reglas de negocio existentes. Un nombre activo ya existente permite usar ese registro sin duplicarlo. Cancelar el modal adicional conserva la cuenta sin cambios. El elemento creado permanece en el catálogo aunque se cancele después la cuenta.
|
||
|
||
La ayuda aparece antes del pie con Guardar y seleccionar / Cancelar. Solo hay un nivel de alta adicional; el nuevo formulario no vuelve a ofrecer altas rápidas.
|
||
|
||
|
||
La revisión de los formularios extiende este patrón a personas, departamentos, cuentas, grupos, recursos, tipos de recurso, permisos y plantillas. Las asignaciones de departamento permiten añadir el primero sin abandonar el formulario. Se reutilizan los formularios y rutas existentes, manteniendo su validación y ayuda inferior.
|
||
|
||
Los entornos necesitan un sistema activo seleccionado. Los permisos nuevos heredan el tipo del recurso elegido. Al crear un grupo desde una pertenencia, sistema y entorno quedan fijados a los de la cuenta. Las plantillas siguen siendo globales; el modal lo explica y carga únicamente sus opciones globales. Los elementos creados desde un selector están activos para poder seleccionarlos.
|
||
|
||
Uso de cuenta, estado de configuración e idioma son listas cerradas. Las referencias históricas de cobertura (necesidad, autorización, titularidad y pertenencia) conservan sus operaciones propias. Los campos inmutables en edición no ofrecen altas. Los permisos múltiples de una necesidad continúan como casillas.
|
||
|
||
El catálogo de activos utiliza SelectWithAction para añadir campos a una plantilla y crearlos mediante un modal adicional. Los tipos de dato constituyen una lista cerrada. Las definiciones de serie se consultan en modales de lectura; su personalización se realiza mediante copias.
|
||
|
||
El inventario añade altas rápidas de plantilla, sede y persona en los activos. La plantilla creada desde un activo solo ofrece campos existentes para mantener un único nivel adicional de modal. Las características utilizan controles según su tipo; los valores sí/no conservan un estado vacío distinto de No.
|
||
|
||
## Listados de entidades
|
||
|
||
El estándar se define en [Patrón de listados de entidades](patron-de-listados-de-entidades.md), con inventario de pantallas y excepciones. EntityListHeader, EntityTable, EntityRowActions, EntityAction y EntityStatus viven en @repo/shadcn-ui/components/entity-list.
|
||
|
||
EntityTable recibe datos y columnas tipadas; utiliza TanStack Table para búsqueda, ordenación, visibilidad y paginación local. Admite búsqueda y filtros controlados por el módulo, conservando su semántica y ámbito. Las columnas de identificación y acciones quedan visibles. Los menús utilizan Base UI y conservan los callbacks de las acciones originales. No realiza peticiones ni conoce DTO de negocio.
|
||
|
||
La paginación es local sobre los conjuntos que las API actuales ya devuelven. No simula paginación de servidor. La selección del activo para el panel sigue perteneciendo a Inventario, fuera de la tabla compartida.
|
||
|
||
EntityTable separa internamente la configuración de columnas/ordenación, el contenido semántico y la paginación; useEntityTable concentra el estado de representación. entityLabel permite contar «plantillas», «campos» o «tickets» aunque el título del espacio sea más amplio. Los tamaños disponibles son 10, 25, 50 y 100; una opción vacía nunca cambia el tamaño a cero.
|
||
|
||
Se declara @tanstack/react-table en el paquete compartido porque es quien consume ahora la librería; se reutiliza la versión 8.21.3 que ya utilizaba Compañías. No se introduce un segundo sistema de tablas.
|
||
|
||
|
||
## Catálogo de marcas y selector funcional
|
||
|
||
BrandCatalogPage reutiliza EntityListHeader/EntityTable con búsqueda, filtro de estado, columnas, ordenación y paginación. BrandEditorDialog compone FormDialog y BrandForm tanto para la gestión global como para las altas relacionadas; el formulario conserva ContextHelp y FormActions.
|
||
|
||
BrandSelect, dentro del módulo assets, adapta SelectWithAction al catálogo remoto: filtra marcas activas, conserva la selección inactiva actual y textos anteriores, ofrece alta rápida y respeta una sola anidación. El borrador padre se conserva y la nueva marca se selecciona por UUID. El componente genérico no conoce BrandDTO, consultas ni endpoints. Véase [Catálogo global de marcas](catalogo-global-de-marcas.md).
|
||
|
||
|
||
## Componentes modales de los módulos
|
||
|
||
El [inventario de modales](inventario-de-modales.md) localiza las interacciones de alta, edición,
|
||
copia, confirmación, detalle, historial, selección, ayuda y configuración, incluyendo las altas
|
||
rápidas dentro de otros formularios. La regla normativa de organización reside en AGENTS.md.
|
||
|
||
Las páginas coordinan apertura, selección y callbacks. El componente modal posee su estructura
|
||
y la lógica que le pertenece; el formulario mantiene validación, borrador, errores y guardado.
|
||
Se reutilizan FormDialog y FormActions sin modificar su implementación vendor. Los diálogos
|
||
específicos de entidades viven junto a sus consumidores, no en la biblioteca visual genérica.
|
||
|
||
CompanyEditorDialog, PersonEditorDialog, DepartmentEditorDialog, SiteEditorDialog,
|
||
BrandEditorDialog y SupportToolEditorDialog se reutilizan donde ya existen operaciones
|
||
equivalentes. Los callbacks de guardado actualizan la selección del consumidor; no se cierra
|
||
un formulario por el mero inicio de una mutación. Los historiales de consulta se derivan de
|
||
los datos remotos actuales, mientras que los borradores de edición permanecen locales.
|
||
|
||
## Acciones de fila ancladas
|
||
|
||
EntityRowActions muestra un botón icon-sm con EllipsisIcon, etiqueta accesible y título traducidos, sin texto visible repetido. EntityTable reserva la identidad de columna actions para operaciones: siempre visible, sin búsqueda/ordenación y fijada a la derecha mediante columnPinning de TanStack Table. EntityTableContent renderiza primero las columnas de datos y después la columna anclada, con cabecera/celdas sticky right-0, alineación derecha, anchura compacta y fondo opaco para evitar solapamientos al desplazar la tabla. Todos los listados consumidores heredan este comportamiento.
|
||
|
||
## Tarjetas de indicadores
|
||
|
||
StatisticCard (`@repo/shadcn-ui/components/statistic-card`) representa un indicador con etiqueta, valor, icono y tinte discreto. Admite carga/error, evita sustituir datos desconocidos por cero y utiliza un botón con foco visible cuando hay navegación. No depende de DTO ni endpoints. Inicio la reutiliza para sus cinco KPI; la organización de bloques y sus estados permanece local al dashboard.
|
||
|
||
## Shell global
|
||
|
||
`App` conserva el acceso y el selector de idioma. `CompaniesWorkspace` compone sidebar,
|
||
header global y contenido con los destinos y estado de navegación existentes. El
|
||
header solo aparece en sesiones autenticadas. Los catálogos se seleccionan en la
|
||
sidebar; la navegación interna de cada compañía permanece en `CompanyDetail`.
|
||
|
||
Los tokens Tailwind 4 `--color-shell-*` de `apps/web/src/global.css` aíslan la paleta del shell de los
|
||
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.
|
||
|
||
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.
|
||
|
||
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
|
||
`--spacing-shell-sidebar-inset` y la utilidad `px-shell-sidebar-inset`. El header global usa
|
||
`bg-shell-header` (#FFFFFF), con un borde inferior `border-shell-border`, sobre el contenido #F7F9FB.
|
||
|
||
### Estilos con Tailwind 4
|
||
|
||
La aplicación y la biblioteca base utilizan Tailwind 4.2.2 y el plugin oficial de Vite.
|
||
El CSS de la aplicación contiene únicamente imports, fuentes de detección y tokens `@theme`;
|
||
los layouts, estados y reglas responsive se expresan mediante utilidades en sus componentes.
|
||
No hay clases globales de componente `desk-*`, `company-space-*`, `dashboard-*` ni de inventario.
|
||
La implementación vendor de `@repo/shadcn-ui` no se modifica para personalizar el shell.
|
||
|
||
Los colores propios utilizan `--color-shell-*`; las dimensiones, `--spacing-shell-*`;
|
||
los radios, `--radius-shell-*` y `--radius-panel`; la sombra del desplegable,
|
||
`--shadow-shell-popover`. El resto de la interfaz conserva los tokens semánticos de shadcn/ui.
|
||
Los valores puntuales que no se repiten se declaran como utilidades arbitrarias, no como
|
||
tokens o abstracciones anticipadas. Los degradados conservan su interpolación CSS original.
|
||
|
||
Los breakpoints de la adaptación se declaran en píxeles para conservar los cortes anteriores:
|
||
`shell` (768px), `inventory` (1100px) y `dashboard` (1200px). Se usan consistentemente dentro
|
||
de cada layout para evitar conflictos de orden entre variantes con distintas unidades.
|
||
|
||
`WorkspaceNavigationButton` compone Button y comparte los niveles principal y de catálogo,
|
||
selección, foco y contracción. `DashboardNavigationButton`, `DashboardStateBadge` y
|
||
`DashboardTable` encapsulan patrones repetidos del dashboard; este último reutiliza Table.
|
||
Cada componente vive en su propio fichero y recibe acciones, textos o contenido del consumidor.
|
||
`LanguageSwitcher` permite ajustar su margen mediante `className`, sin selectores globales
|
||
dependientes de la cabecera que lo contiene.
|
||
|
||
## Avatar de iniciales
|
||
|
||
`InitialsAvatar` (`modules/companies/src/web/components/initials-avatar.tsx`) es un
|
||
componente visual sin dependencias de compañías, DTO ni consultas. Recibe `name`
|
||
y atributos de span; admite `className` para tamaño y `aria-hidden` cuando el
|
||
control contenedor ya tiene nombre accesible. Se reutiliza en las compañías de
|
||
la sidebar y en el perfil del header.
|
||
|
||
Muestra la primera inicial de hasta dos palabras; conserva siglas de una palabra
|
||
como IBM (hasta tres caracteres), y usa «?» para nombres vacíos. El fondo pastel
|
||
se elige de una paleta de doce colores mediante un hash del nombre normalizado:
|
||
mayúsculas, espacios y acentos no cambian el color. El color no depende del orden
|
||
ni cambia al recargar; distintos nombres pueden compartir color. El texto oscuro
|
||
mantiene contraste legible. Los avatares de compañías siguen visibles cuando se
|
||
contrae el menú y conservan la selección y las ayudas del botón de navegación.
|
||
|
||
## Avatar y logotipo de compañía
|
||
|
||
`CompanyAvatar`, `CompanyIdentity` y `CompanyLogoField` reúnen la presentación y selección del logotipo. Su contrato, límites y persistencia se documentan en [Logotipos de compañías](logotipos-de-companias.md). Se usan tamaños de 24–28 px en navegación/listas y 40–56 px en detalle/formulario.
|