@using EntKube.Web.Data @using EntKube.Web.Services @inject IncidentService IncidentService @inject AuthenticationStateProvider AuthStateProvider @if (stats is not null) {
@stats.Active
Active
@stats.Acknowledged
Acknowledged
@stats.Total
Total (@stats.WindowDays)d
@stats.Critical
Critical
@stats.FormatMttr()
MTTR
@stats.FormatMtta()
MTTA
@if (stats.Daily.Count > 1) { int maxCount = stats.Daily.Max(d => d.Count);
Alert volume (30d)
@foreach (DailyAlertCount day in stats.Daily) { int h = maxCount > 0 ? (int)Math.Max(2, day.Count * 32.0 / maxCount) : 2; string color = day.Count == 0 ? "#dee2e6" : "#dc3545";
}
}
}
@incidents.Count incident@(incidents.Count == 1 ? "" : "s")
@if (isLoading) {
Loading incidents…
} else if (incidents.Count == 0) {
No incidents found for the selected filters.
} else {
@foreach (AlertIncident incident in incidents) { bool expanded = expandedId == incident.Id; @if (expanded) { } }
Severity Alert Cluster Started Duration Status Actions
@SeverityBadge(incident.Severity) @incident.AlertName @incident.Cluster?.Name @incident.StartsAt.ToLocalTime().ToString("MMM d HH:mm") @FormatDuration(incident) @StatusBadge(incident.Status) @if (incident.Status == IncidentStatus.Active) { } @if (incident.Status != IncidentStatus.Resolved) { }
@if (!string.IsNullOrEmpty(incident.Summary)) {

Summary: @incident.Summary

} @if (!string.IsNullOrEmpty(incident.Description)) {

@incident.Description

} @if (incident.AcknowledgedBy is not null) {

Acknowledged by @incident.AcknowledgedBy at @incident.AcknowledgedAt?.ToLocalTime().ToString("MMM d HH:mm")

} @if (incident.Notes.Count > 0) {
Notes @foreach (IncidentNote note in incident.Notes) {
@note.Author · @note.CreatedAt.ToLocalTime().ToString("MMM d HH:mm")
@note.Content
}
}
} @code { [Parameter] public required Guid TenantId { get; set; } private List incidents = []; private List clusters = []; private AlertStats? stats; private Guid? expandedId; private string filterStatus = ""; private string filterSeverity = ""; private string filterClusterId = ""; private int filterDays = 7; private string noteText = ""; private bool isLoading = true; private string? currentUser; protected override async Task OnInitializedAsync() { AuthenticationState authState = await AuthStateProvider.GetAuthenticationStateAsync(); currentUser = authState.User.Identity?.Name ?? "Unknown"; await Task.WhenAll(LoadIncidents(), LoadStats()); } private async Task LoadStats() { stats = await IncidentService.GetStatsForTenantAsync(TenantId, windowDays: 30); } private async Task LoadIncidents() { isLoading = true; StateHasChanged(); IncidentStatus? status = filterStatus switch { "Active" => IncidentStatus.Active, "Acknowledged" => IncidentStatus.Acknowledged, "Resolved" => IncidentStatus.Resolved, _ => null }; Guid? clusterId = Guid.TryParse(filterClusterId, out Guid cid) ? cid : null; incidents = await IncidentService.GetIncidentsForTenantAsync( TenantId, status: status, severity: string.IsNullOrEmpty(filterSeverity) ? null : filterSeverity, clusterId: clusterId, from: DateTime.UtcNow.AddDays(-filterDays), to: null); // Collect unique clusters from results clusters = incidents .Where(i => i.Cluster is not null) .Select(i => i.Cluster!) .DistinctBy(c => c.Id) .OrderBy(c => c.Name) .ToList(); isLoading = false; } private void ToggleExpand(Guid id) { expandedId = expandedId == id ? null : id; noteText = ""; } private async Task Acknowledge(AlertIncident incident) { await IncidentService.AcknowledgeAsync(incident.Id, currentUser!); await LoadIncidents(); } private async Task Resolve(AlertIncident incident) { await IncidentService.ResolveAsync(incident.Id, currentUser!); await LoadIncidents(); } private async Task AddNote(AlertIncident incident) { if (string.IsNullOrWhiteSpace(noteText)) return; await IncidentService.AddNoteAsync(incident.Id, currentUser!, noteText.Trim()); noteText = ""; await LoadIncidents(); expandedId = incident.Id; } private static string FormatDuration(AlertIncident incident) { DateTime end = incident.EndsAt ?? DateTime.UtcNow; TimeSpan duration = end - incident.StartsAt; if (duration.TotalDays >= 1) return $"{(int)duration.TotalDays}d {duration.Hours}h"; if (duration.TotalHours >= 1) return $"{(int)duration.TotalHours}h {duration.Minutes}m"; return $"{(int)duration.TotalMinutes}m"; } private static RenderFragment SeverityBadge(string severity) => severity switch { "critical" => @Critical, "warning" => @Warning, "info" => @Info, _ => @@severity }; private static RenderFragment StatusBadge(IncidentStatus status) => status switch { IncidentStatus.Active => @Active, IncidentStatus.Acknowledged => @Acknowledged, IncidentStatus.Resolved => @Resolved, _ => @@status }; }