- 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.
203 lines
14 KiB
Markdown
203 lines
14 KiB
Markdown
# 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](componentes-de-interfaz.md) describe sus APIs y consumidores;
|
|
el [inventario de modales](inventario-de-modales.md) 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.
|