feat(kpi): K14 — Site Calls trend charts
This commit is contained in:
+100
@@ -3,8 +3,10 @@ using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.SiteCalls;
|
||||
|
||||
@@ -94,6 +96,29 @@ public partial class SiteCallsReport
|
||||
private DateTime? _fromFilter;
|
||||
private DateTime? _toFilter;
|
||||
|
||||
// ── Trends (K14) ──────────────────────────────────────────────────────────
|
||||
// Collapsible KPI-history charts for the SiteCallAudit / Global series. The
|
||||
// section is collapsed on init and the series load lazily on first expand
|
||||
// (and on each 24h/7d window toggle), so the page's primary job — the call
|
||||
// list — never waits on the history query. Each metric carries its own
|
||||
// series + availability + error so one metric's failure leaves the other two
|
||||
// charts intact; a GetSeriesAsync throw never breaks the grid above.
|
||||
private bool _trendsExpanded;
|
||||
private bool _trendsLoading;
|
||||
private int _windowHours = 24;
|
||||
|
||||
private IReadOnlyList<KpiSeriesPoint>? _bufferedSeries;
|
||||
private bool _bufferedAvailable = true;
|
||||
private string? _bufferedError;
|
||||
|
||||
private IReadOnlyList<KpiSeriesPoint>? _parkedSeries;
|
||||
private bool _parkedAvailable = true;
|
||||
private string? _parkedError;
|
||||
|
||||
private IReadOnlyList<KpiSeriesPoint>? _failedSeries;
|
||||
private bool _failedAvailable = true;
|
||||
private string? _failedError;
|
||||
|
||||
private bool HasNextPage => _nextCursor is not null;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -521,4 +546,79 @@ public partial class SiteCallsReport
|
||||
|
||||
return Task.FromResult(_permittedSiteIds.Contains(resolved.Id));
|
||||
}
|
||||
|
||||
// ── Trends (K14) ──────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Expand/collapse the Trends section. The series load on first expand (and
|
||||
/// stay loaded across collapse/expand cycles); collapsing leaves the loaded
|
||||
/// series in place so re-expanding is instant. A re-fetch only happens on the
|
||||
/// window toggle.
|
||||
/// </summary>
|
||||
private async Task ToggleTrendsAsync()
|
||||
{
|
||||
_trendsExpanded = !_trendsExpanded;
|
||||
if (_trendsExpanded && _bufferedSeries is null && _parkedSeries is null && _failedSeries is null)
|
||||
{
|
||||
await LoadTrendsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Switch the trend window (24h / 7d) and re-load the series.</summary>
|
||||
private async Task SetWindowAsync(int windowHours)
|
||||
{
|
||||
if (_windowHours == windowHours)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_windowHours = windowHours;
|
||||
await LoadTrendsAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load all three SiteCallAudit / Global trend series for the current window.
|
||||
/// Best-effort: each <see cref="IKpiHistoryQueryService.GetSeriesAsync"/> is
|
||||
/// wrapped independently so one metric's failure renders only that chart
|
||||
/// unavailable, and a throw never propagates to break the Site Calls grid.
|
||||
/// </summary>
|
||||
private async Task LoadTrendsAsync()
|
||||
{
|
||||
_trendsLoading = true;
|
||||
|
||||
var toUtc = DateTime.UtcNow;
|
||||
var fromUtc = toUtc - TimeSpan.FromHours(_windowHours);
|
||||
|
||||
(_bufferedSeries, _bufferedAvailable, _bufferedError) =
|
||||
await LoadSeriesAsync("buffered", fromUtc, toUtc);
|
||||
(_parkedSeries, _parkedAvailable, _parkedError) =
|
||||
await LoadSeriesAsync("parked", fromUtc, toUtc);
|
||||
(_failedSeries, _failedAvailable, _failedError) =
|
||||
await LoadSeriesAsync("failedLastInterval", fromUtc, toUtc);
|
||||
|
||||
_trendsLoading = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetch one SiteCallAudit / Global series, swallowing any failure into the
|
||||
/// chart's unavailable state. Returns the points (null on failure), the
|
||||
/// availability flag, and a short error message for the chart placeholder.
|
||||
/// </summary>
|
||||
private async Task<(IReadOnlyList<KpiSeriesPoint>? Points, bool Available, string? Error)>
|
||||
LoadSeriesAsync(string metric, DateTime fromUtc, DateTime toUtc)
|
||||
{
|
||||
try
|
||||
{
|
||||
var points = await KpiHistory.GetSeriesAsync(
|
||||
KpiSources.SiteCallAudit, metric, KpiScopes.Global, scopeKey: null, fromUtc, toUtc);
|
||||
return (points, true, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// A KPI-history hiccup must never break the Site Calls page — the
|
||||
// chart degrades to its unavailable placeholder instead.
|
||||
Logger.LogWarning(ex, "Failed to load Site Calls trend series for metric {Metric}.", metric);
|
||||
return (null, false, "Trend data unavailable.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user