feat(web): comprehensive dashboard control panel with 13 data panels (#931)

* feat(dashboard): comprehensive control panel with expand/collapse

- Add 13 panels: Convoys, Polecats, Sessions, Activity, Mail, Merge Queue,
  Escalations, Rigs, Dogs, System Health, Open Issues, Hooks, Queues
- Add Mayor status banner and Summary/Alerts section
- Implement instant client-side expand/collapse (no page reload)
- Add responsive grid layout for different window sizes
- Parallel data fetching for faster load times
- Color-coded mail by sender, chronological ordering
- Full titles visible in expanded views (no truncation)
- Auto-refresh every 10 seconds via HTMX

* fix(web): update tests and lint for dashboard control panel

- Update MockConvoyFetcher with 11 new interface methods
- Update MockConvoyFetcherWithErrors with matching methods
- Update test assertions for new template structure:
  - Section headers ("Gas Town Convoys" -> "Convoys")
  - Work status badges (badge-green, badge-yellow, badge-red)
  - CI/merge status display text
  - Empty state messages ("No active convoys")
- Fix linting: explicit _, _ = for fmt.Sscanf returns

Tests and linting now pass with the new dashboard features.

* perf(web): add timeouts and error logging to dashboard

Performance and reliability improvements:

- Add 8-second overall fetch timeout to prevent stuck requests
- Add per-command timeouts: 5s for bd/sqlite3, 10s for gh, 2s for tmux
- Add helper functions runCmd() and runBdCmd() with context timeout
- Add error logging for all 14 fetch operations
- Handler now returns partial data if timeout occurs

This addresses slow loading and "stuck" dashboard issues by ensuring
commands cannot hang indefinitely.
This commit is contained in:
Clay Cantrell
2026-01-25 18:00:46 -08:00
committed by GitHub
parent 75739cbaaf
commit aca753296b
6 changed files with 3046 additions and 442 deletions
+16 -19
View File
@@ -54,13 +54,8 @@ func TestConvoyTemplate_RendersConvoyList(t *testing.T) {
t.Error("Template should contain convoy ID hq-cv-def")
}
// Check titles are rendered
if !strings.Contains(output, "Feature X") {
t.Error("Template should contain title 'Feature X'")
}
if !strings.Contains(output, "Bugfix Y") {
t.Error("Template should contain title 'Bugfix Y'")
}
// The simplified dashboard no longer shows convoy titles in the table,
// only the convoy IDs. Titles are shown in expanded view.
}
func TestConvoyTemplate_LastActivityColors(t *testing.T) {
@@ -184,14 +179,16 @@ func TestConvoyTemplate_StatusIndicators(t *testing.T) {
data := ConvoyData{
Convoys: []ConvoyRow{
{
ID: "hq-cv-open",
Title: "Open Convoy",
Status: "open",
ID: "hq-cv-active",
Title: "Active Convoy",
Status: "open",
WorkStatus: "active",
},
{
ID: "hq-cv-closed",
Title: "Closed Convoy",
Status: "closed",
ID: "hq-cv-stuck",
Title: "Stuck Convoy",
Status: "open",
WorkStatus: "stuck",
},
},
}
@@ -204,12 +201,12 @@ func TestConvoyTemplate_StatusIndicators(t *testing.T) {
output := buf.String()
// Check status indicators
if !strings.Contains(output, "status-open") {
t.Error("Template should contain status-open class")
// Check work status badges are rendered (replaced status-open/closed classes)
if !strings.Contains(output, "badge-green") {
t.Error("Template should contain badge-green class for active status")
}
if !strings.Contains(output, "status-closed") {
t.Error("Template should contain status-closed class")
if !strings.Contains(output, "badge-red") {
t.Error("Template should contain badge-red class for stuck status")
}
}
@@ -232,7 +229,7 @@ func TestConvoyTemplate_EmptyState(t *testing.T) {
output := buf.String()
// Check for empty state message
if !strings.Contains(output, "No convoys") {
if !strings.Contains(output, "No active convoys") {
t.Error("Template should show empty state message when no convoys")
}
}