/** * 3D Print Demand dashboard panel for InvenTree. * * Fetches aggregated demand data from the plugin API and renders * a colour-coded table showing stock vs demand for every 3D-printed part. */ export function renderDashboardItem(target, data) { if (!target) { console.error("No target provided to renderDashboardItem"); return; } target.innerHTML = 'Loading 3D print demand data...'; const headers = { 'Accept': 'application/json', }; // Include CSRF token if available const csrfToken = getCookie('csrftoken'); if (csrfToken) { headers['X-CSRFToken'] = csrfToken; } fetch('/plugin/print-demand/api/demand/', { headers, credentials: 'same-origin' }) .then(response => { if (!response.ok) { return response.json().then(data => { throw new Error(data.error || response.statusText); }); } return response.json(); }) .then(data => { if (!data.length) { target.innerHTML = 'No parts found in the configured category.'; return; } target.innerHTML = buildTable(data); }) .catch(err => { target.innerHTML = `Error: ${escapeHtml(err.message)}`; }); } function buildTable(parts) { const rows = parts.map(p => { const deficitStyle = p.deficit < 0 ? 'color:#c00;font-weight:bold;' : (p.deficit > 0 ? 'color:#080;' : ''); return ` ${escapeHtml(p.IPN ? p.IPN + ' - ' + p.name : p.name)} ${fmt(p.in_stock)} ${fmt(p.allocated_build + p.allocated_sales)} ${fmt(p.available)} ${fmt(p.required_build + p.required_sales)} ${fmt(p.deficit)} `; }).join(''); return `
${rows}
Part In Stock Allocated Available Required Deficit
`; } function fmt(n) { return Number.isInteger(n) ? n.toString() : n.toFixed(1); } function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } function getCookie(name) { const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); return match ? decodeURIComponent(match[2]) : null; }