# DEVELOPMENT.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. --- ## ISTRUZIONI OBBLIGATORIE PER CLAUDE ### Auto-Aggiornamento DEVELOPMENT.md **OBBLIGATORIO:** Claude DEVE aggiornare questo file DEVELOPMENT.md ogni volta che: 1. **Viene completato un task significativo** (fix, nuova feature, refactoring importante) 2. **Viene risolto un problema tecnico** che potrebbe ripresentarsi in futuro 3. **Si scopre un pattern/workaround** importante da ricordare 4. **Termina una sessione di lavoro** - aggiornare "Quick Start - Session Recovery" **Cosa aggiornare:** - **Sezione "Quick Start - Session Recovery":** - Aggiornare "Ultima sessione" con data corrente - Spostare lavoro completato da "ultima sessione" a "sessioni precedenti" - Aggiungere nuovi task completati alla lista - Aggiornare "Prossimi task prioritari" (spuntare completati, aggiungere nuovi) - **Sezione "Problemi Risolti (da ricordare)":** - Aggiungere OGNI problema tecnico risolto con: - Descrizione del problema - Causa root - Soluzione implementata - File coinvolti - **Checklist:** - Aggiornare checkbox `[x]` per task completati - Aggiungere nuovi task se scoperti durante il lavoro **Formato per nuovi problemi risolti:** ```markdown XX. **Nome Problema (FIX/IMPLEMENTATO DATA):** - **Problema:** Descrizione breve - **Causa:** Perché succedeva - **Soluzione:** Come è stato risolto - **File:** File modificati ``` **NON dimenticare:** Questo file è la memoria persistente tra sessioni. Se non viene aggiornato, il lavoro fatto andrà perso e dovrà essere riscoperto. --- ## Quick Start - Session Recovery **Ultima sessione:** 29 Novembre 2025 (pomeriggio) **Stato progetto:** Migrazione Oracle APEX → .NET + React TypeScript in corso **Lavoro completato nell'ultima sessione:** - **NUOVA FEATURE: Gestione Inventario (Frontend)** - COMPLETATO - **Obiettivo:** Interfaccia utente per la gestione completa degli inventari fisici - **Frontend implementato:** - `InventoryListPage.tsx` - Lista inventari con stato, filtri e indicatori di progresso - `InventoryFormPage.tsx` - Form per creazione e modifica testata inventario (con gestione stati) - `InventoryCountPage.tsx` - Pagina di conteggio con griglia editabile, calcolo differenze live - Aggiornati `routes.tsx` e `pages/index.ts` per includere le nuove rotte - **Funzionalità:** - Creazione inventari (Completo, Parziale per categoria/magazzino) - Workflow stati: Bozza -> In Corso -> Completato -> Confermato - Avvio inventario: generazione automatica righe basata su giacenza teorica - Conteggio: inserimento quantità rilevate, evidenziazione differenze - Conferma: generazione automatica movimenti di rettifica (positivo/negativo) - **Integrazione:** - Utilizza `inventoryService` per comunicare con `InventoryController` - Gestione date con `dayjs` - UI coerente con Material-UI e DataGrid - **FIX: Tasto Inventario in Dashboard Magazzino** - RISOLTO - **Problema:** Il tasto "Inventario" nelle azioni rapide portava a una pagina 404 (`/warehouse/inventories/new`) - **Causa:** Errore nel hook `useWarehouseNavigation` che usava il plurale `inventories` invece del singolare `inventory` definito nelle rotte - **Soluzione:** Corretti i percorsi in `useWarehouseNavigation.ts` per corrispondere a `routes.tsx` - **File modificati:** `frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts` - **FIX: Campo Codice Readonly e Codice Alternativo** - COMPLETATO - **Obiettivo:** Il campo "Codice" deve essere sempre auto-generato (non modificabile), aggiungere campo "Codice Alternativo" opzionale - **Backend modificato:** - `WarehouseArticle.cs` - Aggiunto `AlternativeCode` - `WarehouseLocation.cs` - Aggiunto `AlternativeCode` - `WarehouseArticleCategory.cs` - Aggiunto `AlternativeCode` - `Cliente.cs` - Aggiunto `Codice` e `CodiceAlternativo` - `Articolo.cs` - Aggiunto `CodiceAlternativo` - `AutoCodeService.cs` - Aggiornato per gestire unicità `Cliente.Codice` - `ClientiController.cs` - Auto-generazione codice alla creazione - `ArticoliController.cs` - Auto-generazione codice alla creazione - `WarehouseArticlesController.cs` - Fix DTOs per rimuovere `Code` obbligatorio: - `CreateArticleDto` - Rimosso `Code`, aggiunto `AlternativeCode` - `UpdateArticleDto` - Rimosso `Code`, aggiunto `AlternativeCode` - `ArticleDto` - Aggiunto `AlternativeCode` - `MapFromDto` - Rimossa assegnazione Code (generato da service) - `UpdateFromDto` - Rimossa modifica Code (immutabile) - Migration `AddAlternativeCodeFields` creata e applicata - **Frontend modificato:** - `frontend/src/modules/warehouse/types/index.ts` - Aggiunto `alternativeCode` ai DTOs - `frontend/src/types/index.ts` - Aggiunto `codice` e `codiceAlternativo` a Cliente/Articolo - `ArticleFormPage.tsx` - Campo Codice readonly con "(Generato al salvataggio)" - `WarehouseLocationsPage.tsx` - Campo Codice readonly - `ArticoliPage.tsx` - Campo Codice readonly - `ClientiPage.tsx` - Campo Codice readonly - **Comportamento UI:** - In creazione: campo Codice mostra "(Generato al salvataggio)" in corsivo - In modifica: campo Codice mostra il valore reale, sempre disabled - Campo "Codice Alternativo" sempre modificabile (opzionale) **Lavoro completato nelle sessioni precedenti (30 Novembre 2025):** - **NUOVA FEATURE: Sistema Codici Automatici Configurabili** - COMPLETATO - **Obiettivo:** Sistema admin per configurare la generazione automatica di codici (articoli, magazzini, movimenti, ecc.) - **Backend implementato:** - `AutoCode.cs` - Entity con pattern configurabile, prefisso, sequenza, reset periodico - `AutoCodeService.cs` - Logica business (generazione, preview, reset, validazione pattern) - `AutoCodesController.cs` - API REST complete - Migration EF Core `AddAutoCodeSystem` - Seed automatico configurazioni default per tutte le entità - **Frontend implementato:** - `autoCode.ts` - Types TypeScript - `autoCodeService.ts` - API calls - `AutoCodesAdminPage.tsx` - Pagina admin con tabella configurazioni, dialog modifica, guida pattern - **Pattern supportati:** - `{PREFIX}` - Prefisso configurabile - `{SEQ:n}` - Sequenza numerica con n cifre - `{YYYY}`, `{YY}` - Anno - `{MM}`, `{DD}` - Mese, Giorno - **Funzionalità:** - Configurazione per entità (warehouse_article, stock_movement, cliente, evento, ecc.) - Reset sequenza annuale o mensile automatico - Preview prossimo codice senza incremento - Reset manuale sequenza - Abilitazione/disabilitazione per entità - Raggruppamento per modulo nell'UI - **API Endpoints:** - `GET /api/autocodes` - Lista configurazioni - `GET /api/autocodes/{entityCode}` - Dettaglio - `GET /api/autocodes/{entityCode}/preview` - Anteprima prossimo codice - `POST /api/autocodes/{entityCode}/generate` - Genera nuovo codice - `PUT /api/autocodes/{id}` - Aggiorna configurazione - `POST /api/autocodes/{entityCode}/reset-sequence` - Reset sequenza - `GET /api/autocodes/placeholders` - Lista placeholder disponibili - **File principali:** - `src/Apollinare.Domain/Entities/AutoCode.cs` - `src/Apollinare.API/Services/AutoCodeService.cs` - `src/Apollinare.API/Controllers/AutoCodesController.cs` - `frontend/src/pages/AutoCodesAdminPage.tsx` **Lavoro completato nelle sessioni precedenti (29 Novembre 2025 notte):** - **NUOVA FEATURE: Modulo Magazzino (warehouse)** - COMPLETATO - **Backend implementato:** - Entities complete in `/src/Apollinare.Domain/Entities/Warehouse/`: - `WarehouseLocation.cs` - Magazzini fisici/logici con Type enum (Physical, Virtual, Transit) - `WarehouseArticleCategory.cs` - Categorie gerarchiche con Color, Icon, Level, FullPath - `WarehouseArticle.cs` - Articoli con batch/serial management flags, valorizzazione - `ArticleBatch.cs` - Tracciabilità lotti con scadenza - `ArticleSerial.cs` - Tracciabilità numeri seriali - `StockLevel.cs` - Giacenze per articolo/magazzino/batch - `StockMovement.cs` - Movimenti (Inbound/Outbound/Transfer/Adjustment) - `StockMovementLine.cs` - Righe movimento - `MovementReason.cs` - Causali movimento - `ArticleBarcode.cs` - Multi-barcode support - `StockValuation.cs` + `StockValuationLayer.cs` - Valorizzazione periodo e layer FIFO/LIFO - `InventoryCount.cs` + `InventoryCountLine.cs` - Inventari fisici - Service completo `WarehouseService.cs` con: - CRUD articoli, categorie, magazzini - Gestione movimenti (carico/scarico/trasferimento/rettifica) - Conferma movimenti con aggiornamento giacenze - Calcolo valorizzazione (WeightedAverage, FIFO, LIFO, StandardCost) - Gestione partite e seriali - Controllers REST in `/src/Apollinare.API/Modules/Warehouse/Controllers/`: - `WarehouseLocationsController.cs` - `WarehouseArticlesController.cs` - `WarehouseArticleCategoriesController.cs` - `StockMovementsController.cs` - `StockLevelsController.cs` - Seed dati default (magazzino principale + transito, categorie base, causali) - **CONFIGURAZIONE: EF Core Code First Migrations** - COMPLETATO - **Problema:** Le tabelle venivano create manualmente invece che con migrations EF Core - **Soluzione implementata:** - Sostituito `db.Database.EnsureCreated()` con `db.Database.MigrateAsync()` in `Program.cs` - Creata migration `InitialCreate` con tutte le tabelle (sistema + moduli + warehouse) - Le migrations vengono applicate **automaticamente all'avvio** dell'applicazione - Logging delle migrations pendenti prima dell'applicazione - **Comandi per future migrations:** ```bash # Creare nuova migration dotnet ef migrations add NomeMigration \ --project src/Apollinare.Infrastructure \ --startup-project src/Apollinare.API # L'applicazione è AUTOMATICA all'avvio - non serve "dotnet ef database update" ``` - **File modificati:** `Program.cs`, `src/Apollinare.Infrastructure/Migrations/` **Lavoro completato nelle sessioni precedenti (29 Novembre 2025 sera):** - **NUOVA FEATURE: Sistema Moduli Applicativi** - COMPLETATO (continuazione) - **Obiettivo:** Sistema di modularizzazione per gestire licenze, abbonamenti e funzionalità dinamiche - **Backend implementato:** - `AppModule.cs` - Entity per definizione moduli (code, name, description, icon, basePrice, dependencies, etc.) - `ModuleSubscription.cs` - Entity per stato abbonamento (isEnabled, subscriptionType, startDate, endDate, autoRenew) - `ModuleService.cs` - Logica business (enable/disable, check dipendenze, gestione scadenze, cache) - `ModulesController.cs` - API REST complete con DTOs - Tabelle SQLite create manualmente (AppModules, ModuleSubscriptions) - Seed automatico 5 moduli: warehouse, purchases, sales, production, quality - **Frontend implementato:** - `module.ts` - Types TypeScript (ModuleDto, SubscriptionDto, enums, helpers) - `moduleService.ts` - API calls - `ModuleContext.tsx` - React Context con hooks (useModules, useModuleEnabled, useActiveModules) - `ModuleGuard.tsx` - Componente per proteggere route - `ModulesAdminPage.tsx` - Pagina amministrazione moduli con cards, toggle, dettagli subscription - `ModulePurchasePage.tsx` - Pagina acquisto/attivazione modulo con selezione piano - **Integrazione:** - `App.tsx` - ModuleProvider wrappa l'app, route /modules e /modules/purchase/:code - `Layout.tsx` - Voce menu "Moduli" aggiunta - **API Endpoints:** - `GET /api/modules` - Lista tutti i moduli - `GET /api/modules/active` - Solo moduli attivi - `GET /api/modules/{code}` - Dettaglio modulo - `GET /api/modules/{code}/enabled` - Verifica stato - `PUT /api/modules/{code}/enable` - Attiva modulo - `PUT /api/modules/{code}/disable` - Disattiva modulo - `GET /api/modules/subscriptions` - Lista subscription - `PUT /api/modules/{code}/subscription` - Aggiorna subscription - `POST /api/modules/{code}/subscription/renew` - Rinnova - `GET /api/modules/expiring` - Moduli in scadenza - **Funzionalità:** - Gestione dipendenze tra moduli (es. purchases richiede warehouse) - Blocco disattivazione se altri moduli dipendono - Abbonamenti mensili/annuali con date scadenza - Auto-rinnovo opzionale - Cache con invalidazione automatica - Alert moduli in scadenza **Lavoro completato nelle sessioni precedenti (29 Novembre 2025 mattina):** - **NUOVA FEATURE: Sistema Pannelli Drag-and-Drop con Sidebar Ridimensionabili** - COMPLETATO - **Obiettivo:** I pannelli del report designer devono poter essere trascinati tra sidebar sinistra e destra, con ridimensionamento orizzontale a livello sidebar - **Architettura implementata:** - `SidebarDropZone.tsx` - Contenitore sidebar con: - Drop zone per drag-and-drop pannelli (HTML5 Drag and Drop API) - Handle di resize orizzontale sul bordo interno - Indicatore visivo durante il drag - Props: `position`, `width`, `onWidthChange`, `onPanelDrop`, `panelIds` - `ResizablePanel.tsx` - Pannello individuale con: - Header trascinabile (`draggable="true"`) - Resize verticale (flex) tra pannelli nella stessa sidebar - Stato collassato con icona e titolo verticale - `width: 100%` - si adatta alla larghezza della sidebar - `usePanelLayout.ts` - Hook per gestione stato: - `sidebarWidths: { left: number, right: number }` per larghezza sidebar - `panels[]` con `flex` per distribuzione verticale - `movePanelToPosition()` redistribuisce flex quando un pannello viene droppato - Persistenza in localStorage (versione 3) - **Comportamento:** - Trascinando l'header di un pannello si può spostarlo tra sidebar - Quando un pannello viene droppato, tutti i pannelli nella sidebar di destinazione ottengono `flex: 1` (distribuzione equa) - Il resize orizzontale agisce sulla sidebar intera, non sui singoli pannelli - I pannelli adottano automaticamente la larghezza della sidebar - **Layout Full-Width:** - `Layout.tsx` modificato per Report Editor: `p: 0`, `overflow: hidden`, `width: 100vw - drawerWidth` - `ReportEditorPage.tsx`: `flex: 1`, `minHeight: 0` per espansione corretta - Canvas container: `width: 100%` per occupare tutto lo spazio disponibile - **FIX: Layout Report Designer Non Full-Width** - COMPLETATO - **Problema:** Il report designer non occupava tutta la larghezza del browser - **Causa:** Il Layout usava `width: 100%` invece di `100vw` e il padding non veniva rimosso per il report editor - **Soluzione:** - `Layout.tsx`: Usato `width: 100vw` e `calc(100vw - drawerWidth)` invece di `100%` - `Layout.tsx`: Padding condizionale `p: 0` per route `/report-editor` - `Layout.tsx`: `overflow: hidden` e `height: 100vh` per contenitore main - `ReportEditorPage.tsx`: Rimossi margini negativi, usato `flex: 1` e `minHeight: 0` - `EditorCanvas.tsx`: Modifiche utente per larghezza canvas - **File modificati:** `Layout.tsx`, `ReportEditorPage.tsx`, `EditorCanvas.tsx` - **NUOVA FEATURE: Panning e Zoom stile Draw.io nel Report Designer** - COMPLETATO (sessione precedente) - **Problema:** Le scorciatoie da tastiera (Ctrl+X, Ctrl+C, etc.) venivano intercettate dal browser invece che dalla pagina - **Soluzione:** Riscritto completamente l'handler delle scorciatoie con: - Controllo se il focus è su campi input/textarea/contenteditable - Nuovo metodo `isTextEditing()` esposto da EditorCanvasRef per verificare editing testo nel canvas - `e.preventDefault()` per bloccare comportamento browser - **Scorciatoie implementate:** - `Ctrl+Z` - Annulla - `Ctrl+Y` - Ripeti - `Ctrl+S` - Salva - `Ctrl+X` - Taglia - `Ctrl+C` - Copia - `Ctrl+V` - Incolla - `Ctrl+D` - Duplica - `Ctrl+A` - Seleziona tutto - `Ctrl+L` - Blocca/Sblocca elemento - `Ctrl+G` - Raggruppa - `Ctrl+Shift+G` - Separa - `Ctrl+]` - Porta avanti - `Ctrl+Shift+]` - Porta in primo piano - `Ctrl+[` - Porta indietro - `Ctrl+Shift+[` - Porta in fondo - `Delete/Backspace` - Elimina elemento - `Escape` - Deseleziona - **File modificati:** - `EditorCanvas.tsx` - Aggiunto `isTextEditing()` a `EditorCanvasRef` - `ReportEditorPage.tsx` - Importato `EditorCanvasRef`, aggiunto `canvasRef`, riscritto `useEffect` delle scorciatoie - **FIX: Rimossa Toolbar Contestuale che causava Layout Shift** - COMPLETATO - **Problema:** Quando si selezionava un oggetto nel canvas, appariva una toolbar aggiuntiva sotto quella principale che causava uno scroll della pagina - **Soluzione:** Rimossa completamente la toolbar contestuale - le proprietà degli oggetti vengono gestite solo dal `PropertiesPanel` sulla destra - **Rimosso da EditorToolbar.tsx:** - Sezione "Contextual Toolbar Row" (desktop) - Sezione "Contextual toolbar for text/shape" (tablet) - Funzioni: `handleTextFormat()`, `handleTextAlign()`, `renderContextualToolbar()` - Componente `ColorPickerButton` - Costante `COLOR_PRESETS` - Props: `selectedElement`, `onUpdateSelectedElement` - Import non più usati: `TextField`, `ToggleButton`, `ToggleButtonGroup`, icone formattazione - **File modificati:** - `EditorToolbar.tsx` - Rimossa toolbar contestuale e codice correlato - `ReportEditorPage.tsx` - Rimossi props `selectedElement` e `onUpdateSelectedElement` dalla chiamata a EditorToolbar - **NUOVA FEATURE: Selezione Multipla nel Report Editor** - COMPLETATO - Implementato sistema di selezione multipla personalizzato (senza usare ActiveSelection di Fabric.js che causava riposizionamento oggetti) - **Selezione con rettangolo di trascinamento**: trascinando sul canvas vuoto appare rettangolo blu tratteggiato, al rilascio seleziona tutti gli oggetti che intersecano - **Shift+click**: aggiunge/rimuove oggetti dalla selezione - **Spostamento multiplo**: quando più oggetti sono selezionati, trascinandone uno si spostano tutti insieme - **Feedback visivo**: oggetti selezionati mostrano bordo blu (#1976d2) e ombra - **Gestione corretta degli eventi**: i ref (`selectedElementIdsRef`, `onSelectElementRef`, etc.) evitano stale closures negli event handler - **File modificati:** - `EditorCanvas.tsx` - Nuovi handler `handleMouseDown`, `handleMouseUp`, logica selezione multipla, refs per valori correnti - `ReportEditorPage.tsx` - Cambiato `selectedElementId: string | null` → `selectedElementIds: string[]`, aggiornati tutti i riferimenti **Lavoro completato nelle sessioni precedenti (28 Novembre 2025 notte):** - **NUOVA FEATURE: Sincronizzazione Real-Time Efficiente** - COMPLETATO - **Prima:** Al salvataggio veniva inviata solo una notifica `DataSaved`, l'altra sessione ricaricava il template dal server (lento) - **Dopo:** Al salvataggio viene inviato l'intero template via SignalR (`BroadcastTemplateSync`), l'altra sessione lo applica direttamente (istantaneo) - **Compressione automatica** per template > 10KB usando gzip via browser's CompressionStream API - **Version tracking** per gestione conflitti (ignora template con versione più vecchia) - Nuovo metodo Hub: `BroadcastTemplateSync(roomKey, templateJson, version, compressed)` - Nuovo evento: `TemplateSync` con decompressione automatica lato client - **FIX: Limite messaggio SignalR** - Aumentato `MaximumReceiveMessageSize` a 1MB in `Program.cs` (default era 32KB) - **File modificati:** - `CollaborationHub.cs` - Aggiunto `BroadcastTemplateSync` e `TemplateSyncMessage` con campo `Compressed` - `collaboration.ts` - Aggiunto `broadcastTemplateSync()`, utilities compressione/decompressione (`compressString`, `decompressString`), handler `TemplateSync` - `CollaborationContext.tsx` - Esposto `broadcastTemplateSync` e `onTemplateSync` - `ReportEditorPage.tsx` - Sostituito `sendDataSaved()` con `broadcastTemplateSync()`, aggiunto handler per applicare template ricevuti - `Program.cs` - Configurato SignalR con `MaximumReceiveMessageSize = 1MB` - **FIX: Auto-Save Event-Based con Debounce** - COMPLETATO - Riscritto auto-save per triggerare ad ogni modifica (non a intervalli) - Debounce di 500ms per evitare salvataggi multipli durante editing rapido - Usa `useRef` per `saveMutation`, `template`, `templateInfo` per evitare re-creazione del timeout - Dipendenze effect: solo `autoSaveEnabled`, `isNew`, `hasUnsavedChanges`, `templateHistory.undoCount` - Il check `isPending` avviene al momento dell'esecuzione del timeout, non come dipendenza **Lavoro completato nelle sessioni precedenti (28 Novembre 2025 pomeriggio - sera):** - **NUOVA FEATURE: Sistema Collaborazione Real-Time Globale** - COMPLETATO - Migrato da sistema report-specific a sistema globale per tutta l'app - `CollaborationHub.cs` - Hub SignalR generico per qualsiasi entità/pagina - `collaboration.ts` - Service singleton per gestione connessione - `CollaborationContext.tsx` - React Context con hooks (`useCollaboration`, `useCollaborationRoom`) - `CollaborationIndicator.tsx` - Indicatore globale nella UI - Room-based collaboration con formato `{entityType}:{entityId}` - **FIX: Incompatibilità versione SignalR** - Rimosso package `Microsoft.AspNetCore.SignalR.Common` v10.0.0 incompatibile con .NET 9, downgrade client `@microsoft/signalr` a v8.0.7 - **FIX: Auto-save non funzionante** - Usati `useRef` per evitare re-run dell'effect causato da `saveMutation` nelle dependencies **Lavoro completato nelle sessioni precedenti (28 Novembre 2025 tarda notte):** - **NUOVA FEATURE: Toolbar Report Designer Migliorata Drasticamente** - COMPLETATO - Design moderno stile Canva/Figma con gradient buttons e animazioni fluide - **Sezioni etichettate** su desktop (INSERISCI, MODIFICA, CRONOLOGIA, VISTA, ZOOM, PAGINA) - **Toolbar contestuale** dinamica che appare quando un elemento è selezionato: - Per testo: formattazione (grassetto, corsivo, sottolineato), allineamento, color picker - Per forme/linee: color picker riempimento/bordo, spessore bordo - Per immagini: indicatore mantieni proporzioni - Mostra tipo elemento, nome e dimensioni in tempo reale - **Color Picker integrato** con palette di 20 colori predefiniti - **Indicatore stato salvataggio** visivo: Salvato (verde), Non salvato (arancione), Salvataggio... (spinner) - **Badge** sul pulsante Snap che mostra quante opzioni sono attive - **Zoom esteso** fino al 300% con pulsante "Adatta alla finestra" - Pannello scorciatoie tastiera ampliato - Aggiunto `textDecoration` a `AprtStyle` per supportare sottolineato - **FIX: Indicatore "Non Salvato" errato** - RISOLTO - Prima usava `canUndo` che indicava solo presenza di history - Ora usa `undoCount` confrontato con `lastSavedUndoCount` per tracking accurato - **NUOVA FEATURE: Auto-Save con Toggle** - COMPLETATO - Salvataggio automatico dopo 1 secondo di inattività (debounce) - **Abilitato di default** - Toggle nella toolbar (icona AutoMode) per attivare/disattivare - Quando auto-save è attivo, il pulsante "Salva" manuale è nascosto - Quando auto-save è disattivo, il pulsante "Salva" appare - Non si attiva per template nuovi (richiede primo salvataggio manuale) - Non si attiva durante salvataggio in corso **Lavoro completato nelle sessioni precedenti (28 Novembre 2025 notte):** - **NUOVA FEATURE: Responsive Design Completo** - COMPLETATO - Tutta l'applicazione è ora responsive per mobile, tablet e desktop - Breakpoints: mobile (<600px), tablet (600-900px), desktop (>900px) - **Layout.tsx**: Sidebar collassata con icone su tablet, drawer mobile - **ReportTemplatesPage**: Header stackato su mobile, FAB per nuovo template, dialog fullScreen - **ReportEditorPage**: BottomNavigation + SwipeableDrawer (70vh) per pannelli su mobile, auto-zoom - **EditorToolbar**: 3 varianti (mobile compatta con riga collassabile, tablet media, desktop completa) - **Pannelli laterali** (DataBindingPanel, PropertiesPanel, PageNavigator): larghezza piena su mobile - **DatasetSelector**: Header collassabile su mobile - **PreviewDialog**: fullScreen su mobile con navigazione step-by-step (dataset → entity) - **ImageUploadDialog**: fullScreen su mobile, area drag-drop ottimizzata, bottoni stacked **Lavoro completato nelle sessioni precedenti (28 Novembre 2025 sera):** - **FIX: Variabili Globali Report ({{$pageNumber}}, {{$totalPages}}, ecc.)** - RISOLTO - Le variabili speciali ora vengono correttamente risolte nel PDF finale - Aggiunta classe `PageContext` per passare numero pagina e totale pagine durante il rendering - Propagato `PageContext` attraverso tutta la catena di rendering (bitmap, text, binding resolution) - `ResolveBindingPath()` ora restituisce valori reali invece di placeholder **Lavoro completato nelle sessioni precedenti (28 Novembre 2025):** - **NUOVA FEATURE: Gestione Multi-Pagina nel Report Designer** - Completata - Nuovo tipo `AprtPage` per definire pagine con impostazioni individuali (size, orientation, margins, backgroundColor) - Ogni elemento ha `pageId` per assegnazione a pagina specifica - Nuovo componente `PageNavigator.tsx` - sidebar con lista pagine, context menu, rinomina, duplica, elimina, riordina - Navigazione rapida pagine in `EditorToolbar.tsx` (pulsanti prev/next, indicatore "X / Y") - `PropertiesPanel.tsx` aggiornato per mostrare/modificare impostazioni pagina corrente - Backend `ReportGeneratorService.cs` genera PDF multi-pagina correttamente - Migrazione automatica template legacy (elementi senza pageId assegnati a prima pagina) - **FIX CRITICO: Rotazione oggetti nel PDF** - Gli oggetti ruotati nel canvas ora vengono posizionati correttamente nel PDF generato - Implementata la formula Fabric.js per calcolare il centro di rotazione - Coordinate corrette per `originX='left'`, `originY='top'` **Lavoro completato nelle sessioni precedenti:** - Sistema Report PDF con editor visuale (98% completato) - Fabric.js v6 canvas editor funzionante - Multi-dataset support con preview - **FIX: Data binding PDF ora funzionante** - I campi dati vengono risolti correttamente - **FIX: Formattazione campi** - Date, valute, numeri formattati correttamente nel PDF - Sistema Virtual Dataset implementato (CRUD completo) - SignalR real-time updates funzionante - **Context Menu ricco** - Tasto destro con opzioni complete stile Canva (copia, taglia, incolla, layer, allineamento, trasformazioni) - **FIX: Posizionamento assoluto PDF FUNZIONANTE** - SVG con viewBox in mm, coordinate corrette - **FIX: Immagini nel PDF** - Data URI, API URL e risorse embedded ora renderizzate correttamente - **FIX: Font size nel PDF** - Conversione corretta da px screen a mm per SVG **Per riprendere il lavoro sui Report:** 1. Vai alla sezione "Report PDF System - Implementation Details" più sotto 2. Consulta la "Checklist Completamento Report System" per vedere cosa manca 3. I file principali sono: - Backend: `/src/Apollinare.API/Controllers/ReportsController.cs` - Frontend: `/frontend/src/pages/ReportEditorPage.tsx` - Canvas: `/frontend/src/components/reportEditor/EditorCanvas.tsx` - **Context Menu:** `/frontend/src/components/reportEditor/ContextMenu.tsx` - **PDF Generator:** `/src/Apollinare.API/Services/Reports/ReportGeneratorService.cs` - **Page Navigator:** `/frontend/src/components/reportEditor/PageNavigator.tsx` **Prossimi task prioritari:** **MODULI BUSINESS (PRIORITÀ ALTA):** 1. [x] **Implementare modulo Magazzino (warehouse)** - COMPLETATO (backend) - Backend: Entities, Service, Controllers, API completi - Manca: Frontend (pagine React per gestione articoli, movimenti, giacenze) 2. [x] **Frontend modulo Magazzino** - Pagine React per warehouse (Articoli, Movimenti, Giacenze, Inventario) 3. [ ] **Implementare modulo Acquisti (purchases)** - Dipende da Magazzino 4. [ ] **Implementare modulo Vendite (sales)** - Dipende da Magazzino 5. [ ] **Implementare modulo Produzione (production)** - Dipende da Magazzino 6. [ ] **Implementare modulo Qualità (quality)** - Indipendente **Report System (completamento):** - [ ] Aggiungere rendering tabelle dinamiche per collection - [ ] Gestire sezioni header/footer ripetute su ogni pagina - [ ] UI per relazioni tra dataset multipli **NOTA:** Vedere sezione "Prossimi Passi: Implementazione Moduli Business" per dettagli architetturali e principi di personalizzazione. **Comandi utili (usa il Makefile!):** ```bash # IMPORTANTE: Usa sempre il Makefile per compilare e avviare l'applicazione make help # Mostra tutti i comandi disponibili make install # Installa tutte le dipendenze (NuGet + npm) make dev # Avvia backend e frontend in parallelo (dev mode) make build # Compila tutto per produzione make backend-run # Avvia solo backend (porta 5000) make frontend-run # Avvia solo frontend (porta 5173) make backend-watch # Backend con hot reload (dotnet watch) make clean # Pulisce artefatti di build make lint # Esegue ESLint sul frontend make check # Verifica prerequisiti installati (dotnet, node, npm) ``` **IMPORTANTE:** Dopo modifiche al codice, i servizi backend e frontend vanno **sempre riavviati** per vedere le modifiche: - Backend: fermare con `Ctrl+C` e rilanciare `make backend-run` - Frontend: in dev mode (`make frontend-run`) il hot-reload è automatico per la maggior parte delle modifiche, ma per modifiche strutturali (nuovi file, cambi a tipi, etc.) potrebbe essere necessario riavviare --- ## Project Overview This repository contains documentation for migrating the **Apollinare Catering & Banqueting Management Software** from Oracle APEX to .NET + React TypeScript. **Original Application:** - Oracle APEX 21.1.0 (Application ID: 112) - 56 pages, 302 items, 98 processes - Database: Oracle 18 XE (schema: APOLLINARECATERINGPROD) - Language: Italian **Target Stack:** - Backend: .NET (C#) - Frontend: React TypeScript (not Vue - note the user request mentions Vue but the actual target is React TypeScript) - Database: Oracle 18 XE (read-only access for analysis) ## Database Connection (Read-Only) **Connection Details:** - Database: Oracle 18 XE - Username: `apollinarecateringprod` - Password: `bmwmRaSBRT53Z2J8CCvYK45EPDyAJ4` - Database: `xepdb1` - Hostname: `apollinare` - Port: `1521` **Important:** This connection is READ-ONLY. Use it only to analyze schema, extract business logic from procedures/packages/functions, and understand data relationships. ## Application Architecture ### Core Business Domain: Event Catering Management The application manages the complete lifecycle of catering events from quote to execution, including: - Event creation and management - Client and location management - Inventory (articles) with image storage - Quote generation with complex calculations - Resource (staff) scheduling - Kitchen and setup reports - Multi-level authorization system ### Main Business Entities **Events (EVENTI)** - Central entity - Event details (date, location, client, event type) - Guest counts by type (adults, children, seated, buffet) - Status workflow: 0 (Scheda) → 10 (Preventivo/Quote) → 20 (Confermato/Confirmed) - Quote expiration tracking - Distance calculations for location **Event Details (1:N relationships):** - `EVENTI_DET_OSPITI` - Guest type breakdown - `EVENTI_DET_PREL` - Pick lists (articles needed for the event) - `EVENTI_DET_RIS` - Resource assignments (staff) - `EVENTI_DET_DEGUST` - Tasting event details - `EVENTI_ACCONTI` - Deposits/advances - `EVENTI_ALLEG` - Attachments - `EVENTI_ALTRICOSTI` - Other costs **Master Data:** - `ARTICOLI` - Articles/items with images (BLOB), quantities, coefficients - `TB_CODICI_CATEG` - Categories with calculation coefficients (COEFF_A/B/S) - `TB_TIPI_MAT` - Material types - `TB_TIPI_EVENTO` - Event types with meal classifications - `TB_TIPI_OSPITI` - Guest types - `CLIENTI` - Clients - `LOCATION` - Event locations - `RISORSE` - Resources (staff) with type classification ### Critical Business Logic in Database **Key Stored Procedures:** - `EVENTI_AGGIORNA_QTA_LISTA(p_event_id)` - Updates pick list quantities based on guest counts and coefficients - `EVENTI_AGGIORNA_TOT_OSPITI(p_event_id)` - Recalculates total guest count - `EVENTI_RICALCOLA_ACCONTI(p_event_id)` - Recalculates deposit amounts - `EVENTI_COPIA` - Event duplication functionality - `EVENTI_PREPARE` - Event preparation process **Key Functions:** - `F_GET_QTA_IMPEGNATA(cod_articolo, data)` - Returns committed quantity for an article on a specific date (inventory reservation) - `F_EVENTO_SCADUTO(data_scad, stato, ...)` - Checks if event quote has expired - `F_MAX_NUMERO_EVENTI_RAGGIUNTO(data)` - Enforces daily event limit - `F_USER_IN_ROLE(app_user, role)` - Role-based authorization - `STRING_TO_TABLE_ENUM(string, position, delimiter)` - Utility for string parsing **Important Views:** - `V_IMPEGNI_ARTICOLI` - Calculates article commitments across events (inventory availability) - `V_IMPEGNI_ARTICOLI_LOC` - Article commitments by location - `VW_CALENDARIO_EVENTI` - Calendar view of events ### Quantity Calculation Algorithm The application uses a sophisticated coefficient-based system: 1. **Coefficients** are defined at category level (`TB_CODICI_CATEG.COEFF_A/B/S`) 2. **Standard quantities** are stored per article (`ARTICOLI.QTA_STD_A/S/B`) 3. **Guest counts** by type determine multipliers (`EVENTI_DET_OSPITI`) 4. **Final quantities** calculated as: `Guest_Count × Coefficient × Standard_Qty` Types: A (Adulti/Adults), S (Seduti/Seated), B (Buffet) ### Authorization Model **5 Authorization Levels:** 1. **Admin_auth_schema** - Full admin access - Users: admin, monia, andrea, maria, sabrina, nicole, cucina, developer, elia.ballarani 2. **User Read/Write** - Controlled by `USERS_READONLY` table - `FLGWRITE` flag determines write access 3. **Consuntivi** - Access to financial summaries - Users from `GET_CONSUNTIVI_USERS` view 4. **Gestori** (Managers) - Manager-level permissions - Users from `GET_GESTORI_USERS` view 5. **Solo Admins** - Highest level - Only: admin, monia **Session Management:** - `SET_USER_READONLY` process runs before header on every page - Sets `APP_READ_ONLY` application item based on user permissions ### Page Structure (56 Pages) **Master Data Pages:** - Pages 2-3: Articles (list + form) - Pages 4-5: Categories - Pages 6-7: Types - Pages 17-18: Clients - Pages 15, 20: Locations - Page 31: Resources (staff) **Event Management:** - Page 1: Dashboard - Page 8: Event creation wizard - Page 9: Event list - Page 12: Calendar view - Pages 13-14: Event types - **Page 22: Main event form** (most complex - multiple interactive grids) - Page 27, 32: Tastings - Page 35: Event cards/confirmed cards - Page 48: Event templates **Reports:** - Page 16: Grid view - Page 25: Kitchen summary - Page 28: Cakes and extra costs - Page 30: Setup summary - Page 38: Resources summary - Page 39: Article commitments **Admin:** - Page 45: Data management - Page 46: Max events configuration - Page 47: Permissions - Page 49: Scheduled jobs - Page 50: Sent emails - Page 51: Pending emails ### External Integrations **JasperReports:** - Quote reports (preventivi) - Event cards (schede evento) - Kitchen summaries - Custom iframeObj.js wrapper for embedding reports **Email System:** - Mail queue (pages 50-51) - Background job processing (page 49) - Template-based notifications **Custom JavaScript:** - `ajaxUtils.js` - AJAX utilities for dynamic updates - `notifica(pText, pType)` - Dynamic notifications - `setSessionState(elemList, pCallback)` - Session state management - `ajaxExec(...)` - Generic AJAX execution - `execProcessAsync(...)` - Async process execution - `execQueryAsync(...)` - Async query execution ### Migration Considerations **Complex Features Requiring Special Attention:** 1. **Page 22 (Nuovo Evento)** - Most complex page - Multiple editable interactive grids on single page - Master-detail relationships with real-time calculations - Guest type grid → triggers quantity recalculations in pick list grids - Resource assignment grid - Requires careful state management in React 2. **BLOB Storage for Images** - Article images stored as BLOBs in Oracle - Migration strategy needed (Azure Blob Storage, AWS S3, or filesystem) - MIMETYPE tracking for proper rendering 3. **PL/SQL Business Logic** - Decision needed: Port to C# or keep as Oracle functions? - Quantity calculations are complex - ensure parity - Inventory commitment logic (V_IMPEGNI_ARTICOLI) is critical 4. **State Management** - Heavy use of APEX session state - Consider Redux Toolkit or Zustand for React - Real-time grid updates and calculations 5. **Reporting** - JasperReports replacement needed - Options: SSRS, Crystal Reports, DevExpress, or PDF libraries (iTextSharp, QuestPDF) 6. **Email Queue System** - Asynchronous processing required - Consider: Hangfire, Azure Functions, or background services 7. **Calendar Component** - Page 12 uses APEX calendar - React options: FullCalendar, React Big Calendar, @event-calendar/core 8. **Multi-Grid Interactions** - Interactive grids with master-detail relationships - Consider: AG Grid, DevExtreme DataGrid, or Material-UI DataGrid ## Business Rules to Preserve 1. **Event Status Workflow:** Must follow 0 → 10 → 20 progression 2. **Quote Expiration:** Automatic status change when `DATA_SCAD_PREVENTIVO` passed 3. **Max Events Per Day:** Enforced limit (configurable) 4. **Article Commitment Tracking:** Prevent overbooking of inventory 5. **Coefficient-Based Calculations:** Ensure quantity formulas match exactly 6. **Deposit Calculations:** Auto-recalculation on cost changes 7. **Role-Based Access:** 5-level authorization system 8. **Read-Only Mode:** User-specific write restrictions ## Data Extraction Queries When analyzing the database, useful queries: ```sql -- Get all tables in schema SELECT table_name FROM user_tables ORDER BY table_name; -- Get table structure SELECT column_name, data_type, nullable, data_default FROM user_tab_columns WHERE table_name = 'EVENTI' ORDER BY column_id; -- Get all procedures and functions SELECT object_name, object_type FROM user_objects WHERE object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE') ORDER BY object_type, object_name; -- Get procedure source SELECT text FROM user_source WHERE name = 'EVENTI_AGGIORNA_QTA_LISTA' ORDER BY line; -- Get view definitions SELECT view_name, text FROM user_views WHERE view_name LIKE 'V_%' OR view_name LIKE 'VW_%'; -- Get foreign key relationships SELECT a.constraint_name, a.table_name, a.column_name, c_pk.table_name r_table_name, c_pk.constraint_name r_constraint_name FROM user_cons_columns a JOIN user_constraints c ON a.constraint_name = c.constraint_name JOIN user_constraints c_pk ON c.r_constraint_name = c_pk.constraint_name WHERE c.constraint_type = 'R' ORDER BY a.table_name, a.constraint_name; ``` ## File References - `apollinare-db-connection.md` - Database connection details (credentials) - `f112.sql` - Complete APEX export (53,282 lines) - Application structure - Page definitions - Processes and validations - LOVs and static data - JavaScript libraries ## Development Approach When working on migration tasks: 1. **Always query the database** to understand current data structure and relationships 2. **Extract PL/SQL source code** for procedures/functions before implementing equivalent C# logic 3. **Document business rules** discovered in stored procedures 4. **Preserve Italian field names** in database but consider English in application layer 5. **Test quantity calculations** thoroughly - they are core to the business 6. **Map APEX page flows** to React routes and components 7. **Identify reusable components** (grids, forms, lookups) 8. **Plan data migration** for BLOBs and complex relationships ## Key Terminology (Italian → English) - **Scheda** → Card/Draft (Event status 0) - **Preventivo** → Quote (Event status 10) - **Confermato** → Confirmed (Event status 20) - **Lista Prelievo** → Pick List (articles for event) - **Articoli** → Articles/Items - **Ospiti** → Guests - **Risorse** → Resources (staff) - **Degustazioni** → Tastings - **Allestimenti** → Setups - **Acconti** → Deposits/Advances - **Impegni** → Commitments (inventory reservations) ## Notes - The application is mature and in production use - Italian language throughout (UI, database, code comments) - Complex business logic embedded in database layer - Heavy use of APEX-specific features (Interactive Grids, Dynamic Actions) - Real-time calculations and validations are critical to user experience --- ## Report PDF System - Implementation Details ### Overview Sistema completo di generazione report PDF con editor visuale drag-and-drop (stile Canva) e metalinguaggio APRT (Apollinare Report Template) per template portabili. ### Stack Tecnologico **Backend:** - QuestPDF (Community License) - Generazione PDF programmatica - .NET 9 Web API con Entity Framework Core - SQLite per storage template, font e immagini **Frontend:** - React 19 + TypeScript + Vite - Fabric.js v6 - Canvas editor per design visuale - Material-UI per componenti UI ### Stato Corrente dell'Implementazione #### Backend (COMPLETATO) **Entities** (`/src/Apollinare.Domain/Entities/`): - `ReportTemplate.cs` - Template con JSON, thumbnail, metadata - `ReportFont.cs` - Font custom uploadabili (TTF/OTF) - `ReportImage.cs` - Immagini riutilizzabili nei report **Services** (`/src/Apollinare.API/Services/Reports/`): - `ReportGeneratorService.cs` - Parsing APRT e generazione PDF con QuestPDF - `AprtModels.cs` - Modelli C# per il metalinguaggio APRT **Controllers** (`/src/Apollinare.API/Controllers/`): - `ReportTemplatesController.cs` - CRUD template, clone, import/export - `ReportResourcesController.cs` - Gestione font e immagini - `ReportsController.cs` - Generazione PDF, schema dati, dataset management **API Endpoints disponibili:** ``` # Templates GET /api/report-templates GET /api/report-templates/{id} POST /api/report-templates PUT /api/report-templates/{id} DELETE /api/report-templates/{id} POST /api/report-templates/{id}/clone GET /api/report-templates/{id}/export POST /api/report-templates/import GET /api/report-templates/categories # Resources GET /api/report-resources/fonts POST /api/report-resources/fonts DELETE /api/report-resources/fonts/{id} GET /api/report-resources/fonts/families GET /api/report-resources/images POST /api/report-resources/images DELETE /api/report-resources/images/{id} # Report Generation POST /api/reports/generate GET /api/reports/evento/{eventoId} POST /api/reports/preview GET /api/reports/datasets GET /api/reports/schema/{datasetId} GET /api/reports/datasets/{datasetId}/entities ``` #### Frontend (COMPLETATO ~90%) **Pagine** (`/frontend/src/pages/`): - `ReportTemplatesPage.tsx` - Lista template con cards, filtri, import/export - `ReportEditorPage.tsx` - Editor principale con undo/redo, shortcuts **Componenti Editor** (`/frontend/src/components/reportEditor/`): - `EditorCanvas.tsx` - Canvas Fabric.js per design visuale - `EditorToolbar.tsx` - Toolbar con strumenti, zoom, grid, azioni - `PropertiesPanel.tsx` - Pannello proprietà elemento/pagina - `DataBindingPanel.tsx` - Browser campi dati con supporto multi-dataset - `DatasetSelector.tsx` - Selezione dataset per template - `PreviewDialog.tsx` - Dialog selezione entità per anteprima **Types** (`/frontend/src/types/report.ts`): - Definizioni complete APRT (AprtTemplate, AprtElement, AprtStyle, etc.) - DTOs per API (ReportTemplateDto, DataSchemaDto, DatasetTypeDto, etc.) - Utility functions (mmToPx, pxToMm, getPageDimensions) **Services** (`/frontend/src/services/reportService.ts`): - reportTemplateService - CRUD template - reportFontService - Gestione font - reportImageService - Gestione immagini - reportGeneratorService - Generazione PDF e schema ### Metalinguaggio APRT (Apollinare Report Template) Formato JSON esportabile/importabile per portabilità template: ```json { "version": "1.0", "meta": { "name": "Template Evento", "pageSize": "A4", "orientation": "portrait", "margins": { "top": 20, "right": 15, "bottom": 20, "left": 15 } }, "resources": { "fonts": [], "images": [] }, "dataSources": { "evento": { "type": "object", "schema": "evento" } }, "sections": [], "elements": [ { "id": "uuid", "type": "text", "position": { "x": 20, "y": 20, "width": 100, "height": 20 }, "style": { "fontFamily": "Helvetica", "fontSize": 14, "color": "#000000" }, "content": { "type": "binding", "expression": "{{evento.codice}}" }, "section": "body" } ] } ``` **Tipi elemento supportati:** text, image, shape, table, line, barcode **Data binding:** `{{campo}}`, `{{dataset.campo}}`, `{{collection.campo}}` **Variabili speciali:** `{{$pageNumber}}`, `{{$totalPages}}`, `{{$date}}`, `{{$datetime}}` ### Dataset Disponibili | Dataset ID | Nome | Descrizione | | ---------- | -------- | ------------------------------------------------------------------ | | evento | Evento | Dati evento con cliente, location, dettagli ospiti, costi, risorse | | cliente | Cliente | Anagrafica clienti completa | | location | Location | Sedi e location eventi | | articolo | Articolo | Catalogo articoli e materiali | | risorsa | Risorsa | Staff e personale | ### Funzionalità Implementate - [x] Editor visuale drag-and-drop con Fabric.js - [x] Supporto elementi: testo, forme, linee, tabelle, immagini (placeholder) - [x] Gestione zoom (25% - 300%) - [x] Griglia e snap to grid - [x] Undo/Redo (max 100 stati) - [x] Shortcuts tastiera (Ctrl+Z, Ctrl+Y, Ctrl+S, Delete) - [x] Pannello proprietà con posizione, stile, contenuto - [x] Data binding con browser campi disponibili - [x] Selezione multipla dataset per template - [x] Preview con selezione entità reali - [x] Salvataggio/caricamento template - [x] Import/export template come file .aprt - [x] Clone template - [x] Generazione PDF default per eventi - [x] Formattazione campi (valuta, data, numero, percentuale) - [x] **Responsive design completo** (mobile, tablet, desktop) - [x] **Toolbar professionale** stile Canva/Figma con sezioni etichettate - [x] **Toolbar contestuale** per formattazione rapida (testo, forme, immagini) - [x] **Color picker integrato** con palette preset - [x] **Auto-save** con toggle (abilitato di default, 1s debounce) - [x] **Indicatore stato salvataggio** accurato (Salvato/Non salvato/Salvataggio...) ### Cosa Manca per Completare #### Alta Priorità - [ ] **Caricamento immagini reali** - Attualmente placeholder, implementare upload e rendering - [ ] **Tabelle dinamiche** - Rendering collection dati (es. lista ospiti, articoli) - [ ] **Sezioni header/footer** - Ripetizione su ogni pagina - [ ] **Font custom** - Upload e utilizzo font TTF/OTF nei PDF #### Media Priorità - [ ] **Relazioni tra dataset** - UI per collegare campi tra dataset diversi - [ ] **Barcode/QRCode** - Supporto codici a barre - [ ] **Formule calcolate** - Espressioni matematiche nei campi - [ ] **Stili condizionali** - Formattazione basata su valore dati - [ ] **Raggruppamento elementi** - Group/ungroup nel canvas #### Bassa Priorità - [ ] **Template predefiniti** - Library di template pronti all'uso - [ ] **Anteprima live** - Preview in tempo reale durante editing - [x] ~~**Multi-pagina** - Editor pagine multiple~~ - COMPLETATO - [ ] **Righelli e guide** - Ausili allineamento avanzati - [ ] **Esportazione altri formati** - Excel, Word oltre PDF --- ## Checklist Completamento Report System ### Backend - [x] Entity ReportTemplate - [x] Entity ReportFont - [x] Entity ReportImage - [x] ReportTemplatesController (CRUD + clone + import/export) - [x] ReportResourcesController (fonts + images) - [x] ReportsController (generate + preview + schema + datasets) - [x] ReportGeneratorService con QuestPDF - [x] Schema dati per tutti i dataset (evento, cliente, location, articolo, risorsa) - [x] Generazione PDF default evento - [x] Generazione PDF multi-pagina - [ ] Rendering tabelle dinamiche da collection - [ ] Supporto font custom nel PDF - [ ] Rendering immagini da storage ### Frontend - [x] ReportTemplatesPage (lista + filtri + azioni) - [x] ReportEditorPage (editor principale) - [x] EditorCanvas con Fabric.js v6 - [x] EditorToolbar completa - [x] PropertiesPanel (posizione + stile + contenuto) - [x] DataBindingPanel multi-dataset - [x] DatasetSelector - [x] PreviewDialog con selezione entità - [x] Types APRT completi - [x] Services API completi - [x] Undo/Redo - [x] Keyboard shortcuts - [x] PageNavigator (gestione multi-pagina) - [x] Navigazione pagine in toolbar - [x] **Responsive design** (mobile/tablet/desktop) - [ ] Upload e gestione immagini nell'editor - [ ] Editor tabelle avanzato (colonne, binding dati) - [ ] UI relazioni tra dataset - [ ] Gestione sezioni header/footer ### Testing - [x] Build frontend senza errori - [x] Build backend senza errori - [ ] Test funzionale editor canvas - [x] Test generazione PDF con dati reali (binding e formattazione funzionanti) - [ ] Test import/export template - [ ] Test con font e immagini custom ### Documentazione - [x] Documentazione APRT metalanguage - [x] Lista API endpoints - [x] Checklist implementazione - [ ] Guida utente editor - [ ] Esempi template comuni --- ## Note Tecniche per Future Sessioni ### Struttura File Report System ``` src/ ├── Apollinare.Domain/Entities/ │ ├── ReportTemplate.cs # Entity template con TemplateJson │ ├── ReportFont.cs # Font custom uploadati │ └── ReportImage.cs # Immagini riutilizzabili │ ├── Apollinare.API/ │ ├── Controllers/ │ │ ├── ReportTemplatesController.cs # CRUD template │ │ ├── ReportResourcesController.cs # Font e immagini │ │ └── ReportsController.cs # Generazione PDF + schema │ │ │ └── Services/Reports/ │ ├── ReportGeneratorService.cs # QuestPDF generator │ └── AprtModels.cs # Modelli C# per APRT JSON frontend/src/ ├── pages/ │ ├── ReportTemplatesPage.tsx # Lista template │ └── ReportEditorPage.tsx # Editor principale │ ├── components/reportEditor/ │ ├── EditorCanvas.tsx # Fabric.js canvas │ ├── EditorToolbar.tsx # Toolbar strumenti │ ├── PropertiesPanel.tsx # Proprietà elemento │ ├── DataBindingPanel.tsx # Browser campi dati │ ├── DatasetSelector.tsx # Selezione dataset │ ├── PreviewDialog.tsx # Dialog anteprima │ └── ContextMenu.tsx # Menu tasto destro (NEW) │ ├── types/ │ └── report.ts # Types APRT + DTOs │ └── services/ └── reportService.ts # API calls ``` ### Problemi Risolti (da ricordare) 1. **Fabric.js v6 breaking changes:** - `sendToBack()` → `canvas.sendObjectToBack(obj)` - Event handlers hanno signature diversa - Proprietà `data` non è nel tipo base, serve cast a `FabricObjectWithData` 2. **TypeScript strict mode:** - Usare `as any` per event handlers Fabric.js - Interface `FabricObjectWithData` per oggetti con metadata custom 3. **QuestPDF TimeSpan:** - `evento.OraInizio` è `TimeSpan?` non `string` - Formattare con `{evento.OraInizio:hh\\:mm}` 4. **Data Binding PDF (FIX 27/11/2025):** - **Problema:** I binding `{{dataEvento}}` non venivano risolti - PDF mostrava campi vuoti - **Causa:** Il frontend creava binding senza prefisso dataset quando c'era un solo dataset - **Soluzione Frontend** (`DataBindingPanel.tsx`): Sempre includere il prefisso dataset ```typescript // Prima: {{dataEvento}} - non funzionava // Dopo: {{evento.dataEvento}} - corretto const createBinding = (datasetId: string, fieldName: string) => { return `{{${datasetId}.${fieldName}}}`; }; ``` - **Soluzione Backend** (`ReportGeneratorService.cs`): Aggiunto fallback per compatibilità con template esistenti ```csharp // Se binding senza prefisso, cerca in tutti i dataset if (current == null && parts.Length == 1) { foreach (var kvp in dataContext) { var foundValue = GetPropertyValue(kvp.Value, path); if (foundValue != null) { current = foundValue; break; } } } ``` - **Formattazione:** Aggiunto `ResolveBindingWithFormat()` per applicare format (date, currency, etc.) 5. **SignalR Connection (FIX 27/11/2025):** - Aggiunto `app.UseWebSockets()` in Program.cs prima di `app.UseRouting()` - Configurato signalr.ts con `withAutomaticReconnect()` 6. **MUI Fragment in Menu (FIX 27/11/2025):** - Menu component non accetta Fragment come child - Sostituire `<>...` con array `[, ]` 7. **HTML p/div nesting (FIX 27/11/2025):** - Error: `
cannot be a descendant of

