shoc-backend/SeaHaven.Services/DTOs/NotificationFeedDTOs.cs

152 lines
6.1 KiB
C#
Raw Normal View History

namespace SeaHaven.Services.DTOs
{
/// <summary>
/// Feed reasons in their fixed tie-break order. Every reason the Notification Center
/// can show is listed here so a new producer only adds items, never a contract change.
/// </summary>
public static class NotificationReasons
{
public const string SeverityCritical = "severity-critical";
public const string Sla = "sla";
public const string Mentions = "mentions";
public const string Unassigned = "unassigned";
public const string DueSoon = "due-soon";
public const string NoVendor = "no-vendor";
public const string AvetaMissing = "aveta-missing";
public const string Activity = "activity";
public const string VendorConflict = "vendor-conflict";
public static readonly IReadOnlyList<string> Order = new[]
{
SeverityCritical, Sla, Mentions, Unassigned, DueSoon, NoVendor, AvetaMissing, Activity, VendorConflict
};
public static readonly IReadOnlyDictionary<string, string> Labels = new Dictionary<string, string>
{
[SeverityCritical] = "Critical severity",
[Sla] = "SLA at Risk",
[Mentions] = "Mentions",
[Unassigned] = "Unassigned",
[DueSoon] = "Due Soon",
[NoVendor] = "No Vendor",
[AvetaMissing] = "Aveta Missing",
[Activity] = "Recent activity",
[VendorConflict] = "Vendor Conflict"
};
}
public static class NotificationSeverities
{
public const string Critical = "Critical";
public const string High = "High";
public const string Medium = "Medium";
public const string Low = "Low";
public static int Rank(string severity) => severity switch
{
Critical => 3,
High => 2,
Medium => 1,
_ => 0
};
}
/// <summary>
/// How a row leaves the feed. <see cref="Dismissable"/> rows are hidden client-side for the
/// session; <see cref="Acknowledge"/> rows (a missed response deadline) are excluded from
/// dismiss and dismiss-all and leave only through their own acknowledge action.
/// </summary>
public static class NotificationRowTypes
{
public const string Dismissable = "dismissable";
public const string Acknowledge = "acknowledge";
}
public static class NotificationTargetKinds
{
public const string WorkOrder = "workOrder";
public const string Queue = "queue";
}
/// <summary>Named work-order queues a grouped item opens.</summary>
public static class NotificationQueues
{
public const string Unassigned = "unassigned";
/// <summary>The signed-in user's own open work orders.</summary>
public const string AssignedToMe = "assigned-to-me";
}
/// <summary>Work-order detail tabs a single-work-order item can open on.</summary>
public static class NotificationWorkOrderTabs
{
public const string Info = "info";
public const string Comments = "comments";
public const string Extras = "extras";
public const string Uplifts = "uplifts";
}
public sealed class NotificationTargetDto
{
public string Kind { get; init; } = NotificationTargetKinds.WorkOrder;
public int? WorkOrderId { get; init; }
public string? Tab { get; init; }
public string? Queue { get; init; }
}
public sealed class NotificationWorkOrderRefDto
{
public int Id { get; init; }
public string? Number { get; init; }
}
public sealed class NotificationItemDto
{
/// <summary>Stable across reads while the condition holds; the client dismisses by it.</summary>
public string Id { get; init; } = "";
public string Reason { get; init; } = "";
public string Severity { get; init; } = NotificationSeverities.Low;
public string RowType { get; init; } = NotificationRowTypes.Dismissable;
public string Title { get; init; } = "";
/// <summary>Work orders this item represents.</summary>
public int Count { get; init; }
/// <summary>When the condition started; drives recency ordering.</summary>
public DateTime TriggeredAt { get; init; }
public NotificationTargetDto Target { get; init; } = new();
public IReadOnlyList<NotificationWorkOrderRefDto> WorkOrders { get; init; } = Array.Empty<NotificationWorkOrderRefDto>();
}
public sealed class NotificationSectionDto
{
public string Reason { get; init; } = "";
public string Label { get; init; } = "";
/// <summary>Highest item severity in the section.</summary>
public string Severity { get; init; } = NotificationSeverities.Low;
/// <summary>Work orders the whole section represents, including items beyond the returned slice.</summary>
public int Count { get; init; }
public IReadOnlyList<NotificationItemDto> Items { get; init; } = Array.Empty<NotificationItemDto>();
}
/// <summary>
/// A work order past half of its SEV response window and not yet past the whole window, with the
/// server-computed clock. Drives the top-of-app banner and the one-time 50% toast.
/// </summary>
public sealed class NotificationSlaWorkOrderDto
{
public int Id { get; init; }
public string? Number { get; init; }
public int Severity { get; init; }
/// <summary>The work order's creation time; the response clock never starts at the scheduled date.</summary>
public DateTime StartedAt { get; init; }
public DateTime DeadlineAt { get; init; }
public int PercentElapsed { get; init; }
}
public sealed class NotificationFeedDto
{
public DateTime GeneratedAt { get; init; }
public IReadOnlyList<NotificationSectionDto> Sections { get; init; } = Array.Empty<NotificationSectionDto>();
/// <summary>Every at-risk work order in the feed's scope, oldest first; not capped like section items.</summary>
public IReadOnlyList<NotificationSlaWorkOrderDto> SlaAtRisk { get; init; } = Array.Empty<NotificationSlaWorkOrderDto>();
}
}