sqlinsight-pro/src/SQLInsight.Web/Components/Pages/Home.razor
Søren Eggert Lundsteen Olsen 0dc7866808 Separate "check by hand" from "not relevant" for version-gated rules
A rule whose topic applies but whose way of measuring needs a newer
version or patch now gives the new outcome ManualCheck with concrete
Danish guidance, instead of NotApplicable. It is marked explicitly per
rule (IRule.ManualCheck) and only used when every missing capability is
a measurement capability (InstanceCapabilities.IsMeasurement): CHECKDB
status (SQLI-DB-009), VLFs (SQLI-DB-011), spills and memory grants
(SQLI-PLAN-004/005) and deadlocks on 2005 (SQLI-DLK-001). Query Store,
AG and Azure-managed topics stay "not relevant".

Shown in a visible "Skal tjekkes manuelt" section on the instance page,
a KPI on the overview, a "Tjek manuelt" column in the management
report's controls, manualChecks in /api/v1/instances and manualCheck in
rule-states; not counted as not measurable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:59:50 +02:00

82 lines
4.2 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@page "/"
@inject InsightRepository Repository
@inject Display Display
<PageTitle>SQLInsight Pro</PageTitle>
<h1>Overblik</h1>
<p class="lead">Status for alle overvågede SQL Server-instanser.</p>
@if (_instances.Count == 0)
{
<div class="empty">
<p>Der er endnu ingen data. Tjek, at SQLInsight Agent kører og har instanser under <code>SQLInsight:Instances</code> i sin konfiguration.</p>
</div>
}
else
{
<PrivilegeAlert Findings="_active" ShowInstance="true" />
<div class="kpis">
<div class="kpi"><span class="kpi-icon"><Icon Name="servers" /></span><div><strong>@_instances.Count</strong><span>instanser</span></div></div>
<div class="kpi sev-critical"><span class="kpi-icon"><Icon Name="alert" /></span><div><strong>@_instances.Sum(i => i.Critical)</strong><span>kritiske fund</span></div></div>
<div class="kpi sev-high"><span class="kpi-icon"><Icon Name="alert" /></span><div><strong>@_instances.Sum(i => i.High)</strong><span>høje fund</span></div></div>
<div class="kpi sev-info"><span class="kpi-icon"><Icon Name="unplug" /></span><div><strong>@_instances.Count(i => i.IsFailing)</strong><span>kan ikke nås</span></div></div>
<div class="kpi sev-info"><span class="kpi-icon"><Icon Name="gauge" /></span><div><strong>@_instances.Sum(i => i.NotMeasurable)</strong><span>regler ikke målbare</span></div></div>
<div class="kpi sev-info"><span class="kpi-icon"><Icon Name="list" /></span><div><strong>@_instances.Sum(i => i.ManualChecks)</strong><span>skal tjekkes manuelt</span></div></div>
</div>
<section class="card">
<div class="card-head"><h2>Instanser</h2><span class="muted">sorteret efter alvor</span></div>
<table class="instances">
<thead>
<tr><th>Status</th><th>Instans</th><th>Version</th><th>Fund</th><th>Ikke målbar</th><th>Sidst set</th></tr>
</thead>
<tbody>
@foreach (var i in _instances.OrderByDescending(i => i.IsFailing).ThenByDescending(i => i.WorstSeverity ?? (Severity)(-1)).ThenBy(i => i.Name))
{
<tr>
<td>
@if (i.IsFailing)
{
<span class="badge sev-critical" title="@i.LastError">Fejler</span>
}
else
{
<SeverityBadge Severity="i.WorstSeverity" />
}
</td>
<td><a href="/instanser/@Uri.EscapeDataString(i.Name)"><strong>@i.Name</strong></a><div class="muted">@i.ServerName</div></td>
<td>@(i.ProductVersion is null ? "–" : Display.Release(i.ProductVersion, i.EngineEdition))<div class="muted">@i.ProductVersion @i.ProductLevel @i.ProductUpdateLevel</div></td>
<td><div class="counts">
@if (i.Critical > 0) { <SeverityBadge Severity="Severity.Critical" Count="i.Critical" /> }
@if (i.High > 0) { <SeverityBadge Severity="Severity.High" Count="i.High" /> }
@if (i.Medium > 0) { <SeverityBadge Severity="Severity.Medium" Count="i.Medium" /> }
@if (i.Low > 0) { <SeverityBadge Severity="Severity.Low" Count="i.Low" /> }
</div></td>
<td>@i.NotMeasurable</td>
<td title="@Display.Time(i.LastSeenAt)">@Display.Ago(i.LastSeenAt)</td>
</tr>
}
</tbody>
</table>
</section>
<section class="card">
<div class="card-head"><h2>Kritiske og høje fund</h2><a href="/fund">Se alle fund</a></div>
<FindingList Findings="_urgent" ShowInstance="true" Empty="Ingen kritiske eller høje fund." />
</section>
}
@code {
private IReadOnlyList<InstanceOverview> _instances = [];
private IReadOnlyList<StoredFinding> _active = [];
private IReadOnlyList<StoredFinding> _urgent = [];
protected override async Task OnInitializedAsync()
{
_instances = await Repository.GetInstancesAsync();
_active = await Repository.GetActiveFindingsAsync();
_urgent = _active.Where(f => f.Severity >= Severity.High).ToList();
}
}