# Frontend ERP – Integración de módulos con Settings ## 1. Propósito Este documento complementa **Frontend ERP – Sistema modular de Settings** y define cómo debe integrarse un módulo funcional con `/settings` sin acoplarse al shell de aplicación ni a otros módulos. Aplica a módulos actuales y futuros que necesiten exponer configuración de la empresa activa. Ejemplos: ```txt customer-invoices → VeriFactu document-series → Series documentales identity/company → Datos de empresa módulo de certificados → Certificados ``` --- ## 2. Principio operativo ```txt Módulo → declara metadata de Settings → registra su propia route → implementa su propia Page → mantiene su propia persistencia ``` El shell únicamente descubre y presenta la opción. --- ## 3. Separación de responsabilidades ### Settings shell Conoce: ```txt id de sección label descripción orden path permiso ``` No conoce: ```txt DTO Entity Controller formulario endpoint reglas de negocio ``` ### Módulo Conoce: ```txt routing concreto Page/Page Controller controllers shared/api shared/adapters shared/entities shared/hooks validación persistencia reglas de negocio ``` --- ## 4. Flujo completo ```txt Module manifest │ │ settings contribution ▼ SettingsRegistry │ ▼ SettingsNavigation │ │ usuario navega ▼ /settings/
/ │ ▼ Route del módulo │ ▼ Settings Page del módulo │ ▼ Page Controller │ ▼ Controller │ ▼ shared/hooks │ ▼ shared/api ``` La cadena de datos del módulo sigue siendo: ```txt API → DTO → Adapter → Entity → Hook → Controller → Page Controller → UI ``` --- ## 5. Metadata mínima del módulo Contrato conceptual: ```ts export interface ModuleSettingsSectionContribution { id: string; labelKey: string; order?: number; } export interface ModuleSettingsEntryContribution { id: string; sectionId: string; labelKey: string; descriptionKey?: string; path: string; order?: number; permission?: string; } ``` Ejemplo conceptual: ```ts export const customerInvoicesManifest = { settings: { sections: [ { id: "billing", labelKey: "settings.sections.billing", order: 200, }, ], entries: [ { id: "verifactu", sectionId: "billing", labelKey: "customerInvoices.settings.verifactu.title", descriptionKey: "customerInvoices.settings.verifactu.description", path: "/settings/billing/verifactu", order: 100, permission: "customerInvoices.settings.verifactu.manage", }, ], }, }; ``` El shape definitivo debe adaptarse al manifest real del repositorio. --- ## 6. Regla de routing Una contribución de Settings no debe registrar un componente React directamente. Incorrecto: ```ts { id: "verifactu", component: VerifactuSettingsPage, } ``` Correcto conceptualmente: ```ts { id: "verifactu", path: "/settings/billing/verifactu", } ``` Y en el routing propio del módulo: ```txt /settings/billing/verifactu → VerifactuSettingsPage ``` El shell y el módulo se acoplan únicamente mediante una ruta estable. --- ## 7. Ubicación de código dentro del módulo La ubicación concreta debe seguir la estructura real del módulo. Dirección conceptual recomendada: ```txt [module]/ ├── manifest.ts ├── [module]-routes.tsx ├── shared/ │ ├── api/ │ ├── adapters/ │ ├── entities/ │ └── hooks/ │ └── settings/ └── [setting]/ ├── controllers/ ├── entities/ ├── ui/ └── index.ts ``` No introducir esta estructura si el módulo real ya tiene una convención equivalente mejor establecida. --- ## 8. Page Controller Una página de settings debe seguir la misma regla que el resto del ERP. Responsabilidades del Page Controller: ```txt integrar contexto externo componer controllers integrar navegación si aplica exponer API estructurada a la Page ``` No debe: ```txt transportar DTO realizar llamadas API directas contener lógica visual ``` --- ## 9. Controller Responsable de: ```txt estado del caso de uso formulario submit validaciones de flujo side effects coordinación con hooks de shared ``` No debe: ```txt usar useParams/useNavigate directamente usar DTO conocer el SettingsRegistry ``` --- ## 10. Shared Una configuración de módulo reutiliza las reglas generales de `shared/`. ```txt shared/api → transporte remoto shared/adapters → DTO ↔ Entity cuando corresponda shared/entities → contratos frontend shared/hooks → React Query ``` El hecho de que la UI aparezca bajo `/settings` no cambia estas responsabilidades. --- ## 11. Persistencia Cada módulo decide su persistencia. No utilizar por defecto un almacenamiento genérico de Settings. Ejemplos: ```txt VeriFactu → configuración persistida del dominio correspondiente Series documentales → agregado DocumentSeries Datos de empresa → Company/Identity ``` La navegación compartida no implica almacenamiento compartido. --- ## 12. Empresa activa Toda configuración integrada en `/settings` pertenece a la empresa activa. Un módulo no debe introducir su propio selector de empresa si la aplicación ya dispone de contexto global. La query/mutation debe usar la infraestructura existente para resolver la empresa activa. Cuando cambie la empresa activa: ```txt los datos visibles deben corresponder a la nueva empresa ``` La implementación exacta depende de cómo estén construidas actualmente las query keys, headers y mecanismos de invalidación. --- ## 13. Permisos La contribución puede declarar: ```ts permission?: string; ``` El permiso solo controla la disponibilidad de la navegación frontend. La operación backend debe seguir autorizándose independientemente. Un módulo no debe implementar un segundo sistema de permisos para Settings. --- ## 14. i18n La metadata debe usar claves de traducción. Ejemplo: ```ts labelKey: "customerInvoices.settings.verifactu.title"; descriptionKey: "customerInvoices.settings.verifactu.description"; ``` El módulo mantiene sus traducciones. La sección compartida puede usar una key común del shell si el diseño final establece un catálogo global de secciones. --- ## 15. UI La página del módulo debe usar el sistema visual Rdx vigente. El shell proporciona: ```txt layout navegación título general de Configuración ``` El módulo proporciona: ```txt título específico contenido formularios acciones loading/error states ``` Evitar duplicar dentro del módulo el sidebar global de Settings. --- ## 16. Guardado Cada setting debe guardar de forma independiente. Ejemplo: ```txt VeriFactu [Cancelar] [Guardar] ``` No existe un submit global de `/settings`. Si la pantalla tiene varios bloques pero forman una única configuración transaccional, el módulo puede resolverlos con un único formulario/controlador. --- ## 17. Loading y errores El módulo conserva responsabilidad sobre: ```txt loading inicial error inicial refetch error submit error validation errors retry ``` El shell no interpreta errores de dominio. Debe reutilizarse el patrón consolidado de `PageErrorState`, `ErrorAlert` y errores de formulario cuando corresponda. --- ## 18. Unsaved changes Cada módulo es responsable de avisar o bloquear navegación cuando existan cambios no guardados. No crear un estado dirty global dentro del Settings shell. --- ## 19. Deep linking Una entrada debe ser accesible directamente por URL. Ejemplo: ```txt /settings/billing/verifactu ``` El módulo no debe depender de que el usuario haya visitado antes `/settings`. --- ## 20. Varias contribuciones a una misma sección Ejemplo: ### customer-invoices ```txt billing └── VeriFactu ``` ### document-series ```txt billing └── Series documentales ``` Resultado: ```txt Facturación ├── VeriFactu └── Series documentales ``` Los módulos no se importan entre sí. --- ## 21. Caso piloto: VeriFactu El primer setting real será VeriFactu. Su contribución debe limitarse a metadata: ```txt section entry path orden permiso si existe ``` La implementación funcional de VeriFactu debe documentarse en un documento específico posterior. Este documento específico deberá explicar: ```txt estado/configuración de la empresa onboarding inicial valores por defecto de facturación certificados si pertenecen al mismo dominio snapshot de IssuedInvoice API DTOs Entities controllers UI ``` sin redefinir el framework de Settings. --- ## 22. Checklist para añadir un setting nuevo ### Manifest - [ ] Declara sección si es necesaria. - [ ] Declara entry con `id` estable. - [ ] Usa `labelKey`. - [ ] Usa `descriptionKey` si aporta valor. - [ ] Declara `path` estable. - [ ] Declara `order` cuando importe el orden semántico. - [ ] Declara `permission` solo si existe permiso real. ### Routing - [ ] La route pertenece al módulo. - [ ] Funciona mediante deep-link. - [ ] No requiere estado previo del shell. ### Arquitectura - [ ] API → DTO → Adapter → Entity → Hook → Controller → Page Controller → UI. - [ ] No hay DTO en UI/controller. - [ ] No hay routing en controller. - [ ] No hay dependencia del SettingsRegistry en dominio/shared. ### UI - [ ] Usa sistema visual Rdx. - [ ] No duplica SettingsNavigation. - [ ] Soporta teclado y focus-visible. - [ ] Maneja loading/error/empty correctamente. ### Empresa - [ ] Usa la empresa activa existente. - [ ] No introduce un selector paralelo. - [ ] El cambio de empresa no deja datos stale de la anterior. ### Seguridad - [ ] La navegación respeta permisos frontend. - [ ] El backend valida autorización de forma independiente. --- ## 23. Anti-patterns No hacer: ```txt importar SettingsPage desde el shell registrar componentes React como metadata principal crear un GenericSettingsForm crear un GenericSettingsRepository usar localStorage para configuración de negocio crear otro selector de company hacer imports entre módulos para agrupar settings usar un switch central por setting ``` --- ## 24. Regla mental final ```txt El módulo declara que su setting existe. El shell decide cómo descubrirlo y mostrarlo. El router decide cómo llegar. El módulo decide cómo funciona. ``` Criterio de validación: ```txt Si para añadir una nueva configuración hay que modificar SettingsPage, SettingsNavigation o un switch central, la integración está mal diseñada. ```