Dashboard: 'Interns only' filter for the table (persisted; charts stay whole-team)
Browse files
app.py
CHANGED
|
@@ -315,6 +315,12 @@ DASHBOARD_HTML = """<!DOCTYPE html>
|
|
| 315 |
border-radius: 99px; padding: 7px 14px; font-size: 13px; cursor: pointer;
|
| 316 |
display: flex; align-items: center; gap: 6px; white-space: nowrap; }
|
| 317 |
.theme-toggle:hover { border-color: var(--accent); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
/* ---- charts ---- */
|
| 319 |
.charts { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 28px; }
|
| 320 |
.chart-card { background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
|
|
@@ -376,7 +382,12 @@ DASHBOARD_HTML = """<!DOCTYPE html>
|
|
| 376 |
<h1>ASR Annotation — Team Progress</h1>
|
| 377 |
<div class="sub">Live dashboard · auto-refreshes every 15s · <b id="count">0</b> annotators reporting</div>
|
| 378 |
</div>
|
| 379 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 380 |
</div>
|
| 381 |
<div class="charts" id="charts"></div>
|
| 382 |
<div id="table-wrap"></div>
|
|
@@ -417,6 +428,16 @@ themeBtn.addEventListener("click", () => {
|
|
| 417 |
});
|
| 418 |
renderThemeBtn();
|
| 419 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
function fmtTime(ms) {
|
| 421 |
if (!ms) return "—";
|
| 422 |
const d = new Date(ms);
|
|
@@ -583,7 +604,17 @@ async function refresh() {
|
|
| 583 |
const podium = sortDir === "desc" && PODIUM_KEYS.has(sortKey);
|
| 584 |
const MEDALS = ["gold", "silver", "bronze"];
|
| 585 |
|
| 586 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 587 |
const c = r.counts || {};
|
| 588 |
const p = pct(c, r.total || 0);
|
| 589 |
const rank = i + 1;
|
|
|
|
| 315 |
border-radius: 99px; padding: 7px 14px; font-size: 13px; cursor: pointer;
|
| 316 |
display: flex; align-items: center; gap: 6px; white-space: nowrap; }
|
| 317 |
.theme-toggle:hover { border-color: var(--accent); }
|
| 318 |
+
.topbar-controls { display: flex; align-items: center; gap: 10px; }
|
| 319 |
+
.filter-toggle { display: flex; align-items: center; gap: 7px; cursor: pointer;
|
| 320 |
+
background: var(--panel); color: var(--text); border: 1px solid var(--line);
|
| 321 |
+
border-radius: 99px; padding: 7px 14px; font-size: 13px; white-space: nowrap; }
|
| 322 |
+
.filter-toggle:hover { border-color: var(--accent); }
|
| 323 |
+
.filter-toggle input { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; }
|
| 324 |
/* ---- charts ---- */
|
| 325 |
.charts { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 28px; }
|
| 326 |
.chart-card { background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
|
|
|
|
| 382 |
<h1>ASR Annotation — Team Progress</h1>
|
| 383 |
<div class="sub">Live dashboard · auto-refreshes every 15s · <b id="count">0</b> annotators reporting</div>
|
| 384 |
</div>
|
| 385 |
+
<div class="topbar-controls">
|
| 386 |
+
<label class="filter-toggle" title="Show only annotators marked as interns">
|
| 387 |
+
<input type="checkbox" id="interns-only"> Interns only
|
| 388 |
+
</label>
|
| 389 |
+
<button class="theme-toggle" id="theme-toggle" title="Toggle light / dark"></button>
|
| 390 |
+
</div>
|
| 391 |
</div>
|
| 392 |
<div class="charts" id="charts"></div>
|
| 393 |
<div id="table-wrap"></div>
|
|
|
|
| 428 |
});
|
| 429 |
renderThemeBtn();
|
| 430 |
|
| 431 |
+
// ---- Interns-only filter (persisted) ----
|
| 432 |
+
let internsOnly = localStorage.getItem("asr_dash_interns_only") === "1";
|
| 433 |
+
const internsBox = document.getElementById("interns-only");
|
| 434 |
+
internsBox.checked = internsOnly;
|
| 435 |
+
internsBox.addEventListener("change", () => {
|
| 436 |
+
internsOnly = internsBox.checked;
|
| 437 |
+
localStorage.setItem("asr_dash_interns_only", internsOnly ? "1" : "");
|
| 438 |
+
refresh();
|
| 439 |
+
});
|
| 440 |
+
|
| 441 |
function fmtTime(ms) {
|
| 442 |
if (!ms) return "—";
|
| 443 |
const d = new Date(ms);
|
|
|
|
| 604 |
const podium = sortDir === "desc" && PODIUM_KEYS.has(sortKey);
|
| 605 |
const MEDALS = ["gold", "silver", "bronze"];
|
| 606 |
|
| 607 |
+
// "Interns only" filters the table rows (charts + team total stay whole-team).
|
| 608 |
+
const tableReports = internsOnly ? reports.filter(r => r.intern) : reports;
|
| 609 |
+
if (!tableReports.length) {
|
| 610 |
+
document.getElementById("table-wrap").innerHTML =
|
| 611 |
+
'<div class="empty">No interns are reporting yet.</div>';
|
| 612 |
+
document.getElementById("updated").textContent =
|
| 613 |
+
"Last refreshed " + new Date().toLocaleTimeString();
|
| 614 |
+
return;
|
| 615 |
+
}
|
| 616 |
+
|
| 617 |
+
const rows = tableReports.map((r, i) => {
|
| 618 |
const c = r.counts || {};
|
| 619 |
const p = pct(c, r.total || 0);
|
| 620 |
const rank = i + 1;
|