refactor(ui/monitoring): KPI dashboard, message expand, copy, pagination fix
Dashboard: user-info card demoted; 4 KPI cards (Sites, Data
connections, Templates, API keys) sourced from existing repositories;
3 Quick-action link cards (Health, Audit Log, Templates). Inline
max-width style replaced with Bootstrap utilities.
Health: KPI row condensed to Online / Offline / Sites with active
errors (Total Sites and Total Script Errors dropped). Per-site cards
re-laid out 2-column with each subsection (Data Connections,
Instances & Queues, Errors & Parked Messages) inside Bootstrap
collapse panels collapsed by default. Online / Offline / Primary /
Standby badges paired with shape glyphs (o / * / triangle) plus
aria-label.
EventLogs: filter row wrapped in a Bootstrap collapse toggled by
"Filter options (n active)"; per-row View toggle reveals the full
message in a collapse row; "Keyword" relabeled "Message contains";
all filter inputs gain id+label-for+aria-label; severity badges paired
with a leading glyph; explicit "End of results" terminator on
Load more.
ParkedMessages: Message ID rendered as <code>{first 12}...</code>
plus a clipboard button; per-row View toggle reveals full error;
action buttons get aria-label="{Retry|Discard} message {id}";
in-flight spinner inside the active button.
AuditLog: pagination Next-disabled now uses
_page * _pageSize >= _totalCount via HasMore helper (fixes the
exactly-page-size edge case). Clear filters button added. Entity ID
rendered as code + clipboard button. View/Hide buttons gain
aria-label referencing the entry id. State JSON larger than 1 KB
renders a "View in modal" button instead of the inline overflow.
This commit is contained in:
@@ -1,33 +1,118 @@
|
||||
@page "/"
|
||||
@attribute [Authorize]
|
||||
@using ScadaLink.Commons.Interfaces.Repositories
|
||||
@inject ISiteRepository SiteRepository
|
||||
@inject ITemplateEngineRepository TemplateEngineRepository
|
||||
@inject IInboundApiRepository InboundApiRepository
|
||||
|
||||
<div class="container mt-4">
|
||||
<h3>Welcome to ScadaLink</h3>
|
||||
<div class="container-fluid mt-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0">Welcome to ScadaLink</h4>
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<span class="text-muted small">
|
||||
Signed in as <strong>@context.User.FindFirst("DisplayName")?.Value</strong>
|
||||
</span>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
</div>
|
||||
<p class="text-muted">Central management console for the ScadaLink SCADA system.</p>
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<div class="card mt-3" style="max-width: 500px;">
|
||||
<div class="card-body">
|
||||
<h6 class="card-subtitle mb-2 text-muted">Signed in as</h6>
|
||||
<p class="card-text mb-1"><strong>@context.User.FindFirst("DisplayName")?.Value</strong></p>
|
||||
<p class="card-text small text-muted mb-2">@context.User.FindFirst("Username")?.Value</p>
|
||||
|
||||
@{
|
||||
var roles = context.User.FindAll("Role").Select(c => c.Value).ToList();
|
||||
}
|
||||
@if (roles.Count > 0)
|
||||
{
|
||||
<h6 class="card-subtitle mb-1 mt-3 text-muted">Roles</h6>
|
||||
<div>
|
||||
@foreach (var role in roles)
|
||||
{
|
||||
<span class="badge bg-secondary me-1">@role</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@* KPI row *@
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-lg-3 col-md-6 col-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="fs-2 fw-bold">@(_loaded ? _siteCount.ToString() : "—")</div>
|
||||
<div class="text-muted small">Sites configured</div>
|
||||
</div>
|
||||
</div>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-6 col-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="fs-2 fw-bold">@(_loaded ? _dataConnectionCount.ToString() : "—")</div>
|
||||
<div class="text-muted small">Data connections configured</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-6 col-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="fs-2 fw-bold">@(_loaded ? _templateCount.ToString() : "—")</div>
|
||||
<div class="text-muted small">Templates</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-6 col-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="fs-2 fw-bold">@(_loaded ? _apiKeyCount.ToString() : "—")</div>
|
||||
<div class="text-muted small">API keys</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Quick actions *@
|
||||
<h6 class="text-muted text-uppercase small mb-2">Quick actions</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-4 col-md-6 col-12">
|
||||
<a class="card h-100 text-decoration-none text-reset" href="/monitoring/health">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h6 class="mb-1">Health Dashboard</h6>
|
||||
<span class="text-muted">→</span>
|
||||
</div>
|
||||
<p class="text-muted small mb-0">Live cluster, data connection, and queue health per site.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 col-12">
|
||||
<a class="card h-100 text-decoration-none text-reset" href="/monitoring/audit-log">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h6 class="mb-1">Recent Audit Log</h6>
|
||||
<span class="text-muted">→</span>
|
||||
</div>
|
||||
<p class="text-muted small mb-0">Browse changes to configuration and deployments.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 col-12">
|
||||
<a class="card h-100 text-decoration-none text-reset" href="/design/templates">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h6 class="mb-1">Templates</h6>
|
||||
<span class="text-muted">→</span>
|
||||
</div>
|
||||
<p class="text-muted small mb-0">Design templates, shared scripts, and external systems.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private bool _loaded;
|
||||
private int _siteCount;
|
||||
private int _dataConnectionCount;
|
||||
private int _templateCount;
|
||||
private int _apiKeyCount;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_siteCount = (await SiteRepository.GetAllSitesAsync()).Count;
|
||||
_dataConnectionCount = (await SiteRepository.GetAllDataConnectionsAsync()).Count;
|
||||
_templateCount = (await TemplateEngineRepository.GetAllTemplatesAsync()).Count;
|
||||
_apiKeyCount = (await InboundApiRepository.GetAllApiKeysAsync()).Count;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Non-fatal — leave counts at zero with the placeholder rendering.
|
||||
}
|
||||
_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user