refactor(tui): status icons, clean STATUS column, relative time #62
+26
-13
@@ -152,33 +152,46 @@ func fmtRetries(site models.Site) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func fmtStatus(status string, paused bool, inMaint bool, errCategory ErrorCategory) string {
|
func fmtStatus(status string, paused bool, inMaint bool) string {
|
||||||
if paused {
|
if paused {
|
||||||
return warnStyle.Render("PAUSED")
|
return warnStyle.Render("◇ PAUSED")
|
||||||
}
|
}
|
||||||
if inMaint {
|
if inMaint {
|
||||||
return maintStyle.Render("MAINT")
|
return maintStyle.Render("◼ MAINT")
|
||||||
}
|
}
|
||||||
switch status {
|
switch status {
|
||||||
case "DOWN":
|
case "DOWN":
|
||||||
label := "DOWN"
|
return dangerStyle.Render("▼ DOWN")
|
||||||
if errCategory != ErrCatUnknown {
|
|
||||||
label = "DOWN:" + string(errCategory)
|
|
||||||
}
|
|
||||||
return dangerStyle.Render(label)
|
|
||||||
case "SSL EXP":
|
case "SSL EXP":
|
||||||
return dangerStyle.Render(status)
|
return dangerStyle.Render("▼ SSL EXP")
|
||||||
case "LATE":
|
case "LATE":
|
||||||
return warnStyle.Render(status)
|
return warnStyle.Render("◆ LATE")
|
||||||
case "STALE":
|
case "STALE":
|
||||||
return staleStyle.Render(status)
|
return staleStyle.Render("◆ STALE")
|
||||||
case "PENDING":
|
case "PENDING":
|
||||||
return subtleStyle.Render(status)
|
return subtleStyle.Render("○ PENDING")
|
||||||
default:
|
default:
|
||||||
return specialStyle.Render(status)
|
return specialStyle.Render("▲ " + status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fmtTimeAgo(t time.Time) string {
|
||||||
|
if t.IsZero() {
|
||||||
|
return subtleStyle.Render("never")
|
||||||
|
}
|
||||||
|
d := time.Since(t)
|
||||||
|
if d < time.Minute {
|
||||||
|
return fmt.Sprintf("%ds ago", int(d.Seconds()))
|
||||||
|
}
|
||||||
|
if d < time.Hour {
|
||||||
|
return fmt.Sprintf("%dm ago", int(d.Minutes()))
|
||||||
|
}
|
||||||
|
if d < 24*time.Hour {
|
||||||
|
return fmt.Sprintf("%dh ago", int(d.Hours()))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%dd ago", int(d.Hours())/24)
|
||||||
|
}
|
||||||
|
|
||||||
func fmtDuration(d time.Duration) string {
|
func fmtDuration(d time.Duration) string {
|
||||||
if d < time.Minute {
|
if d < time.Minute {
|
||||||
return fmt.Sprintf("%ds", int(d.Seconds()))
|
return fmt.Sprintf("%ds", int(d.Seconds()))
|
||||||
|
|||||||
+12
-17
@@ -55,32 +55,27 @@ func TestSiteOrder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFmtStatus_ErrorCategory(t *testing.T) {
|
func TestFmtStatus(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
status string
|
status string
|
||||||
paused bool
|
paused bool
|
||||||
inMaint bool
|
inMaint bool
|
||||||
cat ErrorCategory
|
|
||||||
wantSub string
|
wantSub string
|
||||||
}{
|
}{
|
||||||
{"DOWN", false, false, ErrCatDNS, "DOWN:DNS"},
|
{"DOWN", false, false, "▼ DOWN"},
|
||||||
{"DOWN", false, false, ErrCatTLS, "DOWN:TLS"},
|
{"UP", false, false, "▲ UP"},
|
||||||
{"DOWN", false, false, ErrCatHTTP, "DOWN:HTTP"},
|
{"SSL EXP", false, false, "▼ SSL EXP"},
|
||||||
{"DOWN", false, false, ErrCatTCP, "DOWN:TCP"},
|
{"LATE", false, false, "◆ LATE"},
|
||||||
{"DOWN", false, false, ErrCatTimeout, "DOWN:TMO"},
|
{"STALE", false, false, "◆ STALE"},
|
||||||
{"DOWN", false, false, ErrCatICMP, "DOWN:ICMP"},
|
{"PENDING", false, false, "○ PENDING"},
|
||||||
{"DOWN", false, false, ErrCatPrivate, "DOWN:PRIV"},
|
{"DOWN", true, false, "◇ PAUSED"},
|
||||||
{"DOWN", false, false, ErrCatUnknown, "DOWN"},
|
{"DOWN", false, true, "◼ MAINT"},
|
||||||
{"UP", false, false, ErrCatUnknown, "UP"},
|
|
||||||
{"SSL EXP", false, false, ErrCatUnknown, "SSL EXP"},
|
|
||||||
{"DOWN", true, false, ErrCatDNS, "PAUSED"},
|
|
||||||
{"DOWN", false, true, ErrCatDNS, "MAINT"},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
got := fmtStatus(tt.status, tt.paused, tt.inMaint, tt.cat)
|
got := fmtStatus(tt.status, tt.paused, tt.inMaint)
|
||||||
if !containsPlain(got, tt.wantSub) {
|
if !containsPlain(got, tt.wantSub) {
|
||||||
t.Errorf("fmtStatus(%q, paused=%v, maint=%v, %q): %q missing %q",
|
t.Errorf("fmtStatus(%q, paused=%v, maint=%v): %q missing %q",
|
||||||
tt.status, tt.paused, tt.inMaint, tt.cat, got, tt.wantSub)
|
tt.status, tt.paused, tt.inMaint, got, tt.wantSub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package tui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.lerkolabs.com/lerkolabs/uptop/internal/monitor"
|
"gitea.lerkolabs.com/lerkolabs/uptop/internal/monitor"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
@@ -146,20 +145,7 @@ func fmtAlertHealth(h monitor.AlertHealth) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fmtAlertLastSent(h monitor.AlertHealth) string {
|
func fmtAlertLastSent(h monitor.AlertHealth) string {
|
||||||
if h.LastSendAt.IsZero() {
|
return fmtTimeAgo(h.LastSendAt)
|
||||||
return subtleStyle.Render("never")
|
|
||||||
}
|
|
||||||
d := time.Since(h.LastSendAt)
|
|
||||||
if d < time.Minute {
|
|
||||||
return fmt.Sprintf("%ds ago", int(d.Seconds()))
|
|
||||||
}
|
|
||||||
if d < time.Hour {
|
|
||||||
return fmt.Sprintf("%dm ago", int(d.Minutes()))
|
|
||||||
}
|
|
||||||
if d < 24*time.Hour {
|
|
||||||
return fmt.Sprintf("%dh ago", int(d.Hours()))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%dd ago", int(d.Hours())/24)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) viewAlertsTab() string {
|
func (m Model) viewAlertsTab() string {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,15 +65,5 @@ func fmtNodeStatus(lastSeen time.Time) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fmtNodeLastSeen(t time.Time) string {
|
func fmtNodeLastSeen(t time.Time) string {
|
||||||
if t.IsZero() {
|
return fmtTimeAgo(t)
|
||||||
return subtleStyle.Render("never")
|
|
||||||
}
|
|
||||||
ago := time.Since(t)
|
|
||||||
if ago < time.Minute {
|
|
||||||
return fmt.Sprintf("%ds ago", int(ago.Seconds()))
|
|
||||||
}
|
|
||||||
if ago < time.Hour {
|
|
||||||
return fmt.Sprintf("%dm ago", int(ago.Minutes()))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%dh ago", int(ago.Hours()))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func (m Model) viewSitesTab() string {
|
|||||||
strconv.Itoa(i + 1),
|
strconv.Itoa(i + 1),
|
||||||
m.zones.Mark(fmt.Sprintf("site-%d", i), icon+" "+limitStr(site.Name, nameW-4)),
|
m.zones.Mark(fmt.Sprintf("site-%d", i), icon+" "+limitStr(site.Name, nameW-4)),
|
||||||
"group",
|
"group",
|
||||||
fmtStatus(site.Status, site.Paused, m.isMonitorInMaintenance(site.ID), ErrCatUnknown),
|
fmtStatus(site.Status, site.Paused, m.isMonitorInMaintenance(site.ID)),
|
||||||
subtleStyle.Render("—"),
|
subtleStyle.Render("—"),
|
||||||
m.groupUptime(site.ID),
|
m.groupUptime(site.ID),
|
||||||
m.groupSparkline(site.ID, sparkWidth),
|
m.groupSparkline(site.ID, sparkWidth),
|
||||||
@@ -175,7 +175,7 @@ func (m Model) viewSitesTab() string {
|
|||||||
strconv.Itoa(i + 1),
|
strconv.Itoa(i + 1),
|
||||||
m.zones.Mark(fmt.Sprintf("site-%d", i), name),
|
m.zones.Mark(fmt.Sprintf("site-%d", i), name),
|
||||||
typeIcon(site.Type, false) + " " + site.Type,
|
typeIcon(site.Type, false) + " " + site.Type,
|
||||||
fmtStatus(site.Status, site.Paused, m.isMonitorInMaintenance(site.ID), classifyError(site.LastError, site.Type, site.StatusCode)),
|
fmtStatus(site.Status, site.Paused, m.isMonitorInMaintenance(site.ID)),
|
||||||
fmtLatency(site.Latency),
|
fmtLatency(site.Latency),
|
||||||
fmtUptime(hist.Statuses),
|
fmtUptime(hist.Statuses),
|
||||||
spark,
|
spark,
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ func (m Model) viewDetailPanel() string {
|
|||||||
b.WriteString("\n" + subtleStyle.Render(" "+label) + "\n")
|
b.WriteString("\n" + subtleStyle.Render(" "+label) + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
errCat := classifyError(site.LastError, site.Type, site.StatusCode)
|
row("Status", fmtStatus(site.Status, site.Paused, m.isMonitorInMaintenance(site.ID)))
|
||||||
row("Status", fmtStatus(site.Status, site.Paused, m.isMonitorInMaintenance(site.ID), errCat))
|
|
||||||
|
|
||||||
if (site.Status == "DOWN" || site.Status == "SSL EXP" || site.Status == "LATE" || site.Status == "STALE") && site.LastError != "" {
|
if (site.Status == "DOWN" || site.Status == "SSL EXP" || site.Status == "LATE" || site.Status == "STALE") && site.LastError != "" {
|
||||||
errWidth := m.termWidth - chromePadH - 19
|
errWidth := m.termWidth - chromePadH - 19
|
||||||
@@ -128,7 +127,7 @@ func (m Model) viewDetailPanel() string {
|
|||||||
row("Latency", fmtLatency(site.Latency))
|
row("Latency", fmtLatency(site.Latency))
|
||||||
row("Uptime", fmtUptime(hist.Statuses))
|
row("Uptime", fmtUptime(hist.Statuses))
|
||||||
if !site.LastCheck.IsZero() {
|
if !site.LastCheck.IsZero() {
|
||||||
row("Last Check", site.LastCheck.Format("15:04:05"))
|
row("Last Check", fmtTimeAgo(site.LastCheck))
|
||||||
}
|
}
|
||||||
|
|
||||||
if site.Type == "http" {
|
if site.Type == "http" {
|
||||||
|
|||||||
Reference in New Issue
Block a user