@model PowderCoating.Application.DTOs.Powder.PowderInsightsDashboardDto @{ ViewData["Title"] = "Powder Insights"; ViewData["PageIcon"] = "bi-graph-up"; var readiness = Model.Readiness; }
Inventory
@* ── KPI cards ── *@
@Model.ActiveJobsNeedingPowder
Active Jobs Needing Powder
@Model.TotalEstimatedPowderNeededLbs.ToString("0.#") lbs
Total Powder in Pipeline
@Model.LowStockAlerts.Count(a => a.IsAtRisk)
At-Risk Stock Items
@readiness.JobsWithActualData
Jobs with Actual Data
@* ── Tabs ── *@
@* ── Tab 1: Stock Forecast (Layer 2, immediate value) ── *@
@if (!Model.LowStockAlerts.Any()) {

No active jobs require powder, or all stock levels are sufficient.

} else {
Powder Demand vs. Stock — Active Jobs
@foreach (var item in Model.LowStockAlerts) { }
Powder On Hand Pipeline Demand Shortfall Jobs Status
@item.Name @if (!string.IsNullOrEmpty(item.ColorName)) {
@item.ColorName @(item.ColorCode != null ? $"({item.ColorCode})" : "") @item.Manufacturer }
@item.CurrentStockLbs.ToString("0.##") lbs @item.ScheduledDemandLbs.ToString("0.##") lbs @Html.Raw(item.ShortfallLbs > 0 ? $"{item.ShortfallLbs:0.##} lbs" : "—") @item.ActiveJobCount @if (item.IsAtRisk) { Order Now } else if (item.IsBelowReorderPoint) { Below Reorder } else { OK }
}
@* ── Tab 2: Coverage Efficiency (Layer 2) ── *@
@if (!readiness.IsLayer2Ready) {
Building Your Dataset

Coverage efficiency trends become meaningful after @readiness.Layer2MinJobs jobs with actual usage recorded.

You have @readiness.JobsWithActualData so far. Keep recording actuals on completed jobs!

} else if (!Model.EfficiencyBySku.Any()) {

No efficiency data yet — record actual powder usage on completed jobs to see this chart.

} else {
Actual vs. Catalog Coverage Rate
Negative variance = using more powder than spec
@foreach (var eff in Model.EfficiencyBySku) { }
Powder Catalog Rate Actual Avg Rate Variance Est. lbs Actual lbs Samples
@eff.Name @if (!string.IsNullOrEmpty(eff.ColorName)) {
@eff.ColorName · @eff.Manufacturer } @if (!eff.HasEnoughData) {
Low confidence — need 5+ samples }
@eff.CatalogCoverageSqFtPerLb.ToString("0.#") sq ft/lb @eff.ActualAvgCoverageSqFtPerLb.ToString("0.#") sq ft/lb @(eff.VariancePct > 0 ? "+" : "")@eff.VariancePct.ToString("0.#")% @eff.TotalEstimatedLbs.ToString("0.#") @eff.TotalActualLbs.ToString("0.#") @eff.SampleCount
}
@* ── Tab 3: Predictive (Layer 3, gated) ── *@
@if (!readiness.IsLayer3Ready) { @* Progress gate *@
Predictive Insights Unlocks at @readiness.Layer3MinJobs Jobs

Once @readiness.Layer3MinJobs jobs have actual powder usage recorded, this tab will show AI-powered reorder suggestions and waste pattern analysis.

@readiness.JobsWithActualData of @readiness.Layer3MinJobs jobs recorded (@readiness.Layer3ProgressPercent%)

What unlocks here
  • Smart reorder suggestions — quantity recommendations based on your actual usage history + scheduled job pipeline
  • Waste pattern detection — identifies jobs and powder types that consistently over-consume
  • Per-powder efficiency corrections — suggests updating coverage defaults based on real data
} else { @* Reorder Suggestions *@
Smart Reorder Suggestions
Based on @readiness.JobsWithActualData jobs of actual data + 30-day pipeline
@if (!Model.ReorderSuggestions.Any()) {
No reorder suggestions — stock levels look good for upcoming pipeline.
} else {
@foreach (var s in Model.ReorderSuggestions) { var confPct = (int)(s.ConfidenceScore * 100); var confClass = confPct >= 70 ? "success" : confPct >= 40 ? "warning" : "secondary"; }
Powder On Hand 30-Day Pipeline Avg Monthly Usage Suggested Order Confidence
@s.Name @if (!string.IsNullOrEmpty(s.ColorName)) {
@s.ColorName · @s.Manufacturer }
@s.CurrentStockLbs.ToString("0.#") lbs @s.PipelineDemand30DaysLbs.ToString("0.#") lbs @s.HistoricalAvgMonthlyUsageLbs.ToString("0.#") lbs @s.SuggestedOrderQtyLbs.ToString("0.#") lbs
@confPct%
@s.SampleJobCount jobs
}
@* Waste Patterns *@
Waste Patterns — coats that used >20% more than estimated
@if (!Model.WastePatterns.Any()) {
No significant waste patterns detected. Great accuracy!
} else {
@foreach (var w in Model.WastePatterns) { }
Job Item / Coat Powder Complexity Estimated Actual Overage
@w.JobNumber @w.ItemDescription
@w.CoatName
@(w.InventoryItemName ?? "Custom") @Html.Raw(w.Complexity ?? "—") @w.EstimatedLbs.ToString("0.##") lbs @w.ActualLbs.ToString("0.##") lbs +@w.OveragePct.ToString("0.#")%
}
}