Files
CutList/CutList.Web/Services/OverviewService.cs
T
aj f3b911f37c
Build CutList image / build-and-push (push) Successful in 19s
feat(web): add data-backed planning overview
2026-08-02 20:18:07 +00:00

92 lines
2.7 KiB
C#

using CutList.Web.Data;
using CutList.Web.Data.Entities;
using Microsoft.EntityFrameworkCore;
namespace CutList.Web.Services;
public class OverviewService
{
private readonly IDbContextFactory<ApplicationDbContext> _factory;
public OverviewService(IDbContextFactory<ApplicationDbContext> factory)
{
_factory = factory;
}
public async Task<OverviewSnapshot> GetSnapshotAsync()
{
await using var context = _factory.CreateDbContext();
var recentJobs = await context.Jobs
.AsNoTracking()
.OrderByDescending(j => j.CreatedAt)
.Take(8)
.Select(j => new RecentJobOverview(
j.Id,
j.JobNumber,
j.Name,
j.Customer,
j.CreatedAt,
j.LockedAt != null,
j.Parts.Sum(p => (int?)p.Quantity) ?? 0))
.ToListAsync();
var frequentStock = await context.JobStocks
.AsNoTracking()
.GroupBy(s => new { s.MaterialId, s.LengthInches })
.Select(g => new
{
g.Key.MaterialId,
g.Key.LengthInches,
JobCount = g.Select(s => s.JobId).Distinct().Count(),
Material = g.Select(s => s.Material).First()
})
.OrderByDescending(s => s.JobCount)
.ThenBy(s => s.Material.Shape)
.ThenBy(s => s.Material.Size)
.ThenBy(s => s.LengthInches)
.Take(5)
.ToListAsync();
var totalJobs = await context.Jobs.CountAsync();
var openJobs = await context.Jobs.CountAsync(j => j.LockedAt == null);
var totalParts = await context.JobParts.SumAsync(p => (int?)p.Quantity) ?? 0;
var activeStockItems = await context.StockItems.CountAsync(s => s.IsActive);
return new OverviewSnapshot(
totalJobs,
openJobs,
totalParts,
activeStockItems,
recentJobs,
frequentStock.Select(s => new FrequentStockOverview(
s.MaterialId,
s.Material.DisplayName,
s.LengthInches,
s.JobCount)).ToList());
}
}
public record OverviewSnapshot(
int TotalJobs,
int OpenJobs,
int TotalParts,
int ActiveStockItems,
IReadOnlyList<RecentJobOverview> RecentJobs,
IReadOnlyList<FrequentStockOverview> FrequentStock);
public record RecentJobOverview(
int Id,
string JobNumber,
string? Name,
string? Customer,
DateTime CreatedAt,
bool IsLocked,
int PartCount);
public record FrequentStockOverview(
int MaterialId,
string MaterialName,
decimal LengthInches,
int JobCount);