Add remaining-weight input mode to inventory scan/usage page
Users can now toggle between 'Amount Used' and 'Remaining Weight' on the QR scan page. In remaining-weight mode, usage is calculated as (current stock - remaining) before submit — no controller changes needed. Includes live hint showing calculated usage and new balance as they type, with validation preventing negative usage or remaining > current stock. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -168,6 +168,23 @@
|
||||
}
|
||||
.reason-pill.selected { border-color: var(--purple); background: #f3effe; color: var(--purple); font-weight: 600; }
|
||||
|
||||
/* ── Input mode toggle ───────────────────────── */
|
||||
.mode-toggle { display: flex; border: 1.5px solid var(--border); border-radius: 8px; overflow: hidden; margin-bottom: 18px; }
|
||||
.mode-btn {
|
||||
flex: 1;
|
||||
padding: 10px 8px;
|
||||
background: #fff;
|
||||
border: none;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background .15s, color .15s;
|
||||
}
|
||||
.mode-btn.active { background: var(--purple); color: #fff; }
|
||||
.mode-btn:first-child { border-right: 1.5px solid var(--border); }
|
||||
|
||||
/* ── Submit / Cancel ─────────────────────────── */
|
||||
.btn-submit {
|
||||
width: 100%;
|
||||
@@ -309,12 +326,28 @@
|
||||
|
||||
<div class="form-card">
|
||||
<h2>2. Enter Quantity</h2>
|
||||
<div class="field">
|
||||
|
||||
<div class="mode-toggle">
|
||||
<button type="button" class="mode-btn active" id="modeUsed" onclick="setMode('used')">Amount Used</button>
|
||||
<button type="button" class="mode-btn" id="modeRemaining" onclick="setMode('remaining')">Remaining Weight</button>
|
||||
</div>
|
||||
|
||||
<!-- amount-used mode -->
|
||||
<div id="usedField" class="field">
|
||||
<label for="quantityInput">Amount Used (@item.UnitOfMeasure) <span class="req">*</span></label>
|
||||
<input type="number" id="quantityInput" name="quantity"
|
||||
min="0" step="any" required placeholder="0" inputmode="decimal" />
|
||||
min="0" step="any" placeholder="0" inputmode="decimal"
|
||||
oninvalid="this.setCustomValidity('')" />
|
||||
<div class="hint" id="balanceHint"></div>
|
||||
</div>
|
||||
|
||||
<!-- remaining-weight mode -->
|
||||
<div id="remainingField" class="field" style="display:none">
|
||||
<label for="remainingInput">Weight Remaining (@item.UnitOfMeasure) <span class="req">*</span></label>
|
||||
<input type="number" id="remainingInput" min="0" step="any"
|
||||
placeholder="0" inputmode="decimal" />
|
||||
<div class="hint" id="remainingHint"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-card">
|
||||
@@ -346,6 +379,21 @@
|
||||
<script>
|
||||
var currentQty = @item.QuantityOnHand;
|
||||
var uom = '@item.UnitOfMeasure';
|
||||
var inputMode = 'used'; // 'used' | 'remaining'
|
||||
|
||||
// ── Input mode toggle ────────────────────────────
|
||||
function setMode(mode) {
|
||||
inputMode = mode;
|
||||
document.getElementById('modeUsed').classList.toggle('active', mode === 'used');
|
||||
document.getElementById('modeRemaining').classList.toggle('active', mode === 'remaining');
|
||||
document.getElementById('usedField').style.display = mode === 'used' ? '' : 'none';
|
||||
document.getElementById('remainingField').style.display = mode === 'remaining' ? '' : 'none';
|
||||
document.getElementById('balanceHint').textContent = '';
|
||||
document.getElementById('remainingHint').textContent = '';
|
||||
// clear both inputs when switching
|
||||
document.getElementById('quantityInput').value = '';
|
||||
document.getElementById('remainingInput').value = '';
|
||||
}
|
||||
|
||||
// ── Job selection ────────────────────────────────
|
||||
function showTab(tab) {
|
||||
@@ -384,7 +432,7 @@
|
||||
document.getElementById('transactionTypeInput').value = el.dataset.val;
|
||||
}
|
||||
|
||||
// ── Balance hint ─────────────────────────────────
|
||||
// ── Balance hint (amount-used mode) ─────────────
|
||||
document.getElementById('quantityInput').addEventListener('input', function() {
|
||||
var qty = parseFloat(this.value) || 0;
|
||||
if (!this.value) { document.getElementById('balanceHint').textContent = ''; return; }
|
||||
@@ -394,6 +442,24 @@
|
||||
'New balance: <strong style="color:' + col + '">' + newBal.toFixed(2) + ' ' + uom + '</strong>';
|
||||
});
|
||||
|
||||
// ── Remaining-weight hint ────────────────────────
|
||||
document.getElementById('remainingInput').addEventListener('input', function() {
|
||||
var hint = document.getElementById('remainingHint');
|
||||
if (!this.value) { hint.textContent = ''; return; }
|
||||
var remaining = parseFloat(this.value);
|
||||
if (isNaN(remaining) || remaining < 0) { hint.innerHTML = '<span style="color:var(--danger)">Enter a valid weight.</span>'; return; }
|
||||
if (remaining > currentQty) {
|
||||
hint.innerHTML = '<span style="color:var(--danger)">Remaining cannot exceed current stock (' + currentQty.toFixed(2) + ' ' + uom + ').</span>';
|
||||
return;
|
||||
}
|
||||
var used = currentQty - remaining;
|
||||
if (used <= 0) {
|
||||
hint.innerHTML = '<span style="color:var(--danger)">No usage to log — remaining equals current stock.</span>';
|
||||
return;
|
||||
}
|
||||
hint.innerHTML = 'Will log <strong>' + used.toFixed(2) + ' ' + uom + '</strong> as used — new balance: <strong style="color:' + (remaining === 0 ? '#343a40' : 'var(--success)') + '">' + remaining.toFixed(2) + ' ' + uom + '</strong>';
|
||||
});
|
||||
|
||||
// ── Preselect job if coming from success page ────
|
||||
@if (preselectedJobId.HasValue)
|
||||
{
|
||||
@@ -406,8 +472,37 @@
|
||||
</text>
|
||||
}
|
||||
|
||||
// ── Submit spinner ───────────────────────────────
|
||||
document.getElementById('usageForm').addEventListener('submit', function() {
|
||||
// ── Submit: resolve quantity from whichever mode is active ──
|
||||
document.getElementById('usageForm').addEventListener('submit', function(e) {
|
||||
if (inputMode === 'remaining') {
|
||||
var remaining = parseFloat(document.getElementById('remainingInput').value);
|
||||
if (isNaN(remaining) || remaining < 0 || remaining > currentQty) {
|
||||
e.preventDefault();
|
||||
document.getElementById('remainingHint').innerHTML =
|
||||
'<span style="color:var(--danger)">Please enter a valid remaining weight.</span>';
|
||||
return;
|
||||
}
|
||||
var used = currentQty - remaining;
|
||||
if (used <= 0) {
|
||||
e.preventDefault();
|
||||
document.getElementById('remainingHint').innerHTML =
|
||||
'<span style="color:var(--danger)">No usage to log — remaining equals current stock.</span>';
|
||||
return;
|
||||
}
|
||||
document.getElementById('quantityInput').value = used.toFixed(4);
|
||||
}
|
||||
|
||||
// validate amount-used mode
|
||||
if (inputMode === 'used') {
|
||||
var qty = parseFloat(document.getElementById('quantityInput').value);
|
||||
if (isNaN(qty) || qty <= 0) {
|
||||
e.preventDefault();
|
||||
document.getElementById('balanceHint').innerHTML =
|
||||
'<span style="color:var(--danger)">Please enter a quantity greater than zero.</span>';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var btn = document.getElementById('submitBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Saving…';
|
||||
|
||||
Reference in New Issue
Block a user