342 lines
18 KiB
Plaintext
342 lines
18 KiB
Plaintext
@{
|
|
ViewData["Title"] = "Sample Panels";
|
|
ViewData["PageIcon"] = "bi-palette";
|
|
var manufacturers = ViewBag.Manufacturers as List<string> ?? new List<string>();
|
|
var selectedMfr = ViewBag.SelectedManufacturer as string;
|
|
var activeTab = ViewBag.ActiveTab as string ?? "need";
|
|
var onHand = ViewBag.OnHandItems as List<PowderCoating.Core.Entities.InventoryItem> ?? new();
|
|
var needOrder = ViewBag.NeedToOrderItems as List<PowderCoating.Core.Entities.InventoryItem> ?? new();
|
|
var totalCoatings = (int)(ViewBag.TotalCoatings ?? 0);
|
|
var totalOnHand = (int)(ViewBag.TotalOnHand ?? 0);
|
|
var totalNeedOrder = (int)(ViewBag.TotalNeedOrder ?? 0);
|
|
string? lastMfr = null;
|
|
}
|
|
|
|
@section Styles {
|
|
<style>
|
|
.panel-row { cursor: pointer; transition: background .12s; }
|
|
.panel-row:hover { background: var(--bs-tertiary-bg); }
|
|
.panel-row td { vertical-align: middle; }
|
|
.panel-badge-on { color: #198754; }
|
|
.panel-badge-need { color: #6c757d; }
|
|
.color-swatch {
|
|
width: 24px; height: 24px;
|
|
border-radius: 4px;
|
|
border: 1px solid var(--bs-border-color);
|
|
display: inline-block;
|
|
flex-shrink: 0;
|
|
}
|
|
</style>
|
|
}
|
|
|
|
<div class="d-flex justify-content-end align-items-center gap-2 mb-4">
|
|
<a asp-action="Index" class="btn btn-outline-secondary">
|
|
<i class="bi bi-box-seam me-1"></i>Back to Inventory
|
|
</a>
|
|
<button class="btn btn-outline-primary" id="btnPrintList">
|
|
<i class="bi bi-printer me-1"></i>Print Need-to-Order
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-sm-4">
|
|
<div class="card border-0 shadow-sm text-center py-3">
|
|
<div class="fs-2 fw-bold text-primary">@totalCoatings</div>
|
|
<div class="text-muted small">Total Coating Colors</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-4">
|
|
<div class="card border-0 shadow-sm text-center py-3">
|
|
<div class="fs-2 fw-bold text-success">@totalOnHand</div>
|
|
<div class="text-muted small">Panels on Wall</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-4">
|
|
<div class="card border-0 shadow-sm text-center py-3">
|
|
<div class="fs-2 fw-bold text-secondary">@totalNeedOrder</div>
|
|
<div class="text-muted small">Need to Order</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Manufacturer Filter -->
|
|
<div class="card border-0 shadow-sm mb-4">
|
|
<div class="card-body py-3">
|
|
<form method="get" class="d-flex gap-2 align-items-center flex-wrap">
|
|
<input type="hidden" name="tab" value="@activeTab" id="filterTabInput" />
|
|
<label class="form-label mb-0 fw-semibold me-1">Filter by Manufacturer:</label>
|
|
<select name="manufacturer" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
|
|
<option value="">— All Manufacturers —</option>
|
|
@foreach (var mfr in manufacturers)
|
|
{
|
|
<option value="@mfr" selected="@(selectedMfr == mfr ? "selected" : null)">@mfr</option>
|
|
}
|
|
</select>
|
|
@if (!string.IsNullOrWhiteSpace(selectedMfr))
|
|
{
|
|
<a asp-action="SamplePanels" asp-route-tab="@activeTab" class="btn btn-sm btn-outline-secondary">
|
|
<i class="bi bi-x me-1"></i>Clear Filter
|
|
</a>
|
|
}
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tabs -->
|
|
<ul class="nav nav-tabs mb-0" id="samplePanelTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link @(activeTab == "need" ? "active" : "")" id="tab-need"
|
|
data-bs-toggle="tab" data-bs-target="#pane-need" type="button" role="tab"
|
|
onclick="document.getElementById('filterTabInput').value='need'">
|
|
<i class="bi bi-bag me-1 text-secondary"></i>
|
|
Need to Order
|
|
<span class="badge bg-secondary rounded-pill ms-1">@needOrder.Count</span>
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link @(activeTab == "onhand" ? "active" : "")" id="tab-onhand"
|
|
data-bs-toggle="tab" data-bs-target="#pane-onhand" type="button" role="tab"
|
|
onclick="document.getElementById('filterTabInput').value='onhand'">
|
|
<i class="bi bi-check-circle me-1 text-success"></i>
|
|
On Wall
|
|
<span class="badge bg-success rounded-pill ms-1">@onHand.Count</span>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content">
|
|
<!-- Need to Order Tab -->
|
|
<div class="tab-pane fade @(activeTab == "need" ? "show active" : "")" id="pane-need" role="tabpanel">
|
|
<div class="card border-0 shadow-sm border-top-0" style="border-radius: 0 0 .5rem .5rem;">
|
|
<div class="card-body p-0">
|
|
@if (!needOrder.Any())
|
|
{
|
|
<div class="text-center py-5 text-muted">
|
|
<i class="bi bi-check2-all display-4 text-success d-block mb-2"></i>
|
|
@if (string.IsNullOrWhiteSpace(selectedMfr))
|
|
{
|
|
<p>All coating colors have a sample panel on the wall!</p>
|
|
}
|
|
else
|
|
{
|
|
<p>All <strong>@selectedMfr</strong> colors have a sample panel on the wall.</p>
|
|
}
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0" id="needTable">
|
|
<thead class="table-group-divider">
|
|
<tr>
|
|
<th style="width:36px;"></th>
|
|
<th>Color / Item</th>
|
|
<th>Manufacturer</th>
|
|
<th>Part #</th>
|
|
<th>Finish</th>
|
|
<th>In Stock</th>
|
|
<th class="text-end">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="needTableBody">
|
|
@{ lastMfr = null; }
|
|
@foreach (var item in needOrder)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(selectedMfr) && item.Manufacturer != lastMfr)
|
|
{
|
|
lastMfr = item.Manufacturer;
|
|
<tr class="table-secondary">
|
|
<td colspan="6" class="py-1 px-3">
|
|
<small class="fw-semibold text-uppercase text-muted">
|
|
@(string.IsNullOrWhiteSpace(item.Manufacturer) ? "No Manufacturer" : item.Manufacturer)
|
|
</small>
|
|
</td>
|
|
</tr>
|
|
}
|
|
<tr class="panel-row" data-id="@item.Id">
|
|
<td class="ps-3">
|
|
@if (!string.IsNullOrWhiteSpace(item.ColorCode))
|
|
{
|
|
<span class="color-swatch" style="background: @(item.ColorCode.StartsWith("#") ? item.ColorCode : "#" + item.ColorCode);"
|
|
title="@item.ColorCode"></span>
|
|
}
|
|
else
|
|
{
|
|
<span class="color-swatch bg-body-secondary"></span>
|
|
}
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold">@(item.ColorName ?? item.Name)</div>
|
|
@if (!string.IsNullOrWhiteSpace(item.ColorName) && item.ColorName != item.Name)
|
|
{
|
|
<div class="text-muted small">@item.Name</div>
|
|
}
|
|
</td>
|
|
<td>@(item.Manufacturer ?? "—")</td>
|
|
<td class="text-muted small">@(item.ManufacturerPartNumber ?? "—")</td>
|
|
<td>@(item.Finish ?? "—")</td>
|
|
<td>
|
|
@if (item.QuantityOnHand > 0)
|
|
{
|
|
<span class="badge bg-success bg-opacity-10 text-success">@item.QuantityOnHand.ToString("N2") @item.UnitOfMeasure</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="text-muted small">None</span>
|
|
}
|
|
</td>
|
|
<td class="text-end pe-3">
|
|
<button class="btn btn-sm btn-outline-success me-1 btn-toggle-panel"
|
|
data-item-id="@item.Id" data-has-panel="true"
|
|
title="Mark as received — panel is on wall">
|
|
<i class="bi bi-check-lg me-1"></i>Got It
|
|
</button>
|
|
<a asp-action="Details" asp-route-id="@item.Id"
|
|
class="btn btn-sm btn-outline-secondary" title="View item">
|
|
<i class="bi bi-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- On Wall Tab -->
|
|
<div class="tab-pane fade @(activeTab == "onhand" ? "show active" : "")" id="pane-onhand" role="tabpanel">
|
|
<div class="card border-0 shadow-sm border-top-0" style="border-radius: 0 0 .5rem .5rem;">
|
|
<div class="card-body p-0">
|
|
@if (!onHand.Any())
|
|
{
|
|
<div class="text-center py-5 text-muted">
|
|
<i class="bi bi-palette display-4 d-block mb-2"></i>
|
|
<p>No sample panels recorded yet. Use the <strong>Need to Order</strong> tab to mark colors as received.</p>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-group-divider">
|
|
<tr>
|
|
<th style="width:36px;"></th>
|
|
<th>Color / Item</th>
|
|
<th>Manufacturer</th>
|
|
<th>Part #</th>
|
|
<th>Finish</th>
|
|
<th class="text-end">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@{ lastMfr = null; }
|
|
@foreach (var item in onHand)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(selectedMfr) && item.Manufacturer != lastMfr)
|
|
{
|
|
lastMfr = item.Manufacturer;
|
|
<tr class="table-secondary">
|
|
<td colspan="6" class="py-1 px-3">
|
|
<small class="fw-semibold text-uppercase text-muted">
|
|
@(string.IsNullOrWhiteSpace(item.Manufacturer) ? "No Manufacturer" : item.Manufacturer)
|
|
</small>
|
|
</td>
|
|
</tr>
|
|
}
|
|
<tr class="panel-row" data-id="@item.Id">
|
|
<td class="ps-3">
|
|
@if (!string.IsNullOrWhiteSpace(item.ColorCode))
|
|
{
|
|
<span class="color-swatch" style="background: @(item.ColorCode.StartsWith("#") ? item.ColorCode : "#" + item.ColorCode);"
|
|
title="@item.ColorCode"></span>
|
|
}
|
|
else
|
|
{
|
|
<span class="color-swatch bg-body-secondary"></span>
|
|
}
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold">@(item.ColorName ?? item.Name)</div>
|
|
@if (!string.IsNullOrWhiteSpace(item.ColorName) && item.ColorName != item.Name)
|
|
{
|
|
<div class="text-muted small">@item.Name</div>
|
|
}
|
|
</td>
|
|
<td>@(item.Manufacturer ?? "—")</td>
|
|
<td class="text-muted small">@(item.ManufacturerPartNumber ?? "—")</td>
|
|
<td>@(item.Finish ?? "—")</td>
|
|
<td class="text-end pe-3">
|
|
<button class="btn btn-sm btn-outline-danger me-1 btn-toggle-panel"
|
|
data-item-id="@item.Id" data-has-panel="false"
|
|
title="Remove — panel no longer on wall">
|
|
<i class="bi bi-x-lg me-1"></i>Remove
|
|
</button>
|
|
<a asp-action="Details" asp-route-id="@item.Id"
|
|
class="btn btn-sm btn-outline-secondary" title="View item">
|
|
<i class="bi bi-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Print-only need-to-order output -->
|
|
<div id="printArea" style="display:none;">
|
|
<h3 style="font-family:sans-serif;">Sample Panels — Need to Order</h3>
|
|
@if (!string.IsNullOrWhiteSpace(selectedMfr))
|
|
{
|
|
<p style="font-family:sans-serif;font-size:.9rem;color:#666;">Manufacturer: @selectedMfr</p>
|
|
}
|
|
<p style="font-family:sans-serif;font-size:.85rem;color:#666;">Printed @DateTime.Now.ToString("MMMM dd, yyyy")</p>
|
|
<table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:.85rem;">
|
|
<thead>
|
|
<tr style="background:#f0f0f0;">
|
|
<th style="border:1px solid #ccc;padding:6px 10px;text-align:left;">Color</th>
|
|
<th style="border:1px solid #ccc;padding:6px 10px;text-align:left;">Manufacturer</th>
|
|
<th style="border:1px solid #ccc;padding:6px 10px;text-align:left;">Part #</th>
|
|
<th style="border:1px solid #ccc;padding:6px 10px;text-align:left;">Finish</th>
|
|
<th style="border:1px solid #ccc;padding:6px 10px;text-align:left;">In Stock</th>
|
|
<th style="border:1px solid #ccc;padding:6px 10px;text-align:left;">Ordered ✓</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@{ lastMfr = null; }
|
|
@foreach (var item in needOrder)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(selectedMfr) && item.Manufacturer != lastMfr)
|
|
{
|
|
lastMfr = item.Manufacturer;
|
|
<tr>
|
|
<td colspan="6" style="border:1px solid #ccc;padding:4px 10px;background:#f7f7f7;">
|
|
<strong>@(string.IsNullOrWhiteSpace(item.Manufacturer) ? "No Manufacturer" : item.Manufacturer)</strong>
|
|
</td>
|
|
</tr>
|
|
}
|
|
<tr>
|
|
<td style="border:1px solid #ccc;padding:6px 10px;">@(item.ColorName ?? item.Name)</td>
|
|
<td style="border:1px solid #ccc;padding:6px 10px;">@(item.Manufacturer ?? "")</td>
|
|
<td style="border:1px solid #ccc;padding:6px 10px;">@(item.ManufacturerPartNumber ?? "")</td>
|
|
<td style="border:1px solid #ccc;padding:6px 10px;">@(item.Finish ?? "")</td>
|
|
<td style="border:1px solid #ccc;padding:6px 10px;">@(item.QuantityOnHand > 0 ? item.QuantityOnHand.ToString("N2") + " " + item.UnitOfMeasure : "—")</td>
|
|
<td style="border:1px solid #ccc;padding:6px 10px;"> </td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
@section Scripts {
|
|
<script src="~/js/sample-panels.js" asp-append-version="true"></script>
|
|
}
|