Files
zentral/CLAUDE.md
2025-11-29 01:45:53 +01:00

55 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

Stato progetto: Migrazione Oracle APEX → .NET + React TypeScript in corso

Lavoro completato nell'ultima sessione:

  • 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 (Report System):

  1. CRITICO: Posizionamento assoluto PDF - COMPLETATO
  2. Implementare caricamento immagini reali - COMPLETATO
  3. FIX: Rotazione oggetti nel PDF - COMPLETATO
  4. Gestione Multi-Pagina - COMPLETATO
  5. Aggiungere rendering tabelle dinamiche per collection
  6. Gestire sezioni header/footer ripetute su ogni pagina
  7. UI per relazioni tra dataset multipli

Comandi utili:

# Build backend
cd src && dotnet build

# Build frontend
cd frontend && npm run build

# Run backend (porta 5000)
cd src/Apollinare.API && dotnet run

# Run frontend dev (porta 5173)
cd frontend && npm run dev

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

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.