diff --git a/CLAUDE.md b/CLAUDE.md index 10e7eca..c5bb4c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,2077 +1 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - ---- - -## ISTRUZIONI OBBLIGATORIE PER CLAUDE - -### Auto-Aggiornamento CLAUDE.md - -**OBBLIGATORIO:** Claude DEVE aggiornare questo file CLAUDE.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:** - -- **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. [ ] **Frontend modulo Magazzino** - Pagine React per warehouse -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 +only use DEVELOPMENT.md diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..3399c2f --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,2077 @@ +# 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:** + +- **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. [ ] **Frontend modulo Magazzino** - Pagine React per warehouse +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 diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..c5bb4c3 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1 @@ +only use DEVELOPMENT.md diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index b83160a..0000000 --- a/PLAN.md +++ /dev/null @@ -1,343 +0,0 @@ -# Piano: Collaborazione Real-Time nel Report Designer - -## Obiettivo - -Implementare la collaborazione in tempo reale nel Report Designer, simile a Google Docs o Excel Online. Quando un utente modifica un template, tutti gli altri utenti collegati allo stesso template vedono le modifiche istantaneamente. - ---- - -## Architettura Proposta - -### Concetti Chiave - -1. **Room-based Collaboration**: Ogni template ha una "room" SignalR dedicata -2. **Operational Transformation Semplificata**: Invece di OT completo (complesso), usiamo un modello "last-write-wins" con sync frequente -3. **Presence Awareness**: Gli utenti vedono chi altro sta modificando il template -4. **Cursor/Selection Sharing**: Mostra quale elemento sta selezionando ogni utente - -### Flusso Dati - -``` -User A modifica elemento - ↓ -Template locale aggiornato (come ora) - ↓ -SignalR invia delta alla room - ↓ -Backend riceve e ritrasmette a tutti nella room (eccetto mittente) - ↓ -User B/C/... ricevono delta - ↓ -Applicano delta al loro template locale - ↓ -Canvas si aggiorna automaticamente (già funziona così) -``` - ---- - -## Implementazione Dettagliata - -### FASE 1: Backend - ReportCollaborationHub - -**File:** `/src/Apollinare.API/Hubs/ReportCollaborationHub.cs` - -Nuovo Hub dedicato per la collaborazione sui report: - -```csharp -public class ReportCollaborationHub : Hub -{ - // Stato in-memory degli utenti per template - private static ConcurrentDictionary> _templateUsers = new(); - - // Join a template room - public async Task JoinTemplate(int templateId, string userName, string userColor) - - // Leave template room - public async Task LeaveTemplate(int templateId) - - // Broadcast element change to room - public async Task ElementChanged(int templateId, ElementChangeDto change) - - // Broadcast element added - public async Task ElementAdded(int templateId, AprtElement element) - - // Broadcast element deleted - public async Task ElementDeleted(int templateId, string elementId) - - // Broadcast page changes - public async Task PageChanged(int templateId, PageChangeDto change) - - // Broadcast selection change (which element user is editing) - public async Task SelectionChanged(int templateId, string? elementId) - - // Broadcast cursor position (optional, for live cursors) - public async Task CursorMoved(int templateId, float x, float y) - - // Request full template sync (when joining late or after reconnect) - public async Task RequestSync(int templateId) - - // Send full template state (host responds to sync requests) - public async Task SendSync(int templateId, string connectionId, AprtTemplate template) -} -``` - -**DTOs:** - -```csharp -public record CollaboratorInfo(string ConnectionId, string UserName, string Color, string? SelectedElementId); - -public record ElementChangeDto( - string ElementId, - string ChangeType, // "position", "style", "content", "visibility", etc. - object NewValue -); - -public record PageChangeDto( - string PageId, - string ChangeType, // "added", "deleted", "reordered", "renamed", "settings" - object? Data -); -``` - -### FASE 2: Frontend - Servizio Collaborazione - -**File:** `/frontend/src/services/reportCollaboration.ts` - -```typescript -class ReportCollaborationService { - private connection: HubConnection | null = null; - private templateId: number | null = null; - private listeners: Map> = new Map(); - - // Connessione e gestione room - async joinTemplate( - templateId: number, - userName: string, - userColor: string, - ): Promise; - async leaveTemplate(): Promise; - - // Invio modifiche (chiamati dal ReportEditorPage) - sendElementChange(elementId: string, changeType: string, newValue: any): void; - sendElementAdded(element: AprtElement): void; - sendElementDeleted(elementId: string): void; - sendPageChange(pageId: string, changeType: string, data?: any): void; - sendSelectionChange(elementId: string | null): void; - - // Sottoscrizione eventi (per ricevere modifiche da altri) - onElementChanged(callback: (change: ElementChange) => void): () => void; - onElementAdded(callback: (element: AprtElement) => void): () => void; - onElementDeleted(callback: (elementId: string) => void): () => void; - onPageChanged(callback: (change: PageChange) => void): () => void; - onCollaboratorsChanged( - callback: (collaborators: Collaborator[]) => void, - ): () => void; - onSelectionChanged( - callback: (userId: string, elementId: string | null) => void, - ): () => void; - onSyncRequested(callback: (requesterId: string) => void): () => void; - - // Sync - requestSync(): void; - sendSync(connectionId: string, template: AprtTemplate): void; -} -``` - -### FASE 3: Frontend - Integrazione in ReportEditorPage - -**Modifiche a:** `/frontend/src/pages/ReportEditorPage.tsx` - -1. **Nuovo State per collaborazione:** - -```typescript -const [collaborators, setCollaborators] = useState([]); -const [remoteSelections, setRemoteSelections] = useState>( - new Map(), -); -const [isCollaborating, setIsCollaborating] = useState(false); -``` - -2. **Join/Leave room al mount/unmount:** - -```typescript -useEffect(() => { - if (!isNew && id) { - const userName = getCurrentUserName(); // Da auth context - const userColor = generateUserColor(userName); - - reportCollaborationService - .joinTemplate(Number(id), userName, userColor) - .then(() => setIsCollaborating(true)); - - return () => { - reportCollaborationService.leaveTemplate(); - }; - } -}, [id, isNew]); -``` - -3. **Sottoscrizione eventi remoti:** - -```typescript -useEffect(() => { - if (!isCollaborating) return; - - const unsubscribers = [ - reportCollaborationService.onElementChanged((change) => { - // Applica modifica senza creare history entry - historyActions.setWithoutHistory((prev) => { - // ... applica change.newValue a element con change.elementId - }); - }), - - reportCollaborationService.onElementAdded((element) => { - historyActions.setWithoutHistory((prev) => ({ - ...prev, - elements: [...prev.elements, element], - })); - }), - - // ... altri handler - - reportCollaborationService.onCollaboratorsChanged(setCollaborators), - ]; - - return () => unsubscribers.forEach((unsub) => unsub()); -}, [isCollaborating]); -``` - -4. **Invio modifiche locali:** - -Modificare `handleUpdateElement` per inviare anche via SignalR: - -```typescript -const handleUpdateElement = useCallback( - (elementId: string, updates: Partial) => { - // Aggiorna stato locale (come ora) - historyActions.set((prev) => ({...})); - - // Invia a collaboratori - if (isCollaborating) { - reportCollaborationService.sendElementChange(elementId, "update", updates); - } - }, - [historyActions, isCollaborating], -); -``` - -### FASE 4: UI Collaborazione - -**File:** `/frontend/src/components/reportEditor/CollaboratorsBar.tsx` - -Barra che mostra gli utenti connessi: - -```typescript -interface CollaboratorsBarProps { - collaborators: Collaborator[]; - remoteSelections: Map; // userId -> elementId -} - -// Mostra: -// - Avatar circolari colorati per ogni collaboratore -// - Tooltip con nome utente -// - Indicatore "sta modificando [elemento]" -// - Badge con conteggio totale collaboratori -``` - -**Modifiche a EditorCanvas:** Evidenziare elementi selezionati da altri utenti con bordo colorato. - -### FASE 5: Gestione Conflitti - -Per semplicità, usiamo strategia **"last-write-wins"** con alcune ottimizzazioni: - -1. **Elementi diversi**: Nessun conflitto, modifiche applicate indipendentemente -2. **Stesso elemento, proprietà diverse**: Merge delle proprietà -3. **Stesso elemento, stessa proprietà**: Ultima modifica vince -4. **Lock visivo**: Quando un utente seleziona un elemento, gli altri vedono un indicatore - -**Opzionale (Fase futura):** Lock pessimistico - solo un utente alla volta può modificare un elemento. - ---- - -## File da Creare/Modificare - -### Nuovi File - -| File | Descrizione | -| ----------------------------------------------------------- | ----------------------------------- | -| `src/Apollinare.API/Hubs/ReportCollaborationHub.cs` | Hub SignalR per collaborazione | -| `src/Apollinare.API/Models/CollaborationDtos.cs` | DTOs per messaggi collaborazione | -| `frontend/src/services/reportCollaboration.ts` | Client SignalR per collaborazione | -| `frontend/src/components/reportEditor/CollaboratorsBar.tsx` | UI collaboratori connessi | -| `frontend/src/types/collaboration.ts` | Types TypeScript per collaborazione | - -### File da Modificare - -| File | Modifiche | -| -------------------------------------------------------- | ------------------------------------------------- | -| `src/Apollinare.API/Program.cs` | Registrare nuovo hub `/hubs/report-collaboration` | -| `frontend/src/pages/ReportEditorPage.tsx` | Integrare collaborazione, stato collaboratori | -| `frontend/src/components/reportEditor/EditorCanvas.tsx` | Mostrare selezioni remote | -| `frontend/src/components/reportEditor/EditorToolbar.tsx` | Mostrare CollaboratorsBar | - ---- - -## Stima Complessità - -| Fase | Complessità | Note | -| ---------------------- | ----------- | ---------------------------------------- | -| 1. Backend Hub | Media | SignalR groups, gestione stato in-memory | -| 2. Frontend Service | Media | Gestione connessione, eventi | -| 3. Integrazione Editor | Alta | Molti handler da modificare | -| 4. UI Collaboratori | Bassa | Componente semplice | -| 5. Gestione Conflitti | Media | Merge logic | - ---- - -## Considerazioni Aggiuntive - -### Performance - -- Throttling degli aggiornamenti durante drag (ogni 50-100ms invece di ogni frame) -- Debounce per modifiche testo (300ms) -- Batch di modifiche multiple in singolo messaggio - -### Scalabilità - -- Per deployment multi-server: usare Redis backplane per SignalR -- Considerare Azure SignalR Service per produzione - -### Autenticazione - -- Aggiungere autenticazione al hub (verificare che utente abbia accesso al template) -- Usare JWT token per identificare utente - -### Edge Cases - -- Reconnessione dopo disconnessione: richiedere sync completo -- Template eliminato mentre utenti connessi: notificare e chiudere -- Conflitto salvataggio: merge o "force save" con conferma - ---- - -## Ordine di Implementazione Consigliato - -1. **Backend Hub base** - Join/Leave room, broadcast semplice -2. **Frontend service base** - Connessione, invio/ricezione messaggi -3. **Integrazione minima** - Solo sync modifiche elementi (no UI collaboratori) -4. **Test funzionale** - Verificare che modifiche si propaghino -5. **UI Collaboratori** - Mostrare chi è connesso -6. **Selezioni remote** - Evidenziare elementi selezionati da altri -7. **Ottimizzazioni** - Throttling, batching, gestione conflitti - ---- - -## Domande per l'Utente - -Prima di procedere, confermare: - -1. **Autenticazione**: Il sistema ha già autenticazione utenti? Devo usare un sistema mock per ora? -2. **Persistenza stato**: Le modifiche devono essere salvate automaticamente o solo quando l'utente clicca "Salva"? -3. **Lock elementi**: Vuoi che solo un utente alla volta possa modificare un elemento (lock pessimistico)? -4. **Cursori live**: Vuoi vedere il cursore degli altri utenti in tempo reale (come Google Docs)? -5. **Nome utente**: Da dove prendo il nome utente da mostrare? (localStorage, auth context, prompt?) diff --git a/src/Apollinare.API/apollinare.db-shm b/src/Apollinare.API/apollinare.db-shm deleted file mode 100644 index 4b9e522..0000000 Binary files a/src/Apollinare.API/apollinare.db-shm and /dev/null differ diff --git a/src/Apollinare.API/apollinare.db-wal b/src/Apollinare.API/apollinare.db-wal deleted file mode 100644 index 114b37d..0000000 Binary files a/src/Apollinare.API/apollinare.db-wal and /dev/null differ