# Piano Implementazione Multi-Pagina per Report Designer ## Obiettivo Aggiungere la gestione di template multi-pagina al report designer, permettendo di creare documenti PDF con più pagine, ognuna con i propri elementi. --- ## Analisi dello Stato Attuale ### Frontend - **EditorCanvas.tsx**: Renderizza un singolo canvas Fabric.js che rappresenta una pagina - **ReportEditorPage.tsx**: Gestisce `template.elements` come array flat (tutti gli elementi) - **AprtTemplate**: Ha già un campo `sections` ma non è usato per le pagine - Gli elementi hanno già un campo `section` (header/body/footer) ma non `pageIndex` ### Backend - **ReportGeneratorService.cs**: Genera PDF con una sola pagina (`container.Page(...)`) - **AprtModels.cs**: Definisce `AprtSection` con `repeatOnPages` (pensato per header/footer ripetuti) ### Metalinguaggio APRT - Attualmente non supporta concetto di "pagine multiple" - Gli elementi hanno posizione assoluta relativa alla pagina --- ## Approccio Proposto ### Opzione Scelta: Array di Pagine nel Template Modificare la struttura del template per includere un array esplicito di pagine: ```typescript interface AprtTemplate { // ... existing fields pages: AprtPage[]; // NUOVO elements: AprtElement[]; // Rimane per backward compatibility } interface AprtPage { id: string; name: string; pageSize?: PageSize; // Override opzionale (default: dal meta) orientation?: PageOrientation; // Override opzionale margins?: AprtMargins; // Override opzionale backgroundColor?: string; elements: string[]; // Array di ID elementi che appartengono a questa pagina } interface AprtElement { // ... existing fields pageId?: string; // NUOVO - ID della pagina di appartenenza } ``` --- ## Modifiche Richieste ### 1. Frontend - Types (`report.ts`) ```typescript // Aggiungere: export interface AprtPage { id: string; name: string; pageSize?: PageSize; orientation?: PageOrientation; margins?: AprtMargins; backgroundColor?: string; } // Modificare AprtTemplate: export interface AprtTemplate { // ... existing pages: AprtPage[]; // Aggiungere } // Modificare AprtElement: export interface AprtElement { // ... existing pageId?: string; // Aggiungere } // Aggiornare defaultTemplate: export const defaultTemplate: AprtTemplate = { // ... existing pages: [{ id: 'page-1', name: 'Pagina 1' }], }; ``` ### 2. Frontend - Nuovo Componente `PageNavigator.tsx` Barra laterale sinistra o tabs per navigare tra le pagine: ``` ┌─────────────────────────────────────────────────┐ │ [+ Aggiungi Pagina] │ ├─────────────────────────────────────────────────┤ │ ┌─────────┐ Pagina 1 (A4 Portrait) [⋮] │ │ │ thumb │ 5 elementi │ │ └─────────┘ │ ├─────────────────────────────────────────────────┤ │ ┌─────────┐ Pagina 2 (A4 Portrait) [⋮] │ │ │ thumb │ 3 elementi ● │ ← Selezionata │ └─────────┘ │ └─────────────────────────────────────────────────┘ ``` Funzionalità: - Lista pagine con miniatura - Aggiunta nuova pagina - Duplicazione pagina - Eliminazione pagina (con conferma) - Riordinamento drag-and-drop - Indicatore pagina attiva ### 3. Frontend - Modifiche a `ReportEditorPage.tsx` ```typescript // Nuovo state: const [currentPageId, setCurrentPageId] = useState('page-1'); // Computed: const currentPage = template.pages.find(p => p.id === currentPageId); const currentPageElements = template.elements.filter(e => e.pageId === currentPageId); // Handlers: const handleAddPage = () => { /* ... */ }; const handleDeletePage = (pageId: string) => { /* ... */ }; const handleDuplicatePage = (pageId: string) => { /* ... */ }; const handleReorderPages = (fromIndex: number, toIndex: number) => { /* ... */ }; const handleUpdatePageSettings = (pageId: string, updates: Partial) => { /* ... */ }; // Quando si aggiunge un elemento, assegnare pageId corrente: const handleAddElement = (type: ElementType) => { const newElement = { // ... existing pageId: currentPageId, // NUOVO }; }; ``` ### 4. Frontend - Modifiche a `EditorCanvas.tsx` - Ricevere solo gli elementi della pagina corrente - Opzionalmente mostrare indicatore numero pagina ```typescript interface EditorCanvasProps { // ... existing pageIndex?: number; // Numero pagina per visualizzazione totalPages?: number; // Totale pagine } ``` ### 5. Frontend - Modifiche a `PropertiesPanel.tsx` Quando nessun elemento è selezionato, mostrare le impostazioni della pagina corrente: - Formato pagina (override rispetto al default) - Orientamento (override) - Margini (override) - Colore sfondo ### 6. Frontend - Modifiche a `EditorToolbar.tsx` Aggiungere pulsanti per navigazione veloce: - `[◀ Pagina Prec]` / `[Pagina Succ ▶]` - Indicatore `Pagina 1 di 3` ### 7. Backend - Modifiche a `AprtModels.cs` ```csharp public class AprtTemplate { // ... existing [JsonPropertyName("pages")] public List Pages { get; set; } = new(); } public class AprtPage { [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; [JsonPropertyName("pageSize")] public string? PageSize { get; set; } [JsonPropertyName("orientation")] public string? Orientation { get; set; } [JsonPropertyName("margins")] public AprtMargins? Margins { get; set; } [JsonPropertyName("backgroundColor")] public string? BackgroundColor { get; set; } } public class AprtElement { // ... existing [JsonPropertyName("pageId")] public string? PageId { get; set; } } ``` ### 8. Backend - Modifiche a `ReportGeneratorService.cs` ```csharp public async Task GeneratePdfAsync(int templateId, Dictionary dataContext) { // ... load template var document = Document.Create(container => { // Raggruppa elementi per pagina var pages = aprt.Pages.Any() ? aprt.Pages : new List { new AprtPage { Id = "default" } }; foreach (var page in pages) { // Determina dimensioni pagina (override o default) var pageSize = page.PageSize ?? aprt.Meta.PageSize; var orientation = page.Orientation ?? aprt.Meta.Orientation; var margins = page.Margins ?? aprt.Meta.Margins; // Filtra elementi per questa pagina var pageElements = aprt.Elements .Where(e => e.Visible && (e.PageId == page.Id || (string.IsNullOrEmpty(e.PageId) && page.Id == pages[0].Id))) .ToList(); container.Page(p => { var dims = GetPageDimensionsMm(pageSize, orientation); p.Size(dims.Width, dims.Height, Unit.Millimetre); p.Margin(0); // Render elementi della pagina var pageImageBytes = RenderContentToBitmap( pageElements, dims.Width, dims.Height, dataContext, resources, 300f); p.Content().Image(pageImageBytes).FitArea(); }); } }); return document.GeneratePdf(); } ``` --- ## Migrazione Template Esistenti Per backward compatibility, i template senza `pages` funzionano come prima: ```csharp // Nel backend: if (!aprt.Pages.Any()) { // Crea pagina di default con tutti gli elementi aprt.Pages.Add(new AprtPage { Id = "page-1", Name = "Pagina 1" }); foreach (var element in aprt.Elements.Where(e => string.IsNullOrEmpty(e.PageId))) { element.PageId = "page-1"; } } ``` ```typescript // Nel frontend (ReportEditorPage.tsx): useEffect(() => { if (existingTemplate) { const parsed = JSON.parse(existingTemplate.templateJson); // Migrazione: se manca pages, creane una di default if (!parsed.pages || parsed.pages.length === 0) { parsed.pages = [{ id: 'page-1', name: 'Pagina 1' }]; // Assegna tutti gli elementi esistenti alla prima pagina parsed.elements?.forEach(el => { if (!el.pageId) el.pageId = 'page-1'; }); } // ... rest of loading } }, [existingTemplate]); ``` --- ## Ordine di Implementazione ### Fase 1: Struttura Dati (30 min) 1. [ ] Aggiornare `report.ts` con `AprtPage` e modifiche a `AprtTemplate`/`AprtElement` 2. [ ] Aggiornare `AprtModels.cs` con le stesse strutture 3. [ ] Aggiornare `defaultTemplate` con pagina di default ### Fase 2: Backend PDF Multi-Pagina (30 min) 4. [ ] Modificare `ReportGeneratorService.GeneratePdfAsync` per iterare sulle pagine 5. [ ] Aggiungere logica di migrazione per template esistenti 6. [ ] Testare generazione PDF con template multi-pagina manuale ### Fase 3: Frontend - PageNavigator (1 ora) 7. [ ] Creare componente `PageNavigator.tsx` 8. [ ] Integrare in `ReportEditorPage.tsx` (layout con sidebar sinistra) 9. [ ] Implementare selezione pagina corrente ### Fase 4: Frontend - Gestione Elementi per Pagina (30 min) 10. [ ] Modificare `ReportEditorPage.tsx` per filtrare elementi per pagina 11. [ ] Assegnare `pageId` agli elementi nuovi 12. [ ] Aggiornare `EditorCanvas.tsx` per ricevere solo elementi pagina corrente ### Fase 5: Frontend - CRUD Pagine (45 min) 13. [ ] Implementare aggiunta pagina 14. [ ] Implementare duplicazione pagina (con copia elementi) 15. [ ] Implementare eliminazione pagina 16. [ ] Implementare riordinamento pagine (drag-and-drop) ### Fase 6: Frontend - Settings Pagina (30 min) 17. [ ] Modificare `PropertiesPanel.tsx` per mostrare settings pagina corrente 18. [ ] Supportare override formato/orientamento/margini per pagina ### Fase 7: UI Polish (30 min) 19. [ ] Aggiungere navigazione rapida in toolbar 20. [ ] Miniature pagine nel navigator 21. [ ] Indicatore pagina corrente nel canvas ### Fase 8: Test e Documentazione (30 min) 22. [ ] Test completo flusso multi-pagina 23. [ ] Test migrazione template esistenti 24. [ ] Aggiornare CLAUDE.md con documentazione --- ## Considerazioni UX 1. **Default intuitivo**: Template nuovo ha 1 pagina, utente può aggiungerne altre 2. **Copia/Incolla tra pagine**: Gli elementi copiati mantengono posizione, si può incollare su altra pagina 3. **Keyboard shortcuts**: - `Page Up` / `Page Down` per navigare - `Ctrl+Shift+N` per nuova pagina 4. **Undo/Redo**: Le operazioni su pagine sono incluse nella history 5. **Preview**: Mostra tutte le pagine del PDF generato --- ## File da Modificare | File | Tipo Modifica | |------|---------------| | `frontend/src/types/report.ts` | Aggiungere `AprtPage`, modificare `AprtTemplate`, `AprtElement` | | `frontend/src/pages/ReportEditorPage.tsx` | Gestione stato pagina corrente, filtro elementi | | `frontend/src/components/reportEditor/PageNavigator.tsx` | NUOVO - Navigatore pagine | | `frontend/src/components/reportEditor/EditorCanvas.tsx` | Ricevere elementi filtrati | | `frontend/src/components/reportEditor/PropertiesPanel.tsx` | Settings pagina corrente | | `frontend/src/components/reportEditor/EditorToolbar.tsx` | Navigazione rapida | | `src/Apollinare.API/Services/Reports/AprtModels.cs` | Aggiungere `AprtPage` | | `src/Apollinare.API/Services/Reports/ReportGeneratorService.cs` | Loop pagine per PDF | | `CLAUDE.md` | Documentazione feature | --- ## Stima Tempo Totale **~4 ore** per implementazione completa