shoc-backend/Api.SeaHavenIndustries/Controllers/DashboardController.cs
Alexandre Brandizzi 4876fa2717 feat(dashboard): count open unassigned work orders for the period (SH-392)
GET /dashboard/stats now returns unassigned: open (not Completed or
Canceled) work orders with no dispatcher in the selected period, within the
caller's server-derived account scope. It uses the same filters as the
Unassigned board search, so the Dashboard number equals the list it drills
into. A dispatcher-scoped Dashboard has no unassigned work and returns 0.
2026-09-24 21:11:31 -03:00

177 lines
6.1 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,
unassigned = stats.Unassigned,
breakdown = stats.Breakdown,
dueCount = stats.DueCount,
completedDueCount = stats.CompletedDueCount,
completionRate = stats.CompletionRate,
averageResolutionDays = stats.AverageResolutionDays,
statusDistribution = stats.StatusDistribution
});
}
[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
});
}
}
}
}