Files
zentral/CLAUDE.md
2025-11-29 16:08:11 +01:00

92 KiB
Raw Blame History

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:

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:

      # 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 | nullselectedElementIds: 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. 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!):

# 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:

-- 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:

{
  "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

  • Editor visuale drag-and-drop con Fabric.js
  • Supporto elementi: testo, forme, linee, tabelle, immagini (placeholder)
  • Gestione zoom (25% - 300%)
  • Griglia e snap to grid
  • Undo/Redo (max 100 stati)
  • Shortcuts tastiera (Ctrl+Z, Ctrl+Y, Ctrl+S, Delete)
  • Pannello proprietà con posizione, stile, contenuto
  • Data binding con browser campi disponibili
  • Selezione multipla dataset per template
  • Preview con selezione entità reali
  • Salvataggio/caricamento template
  • Import/export template come file .aprt
  • Clone template
  • Generazione PDF default per eventi
  • Formattazione campi (valuta, data, numero, percentuale)
  • Responsive design completo (mobile, tablet, desktop)
  • Toolbar professionale stile Canva/Figma con sezioni etichettate
  • Toolbar contestuale per formattazione rapida (testo, forme, immagini)
  • Color picker integrato con palette preset
  • Auto-save con toggle (abilitato di default, 1s debounce)
  • 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
  • Multi-pagina - Editor pagine multiple - COMPLETATO
  • Righelli e guide - Ausili allineamento avanzati
  • Esportazione altri formati - Excel, Word oltre PDF

Checklist Completamento Report System

Backend

  • Entity ReportTemplate
  • Entity ReportFont
  • Entity ReportImage
  • ReportTemplatesController (CRUD + clone + import/export)
  • ReportResourcesController (fonts + images)
  • ReportsController (generate + preview + schema + datasets)
  • ReportGeneratorService con QuestPDF
  • Schema dati per tutti i dataset (evento, cliente, location, articolo, risorsa)
  • Generazione PDF default evento
  • Generazione PDF multi-pagina
  • Rendering tabelle dinamiche da collection
  • Supporto font custom nel PDF
  • Rendering immagini da storage

Frontend

  • ReportTemplatesPage (lista + filtri + azioni)
  • ReportEditorPage (editor principale)
  • EditorCanvas con Fabric.js v6
  • EditorToolbar completa
  • PropertiesPanel (posizione + stile + contenuto)
  • DataBindingPanel multi-dataset
  • DatasetSelector
  • PreviewDialog con selezione entità
  • Types APRT completi
  • Services API completi
  • Undo/Redo
  • Keyboard shortcuts
  • PageNavigator (gestione multi-pagina)
  • Navigazione pagine in toolbar
  • 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

  • Build frontend senza errori
  • Build backend senza errori
  • Test funzionale editor canvas
  • Test generazione PDF con dati reali (binding e formattazione funzionanti)
  • Test import/export template
  • Test con font e immagini custom

Documentazione

  • Documentazione APRT metalanguage
  • Lista API endpoints
  • 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
      // 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
      // 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 [<Divider key="..." />, <MenuItem key="..." />]
  7. HTML p/div nesting (FIX 27/11/2025):

    • Error: <div> cannot be a descendant of <p>
    • Fix: <ListItemText secondaryTypographyProps={{ component: "div" }} />
  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:
      // SVG viewBox in mm - 1 unità SVG = 1mm
      svgBuilder.AppendLine($"<svg xmlns=\"...\" " +
          $"width=\"{contentWidthMm}mm\" height=\"{contentHeightMm}mm\" " +
          $"viewBox=\"0 0 {contentWidthMm} {contentHeightMm}\">");
      
    • 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:
      // 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:

      // 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):

      // 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: GeneratePdfAsyncRenderContentToBitmapRenderElementToCanvasRenderTextToCanvasResolveContentResolveBindingWithFormatResolveBindingPath
      4. ResolveBindingPath() ora usa i valori reali dal contesto:
      "$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 <Collapse> 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:
      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: 100vhheight: 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

// App.tsx
<Route path="/report-templates" element={<ReportTemplatesPage />} />
<Route path="/report-editor" element={<ReportEditorPage />} />
<Route path="/report-editor/:id" element={<ReportEditorPage />} />

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):

// Filtra voci menu in base a moduli attivi
const menuItems = allMenuItems.filter(
  (item) => !item.moduleCode || activeModuleCodes.includes(item.moduleCode),
);

Routing (App.tsx):

// Route protette da modulo
<Route
  path="/warehouse/*"
  element={
    <ModuleGuard moduleCode="warehouse">
      <WarehouseRoutes />
    </ModuleGuard>
  }
/>

// Pagina acquisto modulo
<Route path="/modules/purchase/:code" element={<ModulePurchasePage />} />

Logica Backend

ModuleService:

public class ModuleService
{
    // Verifica se modulo è attivo (usato da altri servizi)
    public async Task<bool> IsModuleEnabledAsync(string code);

    // Verifica subscription valida (non scaduta)
    public async Task<bool> 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:

  • Entity AppModule
  • Entity ModuleSubscription
  • ModuleService con logica business
  • ModulesController con tutti gli endpoint
  • DbSet e migration EF Core (tabelle create manualmente in SQLite)
  • Seed dati iniziali (5 moduli)
  • Job controllo scadenze (opzionale - futuro)

Frontend:

  • Types module.ts
  • Service moduleService.ts
  • Context ModuleContext.tsx
  • Component ModuleGuard.tsx
  • Page ModulesAdminPage.tsx
  • Page ModulePurchasePage.tsx
  • Integrazione Layout.tsx per menu dinamico
  • Route protection in App.tsx

Testing:

  • API CRUD moduli
  • API subscription
  • Redirect su modulo disattivato
  • 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:

// 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<string> CustomFields { get; set; } = new();
}

Esempio hook point per estensioni:

// IWarehouseService.cs
public interface IWarehouseService
{
    // Metodi standard
    Task<Article> GetArticleAsync(int id);
    Task<Article> CreateArticleAsync(ArticleDto dto);

    // Hook points per customizzazione
    event Func<Article, Task>? OnArticleCreated;
    event Func<StockMovement, Task<bool>>? OnBeforeStockMovement;
    event Func<StockMovement, Task>? 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

  1. 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:
      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
  2. 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
  3. 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
  4. 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
  5. 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:
      // Nel service che crea un articolo
      var code = await _autoCodeService.GenerateNextCodeAsync("warehouse_article");
      if (code != null)
          article.Code = code;
      
  6. 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