metis-desk/docs/patron-de-dialogos.md
David 94ddc5cf16 feat: enhance work profile management with new components and functionality
- Updated WorkstationDetailPage to replace BriefcaseIcon with MonitorIcon.
- Modified package.json to include new test files.
- Refactored FormStepActions to improve button state management.
- Introduced profile-creation helper functions for better profile management.
- Added WorkProfileAccountField and WorkProfileAuthorizationNotice components for account handling.
- Created WorkProfileComputerSummary and WorkProfileSummary for displaying profile details.
- Implemented WorkProfileFormFields to consolidate form fields for work profiles.
- Developed WorkProfileIdentityFields for managing identity-related inputs.
- Added WorkProfileSelectionField for selecting accounts, workstations, and persons.
- Created OptionPickerDialog and OptionPickerList for enhanced option selection.
- Introduced OptionSelectionField for visual selection with actions.
- Added tests for selection components to ensure functionality and reliability.
2026-10-16 13:11:52 +02:00

14 KiB

Patrón de diseño de cuadros de diálogo

Fuente normativa para crear o modificar diálogos de Metis Desk. Recoge las decisiones aplicadas a los editores de plantillas de activos y recursos y a los selectores PickerList. La guía de componentes describe sus APIs y consumidores; el inventario de modales recoge las implementaciones existentes.

Composición y reutilización

  • Cada interacción modal con responsabilidad propia vive en un componente y fichero .tsx independientes. El padre decide su apertura y aporta datos y callbacks.
  • Reutilizar FormDialog, FormBody y FormActions de @repo/rdx-ui, apoyados en las primitivas de shadcn/ui y Base UI. Consultar los patrones existentes antes de crear otro.
  • Mantener los bloques visuales genéricos independientes de entidades, DTO, consultas y endpoints. El módulo aporta textos traducidos, controles, datos y acciones.
  • Aplicar el mismo patrón a alta, edición y copia y a entidades equivalentes. Conservar los campos y restricciones propios de cada entidad, como el uso de las plantillas de recursos.

Estados vacíos con incorporación

Cuando un paso todavía no contiene elementos y permite incorporarlos, reutilizar EmptyState de @repo/rdx-ui/components/empty-state: icono, título, descripción y acciones aportados por el consumidor. Mantener los textos traducidos y las acciones independientes, con sus permisos y estados deshabilitados. El componente llena el ancho y puede crecer en un contenedor flex para ocupar el alto restante. Para distribuir ese espacio desde el cuerpo del diálogo, utilizar FormBody contentLayout="fill" y mantener la cadena de crecimiento hasta el estado vacío. No depender de min-height: 100% en contenedores intermedios con altura automática. Conservar el scroll, la altura exterior estable y la ayuda inmediatamente encima del pie del modal.

Grupos, títulos y separación

  • Las etiquetas de campos utilizan FormLabel de @repo/rdx-ui. Añadir required solo cuando el campo sea obligatorio: muestra un asterisco al final en color destructive. Los campos optativos muestran únicamente su nombre, sin «(opcional)».

  • La marca sigue las mismas condiciones que la validación del campo, también en selectores, características de plantillas y fechas condicionales. Conservar htmlFor, los atributos de validación y la ayuda accesible del control. No incluir el asterisco en las traducciones ni en el nombre de la característica. Los grupos con leyenda reutilizan RequiredIndicator.

  • Usar FormSectionTitle para los títulos de grupo: texto pequeño, seminegrita, mayúsculas, espaciado entre letras y color atenuado (text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-4). Conservar el nivel semántico del encabezado; las mayúsculas pertenecen a la presentación CSS y los textos pasan por i18n.

  • El título principal del diálogo y los nombres editables de secciones tienen su propia función. El nombre editable de una sección va en su cabecera, junto a sus acciones.

  • Para los datos generales, preferir FormSection variant="plain", sin borde ni relleno de tarjeta. Los grupos repetidos que necesitan identidad visual usan borde y fondo semánticos discretos. Evitar acumular marcos y rellenos en grupos anidados.

  • Mantener más separación entre grupos principales que entre controles relacionados. En el patrón de plantillas, TemplateMetadataSection deja 36 px antes del grupo de secciones (20 px del cuerpo y 16 px adicionales); las columnas y tarjetas se separan 16 px.

  • Situar las acciones locales en la cabecera del grupo al que afectan. Por ejemplo, «Añadir campo» se ofrece en «Campos» y lleva implícita la sección de destino.

