Analytics API and Dashboard Data
Updates controllers and view model so the dashboard can load analytics data and expose it through the verification API/page.main
parent
ec86ae98e0
commit
2430700dbf
|
|
@ -1,22 +1,39 @@
|
|||
using System.Diagnostics;
|
||||
using AVSCartonShipmentVerifier.Models;
|
||||
using AVSCartonShipmentVerifier.Repositories;
|
||||
using AVSCartonShipmentVerifier.Services;
|
||||
using AVSCartonShipmentVerifier.ViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AVSCartonShipmentVerifier.Controllers;
|
||||
|
||||
public sealed class HomeController(IScanHistoryStore scanHistoryStore) : Controller
|
||||
public sealed class HomeController(
|
||||
IScanHistoryStore scanHistoryStore,
|
||||
ICartonVerificationRepository cartonVerificationRepository,
|
||||
ILogger<HomeController> logger) : Controller
|
||||
{
|
||||
private readonly IScanHistoryStore _scanHistoryStore = scanHistoryStore;
|
||||
private readonly ICartonVerificationRepository _cartonVerificationRepository = cartonVerificationRepository;
|
||||
private readonly ILogger<HomeController> _logger = logger;
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index(CancellationToken cancellationToken)
|
||||
{
|
||||
int? availableCartonsCount = null;
|
||||
try
|
||||
{
|
||||
availableCartonsCount = await _cartonVerificationRepository.GetAvailableCartonCountAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogWarning(exception, "Failed to load available cartons count for dashboard.");
|
||||
}
|
||||
|
||||
var model = new VerificationDashboardViewModel
|
||||
{
|
||||
RecentSuccessfulScans = await _scanHistoryStore.GetLastFiveSuccessfulScansAsync(cancellationToken),
|
||||
RecentRejectedScans = await _scanHistoryStore.GetLastFiveRejectedScansAsync(cancellationToken)
|
||||
RecentRejectedScans = await _scanHistoryStore.GetLastFiveRejectedScansAsync(cancellationToken),
|
||||
AvailableCartonsCount = availableCartonsCount
|
||||
};
|
||||
|
||||
return View(model);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using AVSCartonShipmentVerifier.DTOs;
|
||||
using AVSCartonShipmentVerifier.Repositories;
|
||||
using AVSCartonShipmentVerifier.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
|
@ -8,10 +9,14 @@ namespace AVSCartonShipmentVerifier.Controllers;
|
|||
public sealed class VerificationController(
|
||||
IShipmentVerificationService shipmentVerificationService,
|
||||
IScanHistoryStore scanHistoryStore,
|
||||
ICartonVerificationRepository cartonVerificationRepository,
|
||||
IMovementAnalyticsService movementAnalyticsService,
|
||||
ILogger<VerificationController> logger) : Controller
|
||||
{
|
||||
private readonly IShipmentVerificationService _shipmentVerificationService = shipmentVerificationService;
|
||||
private readonly IScanHistoryStore _scanHistoryStore = scanHistoryStore;
|
||||
private readonly ICartonVerificationRepository _cartonVerificationRepository = cartonVerificationRepository;
|
||||
private readonly IMovementAnalyticsService _movementAnalyticsService = movementAnalyticsService;
|
||||
private readonly ILogger<VerificationController> _logger = logger;
|
||||
|
||||
[HttpPost("scan")]
|
||||
|
|
@ -30,11 +35,14 @@ public sealed class VerificationController(
|
|||
request.EntryMethod ?? "Scanner",
|
||||
cancellationToken);
|
||||
|
||||
var availableCartons = await TryGetAvailableCartonCountAsync(cancellationToken);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
result = verificationResult,
|
||||
recentSuccessfulScans = await _scanHistoryStore.GetLastFiveSuccessfulScansAsync(cancellationToken),
|
||||
recentRejectedScans = await _scanHistoryStore.GetLastFiveRejectedScansAsync(cancellationToken)
|
||||
recentRejectedScans = await _scanHistoryStore.GetLastFiveRejectedScansAsync(cancellationToken),
|
||||
availableCartons
|
||||
});
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
|
@ -52,6 +60,52 @@ public sealed class VerificationController(
|
|||
}
|
||||
}
|
||||
|
||||
[HttpGet("dashboard-summary")]
|
||||
public async Task<IActionResult> GetDashboardSummary(CancellationToken cancellationToken)
|
||||
{
|
||||
var count = await TryGetAvailableCartonCountAsync(cancellationToken);
|
||||
if (count is null)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status503ServiceUnavailable, new { message = "Unable to load available cartons count." });
|
||||
}
|
||||
|
||||
return Ok(new { availableCartons = count.Value });
|
||||
}
|
||||
|
||||
[HttpGet("available-cartons")]
|
||||
public async Task<IActionResult> GetAvailableCartons(CancellationToken cancellationToken)
|
||||
{
|
||||
var count = await TryGetAvailableCartonCountAsync(cancellationToken);
|
||||
if (count is null)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status503ServiceUnavailable, new { message = "Unable to load available cartons count." });
|
||||
}
|
||||
|
||||
return Ok(new { availableCartons = count.Value });
|
||||
}
|
||||
|
||||
[HttpGet("movement-analytics")]
|
||||
public async Task<IActionResult> GetMovementAnalytics(
|
||||
[FromQuery] DateOnly? from,
|
||||
[FromQuery] DateOnly? to,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var today = DateOnly.FromDateTime(DateTime.Now);
|
||||
var toDate = to ?? today;
|
||||
var fromDate = from ?? toDate.AddDays(-6);
|
||||
|
||||
try
|
||||
{
|
||||
var analytics = await _movementAnalyticsService.GetAnalyticsAsync(fromDate, toDate, cancellationToken);
|
||||
return Ok(analytics);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Unexpected error while loading movement analytics.");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Unable to load movement analytics." });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all successful or all rejected scan records from SQLite (not limited to five).
|
||||
/// </summary>
|
||||
|
|
@ -81,5 +135,18 @@ public sealed class VerificationController(
|
|||
return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Unable to load scan history." });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int?> TryGetAvailableCartonCountAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _cartonVerificationRepository.GetAvailableCartonCountAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogWarning(exception, "Failed to load available cartons count from verification database.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,5 +7,8 @@ public sealed class VerificationDashboardViewModel
|
|||
public IReadOnlyCollection<ScannedRecordDto> RecentSuccessfulScans { get; init; } = [];
|
||||
|
||||
public IReadOnlyCollection<RejectedScanRecordDto> RecentRejectedScans { get; init; } = [];
|
||||
|
||||
/// <summary>Cartons with moved_to_warehouse = 0; null when MySQL count could not be loaded.</summary>
|
||||
public int? AvailableCartonsCount { get; init; }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue