feat(overview): replace filter panel with add-filter builder and aging column

Swaps the unbounded vertical filter panel for a Polaris-style add-filter
builder (searchable grouped field picker, type-aware value control, applied
chips) and colour-codes the fees column with each member's period-scoped
unpaid-cycle count.
This commit is contained in:
Simon 2026-07-10 16:27:15 +02:00
parent 7d1a71b1fd
commit c547ca19af
35 changed files with 2817 additions and 3149 deletions

View file

@ -93,17 +93,19 @@ defmodule MvWeb.MemberLive.IndexPaymentPeriodTest do
end
describe "badge/1" do
test "0 renders as Paid (success)" do
test "0 renders as All paid (success/green)" do
badge = PaymentAging.badge(0)
assert badge.variant == :success
assert badge.label == "Paid"
assert badge.color_class == "badge-success"
assert badge.label == "All paid"
end
test "N > 0 renders as N unpaid (error)" do
test "N > 0 renders as N open (error/red)" do
badge = PaymentAging.badge(2)
assert badge.variant == :error
assert badge.color_class == "badge-error"
assert badge.label =~ "2"
assert badge.label =~ "unpaid"
assert badge.label =~ "open"
end
end
@ -114,11 +116,11 @@ defmodule MvWeb.MemberLive.IndexPaymentPeriodTest do
%{actor: actor, ft: ft}
end
test "lists in-period unpaid cycles and flags suspended separately; paid excluded",
test "lists in-period unpaid and suspended cycles chronologically; paid/out-of-period excluded",
%{actor: actor, ft: ft} do
member = member_fixture_with_actor(%{}, actor)
create_cycle(member, ft, %{cycle_start: ~D[2024-01-01], status: :unpaid}, actor)
create_cycle(member, ft, %{cycle_start: ~D[2024-02-01], status: :unpaid}, actor)
create_cycle(member, ft, %{cycle_start: ~D[2024-01-01], status: :unpaid}, actor)
create_cycle(member, ft, %{cycle_start: ~D[2024-03-01], status: :suspended}, actor)
create_cycle(member, ft, %{cycle_start: ~D[2024-04-01], status: :paid}, actor)
# out of period
@ -127,10 +129,13 @@ defmodule MvWeb.MemberLive.IndexPaymentPeriodTest do
member = Ash.load!(member, :membership_fee_cycles, actor: actor)
result = PaymentAging.open_cycles(member, %{from: ~D[2024-01-01], to: ~D[2024-12-31]})
assert length(result.unpaid) == 2
assert length(result.suspended) == 1
assert Enum.all?(result.unpaid, &(&1.status == :unpaid))
assert Enum.all?(result.suspended, &(&1.status == :suspended))
assert Enum.map(result, & &1.status) == [:unpaid, :unpaid, :suspended]
assert Enum.map(result, & &1.cycle_start) == [
~D[2024-01-01],
~D[2024-02-01],
~D[2024-03-01]
]
end
test "nil bounds include every unpaid/suspended cycle", %{actor: actor, ft: ft} do
@ -141,8 +146,22 @@ defmodule MvWeb.MemberLive.IndexPaymentPeriodTest do
member = Ash.load!(member, :membership_fee_cycles, actor: actor)
result = PaymentAging.open_cycles(member, PaymentAging.default_period())
assert length(result.unpaid) == 1
assert length(result.suspended) == 1
assert Enum.map(result, & &1.status) == [:unpaid, :suspended]
end
end
describe "tooltip_cycles/1 (five-slot cap)" do
test "five or fewer cycles show in full with no overflow" do
cycles = for i <- 1..5, do: %{n: i}
assert PaymentAging.tooltip_cycles(cycles) == {cycles, 0}
end
test "more than five collapses to first four cycles plus the overflow count" do
cycles = for i <- 1..19, do: %{n: i}
{shown, overflow} = PaymentAging.tooltip_cycles(cycles)
assert shown == Enum.take(cycles, 4)
assert overflow == 15
end
end
end