102 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 `
<section class="b2-kpis" aria-label="Kennzahlen">
<div class="b2-kpi b2-glass" id="kpiOpen">
<div class="b2-lbl">Open</div>
<div class="b2-val"><span data-k="count"></span> <small data-k="total"></small></div>
<div class="b2-miniarea" data-k="area"></div>
</div>
<div class="b2-kpi b2-glass" id="kpiInProgress">
<div class="b2-lbl">In Progress</div>
<div class="b2-val"><span data-k="count"></span></div>
<div class="b2-chips" data-k="chips"></div>
</div>
<div class="b2-kpi b2-glass" id="kpiReview">
<div class="b2-lbl">Review</div>
<div class="b2-val"><span data-k="count"></span></div>
<div class="b2-chips" data-k="chips"></div>
</div>
<div class="b2-kpi b2-glass" id="kpiDone">
<div class="b2-lbl">Done</div>
<div class="b2-val"><span data-k="count"></span> <small data-k="total"></small></div>
<div class="b2-cap-r" data-k="cap"></div>
<div class="b2-pbar"><i data-k="bar"></i></div>
</div>
</section>`;
}
/**
* 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 = '<svg width="100%" height="26" viewBox="0 0 120 26" preserveAspectRatio="none">'
+ '<path fill="rgba(148,163,184,.18)" d="' + p.area + '"/>'
+ '<path class="b2-spark" fill="none" stroke="#94a3b8" stroke-width="1.5" d="' + p.line + '"/></svg>';
});
// 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' ? '<span class="b2-cdot"></span>' : '';
return '<span class="b2-achip"><i style="background:' + color + '">' + initial + '</i>' + dot + time + '</span>';
}).join('') + (capped.hidden > 0 ? '<span class="b2-achip b2-more">+' + capped.hidden + '</span>' : '');
});
});
// 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 + '%'; });
};`;
}