Add export functionality to /sender Dashboard (Phase 6e)
Finalized the implementation of the export feature for the `EnvelopeSenderPage.razor` in the Blazor WASM Client. The feature supports exporting grid data in XLSX, CSV, and PDF formats, with an option to export only selected rows. - Updated `SF-74-PLAN.6e.md` to reflect the completed Phase 6e. - Integrated a compact UI for export controls in the toolbar: - Dropdown for format selection (XLSX, CSV, PDF). - Checkbox for "Only selected rows" option. - Single `Exportieren` button to trigger the export. - Implemented `ExportSelectedFormatAsync` to handle export logic: - Supported formats: XLSX, CSV, PDF. - Added options for `ExportSelectedRowsOnly` and grouping. - Replaced placeholder `ExportToExcelAsync` with finalized logic. - Documented known limitations (e.g., template columns not exported). - Updated project status table to mark Phase 6e as complete. This commit ensures a streamlined and user-friendly export UX, consistent with the existing design, while addressing known DevExpress limitations.
This commit is contained in:
@@ -1,134 +0,0 @@
|
|||||||
# SF-74 — Phase 6e Plan (Multi-Format Export + Selected Rows)
|
|
||||||
|
|
||||||
**Ticket:** SF-74
|
|
||||||
**Scope:** `/sender` Dashboard (`EnvelopeSenderPage.razor`)
|
|
||||||
**Status:** Planung abgeschlossen, Umsetzung wartet auf Freigabe
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Ziel
|
|
||||||
|
|
||||||
Für die Envelope-Grid-Ansicht auf `/sender` soll ein **einfaches, konsistentes Export-UX** umgesetzt werden:
|
|
||||||
|
|
||||||
1. Mehrere Export-Formate: **XLSX, CSV, PDF**
|
|
||||||
2. Option: **Nur ausgewählte Zeilen exportieren**
|
|
||||||
3. Keine komplexe Grid-Toolbar; stattdessen schlanke Integration in die vorhandene Action-Bar
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Technische Grundlage (DevExpress)
|
|
||||||
|
|
||||||
Direkter Export ist über `DxGrid` möglich (ohne Backend-Endpoint):
|
|
||||||
|
|
||||||
- `ExportToXlsxAsync(...)`
|
|
||||||
- `ExportToCsvAsync(...)`
|
|
||||||
- `ExportToPdfAsync(...)`
|
|
||||||
|
|
||||||
Die Datei wird direkt im Browser heruntergeladen.
|
|
||||||
|
|
||||||
**Wichtige Einschränkung (bekannt):** Inhalte aus `CellDisplayTemplate`/Detail-Templates werden nicht 1:1 exportiert. Das betrifft in unserem Grid v. a. die Template-Spalten `Status` und `EnvelopeReceivers`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## UX-Entscheidung (vereinfacht)
|
|
||||||
|
|
||||||
Anstatt 3–4 separater Export-Buttons:
|
|
||||||
|
|
||||||
- Ein kompakter Export-Block in der rechten Action-Bar
|
|
||||||
- `Format`-Dropdown (XLSX/CSV/PDF)
|
|
||||||
- `Nur ausgewählte` Toggle/Checkbox
|
|
||||||
- Ein einzelner `Exportieren`-Button
|
|
||||||
|
|
||||||
Dadurch bleibt die Oberfläche aufgeräumt und konsistent mit den bestehenden Toolbar-Buttons.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementierungsplan (2 Phasen, strikt sequentiell)
|
|
||||||
|
|
||||||
## Phase 1 — UI/Design Integration (ohne finale Export-Logik)
|
|
||||||
|
|
||||||
### Ziel
|
|
||||||
Export-Bedienelemente visuell integrieren, inklusive responsivem Verhalten und Theme-Konsistenz.
|
|
||||||
|
|
||||||
### Änderungen
|
|
||||||
|
|
||||||
#### 1) `EnvelopeSenderPage.razor`
|
|
||||||
- Export-Block in der bestehenden rechten Toolbar ergänzen.
|
|
||||||
- Neue Controls:
|
|
||||||
- Format-Auswahl (XLSX, CSV, PDF)
|
|
||||||
- `Nur ausgewählte` CheckBox/Toggle
|
|
||||||
- `Exportieren` Button
|
|
||||||
- Initialzustand:
|
|
||||||
- Standardformat = `XLSX`
|
|
||||||
- Export-Button zunächst mit Platzhalter-Handler (oder no-op), bis Phase 2
|
|
||||||
|
|
||||||
#### 2) `wwwroot/css/sender-page.css`
|
|
||||||
- Schlanke Styles für den Export-Block hinzufügen, z. B.:
|
|
||||||
- `.sender-export-group`
|
|
||||||
- `.sender-export-format`
|
|
||||||
- `.sender-export-selected`
|
|
||||||
- Bestehende Button-Designsprache beibehalten (`sender-btn`), keine visuelle Sonderwelt.
|
|
||||||
- Mobile/Wrap-Verhalten absichern (`@media`): Export-Controls dürfen umbrechen, ohne die Action-Bar zu zerlegen.
|
|
||||||
|
|
||||||
### Definition of Done (Phase 1)
|
|
||||||
- Export-UI ist sichtbar und intuitiv platziert.
|
|
||||||
- Layout ist konsistent in allen 3 Themes (`blazing-berry`, `blazing-dark`, `purple`).
|
|
||||||
- Mobile Darstellung bleibt nutzbar.
|
|
||||||
- Noch keine produktive Export-Logik (kommt erst in Phase 2).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 2 — Export-Funktionalität (XLSX/CSV/PDF + Selected Rows)
|
|
||||||
|
|
||||||
### Ziel
|
|
||||||
Ein Export-Handler mit Format-Switch und Selected-Rows-Unterstützung.
|
|
||||||
|
|
||||||
### Änderungen
|
|
||||||
|
|
||||||
#### 1) `EnvelopeSenderPage.razor` — State + Logik
|
|
||||||
- Neue State-Felder:
|
|
||||||
- Exportformat (Enum oder string)
|
|
||||||
- `bool _exportSelectedRowsOnly`
|
|
||||||
- `bool _isExporting`
|
|
||||||
- Export-Methode (zentral):
|
|
||||||
- Aktives Grid bestimmen: `_activeTab == "active" ? _gridActive : _gridCompleted`
|
|
||||||
- Dateiname je Tab/Datum erzeugen
|
|
||||||
- Je nach Format aufrufen:
|
|
||||||
- XLSX: `ExportToXlsxAsync(..., new GridXlExportOptions { ... })`
|
|
||||||
- CSV: `ExportToCsvAsync(..., new GridCsvExportOptions { ... })`
|
|
||||||
- PDF: `ExportToPdfAsync(..., new GridPdfExportOptions { ... })`
|
|
||||||
- Während Export: `_isExporting = true` (Button disable, optional Spinner).
|
|
||||||
|
|
||||||
#### 2) Selected Rows Only
|
|
||||||
- Option in Export-Options durchreichen: `ExportSelectedRowsOnly = _exportSelectedRowsOnly`.
|
|
||||||
- Für robuste Selected-Rows-Exports in beiden Grids:
|
|
||||||
- `KeyFieldName="Id"` setzen.
|
|
||||||
- Falls notwendig: Mehrfachselektion im Grid aktivieren (nur wenn dies die bestehenden Row-Aktionen nicht negativ beeinflusst).
|
|
||||||
|
|
||||||
#### 3) Spalten-Strategie für Export
|
|
||||||
- Template-Spalten (`Status`, `EnvelopeReceivers`) in Phase 2 pragmatisch behandeln:
|
|
||||||
- entweder exportierbar lassen (bekannte Einschränkung akzeptiert),
|
|
||||||
- oder `ExportEnabled="false"` setzen, um leere/irrelevante Spalten zu vermeiden.
|
|
||||||
- Entscheidung wird bei Umsetzung anhand der resultierenden Export-Datei getroffen.
|
|
||||||
|
|
||||||
### Definition of Done (Phase 2)
|
|
||||||
- XLSX/CSV/PDF Export funktioniert aus beiden Tabs (`active`, `completed`).
|
|
||||||
- `Nur ausgewählte` wirkt korrekt auf den Exportumfang.
|
|
||||||
- Export ist gegen Mehrfachklicks abgesichert.
|
|
||||||
- Dateinamen sind konsistent und verständlich.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Risiken / Hinweise
|
|
||||||
|
|
||||||
1. **Template-Spalten-Limitierung (DevExpress):** Kein 1:1 Export von visuell komplexen Templates.
|
|
||||||
2. **Selected Rows UX:** Falls Multi-Select benötigt wird, müssen bestehende Single-Selection-Aktionen (`Bearbeiten`, `Vorschau`, `Löschen`) sauber behandelt werden.
|
|
||||||
3. **PDF Export Layout:** Kann sich visuell vom Grid unterscheiden; ggf. später `CustomizeCell`/`CustomizePageFooter` ergänzen.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Umsetzungstaktik
|
|
||||||
|
|
||||||
- **Keine Parallelisierung der Phasen.**
|
|
||||||
- Erst nach expliziter Freigabe: **Phase 1** implementieren.
|
|
||||||
- Nach vollständigem Abschluss/Review von Phase 1: **Phase 2** implementieren.
|
|
||||||
@@ -39,7 +39,7 @@ EnvelopeGenerator.Server.Client/ → Blazor WASM Client
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✅ Abgeschlossene Phasen (1–5, 6a–6c)
|
## ✅ Abgeschlossene Phasen (1–5, 6a–6e)
|
||||||
|
|
||||||
| Phase | Inhalt | Status |
|
| Phase | Inhalt | Status |
|
||||||
|-------|--------|--------|
|
|-------|--------|--------|
|
||||||
@@ -52,6 +52,7 @@ EnvelopeGenerator.Server.Client/ → Blazor WASM Client
|
|||||||
| 6b | Kunden-Logo Placeholder (sender-customer-logo-area, CSS) | ✅ |
|
| 6b | Kunden-Logo Placeholder (sender-customer-logo-area, CSS) | ✅ |
|
||||||
| 6c | Grid-Spalten (EnvelopeTypeTitle, AddedWhen, ChangedWhen) + Persistence Hint | ✅ |
|
| 6c | Grid-Spalten (EnvelopeTypeTitle, AddedWhen, ChangedWhen) + Persistence Hint | ✅ |
|
||||||
| 6d | Dokument anzeigen (PDF Preview Popup mit DxPdfViewer) | ✅ |
|
| 6d | Dokument anzeigen (PDF Preview Popup mit DxPdfViewer) | ✅ |
|
||||||
|
| 6e | Export (XLSX/CSV/PDF + Selected Rows Only) | ✅ |
|
||||||
|
|
||||||
**Key decisions:** TBDD_CACHE statt eigener DB-Tabelle · `SenderOrReceiver` Policy · Slider statt Toggle · `signflow.theme` in localStorage · DevExpress 25.2.3 hat keine `.dark.`-Suffix-Dateien
|
**Key decisions:** TBDD_CACHE statt eigener DB-Tabelle · `SenderOrReceiver` Policy · Slider statt Toggle · `signflow.theme` in localStorage · DevExpress 25.2.3 hat keine `.dark.`-Suffix-Dateien
|
||||||
|
|
||||||
@@ -310,20 +311,40 @@ Button: nur aktiv wenn Envelope ausgewählt UND Dokument vorhanden (`envelope.Do
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔲 Phase 6e — Export nach Excel
|
## ✅ Phase 6e — Export (XLSX/CSV/PDF)
|
||||||
|
|
||||||
### Technischer Ansatz
|
### Technischer Ansatz
|
||||||
|
|
||||||
`DxGrid.ExportToXlsxAsync(fileName, options)` — direkt im Razor-Code, keine Backend-Implementierung nötig.
|
`DxGrid`-Export direkt im Razor-Code, keine Backend-Implementierung nötig.
|
||||||
Download geht direkt an den Browser.
|
Download geht direkt an den Browser.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
async Task ExportToExcelAsync()
|
async Task ExportSelectedFormatAsync()
|
||||||
{
|
{
|
||||||
var grid = _activeTab == "active" ? _gridActive : _gridCompleted;
|
var grid = _activeTab == "active" ? _gridActive : _gridCompleted;
|
||||||
if (grid is null) return;
|
if (grid is null) return;
|
||||||
var fileName = $"Umschlaege_{DateTime.Now:yyyy-MM-dd}.xlsx";
|
|
||||||
await grid.ExportToXlsxAsync(fileName, new GridXlExportOptions());
|
var baseName = $"Umschlaege_{DateTime.Now:yyyy-MM-dd}";
|
||||||
|
switch (_selectedExportFormat)
|
||||||
|
{
|
||||||
|
case "csv":
|
||||||
|
await grid.ExportToCsvAsync(baseName, new GridCsvExportOptions {
|
||||||
|
ExportSelectedRowsOnly = _exportSelectedRowsOnly
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "pdf":
|
||||||
|
await grid.ExportToPdfAsync(baseName, new GridPdfExportOptions {
|
||||||
|
ExportSelectedRowsOnly = _exportSelectedRowsOnly,
|
||||||
|
SelectedRowsExportMode = GridSelectedRowsExportMode.KeepGrouping
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await grid.ExportToXlsxAsync(baseName, new GridXlExportOptions {
|
||||||
|
ExportSelectedRowsOnly = _exportSelectedRowsOnly,
|
||||||
|
SelectedRowsExportMode = GridSelectedRowsExportMode.KeepGrouping
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -331,13 +352,15 @@ async Task ExportToExcelAsync()
|
|||||||
> Die Spalten `Status`, `EnvelopeReceivers` nutzen Templates → Wert im Export ist leer.
|
> Die Spalten `Status`, `EnvelopeReceivers` nutzen Templates → Wert im Export ist leer.
|
||||||
> **Future Task:** Für vollständigen Export mit formatierten Zellen `CustomizeCell`-Event nutzen.
|
> **Future Task:** Für vollständigen Export mit formatierten Zellen `CustomizeCell`-Event nutzen.
|
||||||
|
|
||||||
Button in Toolbar:
|
UI in Toolbar (kompakt):
|
||||||
```razor
|
```razor
|
||||||
<button class="sender-btn" @onclick="ExportToExcelAsync"
|
<select @bind="_selectedExportFormat">
|
||||||
title="Aktuelle Ansicht als Excel exportieren">
|
<option value="xlsx">XLSX</option>
|
||||||
<!-- Excel-Icon SVG -->
|
<option value="csv">CSV</option>
|
||||||
Export nach Excel
|
<option value="pdf">PDF</option>
|
||||||
</button>
|
</select>
|
||||||
|
<input type="checkbox" @bind="_exportSelectedRowsOnly" /> Nur ausgewählte
|
||||||
|
<button @onclick="ExportSelectedFormatAsync">Exportieren</button>
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -399,7 +422,7 @@ und sollte zuletzt implementiert werden, wenn alle Komponenten fertig sind.
|
|||||||
| # | Thema | Status |
|
| # | Thema | Status |
|
||||||
|---|-------|--------|
|
|---|-------|--------|
|
||||||
| 1 | `DxPdfViewer` → InteractiveServer zwingend | Architektonisch gelöst (6d) |
|
| 1 | `DxPdfViewer` → InteractiveServer zwingend | Architektonisch gelöst (6d) |
|
||||||
| 2 | Excel-Export: Template-Spalten leer im Export | Akzeptiert, Future Task (CustomizeCell) |
|
| 2 | Export: Template-Spalten leer im Export | Akzeptiert, Future Task (CustomizeCell) |
|
||||||
| 3 | `Zurückrufen/löschen` — Bedeutung unklar | Kommentar-Frage vorbereitet (6f) |
|
| 3 | `Zurückrufen/löschen` — Bedeutung unklar | Kommentar-Frage vorbereitet (6f) |
|
||||||
| 4 | `EnvelopeTypeTitle` Mapping prüfen | Vor 6c verifizieren |
|
| 4 | `EnvelopeTypeTitle` Mapping prüfen | Vor 6c verifizieren |
|
||||||
| 5 | Logo-Pfade produktionsspezifisch | Via `appsettings.json` konfigurierbar |
|
| 5 | Logo-Pfade produktionsspezifisch | Via `appsettings.json` konfigurierbar |
|
||||||
|
|||||||
Reference in New Issue
Block a user