The status column now shows computed work status based on progress and activity: - "complete" (green) - all tracked items are done - "active" (green) - recent polecat activity (within 1 min) - "stale" (yellow) - older activity (1-5 min) - "stuck" (red) - stale activity (5+ min) - "waiting" (gray) - no assignee/activity Previously the status column always showed "open" since we only fetch open convoys, making it static and uninformative. Changes: - templates.go: Add WorkStatus field to ConvoyRow, add workStatusClass func - fetcher.go: Add calculateWorkStatus() to compute status from progress/activity - convoy.html: Add work status badge styling, use WorkStatus in table 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
139 lines
3.3 KiB
Go
139 lines
3.3 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
|
|
MergeQueue []MergeQueueRow
|
|
Polecats []PolecatRow
|
|
}
|
|
|
|
// PolecatRow represents a polecat worker in the dashboard.
|
|
type PolecatRow struct {
|
|
Name string // e.g., "dag", "nux"
|
|
Rig string // e.g., "roxas", "gastown"
|
|
SessionID string // e.g., "gt-roxas-dag"
|
|
LastActivity activity.Info // Colored activity display
|
|
StatusHint string // Last line from pane (optional)
|
|
}
|
|
|
|
// MergeQueueRow represents a PR in the merge queue.
|
|
type MergeQueueRow struct {
|
|
Number int
|
|
Repo string // Short repo name (e.g., "roxas", "gastown")
|
|
Title string
|
|
URL string
|
|
CIStatus string // "pass", "fail", "pending"
|
|
Mergeable string // "ready", "conflict", "pending"
|
|
ColorClass string // "mq-green", "mq-yellow", "mq-red"
|
|
}
|
|
|
|
// ConvoyRow represents a single convoy in the dashboard.
|
|
type ConvoyRow struct {
|
|
ID string
|
|
Title string
|
|
Status string // "open" or "closed" (raw beads status)
|
|
WorkStatus string // Computed: "complete", "active", "stale", "stuck", "waiting"
|
|
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,
|
|
"workStatusClass": workStatusClass,
|
|
"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"
|
|
}
|
|
}
|
|
|
|
// workStatusClass returns the CSS class for a computed work status.
|
|
func workStatusClass(workStatus string) string {
|
|
switch workStatus {
|
|
case "complete":
|
|
return "work-complete"
|
|
case "active":
|
|
return "work-active"
|
|
case "stale":
|
|
return "work-stale"
|
|
case "stuck":
|
|
return "work-stuck"
|
|
case "waiting":
|
|
return "work-waiting"
|
|
default:
|
|
return "work-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
|
|
}
|