D4 Part 1: hub fonts + sprite + statusColor semantic remap

- static/fonts/: the 4 vendored woff2 (byte-copied from
  felhom-controller), embedded (embed.go) and served at /static/fonts/
  (font/woff2, immutable) mirroring the chart.min.js pattern. No CDN
  before, none now.
- templates/icons.html: 12-symbol Lucide sprite partial (icon_sprite),
  included at the top of <body> on all 9 pages ({{template}} — the hub
  has no shared layout; per-page include is the minimal shared block).
- statusColor now returns v2 semantic tokens (nominal/warn/crit/
  neutral) consumed as class suffixes: ok->nominal, warn+stale->warn,
  down+fail->crit, pending+disabled->neutral (a not-yet-provisioned or
  deliberately paused customer is a normal fleet state), blocked->warn
  (intentional operator cut-off, attention-worthy not an outage),
  unknown->neutral. The inline style="color: {{statusColor}}" pattern
  is dead: dashboard + customer_unified render a class-based
  .status-dot-<token>; statusIcon (constant "●") retired from funcmap
  and templates.
- Tests (new; the hub web package had no funcmap/template tests):
  TestStatusColorTruthTable over the full enumerated status set —
  red-proven vs the old implementation (ok returned "#4ade80") — and
  TestTemplatesParseWithFuncmap.
This commit is contained in:
2026-07-02 22:34:40 +02:00
parent 43189e8972
commit bc8d54df6a
17 changed files with 112 additions and 14 deletions
+29 -12
View File
@@ -73,7 +73,6 @@ func New(store *store.Store, passwordHash, apiKey, version string, staleThreshol
return timeAgo(*t)
},
"statusColor": statusColor,
"statusIcon": statusIcon,
"formatFloat": func(f float64) string { return fmt.Sprintf("%.0f", f) },
"joinStrings": func(s []string, sep string) string { return strings.Join(s, sep) },
"json": func(v interface{}) template.JS {
@@ -204,6 +203,17 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/javascript")
w.Header().Set("Cache-Control", "public, max-age=86400")
w.Write(chartJS)
case strings.HasPrefix(path, "/static/fonts/"):
// Vendored brand fonts (design system v2) — embedded, no CDN.
name := strings.TrimPrefix(path, "/static/fonts/")
data, err := fontFS.ReadFile("static/fonts/" + name)
if err != nil {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "font/woff2")
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
w.Write(data)
case path == "/configuration":
if r.Method == http.MethodPost {
s.handleConfigurationAction(w, r)
@@ -608,25 +618,32 @@ func timeAgo(t time.Time) string {
return fmt.Sprintf("%dd ago", days)
}
// statusColor maps a status value to a design-system-v2 semantic token, consumed as a
// class suffix (status-dot-nominal, tag-nominal, …) — never as an inline color (D4).
// Exception-color principle: a healthy fleet is blue/neutral; amber/red only on deviation.
// ok -> nominal (blue — operating normally)
// warn, stale -> warn (amber — degraded / stale report)
// down, fail -> crit (red — outage)
// pending -> neutral (a not-yet-provisioned customer is a normal fleet state)
// disabled -> neutral (deliberately paused — not a deviation)
// blocked -> warn (an operator cut a customer off: intentional but attention-worthy)
func statusColor(status string) string {
switch status {
case "ok":
return "#4ade80" // green
case "warn":
return "#facc15" // yellow
return "nominal"
case "warn", "stale":
return "warn"
case "down", "fail":
return "#f87171" // red
case "disabled", "pending", "blocked":
return "#94a3b8" // gray
return "crit"
case "blocked":
return "warn"
case "disabled", "pending":
return "neutral"
default:
return "#94a3b8" // gray
return "neutral"
}
}
func statusIcon(status string) string {
return "●"
}
// handleConfiguration renders the Configuration page.
func (s *Server) handleConfiguration(w http.ResponseWriter, r *http.Request) {
csrfToken := s.getCSRFToken(r)