feat(dashboard): add completion stats panel with progress bar
Adds a Completion Stats panel to the Polaris web dashboard showing: - Completion percentage with large display - Visual progress bar (green) - Breakdown of done/active/pending/total tasks Closes #97 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
32f034e2b2
commit
f929c5b6af
1 changed files with 27 additions and 0 deletions
|
|
@ -2128,6 +2128,10 @@ const POLARIS_DASHBOARD_HTML: &str = r##"<!DOCTYPE html>
|
||||||
<p class="meta" id="meta">Loading...</p>
|
<p class="meta" id="meta">Loading...</p>
|
||||||
</header>
|
</header>
|
||||||
<section class="grid">
|
<section class="grid">
|
||||||
|
<article class="panel" id="stats-panel">
|
||||||
|
<h2>Completion Stats</h2>
|
||||||
|
<div id="stats" class="empty">Loading stats...</div>
|
||||||
|
</article>
|
||||||
<article class="panel">
|
<article class="panel">
|
||||||
<h2>Tasks</h2>
|
<h2>Tasks</h2>
|
||||||
<ul id="tasks" class="task-list"><li class="empty">Loading tasks...</li></ul>
|
<ul id="tasks" class="task-list"><li class="empty">Loading tasks...</li></ul>
|
||||||
|
|
@ -2254,6 +2258,27 @@ const POLARIS_DASHBOARD_HTML: &str = r##"<!DOCTYPE html>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderStats(snapshot) {
|
||||||
|
const tasks = Array.isArray(snapshot.tasks) ? snapshot.tasks : [];
|
||||||
|
if (tasks.length === 0) {
|
||||||
|
document.getElementById("stats").innerHTML = '<span class="empty">No tasks</span>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const done = tasks.filter(t => t.current_state === "done" || t.current_state === "merged").length;
|
||||||
|
const active = tasks.filter(t => t.current_state === "implementing" || t.current_state === "reviewing").length;
|
||||||
|
const pending = tasks.filter(t => t.current_state === "pending" || t.current_state === "draft" || t.current_state === "blocked").length;
|
||||||
|
const total = tasks.length;
|
||||||
|
const pct = total > 0 ? Math.round(done / total * 100) : 0;
|
||||||
|
|
||||||
|
const bar = '<div style="background:#e5e7eb;border-radius:8px;height:20px;margin:8px 0;overflow:hidden">' +
|
||||||
|
'<div style="background:#15803d;height:100%;width:' + pct + '%;transition:width .3s"></div></div>';
|
||||||
|
|
||||||
|
document.getElementById("stats").innerHTML =
|
||||||
|
'<div style="font-size:2rem;font-weight:700">' + pct + '%</div>' +
|
||||||
|
'<div class="task-meta">completed</div>' + bar +
|
||||||
|
'<div class="task-meta">' + done + ' done / ' + active + ' active / ' + pending + ' pending / ' + total + ' total</div>';
|
||||||
|
}
|
||||||
|
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
try {
|
try {
|
||||||
const [statusRes, locksRes, dagRes] = await Promise.all([
|
const [statusRes, locksRes, dagRes] = await Promise.all([
|
||||||
|
|
@ -2272,6 +2297,7 @@ const POLARIS_DASHBOARD_HTML: &str = r##"<!DOCTYPE html>
|
||||||
dagRes.json()
|
dagRes.json()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
renderStats(status);
|
||||||
renderTasks(status);
|
renderTasks(status);
|
||||||
renderLocks(locks);
|
renderLocks(locks);
|
||||||
renderDag(dag);
|
renderDag(dag);
|
||||||
|
|
@ -2281,6 +2307,7 @@ const POLARIS_DASHBOARD_HTML: &str = r##"<!DOCTYPE html>
|
||||||
" | auto-refresh every 3s";
|
" | auto-refresh every 3s";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
document.getElementById("meta").textContent = "Refresh failed: " + error.message;
|
document.getElementById("meta").textContent = "Refresh failed: " + error.message;
|
||||||
|
document.getElementById("stats").innerHTML = '<span class="empty">Failed to load</span>';
|
||||||
setEmpty("tasks", "Failed to load tasks");
|
setEmpty("tasks", "Failed to load tasks");
|
||||||
setEmpty("dag", "Failed to load DAG");
|
setEmpty("dag", "Failed to load DAG");
|
||||||
setEmpty("locks", "Failed to load locks");
|
setEmpty("locks", "Failed to load locks");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue