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

@ -22,6 +22,17 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
require Ash.Query
# Disables the compact Member/Address composites (per-browser view setting) so
# the individual constituent columns (first_name/last_name/email, street/…) are
# offered by the column manager and available via ?fields=.
defp non_compact(conn) do
Plug.Conn.put_session(
conn,
"member_view_settings",
~s({"compact_member":false,"compact_address":false})
)
end
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
@ -112,7 +123,9 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
end
test "displays all member fields in dropdown", %{conn: conn} do
conn = conn_with_oidc_user(conn)
# The individual name/address constituents are only offered when the
# compact Member/Address composites are off, so opt out of them first.
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} = live(conn, "/members")
# Open dropdown
@ -148,9 +161,9 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, "/members")
# The curated Name column carries the email, so it is visible initially.
# The curated Name column carries the member names, so they are visible.
html = render(view)
assert html =~ "alice@example.com"
assert html =~ "Anderson"
# Open dropdown and hide the Name column
view
@ -161,10 +174,10 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
|> element("button[phx-click='select_item'][phx-value-item='name']")
|> render_click()
# The Name column (and the email it carries) is no longer visible
# The Name column (and the names it carries) is no longer visible
html = render(view)
refute html =~ "alice@example.com"
refute html =~ "bob@example.com"
refute html =~ "Anderson"
refute html =~ "Brown"
end
test "hiding custom field removes it from display", %{conn: conn, custom_field: custom_field} do
@ -298,8 +311,8 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, "/members")
# All fields should be visible by default
assert html =~ "alice@example.com"
# The curated Name and Address columns are visible by default.
assert html =~ "Anderson"
assert html =~ "Main St"
end
@ -309,7 +322,7 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, "/members")
# Hide the curated Name column (which carries the email) via dropdown
# Hide the curated Name column (which carries the names) via dropdown
view
|> element("button[aria-controls='field-visibility-menu']")
|> render_click()
@ -319,7 +332,7 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
|> render_click()
html = render(view)
refute html =~ "alice@example.com"
refute html =~ "Anderson"
end
end
@ -328,16 +341,16 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, "/members?fields=")
# Should fall back to global settings
assert html =~ "alice@example.com"
# Should fall back to global settings (curated Name column visible)
assert html =~ "Anderson"
end
test "handles invalid field names in URL", %{conn: conn} do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, "/members?fields=invalid_field,another_invalid")
# Should ignore invalid fields and use defaults
assert html =~ "alice@example.com"
# Should ignore invalid fields and use defaults (curated Name column visible)
assert html =~ "Anderson"
end
test "handles custom field that doesn't exist", %{conn: conn} do
@ -429,9 +442,9 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, "/members")
# The curated Name column carries the email, so it is visible initially.
# The curated Name column carries the names, so they are visible initially.
html = render(view)
assert html =~ "alice@example.com"
assert html =~ "Anderson"
# Open dropdown
view
@ -443,9 +456,9 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
|> element("button[phx-click='select_item'][phx-value-item='name']")
|> render_keydown(%{key: "Enter"})
# The Name column (and the email it carries) is no longer visible
# The Name column (and the names it carries) is no longer visible
html = render(view)
refute html =~ "alice@example.com"
refute html =~ "Anderson"
end
end
end