258 lines
12 KiB
Plaintext
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)" ")
|
|
}
|
|
</TemplateColumn>
|
|
|
|
</QuickGrid>
|
|
</div>
|
|
|
|
<div class="grid-paginator-wrapper">
|
|
<Paginator State="@Pagination" />
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<BlazoredToasts /> |