mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 08:23:12 +00:00
232 lines
10 KiB
C#
232 lines
10 KiB
C#
using System.Security.Claims;
|
|
using SeaHaven.DataServices.Interfaces;
|
|
using SeaHaven.Services.DTOs;
|
|
using SeaHaven.Services.Exceptions;
|
|
using SeaHaven.Services.Helpers;
|
|
using SeaHaven.Services.Interfaces;
|
|
|
|
namespace SeaHaven.Services.Implementation
|
|
{
|
|
public class DashboardService : IDashboardService
|
|
{
|
|
private readonly IDashboardDataService _dataService;
|
|
private readonly IWorkOrderAccountResolver _accountResolver;
|
|
|
|
public DashboardService(
|
|
IDashboardDataService dataService,
|
|
IWorkOrderAccountResolver accountResolver)
|
|
{
|
|
_dataService = dataService;
|
|
_accountResolver = accountResolver;
|
|
}
|
|
|
|
public async Task<DashboardStatsDTO> GetStatsAsync(
|
|
ClaimsPrincipal user,
|
|
DashboardStatsQueryDTO query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var accountId = _accountResolver.ResolveAccountFilter(user);
|
|
var dispatcherId = user.IsInRole("Dispatcher")
|
|
? user.FindFirstValue(ClaimTypes.NameIdentifier)
|
|
: null;
|
|
if (user.IsInRole("Dispatcher") && string.IsNullOrWhiteSpace(dispatcherId))
|
|
throw new WorkOrderBoardValidationException("Forbidden", "Dispatcher identity is required.");
|
|
|
|
var counts = await _dataService.GetWorkOrderCountsAsync(accountId, cancellationToken);
|
|
var workOrders = await _dataService.GetDashboardWorkOrdersAsync(
|
|
accountId,
|
|
dispatcherId,
|
|
query.DateFrom,
|
|
query.DateTo,
|
|
cancellationToken);
|
|
var metrics = DashboardMetrics.Calculate(workOrders, DashboardBusinessTime.Today());
|
|
|
|
var useMetrics = query.DateFrom.HasValue && query.DateTo.HasValue || dispatcherId is not null;
|
|
|
|
return new DashboardStatsDTO
|
|
{
|
|
Total = useMetrics ? metrics.Total : counts.Total,
|
|
Open = useMetrics ? metrics.Open : counts.Open,
|
|
NotDispatched = useMetrics ? metrics.NotDispatched : counts.NotDispatched,
|
|
Completed = useMetrics ? metrics.Completed : counts.Completed,
|
|
Breakdown = metrics.Breakdown,
|
|
DueCount = metrics.DueCount,
|
|
CompletedDueCount = metrics.CompletedDueCount,
|
|
CompletionRate = metrics.CompletionRate,
|
|
AverageResolutionDays = metrics.AverageResolutionDays
|
|
};
|
|
}
|
|
|
|
public async Task<DashboardWorkloadResponseDTO> GetWorkloadAsync(
|
|
ClaimsPrincipal user,
|
|
DashboardStatsQueryDTO query,
|
|
int page,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
if (page < 1)
|
|
throw new ArgumentOutOfRangeException(nameof(page));
|
|
|
|
var accountId = _accountResolver.ResolveAccountFilter(user);
|
|
var dispatcherId = user.IsInRole("Dispatcher")
|
|
? user.FindFirstValue(ClaimTypes.NameIdentifier)
|
|
: null;
|
|
if (user.IsInRole("Dispatcher") && string.IsNullOrWhiteSpace(dispatcherId))
|
|
throw new WorkOrderBoardValidationException("Forbidden", "Dispatcher identity is required.");
|
|
|
|
var rows = await _dataService.GetDispatcherWorkloadAsync(
|
|
accountId,
|
|
dispatcherId,
|
|
query.DateFrom,
|
|
query.DateTo,
|
|
cancellationToken);
|
|
var orderedRows = rows
|
|
.OrderByDescending(row => row.OpenCount)
|
|
.ThenBy(row => row.DispatcherName, StringComparer.OrdinalIgnoreCase)
|
|
.ThenBy(row => row.DispatcherId, StringComparer.Ordinal)
|
|
.ToList();
|
|
const int pageSize = 10;
|
|
|
|
return new DashboardWorkloadResponseDTO
|
|
{
|
|
Items = orderedRows.Skip((page - 1) * pageSize).Take(pageSize)
|
|
.Select(row => new DashboardWorkloadRowDTO
|
|
{
|
|
DispatcherId = row.DispatcherId,
|
|
DispatcherName = row.DispatcherName,
|
|
Color = row.Color,
|
|
TotalCount = row.TotalCount,
|
|
OpenCount = row.OpenCount
|
|
}).ToList(),
|
|
Page = page,
|
|
PageSize = pageSize,
|
|
TotalDispatchers = orderedRows.Count
|
|
};
|
|
}
|
|
|
|
public async Task<DashboardPerformanceResponseDTO> GetPerformanceAsync(
|
|
ClaimsPrincipal user,
|
|
DashboardStatsQueryDTO query,
|
|
int page,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
if (page < 1)
|
|
throw new ArgumentOutOfRangeException(nameof(page));
|
|
|
|
var accountId = _accountResolver.ResolveAccountFilter(user);
|
|
var dispatcherId = user.IsInRole("Dispatcher")
|
|
? user.FindFirstValue(ClaimTypes.NameIdentifier)
|
|
: null;
|
|
if (user.IsInRole("Dispatcher") && string.IsNullOrWhiteSpace(dispatcherId))
|
|
throw new WorkOrderBoardValidationException("Forbidden", "Dispatcher identity is required.");
|
|
|
|
var directory = await _dataService.GetDispatcherWorkloadAsync(
|
|
accountId, dispatcherId, query.DateFrom, query.DateTo, cancellationToken);
|
|
var workOrders = await _dataService.GetDashboardWorkOrdersAsync(
|
|
accountId, dispatcherId, query.DateFrom, query.DateTo, cancellationToken);
|
|
var today = DashboardBusinessTime.Today();
|
|
var rows = directory.Select(dispatcher =>
|
|
{
|
|
var metrics = DashboardMetrics.Calculate(
|
|
workOrders.Where(workOrder => workOrder.AssignTo == dispatcher.DispatcherId).ToList(),
|
|
today);
|
|
return new DashboardPerformanceRowDTO
|
|
{
|
|
DispatcherId = dispatcher.DispatcherId,
|
|
DispatcherName = dispatcher.DispatcherName,
|
|
Color = dispatcher.Color,
|
|
CompletionRate = metrics.CompletionRate,
|
|
AverageResolutionDays = metrics.AverageResolutionDays
|
|
};
|
|
})
|
|
.OrderByDescending(row => row.CompletionRate)
|
|
.ThenBy(row => row.DispatcherName, StringComparer.OrdinalIgnoreCase)
|
|
.ThenBy(row => row.DispatcherId, StringComparer.Ordinal)
|
|
.ToList();
|
|
const int pageSize = 10;
|
|
|
|
return new DashboardPerformanceResponseDTO
|
|
{
|
|
Items = rows.Skip((page - 1) * pageSize).Take(pageSize).ToList(),
|
|
Page = page,
|
|
PageSize = pageSize,
|
|
TotalDispatchers = rows.Count
|
|
};
|
|
}
|
|
|
|
public async Task<DashboardRegionResponseDTO> GetRegionsAsync(
|
|
ClaimsPrincipal user,
|
|
DashboardStatsQueryDTO query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var accountId = _accountResolver.ResolveAccountFilter(user);
|
|
var dispatcherId = user.IsInRole("Dispatcher")
|
|
? user.FindFirstValue(ClaimTypes.NameIdentifier)
|
|
: null;
|
|
if (user.IsInRole("Dispatcher") && string.IsNullOrWhiteSpace(dispatcherId))
|
|
throw new WorkOrderBoardValidationException("Forbidden", "Dispatcher identity is required.");
|
|
|
|
var workOrders = await _dataService.GetDashboardWorkOrdersAsync(
|
|
accountId, dispatcherId, query.DateFrom, query.DateTo, cancellationToken);
|
|
var counts = DashboardRegions.Count(workOrders);
|
|
return new DashboardRegionResponseDTO
|
|
{
|
|
Items = DashboardRegions.Names
|
|
.Select(region => new DashboardRegionRowDTO
|
|
{
|
|
Region = region,
|
|
WorkOrderCount = counts[region]
|
|
})
|
|
.ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<DashboardVendorInsightsResponseDTO> GetVendorInsightsAsync(
|
|
ClaimsPrincipal user,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var accountId = _accountResolver.ResolveAccountFilter(user);
|
|
var workOrders = await _dataService.GetDashboardWorkOrdersAsync(
|
|
accountId, null, null, null, cancellationToken);
|
|
var activeVendorOrders = workOrders
|
|
.Where(workOrder => workOrder.VendorCompanyId.HasValue
|
|
&& workOrder.VendorIsActive
|
|
&& !workOrder.VendorCompanyIsDeleted)
|
|
.ToList();
|
|
var today = DashboardBusinessTime.Today();
|
|
var rows = activeVendorOrders
|
|
.GroupBy(workOrder => new
|
|
{
|
|
Id = workOrder.VendorCompanyId!.Value,
|
|
Name = workOrder.VendorCompanyName ?? string.Empty
|
|
})
|
|
.Select(group =>
|
|
{
|
|
var orders = group.ToList();
|
|
var metrics = DashboardMetrics.Calculate(orders, today);
|
|
var rescheduled = orders.Count(order => order.RescheduleCount >= 1);
|
|
return new DashboardVendorInsightsRowDTO
|
|
{
|
|
VendorCompanyId = group.Key.Id,
|
|
VendorCompanyName = group.Key.Name,
|
|
CompletionRate = metrics.CompletionRate,
|
|
RescheduleRate = Math.Round((decimal)rescheduled / orders.Count * 100, 2),
|
|
AverageResolutionDays = metrics.AverageResolutionDays,
|
|
TotalJobs = orders.Count
|
|
};
|
|
})
|
|
.OrderByDescending(row => row.CompletionRate)
|
|
.ThenBy(row => row.VendorCompanyName, StringComparer.OrdinalIgnoreCase)
|
|
.ThenBy(row => row.VendorCompanyId)
|
|
.ToList();
|
|
const int pageSize = 10;
|
|
|
|
return new DashboardVendorInsightsResponseDTO
|
|
{
|
|
Items = rows.Take(pageSize).ToList(),
|
|
Page = 1,
|
|
PageSize = pageSize,
|
|
TotalVendors = rows.Count
|
|
};
|
|
}
|
|
}
|
|
}
|