inventory dashboard setup
This commit is contained in:
@@ -2,51 +2,97 @@ import * as bootstrap from 'bootstrap';
|
||||
window.bootstrap = bootstrap;
|
||||
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
|
||||
|
||||
// trap browser back and close the modal if open
|
||||
const cardModal = document.getElementById('cardModal');
|
||||
const loadingMsg = cardModal.innerHTML;
|
||||
// Push a new history state when the modal is shown
|
||||
cardModal.addEventListener('shown.bs.modal', () => {
|
||||
history.pushState({ modalOpen: true }, null, '#cardModal');
|
||||
});
|
||||
// Listen for the browser's back button (popstate event)
|
||||
window.addEventListener('popstate', (e) => {
|
||||
if (cardModal.classList.contains('show')) {
|
||||
const modalInstance = bootstrap.Modal.getInstance(cardModal);
|
||||
if (modalInstance) {
|
||||
modalInstance.hide();
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Initialize all Bootstrap modals
|
||||
document.querySelectorAll('.modal').forEach(modalEl => {
|
||||
bootstrap.Modal.getOrCreateInstance(modalEl);
|
||||
});
|
||||
|
||||
// Initialize tooltips
|
||||
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
|
||||
if (!el._tooltipInstance) {
|
||||
el._tooltipInstance = new bootstrap.Tooltip(el, { container: 'body' });
|
||||
}
|
||||
}
|
||||
});
|
||||
// Trigger a back navigation when the modal is closed via its native controls (X, backdrop click)
|
||||
cardModal.addEventListener('hide.bs.modal', () => {
|
||||
cardModal.innerHTML = loadingMsg;
|
||||
if (history.state && history.state.modalOpen) {
|
||||
history.back();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------- DASHBOARD LOGIC ----------------
|
||||
const toggleBtn = document.getElementById("toggleViewBtn");
|
||||
const gridView = document.getElementById("gridView");
|
||||
const tableView = document.getElementById("tableView");
|
||||
const searchInput = document.getElementById("inventorySearch");
|
||||
const tbody = document.getElementById("inventoryRows");
|
||||
|
||||
import { Tooltip } from "bootstrap";
|
||||
|
||||
// Initialize all tooltips globally
|
||||
const initTooltips = () => {
|
||||
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
|
||||
if (!el._tooltipInstance) {
|
||||
el._tooltipInstance = new Tooltip(el, {
|
||||
container: 'body', // ensures tooltip is appended to body, important for modals
|
||||
});
|
||||
if(toggleBtn && gridView && tableView && tbody) {
|
||||
// TOGGLE GRID/TABLE
|
||||
toggleBtn.addEventListener("click", () => {
|
||||
if(gridView.style.display !== "none") {
|
||||
gridView.style.display = "none";
|
||||
tableView.style.display = "block";
|
||||
toggleBtn.textContent = "Switch to Grid View";
|
||||
} else {
|
||||
gridView.style.display = "block";
|
||||
tableView.style.display = "none";
|
||||
toggleBtn.textContent = "Switch to Table View";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Run on page load
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initTooltips);
|
||||
} else {
|
||||
initTooltips();
|
||||
// SEARCH FILTER
|
||||
if(searchInput) {
|
||||
searchInput.addEventListener("input", e => {
|
||||
const term = e.target.value.toLowerCase();
|
||||
[...tbody.querySelectorAll("tr")].forEach(row => {
|
||||
row.style.display = row.textContent.toLowerCase().includes(term) ? "" : "none";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// SORTING
|
||||
document.querySelectorAll("th[data-key]").forEach(th => {
|
||||
let sortAsc = true;
|
||||
th.addEventListener("click", () => {
|
||||
const key = th.dataset.key;
|
||||
const indexMap = {name:0,set:1,condition:2,qty:3,price:4,market:5,gain:6};
|
||||
const idx = indexMap[key];
|
||||
const rows = [...tbody.querySelectorAll("tr")];
|
||||
|
||||
rows.sort((a,b) => {
|
||||
let aText = a.children[idx].textContent.replace(/\$|,/g,'').toLowerCase();
|
||||
let bText = b.children[idx].textContent.replace(/\$|,/g,'').toLowerCase();
|
||||
if(!isNaN(aText) && !isNaN(bText)) return sortAsc ? aText-bText : bText-aText;
|
||||
return sortAsc ? aText.localeCompare(bText) : bText.localeCompare(aText);
|
||||
});
|
||||
|
||||
sortAsc = !sortAsc;
|
||||
tbody.innerHTML="";
|
||||
rows.forEach(r => tbody.appendChild(r));
|
||||
});
|
||||
});
|
||||
|
||||
// INLINE EDITING + GAIN/LOSS UPDATE
|
||||
tbody.addEventListener("input", e => {
|
||||
const row = e.target.closest("tr");
|
||||
if(!row) return;
|
||||
|
||||
const priceCell = row.querySelector(".editable-price");
|
||||
const qtyCell = row.querySelector(".editable-qty");
|
||||
const marketCell = row.children[5];
|
||||
const gainCell = row.querySelector(".gain");
|
||||
|
||||
if(e.target.classList.contains("editable-price")) {
|
||||
e.target.textContent = e.target.textContent.replace(/[^\d.]/g,"");
|
||||
}
|
||||
if(e.target.classList.contains("editable-qty")) {
|
||||
e.target.textContent = e.target.textContent.replace(/\D/g,"");
|
||||
}
|
||||
|
||||
const price = parseFloat(priceCell.textContent) || 0;
|
||||
const qty = parseInt(qtyCell.textContent) || 0;
|
||||
const market = parseFloat(marketCell.textContent) || 0;
|
||||
const gain = market - price;
|
||||
|
||||
gainCell.textContent = (gain>=0 ? "+" : "-") + Math.abs(gain);
|
||||
gainCell.className = gain>=0 ? "gain text-success" : "gain text-danger";
|
||||
});
|
||||
}
|
||||
|
||||
// Optional: observe DOM changes for dynamically added tooltips (e.g., modals loaded later)
|
||||
const observer = new MutationObserver(() => initTooltips());
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
});
|
||||
Reference in New Issue
Block a user