From c754666a3d38db6ae4ed211519ac6f256464ab40 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 23 May 2026 18:37:53 -0400 Subject: [PATCH] fix(ui): carry SourceNode on SiteCallDetail + NotificationDetail records The Site Calls and Notifications detail modals were reading SourceNode from the summary record (d.SourceNode) while every other field read from the detail record (det.X). The pattern works today because the modal always opens via a row click that pre-loads the summary, but a future drill-in from a deep link or refresh path could leave the summary stale or null and the field would render blank or wrong. Add SourceNode to both detail records, project it through the actor's ToDetail mapping, and switch the razor markup to read det.SourceNode. Now the modal binds uniformly to the detail record across all fields. --- .../Components/Pages/Notifications/NotificationReport.razor | 2 +- .../Components/Pages/SiteCalls/SiteCallsReport.razor | 2 +- src/ScadaLink.Commons/Messages/Audit/SiteCallQueries.cs | 3 ++- .../Messages/Notification/NotificationOutboxQueries.cs | 3 ++- src/ScadaLink.NotificationOutbox/NotificationOutboxActor.cs | 3 ++- src/ScadaLink.SiteCallAudit/SiteCallAuditActor.cs | 3 ++- .../NotificationOutboxActorQueryTests.cs | 5 +++++ .../SiteCallAuditActorTests.cs | 6 +++++- 8 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ScadaLink.CentralUI/Components/Pages/Notifications/NotificationReport.razor b/src/ScadaLink.CentralUI/Components/Pages/Notifications/NotificationReport.razor index 207f402a..a1e2c1ea 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Notifications/NotificationReport.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/Notifications/NotificationReport.razor @@ -266,7 +266,7 @@
@SiteName(d.SourceSiteId)
Source node
-
@(string.IsNullOrEmpty(d.SourceNode) ? "—" : d.SourceNode)
+
@(string.IsNullOrEmpty(_detail?.SourceNode) ? "—" : _detail.SourceNode)
Source instance
@(string.IsNullOrEmpty(d.SourceInstanceId) ? "—" : d.SourceInstanceId)
diff --git a/src/ScadaLink.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor b/src/ScadaLink.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor index 511c24c4..d24af8c4 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor @@ -267,7 +267,7 @@
@SiteName(det.SourceSite)
Source node
-
@(string.IsNullOrEmpty(d.SourceNode) ? "—" : d.SourceNode)
+
@(string.IsNullOrEmpty(det.SourceNode) ? "—" : det.SourceNode)
Channel
@det.Channel
diff --git a/src/ScadaLink.Commons/Messages/Audit/SiteCallQueries.cs b/src/ScadaLink.Commons/Messages/Audit/SiteCallQueries.cs index 93658c51..4245e3c4 100644 --- a/src/ScadaLink.Commons/Messages/Audit/SiteCallQueries.cs +++ b/src/ScadaLink.Commons/Messages/Audit/SiteCallQueries.cs @@ -119,7 +119,8 @@ public sealed record SiteCallDetail( DateTime CreatedAtUtc, DateTime UpdatedAtUtc, DateTime? TerminalAtUtc, - DateTime IngestedAtUtc); + DateTime IngestedAtUtc, + string? SourceNode = null); /// /// Site Calls UI -> Central: request for the global SiteCalls KPI summary. diff --git a/src/ScadaLink.Commons/Messages/Notification/NotificationOutboxQueries.cs b/src/ScadaLink.Commons/Messages/Notification/NotificationOutboxQueries.cs index 27bd888d..e6b63654 100644 --- a/src/ScadaLink.Commons/Messages/Notification/NotificationOutboxQueries.cs +++ b/src/ScadaLink.Commons/Messages/Notification/NotificationOutboxQueries.cs @@ -119,7 +119,8 @@ public record NotificationDetail( DateTimeOffset CreatedAt, DateTimeOffset? LastAttemptAt, DateTimeOffset? NextAttemptAt, - DateTimeOffset? DeliveredAt); + DateTimeOffset? DeliveredAt, + string? SourceNode = null); /// /// Outbox UI -> Central: request for the notification outbox KPI summary. diff --git a/src/ScadaLink.NotificationOutbox/NotificationOutboxActor.cs b/src/ScadaLink.NotificationOutbox/NotificationOutboxActor.cs index c7a91efe..8fc69568 100644 --- a/src/ScadaLink.NotificationOutbox/NotificationOutboxActor.cs +++ b/src/ScadaLink.NotificationOutbox/NotificationOutboxActor.cs @@ -749,7 +749,8 @@ public class NotificationOutboxActor : ReceiveActor, IWithTimers notification.CreatedAt, notification.LastAttemptAt, notification.NextAttemptAt, - notification.DeliveredAt); + notification.DeliveredAt, + notification.SourceNode); return new NotificationDetailResponse( request.CorrelationId, Success: true, ErrorMessage: null, detail); diff --git a/src/ScadaLink.SiteCallAudit/SiteCallAuditActor.cs b/src/ScadaLink.SiteCallAudit/SiteCallAuditActor.cs index 7539f3a4..cfc29cef 100644 --- a/src/ScadaLink.SiteCallAudit/SiteCallAuditActor.cs +++ b/src/ScadaLink.SiteCallAudit/SiteCallAuditActor.cs @@ -652,7 +652,8 @@ public class SiteCallAuditActor : ReceiveActor CreatedAtUtc: row.CreatedAtUtc, UpdatedAtUtc: row.UpdatedAtUtc, TerminalAtUtc: row.TerminalAtUtc, - IngestedAtUtc: row.IngestedAtUtc); + IngestedAtUtc: row.IngestedAtUtc, + SourceNode: row.SourceNode); } /// diff --git a/tests/ScadaLink.NotificationOutbox.Tests/NotificationOutboxActorQueryTests.cs b/tests/ScadaLink.NotificationOutbox.Tests/NotificationOutboxActorQueryTests.cs index a7fa0e4b..4f71714a 100644 --- a/tests/ScadaLink.NotificationOutbox.Tests/NotificationOutboxActorQueryTests.cs +++ b/tests/ScadaLink.NotificationOutbox.Tests/NotificationOutboxActorQueryTests.cs @@ -354,6 +354,7 @@ public class NotificationOutboxActorQueryTests : TestKit row.ResolvedTargets = "[\"ops@example.com\",\"oncall@example.com\"]"; row.TypeData = "{\"priority\":\"high\"}"; row.SourceScript = "HighLevelAlarm.csx"; + row.SourceNode = "node-a"; row.SiteEnqueuedAt = DateTimeOffset.UtcNow.AddMinutes(-5); row.DeliveredAt = DateTimeOffset.UtcNow; _repository.GetByIdAsync(row.NotificationId, Arg.Any()).Returns(row); @@ -377,6 +378,10 @@ public class NotificationOutboxActorQueryTests : TestKit Assert.Equal("instance-42", detail.SourceInstanceId); Assert.Equal(2, detail.RetryCount); Assert.Equal("transient blip", detail.LastError); + // SourceNode flows through the detail projection so the report detail + // modal binds uniformly to the detail record (was previously read off + // the summary). + Assert.Equal("node-a", detail.SourceNode); } [Fact] diff --git a/tests/ScadaLink.SiteCallAudit.Tests/SiteCallAuditActorTests.cs b/tests/ScadaLink.SiteCallAudit.Tests/SiteCallAuditActorTests.cs index 0d6fd264..d1814134 100644 --- a/tests/ScadaLink.SiteCallAudit.Tests/SiteCallAuditActorTests.cs +++ b/tests/ScadaLink.SiteCallAudit.Tests/SiteCallAuditActorTests.cs @@ -402,7 +402,8 @@ public class SiteCallAuditActorTests : TestKit, IClassFixture