using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Helpers; using SeaHaven.DataServices.Interfaces; namespace SeaHaven.DataServices.Implementation { public class DashboardDataService : IDashboardDataService { private readonly ApplicationDbContext _context; public DashboardDataService(ApplicationDbContext context) { _context = context; } public async Task GetWorkOrderCountsAsync( int? accountId, CancellationToken cancellationToken) { var query = _context.workOrders.Where(w => w.istemplate != true); if (accountId is int scopedAccountId) { query = query.Where(w => w.AccountId == scopedAccountId); } var total = await query.CountAsync(cancellationToken); var open = await query.CountAsync(w => w.Status == "Open" || w.Status == "In Progress" || w.Status == "On Hold", cancellationToken); var notDispatched = await query.CountAsync(w => (w.Status == "Open") && (w.AssignTo == null || w.AssignTo == ""), cancellationToken); var completed = await query.CountAsync(w => w.Status == "Done", cancellationToken); return new DashboardCounts { Total = total, Open = open, NotDispatched = notDispatched, Completed = completed }; } public async Task> GetDashboardWorkOrdersAsync( int? accountId, string? dispatcherId, DateOnly? dateFrom, DateOnly? dateTo, CancellationToken cancellationToken) { var query = WorkOrderBoardQueryFilters.ApplyBaseScope(_context.workOrders.AsNoTracking()); if (accountId is int scopedAccountId) query = WorkOrderBoardQueryFilters.ApplyAccountScope(query, scopedAccountId); if (dispatcherId != null) query = query.Where(w => w.AssignTo == dispatcherId); if (dateFrom is DateOnly from && dateTo is DateOnly to) { var fromDate = from.ToDateTime(TimeOnly.MinValue); var toDateExclusive = to.AddDays(1).ToDateTime(TimeOnly.MinValue); query = query.Where(w => w.ScheduledDate >= fromDate && w.ScheduledDate < toDateExclusive); } return await query .Select(w => new DashboardWorkOrder( w.Id, w.AssignTo, w.WorkOrderType, w.LifecycleStatus, w.LegacyStatus ?? w.Status, w.ScheduledDate, w.OriginalDate, w.CompletedDate, w.Locations == null ? null : w.Locations.State, w.RescheduleCount, w.PrimaryDispatch == null || w.PrimaryDispatch.Vendor == null ? null : w.PrimaryDispatch.Vendor.CompanyId, w.PrimaryDispatch == null || w.PrimaryDispatch.Vendor == null || w.PrimaryDispatch.Vendor.Company == null ? null : w.PrimaryDispatch.Vendor.Company.Name, w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null && w.PrimaryDispatch.Vendor.IsActive, w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null && w.PrimaryDispatch.Vendor.Company != null && w.PrimaryDispatch.Vendor.Company.IsDeleted == true)) .ToListAsync(cancellationToken); } public async Task GetKpiCountsAsync( int? accountId, string? dispatcherId, DateOnly today, CancellationToken cancellationToken) { var tomorrow = today.AddDays(1); var dayAfterTomorrow = tomorrow.AddDays(1); var workOrders = WorkOrderBoardQueryFilters.ApplyBaseScope(_context.workOrders.AsNoTracking()); if (accountId is int scopedAccountId) workOrders = WorkOrderBoardQueryFilters.ApplyAccountScope(workOrders, scopedAccountId); if (dispatcherId != null) workOrders = workOrders.Where(w => w.AssignTo == dispatcherId); var scheduledTomorrow = await workOrders .Where(w => w.ScheduledDate >= tomorrow.ToDateTime(TimeOnly.MinValue) && w.ScheduledDate < dayAfterTomorrow.ToDateTime(TimeOnly.MinValue)) .CountAsync(cancellationToken); var avetaPending = await workOrders .Where(w => w.ScheduledDate >= today.ToDateTime(TimeOnly.MinValue) && w.ScheduledDate < dayAfterTomorrow.ToDateTime(TimeOnly.MinValue) && w.AvetaRequired && w.LifecycleStatus != LifecycleStatus.Completed && w.LifecycleStatus != LifecycleStatus.Canceled && (w.LifecycleStatus != null || ((w.LegacyStatus ?? w.Status) != "Done" && (w.LegacyStatus ?? w.Status) != "Completed" && (w.LegacyStatus ?? w.Status) != "Complete" && (w.LegacyStatus ?? w.Status) != "Closed" && (w.LegacyStatus ?? w.Status) != "Canceled" && (w.LegacyStatus ?? w.Status) != "Cancelled")) && !w.workOrderAttachments!.Any(attachment => (attachment.IsDeleted == null || attachment.IsDeleted == false) && attachment.Category == WorkOrderMediaCategory.Aveta)) .CountAsync(cancellationToken); var pendingUplifts = _context.DispatchUpliftRequests .AsNoTracking() .Where(request => request.Status == "Pending" && (request.IsDeleted == null || request.IsDeleted == false) && request.Dispatch != null && (request.Dispatch.IsDeleted == null || request.Dispatch.IsDeleted == false)); if (accountId is int account) { pendingUplifts = pendingUplifts.Where(request => (request.Dispatch!.WorkOrder != null && request.Dispatch.WorkOrder.AccountId == account) || request.Dispatch.DispatchWorkOrders!.Any(link => link.WorkOrder != null && link.WorkOrder.AccountId == account)); } if (dispatcherId != null) { pendingUplifts = pendingUplifts.Where(request => (request.Dispatch!.WorkOrder != null && request.Dispatch.WorkOrder.AssignTo == dispatcherId) || request.Dispatch.DispatchWorkOrders!.Any(link => link.WorkOrder != null && link.WorkOrder.AssignTo == dispatcherId)); } return new DashboardKpiCounts( scheduledTomorrow, await pendingUplifts.CountAsync(cancellationToken), avetaPending); } public async Task> GetDispatcherWorkloadAsync( int? accountId, string? dispatcherId, DateOnly? dateFrom, DateOnly? dateTo, CancellationToken cancellationToken) { var dispatcherUsers = from user in _context.Users.AsNoTracking() join userRole in _context.UserRoles on user.Id equals userRole.UserId join role in _context.Roles on userRole.RoleId equals role.Id where user.IsDeleted != true && (role.Name == "Dispatcher" || role.Name == "DISPATCHER") select new { user.Id, Name = ((user.FirstName ?? "") + " " + (user.LastName ?? "")).Trim(), user.Color }; var workOrders = WorkOrderBoardQueryFilters.ApplyBaseScope(_context.workOrders.AsNoTracking()); if (accountId is int scopedAccountId) workOrders = WorkOrderBoardQueryFilters.ApplyAccountScope(workOrders, scopedAccountId); if (dispatcherId != null) workOrders = workOrders.Where(w => w.AssignTo == dispatcherId); if (dateFrom is DateOnly from && dateTo is DateOnly to) { var fromDate = from.ToDateTime(TimeOnly.MinValue); var toDateExclusive = to.AddDays(1).ToDateTime(TimeOnly.MinValue); workOrders = workOrders.Where(w => w.ScheduledDate >= fromDate && w.ScheduledDate < toDateExclusive); } var counts = await workOrders .Where(w => w.AssignTo != null) .GroupBy(w => w.AssignTo!) .Select(group => new { DispatcherId = group.Key, TotalCount = group.Count(), OpenCount = group.Count(w => w.LifecycleStatus != LifecycleStatus.Completed && w.LifecycleStatus != LifecycleStatus.Canceled && (w.LifecycleStatus != null || (w.LegacyStatus ?? w.Status) != "Done" && (w.LegacyStatus ?? w.Status) != "Completed" && (w.LegacyStatus ?? w.Status) != "Complete" && (w.LegacyStatus ?? w.Status) != "Closed" && (w.LegacyStatus ?? w.Status) != "Canceled" && (w.LegacyStatus ?? w.Status) != "Cancelled")) }) .ToListAsync(cancellationToken); var dispatcherList = await dispatcherUsers .Distinct() .ToListAsync(cancellationToken); return dispatcherList .GroupJoin( counts, user => user.Id, count => count.DispatcherId, (user, matchingCounts) => { var count = matchingCounts.SingleOrDefault(); return new DashboardDispatcherWorkload( user.Id, user.Name, user.Color, count?.TotalCount ?? 0, count?.OpenCount ?? 0); }) .ToList(); } } }