@page "/" @using Microsoft.EntityFrameworkCore @using TrafagSalesExporter.Data @using TrafagSalesExporter.Services @inject IDbContextFactory DbFactory @inject ExportOrchestrationService Orchestrator @inject TimerBackgroundService TimerService @inject ISnackbar Snackbar @implements IDisposable Dashboard Dashboard Alle exportieren @if (TimerService.NextRun < DateTime.MaxValue) { @($"Nächster automatischer Lauf: {TimerService.NextRun:dd.MM.yyyy HH:mm}") } else { @("Timer deaktiviert") } Land TSC Schema Server Status Zeilen Letzter Lauf Dauer Aktion @context.Land @context.TSC @context.Schema @context.ServerName @if (Orchestrator.IsExporting(context.SiteId)) { @Orchestrator.GetExportStatus(context.SiteId) } else if (context.LastStatus == "OK") { } else if (context.LastStatus == "Error") { } else { - } @(context.RowCount > 0 ? context.RowCount.ToString("N0") : "-") @(context.LastRun.HasValue ? context.LastRun.Value.ToString("dd.MM.yyyy HH:mm:ss") : "-") @(context.DurationSeconds > 0 ? $"{context.DurationSeconds:F1}s" : "-") Export @code { private List _dashboardRows = new(); private bool _loading = true; private bool _anyRunning; protected override async Task OnInitializedAsync() { Orchestrator.OnExportStatusChanged += HandleStatusChanged; await LoadDataAsync(); } private async Task LoadDataAsync() { _loading = true; using var db = await DbFactory.CreateDbContextAsync(); var sites = await db.Sites.Include(s => s.HanaServer).Where(s => s.IsActive).ToListAsync(); var logs = await db.ExportLogs .GroupBy(l => l.SiteId) .Select(g => g.OrderByDescending(l => l.Timestamp).First()) .ToListAsync(); _dashboardRows = sites.Select(s => { var log = logs.FirstOrDefault(l => l.SiteId == s.Id); return new DashboardRow { SiteId = s.Id, Land = s.Land, TSC = s.TSC, Schema = s.Schema, ServerName = s.HanaServer?.Name ?? "", LastStatus = log?.Status ?? "", RowCount = log?.RowCount ?? 0, LastRun = log?.Timestamp, DurationSeconds = log?.DurationSeconds ?? 0, ErrorMessage = log?.ErrorMessage ?? "" }; }).ToList(); _anyRunning = _dashboardRows.Any(r => Orchestrator.IsExporting(r.SiteId)); _loading = false; } private async Task ExportAll() { _anyRunning = true; _ = Task.Run(async () => { await Orchestrator.ExportAllAsync(); await InvokeAsync(async () => { await LoadDataAsync(); StateHasChanged(); }); }); Snackbar.Add("Export für alle Standorte gestartet", Severity.Info); } private void ExportSingle(int siteId) { _ = Task.Run(async () => { await Orchestrator.ExportSiteByIdAsync(siteId); await InvokeAsync(async () => { await LoadDataAsync(); StateHasChanged(); }); }); Snackbar.Add("Export gestartet", Severity.Info); } private async void HandleStatusChanged() { await InvokeAsync(async () => { _anyRunning = _dashboardRows.Any(r => Orchestrator.IsExporting(r.SiteId)); StateHasChanged(); if (!_anyRunning) { await LoadDataAsync(); StateHasChanged(); } }); } public void Dispose() { Orchestrator.OnExportStatusChanged -= HandleStatusChanged; } private class DashboardRow { public int SiteId { get; set; } public string Land { get; set; } = ""; public string TSC { get; set; } = ""; public string Schema { get; set; } = ""; public string ServerName { get; set; } = ""; public string LastStatus { get; set; } = ""; public int RowCount { get; set; } public DateTime? LastRun { get; set; } public double DurationSeconds { get; set; } public string ErrorMessage { get; set; } = ""; } }