import { capChips, doneStats, backlogSeries, areaPath, laneChips, dayStart, } from './viewmodel.js'; /** Static skeleton: four KPI cards; values are filled client-side. */ export function kpiSkeletonHtml(): string { return `
Open
In Progress
Review
Done
`; } /** * Inline script: injects the pure viewmodel helpers (toString) plus the shared * DAY_MS/dayStart they reference, and defines window.__b2UpdateKpis(tasks, * agentColor), called by the data layer on every refresh. agentColor(name) * comes from the columns module (existing per-agent palette). */ export function kpiJs(): string { return ` var DAY_MS = 24 * 3600 * 1000; ${dayStart.toString()} ${capChips.toString()} ${doneStats.toString()} ${backlogSeries.toString()} ${areaPath.toString()} ${laneChips.toString()} window.__b2UpdateKpis = function (tasks, agentColor) { var kpiPrev = window.__b2KpiPrev || (window.__b2KpiPrev = {}); function flashKpi(card, value) { if (kpiPrev[card] !== undefined && kpiPrev[card] !== value) { var cardEl = document.getElementById(card); if (cardEl) { cardEl.classList.remove('metric-flash'); void cardEl.offsetWidth; cardEl.classList.add('metric-flash'); } } kpiPrev[card] = value; } function setK(card, key, fn) { var el = document.querySelector('#' + card + ' [data-k="' + key + '"]'); if (el) fn(el); } // Open var open = tasks.filter(function (t) { return t.status === 'open'; }); setK('kpiOpen', 'count', function (el) { el.textContent = String(open.length); }); flashKpi('kpiOpen', String(open.length)); setK('kpiOpen', 'total', function (el) { el.textContent = '/ ' + tasks.filter(function (t) { return t.status !== 'cancelled'; }).length; }); setK('kpiOpen', 'area', function (el) { var p = areaPath(backlogSeries(tasks, 14), 120, 26); el.innerHTML = '' + '' + ''; }); // In Progress + Review chips (max 3, then +n) ['in_progress', 'review'].forEach(function (status) { var card = status === 'review' ? 'kpiReview' : 'kpiInProgress'; var chips = laneChips(tasks, status); var capped = capChips(chips, 3); setK(card, 'count', function (el) { el.textContent = String(chips.length); }); flashKpi(card, String(chips.length)); setK(card, 'chips', function (el) { el.innerHTML = capped.visible.map(function (c) { var color = agentColor ? agentColor(c.name) : '#a5b4fc'; var initial = c.name.charAt(0).toUpperCase(); var time = c.minutes == null ? '' : (status === 'review' ? 'prüft ' : '') + c.minutes + 'm'; var dot = status === 'in_progress' ? '' : ''; return '' + initial + '' + dot + time + ''; }).join('') + (capped.hidden > 0 ? '+' + capped.hidden + '' : ''); }); }); // Done var ds = doneStats(tasks); setK('kpiDone', 'count', function (el) { el.textContent = String(ds.done); }); flashKpi('kpiDone', String(ds.done)); setK('kpiDone', 'total', function (el) { el.textContent = '/ ' + ds.total; }); setK('kpiDone', 'cap', function (el) { el.textContent = ds.pct + '% · +' + ds.doneThisWeek + ' diese Woche'; }); setK('kpiDone', 'bar', function (el) { el.style.width = ds.pct + '%'; }); };`; }