From 3f1c0e5018470d3e66dcc02cbac166020c9a66e7 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 21 May 2026 20:30:48 -0400 Subject: [PATCH] fix(centralui): re-apply Audit Log query-string filters on same-page drill-in The drilldown drawer's 'View this/parent execution' actions call NavigationManager.NavigateTo('/audit/log?executionId=...') while the user is already on the routed AuditLogPage. Blazor treats this as a same-component navigation, so OnInitialized does not re-run and ApplyQueryStringFilters() (which was wired only to OnInitialized) never re-parsed the new query string: _currentFilter stayed stale and the results grid never reloaded to the drill-in target. AuditLogPage now subscribes to NavigationManager.LocationChanged, re-applies the query-string filters on every location change (closing the drawer and calling StateHasChanged), and unsubscribes via IDisposable. The 'View parent execution' drill-in now genuinely lands on /audit/log?executionId={parentId} with the grid reloaded. Also corrects the Playwright test wait: a same-page query-string Blazor navigation pushes history.pushState over the SignalR circuit rather than triggering a document load, so WaitForLoadState(NetworkIdle) returned before the URL settled. Switched to WaitForURLAsync, the correct primitive for SPA/pushState navigations. --- .../Pages/Audit/AuditLogPage.razor.cs | 37 ++++++++++++++++++- .../Audit/AuditLogPageTests.cs | 7 +++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/ScadaLink.CentralUI/Components/Pages/Audit/AuditLogPage.razor.cs b/src/ScadaLink.CentralUI/Components/Pages/Audit/AuditLogPage.razor.cs index 56a1227..2aeb593 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Audit/AuditLogPage.razor.cs +++ b/src/ScadaLink.CentralUI/Components/Pages/Audit/AuditLogPage.razor.cs @@ -1,5 +1,6 @@ using System.Globalization; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Routing; using Microsoft.AspNetCore.WebUtilities; using ScadaLink.Commons.Entities.Audit; using ScadaLink.Commons.Types.Audit; @@ -31,8 +32,20 @@ namespace ScadaLink.CentralUI.Components.Pages.Audit; /// without the user clicking Apply. Unknown values (e.g. an invalid enum name) /// are silently dropped — the page still renders, just without that constraint. /// +/// +/// +/// Query-string filters are re-applied on every , +/// not just on init. The drilldown drawer's "View this/parent execution" actions +/// navigate to /audit/log?executionId=… while the user is ALREADY on this +/// routed page — Blazor treats that as a same-component navigation, so +/// does not re-run. Without the +/// subscription the URL would +/// change but would stay stale and the grid would +/// never reload to the new drill-in. The subscription is disposed via +/// . +/// /// -public partial class AuditLogPage +public partial class AuditLogPage : IDisposable { [Inject] private NavigationManager Navigation { get; set; } = null!; @@ -44,6 +57,28 @@ public partial class AuditLogPage protected override void OnInitialized() { ApplyQueryStringFilters(); + Navigation.LocationChanged += HandleLocationChanged; + } + + /// + /// Re-applies the query-string drill-in filters when the URL changes while + /// this page stays routed (e.g. the drawer's "View parent execution" action + /// navigates to /audit/log?executionId=…). Reassigning + /// to a fresh instance is what kicks the results + /// grid into reloading; we also close the drawer so the operator sees the + /// newly filtered grid, and call + /// because this fires outside the normal render lifecycle. + /// + private void HandleLocationChanged(object? sender, LocationChangedEventArgs e) + { + ApplyQueryStringFilters(); + _drawerOpen = false; + StateHasChanged(); + } + + public void Dispose() + { + Navigation.LocationChanged -= HandleLocationChanged; } private void ApplyQueryStringFilters() diff --git a/tests/ScadaLink.CentralUI.PlaywrightTests/Audit/AuditLogPageTests.cs b/tests/ScadaLink.CentralUI.PlaywrightTests/Audit/AuditLogPageTests.cs index 256c7d1..d3e77c8 100644 --- a/tests/ScadaLink.CentralUI.PlaywrightTests/Audit/AuditLogPageTests.cs +++ b/tests/ScadaLink.CentralUI.PlaywrightTests/Audit/AuditLogPageTests.cs @@ -415,7 +415,12 @@ public class AuditLogPageTests var viewParent = page.Locator("[data-test='view-parent-execution']"); await Assertions.Expect(viewParent).ToBeVisibleAsync(); await viewParent.ClickAsync(); - await page.WaitForLoadStateAsync(LoadState.NetworkIdle); + // The drawer's NavigateTo is a same-page (query-string-only) Blazor + // navigation: it pushes history.pushState over the SignalR circuit + // rather than triggering a document load, so WaitForLoadState would + // return before the URL settles. WaitForURLAsync is the correct wait + // primitive for SPA/pushState navigations. + await page.WaitForURLAsync($"**/audit/log?executionId={parentExecutionId}"); // The drill-in lands on ?executionId={parentExecutionId} and auto-loads // the spawner's own row.