// Parts Catalog: read-only stock view from the seeded inventory read-model.
// Low-stock flags + counts are computed server-side; category filter is client-side.
const money = (n) => (n == null ? "—" : `$${Number(n).toFixed(2)}`);
const el = (html) => {
const t = document.createElement("template");
t.innerHTML = html.trim();
return t.content.firstElementChild;
};
let parts = [];
let activeCat = "ALL";
function stockBar(p) {
const pct =
p.reorder_at > 0 ? Math.min(100, Math.round((p.stock / (p.reorder_at * 3)) * 100)) : 100;
const status = p.reorder_at === 0 ? "Service" : p.low ? "LOW" : "OK";
return `
${p.stock} ${p.unit}${status}
`;
}
function render() {
const host = document.getElementById("parts");
host.innerHTML = "";
const shown = activeCat === "ALL" ? parts : parts.filter((p) => p.category === activeCat);
const rows = shown
.map(
(p) => `
| ${p.description} |
${p.category} |
${stockBar(p)} |
${money(p.rate)} |
${p.low ? 'Reorder' : 'In stock'} |
`,
)
.join("");
host.append(
el(`
| Part | Category | Stock | Unit price | Status |
${rows}
`),
);
}
async function load() {
const data = await (await fetch("/api/inventory")).json();
parts = data.parts;
document.getElementById("kpis").append(
el(`Total SKUs
${data.total_skus}
in the catalog
`),
el(`Low stock
${data.low_stock_count}
at or below reorder point
`),
);
const cats = ["ALL", ...new Set(parts.map((p) => p.category))];
const filters = document.getElementById("filters");
cats.forEach((c) => {
const b = el(``);
b.addEventListener("click", () => {
activeCat = c;
filters.querySelectorAll(".filter").forEach((f) => f.classList.toggle("active", f === b));
render();
});
filters.append(b);
});
render();
}
load();