setting up inventory dashboard

This commit is contained in:
Zach Harding
2026-03-25 08:41:21 -04:00
parent 171ce294f4
commit db12844dea
5 changed files with 208 additions and 36 deletions

View File

@@ -226,31 +226,31 @@ const altSearchUrl = (card: any) => {
<ul class="nav nav-tabs nav-fill border-0 me-3 mb-2" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link nm active" id="nm-tab" data-bs-toggle="tab" data-bs-target="#nav-nm" type="button" role="tab" aria-controls="nav-nm" aria-selected="true">
<span class="d-none d-xxl-inline">Near Mint</span><span class="d-xxl-none">NM</span>
<span class="d-none">Near Mint</span><span class="d-inline">NM</span>
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link lp" id="lp-tab" data-bs-toggle="tab" data-bs-target="#nav-lp" type="button" role="tab" aria-controls="nav-lp" aria-selected="false">
<span class="d-none d-xxl-inline">Lightly Played</span><span class="d-xxl-none">LP</span>
<span class="d-none">Lightly Played</span><span class="d-inline">LP</span>
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link mp" id="mp-tab" data-bs-toggle="tab" data-bs-target="#nav-mp" type="button" role="tab" aria-controls="nav-mp" aria-selected="false">
<span class="d-none d-xxl-inline">Moderately Played</span><span class="d-xxl-none">MP</span>
<span class="d-none">Moderately Played</span><span class="d-inline">MP</span>
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link hp" id="hp-tab" data-bs-toggle="tab" data-bs-target="#nav-hp" type="button" role="tab" aria-controls="nav-hp" aria-selected="false">
<span class="d-none d-xxl-inline">Heavily Played</span><span class="d-xxl-none">HP</span>
<span class="d-none">Heavily Played</span><span class="d-inline">HP</span>
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link dmg" id="dmg-tab" data-bs-toggle="tab" data-bs-target="#nav-dmg" type="button" role="tab" aria-controls="nav-dmg" aria-selected="false">
<span class="d-none d-xxl-inline">Damaged</span><span class="d-xxl-none">DMG</span>
<span class="d-none">Damaged</span><span class="d-inline">DMG</span>
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link vendor d-none" id="vendor-tab" data-bs-toggle="tab" data-bs-target="#nav-vendor" type="button" role="tab" aria-controls="nav-vendor" aria-selected="false">
<button class="nav-link vendor" id="vendor-tab" data-bs-toggle="tab" data-bs-target="#nav-vendor" type="button" role="tab" aria-controls="nav-vendor" aria-selected="false">
<span class="d-none d-xxl-inline">Inventory</span><span class="d-xxl-none">+/-</span>
</button>
</li>
@@ -337,7 +337,159 @@ const altSearchUrl = (card: any) => {
);
})}
<div class="tab-pane fade" id="nav-vendor" role="tabpanel" aria-labelledby="nav-vendor" tabindex="0"></div>
<div class="tab-pane fade" id="nav-vendor" role="tabpanel" aria-labelledby="nav-vendor" tabindex="0">
<style>
:root {
--c-nm: 156, 204, 102;
--c-lp: 211, 225, 86;
--c-mp: 255, 238, 87;
--c-hp: 255, 201, 41;
--c-dmg: 255, 167, 36;
}
.btn-check:checked + .btn-cond-nm { background: rgba(var(--c-nm), 1); border-color: rgba(var(--c-nm), 1); color: #2d4a10; }
.btn-check:checked + .btn-cond-lp { background: rgba(var(--c-lp), 1); border-color: rgba(var(--c-lp), 1); color: #3a4310; }
.btn-check:checked + .btn-cond-mp { background: rgba(var(--c-mp), 1); border-color: rgba(var(--c-mp), 1); color: #44420a; }
.btn-check:checked + .btn-cond-hp { background: rgba(var(--c-hp), 1); border-color: rgba(var(--c-hp), 1); color: #4a3608; }
.btn-check:checked + .btn-cond-dmg { background: rgba(var(--c-dmg), 1); border-color: rgba(var(--c-dmg), 1); color: #4a2c08; }
.btn-cond-nm, .btn-cond-lp, .btn-cond-mp, .btn-cond-hp, .btn-cond-dmg {
border: 1px solid rgba(255,255,255,0.15);
color: var(--bs-body-color);
background: transparent;
font-size: 0.8rem;
font-weight: 500;
transition: background 0.1s, border-color 0.1s;
}
.btn-cond-nm:hover { background: rgba(var(--c-nm), 0.2); border-color: rgba(var(--c-nm), 0.6); }
.btn-cond-lp:hover { background: rgba(var(--c-lp), 0.2); border-color: rgba(var(--c-lp), 0.6); }
.btn-cond-mp:hover { background: rgba(var(--c-mp), 0.2); border-color: rgba(var(--c-mp), 0.6); }
.btn-cond-hp:hover { background: rgba(var(--c-hp), 0.2); border-color: rgba(var(--c-hp), 0.6); }
.btn-cond-dmg:hover { background: rgba(var(--c-dmg), 0.2); border-color: rgba(var(--c-dmg), 0.6); }
.price-toggle .btn { font-size: 0.75rem; padding: 0.25rem 0.6rem; line-height: 1; }
</style>
<form id="inventoryForm" novalidate>
<div class="row g-3 mb-3">
<div class="col-4">
<label for="quantity" class="form-label fw-medium">Quantity</label>
<input type="number" class="form-control" id="quantity" name="quantity"
min="1" step="1" value="1" required>
<div class="invalid-feedback">Required.</div>
</div>
<div class="col-8">
<label class="form-label fw-medium">Condition</label>
<div class="btn-group w-100" role="group" aria-label="Condition">
<input type="radio" class="btn-check" name="condition" id="cond-nm" value="Near Mint" autocomplete="off" checked>
<label class="btn btn-cond-nm" for="cond-nm">NM</label>
<input type="radio" class="btn-check" name="condition" id="cond-lp" value="Lightly Played" autocomplete="off">
<label class="btn btn-cond-lp" for="cond-lp">LP</label>
<input type="radio" class="btn-check" name="condition" id="cond-mp" value="Moderately Played" autocomplete="off">
<label class="btn btn-cond-mp" for="cond-mp">MP</label>
<input type="radio" class="btn-check" name="condition" id="cond-hp" value="Heavily Played" autocomplete="off">
<label class="btn btn-cond-hp" for="cond-hp">HP</label>
<input type="radio" class="btn-check" name="condition" id="cond-dmg" value="Damaged" autocomplete="off">
<label class="btn btn-cond-dmg" for="cond-dmg">DMG</label>
</div>
</div>
</div>
<div class="mb-3">
<div class="d-flex justify-content-between align-items-center mb-1">
<label for="purchasePrice" class="form-label fw-medium mb-0">Purchase price</label>
<div class="btn-group btn-group-sm price-toggle" role="group" aria-label="Price mode">
<input type="radio" class="btn-check" name="priceMode" id="mode-dollar" value="dollar" autocomplete="off" checked>
<label class="btn btn-outline-secondary" for="mode-dollar">$ amount</label>
<input type="radio" class="btn-check" name="priceMode" id="mode-percent" value="percent" autocomplete="off">
<label class="btn btn-outline-secondary" for="mode-percent">% of market</label>
</div>
</div>
<div class="input-group">
<span class="input-group-text" id="pricePrefix">$</span>
<input type="number" class="form-control" id="purchasePrice" name="purchasePrice"
min="0" step="0.01" placeholder="0.00"
aria-describedby="pricePrefix priceSuffix priceHint" required>
<span class="input-group-text d-none" id="priceSuffix">%</span>
</div>
<div class="form-text" id="priceHint">Enter the amount you paid.</div>
<div class="invalid-feedback">Enter a purchase price.</div>
</div>
<div class="mb-4">
<label for="note" class="form-label fw-medium">
Note
<span class="text-body-tertiary fw-normal ms-1 small">optional</span>
</label>
<textarea class="form-control" id="note" name="note"
rows="2" maxlength="255"
placeholder="e.g. bought at local shop, gift, graded copy…"></textarea>
<div class="form-text text-end" id="noteCount">0 / 255</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-success flex-fill">Save to inventory</button>
<button type="reset" class="btn btn-outline-secondary">Reset</button>
</div>
</form>
<script>
(function () {
const priceInput = document.getElementById('purchasePrice');
const pricePrefix = document.getElementById('pricePrefix');
const priceSuffix = document.getElementById('priceSuffix');
const priceHint = document.getElementById('priceHint');
const note = document.getElementById('note');
const noteCount = document.getElementById('noteCount');
document.querySelectorAll('input[name="priceMode"]').forEach(radio => {
radio.addEventListener('change', () => {
const isPct = radio.value === 'percent';
pricePrefix.classList.toggle('d-none', isPct);
priceSuffix.classList.toggle('d-none', !isPct);
priceInput.step = isPct ? '1' : '0.01';
priceInput.max = isPct ? '100' : '';
priceInput.placeholder = isPct ? '100' : '0.00';
priceInput.value = '';
priceHint.textContent = isPct
? 'Percentage of the current market price you paid (e.g. 80 = 80%).'
: 'Enter the amount you paid.';
});
});
note.addEventListener('input', () => {
noteCount.textContent = `${note.value.length} / 255`;
});
document.getElementById('inventoryForm').addEventListener('submit', e => {
e.preventDefault();
const form = e.currentTarget;
if (!form.checkValidity()) { form.classList.add('was-validated'); return; }
form.classList.remove('was-validated');
// your save logic here — form data available via new FormData(form)
});
document.getElementById('inventoryForm').addEventListener('reset', () => {
document.getElementById('inventoryForm').classList.remove('was-validated');
noteCount.textContent = '0 / 255';
pricePrefix.classList.remove('d-none');
priceSuffix.classList.add('d-none');
priceInput.step = '0.01';
priceInput.max = '';
priceInput.placeholder = '0.00';
priceHint.textContent = 'Enter the amount you paid.';
document.getElementById('mode-dollar').checked = true;
});
})();
</script>
</div>
</div>
<!-- Chart lives permanently outside tab-content so Bootstrap never hides it. -->