2026-09-18 12:40:33 -03:00
|
|
|
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";
|
2026-09-18 13:15:33 -03:00
|
|
|
/// <summary>The signed-in user's own open work orders.</summary>
|
|
|
|
|
public const string AssignedToMe = "assigned-to-me";
|
2026-09-18 12:40:33 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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";
|
2026-09-18 13:15:33 -03:00
|
|
|
public const string Uplifts = "uplifts";
|
2026-09-18 12:40:33 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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>();
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-25 11:16:21 -03:00
|
|
|
/// <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; }
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-18 12:40:33 -03:00
|
|
|
public sealed class NotificationFeedDto
|
|
|
|
|
{
|
|
|
|
|
public DateTime GeneratedAt { get; init; }
|
|
|
|
|
public IReadOnlyList<NotificationSectionDto> Sections { get; init; } = Array.Empty<NotificationSectionDto>();
|
2026-09-25 11:16:21 -03:00
|
|
|
/// <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>();
|
2026-09-18 12:40:33 -03:00
|
|
|
}
|
|
|
|
|
}
|