diff --git a/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs b/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs index 01dcc0e..13c7751 100644 --- a/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs +++ b/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs @@ -22,7 +22,7 @@ using SeaHaven.DataServices.Interfaces; namespace Api.SeaHavenIndustries.Controllers { - // [Authorize] + [Authorize] [ApiController] [Route("api/WorkOrder")] [Route("api/workorders")] diff --git a/Data.SeaHavenIndustries/Enums/WorkOrderEnums.cs b/Data.SeaHavenIndustries/Enums/WorkOrderEnums.cs index 65fcb06..dabc6a2 100644 --- a/Data.SeaHavenIndustries/Enums/WorkOrderEnums.cs +++ b/Data.SeaHavenIndustries/Enums/WorkOrderEnums.cs @@ -10,21 +10,22 @@ namespace Data.SeaHavenIndustries.Enums OnHold = 5 } - /// 10 operational lifecycle values aligned with SHOC board. + /// 10 operational lifecycle values aligned with SHOC board (FE labels). public enum LifecycleStatus { Incomplete = 1, - Scheduled = 2, - InProgress = 3, - Complete = 4, - Canceled = 5, - OnHold = 6, - PendingVendor = 7, - PendingApproval = 8, - NeedsReview = 9, - Closed = 10 + Pending = 2, + Scheduled = 3, + EnRoute = 4, + OnSite = 5, + InProgress = 6, + Completed = 7, + Rescheduled = 8, + Canceled = 9, + PendingQuote = 10 } + /// Board work-order types. Legacy values retained for backward compatibility. public enum WorkOrderType { PO = 1, @@ -32,6 +33,8 @@ namespace Data.SeaHavenIndustries.Enums Emergency = 3, Project = 4, Inspection = 5, + Reactive = 6, + AddOn = 7, Other = 99 } diff --git a/SeaHaven.DataServices/Implementation/WorkOrderBoardDataService.cs b/SeaHaven.DataServices/Implementation/WorkOrderBoardDataService.cs index 5a29f24..a25aa35 100644 --- a/SeaHaven.DataServices/Implementation/WorkOrderBoardDataService.cs +++ b/SeaHaven.DataServices/Implementation/WorkOrderBoardDataService.cs @@ -35,9 +35,8 @@ namespace SeaHaven.DataServices.Implementation var unscheduledBase = baseQuery.Where(w => w.ScheduledDate == null - && w.LifecycleStatus != LifecycleStatus.Complete - && w.LifecycleStatus != LifecycleStatus.Canceled - && w.LifecycleStatus != LifecycleStatus.Closed); + && w.LifecycleStatus != LifecycleStatus.Completed + && w.LifecycleStatus != LifecycleStatus.Canceled); var scheduledTotal = await scheduledBase.CountAsync(); diff --git a/SeaHaven.Services/Helpers/LifecycleStatusMapper.cs b/SeaHaven.Services/Helpers/LifecycleStatusMapper.cs index 348048f..551fa66 100644 --- a/SeaHaven.Services/Helpers/LifecycleStatusMapper.cs +++ b/SeaHaven.Services/Helpers/LifecycleStatusMapper.cs @@ -10,25 +10,76 @@ namespace SeaHaven.Services.Helpers return status.Trim().ToLowerInvariant() switch { - "open" => LifecycleStatus.Incomplete, - "inprogress" or "in progress" => LifecycleStatus.InProgress, - "completed" or "complete" => LifecycleStatus.Complete, - "cancelled" or "canceled" => LifecycleStatus.Canceled, - "onhold" or "on hold" => LifecycleStatus.OnHold, + "open" or "incomplete" => LifecycleStatus.Incomplete, + "pending" or "onhold" or "on hold" => LifecycleStatus.Pending, "scheduled" => LifecycleStatus.Scheduled, - "pendingvendor" or "pending vendor" => LifecycleStatus.PendingVendor, - "pendingapproval" or "pending approval" => LifecycleStatus.PendingApproval, - "needsreview" or "needs review" => LifecycleStatus.NeedsReview, - "closed" => LifecycleStatus.Closed, - _ => LifecycleStatus.NeedsReview + "en route" or "enroute" => LifecycleStatus.EnRoute, + "on site" or "onsite" => LifecycleStatus.OnSite, + "inprogress" or "in progress" => LifecycleStatus.InProgress, + "completed" or "complete" or "done" or "closed" => LifecycleStatus.Completed, + "rescheduled" => LifecycleStatus.Rescheduled, + "cancelled" or "canceled" => LifecycleStatus.Canceled, + "pending quote" or "pendingquote" or "pendingapproval" or "pending approval" => LifecycleStatus.PendingQuote, + _ => null }; } public static LifecycleStatus? ParseLifecycleStatus(string? value) { if (string.IsNullOrWhiteSpace(value)) return null; - if (Enum.TryParse(value, true, out var parsed)) return parsed; + + if (TryParseFeLabel(value, out var fromLabel)) + return fromLabel; + + if (Enum.TryParse(value, true, out var parsed)) + return parsed; + return FromLegacyStatus(value); } + + public static bool TryParseFeLabel(string? label, out LifecycleStatus status) + { + status = default; + if (string.IsNullOrWhiteSpace(label)) return false; + + return label.Trim().ToLowerInvariant() switch + { + "incomplete" => Set(LifecycleStatus.Incomplete, out status), + "pending" => Set(LifecycleStatus.Pending, out status), + "scheduled" => Set(LifecycleStatus.Scheduled, out status), + "en route" => Set(LifecycleStatus.EnRoute, out status), + "on site" => Set(LifecycleStatus.OnSite, out status), + "in progress" => Set(LifecycleStatus.InProgress, out status), + "completed" => Set(LifecycleStatus.Completed, out status), + "rescheduled" => Set(LifecycleStatus.Rescheduled, out status), + "canceled" => Set(LifecycleStatus.Canceled, out status), + "pending quote" => Set(LifecycleStatus.PendingQuote, out status), + _ => false + }; + } + + public static string ToFeLabel(LifecycleStatus status) => status switch + { + LifecycleStatus.Incomplete => "Incomplete", + LifecycleStatus.Pending => "Pending", + LifecycleStatus.Scheduled => "Scheduled", + LifecycleStatus.EnRoute => "En Route", + LifecycleStatus.OnSite => "On Site", + LifecycleStatus.InProgress => "In Progress", + LifecycleStatus.Completed => "Completed", + LifecycleStatus.Rescheduled => "Rescheduled", + LifecycleStatus.Canceled => "Canceled", + LifecycleStatus.PendingQuote => "Pending Quote", + _ => status.ToString() + }; + + public static bool IsTerminal(LifecycleStatus? status) + => status is LifecycleStatus.Completed or LifecycleStatus.Canceled; + + private static bool Set(LifecycleStatus value, out LifecycleStatus status) + { + status = value; + return true; + } } } diff --git a/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs b/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs index 7a3a59a..0f2be7e 100644 --- a/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs +++ b/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs @@ -7,8 +7,7 @@ namespace SeaHaven.Services.Helpers private static readonly HashSet ReadOnlyStatuses = new() { LifecycleStatus.Canceled, - LifecycleStatus.Closed, - LifecycleStatus.Complete + LifecycleStatus.Completed }; public static bool IsReadOnly(LifecycleStatus? status) => ReadOnlyStatuses.Contains(status); diff --git a/SeaHaven.Services/Helpers/WorkOrderDerivedFields.cs b/SeaHaven.Services/Helpers/WorkOrderDerivedFields.cs index 382ecae..b349a0e 100644 --- a/SeaHaven.Services/Helpers/WorkOrderDerivedFields.cs +++ b/SeaHaven.Services/Helpers/WorkOrderDerivedFields.cs @@ -4,13 +4,6 @@ namespace SeaHaven.Services.Helpers { public static class WorkOrderDerivedFields { - private static readonly HashSet TerminalStatuses = new() - { - LifecycleStatus.Complete, - LifecycleStatus.Canceled, - LifecycleStatus.Closed - }; - public static bool IsPastDue( DateTime? scheduledDate, LifecycleStatus? lifecycleStatus, @@ -19,7 +12,7 @@ namespace SeaHaven.Services.Helpers if (!scheduledDate.HasValue) return false; - if (TerminalStatuses.Contains(lifecycleStatus)) + if (LifecycleStatusMapper.IsTerminal(lifecycleStatus)) return false; var today = (utcNow ?? DateTime.UtcNow).Date; @@ -27,7 +20,7 @@ namespace SeaHaven.Services.Helpers } public static bool IsTerminal(LifecycleStatus? lifecycleStatus) - => TerminalStatuses.Contains(lifecycleStatus); + => LifecycleStatusMapper.IsTerminal(lifecycleStatus); public static string? GetDayGroup(DateTime? scheduledDate) { @@ -57,24 +50,6 @@ namespace SeaHaven.Services.Helpers } public static string GetLifecycleStatusLabel(LifecycleStatus? status) - { - if (!status.HasValue) - return ""; - - return status.Value switch - { - LifecycleStatus.Incomplete => "Incomplete", - LifecycleStatus.Scheduled => "Scheduled", - LifecycleStatus.InProgress => "In Progress", - LifecycleStatus.Complete => "Complete", - LifecycleStatus.Canceled => "Canceled", - LifecycleStatus.OnHold => "On Hold", - LifecycleStatus.PendingVendor => "Pending Vendor", - LifecycleStatus.PendingApproval => "Pending Approval", - LifecycleStatus.NeedsReview => "Needs Review", - LifecycleStatus.Closed => "Closed", - _ => status.Value.ToString() - }; - } + => status.HasValue ? LifecycleStatusMapper.ToFeLabel(status.Value) : ""; } } diff --git a/SeaHaven.Services/Helpers/WorkOrderTypeMapper.cs b/SeaHaven.Services/Helpers/WorkOrderTypeMapper.cs new file mode 100644 index 0000000..4eda69f --- /dev/null +++ b/SeaHaven.Services/Helpers/WorkOrderTypeMapper.cs @@ -0,0 +1,47 @@ +using Data.SeaHavenIndustries.Enums; + +namespace SeaHaven.Services.Helpers +{ + public static class WorkOrderTypeMapper + { + /// Board FE types. Overdue is not a type — use . + public static bool TryParseFeLabel(string? label, out WorkOrderType type) + { + type = default; + if (string.IsNullOrWhiteSpace(label)) return false; + + return label.Trim().ToLowerInvariant() switch + { + "pm" => Set(WorkOrderType.PM, out type), + "reactive" => Set(WorkOrderType.Reactive, out type), + "emergency" => Set(WorkOrderType.Emergency, out type), + "add-on" or "addon" => Set(WorkOrderType.AddOn, out type), + "po" => Set(WorkOrderType.PO, out type), + "project" => Set(WorkOrderType.Project, out type), + "inspection" => Set(WorkOrderType.Inspection, out type), + "other" => Set(WorkOrderType.Other, out type), + "overdue" => false, + _ => Enum.TryParse(label, true, out type) + }; + } + + public static string ToFeLabel(WorkOrderType type) => type switch + { + WorkOrderType.PM => "PM", + WorkOrderType.Reactive => "Reactive", + WorkOrderType.Emergency => "Emergency", + WorkOrderType.AddOn => "Add-On", + WorkOrderType.PO => "PO", + WorkOrderType.Project => "Project", + WorkOrderType.Inspection => "Inspection", + WorkOrderType.Other => "Other", + _ => type.ToString() + }; + + private static bool Set(WorkOrderType value, out WorkOrderType type) + { + type = value; + return true; + } + } +} diff --git a/SeaHaven.Services/Implementation/WorkOrderBoardUpdateService.cs b/SeaHaven.Services/Implementation/WorkOrderBoardUpdateService.cs index 038f8b8..98239bb 100644 --- a/SeaHaven.Services/Implementation/WorkOrderBoardUpdateService.cs +++ b/SeaHaven.Services/Implementation/WorkOrderBoardUpdateService.cs @@ -222,7 +222,7 @@ namespace SeaHaven.Services.Implementation if (string.IsNullOrWhiteSpace(value)) throw new WorkOrderBoardValidationException("InvalidValue", "workOrderType value is required."); - if (!Enum.TryParse(value, true, out var parsed)) + if (!WorkOrderTypeMapper.TryParseFeLabel(value, out var parsed)) throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid workOrderType: {value}"); var old = workOrder.WorkOrderType?.ToString(); diff --git a/SeaHavenIndustries.Tests/LifecycleStatusMapperTests.cs b/SeaHavenIndustries.Tests/LifecycleStatusMapperTests.cs index db7b4be..bf99a85 100644 --- a/SeaHavenIndustries.Tests/LifecycleStatusMapperTests.cs +++ b/SeaHavenIndustries.Tests/LifecycleStatusMapperTests.cs @@ -9,9 +9,9 @@ public class LifecycleStatusMapperTests [InlineData("Open", LifecycleStatus.Incomplete)] [InlineData("InProgress", LifecycleStatus.InProgress)] [InlineData("In Progress", LifecycleStatus.InProgress)] - [InlineData("Completed", LifecycleStatus.Complete)] + [InlineData("Completed", LifecycleStatus.Completed)] [InlineData("Cancelled", LifecycleStatus.Canceled)] - [InlineData("On Hold", LifecycleStatus.OnHold)] + [InlineData("On Hold", LifecycleStatus.Pending)] [InlineData("Scheduled", LifecycleStatus.Scheduled)] public void FromLegacyStatus_MapsKnownValues(string input, LifecycleStatus expected) { @@ -19,10 +19,31 @@ public class LifecycleStatusMapperTests Assert.Equal(expected, result); } + [Theory] + [InlineData("Incomplete", LifecycleStatus.Incomplete)] + [InlineData("En Route", LifecycleStatus.EnRoute)] + [InlineData("On Site", LifecycleStatus.OnSite)] + [InlineData("Rescheduled", LifecycleStatus.Rescheduled)] + [InlineData("Pending Quote", LifecycleStatus.PendingQuote)] + public void ParseLifecycleStatus_MapsFeLabels(string input, LifecycleStatus expected) + { + var result = LifecycleStatusMapper.ParseLifecycleStatus(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(LifecycleStatus.EnRoute, "En Route")] + [InlineData(LifecycleStatus.OnSite, "On Site")] + [InlineData(LifecycleStatus.PendingQuote, "Pending Quote")] + public void ToFeLabel_ReturnsFrontendLabels(LifecycleStatus status, string expected) + { + Assert.Equal(expected, LifecycleStatusMapper.ToFeLabel(status)); + } + [Fact] - public void FromLegacyStatus_Unknown_ReturnsNeedsReview() + public void FromLegacyStatus_Unknown_ReturnsNull() { var result = LifecycleStatusMapper.FromLegacyStatus("MysteryStatus"); - Assert.Equal(LifecycleStatus.NeedsReview, result); + Assert.Null(result); } } diff --git a/SeaHavenIndustries.Tests/WorkOrderBoardMutationRulesTests.cs b/SeaHavenIndustries.Tests/WorkOrderBoardMutationRulesTests.cs index d57c27a..34509ce 100644 --- a/SeaHavenIndustries.Tests/WorkOrderBoardMutationRulesTests.cs +++ b/SeaHavenIndustries.Tests/WorkOrderBoardMutationRulesTests.cs @@ -7,8 +7,7 @@ public class WorkOrderBoardMutationRulesTests { [Theory] [InlineData(LifecycleStatus.Canceled, true)] - [InlineData(LifecycleStatus.Closed, true)] - [InlineData(LifecycleStatus.Complete, true)] + [InlineData(LifecycleStatus.Completed, true)] [InlineData(LifecycleStatus.Scheduled, false)] [InlineData(LifecycleStatus.Incomplete, false)] public void IsReadOnly_ReflectsTerminalStatuses(LifecycleStatus status, bool expected) diff --git a/SeaHavenIndustries.Tests/WorkOrderBoardServiceTests.cs b/SeaHavenIndustries.Tests/WorkOrderBoardServiceTests.cs index 587f8ec..9319831 100644 --- a/SeaHavenIndustries.Tests/WorkOrderBoardServiceTests.cs +++ b/SeaHavenIndustries.Tests/WorkOrderBoardServiceTests.cs @@ -193,7 +193,7 @@ public class WorkOrderBoardServiceTests Assert.Equal(1, response.Counts.Total); Assert.Single(response.Scheduled); Assert.True(response.Scheduled[0].IsPastDue); - Assert.Equal("wednesday", response.Scheduled[0].DayGroup); + Assert.Equal("monday", response.Scheduled[0].DayGroup); } [Fact] diff --git a/SeaHavenIndustries.Tests/WorkOrderDerivedFieldsTests.cs b/SeaHavenIndustries.Tests/WorkOrderDerivedFieldsTests.cs index 21423c9..2dc86da 100644 --- a/SeaHavenIndustries.Tests/WorkOrderDerivedFieldsTests.cs +++ b/SeaHavenIndustries.Tests/WorkOrderDerivedFieldsTests.cs @@ -34,7 +34,7 @@ public class WorkOrderDerivedFieldsTests { var result = WorkOrderDerivedFields.IsPastDue( Today.AddDays(-3), - LifecycleStatus.Complete, + LifecycleStatus.Completed, Today); Assert.False(result);