# Sistema Lego Panels de Simifactu > Fecha: 2025-12-09 > Proposito: Documentar arquitectura Lego Panels para aplicar en zcatgui --- ## Resumen **Lego Panels** es una arquitectura de composicion modular de UI donde: - Cada panel es **autonomo** (maneja su propio estado, UI y logica) - Los paneles son **reutilizables** (mismo panel en diferentes ventanas) - Las ventanas se construyen **componiendo** paneles (no herencia) - La comunicacion usa **patron Observer** (paneles no se conocen entre si) **Resultados en Simifactu:** - 83 modulos - ~112 lineas por archivo (target: 150 max) - 85-98% reutilizacion de codigo - 3x mas rapido crear ventanas nuevas --- ## 1. Principios Core ### 1.1 Panel Autonomo Cada panel: - Tiene su propio estado interno - Construye su propia UI - Maneja sus propios eventos - No conoce a otros paneles - Se comunica via DataManager (observer) ### 1.2 Composicion vs Herencia ``` MAL: WindowA hereda de BaseWindow y override metodos BIEN: WindowA compone PanelX + PanelY + PanelZ ``` ### 1.3 Single Source of Truth DataManager es el hub central: - Todas las entidades pasan por DataManager - Paneles observan cambios - Sin comunicacion directa panel-a-panel --- ## 2. Patrones de Composicion ### 2.1 Vertical Composite (2 paneles) ``` ┌─────────────────────┐ │ Top Panel │ ├─────────────────────┤ │ Bottom Panel │ └─────────────────────┘ ``` **Uso**: Division simple top/bottom ### 2.2 Center Composite (3 paneles) ``` ┌─────────────────────┐ │ WHO Detail │ ├─────────────────────┤ │ Document Detail │ ├─────────────────────┤ │ Lines │ └─────────────────────┘ ``` **Uso**: Detalle de documento (master-detail-lines) ### 2.3 Config Composite (HSplit + dynamic) ``` ┌────────────┬────────────────────┐ │ │ │ │ Categories │ Dynamic Editor │ │ (nav) │ (table/form/etc) │ │ │ │ └────────────┴────────────────────┘ ``` **Uso**: Configuracion (lista izq + editor der cambiante) ### 2.4 Docs Composite (2 columnas) ``` ┌────────────────┬───────────────────┐ │ │ WHO Compact │ │ Doc List ├───────────────────┤ │ │ Document Detail │ └────────────────┴───────────────────┘ ``` **Uso**: Lista de documentos con preview --- ## 3. Interfaz AutonomousPanel ```go type AutonomousPanel interface { // Identidad GetPanelID() string // "who_list", "doc_detail" GetPanelType() string // "list", "detail", "composite" GetEntityType() string // "WHO", "Document", "Line" // Estado GetSelectedEntity() interface{} SetSelectedEntity(interface{}) error // UI BuildUI() fyne.CanvasObject Refresh() // Lifecycle Initialize() error Destroy() error } ``` --- ## 4. Patron Observer ### 4.1 Registro ```go // Panel se registra para recibir cambios de "Document" dataManager.AddObserverForType("Document", myPanel) ``` ### 4.2 Notificacion ```go // Cuando cambia un documento dataManager.NotifyObserversWithChange(NewDataChange( entityType: "Document", changeType: "UPDATE", data: doc, )) ``` ### 4.3 Recepcion ```go // Panel responde al cambio func (p *MyPanel) OnDataChanged(timestamp time.Time) { // Refrescar UI si afecta mis datos } ``` ### 4.4 Dual Notification ```go OnDataChanged() // Cambios UI locales OnDataChangedDB() // Cambios en DB (invalidar cache + recargar) ``` --- ## 5. Aplicacion a zcatgui ### 5.1 Propuesta de Interfaz ```zig /// Panel autonomo pub const AutonomousPanel = struct { /// Identificador unico id: []const u8, /// Tipo de panel panel_type: PanelType, /// Build UI - retorna commands build_fn: *const fn(*Context) void, /// Refresh callback refresh_fn: ?*const fn(*AutonomousPanel) void = null, /// Estado interno (opaco) state: *anyopaque, /// Destructor deinit_fn: ?*const fn(*AutonomousPanel) void = null, }; pub const PanelType = enum { list, // Lista de items detail, // Detalle de un item table, // Tabla editable composite, // Compuesto de otros paneles }; ``` ### 5.2 Patron Composite ```zig /// Composite vertical (2 paneles) pub const VerticalComposite = struct { top: *AutonomousPanel, bottom: *AutonomousPanel, split_ratio: f32 = 0.5, pub fn build(self: *VerticalComposite, ctx: *Context) void { const split = widgets.split.vsplit(ctx, self.split_ratio); self.top.build_fn(ctx.withArea(split.first)); self.bottom.build_fn(ctx.withArea(split.second)); } }; /// HSplit composite (lista + detalle) pub const HSplitComposite = struct { left: *AutonomousPanel, right: *AutonomousPanel, split_ratio: f32 = 0.3, pub fn build(self: *HSplitComposite, ctx: *Context) void { const split = widgets.split.hsplit(ctx, self.split_ratio); self.left.build_fn(ctx.withArea(split.first)); self.right.build_fn(ctx.withArea(split.second)); } }; ``` ### 5.3 DataManager Simplificado ```zig /// Observer callback pub const DataObserver = struct { on_data_changed: ?*const fn(entity_type: []const u8, data: ?*anyopaque) void = null, context: ?*anyopaque = null, }; /// Data manager (singleton) pub const DataManager = struct { observers: std.StringHashMap(std.ArrayList(DataObserver)), pub fn addObserver(self: *DataManager, entity_type: []const u8, observer: DataObserver) void { // ... } pub fn notifyChange(self: *DataManager, entity_type: []const u8, data: ?*anyopaque) void { if (self.observers.get(entity_type)) |observers| { for (observers.items) |obs| { if (obs.on_data_changed) |callback| { callback(entity_type, data); } } } } }; ``` --- ## 6. Ejemplo de Uso ### 6.1 Definir Panel Simple ```zig const CustomerListPanel = struct { state: ListState, customers: []Customer, pub fn build(ctx: *Context) void { widgets.panel.panel(ctx, "Customers", .{}); const result = widgets.list.list(ctx, &state, customers); if (result.selection_changed) { dataManager.notifyChange("Customer", customers[result.selected.?]); } } }; ``` ### 6.2 Definir Panel Detalle ```zig const CustomerDetailPanel = struct { state: FormState, customer: ?Customer, pub fn build(ctx: *Context) void { widgets.panel.panel(ctx, "Customer Detail", .{}); if (self.customer) |c| { widgets.label.label(ctx, c.name); widgets.label.label(ctx, c.email); } else { widgets.label.label(ctx, "Select a customer"); } } pub fn onDataChanged(entity_type: []const u8, data: ?*anyopaque) void { if (std.mem.eql(u8, entity_type, "Customer")) { self.customer = @ptrCast(data); } } }; ``` ### 6.3 Componer en Ventana ```zig pub fn main() !void { var list_panel = CustomerListPanel{}; var detail_panel = CustomerDetailPanel{}; // Registrar observers dataManager.addObserver("Customer", .{ .on_data_changed = detail_panel.onDataChanged, }); // Componer var composite = HSplitComposite{ .left = &list_panel.asPanel(), .right = &detail_panel.asPanel(), .split_ratio = 0.3, }; // Main loop while (running) { composite.build(&ctx); // ... } } ``` --- ## 7. Plan de Implementacion ### Fase 1: Panel Interface (2 horas) - Definir AutonomousPanel trait/interface - Helpers para crear paneles ### Fase 2: Composites Basicos (2 horas) - VerticalComposite - HSplitComposite - TabComposite (tabs) ### Fase 3: DataManager Simple (3 horas) - Observer registration - Notify observers - Entity type filtering ### Fase 4: Ejemplo Completo (2 horas) - Master-detail demo - Config-like layout demo **Total estimado: 9 horas** --- ## 8. Beneficios Esperados 1. **Modularidad**: Paneles independientes, faciles de testear 2. **Reutilizacion**: Mismo panel en multiples ventanas 3. **Mantenibilidad**: Bugs aislados a paneles especificos 4. **Escalabilidad**: Nuevas ventanas = componer paneles existentes 5. **Decoupling**: Sin dependencias directas entre paneles --- ## 9. Referencias - `/mnt/cello2/arno/re/recode/go/simifactu-fyne/docs/arquitectura_canonica/01_filosofia_lego.md` - `/mnt/cello2/arno/re/recode/go/simifactu-fyne/docs/AUDITORIA_ARQUITECTURA_LEGO_V3_SEPT16.md` - `/mnt/cello2/arno/re/recode/go/simifactu-fyne/internal/ui/panels_v3/panels/`