Implement export functionality with loading spinner

Replaced placeholder export method with a fully implemented
`ExportSelectedFormatAsync` method to support exporting grid
data in CSV, PDF, or XLSX formats. Added a loading spinner
to the export button to indicate progress during export
operations. Updated the `IsExportDisabled` method to account
for the new `_isExporting` state.

Disabled exporting for "Status" and "EnvelopeReceivers"
columns by setting their `ExportEnabled` property to `false`.
Made minor grid layout adjustments, including adding the
`KeyFieldName` property for proper grid behavior.
This commit is contained in:
2026-09-24 18:32:03 +02:00
parent 18df6221c9
commit f9f94c900e

View File

@@ -118,14 +118,21 @@
</label>
<button class="sender-btn sender-btn--export"
@onclick="ExportSelectedFormatPlaceholderAsync"
@onclick="ExportSelectedFormatAsync"
disabled="@IsExportDisabled()"
title="Export wird in Phase 2 aktiviert">
title="Aktuelle Ansicht exportieren">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M.5 9.9a.5.5 0 0 1 .5.5V13a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.6a.5.5 0 0 1 1 0V13a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.6a.5.5 0 0 1 .5-.5z" />
<path d="M5.354 6.146a.5.5 0 0 1 .707 0L7.5 7.586V.5a.5.5 0 0 1 1 0v7.086l1.439-1.44a.5.5 0 0 1 .707.708l-2.293 2.293a.5.5 0 0 1-.707 0L5.354 6.854a.5.5 0 0 1 0-.708z" />
</svg>
Exportieren
@if (_isExporting)
{
<span class="spinner-border spinner-border-sm" style="width: 14px; height: 14px;"></span>
}
else
{
<span>Exportieren</span>
}
</button>
</div>
@@ -209,6 +216,7 @@
{
<DxGrid Data="@_activeEnvelopes"
@ref="_gridActive"
KeyFieldName="Id"
ShowFilterRow="true"
ShowSearchBox="true"
AllowColumnReorder="true"
@@ -238,7 +246,7 @@
<strong>@((cellContext.DataItem as EnvelopeDto)?.Title)</strong>
</CellDisplayTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="Status" Caption="Status">
<DxGridDataColumn FieldName="Status" Caption="Status" ExportEnabled="false">
<CellDisplayTemplate Context="cellContext">
@{
var envelope = cellContext.DataItem as EnvelopeDto;
@@ -253,7 +261,7 @@
}
</CellDisplayTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="Empfänger">
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="Empfänger" ExportEnabled="false">
<CellDisplayTemplate Context="cellContext">
@{
var envelope = cellContext.DataItem as EnvelopeDto;
@@ -333,6 +341,7 @@
{
<DxGrid Data="@_completedEnvelopes"
@ref="_gridCompleted"
KeyFieldName="Id"
ShowFilterRow="true"
ShowSearchBox="true"
PageSize="20"
@@ -359,7 +368,7 @@
<strong>@((cellContext.DataItem as EnvelopeDto)?.Title)</strong>
</CellDisplayTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="Status" Caption="Status">
<DxGridDataColumn FieldName="Status" Caption="Status" ExportEnabled="false">
<CellDisplayTemplate Context="cellContext">
@{
var envelope = cellContext.DataItem as EnvelopeDto;
@@ -374,7 +383,7 @@
}
</CellDisplayTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="Empfänger">
<DxGridDataColumn FieldName="EnvelopeReceivers" Caption="Empfänger" ExportEnabled="false">
<CellDisplayTemplate Context="cellContext">
@{
var envelope = cellContext.DataItem as EnvelopeDto;
@@ -542,6 +551,7 @@
// Phase 6e (step 1): UI state only. Export logic is added in step 2.
private string _selectedExportFormat = "xlsx";
private bool _exportSelectedRowsOnly = false;
private bool _isExporting = false;
// Stable keys used to store/restore each grid's layout in UserPreferences.GridLayouts
private const string GridKeyActive = "sender.active-envelopes";
@@ -603,15 +613,62 @@
bool IsExportDisabled()
{
if (_isLoading)
if (_isLoading || _isExporting)
return true;
return _exportSelectedRowsOnly && _selectedEnvelope is null;
}
Task ExportSelectedFormatPlaceholderAsync()
async Task ExportSelectedFormatAsync()
{
return Task.CompletedTask;
var grid = _activeTab == "active" ? _gridActive : _gridCompleted;
if (grid is null)
return;
_isExporting = true;
await InvokeAsync(StateHasChanged);
try
{
var tabToken = _activeTab == "completed" ? "abgeschlossen" : "aktiv";
var stamp = DateTime.Now.ToString("yyyy-MM-dd");
var exportBaseName = $"Umschlaege_{tabToken}_{stamp}";
switch (_selectedExportFormat)
{
case "csv":
await grid.ExportToCsvAsync(
exportBaseName,
new GridCsvExportOptions
{
ExportSelectedRowsOnly = _exportSelectedRowsOnly
});
break;
case "pdf":
await grid.ExportToPdfAsync(
exportBaseName,
new GridPdfExportOptions
{
ExportSelectedRowsOnly = _exportSelectedRowsOnly,
SelectedRowsExportMode = GridSelectedRowsExportMode.KeepGrouping
});
break;
default:
await grid.ExportToXlsxAsync(
exportBaseName,
new GridXlExportOptions
{
ExportSelectedRowsOnly = _exportSelectedRowsOnly,
SelectedRowsExportMode = GridSelectedRowsExportMode.KeepGrouping
});
break;
}
}
finally
{
_isExporting = false;
await InvokeAsync(StateHasChanged);
}
}
void CreateEnvelope()