Files
scadalink-design/src/ScadaLink.Commons/Messages/Notification/NotificationOutboxQueries.cs
Joseph Doherty c754666a3d 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.
2026-05-23 18:37:53 -04:00

162 lines
4.9 KiB
C#

using ScadaLink.Commons.Types.Notifications;
namespace ScadaLink.Commons.Messages.Notification;
/// <summary>
/// Outbox UI -> Central: paginated, filtered query over the notification outbox.
/// All filter fields are optional; <see cref="StuckOnly"/> restricts results to stuck notifications.
/// </summary>
public record NotificationOutboxQueryRequest(
string CorrelationId,
string? StatusFilter,
string? TypeFilter,
string? SourceSiteFilter,
string? ListNameFilter,
bool StuckOnly,
string? SubjectKeyword,
DateTimeOffset? From,
DateTimeOffset? To,
int PageNumber,
int PageSize,
string? SourceNodeFilter = null);
/// <summary>
/// A single notification row summarised for outbox UI display.
/// </summary>
public record NotificationSummary(
string NotificationId,
string Type,
string ListName,
string Subject,
string Status,
int RetryCount,
string? LastError,
string SourceSiteId,
string? SourceInstanceId,
DateTimeOffset CreatedAt,
DateTimeOffset? DeliveredAt,
bool IsStuck,
string? SourceNode = null);
/// <summary>
/// Central -> Outbox UI: paginated response for a <see cref="NotificationOutboxQueryRequest"/>.
/// </summary>
public record NotificationOutboxQueryResponse(
string CorrelationId,
bool Success,
string? ErrorMessage,
IReadOnlyList<NotificationSummary> Notifications,
int TotalCount);
/// <summary>
/// Outbox UI -> Central: request to immediately retry delivery of a notification.
/// </summary>
public record RetryNotificationRequest(
string CorrelationId,
string NotificationId);
/// <summary>
/// Central -> Outbox UI: result of a <see cref="RetryNotificationRequest"/>.
/// </summary>
public record RetryNotificationResponse(
string CorrelationId,
bool Success,
string? ErrorMessage);
/// <summary>
/// Outbox UI -> Central: request to discard (cancel) a pending or stuck notification.
/// </summary>
public record DiscardNotificationRequest(
string CorrelationId,
string NotificationId);
/// <summary>
/// Central -> Outbox UI: result of a <see cref="DiscardNotificationRequest"/>.
/// </summary>
public record DiscardNotificationResponse(
string CorrelationId,
bool Success,
string? ErrorMessage);
/// <summary>
/// Outbox UI -> Central: request for the full detail of a single notification
/// (including Body and resolved recipients), for the report detail modal.
/// </summary>
public record NotificationDetailRequest(
string CorrelationId,
string NotificationId);
/// <summary>
/// Central -> Outbox UI: full detail for one notification. On a repository fault or
/// missing row, Success is false / Detail is null and ErrorMessage carries the cause.
/// </summary>
public record NotificationDetailResponse(
string CorrelationId,
bool Success,
string? ErrorMessage,
NotificationDetail? Detail);
/// <summary>
/// Full notification detail for the report detail modal — everything in the grid's
/// NotificationSummary plus Body, ResolvedTargets (recipients), TypeData, SourceScript,
/// and the additional lifecycle timestamps.
/// </summary>
public record NotificationDetail(
string NotificationId,
string Type,
string ListName,
string Subject,
string Body,
string Status,
int RetryCount,
string? LastError,
string? ResolvedTargets,
string? TypeData,
string SourceSiteId,
string? SourceInstanceId,
string? SourceScript,
DateTimeOffset SiteEnqueuedAt,
DateTimeOffset CreatedAt,
DateTimeOffset? LastAttemptAt,
DateTimeOffset? NextAttemptAt,
DateTimeOffset? DeliveredAt,
string? SourceNode = null);
/// <summary>
/// Outbox UI -> Central: request for the notification outbox KPI summary.
/// </summary>
public record NotificationKpiRequest(
string CorrelationId);
/// <summary>
/// Central -> Outbox UI: KPI summary for the notification outbox dashboard.
/// On a repository fault <see cref="Success"/> is <c>false</c>, <see cref="ErrorMessage"/>
/// carries the cause, and the KPI fields are zeroed/<c>null</c>.
/// </summary>
public record NotificationKpiResponse(
string CorrelationId,
bool Success,
string? ErrorMessage,
int QueueDepth,
int StuckCount,
int ParkedCount,
int DeliveredLastInterval,
TimeSpan? OldestPendingAge);
/// <summary>
/// Outbox UI -> Central: request for the per-source-site notification outbox KPI breakdown.
/// </summary>
public record PerSiteNotificationKpiRequest(
string CorrelationId);
/// <summary>
/// Central -> Outbox UI: per-site KPI breakdown for the Notification KPIs page.
/// On a repository fault <see cref="Success"/> is <c>false</c>, <see cref="ErrorMessage"/>
/// carries the cause, and <see cref="Sites"/> is empty.
/// </summary>
public record PerSiteNotificationKpiResponse(
string CorrelationId,
bool Success,
string? ErrorMessage,
IReadOnlyList<SiteNotificationKpiSnapshot> Sites);