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.