feat: Stage 2 — alert dashboard with live table and stats

Adds a bubbles/table alert list showing Name, Status, Started (relative
time), and Ack By columns. Stats bar shows total/firing/resolved counts.
Filter cycles firing → resolved → all with [f]. Auto-refresh fires on
the configured interval via tea.Tick. Table scrolls with j/k.
This commit is contained in:
Niklas Ye
2026-05-21 13:37:05 +02:00
parent 55ee64530b
commit e29cff21ae
5 changed files with 304 additions and 59 deletions
+58 -8
View File
@@ -9,27 +9,55 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
m.rebuildTable()
return m, nil
case connectedMsg:
m.connected = true
m.err = nil
m.statusMsg = "Connected to " + m.serverURL
return m, tea.Batch(tickCmd(m.refreshInterval), clearStatusCmd())
return m, tea.Batch(
tickCmd(m.refreshInterval),
fetchAlertsCmd(m.client, m.filterStatus),
fetchStatsCmd(m.client),
)
case connectErrMsg:
m.connected = false
m.err = msg.err
return m, nil
case alertsFetchedMsg:
m.alerts = msg.alerts
m.loading = false
m.rebuildTable()
return m, nil
case statsFetchedMsg:
m.stats = &msg.stats
return m, nil
case fetchDataErrMsg:
m.statusMsg = "error: " + msg.err.Error()
return m, clearStatusCmd()
case tickMsg:
return m, tickCmd(m.refreshInterval)
return m, tea.Batch(
tickCmd(m.refreshInterval),
fetchAlertsCmd(m.client, m.filterStatus),
fetchStatsCmd(m.client),
)
case clearStatusMsg:
m.statusMsg = ""
return m, nil
case tea.KeyMsg:
if m.activeSection == sectionAlerts && m.connected {
var tableCmd tea.Cmd
m.alertTable, tableCmd = m.alertTable.Update(msg)
m2, ourCmd := m.handleKey(msg)
return m2, tea.Batch(tableCmd, ourCmd)
}
return m.handleKey(msg)
}
@@ -37,17 +65,39 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
func (m Model) handleKey(msg tea.KeyMsg) (Model, tea.Cmd) {
switch {
case msg.String() == "q" || msg.String() == "ctrl+c":
switch msg.String() {
case "q", "ctrl+c":
return m, tea.Quit
case msg.String() == "tab":
case "tab":
m.activeSection = (m.activeSection + 1) % 3
return m, nil
case msg.String() == "r":
case "r":
if !m.connected {
return m, connectCmd(m.client)
}
m.statusMsg = "Refreshing…"
return m, tea.Batch(connectCmd(m.client), clearStatusCmd())
return m, tea.Batch(
fetchAlertsCmd(m.client, m.filterStatus),
fetchStatsCmd(m.client),
clearStatusCmd(),
)
case "f":
if m.activeSection != sectionAlerts || !m.connected {
return m, nil
}
switch m.filterStatus {
case "firing":
m.filterStatus = "resolved"
case "resolved":
m.filterStatus = ""
default:
m.filterStatus = "firing"
}
m.loading = true
return m, fetchAlertsCmd(m.client, m.filterStatus)
}
return m, nil