feat(centralui): Bundle Import filter on ConfigurationAuditLog page
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
@using ScadaLink.Commons.Interfaces.Repositories
|
||||
@attribute [Authorize(Policy = AuthorizationPolicies.OperationalAudit)]
|
||||
@inject ICentralUiRepository CentralUiRepository
|
||||
@inject NavigationManager Nav
|
||||
@inject IJSRuntime JS
|
||||
|
||||
<div class="container-fluid mt-3">
|
||||
@@ -12,6 +13,23 @@
|
||||
|
||||
<ToastNotification @ref="_toast" />
|
||||
|
||||
@* Bundle Import filter chip (T24). Set via ?bundleImportId={guid} query
|
||||
string so drill-ins from the Import wizard / other pages can scope this
|
||||
page to a single import run. Cleared via the × button, which navigates
|
||||
back to the page without the query param so the user sees all rows. *@
|
||||
@if (BundleImportId is Guid bundleId)
|
||||
{
|
||||
<div class="mb-3">
|
||||
<span class="badge bg-primary p-2">
|
||||
Filtered by Bundle Import: <code class="text-light">@bundleId.ToString()[..8]</code>
|
||||
<button type="button"
|
||||
class="btn-close btn-close-white btn-sm ms-2"
|
||||
aria-label="Clear Bundle Import filter"
|
||||
@onclick="ClearBundleImportFilter"></button>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row mb-3 g-2 align-items-end">
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small" for="audit-filter-user">User</label>
|
||||
@@ -190,6 +208,13 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// T24 (Transport). When non-null, scopes the page to a single bundle
|
||||
/// import run. Set via the <c>?bundleImportId=</c> query string from
|
||||
/// drill-ins (Import wizard summary, future BundleImported row links).
|
||||
/// </summary>
|
||||
[SupplyParameterFromQuery, Parameter] public Guid? BundleImportId { get; set; }
|
||||
|
||||
private string? _filterUser;
|
||||
private string? _filterEntityType;
|
||||
private string? _filterAction;
|
||||
@@ -216,6 +241,10 @@
|
||||
private int TotalPages => _pageSize > 0 ? Math.Max(1, (_totalCount + _pageSize - 1) / _pageSize) : 1;
|
||||
private bool HasMore => _page * _pageSize < _totalCount;
|
||||
|
||||
// Tracks the BundleImportId we last fetched against so a re-render with the
|
||||
// same query param doesn't re-run the query on every parameter set.
|
||||
private Guid? _lastFetchedBundleImportId;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!firstRender) return;
|
||||
@@ -233,6 +262,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// T24: when the BundleImportId query param is set (or cleared), refetch
|
||||
// automatically so the user lands on a pre-filtered page from a drill-in
|
||||
// link without having to click Search.
|
||||
if (BundleImportId != _lastFetchedBundleImportId)
|
||||
{
|
||||
_lastFetchedBundleImportId = BundleImportId;
|
||||
_page = 1;
|
||||
await FetchPage();
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearBundleImportFilter()
|
||||
{
|
||||
// Strip the query param by navigating to the bare page route. The
|
||||
// resulting OnParametersSetAsync run will refetch with BundleImportId
|
||||
// back to null.
|
||||
Nav.NavigateTo("/audit/configuration");
|
||||
}
|
||||
|
||||
private async Task Search()
|
||||
{
|
||||
_page = 1;
|
||||
@@ -265,6 +315,7 @@
|
||||
action: string.IsNullOrWhiteSpace(_filterAction) ? null : _filterAction.Trim(),
|
||||
from: BrowserTime.LocalInputToUtc(_filterFrom, _browserUtcOffsetMinutes),
|
||||
to: BrowserTime.LocalInputToUtc(_filterTo, _browserUtcOffsetMinutes),
|
||||
bundleImportId: BundleImportId,
|
||||
page: _page,
|
||||
pageSize: _pageSize);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public interface ICentralUiRepository
|
||||
DateTimeOffset? to = null,
|
||||
string? entityId = null,
|
||||
string? entityName = null,
|
||||
Guid? bundleImportId = null,
|
||||
int page = 1,
|
||||
int pageSize = 50,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
@@ -104,6 +104,7 @@ public class CentralUiRepository : ICentralUiRepository
|
||||
DateTimeOffset? to = null,
|
||||
string? entityId = null,
|
||||
string? entityName = null,
|
||||
Guid? bundleImportId = null,
|
||||
int page = 1,
|
||||
int pageSize = 50,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -131,6 +132,9 @@ public class CentralUiRepository : ICentralUiRepository
|
||||
if (!string.IsNullOrWhiteSpace(entityName))
|
||||
query = query.Where(a => a.EntityName.Contains(entityName));
|
||||
|
||||
if (bundleImportId is Guid bundleId)
|
||||
query = query.Where(a => a.BundleImportId == bundleId);
|
||||
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
|
||||
var entries = await query
|
||||
|
||||
@@ -353,6 +353,29 @@ public class CentralUiRepositoryTests : IDisposable
|
||||
Assert.Single(entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAuditLogEntries_FiltersByBundleImportId()
|
||||
{
|
||||
// T24 — Bundle Import filter on the Configuration Audit Log page is
|
||||
// backed by the new optional bundleImportId arg on the repo query.
|
||||
// Only rows stamped with the given id should come back.
|
||||
var importA = Guid.NewGuid();
|
||||
var importB = Guid.NewGuid();
|
||||
_context.AuditLogEntries.AddRange(
|
||||
new AuditLogEntry("admin", "Create", "Template", "1", "T1")
|
||||
{ Timestamp = DateTimeOffset.UtcNow, BundleImportId = importA },
|
||||
new AuditLogEntry("admin", "Create", "Template", "2", "T2")
|
||||
{ Timestamp = DateTimeOffset.UtcNow, BundleImportId = importB },
|
||||
new AuditLogEntry("admin", "Update", "Template", "3", "T3")
|
||||
{ Timestamp = DateTimeOffset.UtcNow });
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var (entries, total) = await _repository.GetAuditLogEntriesAsync(bundleImportId: importA);
|
||||
Assert.Single(entries);
|
||||
Assert.Equal(1, total);
|
||||
Assert.Equal(importA, entries[0].BundleImportId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAuditLogEntries_ReverseChronologicalWithPagination()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user