Distribución responsive y alturas

  • Decidir las columnas por el ancho disponible del contenedor, mediante container queries. En espacios estrechos, apilar el contenido y permitir que nombres y acciones se adapten.
  • En formularios con nombre, icono y descripción, reutilizar TemplateMetadataSection: nombre e icono debajo a la izquierda, descripción a la derecha, en dos columnas iguales desde 560 px. Ambas columnas tienen igual altura y el área de descripción ocupa el alto restante. En una columna, el recorrido es nombre, icono y descripción.
  • Para secciones repetidas equivalentes, usar una columna con poco espacio y dos cuando sus controles quepan con comodidad. TemplateSectionsEditor sectionLayout="responsive" cambia a dos columnas desde 1.000 px. Las secciones de cada fila tienen la misma altura, determinada por la más alta, y se reajustan al plegar, desplegar o cambiar su contenido.
  • Mantener el orden de izquierda a derecha y de arriba abajo en el DOM, la numeración, el teclado y el arrastre. No introducir un orden visual diferente mediante CSS.
  • Cada grupo de campos responde a su propio ancho, también cuando su sección ocupa media fila. El bloque «Sin sección» permanece debajo de las secciones, a ancho completo.
  • No convertir formularios sencillos en cuadrículas de secciones ni imponer alturas absolutas que superen la ventana. La estabilidad de la altura exterior se rige por la regla siguiente; las secciones interiores conservan altura según su contenido. Los umbrales anteriores corresponden a los componentes citados; otros contenidos deben elegir su disposición según el espacio que necesitan sus controles.

Estabilidad de altura del modal

Esta regla se aplica a los cuadros de diálogo actuales y futuros. Al crear o modificar un diálogo, comprobar si su contenido puede variar mientras permanece abierto: pasos, selecciones, altas anidadas, filtros, carga, errores, ayuda plegable o grupos condicionales. En esos casos, la altura exterior debe mantenerse estable durante toda la interacción. No debe crecer, encogerse ni desplazar el pie por cada elección del usuario.

  • Utilizar FormDialog layout="stable" para formularios y asistentes con contenido variable, como el alta de puestos. Reservar la altura desde la apertura, también cuando el primer paso sea corto o los datos todavía estén cargando.
  • La altura se adapta a la ventana: en móvil ocupa la altura dinámica disponible menos 2 rem de margen; desde sm, el menor valor entre el 85 % de la altura dinámica y 900 px, respetando siempre el margen. Puede reajustarse al cambiar la ventana, orientación o espacio disponible del teclado; no depende del contenido ni se mide con JavaScript.
  • Conservar cabecera y pie visibles, min-h-0 en la cadena flex, FormBody desplazable y espacio reservado para la barra vertical. El contenido puede cambiar de alto dentro del cuerpo, sin recortar campos, errores o acciones ni reducirlos para hacerlos caber.
  • Mantener layout="picker" para selectores con tabla que ocupa el espacio restante; ya proporciona altura estable y su distribución específica. No usarlo para fijar la altura de formularios que no sean selectores.
  • layout="content" queda para confirmaciones y diálogos breves cuyo contenido no cambia de tamaño. Si incorporan contenido variable, aplicar la variante estable al modificarlos.
  • Revisar pasos cortos y largos, listas vacías y extensas, carga/error y altas anidadas, tanto en móvil como en escritorio. Deben conservarse la altura exterior y la posición de las acciones, con acceso mediante scroll y teclado a todo el contenido.

Secciones plegables y filas compactas

  • Las secciones extensas o repetidas utilizan la primitiva Collapsible mediante TemplateSectionEditor. Empiezan abiertas y conservan el estado de apertura por identidad al reordenarse. El nombre, el agarre y las acciones permanecen en la cabecera.
  • Conservar los controles al plegar. Si la validación detecta un control inválido oculto, abrir la sección antes de enfocarlo. Plegar no modifica los datos.
  • En listas de configuración de campos, utilizar la presentación compacta TemplateSectionsEditor fieldLayout="compact": agarre, switch de obligatoriedad, nombre, flechas de desplazamiento y papelera. Evitar una segunda fila de controles cuando esta configuración sea suficiente. Esta pauta corresponde a referencias de campos dentro de una plantilla; los formularios de edición de sus valores conservan sus controles propios.
  • Dar nombre accesible a los switches y botones de icono, incluyendo el campo o la sección. La obligatoriedad conserva su etiqueta accesible y ayuda al pasar el ratón.
  • Reutilizar IconPicker como desplegable compacto con icono y texto, incluida la opción sin icono. Evitar una fila permanente con todas las opciones de iconos.

Ordenación y selección de campos

  • Reutilizar useDragReorder, ReorderHandle y el indicador de destino. Mantener flechas explícitas y ordenación por teclado; el arrastre no es la única forma de cambiar el orden.
  • Las secciones se ordenan entre sí y los campos dentro de su sección. Conservar el foco y bloquear los movimientos imposibles o las acciones durante el guardado. Cancelar un arrastre no cambia los datos y guardar confirma el borrador del formulario.
  • Cuando se incorporen varias referencias de un catálogo, utilizar un selector múltiple en diálogo en lugar de un desplegable que obligue a añadirlas una a una. En plantillas, reutilizar CatalogFieldPickerDialog desde la cabecera de cada grupo de campos, sin un bloque independiente de incorporación tras los datos generales.
  • Confirmar la selección como lote, conservarla al buscar o paginar y descartar la selección provisional al cancelar. Respetar límites, disponibilidad y ausencia de duplicados.
  • El alta rápida opcional se ofrece a la izquierda del pie mediante leadingActions. Debe poder ocultarse o deshabilitarse desde el consumidor. Mantener el selector y su estado durante el alta anidada; crear el elemento no confirma por sí solo el lote seleccionado.

