using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Interfaces; namespace SeaHaven.DataServices.Implementation { public class WorkOrderBoardDataService : IWorkOrderBoardDataService { private readonly ApplicationDbContext _context; public WorkOrderBoardDataService(ApplicationDbContext context) { _context = context; } public async Task GetBoardRowsAsync(WorkOrderBoardQuery query) { var weekStartDate = query.WeekStart.ToDateTime(TimeOnly.MinValue); var weekEndDate = query.WeekEnd.ToDateTime(TimeOnly.MinValue); var baseQuery = _context.workOrders .AsNoTracking() .Where(w => w.istemplate != true && (w.IsDeleted != true || w.IsDeleted == null)); baseQuery = ApplyDispatcherFilter(baseQuery, query); baseQuery = ApplyTypeFilter(baseQuery, query); var scheduledBase = baseQuery.Where(w => (w.ScheduledDate != null && w.ScheduledDate.Value.Date >= weekStartDate.Date && w.ScheduledDate.Value.Date <= weekEndDate.Date) || (w.ScheduleWeekOnly == true && w.TargetWeek == query.WeekStart)); var unscheduledBase = baseQuery.Where(w => w.ScheduledDate == null && w.LifecycleStatus != LifecycleStatus.Completed && w.LifecycleStatus != LifecycleStatus.Canceled); var scheduledTotal = await scheduledBase.CountAsync(); if (!string.IsNullOrWhiteSpace(query.Search)) { scheduledBase = ApplySearchFilter(scheduledBase, query.Search); unscheduledBase = ApplySearchFilter(unscheduledBase, query.Search); } var scheduledRows = await ProjectRows(scheduledBase, isUnscheduled: false); var unscheduledRows = await ProjectRows(unscheduledBase, isUnscheduled: true); return new WorkOrderBoardQueryResult(scheduledRows, unscheduledRows, scheduledTotal); } public async Task> GetDispatcherLookupsAsync() { return await _context.Users .AsNoTracking() .Where(u => u.IsDeleted != true) .OrderBy(u => u.FirstName) .ThenBy(u => u.LastName) .Select(u => new DispatcherLookupRow( u.Id, u.FirstName ?? "", u.LastName, u.Initials, u.Color)) .ToListAsync(); } private static IQueryable ApplyDispatcherFilter(IQueryable query, WorkOrderBoardQuery boardQuery) { if (boardQuery.MyWorkOrders && !string.IsNullOrEmpty(boardQuery.CurrentUserId)) return query.Where(w => w.AssignTo == boardQuery.CurrentUserId); if (boardQuery.Dispatchers == null || boardQuery.Dispatchers.Count == 0) return query; var ids = boardQuery.Dispatchers .Where(d => d != "__unassigned__") .ToList(); var includeUnassigned = boardQuery.Dispatchers.Contains("__unassigned__"); if (includeUnassigned && ids.Count == 0) return query.Where(w => w.AssignTo == null || w.AssignTo == ""); if (includeUnassigned && ids.Count > 0) { return query.Where(w => w.AssignTo == null || w.AssignTo == "" || ids.Contains(w.AssignTo)); } return query.Where(w => w.AssignTo != null && ids.Contains(w.AssignTo)); } private static IQueryable ApplyTypeFilter(IQueryable query, WorkOrderBoardQuery boardQuery) { if (boardQuery.Types == null || boardQuery.Types.Count == 0) return query; return query.Where(w => w.WorkOrderType != null && boardQuery.Types.Contains(w.WorkOrderType.Value)); } private static IQueryable ApplySearchFilter(IQueryable query, string search) { var s = search.Trim().ToLower(); return query.Where(w => (w.SiteCode ?? "").ToLower().Contains(s) || (w.InternalWONumber ?? "").ToLower().Contains(s) || (w.WorkerOrderNumber ?? "").ToLower().Contains(s) || (w.Locations != null && (w.Locations.Name ?? "").ToLower().Contains(s)) || (w.AssignToUser != null && ((w.AssignToUser.FirstName ?? "") + " " + (w.AssignToUser.LastName ?? "")).ToLower().Contains(s)) || (w.Trade ?? "").ToLower().Contains(s) || (w.Problem ?? "").ToLower().Contains(s) || (w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null && (w.PrimaryDispatch.Vendor.CompanyName ?? "").ToLower().Contains(s)) || (w.Status ?? "").ToLower().Contains(s)); } private async Task> ProjectRows( IQueryable query, bool isUnscheduled) { var rows = await query .Include(w => w.AssignToUser) .Include(w => w.Locations) .Include(w => w.PrimaryDispatch!) .ThenInclude(d => d!.Vendor) .Include(w => w.WorkOrderContacts!) .ThenInclude(c => c.POC) .OrderBy(w => w.ScheduledDate) .ThenBy(w => w.InternalWONumber) .Select(w => new { w.Id, w.InternalWONumber, w.RescheduleCount, w.CarriedOver, w.WorkOrderType, w.SiteCode, LocationName = w.Locations != null ? w.Locations.Name : null, Poc = w.WorkOrderContacts! .OrderBy(c => c.Id) .Select(c => new { Name = c.POC != null ? ((c.POC.FirstName ?? "") + " " + (c.POC.LastName ?? "")).Trim() : null, c.POC!.PhoneNumber, c.Notes }) .FirstOrDefault(), w.LifecycleStatus, w.LegacyStatus, w.Status, w.AssignTo, w.AssignToUser!.FirstName, w.AssignToUser!.LastName, w.AssignToUser!.Initials, w.AssignToUser!.Color, w.DueDate, w.ScheduledDate, w.ScheduledStart, w.ScheduledEnd, w.TargetWeek, w.ScheduleWeekOnly, VendorId = w.PrimaryDispatch != null ? (int?)w.PrimaryDispatch.VendorId : null, VendorName = w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null ? w.PrimaryDispatch.Vendor.CompanyName : null, TechName = w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null ? w.PrimaryDispatch.Vendor.ContactName : null, TechPhone = w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null ? w.PrimaryDispatch.Vendor.Phone : null, DispatchApptDate = w.PrimaryDispatch != null ? w.PrimaryDispatch.ScheduledDate : null, w.Trade, w.Problem, w.DocStatus, w.RowVersion }) .ToListAsync(); return rows.Select(w => new WorkOrderBoardRawRow( w.Id, w.InternalWONumber, w.RescheduleCount, w.CarriedOver, w.WorkOrderType, w.SiteCode, w.LocationName, w.Poc?.Name, w.Poc?.PhoneNumber, w.Poc?.Notes, w.LifecycleStatus, w.LegacyStatus ?? w.Status, w.AssignTo, w.FirstName, w.LastName, w.Initials, w.Color, w.DueDate, w.ScheduledDate, w.ScheduledStart, w.ScheduledEnd, w.TargetWeek, w.ScheduleWeekOnly, w.VendorId, w.VendorName, w.TechName, w.TechPhone, w.DispatchApptDate, w.Trade ?? w.Problem, w.Problem, w.DocStatus, w.RowVersion, isUnscheduled)).ToList(); } } }