feat(clients): filter the client list by clicking a summary stat card

Each card on the Clients page now toggles its status bucket as the sole
filter, and the Clients card clears it. The bucket filters used to be
wider than the card counts: "active" still included clients near
depletion and "deactive" included disabled clients that had run out, so
a filtered list could disagree with the number on the card. Both filters
now reuse the summary expressions, and a test pins each card's count to
the size of its filtered list.
This commit is contained in:
Sanaei
2026-09-15 22:06:22 +02:00
parent 5fe4f241c1
commit 5008906c4c
4 changed files with 120 additions and 76 deletions
+17 -4
View File
@@ -157,9 +157,9 @@ func TestListPagedFilters(t *testing.T) {
want: []string{"charlie@x", "delta@x", "foxtrot@x"},
},
{
name: "deactive bucket is every disabled client",
name: "deactive bucket leaves a disabled client that ran out to depleted",
params: ClientPageParams{PageSize: 50, Filter: "deactive"},
want: []string{"echo@x", "foxtrot@x"},
want: []string{"echo@x"},
},
{
name: "expiring bucket covers near expiry and near quota",
@@ -167,9 +167,9 @@ func TestListPagedFilters(t *testing.T) {
want: []string{"golf@x", "hotel@x"},
},
{
name: "active bucket keeps enabled clients that still have room",
name: "active bucket leaves clients near depletion to expiring",
params: ClientPageParams{PageSize: 50, Filter: "active"},
want: []string{"alpha@x", "bravo@x", "golf@x", "hotel@x", "india@x", "juliet@x", "kilo_1@x", "kilo1@x"},
want: []string{"alpha@x", "bravo@x", "india@x", "juliet@x", "kilo_1@x", "kilo1@x"},
},
{
name: "buckets are ORed",
@@ -477,6 +477,19 @@ func TestListPagedSummary(t *testing.T) {
}
})
t.Run("clicking a stat card filters to exactly the clients it counts", func(t *testing.T) {
cards := map[string]int{"active": s.Active, "depleted": s.DepletedCount, "expiring": s.ExpiringCount, "deactive": s.DeactiveCount}
for bucket, count := range cards {
page, err := svc.ListPaged(inboundSvc, settingSvc, ClientPageParams{PageSize: 50, Filter: bucket})
if err != nil {
t.Fatalf("ListPaged(%s): %v", bucket, err)
}
if page.Filtered != count {
t.Fatalf("filter %q matched %d clients, card counts %d", bucket, page.Filtered, count)
}
}
})
t.Run("bucket lists carry the matching emails", func(t *testing.T) {
if want := []string{"charlie@x", "delta@x", "foxtrot@x"}; !slices.Equal(s.Depleted, want) {
t.Fatalf("depleted = %v, want %v", s.Depleted, want)