Files
Khwezi Mngoma 51946a1388
continuous-integration/drone/pr Build is passing
Started producer design
2026-05-18 19:19:26 +02:00

258 lines
12 KiB
Plaintext

@page "/notifications"
@using Blazored.Toast
@using Microsoft.AspNetCore.Components.QuickGrid
@rendermode RenderMode.InteractiveServer
<PageTitle>Notifications | Shop Console</PageTitle>
<div class="workspace-ambient-backdrop"></div>
@if (NotificationQueryable == null && IsLoading)
{
<div class="initial-page-fullscreen-loader">
<div class="loader-content">
<span class="spinner"></span>
<p>Initializing Operational Console registers...</p>
</div>
</div>
}
<div class="console-workspace">
<div class="page-header">
<div>
<h2>System Logs & Notifications</h2>
<p class="text-muted">Inbound and outbound communication engine event log tracking.</p>
</div>
<button class="btn-refresh" @onclick="HandleRefreshClickAsync" disabled="@IsLoading">
<svg class="refresh-icon @(IsLoading ? "spinning" : "")" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67" />
</svg>
Sync Console
</button>
</div>
<div class="dashboard-control-deck">
<div class="top-row-panels">
<div class="mini-table-card shadow-card">
<span class="panel-title-lbl">Operational Inventory</span>
<table class="dashboard-mini-table">
<thead>
<tr>
<th>Metric Item</th>
<th>Telemetry Node</th>
<th style="text-align: right;">Register</th>
</tr>
</thead>
<tbody>
<tr>
<td>Total Inbound Volume</td>
<td class="node-dim">Core-System</td>
<td class="text-cyan text-mono">@TotalCount</td>
</tr>
<tr>
<td>Operational Failures</td>
<td class="node-dim">Fault-Engine</td>
<td class="text-red text-mono">@ErrorCount</td>
</tr>
<tr>
<td>Pending Dispatches</td>
<td class="node-dim">Queue-Worker</td>
<td class="text-yellow text-mono">@PendingCount</td>
</tr>
</tbody>
</table>
</div>
<div class="radial-gauge-card shadow-card">
<span class="panel-title-lbl">Health Efficiency</span>
<div class="gauge-presentation-box">
<div class="svg-gauge-container">
<svg class="gauge-ring-matrix" viewBox="0 0 36 36">
<path class="gauge-bg-track" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="gauge-active-fill text-purple" stroke-dasharray="@SuccessRate, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div class="gauge-center-text text-purple">@SuccessRate%</div>
</div>
<div class="svg-gauge-container">
<svg class="gauge-ring-matrix" viewBox="0 0 36 36">
<path class="gauge-bg-track" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="gauge-active-fill text-cyan" stroke-dasharray="@(TotalCount > 0 ? Math.Round(((double)PendingCount / TotalCount) * 100) : 0), 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div class="gauge-center-text text-cyan">@((TotalCount > 0) ? Math.Round(((double)PendingCount / TotalCount) * 100) : 0)%</div>
</div>
</div>
</div>
</div>
<div class="filter-panel shadow-card">
<span class="panel-title-lbl" style="margin-bottom:1.25rem; display:block;">Telemetry Filters</span>
<div class="filter-grid-form">
<div class="input-wrapper full-width">
<label>Search Text</label>
<input type="text" placeholder="Search subject, sender or recipient..." @bind-value="SearchFilter" @bind-value:event="oninput" />
</div>
<div class="input-wrapper">
<label>From Date</label>
<div class="themed-date-picker-box">
<input type="date" @bind="FromDate" @bind:after="LoadNotificationDataAsync" />
</div>
</div>
<div class="input-wrapper">
<label>To Date</label>
<div class="themed-date-picker-box">
<input type="date" @bind="ToDate" @bind:after="LoadNotificationDataAsync" />
</div>
</div>
<div class="input-wrapper">
<label>Max Records</label>
<div class="themed-dropdown-box">
<select @bind="MaxRecords">
<option value="50">50 rows</option>
<option value="100">100 rows</option>
<option value="500">500 rows</option>
</select>
</div>
</div>
<div class="input-wrapper action-wrapper">
<button class="btn-apply-filters" @onclick="HandleRefreshClickAsync" disabled="@IsLoading">Apply</button>
</div>
</div>
</div>
<div class="trend-spline-card shadow-card full-width-row">
<span class="panel-title-lbl">Operational Load Density Timeline</span>
<div class="vector-chart-viewport">
<svg class="area-spline-graph" viewBox="0 0 500 100" preserveAspectRatio="none">
<defs>
<linearGradient id="purpleGlow" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#bd00ff" stop-opacity="0.25" />
<stop offset="100%" stop-color="#bd00ff" stop-opacity="0.0" />
</linearGradient>
</defs>
<path class="area-fill-path" d="M 0 90 Q 50 85, 100 60 T 200 45 T 300 80 T 400 30 T 500 15 L 500 100 L 0 100 Z" fill="url(#purpleGlow)" />
<path class="line-stroke-path" d="M 0 90 Q 50 85, 100 60 T 200 45 T 300 80 T 400 30 T 500 15" fill="none" stroke="#bd00ff" stroke-width="2" />
</svg>
<div class="chart-axis-labels">
<span>04/17</span>
<span>04/24</span>
<span>05/01</span>
<span>05/08</span>
<span>05/17</span>
</div>
</div>
</div>
</div>
@if (NotificationQueryable == null && IsLoading)
{
<div class="grid-inline-loading-veil">
<div class="veil-content">
<div class="console-sync-loader">
<div class="inner-ring"></div>
<div class="outer-ring"></div>
</div>
<p class="sync-text">Syncing system log registers...</p>
</div>
</div>
}
else
{
<div class="grid-card-wrapper full-width-table relative-grid-container">
@if (IsLoading)
{
<div class="grid-inline-loading-veil">
<div class="veil-content">
<span class="spinner"></span>
<p>Syncing system log registers...</p>
</div>
</div>
}
<QuickGrid Items="@NotificationQueryable" Pagination="@Pagination" RowClass="@GetRowClass">
<TemplateColumn Title="Origin" SortBy="@SortByOrigin" Sortable="true">
<div class="origin-composite">
<span class="platform-lbl">@context.Platform</span>
<span class="direction-lbl text-muted">@context.Direction</span>
</div>
</TemplateColumn>
<TemplateColumn Title="Priority" SortBy="@SortByPriority" Sortable="true">
<span class="priority-tag @context.Priority.ToString().ToLower()">
@context.Priority
</span>
</TemplateColumn>
<PropertyColumn Property="@(n => n.CreatedAt)" Title="Created At" Format="yyyy-MM-dd HH:mm" Sortable="true" />
<TemplateColumn Title="Sender" SortBy="@SortBySender" Sortable="true">
<div class="contact-composite" title="@context.SenderAddress">
<span class="contact-name">@(string.IsNullOrWhiteSpace(context.SenderName) ? "System Core" : context.SenderName)</span>
<span class="contact-email text-muted">@context.SenderAddress</span>
</div>
</TemplateColumn>
<TemplateColumn Title="Subject" SortBy="@SortBySubject" Sortable="true">
<div class="subject-cell">
<span class="subject-text" title="@context.Subject">@context.Subject</span>
@if (!string.IsNullOrWhiteSpace(context.Message))
{
<span class="message-subtext text-muted" title="@context.Message">@context.Message</span>
}
</div>
</TemplateColumn>
<TemplateColumn Title="Recipient" SortBy="@SortByRecipient" Sortable="true">
<div class="contact-composite" title="@context.RecipientAddress">
<span class="contact-name">@context.RecipientName</span>
<span class="contact-email text-muted">@context.RecipientAddress</span>
</div>
</TemplateColumn>
<TemplateColumn Title="Status" SortBy="@SortByStatus" Sortable="true" Align="Align.Center">
@if (context.HasError)
{
<span class="status-indicator alert-text">✕ Error</span>
}
else if (context.Processed)
{
<span class="status-indicator success-text">✓ Processed</span>
}
else
{
<span class="status-indicator idle-text">⚙ Pending</span>
}
</TemplateColumn>
<TemplateColumn Align="Align.Right">
@if (context.HasError)
{
<button class="grid-retry-btn" @onclick="() => HandleRetryRowAsync(context)">
Retry
</button>
}
else
{
@((MarkupString)"&nbsp;")
}
</TemplateColumn>
</QuickGrid>
</div>
<div class="grid-paginator-wrapper">
<Paginator State="@Pagination" />
</div>
}
</div>
<BlazoredToasts />