# Righe Non Raggiungibili in Lista Eventi — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Per i certificati non in quotazione, visualizzare in grigio dimmed le righe della Lista Eventi con Data Osservazione > Data Rimborso, per evidenziare che non sono più raggiungibili. **Architecture:** Aggiunta di un brush `GrayedOutBrush` in `PdfTheme.cs` e logica di confronto date in `EventiSectionRenderer.cs`. Il parse di `DataRimborso` avviene una volta prima del loop; per ogni riga si effettua il parse di `ObservationDate` e si applica il brush grigio se la riga è non raggiungibile. Nessuna modifica al modello dati né alle SP. **Tech Stack:** C# / ASP.NET Core 8, Syncfusion PDF v33, `System.Globalization.CultureInfo` **Spec:** `docs/superpowers/specs/2026-03-24-eventi-righe-non-raggiungibili-design.md` --- ## File Map | File | Azione | Responsabilità | |------|--------|---------------| | `CertReports.Syncfusion/Helpers/PdfTheme.cs` | Modify | Aggiungere colore e brush `GrayedOut` | | `CertReports.Syncfusion/Services/Implementations/EventiSectionRenderer.cs` | Modify | Logica confronto date + applicazione brush grigio | --- ## Task 1: Aggiungere GrayedOutBrush in PdfTheme **Files:** - Modify: `CertReports.Syncfusion/Helpers/PdfTheme.cs` - [ ] **Step 1: Aggiungere il colore GrayedOut nella sezione colori** Aprire `PdfTheme.cs`. Trovare il blocco "Nuovi colori stile Ibrido elegante" (attorno a riga 27). Aggiungere dopo la riga con `TableAltRow`: ```csharp public static readonly Color GrayedOut = Color.FromArgb(255, 170, 170, 170); // #AAAAAA ``` > `Color` (non `PdfColor`) è il tipo corretto: tutti i colori esistenti in `PdfTheme.cs` usano già `Color` (es. riga 16: `public static readonly Color HeaderBackground = ...`). - [ ] **Step 2: Aggiungere il brush corrispondente nella sezione brush** Trovare il blocco dei brush statici (cercare `AccentBlueBrush`, `NegativeRedBrush`, ecc.). Aggiungere nella stessa sezione: ```csharp public static readonly PdfBrush GrayedOutBrush = new PdfSolidBrush(GrayedOut); ``` > Nota: `PdfSolidBrush` non è IDisposable in Syncfusion v33 — non usare `using`. - [ ] **Step 3: Build per verificare nessun errore** ```bash dotnet build CertReports.Syncfusion ``` Expected: `Build succeeded. 0 Error(s)` - [ ] **Step 4: Commit** ```bash git add CertReports.Syncfusion/Helpers/PdfTheme.cs git commit -m "feat: add GrayedOutBrush to PdfTheme for dimmed event rows" ``` --- ## Task 2: Logica righe non raggiungibili in EventiSectionRenderer **Files:** - Modify: `CertReports.Syncfusion/Services/Implementations/EventiSectionRenderer.cs` - [ ] **Step 1: Aggiungere using System.Globalization** In cima al file `EventiSectionRenderer.cs`, aggiungere dopo gli altri `using`: ```csharp using System.Globalization; ``` - [ ] **Step 2: Parse DataRimborso prima del loop righe** Nel metodo `Render()`, trovare il commento `// Righe dati` (attorno alla riga 90, prima di `for (int i = 0; ...)`). Aggiungere immediatamente prima del `for`: ```csharp // Pre-parse DataRimborso per confronto righe non raggiungibili (solo expired) DateTime? rimborsoDate = null; if (isExpired && !string.IsNullOrEmpty(data.Info.DataRimborso) && DateTime.TryParseExact(data.Info.DataRimborso, "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out var rd)) { rimborsoDate = rd; } ``` - [ ] **Step 3: Applicare il brush grigio sulle righe non raggiungibili** Sempre nel loop righe, trovare il blocco dell'evidenziazione verde "SI" (attorno a riga 134): ```csharp // Evidenzia "SI" nella colonna Pagato if (evt.Paid == "SI") row.Cells[paidColIndex].Style.TextBrush = PdfTheme.PositiveBrush as PdfBrush; ``` Aggiungere **dopo** questo blocco (non prima — il grigio deve sovrascrivere il verde): ```csharp // Righe non raggiungibili: testo grigio dimmed (ObservationDate > DataRimborso) if (rimborsoDate.HasValue && DateTime.TryParseExact(evt.ObservationDate, "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out var obsDate) && obsDate > rimborsoDate.Value) { foreach (var cell in row.Cells.OfType()) cell.Style.TextBrush = PdfTheme.GrayedOutBrush; } ``` > Il grigio va applicato **dopo** l'evidenziazione verde così la sovrascrive. Il cast `as PdfBrush` non serve perché `GrayedOutBrush` è già dichiarato come `PdfBrush` in `PdfTheme`. - [ ] **Step 4: Build** ```bash dotnet build CertReports.Syncfusion ``` Expected: `Build succeeded. 0 Error(s)` - [ ] **Step 5: Test manuale** Avviare il progetto e richiedere un report per un certificato non in quotazione (Stato = "Scaduto"/"Rimborsato"/"Revocato") che abbia una `DataRimborso` valorizzata e almeno una riga eventi con `ObservationDate > DataRimborso`. ```bash dotnet run --project CertReports.Syncfusion # GET http://localhost:{porta}/api/report/by-isin/{ISIN_SCADUTO} ``` Verificare nel PDF generato: - Le righe con `ObservationDate <= DataRimborso` → testo nero normale - Le righe con `ObservationDate > DataRimborso` → testo grigio `#AAAAAA` - Il pattern zebra (sfondo bianco/azzurro) rimane invariato su tutte le righe - Le righe raggiungibili (`ObservationDate <= DataRimborso`) con `Paid = "SI"` mantengono il testo verde — il grigio si applica solo alle righe non raggiungibili, dove il verde viene sovrascritto - [ ] **Step 6: Commit** ```bash git add CertReports.Syncfusion/Services/Implementations/EventiSectionRenderer.cs git commit -m "feat: dim unreachable event rows in expired certificate reports" ``` --- ## Edge Cases (già gestiti dall'implementazione) | Caso | Comportamento atteso | |------|---------------------| | `DataRimborso` vuota | `rimborsoDate` resta `null`, nessuna riga dimmed | | `DataRimborso` non parseable | stessa cosa | | `ObservationDate` non parseable | `TryParseExact` ritorna `false`, quella riga non viene dimmed | | `ObservationDate == DataRimborso` | `obsDate > rimborsoDate` è `false` → non dimmed | | Certificato in quotazione (`!isExpired`) | `rimborsoDate` non viene mai inizializzato (condizione `isExpired`) |