feat(member-live): sort the composite Member column by first or last name

This commit is contained in:
Simon 2026-07-16 09:39:07 +02:00
parent ca2aaa069c
commit f7005f395f
11 changed files with 443 additions and 34 deletions

View file

@ -172,7 +172,7 @@ defmodule MvWeb.MemberLive.Index do
socket
|> Layouts.assign_page_title(gettext("Members"))
|> assign(:query, "")
|> assign_new(:sort_field, fn -> :first_name end)
|> assign_new(:sort_field, fn -> :last_name end)
|> assign_new(:sort_order, fn -> :asc end)
|> assign(:cycle_status_filter, nil)
|> assign(:group_filters, %{})
@ -379,6 +379,22 @@ defmodule MvWeb.MemberLive.Index do
end
{new_field, new_order} = determine_new_sort(field, socket)
{:noreply, apply_sort_change(socket, new_field, new_order)}
end
# The composite "Member" column's sub-field menu (first name / last name) always
# sorts ascending by the chosen field: it switches the sort key rather than
# toggling direction (a click on the header itself toggles direction).
def handle_event("sort_composite", %{"field" => field_str}, socket) do
field = String.to_existing_atom(field_str)
{:noreply, apply_sort_change(socket, field, :asc)}
end
# Applies a resolved (field, order) sort: updates assigns and the sort-header
# components, reloads the keyset stream from page 1, scrolls to top, and syncs
# the URL. Shared by the header-click ("sort") and sub-field menu
# ("sort_composite") paths.
defp apply_sort_change(socket, new_field, new_order) do
old_field = socket.assigns.sort_field
socket =
@ -394,8 +410,8 @@ defmodule MvWeb.MemberLive.Index do
query_params =
build_query_params(
opts_for_query_params(socket, %{
sort_field: ExportPayload.sort_field(socket.assigns.sort_field),
sort_order: ExportPayload.sort_order(socket.assigns.sort_order)
sort_field: ExportPayload.sort_field(new_field),
sort_order: ExportPayload.sort_order(new_order)
})
)
|> maybe_add_field_selection(
@ -403,7 +419,7 @@ defmodule MvWeb.MemberLive.Index do
socket.assigns[:fields_in_url?] || false
)
{:noreply, push_reload(socket, ~p"/members?#{query_params}")}
push_reload(socket, ~p"/members?#{query_params}")
end
# -----------------------------------------------------------------
@ -1488,9 +1504,11 @@ defmodule MvWeb.MemberLive.Index do
defp determine_field(default, nil), do: default
# Computed/pseudo fields that are nonetheless sortable (they resolve to DB
# sort keys in OverviewQuery): groups aggregate and the composite Name/Address
# columns. Other computed fields (e.g. membership_fee_status) are not sortable.
@sortable_computed_fields [:groups, :name, :address]
# sort keys in OverviewQuery): the groups aggregate and the composite Address
# column. The composite Member column sorts by its real first/last-name
# sub-fields (chosen via its sub-field menu), so it needs no pseudo token here.
# Other computed fields (e.g. membership_fee_status) are not sortable.
@sortable_computed_fields [:groups, :address]
defp determine_field(default, sf) when is_binary(sf) do
sortable_strings = Enum.map(@sortable_computed_fields, &Atom.to_string/1)