feat(overview): add period-scoped payment filter and field-picker descriptor

This commit is contained in:
Simon 2026-07-10 16:27:15 +02:00
parent 95ef3177ac
commit 7d1a71b1fd
12 changed files with 654 additions and 0 deletions

View file

@ -0,0 +1,82 @@
defmodule MvWeb.MemberLive.IndexFieldPickerTest do
@moduledoc """
§1.10 / §3.5 the FilterDescriptor catalog behind the field picker.
Fields are grouped Quick / Membership / Custom fields; a group with no
available fields is omitted (renders no header). The catalog is derived
purely from the field context and is serializable (the persistence basis for
saved views, #549).
"""
use ExUnit.Case, async: true
alias MvWeb.MemberLive.Index.FilterDescriptor
defp group_keys(descriptors, group) do
descriptors
|> Enum.filter(&(&1.group == group))
|> Enum.map(& &1.key)
end
test "groups are ordered Quick / Membership / Custom fields" do
context = %{
groups: [%{id: Ecto.UUID.generate(), name: "Board"}],
fee_types: [%{id: Ecto.UUID.generate(), name: "Standard"}],
custom_fields: [%{id: Ecto.UUID.generate(), name: "Newsletter", value_type: :boolean}]
}
descriptors = FilterDescriptor.all(context)
assert Enum.map(FilterDescriptor.visible_groups(descriptors), &elem(&1, 0)) ==
[:quick, :membership, :custom_fields]
end
test "quick and membership fields are present with type-aware controls" do
descriptors = FilterDescriptor.all(%{groups: [%{id: Ecto.UUID.generate(), name: "G"}]})
assert group_keys(descriptors, :quick) == [:payment, :active_former]
assert :group in group_keys(descriptors, :membership)
assert :join_date in group_keys(descriptors, :membership)
assert :exit_date in group_keys(descriptors, :membership)
payment = Enum.find(descriptors, &(&1.key == :payment))
assert payment.control == :payment_count
end
test "empty custom-fields group is omitted (no header)" do
descriptors = FilterDescriptor.all(%{custom_fields: []})
groups = Enum.map(FilterDescriptor.visible_groups(descriptors), &elem(&1, 0))
refute :custom_fields in groups
end
test "membership omits Group/Fee type when the club has none" do
descriptors = FilterDescriptor.all(%{groups: [], fee_types: []})
membership_keys = group_keys(descriptors, :membership)
refute :group in membership_keys
refute :fee_type in membership_keys
# Join/exit date are always available.
assert :join_date in membership_keys
assert :exit_date in membership_keys
end
test "one custom-fields descriptor per filterable field; non-filterable types dropped" do
date_id = Ecto.UUID.generate()
bool_id = Ecto.UUID.generate()
context = %{
custom_fields: [
%{id: date_id, name: "Birthday", value_type: :date},
%{id: bool_id, name: "Consent", value_type: :boolean},
%{id: Ecto.UUID.generate(), name: "Phone", value_type: :string}
]
}
custom = Enum.filter(FilterDescriptor.all(context), &(&1.group == :custom_fields))
assert Enum.map(custom, & &1.key) == [to_string(date_id), to_string(bool_id)]
# Keys are plain strings — serializable for #549.
assert Enum.all?(custom, &is_binary(&1.key))
assert Enum.find(custom, &(&1.key == to_string(date_id))).control == :date_range
end
end