# Frontend de suppliers: diseño e implementación UI/UX ## 1. Objetivo El frontend de `suppliers` debe ofrecer listado, detalle, creación y edición con el mismo lenguaje visual y los mismos patrones de navegación que `customers`, respetando el contrato funcional más pequeño de Supplier. Este documento es la fuente normativa para la implementación. La paridad buscada es de arquitectura y experiencia, no de campos ni capacidades de negocio. ## 2. Estado actual Antes de esta implementación, Supplier solo disponía de entidades frontend, adapters, APIs y hooks de TanStack Query. La sidebar enlazaba a `/suppliers`, pero no existían manifest, rutas, páginas, formularios ni traducciones del módulo. El API client tenía una incompatibilidad activa: la actualización delegaba en `IDataSource.updateOne()`, que usa PUT, aunque Supplier solo expone PATCH. ## 3. Customer como referencia Se adoptan de Customer: - manifest y rutas lazy por módulo; - páginas de listado con `RdxPageContainer`, `RdxPageHeader`, búsqueda con debounce y `DataTable`; - editores fullscreen con `RdxEditorPageLayout`, header persistente, protección de cambios sin guardar y secciones Rdx; - `PageErrorState`, skeletons, alertas y toasts del sistema; - React Hook Form con validación Zod; - queries y mutations encapsuladas en hooks y adapters de transporte. No se copia la fiscalidad, la eliminación, el panel lateral ni las acciones exclusivas de Customer. ## 4. Diferencias funcionales Customer/Supplier | Área | Customer | Supplier | | --- | --- | --- | | Lifecycle | Puede tener capacidades propias | Status solo informativo | | Eliminación | Existe contrato Customer | No existe DELETE Supplier | | Fiscalidad | Incluye configuración fiscal | Fuera de alcance | | Pagos | Puede incluir términos y métodos | Fuera de alcance | | Dirección | Contrato agrupado en áreas modernas | DTO Supplier plano | | Actualización | Contrato propio | PATCH parcial, sin `null` para borrar | ## 5. Arquitectura frontend Supplier ```txt Page -> controller hook -> query/mutation hook -> API function -> DTO adapter -> frontend entity ``` Los DTO snake_case quedan limitados a `common`, API y adapters. Páginas, formularios y entidades usan camelCase. ## 6. Rutas | Ruta | Pantalla | Layout | | --- | --- | --- | | `/suppliers` | Listado | `app-sidebar` | | `/suppliers/create` | Creación | `app-fullscreen` | | `/suppliers/:id` | Detalle | `app-fullscreen` | | `/suppliers/:id/edit` | Edición | `app-fullscreen` | Se adopta `edit`, que es la convención vigente de Customer. No se crean rutas DELETE, activate o deactivate. ## 7. Listado El listado usa header, descripción, acción principal, `SimpleSearchInput`, tabla server-side y paginación. Las columnas son: - identidad: nombre, nombre comercial, TIN y tipo empresa/persona; - referencia; - contacto principal; - status no interactivo; - acciones Ver y Editar. Solo `name`, `reference` y `email_primary` ofrecen sorting, porque pertenecen a la whitelist real. La búsqueda usa el query param `q`, debounce de 300 ms y reinicia la página. ## 8. Detail El detalle presenta un header con nombre, contexto secundario y badge de status. Se agrupa en Identificación, Contacto, Dirección y Preferencias. Los valores ausentes muestran `-` para mantener una cuadrícula estable. Las únicas acciones son Volver y Editar. ## 9. Create Create reutiliza el editor común Supplier y genera el UUID en el cliente. El payload conserva el DTO plano del backend y omite campos opcionales vacíos. Tras éxito actualiza caché, muestra feedback y navega al detalle. ## 10. Update Update carga el detalle, adapta la entidad al formulario y construye el PATCH desde `dirtyFields`. Solo se envían campos modificados con valor utilizable. Un campo vacío se omite porque el contrato vigente no admite `null => borrar`; la UI no inventa esa semántica. La petición se realiza explícitamente con método PATCH, nunca con `IDataSource.updateOne()`. ## 11. Formularios y validación Un único `SupplierFormSchema` en camelCase valida create y update. Se reutilizan schemas semánticos de Core para TIN, país, código postal, idioma, moneda, teléfono y web. Las secciones son Identificación, Dirección, Contacto y Preferencias. `isCompany` es boolean y usa el selector visual empresa/persona. `status` no forma parte del formulario. ## 12. API / React Query Las keys separan listado y detalle. Create establece el detalle e invalida listados. Update establece el detalle, parchea filas presentes e invalida listados. El cambio de empresa reutiliza el reset global de queries y el interceptor común que añade `X-Company-Id`. ## 13. Estados loading/error/empty - List usa `SkeletonDataTable` en carga inicial. - Detail y Update usan skeleton de editor. - Errores bloqueantes usan `PageErrorState` con reintento y regreso. - Errores de mutación usan `ErrorAlert` y toast. - La tabla conserva el empty state común; una búsqueda sin resultados mantiene visible el buscador. ## 14. Responsive Header y acciones apilan en móvil. Formularios pasan de dos columnas a una. Las tarjetas de detalle usan una columna en móvil y dos en escritorio. La tabla conserva scroll horizontal y paginación del componente compartido. ## 15. Accesibilidad Todos los inputs tienen labels asociados mediante los componentes Rdx. Los botones incluyen nombre accesible, los iconos decorativos usan `aria-hidden`, las filas de acciones son navegables y los errores se asocian mediante React Hook Form. El foco se mueve al primer campo inválido. ## 16. i18n Todos los textos visibles pertenecen al namespace `Suppliers`. Se mantienen recursos completos en `es` y `en`; no se insertan textos de interfaz específicos en componentes. ## 17. Componentes reutilizados - `RdxPageContainer`, `RdxPageStack`, `RdxPageHeader`, `RdxPageActions`; - `RdxEditorPageLayout`, `RdxPanel`, `RdxFormGrid`; - `DataTable`, `SkeletonDataTable`, `DataTableColumnHeader`, `RdxRowActions`; - `TextField`, `SelectField`, `PageFormHeader`, `FormActionsBar`; - `SimpleSearchInput`, `PageErrorState`, `ErrorAlert`; - `UnsavedChangesProvider` y `useReturnToNavigation`. ## 18. Componentes Supplier específicos - `SuppliersGrid` y sus columnas, porque expresan identidad y acciones Supplier; - `SupplierStatusBadge`, limitado al status del dominio; - `SupplierEditorForm` y sus secciones, porque el DTO plano y la ausencia de fiscalidad son específicos; - páginas y controllers de List, Detail, Create y Update. No se extraen componentes desde Customer: la similitud visual no justifica acoplar ambos dominios. ## 19. Decisiones descartadas Quedan expresamente fuera: - DELETE; - activate/deactivate y cualquier lifecycle HTTP; - `paymentTermId`; - `paymentMethodId`; - fiscalidad y defaults fiscales; - integración con `supplier-invoices`; - edición de status; - refactor del DTO plano de dirección; - semántica `null => borrar` no soportada. ## 20. Plan de implementación 1. Corregir API PATCH, keys, adapters y estrategia de caché. 2. Registrar manifest y rutas Supplier. 3. Implementar listado y navegación. 4. Implementar detalle. 5. Implementar formulario compartido y Create. 6. Implementar Update con `dirtyFields`. 7. Añadir recursos `es` y `en`. 8. Revisar responsive y accesibilidad. 9. Añadir tests de mappers, PATCH parcial y rutas. 10. Ejecutar tests, typecheck, lint, build dirigido y checklist manual. ### Checklist manual - Abrir listado, buscar por nombre, TIN y referencia. - Ordenar nombre, referencia y email. - Abrir detalle y navegar a editar. - Guardar un único campo y confirmar que el PATCH es parcial. - Guardar `isCompany = false`. - Crear Supplier y validar errores de formulario/API. - Confirmar actualización de detail y list. - Cambiar de empresa y comprobar aislamiento. - Revisar listado, detalle y formularios en desktop, tablet y móvil.