Add admin access and landing dashboard
This commit is contained in:
@@ -0,0 +1,606 @@
|
||||
@page "/export-dashboard"
|
||||
@using System.Diagnostics
|
||||
@using TrafagSalesExporter.Services
|
||||
@inject IDashboardPageService DashboardPageActions
|
||||
@inject ExportOrchestrationService Orchestrator
|
||||
@inject TimerBackgroundService TimerService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IUiTextService UiText
|
||||
@implements IDisposable
|
||||
|
||||
<PageTitle>@T("Export Dashboard", "Export dashboard")</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h4" Class="mb-4">@T("Export Dashboard", "Export dashboard")</MudText>
|
||||
|
||||
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
||||
<div class="dashboard-header">
|
||||
<MudStack Row AlignItems="AlignItems.Center" Spacing="4" Class="dashboard-actions">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.PlayArrow"
|
||||
OnClick="ExportAll" Disabled="_anyRunning">
|
||||
@T("Alle exportieren", "Export all")
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.TableView"
|
||||
OnClick="ExportConsolidatedOnly" Disabled="_anyRunning">
|
||||
@T("Zentrale Datei neu erzeugen", "Rebuild consolidated file")
|
||||
</MudButton>
|
||||
<MudText Typo="Typo.body1">
|
||||
@if (TimerService.NextRun < DateTime.MaxValue)
|
||||
{
|
||||
<MudIcon Icon="@Icons.Material.Filled.Schedule" Size="Size.Small" Class="mr-1" />
|
||||
@(string.Format(T("Naechster automatischer Lauf: {0}", "Next automatic run: {0}"), TimerService.NextRun.ToString("dd.MM.yyyy HH:mm")))
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIcon Icon="@Icons.Material.Filled.TimerOff" Size="Size.Small" Class="mr-1" />
|
||||
@T("Timer deaktiviert", "Timer disabled")
|
||||
}
|
||||
</MudText>
|
||||
</MudStack>
|
||||
<div class="dashboard-manometer" aria-label="Export activity manometer">
|
||||
<div class="manometer-arc">
|
||||
<span class="tick tick-0"></span>
|
||||
<span class="tick tick-1"></span>
|
||||
<span class="tick tick-2"></span>
|
||||
<span class="tick tick-3"></span>
|
||||
<span class="tick tick-4"></span>
|
||||
<span class="needle"></span>
|
||||
<span class="hub"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MudPaper>
|
||||
|
||||
@if (_readinessWarnings.Count > 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense Class="mb-4">
|
||||
<MudText Typo="Typo.body2">@T("Aktive Standorte sind noch nicht vollstaendig bereit:", "Active sites are not fully ready:")</MudText>
|
||||
@foreach (var warning in _readinessWarnings)
|
||||
{
|
||||
<MudText Typo="Typo.caption">@warning</MudText>
|
||||
}
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@if (_consolidatedStale)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense Class="mb-4">
|
||||
@T("Seit der letzten zentralen Excel wurde mindestens ein Standort neu exportiert. Bitte `Zentrale Datei neu erzeugen` ausfuehren, damit das Endexcel aktuell ist.",
|
||||
"At least one site was exported after the last consolidated Excel. Please rebuild the consolidated file so the final Excel is current.")
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
<MudTable Items="_dashboardRows" Dense Hover Striped Loading="_loading">
|
||||
<HeaderContent>
|
||||
<MudTh>@T("Land", "Country")</MudTh>
|
||||
<MudTh>@T("Basis", "Basis")</MudTh>
|
||||
<MudTh>TSC</MudTh>
|
||||
<MudTh>@T("Schema", "Schema")</MudTh>
|
||||
<MudTh>@T("Server", "Server")</MudTh>
|
||||
<MudTh>@T("Status", "Status")</MudTh>
|
||||
<MudTh>@T("Live-Status", "Live status")</MudTh>
|
||||
<MudTh>@T("Zeilen", "Rows")</MudTh>
|
||||
<MudTh>@T("Letzter Lauf", "Last run")</MudTh>
|
||||
<MudTh>@T("Dauer", "Duration")</MudTh>
|
||||
<MudTh>@T("Aktion", "Action")</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Land</MudTd>
|
||||
<MudTd>
|
||||
<MudTooltip Text="@context.DataBasis">
|
||||
<MudStack Row AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@GetDataBasisIcon(context.DataBasis)" Color="@GetDataBasisColor(context.DataBasis)" Size="Size.Small" />
|
||||
<MudText Typo="Typo.caption">@context.DataBasis</MudText>
|
||||
</MudStack>
|
||||
</MudTooltip>
|
||||
</MudTd>
|
||||
<MudTd>@context.TSC</MudTd>
|
||||
<MudTd>@context.Schema</MudTd>
|
||||
<MudTd>@context.ServerName</MudTd>
|
||||
<MudTd>
|
||||
@if (Orchestrator.IsExporting(context.SiteId))
|
||||
{
|
||||
<MudProgressCircular Size="Size.Small" Indeterminate Color="Color.Primary" Class="mr-1" />
|
||||
<MudText Typo="Typo.caption">@Orchestrator.GetExportStatus(context.SiteId)</MudText>
|
||||
}
|
||||
else if (context.LastStatus == "OK")
|
||||
{
|
||||
<MudIcon Icon="@Icons.Material.Filled.CheckCircle" Color="Color.Success" Size="Size.Small" />
|
||||
}
|
||||
else if (context.LastStatus == "Error")
|
||||
{
|
||||
<MudTooltip Text="@context.ErrorMessage">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Error" Color="Color.Error" Size="Size.Small" />
|
||||
</MudTooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Default">-</MudText>
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
@if (!string.IsNullOrWhiteSpace(context.LiveMessage))
|
||||
{
|
||||
<MudTooltip Text="@context.LiveDetails">
|
||||
<MudText Typo="Typo.caption" Style="max-width:360px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:block;">
|
||||
@context.LiveMessage
|
||||
</MudText>
|
||||
</MudTooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Default">-</MudText>
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd>@(context.RowCount > 0 ? context.RowCount.ToString("N0") : "-")</MudTd>
|
||||
<MudTd>@(context.LastRun.HasValue ? context.LastRun.Value.ToString("dd.MM.yyyy HH:mm:ss") : "-")</MudTd>
|
||||
<MudTd>@(context.DurationSeconds > 0 ? $"{context.DurationSeconds:F1}s" : "-")</MudTd>
|
||||
<MudTd>
|
||||
<MudStack Row Spacing="1">
|
||||
<MudButton Size="Size.Small" Variant="Variant.Outlined" Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.FileDownload"
|
||||
OnClick="() => ExportSingle(context.SiteId)"
|
||||
Disabled="Orchestrator.IsExporting(context.SiteId)">
|
||||
Export
|
||||
</MudButton>
|
||||
<MudButton Size="Size.Small" Variant="Variant.Text" Color="Color.Info"
|
||||
StartIcon="@Icons.Material.Filled.OpenInNew"
|
||||
OnClick="() => OpenExportFile(context)"
|
||||
Disabled="@(!context.HasOpenableFile || Orchestrator.IsExporting(context.SiteId))">
|
||||
@T("Excel oeffnen", "Open Excel")
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
<MudPaper Class="pa-4 mt-4" Elevation="1">
|
||||
<MudText Typo="Typo.h6" Class="mb-3">@T("Zentrale Datei", "Consolidated file")</MudText>
|
||||
<MudTable Items="_consolidatedRows" Dense Hover Striped>
|
||||
<HeaderContent>
|
||||
<MudTh>@T("Datei", "File")</MudTh>
|
||||
<MudTh>Pfad</MudTh>
|
||||
<MudTh>Letzte Änderung</MudTh>
|
||||
<MudTh>@T("Status", "Status")</MudTh>
|
||||
<MudTh>@T("Aktion", "Action")</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Label</MudTd>
|
||||
<MudTd>@context.DisplayPath</MudTd>
|
||||
<MudTd>@(context.LastModified.HasValue ? context.LastModified.Value.ToString("dd.MM.yyyy HH:mm:ss") : "-")</MudTd>
|
||||
<MudTd>
|
||||
@if (Orchestrator.IsConsolidatedExporting())
|
||||
{
|
||||
<MudProgressCircular Size="Size.Small" Indeterminate Color="Color.Primary" Class="mr-1" />
|
||||
<MudText Typo="Typo.caption">@Orchestrator.GetConsolidatedExportStatus()</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Default">-</MudText>
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
<MudButton Size="Size.Small" Variant="Variant.Text" Color="Color.Info"
|
||||
StartIcon="@Icons.Material.Filled.OpenInNew"
|
||||
OnClick="() => OpenFile(context.FilePath)"
|
||||
Disabled="@(!context.HasOpenableFile)">
|
||||
@T("Excel oeffnen", "Open Excel")
|
||||
</MudButton>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
<NoRecordsContent>
|
||||
<MudText Typo="Typo.caption">@T("Keine zentrale Excel-Datei gefunden.", "No consolidated Excel file found.")</MudText>
|
||||
</NoRecordsContent>
|
||||
</MudTable>
|
||||
</MudPaper>
|
||||
|
||||
<style>
|
||||
.dashboard-header {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 220px;
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dashboard-actions {
|
||||
min-width: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.dashboard-manometer {
|
||||
justify-self: end;
|
||||
width: 210px;
|
||||
height: 118px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
border: 1px solid #111;
|
||||
border-radius: 6px;
|
||||
padding: 12px 14px 10px;
|
||||
}
|
||||
|
||||
.manometer-arc {
|
||||
position: relative;
|
||||
width: 170px;
|
||||
height: 86px;
|
||||
border: 8px solid #111;
|
||||
border-bottom: 0;
|
||||
border-radius: 170px 170px 0 0;
|
||||
background: #fff;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.manometer-arc::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
bottom: -1px;
|
||||
height: 70px;
|
||||
border: 2px solid #111;
|
||||
border-bottom: 0;
|
||||
border-radius: 140px 140px 0 0;
|
||||
}
|
||||
|
||||
.tick {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
height: 16px;
|
||||
background: #111;
|
||||
transform-origin: 50% 78px;
|
||||
}
|
||||
|
||||
.tick-0 { transform: translateX(-50%) rotate(-70deg); }
|
||||
.tick-1 { transform: translateX(-50%) rotate(-35deg); }
|
||||
.tick-2 { transform: translateX(-50%) rotate(0deg); }
|
||||
.tick-3 { transform: translateX(-50%) rotate(35deg); }
|
||||
.tick-4 { transform: translateX(-50%) rotate(70deg); }
|
||||
|
||||
.needle {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
height: 72px;
|
||||
background: #111;
|
||||
border-radius: 4px;
|
||||
transform-origin: 50% 100%;
|
||||
animation: manometer-sweep 5.8s infinite cubic-bezier(.45, 0, .25, 1);
|
||||
}
|
||||
|
||||
.hub {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: -8px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
background: #111;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@@keyframes manometer-sweep {
|
||||
0% { transform: translateX(-50%) rotate(-52deg); }
|
||||
11% { transform: translateX(-50%) rotate(18deg); }
|
||||
19% { transform: translateX(-50%) rotate(-8deg); }
|
||||
33% { transform: translateX(-50%) rotate(63deg); }
|
||||
48% { transform: translateX(-50%) rotate(4deg); }
|
||||
61% { transform: translateX(-50%) rotate(38deg); }
|
||||
74% { transform: translateX(-50%) rotate(-41deg); }
|
||||
88% { transform: translateX(-50%) rotate(55deg); }
|
||||
100% { transform: translateX(-50%) rotate(-52deg); }
|
||||
}
|
||||
|
||||
@@media (max-width: 900px) {
|
||||
.dashboard-header {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.dashboard-manometer {
|
||||
justify-self: start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
private List<DashboardRow> _dashboardRows = new();
|
||||
private List<ConsolidatedDashboardRow> _consolidatedRows = new();
|
||||
private List<string> _readinessWarnings = new();
|
||||
private bool _consolidatedStale;
|
||||
private bool _loading = true;
|
||||
private bool _anyRunning;
|
||||
private CancellationTokenSource? _pollingCts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Orchestrator.OnExportStatusChanged += HandleStatusChanged;
|
||||
await LoadDataAsync();
|
||||
}
|
||||
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
_loading = true;
|
||||
var state = await DashboardPageActions.LoadAsync();
|
||||
_dashboardRows = state.DashboardRows;
|
||||
_consolidatedRows = state.ConsolidatedRows;
|
||||
_readinessWarnings = state.ReadinessWarnings;
|
||||
_consolidatedStale = state.IsConsolidatedStale;
|
||||
|
||||
_anyRunning = _dashboardRows.Any(r => Orchestrator.IsExporting(r.SiteId)) || Orchestrator.IsConsolidatedExporting();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
private async Task ExportAll()
|
||||
{
|
||||
if (_readinessWarnings.Count > 0)
|
||||
{
|
||||
Snackbar.Add(T("Es gibt aktive Standorte mit fehlender manueller Datei. Bitte Warnung im Dashboard pruefen.",
|
||||
"There are active sites with missing manual files. Please check the dashboard warning."), Severity.Warning);
|
||||
}
|
||||
|
||||
_anyRunning = true;
|
||||
await LoadDataAsync();
|
||||
StartPolling();
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Orchestrator.ExportAllAsync();
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(T("Export fuer alle Standorte beendet", "Export completed for all sites"), Severity.Success));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(string.Format(T("Export fuer alle Standorte fehlgeschlagen: {0}", "Export for all sites failed: {0}"), FormatException(ex)), Severity.Error));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
await LoadDataAsync();
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
});
|
||||
Snackbar.Add(T("Export fuer alle Standorte gestartet", "Export started for all sites"), Severity.Info);
|
||||
}
|
||||
|
||||
private async Task ExportConsolidatedOnly()
|
||||
{
|
||||
_anyRunning = true;
|
||||
await LoadDataAsync();
|
||||
StartPolling();
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var filePath = await Orchestrator.ExportConsolidatedOnlyAsync();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(filePath))
|
||||
{
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(string.Format(T("Zentrale Datei erzeugt: {0}", "Consolidated file created: {0}"), filePath), Severity.Success));
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(T("Zentrale Datei konnte nicht erzeugt werden. Details stehen in den Logs.", "Consolidated file could not be created. Details are in the logs."), Severity.Warning));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(string.Format(T("Zentrale Datei fehlgeschlagen: {0}", "Consolidated file failed: {0}"), FormatException(ex)), Severity.Error));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
await LoadDataAsync();
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
});
|
||||
Snackbar.Add(T("Zentrale Datei wird erzeugt", "Building consolidated file"), Severity.Info);
|
||||
}
|
||||
|
||||
private void ExportSingle(int siteId)
|
||||
{
|
||||
_anyRunning = true;
|
||||
_ = InvokeAsync(async () => await LoadDataAsync());
|
||||
StartPolling();
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await Orchestrator.ExportSiteByIdAsync(siteId);
|
||||
|
||||
if (result?.Log.Status == "OK" && !string.IsNullOrWhiteSpace(result.FilePath))
|
||||
{
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(string.Format(T("Export gespeichert: {0}", "Export saved: {0}"), result.FilePath), Severity.Success));
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(T("Die zentrale Excel ist danach noch nicht automatisch aktualisiert. Bitte `Zentrale Datei neu erzeugen` starten.",
|
||||
"The consolidated Excel is not automatically updated after this. Please rebuild the consolidated file."), Severity.Info));
|
||||
}
|
||||
else if (result?.Log.Status == "Error" && !string.IsNullOrWhiteSpace(result.Log.ErrorMessage))
|
||||
{
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(string.Format(T("Export fehlgeschlagen: {0}", "Export failed: {0}"), result.Log.ErrorMessage), Severity.Error));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await InvokeAsync(() =>
|
||||
Snackbar.Add(string.Format(T("Export fehlgeschlagen: {0}", "Export failed: {0}"), FormatException(ex)), Severity.Error));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
await LoadDataAsync();
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
});
|
||||
Snackbar.Add(T("Export gestartet", "Export started"), Severity.Info);
|
||||
}
|
||||
|
||||
private async void HandleStatusChanged()
|
||||
{
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
_anyRunning = _dashboardRows.Any(r => Orchestrator.IsExporting(r.SiteId)) || Orchestrator.IsConsolidatedExporting() || _dashboardRows.Count == 0;
|
||||
if (_anyRunning)
|
||||
{
|
||||
StartPolling();
|
||||
await RefreshLiveDataAsync();
|
||||
StateHasChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
StopPolling();
|
||||
await LoadDataAsync();
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopPolling();
|
||||
Orchestrator.OnExportStatusChanged -= HandleStatusChanged;
|
||||
}
|
||||
|
||||
private void OpenExportFile(DashboardRow row)
|
||||
{
|
||||
OpenFile(row.FilePath);
|
||||
}
|
||||
|
||||
private void OpenFile(string filePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath))
|
||||
{
|
||||
Snackbar.Add(T("Exportdatei nicht gefunden.", "Export file not found."), Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = filePath,
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add(string.Format(T("Datei konnte nicht geoeffnet werden: {0}", "Could not open file: {0}"), ex.Message), Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartPolling()
|
||||
{
|
||||
if (_pollingCts is not null && !_pollingCts.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
_pollingCts = new CancellationTokenSource();
|
||||
_ = PollDashboardAsync(_pollingCts.Token);
|
||||
}
|
||||
|
||||
private void StopPolling()
|
||||
{
|
||||
_pollingCts?.Cancel();
|
||||
_pollingCts?.Dispose();
|
||||
_pollingCts = null;
|
||||
}
|
||||
|
||||
private async Task PollDashboardAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(3));
|
||||
|
||||
try
|
||||
{
|
||||
while (await timer.WaitForNextTickAsync(cancellationToken))
|
||||
{
|
||||
var anyRunning = _dashboardRows.Any(r => Orchestrator.IsExporting(r.SiteId)) || Orchestrator.IsConsolidatedExporting();
|
||||
if (!anyRunning)
|
||||
{
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
_anyRunning = false;
|
||||
await LoadDataAsync();
|
||||
StateHasChanged();
|
||||
});
|
||||
StopPolling();
|
||||
break;
|
||||
}
|
||||
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
_anyRunning = true;
|
||||
await RefreshLiveDataAsync();
|
||||
StateHasChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private Task RefreshLiveDataAsync()
|
||||
{
|
||||
foreach (var row in _dashboardRows)
|
||||
{
|
||||
if (!Orchestrator.IsExporting(row.SiteId))
|
||||
continue;
|
||||
|
||||
row.LiveMessage = Orchestrator.GetExportStatus(row.SiteId);
|
||||
row.LiveDetails = string.Empty;
|
||||
}
|
||||
|
||||
_anyRunning = _dashboardRows.Any(r => Orchestrator.IsExporting(r.SiteId)) || Orchestrator.IsConsolidatedExporting();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static string FormatException(Exception ex)
|
||||
=> ex.InnerException is null ? ex.Message : $"{ex.Message} Inner: {ex.InnerException.Message}";
|
||||
|
||||
private static string GetDataBasisIcon(string dataBasis)
|
||||
{
|
||||
if (dataBasis.Contains("Excel", StringComparison.OrdinalIgnoreCase))
|
||||
return Icons.Material.Filled.TableView;
|
||||
if (dataBasis.Contains("CSV", StringComparison.OrdinalIgnoreCase) ||
|
||||
dataBasis.Contains("Datei", StringComparison.OrdinalIgnoreCase))
|
||||
return Icons.Material.Filled.Description;
|
||||
if (dataBasis.Contains("SAP", StringComparison.OrdinalIgnoreCase))
|
||||
return Icons.Material.Filled.CloudSync;
|
||||
if (dataBasis.Contains("Server", StringComparison.OrdinalIgnoreCase))
|
||||
return Icons.Material.Filled.Storage;
|
||||
|
||||
return Icons.Material.Filled.Source;
|
||||
}
|
||||
|
||||
private static Color GetDataBasisColor(string dataBasis)
|
||||
{
|
||||
if (dataBasis.Contains("Excel", StringComparison.OrdinalIgnoreCase))
|
||||
return Color.Success;
|
||||
if (dataBasis.Contains("CSV", StringComparison.OrdinalIgnoreCase) ||
|
||||
dataBasis.Contains("Datei", StringComparison.OrdinalIgnoreCase))
|
||||
return Color.Info;
|
||||
if (dataBasis.Contains("SAP", StringComparison.OrdinalIgnoreCase))
|
||||
return Color.Primary;
|
||||
if (dataBasis.Contains("Server", StringComparison.OrdinalIgnoreCase))
|
||||
return Color.Secondary;
|
||||
|
||||
return Color.Default;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@code {
|
||||
private string T(string german, string english) => UiText.Text(german, english);
|
||||
}
|
||||
Reference in New Issue
Block a user