Files
zentral/CLAUDE.md
2025-11-29 01:36:07 +01:00

1209 lines
53 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
---
## ISTRUZIONI OBBLIGATORIE PER CLAUDE
### Auto-Aggiornamento CLAUDE.md
**OBBLIGATORIO:** Claude DEVE aggiornare questo file CLAUDE.md ogni volta che:
1. **Viene completato un task significativo** (fix, nuova feature, refactoring importante)
2. **Viene risolto un problema tecnico** che potrebbe ripresentarsi in futuro
3. **Si scopre un pattern/workaround** importante da ricordare
4. **Termina una sessione di lavoro** - aggiornare "Quick Start - Session Recovery"
**Cosa aggiornare:**
- **Sezione "Quick Start - Session Recovery":**
- Aggiornare "Ultima sessione" con data corrente
- Spostare lavoro completato da "ultima sessione" a "sessioni precedenti"
- Aggiungere nuovi task completati alla lista
- Aggiornare "Prossimi task prioritari" (spuntare completati, aggiungere nuovi)
- **Sezione "Problemi Risolti (da ricordare)":**
- Aggiungere OGNI problema tecnico risolto con:
- Descrizione del problema
- Causa root
- Soluzione implementata
- File coinvolti
- **Checklist:**
- Aggiornare checkbox `[x]` per task completati
- Aggiungere nuovi task se scoperti durante il lavoro
**Formato per nuovi problemi risolti:**
```markdown
XX. **Nome Problema (FIX/IMPLEMENTATO DATA):** - **Problema:** Descrizione breve - **Causa:** Perché succedeva - **Soluzione:** Come è stato risolto - **File:** File modificati
```
**NON dimenticare:** Questo file è la memoria persistente tra sessioni. Se non viene aggiornato, il lavoro fatto andrà perso e dovrà essere riscoperto.
---
## Quick Start - Session Recovery
**Ultima sessione:** 29 Novembre 2025
**Stato progetto:** Migrazione Oracle APEX → .NET + React TypeScript in corso
**Lavoro completato nell'ultima sessione:**
- **NUOVA FEATURE: Selezione Multipla nel Report Editor** - COMPLETATO
- Implementato sistema di selezione multipla personalizzato (senza usare ActiveSelection di Fabric.js che causava riposizionamento oggetti)
- **Selezione con rettangolo di trascinamento**: trascinando sul canvas vuoto appare rettangolo blu tratteggiato, al rilascio seleziona tutti gli oggetti che intersecano
- **Shift+click**: aggiunge/rimuove oggetti dalla selezione
- **Spostamento multiplo**: quando più oggetti sono selezionati, trascinandone uno si spostano tutti insieme
- **Feedback visivo**: oggetti selezionati mostrano bordo blu (#1976d2) e ombra
- **Gestione corretta degli eventi**: i ref (`selectedElementIdsRef`, `onSelectElementRef`, etc.) evitano stale closures negli event handler
- **File modificati:**
- `EditorCanvas.tsx` - Nuovi handler `handleMouseDown`, `handleMouseUp`, logica selezione multipla, refs per valori correnti
- `ReportEditorPage.tsx` - Cambiato `selectedElementId: string | null``selectedElementIds: string[]`, aggiornati tutti i riferimenti
**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 notte):**
- **NUOVA FEATURE: Sincronizzazione Real-Time Efficiente** - COMPLETATO
- **Prima:** Al salvataggio veniva inviata solo una notifica `DataSaved`, l'altra sessione ricaricava il template dal server (lento)
- **Dopo:** Al salvataggio viene inviato l'intero template via SignalR (`BroadcastTemplateSync`), l'altra sessione lo applica direttamente (istantaneo)
- **Compressione automatica** per template > 10KB usando gzip via browser's CompressionStream API
- **Version tracking** per gestione conflitti (ignora template con versione più vecchia)
- Nuovo metodo Hub: `BroadcastTemplateSync(roomKey, templateJson, version, compressed)`
- Nuovo evento: `TemplateSync` con decompressione automatica lato client
- **FIX: Limite messaggio SignalR** - Aumentato `MaximumReceiveMessageSize` a 1MB in `Program.cs` (default era 32KB)
- **File modificati:**
- `CollaborationHub.cs` - Aggiunto `BroadcastTemplateSync` e `TemplateSyncMessage` con campo `Compressed`
- `collaboration.ts` - Aggiunto `broadcastTemplateSync()`, utilities compressione/decompressione (`compressString`, `decompressString`), handler `TemplateSync`
- `CollaborationContext.tsx` - Esposto `broadcastTemplateSync` e `onTemplateSync`
- `ReportEditorPage.tsx` - Sostituito `sendDataSaved()` con `broadcastTemplateSync()`, aggiunto handler per applicare template ricevuti
- `Program.cs` - Configurato SignalR con `MaximumReceiveMessageSize = 1MB`
- **FIX: Auto-Save Event-Based con Debounce** - COMPLETATO
- Riscritto auto-save per triggerare ad ogni modifica (non a intervalli)
- Debounce di 500ms per evitare salvataggi multipli durante editing rapido
- Usa `useRef` per `saveMutation`, `template`, `templateInfo` per evitare re-creazione del timeout
- Dipendenze effect: solo `autoSaveEnabled`, `isNew`, `hasUnsavedChanges`, `templateHistory.undoCount`
- Il check `isPending` avviene al momento dell'esecuzione del timeout, non come dipendenza
**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 pomeriggio - sera):**
- **NUOVA FEATURE: Sistema Collaborazione Real-Time Globale** - COMPLETATO
- Migrato da sistema report-specific a sistema globale per tutta l'app
- `CollaborationHub.cs` - Hub SignalR generico per qualsiasi entità/pagina
- `collaboration.ts` - Service singleton per gestione connessione
- `CollaborationContext.tsx` - React Context con hooks (`useCollaboration`, `useCollaborationRoom`)
- `CollaborationIndicator.tsx` - Indicatore globale nella UI
- Room-based collaboration con formato `{entityType}:{entityId}`
- **FIX: Incompatibilità versione SignalR** - Rimosso package `Microsoft.AspNetCore.SignalR.Common` v10.0.0 incompatibile con .NET 9, downgrade client `@microsoft/signalr` a v8.0.7
- **FIX: Auto-save non funzionante** - Usati `useRef` per evitare re-run dell'effect causato da `saveMutation` nelle dependencies
**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 tarda notte):**
- **NUOVA FEATURE: Toolbar Report Designer Migliorata Drasticamente** - COMPLETATO
- Design moderno stile Canva/Figma con gradient buttons e animazioni fluide
- **Sezioni etichettate** su desktop (INSERISCI, MODIFICA, CRONOLOGIA, VISTA, ZOOM, PAGINA)
- **Toolbar contestuale** dinamica che appare quando un elemento è selezionato:
- Per testo: formattazione (grassetto, corsivo, sottolineato), allineamento, color picker
- Per forme/linee: color picker riempimento/bordo, spessore bordo
- Per immagini: indicatore mantieni proporzioni
- Mostra tipo elemento, nome e dimensioni in tempo reale
- **Color Picker integrato** con palette di 20 colori predefiniti
- **Indicatore stato salvataggio** visivo: Salvato (verde), Non salvato (arancione), Salvataggio... (spinner)
- **Badge** sul pulsante Snap che mostra quante opzioni sono attive
- **Zoom esteso** fino al 300% con pulsante "Adatta alla finestra"
- Pannello scorciatoie tastiera ampliato
- Aggiunto `textDecoration` a `AprtStyle` per supportare sottolineato
- **FIX: Indicatore "Non Salvato" errato** - RISOLTO
- Prima usava `canUndo` che indicava solo presenza di history
- Ora usa `undoCount` confrontato con `lastSavedUndoCount` per tracking accurato
- **NUOVA FEATURE: Auto-Save con Toggle** - COMPLETATO
- Salvataggio automatico dopo 1 secondo di inattività (debounce)
- **Abilitato di default**
- Toggle nella toolbar (icona AutoMode) per attivare/disattivare
- Quando auto-save è attivo, il pulsante "Salva" manuale è nascosto
- Quando auto-save è disattivo, il pulsante "Salva" appare
- Non si attiva per template nuovi (richiede primo salvataggio manuale)
- Non si attiva durante salvataggio in corso
**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 notte):**
- **NUOVA FEATURE: Responsive Design Completo** - COMPLETATO
- Tutta l'applicazione è ora responsive per mobile, tablet e desktop
- Breakpoints: mobile (<600px), tablet (600-900px), desktop (>900px)
- **Layout.tsx**: Sidebar collassata con icone su tablet, drawer mobile
- **ReportTemplatesPage**: Header stackato su mobile, FAB per nuovo template, dialog fullScreen
- **ReportEditorPage**: BottomNavigation + SwipeableDrawer (70vh) per pannelli su mobile, auto-zoom
- **EditorToolbar**: 3 varianti (mobile compatta con riga collassabile, tablet media, desktop completa)
- **Pannelli laterali** (DataBindingPanel, PropertiesPanel, PageNavigator): larghezza piena su mobile
- **DatasetSelector**: Header collassabile su mobile
- **PreviewDialog**: fullScreen su mobile con navigazione step-by-step (dataset → entity)
- **ImageUploadDialog**: fullScreen su mobile, area drag-drop ottimizzata, bottoni stacked
**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 sera):**
- **FIX: Variabili Globali Report ({{$pageNumber}}, {{$totalPages}}, ecc.)** - RISOLTO
- Le variabili speciali ora vengono correttamente risolte nel PDF finale
- Aggiunta classe `PageContext` per passare numero pagina e totale pagine durante il rendering
- Propagato `PageContext` attraverso tutta la catena di rendering (bitmap, text, binding resolution)
- `ResolveBindingPath()` ora restituisce valori reali invece di placeholder
**Lavoro completato nelle sessioni precedenti (28 Novembre 2025):**
- **NUOVA FEATURE: Gestione Multi-Pagina nel Report Designer** - Completata
- Nuovo tipo `AprtPage` per definire pagine con impostazioni individuali (size, orientation, margins, backgroundColor)
- Ogni elemento ha `pageId` per assegnazione a pagina specifica
- Nuovo componente `PageNavigator.tsx` - sidebar con lista pagine, context menu, rinomina, duplica, elimina, riordina
- Navigazione rapida pagine in `EditorToolbar.tsx` (pulsanti prev/next, indicatore "X / Y")
- `PropertiesPanel.tsx` aggiornato per mostrare/modificare impostazioni pagina corrente
- Backend `ReportGeneratorService.cs` genera PDF multi-pagina correttamente
- Migrazione automatica template legacy (elementi senza pageId assegnati a prima pagina)
- **FIX CRITICO: Rotazione oggetti nel PDF** - Gli oggetti ruotati nel canvas ora vengono posizionati correttamente nel PDF generato
- Implementata la formula Fabric.js per calcolare il centro di rotazione
- Coordinate corrette per `originX='left'`, `originY='top'`
**Lavoro completato nelle sessioni precedenti:**
- Sistema Report PDF con editor visuale (98% completato)
- Fabric.js v6 canvas editor funzionante
- Multi-dataset support con preview
- **FIX: Data binding PDF ora funzionante** - I campi dati vengono risolti correttamente
- **FIX: Formattazione campi** - Date, valute, numeri formattati correttamente nel PDF
- Sistema Virtual Dataset implementato (CRUD completo)
- SignalR real-time updates funzionante
- **Context Menu ricco** - Tasto destro con opzioni complete stile Canva (copia, taglia, incolla, layer, allineamento, trasformazioni)
- **FIX: Posizionamento assoluto PDF FUNZIONANTE** - SVG con viewBox in mm, coordinate corrette
- **FIX: Immagini nel PDF** - Data URI, API URL e risorse embedded ora renderizzate correttamente
- **FIX: Font size nel PDF** - Conversione corretta da px screen a mm per SVG
**Per riprendere il lavoro sui Report:**
1. Vai alla sezione "Report PDF System - Implementation Details" più sotto
2. Consulta la "Checklist Completamento Report System" per vedere cosa manca
3. I file principali sono:
- Backend: `/src/Apollinare.API/Controllers/ReportsController.cs`
- Frontend: `/frontend/src/pages/ReportEditorPage.tsx`
- Canvas: `/frontend/src/components/reportEditor/EditorCanvas.tsx`
- **Context Menu:** `/frontend/src/components/reportEditor/ContextMenu.tsx`
- **PDF Generator:** `/src/Apollinare.API/Services/Reports/ReportGeneratorService.cs`
- **Page Navigator:** `/frontend/src/components/reportEditor/PageNavigator.tsx`
**Prossimi task prioritari (Report System):**
1. [x] ~~**CRITICO: Posizionamento assoluto PDF**~~ - COMPLETATO
2. [x] ~~Implementare caricamento immagini reali~~ - COMPLETATO
3. [x] ~~**FIX: Rotazione oggetti nel PDF**~~ - COMPLETATO
4. [x] ~~**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:**
```bash
# 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:
```sql
-- Get all tables in schema
SELECT table_name FROM user_tables ORDER BY table_name;
-- Get table structure
SELECT column_name, data_type, nullable, data_default
FROM user_tab_columns
WHERE table_name = 'EVENTI'
ORDER BY column_id;
-- Get all procedures and functions
SELECT object_name, object_type
FROM user_objects
WHERE object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE')
ORDER BY object_type, object_name;
-- Get procedure source
SELECT text FROM user_source
WHERE name = 'EVENTI_AGGIORNA_QTA_LISTA'
ORDER BY line;
-- Get view definitions
SELECT view_name, text FROM user_views
WHERE view_name LIKE 'V_%' OR view_name LIKE 'VW_%';
-- Get foreign key relationships
SELECT a.constraint_name, a.table_name, a.column_name,
c_pk.table_name r_table_name, c_pk.constraint_name r_constraint_name
FROM user_cons_columns a
JOIN user_constraints c ON a.constraint_name = c.constraint_name
JOIN user_constraints c_pk ON c.r_constraint_name = c_pk.constraint_name
WHERE c.constraint_type = 'R'
ORDER BY a.table_name, a.constraint_name;
```
## File References
- `apollinare-db-connection.md` - Database connection details (credentials)
- `f112.sql` - Complete APEX export (53,282 lines)
- Application structure
- Page definitions
- Processes and validations
- LOVs and static data
- JavaScript libraries
## Development Approach
When working on migration tasks:
1. **Always query the database** to understand current data structure and relationships
2. **Extract PL/SQL source code** for procedures/functions before implementing equivalent C# logic
3. **Document business rules** discovered in stored procedures
4. **Preserve Italian field names** in database but consider English in application layer
5. **Test quantity calculations** thoroughly - they are core to the business
6. **Map APEX page flows** to React routes and components
7. **Identify reusable components** (grids, forms, lookups)
8. **Plan data migration** for BLOBs and complex relationships
## Key Terminology (Italian → English)
- **Scheda** → Card/Draft (Event status 0)
- **Preventivo** → Quote (Event status 10)
- **Confermato** → Confirmed (Event status 20)
- **Lista Prelievo** → Pick List (articles for event)
- **Articoli** → Articles/Items
- **Ospiti** → Guests
- **Risorse** → Resources (staff)
- **Degustazioni** → Tastings
- **Allestimenti** → Setups
- **Acconti** → Deposits/Advances
- **Impegni** → Commitments (inventory reservations)
## Notes
- The application is mature and in production use
- Italian language throughout (UI, database, code comments)
- Complex business logic embedded in database layer
- Heavy use of APEX-specific features (Interactive Grids, Dynamic Actions)
- Real-time calculations and validations are critical to user experience
---
## Report PDF System - Implementation Details
### Overview
Sistema completo di generazione report PDF con editor visuale drag-and-drop (stile Canva) e metalinguaggio APRT (Apollinare Report Template) per template portabili.
### Stack Tecnologico
**Backend:**
- QuestPDF (Community License) - Generazione PDF programmatica
- .NET 9 Web API con Entity Framework Core
- SQLite per storage template, font e immagini
**Frontend:**
- React 19 + TypeScript + Vite
- Fabric.js v6 - Canvas editor per design visuale
- Material-UI per componenti UI
### Stato Corrente dell'Implementazione
#### Backend (COMPLETATO)
**Entities** (`/src/Apollinare.Domain/Entities/`):
- `ReportTemplate.cs` - Template con JSON, thumbnail, metadata
- `ReportFont.cs` - Font custom uploadabili (TTF/OTF)
- `ReportImage.cs` - Immagini riutilizzabili nei report
**Services** (`/src/Apollinare.API/Services/Reports/`):
- `ReportGeneratorService.cs` - Parsing APRT e generazione PDF con QuestPDF
- `AprtModels.cs` - Modelli C# per il metalinguaggio APRT
**Controllers** (`/src/Apollinare.API/Controllers/`):
- `ReportTemplatesController.cs` - CRUD template, clone, import/export
- `ReportResourcesController.cs` - Gestione font e immagini
- `ReportsController.cs` - Generazione PDF, schema dati, dataset management
**API Endpoints disponibili:**
```
# Templates
GET /api/report-templates
GET /api/report-templates/{id}
POST /api/report-templates
PUT /api/report-templates/{id}
DELETE /api/report-templates/{id}
POST /api/report-templates/{id}/clone
GET /api/report-templates/{id}/export
POST /api/report-templates/import
GET /api/report-templates/categories
# Resources
GET /api/report-resources/fonts
POST /api/report-resources/fonts
DELETE /api/report-resources/fonts/{id}
GET /api/report-resources/fonts/families
GET /api/report-resources/images
POST /api/report-resources/images
DELETE /api/report-resources/images/{id}
# Report Generation
POST /api/reports/generate
GET /api/reports/evento/{eventoId}
POST /api/reports/preview
GET /api/reports/datasets
GET /api/reports/schema/{datasetId}
GET /api/reports/datasets/{datasetId}/entities
```
#### Frontend (COMPLETATO ~90%)
**Pagine** (`/frontend/src/pages/`):
- `ReportTemplatesPage.tsx` - Lista template con cards, filtri, import/export
- `ReportEditorPage.tsx` - Editor principale con undo/redo, shortcuts
**Componenti Editor** (`/frontend/src/components/reportEditor/`):
- `EditorCanvas.tsx` - Canvas Fabric.js per design visuale
- `EditorToolbar.tsx` - Toolbar con strumenti, zoom, grid, azioni
- `PropertiesPanel.tsx` - Pannello proprietà elemento/pagina
- `DataBindingPanel.tsx` - Browser campi dati con supporto multi-dataset
- `DatasetSelector.tsx` - Selezione dataset per template
- `PreviewDialog.tsx` - Dialog selezione entità per anteprima
**Types** (`/frontend/src/types/report.ts`):
- Definizioni complete APRT (AprtTemplate, AprtElement, AprtStyle, etc.)
- DTOs per API (ReportTemplateDto, DataSchemaDto, DatasetTypeDto, etc.)
- Utility functions (mmToPx, pxToMm, getPageDimensions)
**Services** (`/frontend/src/services/reportService.ts`):
- reportTemplateService - CRUD template
- reportFontService - Gestione font
- reportImageService - Gestione immagini
- reportGeneratorService - Generazione PDF e schema
### Metalinguaggio APRT (Apollinare Report Template)
Formato JSON esportabile/importabile per portabilità template:
```json
{
"version": "1.0",
"meta": {
"name": "Template Evento",
"pageSize": "A4",
"orientation": "portrait",
"margins": { "top": 20, "right": 15, "bottom": 20, "left": 15 }
},
"resources": {
"fonts": [],
"images": []
},
"dataSources": {
"evento": { "type": "object", "schema": "evento" }
},
"sections": [],
"elements": [
{
"id": "uuid",
"type": "text",
"position": { "x": 20, "y": 20, "width": 100, "height": 20 },
"style": {
"fontFamily": "Helvetica",
"fontSize": 14,
"color": "#000000"
},
"content": { "type": "binding", "expression": "{{evento.codice}}" },
"section": "body"
}
]
}
```
**Tipi elemento supportati:** text, image, shape, table, line, barcode
**Data binding:** `{{campo}}`, `{{dataset.campo}}`, `{{collection.campo}}`
**Variabili speciali:** `{{$pageNumber}}`, `{{$totalPages}}`, `{{$date}}`, `{{$datetime}}`
### Dataset Disponibili
| Dataset ID | Nome | Descrizione |
| ---------- | -------- | ------------------------------------------------------------------ |
| evento | Evento | Dati evento con cliente, location, dettagli ospiti, costi, risorse |
| cliente | Cliente | Anagrafica clienti completa |
| location | Location | Sedi e location eventi |
| articolo | Articolo | Catalogo articoli e materiali |
| risorsa | Risorsa | Staff e personale |
### Funzionalità Implementate
- [x] Editor visuale drag-and-drop con Fabric.js
- [x] Supporto elementi: testo, forme, linee, tabelle, immagini (placeholder)
- [x] Gestione zoom (25% - 300%)
- [x] Griglia e snap to grid
- [x] Undo/Redo (max 100 stati)
- [x] Shortcuts tastiera (Ctrl+Z, Ctrl+Y, Ctrl+S, Delete)
- [x] Pannello proprietà con posizione, stile, contenuto
- [x] Data binding con browser campi disponibili
- [x] Selezione multipla dataset per template
- [x] Preview con selezione entità reali
- [x] Salvataggio/caricamento template
- [x] Import/export template come file .aprt
- [x] Clone template
- [x] Generazione PDF default per eventi
- [x] Formattazione campi (valuta, data, numero, percentuale)
- [x] **Responsive design completo** (mobile, tablet, desktop)
- [x] **Toolbar professionale** stile Canva/Figma con sezioni etichettate
- [x] **Toolbar contestuale** per formattazione rapida (testo, forme, immagini)
- [x] **Color picker integrato** con palette preset
- [x] **Auto-save** con toggle (abilitato di default, 1s debounce)
- [x] **Indicatore stato salvataggio** accurato (Salvato/Non salvato/Salvataggio...)
### Cosa Manca per Completare
#### Alta Priorità
- [ ] **Caricamento immagini reali** - Attualmente placeholder, implementare upload e rendering
- [ ] **Tabelle dinamiche** - Rendering collection dati (es. lista ospiti, articoli)
- [ ] **Sezioni header/footer** - Ripetizione su ogni pagina
- [ ] **Font custom** - Upload e utilizzo font TTF/OTF nei PDF
#### Media Priorità
- [ ] **Relazioni tra dataset** - UI per collegare campi tra dataset diversi
- [ ] **Barcode/QRCode** - Supporto codici a barre
- [ ] **Formule calcolate** - Espressioni matematiche nei campi
- [ ] **Stili condizionali** - Formattazione basata su valore dati
- [ ] **Raggruppamento elementi** - Group/ungroup nel canvas
#### Bassa Priorità
- [ ] **Template predefiniti** - Library di template pronti all'uso
- [ ] **Anteprima live** - Preview in tempo reale durante editing
- [x] ~~**Multi-pagina** - Editor pagine multiple~~ - COMPLETATO
- [ ] **Righelli e guide** - Ausili allineamento avanzati
- [ ] **Esportazione altri formati** - Excel, Word oltre PDF
---
## Checklist Completamento Report System
### Backend
- [x] Entity ReportTemplate
- [x] Entity ReportFont
- [x] Entity ReportImage
- [x] ReportTemplatesController (CRUD + clone + import/export)
- [x] ReportResourcesController (fonts + images)
- [x] ReportsController (generate + preview + schema + datasets)
- [x] ReportGeneratorService con QuestPDF
- [x] Schema dati per tutti i dataset (evento, cliente, location, articolo, risorsa)
- [x] Generazione PDF default evento
- [x] Generazione PDF multi-pagina
- [ ] Rendering tabelle dinamiche da collection
- [ ] Supporto font custom nel PDF
- [ ] Rendering immagini da storage
### Frontend
- [x] ReportTemplatesPage (lista + filtri + azioni)
- [x] ReportEditorPage (editor principale)
- [x] EditorCanvas con Fabric.js v6
- [x] EditorToolbar completa
- [x] PropertiesPanel (posizione + stile + contenuto)
- [x] DataBindingPanel multi-dataset
- [x] DatasetSelector
- [x] PreviewDialog con selezione entità
- [x] Types APRT completi
- [x] Services API completi
- [x] Undo/Redo
- [x] Keyboard shortcuts
- [x] PageNavigator (gestione multi-pagina)
- [x] Navigazione pagine in toolbar
- [x] **Responsive design** (mobile/tablet/desktop)
- [ ] Upload e gestione immagini nell'editor
- [ ] Editor tabelle avanzato (colonne, binding dati)
- [ ] UI relazioni tra dataset
- [ ] Gestione sezioni header/footer
### Testing
- [x] Build frontend senza errori
- [x] Build backend senza errori
- [ ] Test funzionale editor canvas
- [x] Test generazione PDF con dati reali (binding e formattazione funzionanti)
- [ ] Test import/export template
- [ ] Test con font e immagini custom
### Documentazione
- [x] Documentazione APRT metalanguage
- [x] Lista API endpoints
- [x] Checklist implementazione
- [ ] Guida utente editor
- [ ] Esempi template comuni
---
## Note Tecniche per Future Sessioni
### Struttura File Report System
```
src/
├── Apollinare.Domain/Entities/
│ ├── ReportTemplate.cs # Entity template con TemplateJson
│ ├── ReportFont.cs # Font custom uploadati
│ └── ReportImage.cs # Immagini riutilizzabili
├── Apollinare.API/
│ ├── Controllers/
│ │ ├── ReportTemplatesController.cs # CRUD template
│ │ ├── ReportResourcesController.cs # Font e immagini
│ │ └── ReportsController.cs # Generazione PDF + schema
│ │
│ └── Services/Reports/
│ ├── ReportGeneratorService.cs # QuestPDF generator
│ └── AprtModels.cs # Modelli C# per APRT JSON
frontend/src/
├── pages/
│ ├── ReportTemplatesPage.tsx # Lista template
│ └── ReportEditorPage.tsx # Editor principale
├── components/reportEditor/
│ ├── EditorCanvas.tsx # Fabric.js canvas
│ ├── EditorToolbar.tsx # Toolbar strumenti
│ ├── PropertiesPanel.tsx # Proprietà elemento
│ ├── DataBindingPanel.tsx # Browser campi dati
│ ├── DatasetSelector.tsx # Selezione dataset
│ ├── PreviewDialog.tsx # Dialog anteprima
│ └── ContextMenu.tsx # Menu tasto destro (NEW)
├── types/
│ └── report.ts # Types APRT + DTOs
└── services/
└── reportService.ts # API calls
```
### Problemi Risolti (da ricordare)
1. **Fabric.js v6 breaking changes:**
- `sendToBack()``canvas.sendObjectToBack(obj)`
- Event handlers hanno signature diversa
- Proprietà `data` non è nel tipo base, serve cast a `FabricObjectWithData`
2. **TypeScript strict mode:**
- Usare `as any` per event handlers Fabric.js
- Interface `FabricObjectWithData` per oggetti con metadata custom
3. **QuestPDF TimeSpan:**
- `evento.OraInizio` è `TimeSpan?` non `string`
- Formattare con `{evento.OraInizio:hh\\:mm}`
4. **Data Binding PDF (FIX 27/11/2025):**
- **Problema:** I binding `{{dataEvento}}` non venivano risolti - PDF mostrava campi vuoti
- **Causa:** Il frontend creava binding senza prefisso dataset quando c'era un solo dataset
- **Soluzione Frontend** (`DataBindingPanel.tsx`): Sempre includere il prefisso dataset
```typescript
// Prima: {{dataEvento}} - non funzionava
// Dopo: {{evento.dataEvento}} - corretto
const createBinding = (datasetId: string, fieldName: string) => {
return `{{${datasetId}.${fieldName}}}`;
};
```
- **Soluzione Backend** (`ReportGeneratorService.cs`): Aggiunto fallback per compatibilità con template esistenti
```csharp
// Se binding senza prefisso, cerca in tutti i dataset
if (current == null && parts.Length == 1)
{
foreach (var kvp in dataContext)
{
var foundValue = GetPropertyValue(kvp.Value, path);
if (foundValue != null) { current = foundValue; break; }
}
}
```
- **Formattazione:** Aggiunto `ResolveBindingWithFormat()` per applicare format (date, currency, etc.)
5. **SignalR Connection (FIX 27/11/2025):**
- Aggiunto `app.UseWebSockets()` in Program.cs prima di `app.UseRouting()`
- Configurato signalr.ts con `withAutomaticReconnect()`
6. **MUI Fragment in Menu (FIX 27/11/2025):**
- Menu component non accetta Fragment come child
- Sostituire `<>...</>` con array `[<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:**
```csharp
// 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:
```csharp
// 1px @ 96 DPI = 0.2646mm
var fontSizeMm = (style?.FontSize ?? 12) * 0.2646f;
```
- **File:** `ReportGeneratorService.cs` - Metodo `RenderElementToSvg()` case "text"
13. **Rotazione Oggetti nel PDF (RISOLTO 28/11/2025):**
- **Problema:** Gli oggetti ruotati nel canvas Fabric.js venivano posizionati in modo completamente errato nel PDF
- **Causa:** In Fabric.js con `originX='left'` e `originY='top'`, quando un oggetto viene ruotato:
- L'oggetto ruota attorno al suo centro geometrico
- Le coordinate `left`/`top` salvate rappresentano la posizione dell'origin point **dopo** la rotazione
- Il backend calcolava il centro di rotazione in modo errato usando `(x + width/2, y + height/2)`
- **Soluzione:** Implementata la formula corretta di Fabric.js per calcolare il centro:
```csharp
// Calcolo del centro usando la formula Fabric.js (originX='left', originY='top')
var angleRad = rotation * Math.PI / 180f;
var halfWidth = width / 2;
var halfHeight = height / 2;
var cos = (float)Math.Cos(angleRad);
var sin = (float)Math.Sin(angleRad);
// Centro dell'oggetto in coordinate canvas
var centerX = left + halfWidth * cos - halfHeight * sin;
var centerY = top + halfWidth * sin + halfHeight * cos;
// Posizione di disegno (angolo sup-sinistro del rettangolo non ruotato centrato sul centro)
var drawX = centerX - halfWidth;
var drawY = centerY - halfHeight;
// Ruota attorno al centro calcolato
canvas.RotateDegrees(rotation, centerX, centerY);
// Disegna a (drawX, drawY)
```
- **File:** `ReportGeneratorService.cs` - Metodi `RenderElementToCanvas()` e `GenerateSvgContent()` + `RenderElementToSvg()`
- **Nota:** La stessa logica è applicata sia al rendering bitmap (SkiaSharp) che SVG
14. **Gestione Multi-Pagina (IMPLEMENTATO 28/11/2025):**
- **Struttura dati:**
- `AprtPage`: `{ id, name, pageSize?, orientation?, margins?, backgroundColor? }`
- `AprtElement.pageId`: ID della pagina a cui appartiene l'elemento
- `AprtTemplate.pages`: Array di pagine del template
- **Frontend:**
- `PageNavigator.tsx`: Sidebar con lista pagine, context menu (rinomina, duplica, elimina, sposta)
- `EditorToolbar.tsx`: Pulsanti prev/next pagina, indicatore "Pagina X di Y"
- `ReportEditorPage.tsx`: State `currentPageId`, filtro elementi per pagina, handlers CRUD pagine
- `PropertiesPanel.tsx`: Modifica nome pagina e impostazioni (format, orientation, margins, background)
- **Backend (`ReportGeneratorService.cs`):**
```csharp
// Pre-render di ogni pagina separatamente
foreach (var pageDefinition in aprt.Pages)
{
var pageElements = aprt.Elements
.Where(e => e.Visible && (e.PageId == pageDefinition.Id ||
(string.IsNullOrEmpty(e.PageId) && pageDefinition.Id == aprt.Pages[0].Id)))
.ToList();
var pageImageBytes = RenderContentToBitmap(pageElements, pageWidth, pageHeight, ...);
pageRenderData.Add((pageWidth, pageHeight, bgColor, pageImageBytes));
}
// Genera PDF con pagine separate
Document.Create(container => {
foreach (var (pageWidth, pageHeight, bgColor, imageBytes) in pageRenderData)
container.Page(page => { page.Size(...); page.Content().Image(imageBytes); });
});
```
- **Migrazione template legacy:** `MigrateTemplatePages()` crea pagina default e assegna elementi orfani
15. **Variabili Globali Report (FIX 28/11/2025 sera):**
- **Problema:** Le variabili speciali `{{$pageNumber}}`, `{{$totalPages}}`, `{{$date}}`, `{{$datetime}}`, `{{$time}}` non venivano stampate nel PDF - restavano come placeholder
- **Causa:** `ResolveBindingPath()` restituiva placeholder statici (`"{{PAGE}}"`) invece dei valori reali perché il contesto pagina non veniva passato durante il rendering
- **Soluzione:**
1. Aggiunta classe `PageContext` con `PageNumber` e `TotalPages`
2. Il ciclo di rendering ora traccia l'indice pagina corrente
3. `PageContext` propagato attraverso tutta la catena: `GeneratePdfAsync` → `RenderContentToBitmap` → `RenderElementToCanvas` → `RenderTextToCanvas` → `ResolveContent` → `ResolveBindingWithFormat` → `ResolveBindingPath`
4. `ResolveBindingPath()` ora usa i valori reali dal contesto:
```csharp
"$pageNumber" => pageContext?.PageNumber.ToString() ?? "1",
"$totalPages" => pageContext?.TotalPages.ToString() ?? "1",
"$date" => DateTime.Now.ToString("dd/MM/yyyy"),
"$time" => DateTime.Now.ToString("HH:mm"),
"$datetime" => DateTime.Now.ToString("dd/MM/yyyy HH:mm"),
```
- **File:** `ReportGeneratorService.cs` - Metodi `GeneratePdfAsync()`, `RenderContentToBitmap()`, `RenderElementToCanvas()`, `RenderTextToCanvas()`, `ResolveContent()`, `ResolveBindingWithFormat()`, `ResolveBinding()`, `ResolveExpression()`, `ResolveBindingPath()`
16. **Responsive Design Completo (IMPLEMENTATO 28/11/2025 notte):**
- **Obiettivo:** Rendere tutta l'applicazione responsive per mobile, tablet e desktop
- **Breakpoints MUI utilizzati:**
- Mobile: `theme.breakpoints.down("sm")` → < 600px
- Tablet: `theme.breakpoints.between("sm", "md")` → 600-900px
- Desktop: `theme.breakpoints.up("md")` → > 900px
- **Pattern principale per Report Editor su mobile:**
- `BottomNavigation` per switch tra pannelli (Pagine, Dati, Proprietà)
- `SwipeableDrawer` con `anchor="bottom"` e altezza 70vh per contenuto pannelli
- Auto-zoom canvas: 0.5 mobile, 0.75 tablet, 1 desktop
- **Pattern per toolbar mobile:**
- Riga primaria con azioni essenziali sempre visibili
- Riga secondaria collassabile con `<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<Byte>)'." - connessioni SignalR fallivano immediatamente
- **Causa:** Package `Microsoft.AspNetCore.SignalR.Common` v10.0.0 nel backend incompatibile con .NET 9 (è per .NET 10 preview)
- **Soluzione:**
- Rimosso `Microsoft.AspNetCore.SignalR.Common` dal .csproj (SignalR è già incluso in ASP.NET Core)
- Downgrade frontend `@microsoft/signalr` da v10.0.0 a v8.0.7
- **File:** `Apollinare.API.csproj`, `frontend/package.json`
21. **Auto-Save Non Funzionante (FIX 28/11/2025 pomeriggio):**
- **Problema:** Con auto-save attivo, le modifiche non venivano salvate (l'indicatore mostrava "Non salvato" sempre)
- **Causa:** `saveMutation` nell'array di dipendenze dell'`useEffect` causava il reset del timeout ad ogni render (React Query crea un nuovo oggetto ad ogni render)
- **Soluzione:** Usati `useRef` per `saveMutation`, `template`, e `templateInfo` per evitare che l'effect si ri-esegua inutilmente
- **File:** `ReportEditorPage.tsx`
22. **Sistema Collaborazione Real-Time (COMPLETATO 28/11/2025):**
- **Obiettivo:** Collaborazione stile Google Docs su tutto l'applicativo
- **Architettura implementata:**
- `CollaborationHub.cs` - Hub SignalR generico con room-based collaboration
- `collaboration.ts` - Service singleton frontend
- `CollaborationContext.tsx` - React Context con `useCollaborationRoom` hook
- Room key format: `{entityType}:{entityId}` (es. `report-template:2`)
- **File principali:**
- Backend: `CollaborationHub.cs`
- Frontend: `collaboration.ts`, `CollaborationContext.tsx`, `ReportEditorPage.tsx`
23. **Sincronizzazione Real-Time Lenta (FIX 28/11/2025 sera):**
- **Problema:** Al salvataggio (manuale o auto-save), l'altra sessione impiegava diversi secondi per vedere le modifiche
- **Causa:** Il sistema usava `DataSaved` notification che causava un reload del template dal server (`queryClient.invalidateQueries`)
- **Soluzione:** Implementato `BroadcastTemplateSync` che invia l'intero template via SignalR direttamente alle altre sessioni:
- Nuovo metodo Hub `BroadcastTemplateSync(roomKey, templateJson, version, compressed)`
- L'altra sessione riceve il template e lo applica istantaneamente con `historyActions.setWithoutHistory()`
- Aggiunto version tracking per evitare di applicare versioni più vecchie
- Compressione automatica gzip per template > 10KB (usa browser's CompressionStream API)
- **File:** `CollaborationHub.cs`, `collaboration.ts`, `CollaborationContext.tsx`, `ReportEditorPage.tsx`
24. **Limite Messaggio SignalR (FIX 28/11/2025 notte):**
- **Problema:** La connessione SignalR si disconnetteva con errore "The maximum message size of 32768B was exceeded"
- **Causa:** Il template compresso (~110KB) superava il limite di default di SignalR (32KB)
- **Soluzione:** Configurato `MaximumReceiveMessageSize = 1MB` in `Program.cs`:
```csharp
builder.Services.AddSignalR(options => {
options.MaximumReceiveMessageSize = 1024 * 1024; // 1MB
})
```
- **File:** `Program.cs`
25. **Auto-Save Non Funzionante (FIX 28/11/2025 notte):**
- **Problema:** L'auto-save non si attivava anche con modifiche non salvate
- **Causa:** Le dipendenze dell'effect includevano `saveMutation`, `template`, `templateInfo` che cambiano ad ogni render, causando reset continui del timeout
- **Soluzione:** Approccio event-based con debounce usando refs:
- `saveMutationRef`, `templateForSaveRef`, `templateInfoForSaveRef` per accedere ai valori correnti senza re-triggerare l'effect
- Dipendenze effect ridotte a: `autoSaveEnabled`, `isNew`, `hasUnsavedChanges`, `templateHistory.undoCount`
- Check `isPending` spostato dentro il callback del setTimeout
- **File:** `ReportEditorPage.tsx`
26. **Selezione Multipla Fabric.js - Riposizionamento Oggetti (FIX 29/11/2025):**
- **Problema:** Usando `ActiveSelection` di Fabric.js per la selezione multipla, gli oggetti venivano riposizionati/spostati quando selezionati
- **Causa:** `ActiveSelection` raggruppa gli oggetti e le loro coordinate diventano relative al centro del gruppo. Inoltre, ricreando l'`ActiveSelection` nell'effect quando cambiava `selectedElementIds`, gli oggetti venivano spostati
- **Soluzione:** Sistema di selezione multipla completamente personalizzato:
- Disabilitata selezione nativa di Fabric.js (`selection: false` nel canvas)
- Implementato `handleMouseDown` per:
- Click su oggetto → selezione singola
- Shift+click → aggiunge/rimuove dalla selezione
- Click su canvas vuoto → inizio rettangolo di selezione
- Click su oggetto già selezionato (multi) → inizio drag multiplo
- Implementato `handleMouseMove` per:
- Disegno rettangolo di selezione (Rect blu tratteggiato)
- Spostamento multiplo oggetti (aggiorna `left`/`top` di ogni oggetto)
- Implementato `handleMouseUp` per:
- Fine rettangolo → calcola intersezione e seleziona oggetti
- Fine drag multiplo → aggiorna template con nuove posizioni
- Feedback visivo: bordo blu e ombra sugli oggetti selezionati (invece di ActiveSelection)
- Usati refs (`selectedElementIdsRef`, `onSelectElementRef`, etc.) per evitare stale closures negli event handler registrati una sola volta
- **File:** `EditorCanvas.tsx`, `ReportEditorPage.tsx`
### Schema Database Report System
Le tabelle sono già nel DbContext (`AppollinareDbContext.cs`):
- `ReportTemplates` - Template salvati
- `ReportFonts` - Font custom
- `ReportImages` - Immagini riutilizzabili
Migration già applicata per SQLite.
### Dipendenze Chiave
**Backend (NuGet):**
- `QuestPDF` - Generazione PDF (Community License, gratis per revenue < $1M)
**Frontend (npm):**
- `fabric` v6.x - Canvas editor
- `uuid` - Generazione ID elementi
- `@tanstack/react-query` - Data fetching
### Routes Frontend
```typescript
// App.tsx
<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.