* Add LastActivity calculation for convoy dashboard (hq-x2xy) Adds internal/activity package with color-coded activity tracking: - Green: <2 minutes (active) - Yellow: 2-5 minutes (stale) - Red: >5 minutes (stuck) Features: - Calculate() function returns Info with formatted age and color class - Helper methods: IsActive(), IsStale(), IsStuck() - Handles edge cases: zero time, future time (clock skew) Tests: 8 test functions with 25 sub-tests covering all thresholds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add convoy dashboard HTML template with Last Activity (hq-fq1g) Adds internal/web package with convoy dashboard template: - convoy.html with Last Activity column and color coding - Green (<2min), Yellow (2-5min), Red (>5min) activity indicators - htmx auto-refresh every 30 seconds - Progress bars for convoy completion - Status indicators for open/closed convoys - Empty state when no convoys Also includes internal/activity package (dependency from hq-x2xy): - Calculate() returns Info with formatted age and color class - Helper methods: IsActive(), IsStale(), IsStuck() Tests: 6 template tests + 8 activity tests, all passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add convoy list handler with activity data (hq-3edt) Adds HTTP handler that wires convoy dashboard template to real data: - ConvoyHandler: HTTP handler for GET / rendering convoy dashboard - LiveConvoyFetcher: Fetches convoys from beads with activity data - ConvoyFetcher interface: Enables mocking for tests Features: - Fetches open convoys from town beads - Calculates progress (completed/total) from tracked issues - Gets Last Activity from worker agent beads - Color codes activity: Green (<2min), Yellow (2-5min), Red (>5min) Includes dependencies (not yet merged): - internal/activity: Activity calculation (hq-x2xy) - internal/web/templates: HTML template (hq-fq1g) Tests: 5 handler tests + 6 template tests + 8 activity tests = 19 total 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add 'gt dashboard' CLI command (hq-s1bg) Add dashboard command to start the convoy tracking web server. Usage: gt dashboard [--port=8080] [--open] Features: - --port: Configurable HTTP port (default 8080) - --open: Auto-open browser on start - Cross-platform browser launch (darwin/linux/windows) - Graceful workspace detection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
97 lines
2.1 KiB
Go
97 lines
2.1 KiB
Go
// Package web provides HTTP server and templates for the Gas Town dashboard.
|
|
package web
|
|
|
|
import (
|
|
"embed"
|
|
"html/template"
|
|
"io/fs"
|
|
|
|
"github.com/steveyegge/gastown/internal/activity"
|
|
)
|
|
|
|
//go:embed templates/*.html
|
|
var templateFS embed.FS
|
|
|
|
// ConvoyData represents data passed to the convoy template.
|
|
type ConvoyData struct {
|
|
Convoys []ConvoyRow
|
|
}
|
|
|
|
// ConvoyRow represents a single convoy in the dashboard.
|
|
type ConvoyRow struct {
|
|
ID string
|
|
Title string
|
|
Status string // "open" or "closed"
|
|
Progress string // e.g., "2/5"
|
|
Completed int
|
|
Total int
|
|
LastActivity activity.Info
|
|
TrackedIssues []TrackedIssue
|
|
}
|
|
|
|
// TrackedIssue represents an issue tracked by a convoy.
|
|
type TrackedIssue struct {
|
|
ID string
|
|
Title string
|
|
Status string
|
|
Assignee string
|
|
}
|
|
|
|
// LoadTemplates loads and parses all HTML templates.
|
|
func LoadTemplates() (*template.Template, error) {
|
|
// Define template functions
|
|
funcMap := template.FuncMap{
|
|
"activityClass": activityClass,
|
|
"statusClass": statusClass,
|
|
"progressPercent": progressPercent,
|
|
}
|
|
|
|
// Get the templates subdirectory
|
|
subFS, err := fs.Sub(templateFS, "templates")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse all templates
|
|
tmpl, err := template.New("").Funcs(funcMap).ParseFS(subFS, "*.html")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return tmpl, nil
|
|
}
|
|
|
|
// activityClass returns the CSS class for an activity color.
|
|
func activityClass(info activity.Info) string {
|
|
switch info.ColorClass {
|
|
case activity.ColorGreen:
|
|
return "activity-green"
|
|
case activity.ColorYellow:
|
|
return "activity-yellow"
|
|
case activity.ColorRed:
|
|
return "activity-red"
|
|
default:
|
|
return "activity-unknown"
|
|
}
|
|
}
|
|
|
|
// statusClass returns the CSS class for a convoy status.
|
|
func statusClass(status string) string {
|
|
switch status {
|
|
case "open":
|
|
return "status-open"
|
|
case "closed":
|
|
return "status-closed"
|
|
default:
|
|
return "status-unknown"
|
|
}
|
|
}
|
|
|
|
// progressPercent calculates percentage as an integer for progress bars.
|
|
func progressPercent(completed, total int) int {
|
|
if total == 0 {
|
|
return 0
|
|
}
|
|
return (completed * 100) / total
|
|
}
|