feat(tui): add summary stats bar below sparse tab tables

Alerts: channel count, type count, total sent, failures.
Nodes: online/total, leader ID, region count.
Maint: active, scheduled, ended counts.

Muted subtitle style — adds useful context without visual noise.
This commit is contained in:
2026-06-20 16:44:12 -04:00
parent 0e5f2dded5
commit d760420f7c
3 changed files with 63 additions and 3 deletions
+20 -1
View File
@@ -185,7 +185,7 @@ func (m Model) viewAlertsTab() string {
nameW := widths[2] nameW := widths[2]
cfgW := widths[4] cfgW := widths[4]
return m.renderTable( tbl := m.renderTable(
headers, headers,
len(m.alerts), len(m.alerts),
func(start, end int) [][]string { func(start, end int) [][]string {
@@ -206,6 +206,25 @@ func (m Model) viewAlertsTab() string {
}, },
widths, nil, widths, nil,
) )
var totalSent, totalFail int
for _, a := range m.alerts {
h := m.engine.GetAlertHealth(a.ID)
totalSent += h.SendCount
totalFail += h.FailCount
}
types := make(map[string]bool)
for _, a := range m.alerts {
types[a.Type] = true
}
failLabel := "failures"
if totalFail == 1 {
failLabel = "failure"
}
summary := fmt.Sprintf("%d channels · %d types · %d sent · %d %s",
len(m.alerts), len(types), totalSent, totalFail, failLabel)
return tbl + "\n " + m.st.subtleStyle.Render(summary)
} }
func (m Model) viewAlertDetailPanel() string { func (m Model) viewAlertDetailPanel() string {
+16 -1
View File
@@ -107,7 +107,7 @@ func (m Model) viewMaintTab() string {
monW := widths[3] monW := widths[3]
timeW := widths[5] timeW := widths[5]
return m.renderTable( tbl := m.renderTable(
headers, headers,
len(m.maintenanceWindows), len(m.maintenanceWindows),
func(start, end int) [][]string { func(start, end int) [][]string {
@@ -130,6 +130,21 @@ func (m Model) viewMaintTab() string {
widths, widths,
nil, nil,
) )
now := time.Now()
var active, scheduled, ended int
for _, mw := range m.maintenanceWindows {
if mw.StartTime.After(now) {
scheduled++
} else if !mw.EndTime.IsZero() && mw.EndTime.Before(now) {
ended++
} else {
active++
}
}
summary := fmt.Sprintf("%d active · %d scheduled · %d ended", active, scheduled, ended)
return tbl + "\n " + m.st.subtleStyle.Render(summary)
} }
func (m *Model) initMaintHuhForm() tea.Cmd { func (m *Model) initMaintHuhForm() tea.Cmd {
+27 -1
View File
@@ -1,6 +1,8 @@
package tui package tui
import ( import (
"fmt"
"strings"
"time" "time"
) )
@@ -20,7 +22,7 @@ func (m Model) viewNodesTab() string {
} }
nameW := widths[0] nameW := widths[0]
return m.renderTable( tbl := m.renderTable(
headers, headers,
len(m.nodes), len(m.nodes),
func(start, end int) [][]string { func(start, end int) [][]string {
@@ -48,6 +50,30 @@ func (m Model) viewNodesTab() string {
widths, widths,
nil, nil,
) )
var online int
regions := make(map[string]bool)
var leader string
for _, n := range m.nodes {
if time.Since(n.LastSeen) < 60*time.Second {
online++
}
if n.Region != "" {
regions[n.Region] = true
}
if n.Name == "leader" {
leader = n.ID
}
}
parts := []string{fmt.Sprintf("%d/%d online", online, len(m.nodes))}
if leader != "" {
parts = append(parts, fmt.Sprintf("leader: %s", leader))
}
if len(regions) > 0 {
parts = append(parts, fmt.Sprintf("%d regions", len(regions)))
}
return tbl + "\n " + m.st.subtleStyle.Render(strings.Join(parts, " · "))
} }
func (m Model) fmtNodeStatus(lastSeen time.Time) string { func (m Model) fmtNodeStatus(lastSeen time.Time) string {