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:
parent
7d1a71b1fd
commit
c547ca19af
35 changed files with 2817 additions and 3149 deletions
|
|
@ -36,6 +36,7 @@ defmodule MvWeb.ConnCase do
|
|||
import Plug.Conn
|
||||
import Phoenix.ConnTest
|
||||
import MvWeb.ConnCase
|
||||
import MvWeb.FilterBuilderHelpers
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
83
test/support/filter_builder_helpers.ex
Normal file
83
test/support/filter_builder_helpers.ex
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
defmodule MvWeb.FilterBuilderHelpers do
|
||||
@moduledoc """
|
||||
Test helpers for driving the member-overview `AddFilterBuilderComponent`.
|
||||
|
||||
The add-filter flow is: open the picker → pick a field → change the focused
|
||||
value control (which commits the filter and re-loads the list). These helpers
|
||||
encapsulate that sequence so tests read at the intent level and stay robust to
|
||||
the builder's internal markup.
|
||||
|
||||
Each `apply_*` returns the patched path (the new `/members?…` URL) so callers
|
||||
can assert the URL-encoded filter contract.
|
||||
"""
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
@builder_id "member-filter"
|
||||
|
||||
@doc "Opens the field picker."
|
||||
def open_picker(view) do
|
||||
view |> element("[data-testid='add-filter-trigger']") |> render_click()
|
||||
end
|
||||
|
||||
@doc "Opens the picker and selects the field with the given descriptor key."
|
||||
def pick_field(view, key) do
|
||||
open_picker(view)
|
||||
view |> element("##{@builder_id}-field-opt-#{key}") |> render_click()
|
||||
end
|
||||
|
||||
@doc "Applies a group in/not_in/all filter through the builder."
|
||||
def apply_group_filter(view, group_id, value) do
|
||||
pick_field(view, "group")
|
||||
|
||||
view
|
||||
|> element("[data-testid='value-control-group']")
|
||||
|> render_change(%{"group_#{group_id}" => to_string(value)})
|
||||
|
||||
flush_and_patch(view)
|
||||
end
|
||||
|
||||
@doc "Applies a fee-type in/not_in/all filter through the builder."
|
||||
def apply_fee_type_filter(view, fee_type_id, value) do
|
||||
pick_field(view, "fee_type")
|
||||
|
||||
view
|
||||
|> element("[data-testid='value-control-fee-type']")
|
||||
|> render_change(%{"fee_type_#{fee_type_id}" => to_string(value)})
|
||||
|
||||
flush_and_patch(view)
|
||||
end
|
||||
|
||||
@doc "Applies a boolean custom-field filter (true/false/all) through the builder."
|
||||
def apply_boolean_filter(view, field_id, value) do
|
||||
pick_field(view, field_id)
|
||||
|
||||
view
|
||||
|> element("[data-testid='value-control-boolean']")
|
||||
|> render_change(%{"custom_boolean" => %{"#{field_id}" => to_string(value)}})
|
||||
|
||||
flush_and_patch(view)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Applies a payment-count filter (and optionally a period) through the builder.
|
||||
`filter` is one of `"all"`, `"fully_paid"`, `"unpaid_1"`, `"unpaid_2"`,
|
||||
`"unpaid_3"`; `from`/`to` are ISO-8601 strings or `""`.
|
||||
"""
|
||||
def apply_payment_filter(view, filter, from \\ "", to \\ "") do
|
||||
pick_field(view, "payment")
|
||||
|
||||
view
|
||||
|> element("[data-testid='value-control-payment']")
|
||||
|> render_change(%{"pay" => filter, "pay_from" => from, "pay_to" => to})
|
||||
|
||||
flush_and_patch(view)
|
||||
end
|
||||
|
||||
# Forces the LiveView to process the queued filter message(s) and returns the
|
||||
# resulting patched path.
|
||||
defp flush_and_patch(view) do
|
||||
_ = render(view)
|
||||
assert_patch(view)
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue