Promote job powders to top of Log Material dropdown
Powders already assigned to this job's coats appear under a 'This Job' section header, then a divider, then 'All Inventory' — so the most relevant choices are always one click away. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -503,9 +503,17 @@ public class JobsController : Controller
|
|||||||
.OrderBy(i => i.Name)
|
.OrderBy(i => i.Name)
|
||||||
.Select(i => new { i.Id, i.Name, i.Manufacturer, i.UnitOfMeasure, i.QuantityOnHand })
|
.Select(i => new { i.Id, i.Name, i.Manufacturer, i.UnitOfMeasure, i.QuantityOnHand })
|
||||||
.ToList();
|
.ToList();
|
||||||
ViewBag.InventoryItemsForModal = System.Text.Json.JsonSerializer.Serialize(
|
var jsonOpts = new System.Text.Json.JsonSerializerOptions { PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase };
|
||||||
inventoryItemsForModal,
|
ViewBag.InventoryItemsForModal = System.Text.Json.JsonSerializer.Serialize(inventoryItemsForModal, jsonOpts);
|
||||||
new System.Text.Json.JsonSerializerOptions { PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase });
|
|
||||||
|
// IDs of powders already assigned to this job's coats — shown at top of log-material dropdown
|
||||||
|
var jobPowderIds = (jobDto.Items ?? new List<PowderCoating.Application.DTOs.Job.JobItemDto>())
|
||||||
|
.SelectMany(i => i.Coats ?? new List<PowderCoating.Application.DTOs.Job.JobItemCoatDto>())
|
||||||
|
.Where(c => c.InventoryItemId.HasValue)
|
||||||
|
.Select(c => c.InventoryItemId!.Value)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
ViewBag.JobPowderIds = System.Text.Json.JsonSerializer.Serialize(jobPowderIds, jsonOpts);
|
||||||
|
|
||||||
// Pre-logged powder grouped by InventoryItemId (for Complete Job modal pre-fill)
|
// Pre-logged powder grouped by InventoryItemId (for Complete Job modal pre-fill)
|
||||||
ViewBag.PreLoggedPowder = allJobTransactions
|
ViewBag.PreLoggedPowder = allJobTransactions
|
||||||
|
|||||||
@@ -3162,10 +3162,11 @@
|
|||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const inventoryItems = @Html.Raw(ViewBag.InventoryItemsForModal ?? "[]");
|
const inventoryItems = @Html.Raw(ViewBag.InventoryItemsForModal ?? "[]");
|
||||||
|
const jobPowderIds = @Html.Raw(ViewBag.JobPowderIds ?? "[]");
|
||||||
const jobId = @Model.Id;
|
const jobId = @Model.Id;
|
||||||
const logUrl = '@Url.Action("LogMaterial", "Jobs")';
|
const logUrl = '@Url.Action("LogMaterial", "Jobs")';
|
||||||
const token = document.querySelector('input[name="__RequestVerificationToken"]')?.value ?? '';
|
const token = document.querySelector('input[name="__RequestVerificationToken"]')?.value ?? '';
|
||||||
window.__logMaterial = { inventoryItems, jobId, logUrl, token };
|
window.__logMaterial = { inventoryItems, jobPowderIds, jobId, logUrl, token };
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
let _items = [];
|
let _items = [];
|
||||||
|
let _jobPowderIds = new Set();
|
||||||
let _modal = null;
|
let _modal = null;
|
||||||
|
|
||||||
// ── Combobox state ────────────────────────────────────────────────────────
|
// ── Combobox state ────────────────────────────────────────────────────────
|
||||||
@@ -35,6 +36,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lmMakeRow(it) {
|
||||||
|
const label = it.name + (it.unitOfMeasure ? ' (' + it.unitOfMeasure + ')' : '');
|
||||||
|
const mfr = it.manufacturer
|
||||||
|
? `<span class="text-muted ms-1" style="font-size:.78rem;">${escLm(it.manufacturer)}</span>`
|
||||||
|
: '';
|
||||||
|
return `<div class="lm-item-opt" style="padding:.35rem .75rem;font-size:.875rem;cursor:pointer;"
|
||||||
|
data-id="${it.id}"
|
||||||
|
data-qty="${it.quantityOnHand}"
|
||||||
|
data-uom="${escLm(it.unitOfMeasure || '')}"
|
||||||
|
data-label="${escLm(label)}"
|
||||||
|
onmousedown="event.preventDefault(); lmComboSelect(this)"
|
||||||
|
onmouseenter="this.style.background='#f0f4ff'"
|
||||||
|
onmouseleave="this.classList.contains('lm-active') ? null : this.style.background=''">
|
||||||
|
${escLm(label)}${mfr}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
function lmComboRender(query) {
|
function lmComboRender(query) {
|
||||||
const dd = document.getElementById('lmItemDropdown');
|
const dd = document.getElementById('lmItemDropdown');
|
||||||
if (!dd) return;
|
if (!dd) return;
|
||||||
@@ -47,20 +65,21 @@
|
|||||||
dd.innerHTML = '<div class="px-3 py-2 text-muted small">No items match.</div>';
|
dd.innerHTML = '<div class="px-3 py-2 text-muted small">No items match.</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dd.innerHTML = filtered.map(it => {
|
|
||||||
const label = it.name + (it.unitOfMeasure ? ' (' + it.unitOfMeasure + ')' : '');
|
const jobItems = filtered.filter(it => _jobPowderIds.has(it.id));
|
||||||
const mfr = it.manufacturer ? `<span class="text-muted ms-1" style="font-size:.78rem;">${escLm(it.manufacturer)}</span>` : '';
|
const otherItems = filtered.filter(it => !_jobPowderIds.has(it.id));
|
||||||
return `<div class="lm-item-opt" style="padding:.35rem .75rem;font-size:.875rem;cursor:pointer;"
|
|
||||||
data-id="${it.id}"
|
let html = '';
|
||||||
data-qty="${it.quantityOnHand}"
|
if (jobItems.length > 0) {
|
||||||
data-uom="${escLm(it.unitOfMeasure || '')}"
|
html += '<div class="px-3 py-1 text-muted" style="font-size:.72rem;letter-spacing:.04em;text-transform:uppercase;background:#f8f9fa;border-bottom:1px solid #dee2e6;">This Job</div>';
|
||||||
data-label="${escLm(label)}"
|
html += jobItems.map(lmMakeRow).join('');
|
||||||
onmousedown="event.preventDefault(); lmComboSelect(this)"
|
if (otherItems.length > 0) {
|
||||||
onmouseenter="this.style.background='#f0f4ff'"
|
html += '<div style="height:1px;background:#dee2e6;margin:.25rem 0;"></div>';
|
||||||
onmouseleave="this.classList.contains('lm-active') ? null : this.style.background=''">
|
html += '<div class="px-3 py-1 text-muted" style="font-size:.72rem;letter-spacing:.04em;text-transform:uppercase;background:#f8f9fa;border-bottom:1px solid #dee2e6;">All Inventory</div>';
|
||||||
${escLm(label)}${mfr}
|
}
|
||||||
</div>`;
|
}
|
||||||
}).join('');
|
html += otherItems.map(lmMakeRow).join('');
|
||||||
|
dd.innerHTML = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lmComboShow() {
|
function lmComboShow() {
|
||||||
@@ -245,6 +264,7 @@
|
|||||||
if (!cfg) return;
|
if (!cfg) return;
|
||||||
|
|
||||||
_items = cfg.inventoryItems || [];
|
_items = cfg.inventoryItems || [];
|
||||||
|
_jobPowderIds = new Set(cfg.jobPowderIds || []);
|
||||||
_modal = new bootstrap.Modal(document.getElementById('logMaterialModal'));
|
_modal = new bootstrap.Modal(document.getElementById('logMaterialModal'));
|
||||||
|
|
||||||
document.getElementById('lmQuantity').addEventListener('input', lmOnQtyInput);
|
document.getElementById('lmQuantity').addEventListener('input', lmOnQtyInput);
|
||||||
|
|||||||
Reference in New Issue
Block a user