@@ -12,6 +13,23 @@
+ @* 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)
+ {
+
+
+ Filtered by Bundle Import: @bundleId.ToString()[..8]
+
+
+
+ }
+
@@ -190,6 +208,13 @@
@code {
+ ///
+ /// T24 (Transport). When non-null, scopes the page to a single bundle
+ /// import run. Set via the ?bundleImportId= query string from
+ /// drill-ins (Import wizard summary, future BundleImported row links).
+ ///
+ [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);
diff --git a/src/ScadaLink.Commons/Interfaces/Repositories/ICentralUiRepository.cs b/src/ScadaLink.Commons/Interfaces/Repositories/ICentralUiRepository.cs
index ff012ad..8fad6b7 100644
--- a/src/ScadaLink.Commons/Interfaces/Repositories/ICentralUiRepository.cs
+++ b/src/ScadaLink.Commons/Interfaces/Repositories/ICentralUiRepository.cs
@@ -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);
diff --git a/src/ScadaLink.ConfigurationDatabase/Repositories/CentralUiRepository.cs b/src/ScadaLink.ConfigurationDatabase/Repositories/CentralUiRepository.cs
index 7973cef..2261334 100644
--- a/src/ScadaLink.ConfigurationDatabase/Repositories/CentralUiRepository.cs
+++ b/src/ScadaLink.ConfigurationDatabase/Repositories/CentralUiRepository.cs
@@ -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
diff --git a/tests/ScadaLink.ConfigurationDatabase.Tests/RepositoryTests.cs b/tests/ScadaLink.ConfigurationDatabase.Tests/RepositoryTests.cs
index aa6ba9c..a8d25af 100644
--- a/tests/ScadaLink.ConfigurationDatabase.Tests/RepositoryTests.cs
+++ b/tests/ScadaLink.ConfigurationDatabase.Tests/RepositoryTests.cs
@@ -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()
{