namespace SeaHaven.Services.DTOs
{
///
/// 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.
///
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 Order = new[]
{
SeverityCritical, Sla, Mentions, Unassigned, DueSoon, NoVendor, AvetaMissing, Activity, VendorConflict
};
public static readonly IReadOnlyDictionary Labels = new Dictionary
{
[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
};
}
///
/// How a row leaves the feed. rows are hidden client-side for the
/// session; rows (a missed response deadline) are excluded from
/// dismiss and dismiss-all and leave only through their own acknowledge action.
///
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";
}
/// Named work-order queues a grouped item opens.
public static class NotificationQueues
{
public const string Unassigned = "unassigned";
/// The signed-in user's own open work orders.
public const string AssignedToMe = "assigned-to-me";
}
/// Work-order detail tabs a single-work-order item can open on.
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
{
/// Stable across reads while the condition holds; the client dismisses by it.
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; } = "";
/// Work orders this item represents.
public int Count { get; init; }
/// When the condition started; drives recency ordering.
public DateTime TriggeredAt { get; init; }
public NotificationTargetDto Target { get; init; } = new();
public IReadOnlyList WorkOrders { get; init; } = Array.Empty();
}
public sealed class NotificationSectionDto
{
public string Reason { get; init; } = "";
public string Label { get; init; } = "";
/// Highest item severity in the section.
public string Severity { get; init; } = NotificationSeverities.Low;
/// Work orders the whole section represents, including items beyond the returned slice.
public int Count { get; init; }
public IReadOnlyList Items { get; init; } = Array.Empty();
}
///
/// 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.
///
public sealed class NotificationSlaWorkOrderDto
{
public int Id { get; init; }
public string? Number { get; init; }
public int Severity { get; init; }
/// The work order's creation time; the response clock never starts at the scheduled date.
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 Sections { get; init; } = Array.Empty();
/// Every at-risk work order in the feed's scope, oldest first; not capped like section items.
public IReadOnlyList SlaAtRisk { get; init; } = Array.Empty();
}
}