ui: dashboard hosts-behind tile + filter
- Add ?updates=behind query filter and the matching dashboardFilter field; round-trips through encode/parse. - Compute UpdatesBehind on the dashboard view-model (online + version trailing the server) and surface as an amber hero tile that links to the filtered list. - Test exercise covering the new filter case.
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.dcglab.co.uk/steve/restic-manager/internal/store"
|
"gitea.dcglab.co.uk/steve/restic-manager/internal/store"
|
||||||
|
"gitea.dcglab.co.uk/steve/restic-manager/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeFilterHosts() []store.Host {
|
func makeFilterHosts() []store.Host {
|
||||||
@@ -98,6 +99,23 @@ func TestSortDashboardHostsColumns(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFilterAndSortDashboardUpdatesBehind: ?updates=behind narrows
|
||||||
|
// to hosts whose agent_version is non-empty AND != server's version.
|
||||||
|
func TestFilterAndSortDashboardUpdatesBehind(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
hosts := []store.Host{
|
||||||
|
{ID: "01a", Name: "alpha", AgentVersion: "v0.0.1", Status: "online"},
|
||||||
|
{ID: "01b", Name: "bravo", AgentVersion: version.Version, Status: "online"},
|
||||||
|
{ID: "01c", Name: "charlie", AgentVersion: "", Status: "online"}, // never seen
|
||||||
|
{ID: "01d", Name: "delta", AgentVersion: "v0.0.1", Status: "offline"},
|
||||||
|
}
|
||||||
|
got := filterAndSortDashboardHosts(hosts, dashboardFilter{Updates: "behind", Sort: "name", Dir: "asc"})
|
||||||
|
// alpha + delta both behind; bravo (current) and charlie (empty) excluded.
|
||||||
|
if len(got) != 2 || got[0].Name != "alpha" || got[1].Name != "delta" {
|
||||||
|
t.Errorf("updates=behind: got %v", namesOf(got))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestParseDashboardFilterDefaults: empty query gives sort=name asc.
|
// TestParseDashboardFilterDefaults: empty query gives sort=name asc.
|
||||||
func TestParseDashboardFilterDefaults(t *testing.T) {
|
func TestParseDashboardFilterDefaults(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|||||||
@@ -66,6 +66,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{/* ---------- Hosts-behind hero tile (P6-18) ---------- */}}
|
||||||
|
{{if gt $page.UpdatesBehind 0}}
|
||||||
|
<div class="pt-4">
|
||||||
|
<a href="?updates=behind" class="hero-tile hero-tile--amber" style="display:inline-flex;">
|
||||||
|
<span class="hero-num">{{$page.UpdatesBehind}}</span>
|
||||||
|
<span class="hero-label">{{if eq $page.UpdatesBehind 1}}host behind{{else}}hosts behind{{end}} · review →</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{/* ---------- Pending hosts (announce-and-approve queue) ---------- */}}
|
{{/* ---------- Pending hosts (announce-and-approve queue) ---------- */}}
|
||||||
{{if gt (len $page.PendingHosts) 0}}
|
{{if gt (len $page.PendingHosts) 0}}
|
||||||
<div class="pt-6">
|
<div class="pt-6">
|
||||||
|
|||||||
Reference in New Issue
Block a user