mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
GetKpiCountsAsync computes ScheduledTomorrow, PendingUplifts and AvetaPending and DashboardStatsDTO carries them, but the Stats endpoint's anonymous wire object never serialised them, so the tiles had nothing to read. Add scheduledTomorrow, pendingUplifts and avetaPending to the response. Add a DashboardServiceTests case covering GetKpiCountsAsync end to end: it asserts all three counts and pins the pendingUplifts semantics to Status "Pending" only, excluding "ChangesRequested" (which is back with the vendor, not awaiting an approval decision).
175 lines
6 KiB
C#
175 lines
6 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using SeaHaven.Services.DTOs;
|
|
using SeaHaven.Services.Interfaces;
|
|
|
|
namespace Api.SeaHavenIndustries.Controllers
|
|
{
|
|
[Authorize]
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public class DashboardController : Controller
|
|
{
|
|
private readonly IDashboardService _dashboardService;
|
|
|
|
public DashboardController(IDashboardService dashboardService)
|
|
{
|
|
_dashboardService = dashboardService;
|
|
}
|
|
|
|
[HttpGet("Stats")]
|
|
public async Task<IActionResult> GetStats(
|
|
[FromQuery] DashboardStatsQueryDTO query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var stats = await _dashboardService.GetStatsAsync(User, query, cancellationToken);
|
|
|
|
return Ok(new
|
|
{
|
|
total = stats.Total,
|
|
open = stats.Open,
|
|
notDispatched = stats.NotDispatched,
|
|
completed = stats.Completed,
|
|
scheduledTomorrow = stats.ScheduledTomorrow,
|
|
pendingUplifts = stats.PendingUplifts,
|
|
avetaPending = stats.AvetaPending,
|
|
breakdown = stats.Breakdown,
|
|
dueCount = stats.DueCount,
|
|
completedDueCount = stats.CompletedDueCount,
|
|
completionRate = stats.CompletionRate,
|
|
averageResolutionDays = stats.AverageResolutionDays
|
|
});
|
|
}
|
|
|
|
[HttpGet("Workload")]
|
|
public async Task<IActionResult> GetWorkload(
|
|
[FromQuery] DashboardStatsQueryDTO query,
|
|
[FromQuery] int page = 1,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
return Ok(await _dashboardService.GetWorkloadAsync(
|
|
User, query, page, cancellationToken));
|
|
}
|
|
catch (SeaHaven.Services.Exceptions.WorkOrderBoardValidationException ex)
|
|
when (ex.Code == "Forbidden")
|
|
{
|
|
return StatusCode(StatusCodes.Status403Forbidden, new
|
|
{
|
|
code = ex.Code,
|
|
message = ex.Message
|
|
});
|
|
}
|
|
catch (ArgumentOutOfRangeException ex)
|
|
{
|
|
return BadRequest(ex.Message);
|
|
}
|
|
}
|
|
|
|
[HttpGet("Performance")]
|
|
public async Task<IActionResult> GetPerformance(
|
|
[FromQuery] DashboardStatsQueryDTO query,
|
|
[FromQuery] int page = 1,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
return Ok(await _dashboardService.GetPerformanceAsync(
|
|
User, query, page, cancellationToken));
|
|
}
|
|
catch (SeaHaven.Services.Exceptions.WorkOrderBoardValidationException ex)
|
|
when (ex.Code == "Forbidden")
|
|
{
|
|
return StatusCode(StatusCodes.Status403Forbidden, new
|
|
{
|
|
code = ex.Code,
|
|
message = ex.Message
|
|
});
|
|
}
|
|
catch (ArgumentOutOfRangeException ex)
|
|
{
|
|
return BadRequest(ex.Message);
|
|
}
|
|
}
|
|
|
|
[HttpGet("Regions")]
|
|
public async Task<IActionResult> GetRegions(
|
|
[FromQuery] DashboardStatsQueryDTO query,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
return Ok(await _dashboardService.GetRegionsAsync(User, query, cancellationToken));
|
|
}
|
|
catch (SeaHaven.Services.Exceptions.WorkOrderBoardValidationException ex)
|
|
when (ex.Code == "Forbidden")
|
|
{
|
|
return StatusCode(StatusCodes.Status403Forbidden, new
|
|
{
|
|
code = ex.Code,
|
|
message = ex.Message
|
|
});
|
|
}
|
|
}
|
|
|
|
[HttpGet("Trend")]
|
|
public async Task<IActionResult> GetTrend(
|
|
[FromQuery] DashboardTrendQueryDTO query,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var trend = await _dashboardService.GetTrendAsync(User, query, cancellationToken);
|
|
|
|
return Ok(new
|
|
{
|
|
granularity = trend.Granularity,
|
|
today = trend.Today,
|
|
buckets = trend.Buckets.Select(bucket => new
|
|
{
|
|
date = bucket.Date,
|
|
label = bucket.Label,
|
|
total = bucket.Total,
|
|
open = bucket.Open,
|
|
completed = bucket.Completed,
|
|
canceled = bucket.Canceled,
|
|
overdue = bucket.Overdue,
|
|
isCurrent = bucket.IsCurrent
|
|
})
|
|
});
|
|
}
|
|
catch (SeaHaven.Services.Exceptions.WorkOrderBoardValidationException ex)
|
|
when (ex.Code == "Forbidden")
|
|
{
|
|
return StatusCode(StatusCodes.Status403Forbidden, new
|
|
{
|
|
code = ex.Code,
|
|
message = ex.Message
|
|
});
|
|
}
|
|
catch (ArgumentOutOfRangeException ex)
|
|
{
|
|
return BadRequest(ex.Message);
|
|
}
|
|
}
|
|
|
|
[HttpGet("VendorInsights")]
|
|
public async Task<IActionResult> GetVendorInsights(CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
return Ok(await _dashboardService.GetVendorInsightsAsync(User, cancellationToken));
|
|
}
|
|
catch (SeaHaven.Services.Exceptions.WorkOrderBoardValidationException ex)
|
|
when (ex.Code == "Forbidden")
|
|
{
|
|
return StatusCode(StatusCodes.Status403Forbidden, new
|
|
{
|
|
code = ex.Code,
|
|
message = ex.Message
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|