50 KiB
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:
- Viene completato un task significativo (fix, nuova feature, refactoring importante)
- Viene risolto un problema tecnico che potrebbe ripresentarsi in futuro
- Si scopre un pattern/workaround importante da ricordare
- 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
- Aggiungere OGNI problema tecnico risolto con:
-
Checklist:
- Aggiornare checkbox
[x]per task completati - Aggiungere nuovi task se scoperti durante il lavoro
- Aggiornare checkbox
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: 28 Novembre 2025 (notte)
Stato progetto: Migrazione Oracle APEX → .NET + React TypeScript in corso
Lavoro completato nell'ultima sessione:
-
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:
TemplateSynccon decompressione automatica lato client - FIX: Limite messaggio SignalR - Aumentato
MaximumReceiveMessageSizea 1MB inProgram.cs(default era 32KB) - File modificati:
CollaborationHub.cs- AggiuntoBroadcastTemplateSynceTemplateSyncMessagecon campoCompressedcollaboration.ts- AggiuntobroadcastTemplateSync(), utilities compressione/decompressione (compressString,decompressString), handlerTemplateSyncCollaborationContext.tsx- EspostobroadcastTemplateSynceonTemplateSyncReportEditorPage.tsx- SostituitosendDataSaved()conbroadcastTemplateSync(), aggiunto handler per applicare template ricevutiProgram.cs- Configurato SignalR conMaximumReceiveMessageSize = 1MB
- Prima: Al salvataggio veniva inviata solo una notifica
-
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
useRefpersaveMutation,template,templateInfoper evitare re-creazione del timeout - Dipendenze effect: solo
autoSaveEnabled,isNew,hasUnsavedChanges,templateHistory.undoCount - Il check
isPendingavviene 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à/paginacollaboration.ts- Service singleton per gestione connessioneCollaborationContext.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.Commonv10.0.0 incompatibile con .NET 9, downgrade client@microsoft/signalra v8.0.7 - FIX: Auto-save non funzionante - Usati
useRefper evitare re-run dell'effect causato dasaveMutationnelle 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
textDecorationaAprtStyleper supportare sottolineato
-
FIX: Indicatore "Non Salvato" errato - RISOLTO
- Prima usava
canUndoche indicava solo presenza di history - Ora usa
undoCountconfrontato conlastSavedUndoCountper tracking accurato
- Prima usava
-
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
PageContextper passare numero pagina e totale pagine durante il rendering - Propagato
PageContextattraverso 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
AprtPageper definire pagine con impostazioni individuali (size, orientation, margins, backgroundColor) - Ogni elemento ha
pageIdper 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.tsxaggiornato per mostrare/modificare impostazioni pagina corrente- Backend
ReportGeneratorService.csgenera PDF multi-pagina correttamente - Migrazione automatica template legacy (elementi senza pageId assegnati a prima pagina)
- Nuovo tipo
-
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:
- Vai alla sezione "Report PDF System - Implementation Details" più sotto
- Consulta la "Checklist Completamento Report System" per vedere cosa manca
- 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
- Backend:
Prossimi task prioritari (Report System):
CRITICO: Posizionamento assoluto PDF- COMPLETATOImplementare caricamento immagini reali- COMPLETATOFIX: Rotazione oggetti nel PDF- COMPLETATOGestione Multi-Pagina- COMPLETATO- Aggiungere rendering tabelle dinamiche per collection
- Gestire sezioni header/footer ripetute su ogni pagina
- 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 breakdownEVENTI_DET_PREL- Pick lists (articles needed for the event)EVENTI_DET_RIS- Resource assignments (staff)EVENTI_DET_DEGUST- Tasting event detailsEVENTI_ACCONTI- Deposits/advancesEVENTI_ALLEG- AttachmentsEVENTI_ALTRICOSTI- Other costs
Master Data:
ARTICOLI- Articles/items with images (BLOB), quantities, coefficientsTB_CODICI_CATEG- Categories with calculation coefficients (COEFF_A/B/S)TB_TIPI_MAT- Material typesTB_TIPI_EVENTO- Event types with meal classificationsTB_TIPI_OSPITI- Guest typesCLIENTI- ClientsLOCATION- Event locationsRISORSE- 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 coefficientsEVENTI_AGGIORNA_TOT_OSPITI(p_event_id)- Recalculates total guest countEVENTI_RICALCOLA_ACCONTI(p_event_id)- Recalculates deposit amountsEVENTI_COPIA- Event duplication functionalityEVENTI_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 expiredF_MAX_NUMERO_EVENTI_RAGGIUNTO(data)- Enforces daily event limitF_USER_IN_ROLE(app_user, role)- Role-based authorizationSTRING_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 locationVW_CALENDARIO_EVENTI- Calendar view of events
Quantity Calculation Algorithm
The application uses a sophisticated coefficient-based system:
- Coefficients are defined at category level (
TB_CODICI_CATEG.COEFF_A/B/S) - Standard quantities are stored per article (
ARTICOLI.QTA_STD_A/S/B) - Guest counts by type determine multipliers (
EVENTI_DET_OSPITI) - Final quantities calculated as:
Guest_Count × Coefficient × Standard_Qty
Types: A (Adulti/Adults), S (Seduti/Seated), B (Buffet)
Authorization Model
5 Authorization Levels:
-
Admin_auth_schema - Full admin access
- Users: admin, monia, andrea, maria, sabrina, nicole, cucina, developer, elia.ballarani
-
User Read/Write - Controlled by
USERS_READONLYtableFLGWRITEflag determines write access
-
Consuntivi - Access to financial summaries
- Users from
GET_CONSUNTIVI_USERSview
- Users from
-
Gestori (Managers) - Manager-level permissions
- Users from
GET_GESTORI_USERSview
- Users from
-
Solo Admins - Highest level
- Only: admin, monia
Session Management:
SET_USER_READONLYprocess runs before header on every page- Sets
APP_READ_ONLYapplication 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 updatesnotifica(pText, pType)- Dynamic notificationssetSessionState(elemList, pCallback)- Session state managementajaxExec(...)- Generic AJAX executionexecProcessAsync(...)- Async process executionexecQueryAsync(...)- Async query execution
Migration Considerations
Complex Features Requiring Special Attention:
-
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
-
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
-
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
-
State Management
- Heavy use of APEX session state
- Consider Redux Toolkit or Zustand for React
- Real-time grid updates and calculations
-
Reporting
- JasperReports replacement needed
- Options: SSRS, Crystal Reports, DevExpress, or PDF libraries (iTextSharp, QuestPDF)
-
Email Queue System
- Asynchronous processing required
- Consider: Hangfire, Azure Functions, or background services
-
Calendar Component
- Page 12 uses APEX calendar
- React options: FullCalendar, React Big Calendar, @event-calendar/core
-
Multi-Grid Interactions
- Interactive grids with master-detail relationships
- Consider: AG Grid, DevExtreme DataGrid, or Material-UI DataGrid
Business Rules to Preserve
- Event Status Workflow: Must follow 0 → 10 → 20 progression
- Quote Expiration: Automatic status change when
DATA_SCAD_PREVENTIVOpassed - Max Events Per Day: Enforced limit (configurable)
- Article Commitment Tracking: Prevent overbooking of inventory
- Coefficient-Based Calculations: Ensure quantity formulas match exactly
- Deposit Calculations: Auto-recalculation on cost changes
- Role-Based Access: 5-level authorization system
- 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:
- Always query the database to understand current data structure and relationships
- Extract PL/SQL source code for procedures/functions before implementing equivalent C# logic
- Document business rules discovered in stored procedures
- Preserve Italian field names in database but consider English in application layer
- Test quantity calculations thoroughly - they are core to the business
- Map APEX page flows to React routes and components
- Identify reusable components (grids, forms, lookups)
- 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, metadataReportFont.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 QuestPDFAprtModels.cs- Modelli C# per il metalinguaggio APRT
Controllers (/src/Apollinare.API/Controllers/):
ReportTemplatesController.cs- CRUD template, clone, import/exportReportResourcesController.cs- Gestione font e immaginiReportsController.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/exportReportEditorPage.tsx- Editor principale con undo/redo, shortcuts
Componenti Editor (/frontend/src/components/reportEditor/):
EditorCanvas.tsx- Canvas Fabric.js per design visualeEditorToolbar.tsx- Toolbar con strumenti, zoom, grid, azioniPropertiesPanel.tsx- Pannello proprietà elemento/paginaDataBindingPanel.tsx- Browser campi dati con supporto multi-datasetDatasetSelector.tsx- Selezione dataset per templatePreviewDialog.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)
-
Fabric.js v6 breaking changes:
sendToBack()→canvas.sendObjectToBack(obj)- Event handlers hanno signature diversa
- Proprietà
datanon è nel tipo base, serve cast aFabricObjectWithData
-
TypeScript strict mode:
- Usare
as anyper event handlers Fabric.js - Interface
FabricObjectWithDataper oggetti con metadata custom
- Usare
-
QuestPDF TimeSpan:
evento.OraInizioèTimeSpan?nonstring- Formattare con
{evento.OraInizio:hh\\:mm}
-
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.)
- Problema: I binding
-
SignalR Connection (FIX 27/11/2025):
- Aggiunto
app.UseWebSockets()in Program.cs prima diapp.UseRouting() - Configurato signalr.ts con
withAutomaticReconnect()
- Aggiunto
-
MUI Fragment in Menu (FIX 27/11/2025):
- Menu component non accetta Fragment come child
- Sostituire
<>...</>con array[<Divider key="..." />, <MenuItem key="..." />]
-
HTML p/div nesting (FIX 27/11/2025):
- Error:
<div> cannot be a descendant of <p> - Fix:
<ListItemText secondaryTypographyProps={{ component: "div" }} />
- Error:
-
Context Menu Browser Default (FIX 27/11/2025 notte):
- Problema: Il menu contestuale del browser appariva invece di quello custom
- Soluzione: Usare
onContextMenusu Box container React invece di eventi Fabric.js - File:
EditorCanvas.tsx- Aggiunto handler sul Box wrapper
-
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
- Problema:
-
QuestPDF Posizionamento Assoluto (RISOLTO 27/11/2025):
- Problema: QuestPDF non ha API
.Position()per posizionamento assoluto - Soluzione: Usare SVG con
viewBoxin mm per avere coordinate 1:1 con il designer - File:
ReportGeneratorService.cs- MetodiGenerateSvgContent(),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
- Problema: QuestPDF non ha API
-
Immagini nel PDF (RISOLTO 27/11/2025):
- Problema: Le immagini embedded come data URI in
imageSettings.srcnon venivano renderizzate - Soluzione:
RenderImageToSvg()ora gestisce 3 fonti:- Data URI (
data:image/...;base64,...) - usato direttamente - API URL (
/api/report-resources/images/{id}) - caricato da DB e convertito in data URI - URL esterni (
http://...) - passato direttamente
- Data URI (
- File:
ReportGeneratorService.cs- MetodoRenderImageToSvg(),GuessMimeType()
- Problema: Le immagini embedded come data URI in
-
Font Size nel PDF (RISOLTO 27/11/2025):
- Problema: Il font size appariva troppo grande/piccolo nel PDF
- Causa: La formula
fontSize * mmToPx / 3era 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- MetodoRenderElementToSvg()case "text"
-
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'eoriginY='top', quando un oggetto viene ruotato:- L'oggetto ruota attorno al suo centro geometrico
- Le coordinate
left/topsalvate 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- MetodiRenderElementToCanvas()eGenerateSvgContent()+RenderElementToSvg() -
Nota: La stessa logica è applicata sia al rendering bitmap (SkiaSharp) che SVG
-
-
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'elementoAprtTemplate.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: StatecurrentPageId, filtro elementi per pagina, handlers CRUD paginePropertiesPanel.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
-
-
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:
- Aggiunta classe
PageContextconPageNumbereTotalPages - Il ciclo di rendering ora traccia l'indice pagina corrente
PageContextpropagato attraverso tutta la catena:GeneratePdfAsync→RenderContentToBitmap→RenderElementToCanvas→RenderTextToCanvas→ResolveContent→ResolveBindingWithFormat→ResolveBindingPathResolveBindingPath()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"), - Aggiunta classe
- File:
ReportGeneratorService.cs- MetodiGeneratePdfAsync(),RenderContentToBitmap(),RenderElementToCanvas(),RenderTextToCanvas(),ResolveContent(),ResolveBindingWithFormat(),ResolveBinding(),ResolveExpression(),ResolveBindingPath()
- Problema: Le variabili speciali
-
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
- Mobile:
- Pattern principale per Report Editor su mobile:
BottomNavigationper switch tra pannelli (Pagine, Dati, Proprietà)SwipeableDrawerconanchor="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:
fullScreenprop 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 tabletReportTemplatesPage.tsx- FAB, fullScreen dialogsReportEditorPage.tsx- BottomNavigation + SwipeableDrawerEditorToolbar.tsx- 3 varianti layout (mobile/tablet/desktop)DataBindingPanel.tsx,PropertiesPanel.tsx,PageNavigator.tsx- Width responsiveDatasetSelector.tsx- Header collapsiblePreviewDialog.tsx,ImageUploadDialog.tsx- fullScreen + step navigation
-
Indicatore "Non Salvato" Errato (FIX 28/11/2025 tarda notte):
- Problema: Dopo il salvataggio, l'indicatore continuava a mostrare "Non salvato"
- Causa:
hasUnsavedChangesera basato sutemplateHistory.canUndoche indica solo se c'è history disponibile, non se ci sono modifiche non salvate - Soluzione: Introdotto
lastSavedUndoCountche viene aggiornato dopo ogni salvataggio riuscito.hasUnsavedChangesora confrontatemplateHistory.undoCount !== lastSavedUndoCount - File:
ReportEditorPage.tsx
-
Auto-Save Feature (IMPLEMENTATO 28/11/2025 tarda notte):
- Funzionalità: Salvataggio automatico dopo 1 secondo di inattività
- Implementazione:
- Stato
autoSaveEnabled(default: true) inReportEditorPage.tsx useEffectcon debounce di 1000ms che triggerasaveMutation.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
- Stato
- Props toolbar:
autoSaveEnabled,onAutoSaveToggle - File:
ReportEditorPage.tsx,EditorToolbar.tsx
-
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:
textDecorationinAprtStyle - File:
EditorToolbar.tsx,types/report.ts
- Miglioramenti:
-
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.Commonv10.0.0 nel backend incompatibile con .NET 9 (è per .NET 10 preview) - Soluzione:
- Rimosso
Microsoft.AspNetCore.SignalR.Commondal .csproj (SignalR è già incluso in ASP.NET Core) - Downgrade frontend
@microsoft/signalrda v10.0.0 a v8.0.7
- Rimosso
- File:
Apollinare.API.csproj,frontend/package.json
-
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:
saveMutationnell'array di dipendenze dell'useEffectcausava il reset del timeout ad ogni render (React Query crea un nuovo oggetto ad ogni render) - Soluzione: Usati
useRefpersaveMutation,template, etemplateInfoper evitare che l'effect si ri-esegua inutilmente - File:
ReportEditorPage.tsx
-
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 collaborationcollaboration.ts- Service singleton frontendCollaborationContext.tsx- React Context conuseCollaborationRoomhook- Room key format:
{entityType}:{entityId}(es.report-template:2)
- File principali:
- Backend:
CollaborationHub.cs - Frontend:
collaboration.ts,CollaborationContext.tsx,ReportEditorPage.tsx
- Backend:
-
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
DataSavednotification che causava un reload del template dal server (queryClient.invalidateQueries) - Soluzione: Implementato
BroadcastTemplateSyncche 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)
- Nuovo metodo Hub
- File:
CollaborationHub.cs,collaboration.ts,CollaborationContext.tsx,ReportEditorPage.tsx
-
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 = 1MBinProgram.cs:builder.Services.AddSignalR(options => { options.MaximumReceiveMessageSize = 1024 * 1024; // 1MB }) - File:
Program.cs
-
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,templateInfoche cambiano ad ogni render, causando reset continui del timeout - Soluzione: Approccio event-based con debounce usando refs:
saveMutationRef,templateForSaveRef,templateInfoForSaveRefper accedere ai valori correnti senza re-triggerare l'effect- Dipendenze effect ridotte a:
autoSaveEnabled,isNew,hasUnsavedChanges,templateHistory.undoCount - Check
isPendingspostato dentro il callback del setTimeout
- File:
ReportEditorPage.tsx
Schema Database Report System
Le tabelle sono già nel DbContext (AppollinareDbContext.cs):
ReportTemplates- Template salvatiReportFonts- Font customReportImages- Immagini riutilizzabili
Migration già applicata per SQLite.
Dipendenze Chiave
Backend (NuGet):
QuestPDF- Generazione PDF (Community License, gratis per revenue < $1M)
Frontend (npm):
fabricv6.x - Canvas editoruuid- 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.