Diálogos de selección PickerList

  • Todos utilizan FormDialog layout="picker" y EntityTable layout="fill": altura estable adaptada a la ventana, independiente de búsquedas, carga y número de resultados. La tabla ocupa el espacio restante y mantiene desplazamiento interno con espacio reservado para la barra; búsqueda, filtros, paginación y acciones permanecen estables.
  • Mantener el área de resultados incluso vacía y las cabeceras visibles durante el scroll. En ventanas pequeñas, permitir el desplazamiento adicional del cuerpo para acceder a todos los controles.
  • La columna selection es la primera, antes del nombre: anchura ajustada al checkbox o radio, poco espacio hasta el nombre y cabecera visualmente oculta con nombre accesible. No se ordena, no se oculta y no se ancla a la derecha como las acciones.
  • Pulsar una zona no interactiva de una fila disponible alterna su checkbox o selecciona su radio. Reutilizar onRowClick de la tabla; los controles internos ejecutan solo su propia acción, sin duplicar cambios. Respetar filas deshabilitadas y navegación por teclado.

Foco inicial en la búsqueda rápida

Regla para todos los diálogos PickerList actuales y futuros: al abrirlos, el foco inicial debe situarse en el cuadro de búsqueda rápida, listo para escribir. No debe quedar en las pestañas, filtros, controles de selección o botones del pie.

  • Reutilizar FormDialog layout="picker" y la búsqueda de EntityTable, que se identifica con data-quick-search. FormDialog establece el foco mediante initialFocus de Base UI, limitado al input del propio modal. Los selectores con búsqueda propia deben mantener el mismo atributo en su input y la búsqueda debe estar montada y habilitada al abrir.
  • Mantener la búsqueda visible y disponible durante carga, errores y resultados vacíos. No esperar a los datos para montarla ni mostrar un mensaje de vacío mientras se carga.
  • Aplicar el foco al abrir, también al reabrir; no volver a enfocarlo por cada tecla, filtrado, cambio de pestaña, paginación o actualización de datos. No utilizar temporizadores ni efectos que roben el foco durante la interacción.
  • Mantener el aislamiento de los diálogos anidados y la restauración de foco de Base UI: cerrar un alta relacionada devuelve el foco a su disparador dentro del picker; cerrar el picker devuelve el foco al control que lo abrió.
  • Verificar apertura con ratón y teclado, reapertura, carga inicial y altas anidadas. El foco visible y el uso de Tab, flechas y Escape deben seguir funcionando.

Cuerpo, pie y accesibilidad

  • Elegir la anchura del diálogo según su contenido y respetar los márgenes de la ventana. Mantener cabecera y pie visibles y el cuerpo desplazable cuando el contenido lo requiera.
  • Las altas guiadas de recursos y activos desde el inventario de compañía siguen tres pasos: plantilla, características y datos finales. Reutilizar FormProgress y FormStepActions, validar antes de avanzar y conservar el borrador al retroceder. Guardar se muestra al final; ayuda, cancelación y errores permanecen dentro del diálogo. Los puestos y perfiles siguen cuatro pasos adaptados a sus propias referencias; comparten FormProgress, FormStepActions y FormHelp. La cuenta de un perfil sigue siendo opcional y su ordenador se deriva del puesto, sin añadir una segunda elección del dispositivo. El alta usa tamaño medio y la edición completa conserva tamaño grande.
  • Los formularios con contenido variable mantienen altura exterior estable según la regla de estabilidad; sus grupos interiores conservan altura según contenido. No introducir pestañas ni pasos para resolver solo espacio.
  • Reutilizar FormHelp para la ayuda contextual general inmediatamente antes de FormActions. En los diálogos de altura estable debe quedar anclada abajo, fuera del scroll principal: campos, progreso y errores se desplazan en FormBody, mientras la ayuda permanece pegada al pie incluso en pasos cortos. Eliminar márgenes superiores innecesarios y conservar el padding normal junto al footer. Limitar su altura y permitir scroll propio al desplegar ejemplos, para no ocultar los campos ni las acciones en ventanas pequeñas. Mantener errores junto al contexto que los origina y las acciones dentro del formulario.
  • Conservar foco, cancelación, cierre por teclado, estados de carga, error, vacío y envío. No cerrar ni limpiar el formulario antes de confirmar una operación correcta.
  • Validar los cambios de interacción con pruebas dirigidas y revisar visualmente los anchos estrecho y amplio, nombres largos, secciones de distinta altura, plegado y resultados vacíos cuando correspondan. Informar de las comprobaciones que no se hayan podido ejecutar.