` - Fix: `` 8. **Context Menu Browser Default (FIX 27/11/2025 notte):** - **Problema:** Il menu contestuale del browser appariva invece di quello custom - **Soluzione:** Usare `onContextMenu` su Box container React invece di eventi Fabric.js - **File:** `EditorCanvas.tsx` - Aggiunto handler sul Box wrapper 9. **Fabric.js v6 Layer Ordering (FIX 27/11/2025 notte):** - **Problema:** `canvas.moveTo()` non esiste in Fabric.js v6 - **Soluzione:** Usare `canvas.remove(obj)` + `canvas.insertAt(targetIndex, obj)` - **File:** `EditorCanvas.tsx` - Aggiunto z-index sync in useEffect 10. **QuestPDF Posizionamento Assoluto (RISOLTO 27/11/2025):** - **Problema:** QuestPDF non ha API `.Position()` per posizionamento assoluto - **Soluzione:** Usare SVG con `viewBox` in mm per avere coordinate 1:1 con il designer - **File:** `ReportGeneratorService.cs` - Metodi `GenerateSvgContent()`, `RenderElementToSvg()` - **Chiave della soluzione:** ```csharp // SVG viewBox in mm - 1 unità SVG = 1mm svgBuilder.AppendLine($""); ``` - Le coordinate dal template sono già in mm relative all'area contenuto (dentro i margini) - Non serve più conversione mm→px: usiamo mm direttamente nel viewBox 11. **Immagini nel PDF (RISOLTO 27/11/2025):** - **Problema:** Le immagini embedded come data URI in `imageSettings.src` non venivano renderizzate - **Soluzione:** `RenderImageToSvg()` ora gestisce 3 fonti: 1. Data URI (`data:image/...;base64,...`) - usato direttamente 2. API URL (`/api/report-resources/images/{id}`) - caricato da DB e convertito in data URI 3. URL esterni (`http://...`) - passato direttamente - **File:** `ReportGeneratorService.cs` - Metodo `RenderImageToSvg()`, `GuessMimeType()` 12. **Font Size nel PDF (RISOLTO 27/11/2025):** - **Problema:** Il font size appariva troppo grande/piccolo nel PDF - **Causa:** La formula `fontSize * mmToPx / 3` era un'approssimazione incorretta - **Soluzione:** Conversione corretta da px screen (96 DPI) a mm: ```csharp // 1px @ 96 DPI = 0.2646mm var fontSizeMm = (style?.FontSize ?? 12) * 0.2646f; ``` - **File:** `ReportGeneratorService.cs` - Metodo `RenderElementToSvg()` case "text" 13. **Rotazione Oggetti nel PDF (RISOLTO 28/11/2025):** - **Problema:** Gli oggetti ruotati nel canvas Fabric.js venivano posizionati in modo completamente errato nel PDF - **Causa:** In Fabric.js con `originX='left'` e `originY='top'`, quando un oggetto viene ruotato: - L'oggetto ruota attorno al suo centro geometrico - Le coordinate `left`/`top` salvate rappresentano la posizione dell'origin point **dopo** la rotazione - Il backend calcolava il centro di rotazione in modo errato usando `(x + width/2, y + height/2)` - **Soluzione:** Implementata la formula corretta di Fabric.js per calcolare il centro: ```csharp // Calcolo del centro usando la formula Fabric.js (originX='left', originY='top') var angleRad = rotation * Math.PI / 180f; var halfWidth = width / 2; var halfHeight = height / 2; var cos = (float)Math.Cos(angleRad); var sin = (float)Math.Sin(angleRad); // Centro dell'oggetto in coordinate canvas var centerX = left + halfWidth * cos - halfHeight * sin; var centerY = top + halfWidth * sin + halfHeight * cos; // Posizione di disegno (angolo sup-sinistro del rettangolo non ruotato centrato sul centro) var drawX = centerX - halfWidth; var drawY = centerY - halfHeight; // Ruota attorno al centro calcolato canvas.RotateDegrees(rotation, centerX, centerY); // Disegna a (drawX, drawY) ``` - **File:** `ReportGeneratorService.cs` - Metodi `RenderElementToCanvas()` e `GenerateSvgContent()` + `RenderElementToSvg()` - **Nota:** La stessa logica è applicata sia al rendering bitmap (SkiaSharp) che SVG 14. **Gestione Multi-Pagina (IMPLEMENTATO 28/11/2025):** - **Struttura dati:** - `AprtPage`: `{ id, name, pageSize?, orientation?, margins?, backgroundColor? }` - `AprtElement.pageId`: ID della pagina a cui appartiene l'elemento - `AprtTemplate.pages`: Array di pagine del template - **Frontend:** - `PageNavigator.tsx`: Sidebar con lista pagine, context menu (rinomina, duplica, elimina, sposta) - `EditorToolbar.tsx`: Pulsanti prev/next pagina, indicatore "Pagina X di Y" - `ReportEditorPage.tsx`: State `currentPageId`, filtro elementi per pagina, handlers CRUD pagine - `PropertiesPanel.tsx`: Modifica nome pagina e impostazioni (format, orientation, margins, background) - **Backend (`ReportGeneratorService.cs`):** ```csharp // Pre-render di ogni pagina separatamente foreach (var pageDefinition in aprt.Pages) { var pageElements = aprt.Elements .Where(e => e.Visible && (e.PageId == pageDefinition.Id || (string.IsNullOrEmpty(e.PageId) && pageDefinition.Id == aprt.Pages[0].Id))) .ToList(); var pageImageBytes = RenderContentToBitmap(pageElements, pageWidth, pageHeight, ...); pageRenderData.Add((pageWidth, pageHeight, bgColor, pageImageBytes)); } // Genera PDF con pagine separate Document.Create(container => { foreach (var (pageWidth, pageHeight, bgColor, imageBytes) in pageRenderData) container.Page(page => { page.Size(...); page.Content().Image(imageBytes); }); }); ``` - **Migrazione template legacy:** `MigrateTemplatePages()` crea pagina default e assegna elementi orfani 15. **Variabili Globali Report (FIX 28/11/2025 sera):** - **Problema:** Le variabili speciali `{{$pageNumber}}`, `{{$totalPages}}`, `{{$date}}`, `{{$datetime}}`, `{{$time}}` non venivano stampate nel PDF - restavano come placeholder - **Causa:** `ResolveBindingPath()` restituiva placeholder statici (`"{{PAGE}}"`) invece dei valori reali perché il contesto pagina non veniva passato durante il rendering - **Soluzione:** 1. Aggiunta classe `PageContext` con `PageNumber` e `TotalPages` 2. Il ciclo di rendering ora traccia l'indice pagina corrente 3. `PageContext` propagato attraverso tutta la catena: `GeneratePdfAsync` → `RenderContentToBitmap` → `RenderElementToCanvas` → `RenderTextToCanvas` → `ResolveContent` → `ResolveBindingWithFormat` → `ResolveBindingPath` 4. `ResolveBindingPath()` ora usa i valori reali dal contesto: ```csharp "$pageNumber" => pageContext?.PageNumber.ToString() ?? "1", "$totalPages" => pageContext?.TotalPages.ToString() ?? "1", "$date" => DateTime.Now.ToString("dd/MM/yyyy"), "$time" => DateTime.Now.ToString("HH:mm"), "$datetime" => DateTime.Now.ToString("dd/MM/yyyy HH:mm"), ``` - **File:** `ReportGeneratorService.cs` - Metodi `GeneratePdfAsync()`, `RenderContentToBitmap()`, `RenderElementToCanvas()`, `RenderTextToCanvas()`, `ResolveContent()`, `ResolveBindingWithFormat()`, `ResolveBinding()`, `ResolveExpression()`, `ResolveBindingPath()` 16. **Responsive Design Completo (IMPLEMENTATO 28/11/2025 notte):** - **Obiettivo:** Rendere tutta l'applicazione responsive per mobile, tablet e desktop - **Breakpoints MUI utilizzati:** - Mobile: `theme.breakpoints.down("sm")` → < 600px - Tablet: `theme.breakpoints.between("sm", "md")` → 600-900px - Desktop: `theme.breakpoints.up("md")` → > 900px - **Pattern principale per Report Editor su mobile:** - `BottomNavigation` per switch tra pannelli (Pagine, Dati, Proprietà) - `SwipeableDrawer` con `anchor="bottom"` e altezza 70vh per contenuto pannelli - Auto-zoom canvas: 0.5 mobile, 0.75 tablet, 1 desktop - **Pattern per toolbar mobile:** - Riga primaria con azioni essenziali sempre visibili - Riga secondaria collassabile con `` per azioni secondarie - **Pattern per dialog mobile:** - `fullScreen` prop su Dialog - AppBar con pulsante back invece di DialogTitle - Navigazione step-by-step invece di layout side-by-side - **File modificati:** - `Layout.tsx` - Sidebar collassata su tablet - `ReportTemplatesPage.tsx` - FAB, fullScreen dialogs - `ReportEditorPage.tsx` - BottomNavigation + SwipeableDrawer - `EditorToolbar.tsx` - 3 varianti layout (mobile/tablet/desktop) - `DataBindingPanel.tsx`, `PropertiesPanel.tsx`, `PageNavigator.tsx` - Width responsive - `DatasetSelector.tsx` - Header collapsible - `PreviewDialog.tsx`, `ImageUploadDialog.tsx` - fullScreen + step navigation 17. **Indicatore "Non Salvato" Errato (FIX 28/11/2025 tarda notte):** - **Problema:** Dopo il salvataggio, l'indicatore continuava a mostrare "Non salvato" - **Causa:** `hasUnsavedChanges` era basato su `templateHistory.canUndo` che indica solo se c'è history disponibile, non se ci sono modifiche non salvate - **Soluzione:** Introdotto `lastSavedUndoCount` che viene aggiornato dopo ogni salvataggio riuscito. `hasUnsavedChanges` ora confronta `templateHistory.undoCount !== lastSavedUndoCount` - **File:** `ReportEditorPage.tsx` 18. **Auto-Save Feature (IMPLEMENTATO 28/11/2025 tarda notte):** - **Funzionalità:** Salvataggio automatico dopo 1 secondo di inattività - **Implementazione:** - Stato `autoSaveEnabled` (default: true) in `ReportEditorPage.tsx` - `useEffect` con debounce di 1000ms che triggera `saveMutation.mutate()` - Non si attiva se: `isNew`, `!hasUnsavedChanges`, `saveMutation.isPending` - Toggle nella toolbar con icona `AutoSaveIcon` (da @mui/icons-material) - Pulsante "Salva" nascosto quando auto-save è attivo - **Props toolbar:** `autoSaveEnabled`, `onAutoSaveToggle` - **File:** `ReportEditorPage.tsx`, `EditorToolbar.tsx` 19. **Toolbar Migliorata Stile Canva/Figma (IMPLEMENTATO 28/11/2025 tarda notte):** - **Miglioramenti:** - Design moderno con gradient buttons e animazioni fluide - Sezioni etichettate su desktop (INSERISCI, MODIFICA, CRONOLOGIA, VISTA, ZOOM, PAGINA) - Toolbar contestuale dinamica basata su tipo elemento selezionato - Color picker integrato con 20 colori preset - Indicatore stato salvataggio visivo - Badge su pulsante Snap - Zoom esteso fino a 300% - **Componenti aggiunti:** `ToolbarSection`, `StyledIconButton`, `ColorPickerButton` - **Type aggiunto:** `textDecoration` in `AprtStyle` - **File:** `EditorToolbar.tsx`, `types/report.ts` 20. **Incompatibilità Versione SignalR (FIX 28/11/2025 pomeriggio):** - **Problema:** Errore "Method not found: 'System.String Microsoft.AspNetCore.SignalR.IInvocationBinder.GetTarget(System.ReadOnlySpan`1)'." - connessioni SignalR fallivano immediatamente - **Causa:** Package `Microsoft.AspNetCore.SignalR.Common` v10.0.0 nel backend incompatibile con .NET 9 (è per .NET 10 preview) - **Soluzione:** - Rimosso `Microsoft.AspNetCore.SignalR.Common` dal .csproj (SignalR è già incluso in ASP.NET Core) - Downgrade frontend `@microsoft/signalr` da v10.0.0 a v8.0.7 - **File:** `Apollinare.API.csproj`, `frontend/package.json` 21. **Auto-Save Non Funzionante (FIX 28/11/2025 pomeriggio):** - **Problema:** Con auto-save attivo, le modifiche non venivano salvate (l'indicatore mostrava "Non salvato" sempre) - **Causa:** `saveMutation` nell'array di dipendenze dell'`useEffect` causava il reset del timeout ad ogni render (React Query crea un nuovo oggetto ad ogni render) - **Soluzione:** Usati `useRef` per `saveMutation`, `template`, e `templateInfo` per evitare che l'effect si ri-esegua inutilmente - **File:** `ReportEditorPage.tsx` 22. **Sistema Collaborazione Real-Time (COMPLETATO 28/11/2025):** - **Obiettivo:** Collaborazione stile Google Docs su tutto l'applicativo - **Architettura implementata:** - `CollaborationHub.cs` - Hub SignalR generico con room-based collaboration - `collaboration.ts` - Service singleton frontend - `CollaborationContext.tsx` - React Context con `useCollaborationRoom` hook - Room key format: `{entityType}:{entityId}` (es. `report-template:2`) - **File principali:** - Backend: `CollaborationHub.cs` - Frontend: `collaboration.ts`, `CollaborationContext.tsx`, `ReportEditorPage.tsx` 23. **Sincronizzazione Real-Time Lenta (FIX 28/11/2025 sera):** - **Problema:** Al salvataggio (manuale o auto-save), l'altra sessione impiegava diversi secondi per vedere le modifiche - **Causa:** Il sistema usava `DataSaved` notification che causava un reload del template dal server (`queryClient.invalidateQueries`) - **Soluzione:** Implementato `BroadcastTemplateSync` che invia l'intero template via SignalR direttamente alle altre sessioni: - Nuovo metodo Hub `BroadcastTemplateSync(roomKey, templateJson, version, compressed)` - L'altra sessione riceve il template e lo applica istantaneamente con `historyActions.setWithoutHistory()` - Aggiunto version tracking per evitare di applicare versioni più vecchie - Compressione automatica gzip per template > 10KB (usa browser's CompressionStream API) - **File:** `CollaborationHub.cs`, `collaboration.ts`, `CollaborationContext.tsx`, `ReportEditorPage.tsx` 24. **Limite Messaggio SignalR (FIX 28/11/2025 notte):** - **Problema:** La connessione SignalR si disconnetteva con errore "The maximum message size of 32768B was exceeded" - **Causa:** Il template compresso (~110KB) superava il limite di default di SignalR (32KB) - **Soluzione:** Configurato `MaximumReceiveMessageSize = 1MB` in `Program.cs`: ```csharp builder.Services.AddSignalR(options => { options.MaximumReceiveMessageSize = 1024 * 1024; // 1MB }) ``` - **File:** `Program.cs` 25. **Auto-Save Non Funzionante (FIX 28/11/2025 notte):** - **Problema:** L'auto-save non si attivava anche con modifiche non salvate - **Causa:** Le dipendenze dell'effect includevano `saveMutation`, `template`, `templateInfo` che cambiano ad ogni render, causando reset continui del timeout - **Soluzione:** Approccio event-based con debounce usando refs: - `saveMutationRef`, `templateForSaveRef`, `templateInfoForSaveRef` per accedere ai valori correnti senza re-triggerare l'effect - Dipendenze effect ridotte a: `autoSaveEnabled`, `isNew`, `hasUnsavedChanges`, `templateHistory.undoCount` - Check `isPending` spostato dentro il callback del setTimeout - **File:** `ReportEditorPage.tsx` 26. **Selezione Multipla Fabric.js - Riposizionamento Oggetti (FIX 29/11/2025):** - **Problema:** Usando `ActiveSelection` di Fabric.js per la selezione multipla, gli oggetti venivano riposizionati/spostati quando selezionati - **Causa:** `ActiveSelection` raggruppa gli oggetti e le loro coordinate diventano relative al centro del gruppo. Inoltre, ricreando l'`ActiveSelection` nell'effect quando cambiava `selectedElementIds`, gli oggetti venivano spostati - **Soluzione:** Sistema di selezione multipla completamente personalizzato: - Disabilitata selezione nativa di Fabric.js (`selection: false` nel canvas) - Implementato `handleMouseDown` per: - Click su oggetto → selezione singola - Shift+click → aggiunge/rimuove dalla selezione - Click su canvas vuoto → inizio rettangolo di selezione - Click su oggetto già selezionato (multi) → inizio drag multiplo - Implementato `handleMouseMove` per: - Disegno rettangolo di selezione (Rect blu tratteggiato) - Spostamento multiplo oggetti (aggiorna `left`/`top` di ogni oggetto) - Implementato `handleMouseUp` per: - Fine rettangolo → calcola intersezione e seleziona oggetti - Fine drag multiplo → aggiorna template con nuove posizioni - Feedback visivo: bordo blu e ombra sugli oggetti selezionati (invece di ActiveSelection) - Usati refs (`selectedElementIdsRef`, `onSelectElementRef`, etc.) per evitare stale closures negli event handler registrati una sola volta - **File:** `EditorCanvas.tsx`, `ReportEditorPage.tsx` 27. **Toolbar Contestuale Layout Shift (FIX 29/11/2025):** - **Problema:** Quando si selezionava un oggetto nel canvas del report designer, appariva una toolbar contestuale aggiuntiva sotto quella principale, causando uno scroll/layout shift della pagina - **Causa:** La toolbar contestuale (`renderContextualToolbar()`) veniva renderizzata condizionalmente quando `selectedElement` era presente, aggiungendo una riga extra all'altezza della toolbar - **Soluzione:** Rimossa completamente la toolbar contestuale - le proprietà degli oggetti selezionati vengono gestite esclusivamente dal `PropertiesPanel` sulla destra (che è sempre visibile) - **Rimosso:** - `renderContextualToolbar()` - funzione che rendeva la toolbar - `handleTextFormat()`, `handleTextAlign()` - handler per formattazione - `ColorPickerButton` component - color picker inline - `COLOR_PRESETS` - palette colori - Props `selectedElement`, `onUpdateSelectedElement` dall'interfaccia - Import inutilizzati: `TextField`, `ToggleButton`, `ToggleButtonGroup`, icone formattazione - **File:** `EditorToolbar.tsx`, `ReportEditorPage.tsx` 28. **Scorciatoie da Tastiera Intercettate dal Browser (FIX 29/11/2025):** - **Problema:** Le scorciatoie da tastiera (Ctrl+S, Ctrl+C, Ctrl+V, etc.) nel report designer venivano catturate dal browser invece che dalla pagina - ad esempio Ctrl+S apriva il dialog di salvataggio del browser invece di salvare il template - **Causa:** L'handler delle scorciatoie non chiamava `e.preventDefault()` in modo consistente e non verificava se l'utente stava modificando testo in un input field o nel canvas - **Soluzione:** Riscritto completamente l'handler delle scorciatoie: - Verifica se il target è un campo input (`INPUT`, `TEXTAREA`, `contentEditable`) - Nuovo metodo `isTextEditing()` esposto da `EditorCanvasRef` per verificare se un elemento testo è in editing nel canvas Fabric.js - `e.preventDefault()` chiamato subito dopo il riconoscimento della scorciatoia - Implementate tutte le scorciatoie dichiarate nel context menu (Ctrl+X/C/V/D/A/L/G, Ctrl+[/], Delete, Escape) - **File:** `EditorCanvas.tsx` (aggiunto `isTextEditing()` a `EditorCanvasRef`), `ReportEditorPage.tsx` (riscritto `useEffect` delle scorciatoie, aggiunto `canvasRef`) 29. **Sistema Pannelli Drag-and-Drop tra Sidebar (IMPLEMENTATO 29/11/2025):** - **Obiettivo:** Permettere agli utenti di trascinare i pannelli tra sidebar sinistra e destra, con ridimensionamento a livello sidebar - **Implementazione:** - `SidebarDropZone.tsx` - Drop zone con HTML5 Drag and Drop API e resize orizzontale - `ResizablePanel.tsx` - Pannello con header draggable e resize verticale (flex) - `usePanelLayout.ts` - Hook con `sidebarWidths` invece di larghezza per-pannello - Versione config incrementata a 3 per forzare migrazione localStorage - **Comportamento chiave:** - `movePanelToPosition()` redistribuisce `flex: 1` a tutti i pannelli nella sidebar di destinazione - Pannelli usano `width: 100%` per adattarsi alla sidebar - `PANEL_DRAG_TYPE = "application/x-panel-id"` per identificare i drag - **File:** `SidebarDropZone.tsx`, `ResizablePanel.tsx`, `usePanelLayout.ts`, `ReportEditorPage.tsx` 30. **Layout Report Designer Non Full-Width (FIX 29/11/2025):** - **Problema:** Il report designer non occupava tutta la larghezza disponibile del browser - **Causa:** Il Layout usava `width: 100%` che si riferisce al parent, non al viewport. Inoltre il padding non veniva rimosso per il report editor - **Soluzione:** - `Layout.tsx`: Cambiato `width: 100%` → `width: 100vw` e `calc(100vw - drawerWidth)` - `Layout.tsx`: Aggiunto controllo condizionale per route `/report-editor`: `p: 0`, `overflow: hidden` - `Layout.tsx`: Cambiato `minHeight: 100vh` → `height: 100vh` con `overflow: hidden` - `Layout.tsx`: Content box usa `flex: 1` con `minHeight: 0` (fondamentale per flexbox) - `ReportEditorPage.tsx`: Rimossi margini negativi, usato `flex: 1` e `minHeight: 0` - `ReportEditorPage.tsx`: Canvas container con `width: 100%` - **Nota:** `minHeight: 0` è fondamentale in flexbox per permettere ai contenitori di ridursi sotto la dimensione del loro contenuto - **File:** `Layout.tsx`, `ReportEditorPage.tsx`, `EditorCanvas.tsx` ### Schema Database Report System Le tabelle sono già nel DbContext (`AppollinareDbContext.cs`): - `ReportTemplates` - Template salvati - `ReportFonts` - Font custom - `ReportImages` - Immagini riutilizzabili Migration già applicata per SQLite. ### Dipendenze Chiave **Backend (NuGet):** - `QuestPDF` - Generazione PDF (Community License, gratis per revenue < $1M) **Frontend (npm):** - `fabric` v6.x - Canvas editor - `uuid` - Generazione ID elementi - `@tanstack/react-query` - Data fetching ### Routes Frontend ```typescript // App.tsx } /> } /> } /> ``` Menu aggiunto in `Layout.tsx` sotto "Report" con icona PrintIcon. --- ## Sistema Moduli - Architettura e Implementazione ### Overview Sistema di modularizzazione dell'applicazione per gestione licenze, abbonamenti e funzionalità dinamiche. Ogni modulo rappresenta una sezione business completa (es. Magazzino, Acquisti, Vendite) che può essere attivata/disattivata per cliente. ### Requisiti Funzionali **Moduli previsti:** - `warehouse` - Magazzino (gestione inventario, movimenti, giacenze) - `purchases` - Acquisti (ordini fornitori, DDT entrata, fatture passive) - `sales` - Vendite (ordini clienti, DDT uscita, fatture attive) - `production` - Produzione (cicli produttivi, distinte base, MRP) - `quality` - Qualità (controlli, non conformità, certificazioni) **Funzionalità Core (sempre attive):** - Report e template PDF - Gestione utenti e autenticazione (futuro) - Dashboard e navigazione base - Impostazioni sistema ### Comportamento UI 1. **Modulo attivo:** Menu visibile, route accessibili, funzionalità complete 2. **Modulo disattivato:** - Menu nascosto - Route redirect a pagina `/modules/purchase/{moduleCode}` - Le funzioni di altri moduli che usavano dati del modulo disattivato continuano a funzionare (dati storici preservati) ### Modello Dati ``` AppModule (tabella moduli disponibili) ├── Id: int (PK) ├── Code: string (unique, es. "warehouse") ├── Name: string (es. "Magazzino") ├── Description: string ├── Icon: string (nome icona MUI) ├── BasePrice: decimal (prezzo base annuale) ├── MonthlyMultiplier: decimal (moltiplicatore per abbonamento mensile, es. 1.2) ├── SortOrder: int (ordine nel menu) ├── IsCore: bool (true = sempre attivo, non disattivabile) ├── Dependencies: string[] (codici moduli prerequisiti) ├── CreatedAt: DateTime ├── UpdatedAt: DateTime ModuleSubscription (stato abbonamento per istanza) ├── Id: int (PK) ├── ModuleId: int (FK → AppModule) ├── IsEnabled: bool ├── SubscriptionType: enum (None, Monthly, Annual) ├── StartDate: DateTime? ├── EndDate: DateTime? ├── AutoRenew: bool ├── CreatedAt: DateTime ├── UpdatedAt: DateTime ``` ### API Endpoints ``` # Moduli (lettura per tutti, scrittura solo admin) GET /api/modules # Lista tutti i moduli con stato subscription GET /api/modules/{code} # Dettaglio singolo modulo GET /api/modules/active # Solo moduli attivi (per menu) PUT /api/modules/{code}/enable # Attiva modulo PUT /api/modules/{code}/disable # Disattiva modulo # Subscriptions (admin only) GET /api/modules/subscriptions # Lista tutte le subscription PUT /api/modules/{code}/subscription # Aggiorna subscription (tipo, date) POST /api/modules/{code}/subscription/renew # Rinnova abbonamento ``` ### Frontend Architecture **Context e State:** - `ModuleContext.tsx` - React Context con stato moduli globale - `useModules()` - Hook per accesso a lista moduli - `useModuleEnabled(code)` - Hook per check singolo modulo - `useActiveModules()` - Hook per moduli attivi (per menu) **Componenti:** - `ModuleGuard.tsx` - HOC/wrapper che verifica accesso a route - `ModulePurchasePage.tsx` - Pagina acquisto/attivazione modulo - `ModulesAdminPage.tsx` - Pannello admin gestione moduli **Integrazione Menu (Layout.tsx):** ```typescript // Filtra voci menu in base a moduli attivi const menuItems = allMenuItems.filter( (item) => !item.moduleCode || activeModuleCodes.includes(item.moduleCode), ); ``` **Routing (App.tsx):** ```typescript // Route protette da modulo } /> // Pagina acquisto modulo } /> ``` ### Logica Backend **ModuleService:** ```csharp public class ModuleService { // Verifica se modulo è attivo (usato da altri servizi) public async Task IsModuleEnabledAsync(string code); // Verifica subscription valida (non scaduta) public async Task HasValidSubscriptionAsync(string code); // Attiva modulo (crea/aggiorna subscription) public async Task EnableModuleAsync(string code, SubscriptionType type, DateTime? endDate); // Disattiva modulo (preserva dati) public async Task DisableModuleAsync(string code); // Job schedulato: controlla scadenze e disattiva moduli scaduti public async Task CheckExpiredSubscriptionsAsync(); } ``` ### Principi di Design 1. **Riutilizzo codice:** I moduli possono importare servizi/componenti da altri moduli 2. **Dati persistenti:** Disattivare un modulo non elimina i dati, solo nasconde l'accesso 3. **Dipendenze:** Un modulo può richiedere altri moduli (es. Produzione richiede Magazzino) 4. **Core inattaccabile:** Report, utenti, dashboard non sono disattivabili 5. **Check lato backend:** La verifica modulo avviene sempre sul server, mai solo frontend 6. **Cache:** Stato moduli cachato con invalidazione su modifica ### Struttura File ``` src/Apollinare.Domain/Entities/ ├── AppModule.cs └── ModuleSubscription.cs src/Apollinare.API/ ├── Controllers/ │ └── ModulesController.cs ├── Services/ │ └── ModuleService.cs └── DTOs/ └── ModuleDtos.cs frontend/src/ ├── contexts/ │ └── ModuleContext.tsx ├── components/ │ └── ModuleGuard.tsx ├── pages/ │ ├── ModulesAdminPage.tsx │ └── ModulePurchasePage.tsx ├── services/ │ └── moduleService.ts └── types/ └── module.ts ``` ### Checklist Implementazione **Backend:** - [x] Entity `AppModule` - [x] Entity `ModuleSubscription` - [x] `ModuleService` con logica business - [x] `ModulesController` con tutti gli endpoint - [x] DbSet e migration EF Core (tabelle create manualmente in SQLite) - [x] Seed dati iniziali (5 moduli) - [ ] Job controllo scadenze (opzionale - futuro) **Frontend:** - [x] Types `module.ts` - [x] Service `moduleService.ts` - [x] Context `ModuleContext.tsx` - [x] Component `ModuleGuard.tsx` - [x] Page `ModulesAdminPage.tsx` - [x] Page `ModulePurchasePage.tsx` - [x] Integrazione `Layout.tsx` per menu dinamico - [x] Route protection in `App.tsx` **Testing:** - [x] API CRUD moduli - [x] API subscription - [x] Redirect su modulo disattivato - [x] Menu filtrato correttamente - [ ] Scadenza subscription (da testare con date reali) --- ## Prossimi Passi: Implementazione Moduli Business **PRIORITÀ ALTA - Da implementare:** I moduli sono stati definiti a livello infrastrutturale (sistema licenze/abbonamenti). Ora bisogna implementare le funzionalità reali di ogni modulo. ### Ordine di Implementazione Consigliato 1. **Magazzino (warehouse)** - Base per tutti gli altri moduli 2. **Acquisti (purchases)** - Dipende da Magazzino 3. **Vendite (sales)** - Dipende da Magazzino 4. **Produzione (production)** - Dipende da Magazzino 5. **Qualità (quality)** - Indipendente ### Architettura Modulare - Principi di Personalizzazione **IMPORTANTE:** Ogni modulo deve essere facilmente personalizzabile da codice per adattarsi a esigenze specifiche del cliente. **Struttura consigliata per ogni modulo:** ``` src/Apollinare.API/ ├── Modules/ │ ├── Warehouse/ │ │ ├── Controllers/ │ │ │ └── WarehouseController.cs │ │ ├── Services/ │ │ │ ├── IWarehouseService.cs # Interfaccia per DI/mock │ │ │ └── WarehouseService.cs │ │ ├── DTOs/ │ │ │ └── WarehouseDtos.cs │ │ └── Configuration/ │ │ └── WarehouseConfig.cs # Configurazione modulo │ ├── Purchases/ │ │ └── ... │ └── Sales/ │ └── ... src/Apollinare.Domain/ ├── Entities/ │ ├── Warehouse/ │ │ ├── Article.cs │ │ ├── StockMovement.cs │ │ └── Warehouse.cs │ ├── Purchases/ │ │ ├── PurchaseOrder.cs │ │ └── Supplier.cs │ └── Sales/ │ ├── SalesOrder.cs │ └── Customer.cs frontend/src/ ├── modules/ │ ├── warehouse/ │ │ ├── pages/ │ │ │ ├── ArticlesPage.tsx │ │ │ ├── StockMovementsPage.tsx │ │ │ └── InventoryPage.tsx │ │ ├── components/ │ │ │ └── ArticleForm.tsx │ │ ├── services/ │ │ │ └── warehouseService.ts │ │ ├── types/ │ │ │ └── warehouse.ts │ │ ├── hooks/ │ │ │ └── useWarehouse.ts │ │ └── routes.tsx # Route del modulo │ ├── purchases/ │ │ └── ... │ └── sales/ │ └── ... ``` **Pattern di Personalizzazione:** 1. **Interfacce per Services:** Usare sempre interfacce (`IWarehouseService`) per permettere override tramite DI 2. **Configurazione esterna:** Parametri configurabili in `appsettings.json` o database 3. **Hook points:** Esporre eventi/callback per estensioni custom 4. **Component composition:** Componenti React piccoli e componibili 5. **Feature flags:** Sotto-funzionalità attivabili/disattivabili per modulo **Esempio configurazione modulo:** ```csharp // WarehouseConfig.cs public class WarehouseConfig { public bool EnableBarcodeScanning { get; set; } = true; public bool EnableMultiWarehouse { get; set; } = false; public bool EnableSerialTracking { get; set; } = false; public bool EnableBatchTracking { get; set; } = false; public int LowStockThreshold { get; set; } = 10; public List CustomFields { get; set; } = new(); } ``` **Esempio hook point per estensioni:** ```csharp // IWarehouseService.cs public interface IWarehouseService { // Metodi standard Task
GetArticleAsync(int id); Task
CreateArticleAsync(ArticleDto dto); // Hook points per customizzazione event Func? OnArticleCreated; event Func>? OnBeforeStockMovement; event Func? OnAfterStockMovement; } ``` ### Dettaglio Moduli da Implementare #### 1. Magazzino (warehouse) **Funzionalità:** - Anagrafica articoli (CRUD, categorie, immagini) - Gestione magazzini multipli (opzionale) - Movimenti di magazzino (carico, scarico, trasferimento) - Giacenze e disponibilità - Inventario e rettifiche - Alert scorte minime - Codici a barre / QR code (opzionale) - Tracciabilità lotti/serial (opzionale) **Entità principali:** - `Article` - Articolo/prodotto - `ArticleCategory` - Categorie articoli - `Warehouse` - Magazzino fisico - `StockMovement` - Movimento di magazzino - `StockLevel` - Giacenza per articolo/magazzino #### 2. Acquisti (purchases) **Funzionalità:** - Anagrafica fornitori - Richieste di acquisto (RDA) - Ordini a fornitore (OdA) - DDT entrata (ricezione merce) - Fatture passive - Listini fornitore - Storico prezzi **Entità principali:** - `Supplier` - Fornitore - `PurchaseRequest` - Richiesta d'acquisto - `PurchaseOrder` - Ordine a fornitore - `PurchaseOrderLine` - Riga ordine - `GoodsReceipt` - DDT entrata - `SupplierInvoice` - Fattura passiva #### 3. Vendite (sales) **Funzionalità:** - Anagrafica clienti - Preventivi - Ordini cliente - DDT uscita - Fatture attive - Listini cliente - Provvigioni agenti (opzionale) **Entità principali:** - `Customer` - Cliente - `Quote` - Preventivo - `SalesOrder` - Ordine cliente - `SalesOrderLine` - Riga ordine - `DeliveryNote` - DDT uscita - `Invoice` - Fattura attiva #### 4. Produzione (production) **Funzionalità:** - Distinte base (BOM) - Cicli di lavorazione - Ordini di produzione - Avanzamento produzione - Pianificazione (MRP semplificato) - Costi di produzione **Entità principali:** - `BillOfMaterials` - Distinta base - `BomComponent` - Componente distinta - `WorkCenter` - Centro di lavoro - `ProductionOrder` - Ordine di produzione - `ProductionStep` - Fase produzione #### 5. Qualità (quality) **Funzionalità:** - Piani di controllo - Controlli in accettazione - Controlli in produzione - Non conformità - Azioni correttive - Certificazioni/documenti **Entità principali:** - `ControlPlan` - Piano di controllo - `QualityCheck` - Controllo qualità - `NonConformity` - Non conformità - `CorrectiveAction` - Azione correttiva - `Certificate` - Certificazione --- ### Problemi Risolti Durante Implementazione 31. **EF Core Migration Fallita per Tabella Esistente (FIX 29/11/2025):** - **Problema:** `dotnet ef database update` falliva con "Table 'Clienti' already exists" - **Causa:** La migration tentava di ricreare tutte le tabelle invece di aggiungere solo quelle nuove - **Soluzione:** Create tabelle manualmente via SQLite: ```sql CREATE TABLE AppModules ( Id INTEGER PRIMARY KEY AUTOINCREMENT, Code TEXT NOT NULL UNIQUE, Name TEXT NOT NULL, ... ); CREATE TABLE ModuleSubscriptions ( Id INTEGER PRIMARY KEY AUTOINCREMENT, ModuleId INTEGER NOT NULL REFERENCES AppModules(Id), ... ); ``` - **File:** Database SQLite, rimossa migration problematica 32. **TypeScript Unused Variables Build Errors (FIX 29/11/2025):** - **Problema:** Build frontend falliva per variabili importate ma non usate - **Soluzione:** Rimossi import inutilizzati: - `ModuleGuard.tsx`: Rimosso `CircularProgress`, `showLoader` - `ModuleContext.tsx`: Rimosso `useState`, `useEffect` - `ModulePurchasePage.tsx`: Rimosso `moduleService` import - `ModulesAdminPage.tsx`: Rimosso `PowerIcon`, `CheckIcon`, `CancelIcon` - **File:** Vari componenti frontend 33. **EF Core Code First vs Database First (FIX 29/11/2025):** - **Problema:** Le tabelle venivano create manualmente con SQL invece di usare EF Core migrations - **Causa:** `db.Database.EnsureCreated()` non supporta migrations e crea le tabelle direttamente - **Soluzione:** - Sostituito `EnsureCreated()` con `MigrateAsync()` in `Program.cs` - Rimosso database e migrations esistenti - Creata nuova migration `InitialCreate` con `dotnet ef migrations add` - Le migrations vengono ora applicate automaticamente all'avvio - **File:** `Program.cs`, `src/Apollinare.Infrastructure/Migrations/20251129134709_InitialCreate.cs` 34. **Modulo Warehouse - Struttura Completa (IMPLEMENTATO 29/11/2025):** - **Entities in `/src/Apollinare.Domain/Entities/Warehouse/`:** - `WarehouseLocation.cs` - Magazzini (Physical, Virtual, Transit) - `WarehouseArticle.cs` - Articoli con batch/serial flags - `WarehouseArticleCategory.cs` - Categorie gerarchiche - `ArticleBatch.cs` - Lotti con scadenza - `ArticleSerial.cs` - Numeri seriali - `StockLevel.cs` - Giacenze - `StockMovement.cs` + `StockMovementLine.cs` - Movimenti - `MovementReason.cs` - Causali - `ArticleBarcode.cs` - Multi-barcode - `StockValuation.cs` + `StockValuationLayer.cs` - Valorizzazione - `InventoryCount.cs` + `InventoryCountLine.cs` - Inventari - **Service:** `WarehouseService.cs` con CRUD completo, movimenti, giacenze, valorizzazione - **Controllers:** `WarehouseLocationsController`, `WarehouseArticlesController`, `WarehouseArticleCategoriesController`, `StockMovementsController`, `StockLevelsController` - **API Endpoints principali:** - `GET/POST /api/warehouse/locations` - Magazzini - `GET/POST /api/warehouse/articles` - Articoli - `GET/POST /api/warehouse/categories` - Categorie - `POST /api/warehouse/movements/inbound` - Carichi - `POST /api/warehouse/movements/outbound` - Scarichi - `POST /api/warehouse/movements/{id}/confirm` - Conferma movimento - `GET /api/warehouse/articles/{id}/stock` - Giacenza articolo 35. **Sistema Codici Automatici Configurabili (IMPLEMENTATO 30/11/2025):** - **Obiettivo:** Sistema per generare automaticamente codici univoci per tutte le entità (articoli, magazzini, movimenti, clienti, eventi, ecc.) - **Entity:** `AutoCode.cs` in `/src/Apollinare.Domain/Entities/` - `EntityCode` - Identificativo entità (es. "warehouse_article") - `EntityName` - Nome visualizzato - `Prefix` - Prefisso per {PREFIX} - `Pattern` - Pattern con placeholder (es. "{PREFIX}{YYYY}-{SEQ:5}") - `LastSequence` - Ultimo numero usato - `ResetSequenceYearly` / `ResetSequenceMonthly` - Reset automatico - `IsEnabled` - Abilita generazione - `IsReadOnly` - Codice non modificabile - `ModuleCode` - Raggruppa per modulo - **Service:** `AutoCodeService.cs` in `/src/Apollinare.API/Services/` - `GenerateNextCodeAsync(entityCode)` - Genera e incrementa - `PreviewNextCodeAsync(entityCode)` - Anteprima senza incremento - `IsCodeUniqueAsync(entityCode, code)` - Verifica univocità - `ResetSequenceAsync(entityCode)` - Reset manuale - `SeedDefaultConfigurationsAsync()` - Seed configurazioni default - **Controller:** `AutoCodesController.cs` - **Frontend:** - `AutoCodesAdminPage.tsx` - Pagina admin con accordions per modulo - `autoCodeService.ts` - API calls - `autoCode.ts` - Types - **Pattern supportati:** - `{PREFIX}` - Prefisso configurabile - `{SEQ:n}` - Sequenza con n cifre (es. {SEQ:5} → 00001) - `{YYYY}`, `{YY}` - Anno 4 o 2 cifre - `{MM}`, `{DD}` - Mese e giorno - Testo statico (es. "-", "/") - **Entità preconfigurate:** - Core: cliente, evento, articolo - Warehouse: warehouse_location, warehouse_article, warehouse_category, stock_movement, inventory_count, article_batch - Purchases (future): purchase_order, supplier - Sales (future): sales_order, invoice - **Esempio utilizzo nel codice:** ```csharp // Nel service che crea un articolo var code = await _autoCodeService.GenerateNextCodeAsync("warehouse_article"); if (code != null) article.Code = code; ``` 36. **Campo Codice Readonly e Codice Alternativo (FIX 29/11/2025 pomeriggio):** - **Problema:** Errore 400 Bad Request "The Code field is required" quando si creava un nuovo articolo di magazzino - **Causa:** Il `CreateArticleDto` nel backend richiedeva il campo `Code` come obbligatorio, ma il frontend non lo inviava (correttamente, perché dovrebbe essere auto-generato) - **Soluzione:** - Backend: Modificato `CreateArticleDto` per rimuovere `Code` e aggiungere `AlternativeCode` opzionale - Backend: Modificato `UpdateArticleDto` per rimuovere `Code` e aggiungere `AlternativeCode` - Backend: Aggiunto `AlternativeCode` ad `ArticleDto` per la risposta - Backend: Aggiornato `MapFromDto` per non settare `Code` (viene generato da `WarehouseService.CreateArticleAsync`) - Backend: Aggiornato `UpdateFromDto` per non modificare `Code` (immutabile dopo creazione) - Backend: Aggiornato `MapToDto` per includere `AlternativeCode` - Frontend: I tipi erano già corretti, il form già mostrava "(Generato al salvataggio)" - **File modificati:** - `src/Apollinare.API/Modules/Warehouse/Controllers/WarehouseArticlesController.cs` - **Comportamento risultante:** - Creazione: `Code` generato automaticamente (es. `WA000001`), `AlternativeCode` opzionale - Modifica: `Code` non modificabile, `AlternativeCode` modificabile - UI: Campo Codice sempre disabled, mostra placeholder in creazione