feat(member): let members tailor overview density and visible columns

The View dropdown drives row density and whether the Member and Address
fields render as composite cells or split into their underlying columns;
the column manager toggles visibility and resets to the curated default.
All choices persist per browser through the URL/session/cookie/global
chain, so a saved layout survives reloads without a per-account store.
This commit is contained in:
Simon 2026-07-06 10:45:53 +02:00
parent af2cc2e0d4
commit dfc616257d
21 changed files with 1643 additions and 263 deletions

View file

@ -13,6 +13,19 @@ defmodule MvWeb.MemberLive.IndexTest do
alias Mv.Membership.CustomFieldValue
alias MvWeb.MemberLive.Index, as: MemberIndex
# Disables the compact Member/Address composites (per-browser view setting) so
# the individual constituent columns (first_name/last_name, street/…) are
# offered by the column manager and selectable via ?fields=. The composite
# E-Mail column is offered even in compact mode, so email-only tests do not
# need this.
defp non_compact(conn) do
Plug.Conn.put_session(
conn,
"member_view_settings",
~s({"compact_member":false,"compact_address":false})
)
end
describe "desktop layout: scroll container and sticky table header" do
@describetag :ui
@ -155,9 +168,11 @@ defmodule MvWeb.MemberLive.IndexTest do
describe "sorting integration" do
@describetag :ui
# The curated default columns no longer expose the individual name/address
# sort headers, so these tests make the relevant column visible via ?fields=.
# That puts a `fields` param on every push_patch, hence the relaxed
# substring assertions instead of exact patch strings.
# sort headers. The composite E-Mail column is offered even in compact mode
# (so email-only tests just add it via ?fields=), but the name/address
# constituents are only offered once their composite view setting is off —
# those tests additionally opt out via `non_compact/1`. Either way a `fields`
# param rides on every push_patch, hence the relaxed substring assertions.
test "clicking a column header toggles sort order and updates the URL", %{conn: conn} do
conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, "/members?fields=email")
@ -183,7 +198,7 @@ defmodule MvWeb.MemberLive.IndexTest do
end
test "clicking different column header resets order to ascending", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} =
live(conn, "/members?fields=first_name,email&sort_field=email&sort_order=desc")
@ -199,7 +214,7 @@ defmodule MvWeb.MemberLive.IndexTest do
end
test "all sortable columns work correctly", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} =
live(
@ -275,7 +290,7 @@ defmodule MvWeb.MemberLive.IndexTest do
end
test "handle_params handles invalid sort field gracefully", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} =
live(conn, "/members?fields=first_name&query=&sort_field=invalid_field&sort_order=asc")