From da71a1bface77cc892b3f49c011ddf3ed93395ac Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 25 Feb 2026 23:43:44 +0100 Subject: [PATCH] Dashboard: make status cards interactive links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Active Workstreams → navigates to ./workstreams page - Blocking Decisions → anchor-scrolls to #blocking-decisions section - Blocked Tasks → click toggles inline panel showing each blocked task with workstream name and blocking reason; label toggles expand/collapse - Events Today → anchor-scrolls to #recent-activity section - All cards get hover lift effect (box-shadow + 1px translateY) Co-Authored-By: Claude Sonnet 4.6 --- dashboard/src/index.md | 80 +++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/dashboard/src/index.md b/dashboard/src/index.md index cd76269..f53b2cc 100644 --- a/dashboard/src/index.md +++ b/dashboard/src/index.md @@ -83,29 +83,60 @@ if (summary.error) display(html`
⚠️ ${summary.error} -
-

Active Workstreams

-

${ws.active ?? 0}

- ${ws.blocked ?? 0} blocked +const blockedTasks = summary.blocked_tasks ?? []; +const wsById = Object.fromEntries((summary.open_workstreams ?? []).map(w => [w.id, w])); +const todayCount = (summary.recent_progress ?? []).filter(e => + e.created_at?.startsWith(new Date().toISOString().slice(0, 10))).length; +const decCount = (decisions.open ?? 0) + (decisions.escalated ?? 0); + +const statusEl = html`
+ -
-

Blocking Decisions

-

${(decisions.open ?? 0) + (decisions.escalated ?? 0)}

- ${decisions.escalated ?? 0} escalated + + -
-

Blocked Tasks

-

${tasks.blocked ?? 0}

- of ${tasks.total ?? 0} total -
-
-

Events Today

-

${(summary.recent_progress ?? []).filter(e => - e.created_at?.startsWith(new Date().toISOString().slice(0,10))).length}

- last 20 shown below -
-
`); +
`; + +statusEl.querySelector('[data-toggle="blocked-panel"]').addEventListener('click', () => { + const panel = statusEl.querySelector('#blocked-panel'); + const isOpen = panel.style.display !== 'none'; + panel.style.display = isOpen ? 'none' : 'block'; + statusEl.querySelector('[data-toggle="blocked-panel"] small').textContent = + isOpen ? `of ${tasks.total ?? 0} total · click to expand` : `of ${tasks.total ?? 0} total · click to collapse`; +}); + +display(statusEl); ``` ## What's next? @@ -367,6 +398,13 @@ display(Inputs.table((summary.recent_progress ?? []).map(e => ({ .live-bar { font-size: 0.8rem; color: gray; text-align: right; margin-bottom: 0.5rem; } .card { background: var(--theme-background-alt); border-radius: 8px; padding: 1rem; } .card.warn { border: 2px solid orange; } +.card-link { cursor: pointer; transition: box-shadow 0.15s, transform 0.1s; text-decoration: none; color: inherit; display: block; } +.card-link:hover { box-shadow: 0 3px 10px rgba(0,0,0,0.13); transform: translateY(-1px); } +.bt-list { display: flex; flex-direction: column; gap: 0.5rem; } +.bt-row { background: var(--theme-background-alt); border-radius: 6px; padding: 0.6rem 0.9rem; border-left: 3px solid #ff7043; } +.bt-meta { font-size: 0.7rem; color: gray; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 0.15rem; } +.bt-title { font-weight: 600; font-size: 0.9rem; } +.bt-reason { font-size: 0.8rem; color: #b45309; margin-top: 0.25rem; } .big-num { font-size: 2.5rem; font-weight: bold; margin: 0.25rem 0; } .warning { background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 0.75rem; } .hint-box { background: var(--theme-background-alt); border-left: 3px solid steelblue; border-radius: 4px; padding: 0.75rem 1rem; margin-top: 0.75rem; font-size: 0.9rem; }