Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
28fb6c8889
|
|||
|
d982359f25
|
+22
-4
@@ -49,7 +49,7 @@ func writeCmd(op string, fn func() error) tea.Cmd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortSitesForDisplay(allSites []models.Site, collapsed map[int]bool) []models.Site {
|
func sortSitesForDisplay(allSites []models.Site, collapsed map[int]bool, sortCol int, sortAsc bool) []models.Site {
|
||||||
var groups, ungrouped []models.Site
|
var groups, ungrouped []models.Site
|
||||||
children := make(map[int][]models.Site)
|
children := make(map[int][]models.Site)
|
||||||
for _, s := range allSites {
|
for _, s := range allSites {
|
||||||
@@ -68,8 +68,26 @@ func sortSitesForDisplay(allSites []models.Site, collapsed map[int]bool) []model
|
|||||||
sort.SliceStable(c, func(i, j int) bool { return siteOrder(c[i]) < siteOrder(c[j]) })
|
sort.SliceStable(c, func(i, j int) bool { return siteOrder(c[i]) < siteOrder(c[j]) })
|
||||||
children[pid] = c
|
children[pid] = c
|
||||||
}
|
}
|
||||||
sort.Slice(ungrouped, func(i, j int) bool { return ungrouped[i].ID < ungrouped[j].ID })
|
|
||||||
sort.SliceStable(ungrouped, func(i, j int) bool { return siteOrder(ungrouped[i]) < siteOrder(ungrouped[j]) })
|
sortSlice := func(s []models.Site) {
|
||||||
|
sort.Slice(s, func(i, j int) bool { return s[i].ID < s[j].ID })
|
||||||
|
sort.SliceStable(s, func(i, j int) bool {
|
||||||
|
var less bool
|
||||||
|
switch sortCol {
|
||||||
|
case sortName:
|
||||||
|
less = strings.ToLower(s[i].Name) < strings.ToLower(s[j].Name)
|
||||||
|
case sortLatency:
|
||||||
|
less = s[i].Latency < s[j].Latency
|
||||||
|
default:
|
||||||
|
less = siteOrder(s[i]) < siteOrder(s[j])
|
||||||
|
}
|
||||||
|
if !sortAsc {
|
||||||
|
return less
|
||||||
|
}
|
||||||
|
return !less
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sortSlice(ungrouped)
|
||||||
|
|
||||||
var ordered []models.Site
|
var ordered []models.Site
|
||||||
for _, g := range groups {
|
for _, g := range groups {
|
||||||
@@ -99,7 +117,7 @@ func filterSites(sites []models.Site, needle string) []models.Site {
|
|||||||
// separately via loadTabDataCmd.
|
// separately via loadTabDataCmd.
|
||||||
func (m *Model) refreshLive() {
|
func (m *Model) refreshLive() {
|
||||||
allSites := m.engine.GetAllSites()
|
allSites := m.engine.GetAllSites()
|
||||||
ordered := sortSitesForDisplay(allSites, m.collapsed)
|
ordered := sortSitesForDisplay(allSites, m.collapsed, m.sortColumn, m.sortAsc)
|
||||||
if m.filterText != "" {
|
if m.filterText != "" {
|
||||||
ordered = filterSites(ordered, m.filterText)
|
ordered = filterSites(ordered, m.filterText)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func TestSortSitesForDisplay_GroupsFirst(t *testing.T) {
|
|||||||
{SiteConfig: models.SiteConfig{ID: 1, Name: "group-a", Type: "group"}, SiteState: models.SiteState{Status: "UP"}},
|
{SiteConfig: models.SiteConfig{ID: 1, Name: "group-a", Type: "group"}, SiteState: models.SiteState{Status: "UP"}},
|
||||||
{SiteConfig: models.SiteConfig{ID: 2, Name: "child", Type: "http", ParentID: 1}, SiteState: models.SiteState{Status: "UP"}},
|
{SiteConfig: models.SiteConfig{ID: 2, Name: "child", Type: "http", ParentID: 1}, SiteState: models.SiteState{Status: "UP"}},
|
||||||
}
|
}
|
||||||
result := sortSitesForDisplay(sites, nil)
|
result := sortSitesForDisplay(sites, nil, sortStatus, false)
|
||||||
if len(result) != 3 {
|
if len(result) != 3 {
|
||||||
t.Fatalf("expected 3 sites, got %d", len(result))
|
t.Fatalf("expected 3 sites, got %d", len(result))
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ func TestSortSitesForDisplay_CollapsedHidesChildren(t *testing.T) {
|
|||||||
{SiteConfig: models.SiteConfig{ID: 3, Name: "child-2", Type: "http", ParentID: 1}, SiteState: models.SiteState{Status: "UP"}},
|
{SiteConfig: models.SiteConfig{ID: 3, Name: "child-2", Type: "http", ParentID: 1}, SiteState: models.SiteState{Status: "UP"}},
|
||||||
}
|
}
|
||||||
collapsed := map[int]bool{1: true}
|
collapsed := map[int]bool{1: true}
|
||||||
result := sortSitesForDisplay(sites, collapsed)
|
result := sortSitesForDisplay(sites, collapsed, sortStatus, false)
|
||||||
if len(result) != 1 {
|
if len(result) != 1 {
|
||||||
t.Fatalf("collapsed group should hide children, got %d items", len(result))
|
t.Fatalf("collapsed group should hide children, got %d items", len(result))
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ func TestSortSitesForDisplay_StatusOrdering(t *testing.T) {
|
|||||||
{SiteConfig: models.SiteConfig{ID: 2, Name: "down-site", Type: "http"}, SiteState: models.SiteState{Status: "DOWN"}},
|
{SiteConfig: models.SiteConfig{ID: 2, Name: "down-site", Type: "http"}, SiteState: models.SiteState{Status: "DOWN"}},
|
||||||
{SiteConfig: models.SiteConfig{ID: 3, Name: "late-site", Type: "http"}, SiteState: models.SiteState{Status: "LATE"}},
|
{SiteConfig: models.SiteConfig{ID: 3, Name: "late-site", Type: "http"}, SiteState: models.SiteState{Status: "LATE"}},
|
||||||
}
|
}
|
||||||
result := sortSitesForDisplay(sites, nil)
|
result := sortSitesForDisplay(sites, nil, sortStatus, false)
|
||||||
if result[0].Status != "DOWN" {
|
if result[0].Status != "DOWN" {
|
||||||
t.Errorf("DOWN should sort first, got %s", result[0].Status)
|
t.Errorf("DOWN should sort first, got %s", result[0].Status)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,24 @@ func (m Model) computeLayout() tableLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sortColMap := map[int]colKey{
|
||||||
|
sortStatus: colStatus,
|
||||||
|
sortName: colName,
|
||||||
|
sortLatency: colLatency,
|
||||||
|
}
|
||||||
|
if sortedKey, ok := sortColMap[m.sortColumn]; ok {
|
||||||
|
arrow := "▼"
|
||||||
|
if m.sortAsc {
|
||||||
|
arrow = "▲"
|
||||||
|
}
|
||||||
|
for i, k := range active {
|
||||||
|
if k == sortedKey {
|
||||||
|
headers[i] = headers[i] + arrow
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
numCols := len(headers)
|
numCols := len(headers)
|
||||||
borderOverhead := 2 + (numCols - 1)
|
borderOverhead := 2 + (numCols - 1)
|
||||||
avail := cw - chromePadH - 2 - borderOverhead - fixed
|
avail := cw - chromePadH - 2 - borderOverhead - fixed
|
||||||
|
|||||||
@@ -102,6 +102,13 @@ const (
|
|||||||
panelDetail = 2
|
panelDetail = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sortStatus = 0
|
||||||
|
sortName = 1
|
||||||
|
sortLatency = 2
|
||||||
|
sortMax = 3
|
||||||
|
)
|
||||||
|
|
||||||
type sessionState int
|
type sessionState int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -125,6 +132,8 @@ type Model struct {
|
|||||||
settingsSection int
|
settingsSection int
|
||||||
cursor int
|
cursor int
|
||||||
selectedID int
|
selectedID int
|
||||||
|
sortColumn int
|
||||||
|
sortAsc bool
|
||||||
tableOffset int
|
tableOffset int
|
||||||
maxTableRows int
|
maxTableRows int
|
||||||
termWidth int
|
termWidth int
|
||||||
|
|||||||
@@ -532,6 +532,23 @@ func (m *Model) handleDashboardKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
m.recalcLayout()
|
m.recalcLayout()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
case ">", ".":
|
||||||
|
if m.currentTab == tabMonitors {
|
||||||
|
m.sortColumn = (m.sortColumn + 1) % sortMax
|
||||||
|
m.sortAsc = false
|
||||||
|
m.refreshLive()
|
||||||
|
}
|
||||||
|
case "<", ",":
|
||||||
|
if m.currentTab == tabMonitors {
|
||||||
|
m.sortColumn = (m.sortColumn - 1 + sortMax) % sortMax
|
||||||
|
m.sortAsc = false
|
||||||
|
m.refreshLive()
|
||||||
|
}
|
||||||
|
case "r":
|
||||||
|
if m.currentTab == tabMonitors {
|
||||||
|
m.sortAsc = !m.sortAsc
|
||||||
|
m.refreshLive()
|
||||||
|
}
|
||||||
case "tab":
|
case "tab":
|
||||||
m.switchTab(m.currentTab + 1)
|
m.switchTab(m.currentTab + 1)
|
||||||
case "left":
|
case "left":
|
||||||
|
|||||||
@@ -233,6 +233,12 @@ type tabEntry struct {
|
|||||||
func (m Model) renderTabBar(stats dashboardStats) string {
|
func (m Model) renderTabBar(stats dashboardStats) string {
|
||||||
settingsCount := len(m.alerts) + len(m.nodes)
|
settingsCount := len(m.alerts) + len(m.nodes)
|
||||||
settingsWarn := stats.offlineNodes
|
settingsWarn := stats.offlineNodes
|
||||||
|
for _, a := range m.alerts {
|
||||||
|
h := m.engine.GetAlertHealth(a.ID)
|
||||||
|
if !h.LastSendOK && !h.LastSendAt.IsZero() {
|
||||||
|
settingsWarn++
|
||||||
|
}
|
||||||
|
}
|
||||||
if m.isAdmin {
|
if m.isAdmin {
|
||||||
settingsCount += len(m.users)
|
settingsCount += len(m.users)
|
||||||
}
|
}
|
||||||
@@ -308,7 +314,7 @@ func (m Model) renderFooter(stats dashboardStats) string {
|
|||||||
} else if m.detailOpen {
|
} else if m.detailOpen {
|
||||||
keys = "[i]Close [Enter]Expand [h]History [s]SLA [e]Edit [l]Logs [↑/↓]Select [T]Theme [q]Quit"
|
keys = "[i]Close [Enter]Expand [h]History [s]SLA [e]Edit [l]Logs [↑/↓]Select [T]Theme [q]Quit"
|
||||||
} else {
|
} else {
|
||||||
keys = "[/]Filter [i]Info [Enter]Detail [n]New [e]Edit [d]Del [l]Logs [T]Theme [Tab]Switch [q]Quit"
|
keys = "[/]Filter [i]Info [Enter]Detail [</>]Sort [r]Reverse [n]New [e]Edit [d]Del [l]Logs [T]Theme [Tab]Switch [q]Quit"
|
||||||
}
|
}
|
||||||
case tabMaint:
|
case tabMaint:
|
||||||
keys = "[n]New [x]End [d]Del [T]Theme [Tab]Switch [q]Quit"
|
keys = "[n]New [x]End [d]Del [T]Theme [Tab]Switch [q]Quit"
|
||||||
|
|||||||
Reference in New Issue
Block a user