namespace SeaHaven.Services.Helpers { public enum WorkOrderAdvancedSearchDatePreset { ThisWeek, LastWeek, ThisMonth, Last3Months, NextWeek, NextMonth, Custom } public record WorkOrderSearchDateRange(DateOnly From, DateOnly To); public static class WorkOrderSearchDateRangeResolver { // Widest windows the frontend sends for "every week": the no-range search // (ADVANCED_SEARCH_ALL_WEEKS_FROM/TO, 2000-01-01..2099-12-31) and the pinned // Unassigned queue (UNASSIGNED_QUEUE_DATE_FROM/TO, 1970-01-01..2099-12-31). private static readonly DateOnly AllWeeksFrom = new(2000, 1, 1); private static readonly DateOnly AllWeeksTo = new(2099, 12, 31); /// /// Whether a board search adds open undated rows to its date range (SH-391). /// An explicit wins. When it is omitted, /// as by clients that predate the flag, a search with no date input or with /// an all-weeks Custom window still returns undated rows, as before SH-391: /// those requests mean "no range selected". Any other range is strict. /// public static bool ResolveIncludeDateless( bool? includeDateless, WorkOrderAdvancedSearchDatePreset? preset, DateOnly? dateFrom, DateOnly? dateTo) { if (includeDateless.HasValue) return includeDateless.Value; if (!preset.HasValue && !dateFrom.HasValue && !dateTo.HasValue) return true; return preset == WorkOrderAdvancedSearchDatePreset.Custom && dateFrom <= AllWeeksFrom && dateTo >= AllWeeksTo; } public static WorkOrderSearchDateRange Resolve( WorkOrderAdvancedSearchDatePreset? preset, DateOnly? dateFrom, DateOnly? dateTo, DateOnly? referenceDate = null) { var today = referenceDate ?? DateOnly.FromDateTime(DateTime.UtcNow); var resolvedPreset = preset ?? WorkOrderAdvancedSearchDatePreset.ThisWeek; if (resolvedPreset == WorkOrderAdvancedSearchDatePreset.Custom) { if (!dateFrom.HasValue || !dateTo.HasValue) throw new ArgumentException("dateFrom and dateTo are required when datePreset is custom."); if (dateTo.Value < dateFrom.Value) throw new ArgumentException("dateTo must be on or after dateFrom."); return new WorkOrderSearchDateRange(dateFrom.Value, dateTo.Value); } return resolvedPreset switch { WorkOrderAdvancedSearchDatePreset.ThisWeek => ThisIsoWeek(today), WorkOrderAdvancedSearchDatePreset.LastWeek => LastIsoWeek(today), WorkOrderAdvancedSearchDatePreset.ThisMonth => ThisMonth(today), WorkOrderAdvancedSearchDatePreset.Last3Months => Last3Months(today), WorkOrderAdvancedSearchDatePreset.NextWeek => NextIsoWeek(today), WorkOrderAdvancedSearchDatePreset.NextMonth => NextMonth(today), _ => ThisIsoWeek(today) }; } public static DateOnly GetIsoWeekStart(DateOnly date) { var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7; return date.AddDays(-diff); } private static WorkOrderSearchDateRange ThisIsoWeek(DateOnly today) { var start = GetIsoWeekStart(today); return new WorkOrderSearchDateRange(start, start.AddDays(4)); } private static WorkOrderSearchDateRange LastIsoWeek(DateOnly today) { var start = GetIsoWeekStart(today).AddDays(-7); return new WorkOrderSearchDateRange(start, start.AddDays(4)); } private static WorkOrderSearchDateRange NextIsoWeek(DateOnly today) { var start = GetIsoWeekStart(today).AddDays(7); return new WorkOrderSearchDateRange(start, start.AddDays(4)); } private static WorkOrderSearchDateRange ThisMonth(DateOnly today) { var start = new DateOnly(today.Year, today.Month, 1); var end = start.AddMonths(1).AddDays(-1); return new WorkOrderSearchDateRange(start, end); } private static WorkOrderSearchDateRange NextMonth(DateOnly today) { var start = new DateOnly(today.Year, today.Month, 1).AddMonths(1); var end = start.AddMonths(1).AddDays(-1); return new WorkOrderSearchDateRange(start, end); } private static WorkOrderSearchDateRange Last3Months(DateOnly today) { var end = today; var start = today.AddMonths(-3); return new WorkOrderSearchDateRange(start, end); } } }