feat(tui): upgrade alerts tab with lipgloss table, click zones, colored types
Alerts tab now matches sites/users quality: bordered table, click-to-select, color-coded provider types, and improved config column display.
This commit is contained in:
+99
-21
@@ -4,10 +4,30 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go-upkeep/internal/store"
|
"go-upkeep/internal/store"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/huh"
|
"github.com/charmbracelet/huh"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/charmbracelet/lipgloss/table"
|
||||||
|
)
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
var (
|
||||||
|
alertHeaderStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#7D56F4")).
|
||||||
|
Bold(true).
|
||||||
|
Padding(0, 1)
|
||||||
|
|
||||||
|
alertCellStyle = lipgloss.NewStyle().Padding(0, 1)
|
||||||
|
|
||||||
|
alertSelectedStyle = lipgloss.NewStyle().
|
||||||
|
Padding(0, 1).
|
||||||
|
Bold(true).
|
||||||
|
Foreground(lipgloss.Color("#ffffff")).
|
||||||
|
Background(lipgloss.Color("#3b3b5c"))
|
||||||
|
|
||||||
|
alertBorderStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#444"))
|
||||||
|
|
||||||
|
alertColWidths = []int{4, 16, 10, 36}
|
||||||
)
|
)
|
||||||
|
|
||||||
type alertFormData struct {
|
type alertFormData struct {
|
||||||
@@ -22,34 +42,92 @@ type alertFormData struct {
|
|||||||
EmailTo string
|
EmailTo string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fmtAlertType(t string) string {
|
||||||
|
switch t {
|
||||||
|
case "discord":
|
||||||
|
return lipgloss.NewStyle().Foreground(lipgloss.Color("#5865F2")).Render(t)
|
||||||
|
case "slack":
|
||||||
|
return lipgloss.NewStyle().Foreground(lipgloss.Color("#E01E5A")).Render(t)
|
||||||
|
case "webhook":
|
||||||
|
return lipgloss.NewStyle().Foreground(lipgloss.Color("#F0E442")).Render(t)
|
||||||
|
case "email":
|
||||||
|
return lipgloss.NewStyle().Foreground(lipgloss.Color("#73F59F")).Render(t)
|
||||||
|
default:
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fmtAlertConfig(alert struct {
|
||||||
|
Type string
|
||||||
|
Settings map[string]string
|
||||||
|
}) string {
|
||||||
|
if alert.Type == "email" {
|
||||||
|
host := alert.Settings["host"]
|
||||||
|
to := alert.Settings["to"]
|
||||||
|
if host != "" && to != "" {
|
||||||
|
return limitStr(fmt.Sprintf("%s → %s", host, to), 34)
|
||||||
|
}
|
||||||
|
if host != "" {
|
||||||
|
return limitStr(host, 34)
|
||||||
|
}
|
||||||
|
return subtleStyle.Render("—")
|
||||||
|
}
|
||||||
|
if val, ok := alert.Settings["url"]; ok {
|
||||||
|
return limitStr(val, 34)
|
||||||
|
}
|
||||||
|
return subtleStyle.Render("—")
|
||||||
|
}
|
||||||
|
|
||||||
func (m Model) viewAlertsTab() string {
|
func (m Model) viewAlertsTab() string {
|
||||||
var content string
|
if len(m.alerts) == 0 {
|
||||||
content += fmt.Sprintf("\n%-3s %-15s %-10s %s\n", "ID", "NAME", "TYPE", "CONFIG")
|
return "\n No alert channels configured. Press [n] to add one."
|
||||||
content += subtleStyle.Render("----------------------------------------------------------------") + "\n"
|
}
|
||||||
|
|
||||||
end := m.tableOffset + m.maxTableRows
|
end := m.tableOffset + m.maxTableRows
|
||||||
if end > len(m.alerts) {
|
if end > len(m.alerts) {
|
||||||
end = len(m.alerts)
|
end = len(m.alerts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectedVisual := m.cursor - m.tableOffset
|
||||||
|
|
||||||
|
var rows [][]string
|
||||||
for i := m.tableOffset; i < end; i++ {
|
for i := m.tableOffset; i < end; i++ {
|
||||||
alert := m.alerts[i]
|
alert := m.alerts[i]
|
||||||
cursor := " "
|
rows = append(rows, []string{
|
||||||
if m.cursor == i {
|
fmt.Sprintf("%d", alert.ID),
|
||||||
cursor = ">"
|
m.zones.Mark(fmt.Sprintf("alert-%d", i), limitStr(alert.Name, 15)),
|
||||||
}
|
fmtAlertType(alert.Type),
|
||||||
confStr := "settings..."
|
fmtAlertConfig(struct {
|
||||||
if val, ok := alert.Settings["url"]; ok {
|
Type string
|
||||||
confStr = limitStr(val, 30)
|
Settings map[string]string
|
||||||
}
|
}{alert.Type, alert.Settings}),
|
||||||
if alert.Type == "email" {
|
})
|
||||||
confStr = fmt.Sprintf("SMTP: %s", alert.Settings["host"])
|
|
||||||
}
|
|
||||||
row := fmt.Sprintf("%s %-3d %-15s %-10s %s", cursor, alert.ID, limitStr(alert.Name, 15), alert.Type, confStr)
|
|
||||||
if m.cursor == i {
|
|
||||||
row = lipgloss.NewStyle().Bold(true).Render(row)
|
|
||||||
}
|
|
||||||
content += row + "\n"
|
|
||||||
}
|
}
|
||||||
return content
|
|
||||||
|
t := table.New().
|
||||||
|
Border(lipgloss.RoundedBorder()).
|
||||||
|
BorderStyle(alertBorderStyle).
|
||||||
|
Headers("ID", "NAME", "TYPE", "CONFIG").
|
||||||
|
Rows(rows...).
|
||||||
|
StyleFunc(func(row, col int) lipgloss.Style {
|
||||||
|
if row == table.HeaderRow {
|
||||||
|
s := alertHeaderStyle
|
||||||
|
if col < len(alertColWidths) {
|
||||||
|
s = s.Width(alertColWidths[col])
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
s := alertCellStyle
|
||||||
|
if row == selectedVisual {
|
||||||
|
s = alertSelectedStyle
|
||||||
|
}
|
||||||
|
if col < len(alertColWidths) {
|
||||||
|
s = s.Width(alertColWidths[col])
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
|
||||||
|
return "\n" + t.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) initAlertHuhForm() tea.Cmd {
|
func (m *Model) initAlertHuhForm() tea.Cmd {
|
||||||
|
|||||||
@@ -268,6 +268,19 @@ func (m *Model) handleClick(msg tea.MouseMsg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.currentTab == 1 {
|
||||||
|
end := m.tableOffset + m.maxTableRows
|
||||||
|
if end > len(m.alerts) {
|
||||||
|
end = len(m.alerts)
|
||||||
|
}
|
||||||
|
for i := m.tableOffset; i < end; i++ {
|
||||||
|
if m.zones.Get(fmt.Sprintf("alert-%d", i)).InBounds(msg) {
|
||||||
|
m.cursor = i
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if m.currentTab == 3 {
|
if m.currentTab == 3 {
|
||||||
end := m.tableOffset + m.maxTableRows
|
end := m.tableOffset + m.maxTableRows
|
||||||
if end > len(m.users) {
|
if end > len(m.users) {
|
||||||
|
|||||||
Reference in New Issue
Block a user