inventory dashboard setup
This commit is contained in:
@@ -44,15 +44,54 @@ import BackToTop from "./BackToTop.astro"
|
||||
|
||||
<BackToTop />
|
||||
|
||||
<!-- <script src="src/assets/js/holofoil-init.js" is:inline></script> -->
|
||||
|
||||
<script is:inline>
|
||||
(function () {
|
||||
|
||||
function initInventoryForms(root = document) {
|
||||
const forms = root.querySelectorAll('[data-inventory-form]');
|
||||
|
||||
forms.forEach((form) => {
|
||||
if (form.dataset.inventoryBound === 'true') return;
|
||||
form.dataset.inventoryBound = 'true';
|
||||
|
||||
const priceInput = form.querySelector('#purchasePrice');
|
||||
const pricePrefix = form.querySelector('#pricePrefix');
|
||||
const priceSuffix = form.querySelector('#priceSuffix');
|
||||
const priceHint = form.querySelector('#priceHint');
|
||||
const modeInputs = form.querySelectorAll('input[name="priceMode"]');
|
||||
|
||||
if (!priceInput || !pricePrefix || !priceSuffix || !priceHint || !modeInputs.length) return;
|
||||
|
||||
function updatePriceMode(mode) {
|
||||
const isPct = mode === '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
|
||||
? 'Enter the percentage of market price you paid.'
|
||||
: 'Enter the purchase price.';
|
||||
|
||||
// swap rounded edge classes based on visible prepend/append
|
||||
priceInput.classList.toggle('rounded-end', !isPct);
|
||||
priceInput.classList.toggle('rounded-start', isPct);
|
||||
}
|
||||
|
||||
modeInputs.forEach((input) => {
|
||||
input.addEventListener('change', () => updatePriceMode(input.value));
|
||||
});
|
||||
|
||||
const checked = form.querySelector('input[name="priceMode"]:checked');
|
||||
updatePriceMode(checked ? checked.value : 'dollar');
|
||||
});
|
||||
}
|
||||
|
||||
// ── Sort dropdown ─────────────────────────────────────────────────────────
|
||||
document.addEventListener('click', (e) => {
|
||||
const sortBy = document.getElementById('sortBy');
|
||||
|
||||
const btn = e.target.closest('#sortBy [data-bs-toggle="dropdown"]');
|
||||
if (btn) {
|
||||
e.preventDefault();
|
||||
@@ -172,15 +211,16 @@ import BackToTop from "./BackToTop.astro"
|
||||
}
|
||||
|
||||
// ── Tab switching helper ──────────────────────────────────────────────────
|
||||
// Called after every modal swap. Checks sessionStorage for a tab request
|
||||
// set by the inventory button click, activates it once, then clears it.
|
||||
function switchToRequestedTab() {
|
||||
const tab = sessionStorage.getItem('openModalTab');
|
||||
if (!tab) return;
|
||||
sessionStorage.removeItem('openModalTab');
|
||||
requestAnimationFrame(() => {
|
||||
const tabEl = document.querySelector(`#cardModal [data-bs-target="#${tab}"]`);
|
||||
if (tabEl) bootstrap.Tab.getOrCreateInstance(tabEl).show();
|
||||
try {
|
||||
const tabEl = document.querySelector(`#cardModal [data-bs-target="#${tab}"]`);
|
||||
if (tabEl) bootstrap.Tab.getOrCreateInstance(tabEl).show();
|
||||
} catch (e) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -271,8 +311,14 @@ import BackToTop from "./BackToTop.astro"
|
||||
|
||||
if (modal._reconnectChartObserver) modal._reconnectChartObserver();
|
||||
|
||||
modal.querySelectorAll('[data-bs-toggle="tab"]').forEach(el => {
|
||||
bootstrap.Tab.getInstance(el)?.dispose();
|
||||
});
|
||||
|
||||
modal.innerHTML = html;
|
||||
|
||||
if (typeof htmx !== 'undefined') htmx.process(modal);
|
||||
initInventoryForms(modal);
|
||||
updateNavButtons(modal);
|
||||
initChartAfterSwap(modal);
|
||||
switchToRequestedTab();
|
||||
@@ -360,8 +406,14 @@ import BackToTop from "./BackToTop.astro"
|
||||
|
||||
if (target._reconnectChartObserver) target._reconnectChartObserver();
|
||||
|
||||
target.querySelectorAll('[data-bs-toggle="tab"]').forEach(el => {
|
||||
bootstrap.Tab.getInstance(el)?.dispose();
|
||||
});
|
||||
|
||||
target.innerHTML = html;
|
||||
|
||||
if (typeof htmx !== 'undefined') htmx.process(target);
|
||||
initInventoryForms(target);
|
||||
|
||||
const destImg = target.querySelector('img.card-image');
|
||||
if (destImg) {
|
||||
@@ -397,12 +449,18 @@ import BackToTop from "./BackToTop.astro"
|
||||
cardModal.addEventListener('shown.bs.modal', () => {
|
||||
updateNavButtons(cardModal);
|
||||
initChartAfterSwap(cardModal);
|
||||
initInventoryForms(cardModal);
|
||||
switchToRequestedTab();
|
||||
});
|
||||
|
||||
cardModal.addEventListener('hidden.bs.modal', () => {
|
||||
currentCardId = null;
|
||||
updateNavButtons(null);
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initInventoryForms();
|
||||
});
|
||||
|
||||
})();
|
||||
</script>
|
||||
37
src/components/InventoryTable.astro
Normal file
37
src/components/InventoryTable.astro
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
const mockInventory = [
|
||||
{ name: "Charizard", set: "Base Set", condition: "NM", qty: 2, price: 350, market: 400, gain: 50 },
|
||||
{ name: "Pikachu", set: "Shining Legends", condition: "LP", qty: 5, price: 15, market: 20, gain: 5 },
|
||||
];
|
||||
---
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Card</th>
|
||||
<th>Set</th>
|
||||
<th>Condition</th>
|
||||
<th>Qty</th>
|
||||
<th>Price</th>
|
||||
<th>Market</th>
|
||||
<th>Gain/Loss</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{mockInventory.map(card => (
|
||||
<tr>
|
||||
<td>{card.name}</td>
|
||||
<td>{card.set}</td>
|
||||
<td>{card.condition}</td>
|
||||
<td>{card.qty}</td>
|
||||
<td>${card.price}</td>
|
||||
<td>${card.market}</td>
|
||||
<td class={card.gain >= 0 ? "text-success" : "text-danger"}>
|
||||
{card.gain >= 0 ? "+" : "-"}${Math.abs(card.gain)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
Reference in New Issue
Block a user