refactor(member-live): modularize the overview and drop the superseded in-memory paths
Extract the cookie parser, export-payload builder and fee-status helper into their own modules and remove the filter/sort/load helpers the database pushdown made dead, so the overview LiveView stays a thin coordinator over focused units.
This commit is contained in:
parent
c2cb3edab8
commit
77fc11a0b0
17 changed files with 375 additions and 875 deletions
|
|
@ -35,6 +35,7 @@ defmodule MvWeb.MemberLive.Index do
|
|||
alias MvWeb.Helpers.DateFormatter
|
||||
alias MvWeb.MemberLive.Index.CustomFieldValueLookup
|
||||
alias MvWeb.MemberLive.Index.DateFilter
|
||||
alias MvWeb.MemberLive.Index.ExportPayload
|
||||
alias MvWeb.MemberLive.Index.FieldSelection
|
||||
alias MvWeb.MemberLive.Index.FieldVisibility
|
||||
alias MvWeb.MemberLive.Index.FilterParams
|
||||
|
|
@ -377,8 +378,8 @@ defmodule MvWeb.MemberLive.Index do
|
|||
query_params =
|
||||
build_query_params(
|
||||
opts_for_query_params(socket, %{
|
||||
sort_field: export_sort_field(socket.assigns.sort_field),
|
||||
sort_order: export_sort_order(socket.assigns.sort_order)
|
||||
sort_field: ExportPayload.sort_field(socket.assigns.sort_field),
|
||||
sort_order: ExportPayload.sort_order(socket.assigns.sort_order)
|
||||
})
|
||||
)
|
||||
|> maybe_add_field_selection(
|
||||
|
|
@ -553,53 +554,6 @@ defmodule MvWeb.MemberLive.Index do
|
|||
{:noreply, push_reload(socket, new_path)}
|
||||
end
|
||||
|
||||
# Backward compatibility: tuple form delegates to map form
|
||||
def handle_info({:reset_all_filters, cycle_status_filter, boolean_filters}, socket) do
|
||||
handle_info(
|
||||
{:reset_all_filters,
|
||||
%{
|
||||
cycle_status_filter: cycle_status_filter,
|
||||
boolean_filters: boolean_filters,
|
||||
group_filters: %{},
|
||||
fee_type_filters: %{}
|
||||
}},
|
||||
socket
|
||||
)
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
{:reset_all_filters, cycle_status_filter, boolean_filters, group_filters},
|
||||
socket
|
||||
) do
|
||||
handle_info(
|
||||
{:reset_all_filters,
|
||||
%{
|
||||
cycle_status_filter: cycle_status_filter,
|
||||
boolean_filters: boolean_filters,
|
||||
group_filters: group_filters,
|
||||
fee_type_filters: %{}
|
||||
}},
|
||||
socket
|
||||
)
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
{:reset_all_filters, cycle_status_filter, boolean_filters, group_filters,
|
||||
fee_type_filters},
|
||||
socket
|
||||
) do
|
||||
handle_info(
|
||||
{:reset_all_filters,
|
||||
%{
|
||||
cycle_status_filter: cycle_status_filter,
|
||||
boolean_filters: boolean_filters,
|
||||
group_filters: group_filters,
|
||||
fee_type_filters: fee_type_filters
|
||||
}},
|
||||
socket
|
||||
)
|
||||
end
|
||||
|
||||
def handle_info({:reset_all_filters, %{} = opts}, socket) do
|
||||
socket =
|
||||
socket
|
||||
|
|
@ -1348,7 +1302,7 @@ defmodule MvWeb.MemberLive.Index do
|
|||
|> OverviewQuery.build()
|
||||
|> Ash.Query.select(@overview_fields)
|
||||
|> load_custom_field_values(compute_ids_to_load(socket))
|
||||
|> MembershipFeeStatus.load_cycles_for_members(socket.assigns.show_current_cycle)
|
||||
|> MembershipFeeStatus.load_cycles_for_members()
|
||||
|> Ash.Query.load(groups: [:id, :name, :slug])
|
||||
|> maybe_load_fee_type(socket)
|
||||
end
|
||||
|
|
@ -1490,8 +1444,6 @@ defmodule MvWeb.MemberLive.Index do
|
|||
String.starts_with?(field, @custom_field_prefix)
|
||||
end
|
||||
|
||||
defp custom_field_sort?(_), do: false
|
||||
|
||||
defp extract_custom_field_ids(visible_custom_fields) do
|
||||
Enum.map(visible_custom_fields, fn field_string ->
|
||||
case String.split(field_string, @custom_field_prefix) do
|
||||
|
|
@ -1781,13 +1733,6 @@ defmodule MvWeb.MemberLive.Index do
|
|||
CustomFieldValueLookup.find_by_field(member, custom_field)
|
||||
end
|
||||
|
||||
def get_boolean_custom_field_value(member, custom_field) do
|
||||
case get_custom_field_value(member, custom_field) do
|
||||
nil -> nil
|
||||
cfv -> extract_boolean_value(cfv.value)
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_boolean_value(%Ash.Union{value: value, type: :boolean}),
|
||||
do: extract_boolean_value(value)
|
||||
|
||||
|
|
@ -1843,14 +1788,6 @@ defmodule MvWeb.MemberLive.Index do
|
|||
end
|
||||
end
|
||||
|
||||
def format_selected_member_emails(members, selected_members) do
|
||||
members
|
||||
|> Enum.filter(fn member ->
|
||||
MapSet.member?(selected_members, member.id) && member.email && member.email != ""
|
||||
end)
|
||||
|> Enum.map(&format_member_email/1)
|
||||
end
|
||||
|
||||
def checkbox_column_click(member), do: JS.push("select_member", value: %{id: member.id})
|
||||
|
||||
def format_member_email(member) do
|
||||
|
|
@ -1997,149 +1934,7 @@ defmodule MvWeb.MemberLive.Index do
|
|||
end
|
||||
|
||||
defp assign_export_payload(socket) do
|
||||
payload = build_export_payload(socket)
|
||||
payload = ExportPayload.build(socket.assigns)
|
||||
assign(socket, :export_payload_json, Jason.encode!(payload))
|
||||
end
|
||||
|
||||
defp build_export_payload(socket) do
|
||||
visible_custom_field_ids = socket.assigns[:visible_custom_field_ids] || []
|
||||
|
||||
member_fields_db = socket.assigns[:member_fields_visible_db] || []
|
||||
member_fields_computed = socket.assigns[:member_fields_visible_computed] || []
|
||||
|
||||
# Order DB member fields exactly like the table/constants
|
||||
ordered_member_fields_db =
|
||||
Mv.Constants.member_fields()
|
||||
|> Enum.filter(&(&1 in member_fields_db))
|
||||
|
||||
# Order computed fields in canonical order
|
||||
ordered_computed_fields =
|
||||
FieldVisibility.computed_member_fields()
|
||||
|> Enum.filter(&(&1 in member_fields_computed))
|
||||
|
||||
member_fields_with_groups =
|
||||
build_export_member_fields_list(
|
||||
ordered_member_fields_db,
|
||||
socket.assigns[:member_fields_visible]
|
||||
)
|
||||
|
||||
# Order custom fields like the table (same as dynamic_cols / all_custom_fields order)
|
||||
ordered_custom_field_ids =
|
||||
socket.assigns.all_custom_fields
|
||||
|> Enum.map(&to_string(&1.id))
|
||||
|> Enum.filter(&(&1 in visible_custom_field_ids))
|
||||
|
||||
%{
|
||||
selected_ids: socket.assigns.selected_members |> MapSet.to_list(),
|
||||
member_fields:
|
||||
Enum.map(member_fields_with_groups, fn
|
||||
f when is_atom(f) -> Atom.to_string(f)
|
||||
f when is_binary(f) -> f
|
||||
end),
|
||||
computed_fields: Enum.map(ordered_computed_fields, &Atom.to_string/1),
|
||||
custom_field_ids: ordered_custom_field_ids,
|
||||
column_order:
|
||||
export_column_order(
|
||||
ordered_member_fields_db,
|
||||
ordered_computed_fields,
|
||||
ordered_custom_field_ids,
|
||||
:membership_fee_type in socket.assigns[:member_fields_visible],
|
||||
:groups in socket.assigns[:member_fields_visible]
|
||||
),
|
||||
query: socket.assigns[:query] || nil,
|
||||
sort_field: export_sort_field(socket.assigns[:sort_field]),
|
||||
sort_order: export_sort_order(socket.assigns[:sort_order]),
|
||||
show_current_cycle: socket.assigns[:show_current_cycle] || false,
|
||||
cycle_status_filter: export_cycle_status_filter(socket.assigns[:cycle_status_filter]),
|
||||
boolean_filters: socket.assigns[:boolean_custom_field_filters] || %{}
|
||||
}
|
||||
end
|
||||
|
||||
defp expand_db_string_for_export(f, membership_fee_type_visible, computed_strings) do
|
||||
if f == "membership_fee_start_date" do
|
||||
extra =
|
||||
if(membership_fee_type_visible, do: ["membership_fee_type"], else: []) ++
|
||||
if "membership_fee_status" in computed_strings, do: ["membership_fee_status"], else: []
|
||||
|
||||
[f] ++ extra
|
||||
else
|
||||
[f]
|
||||
end
|
||||
end
|
||||
|
||||
defp build_export_member_fields_list(ordered_db, member_fields_visible) do
|
||||
with_extras =
|
||||
Enum.flat_map(ordered_db, fn f ->
|
||||
if f == :membership_fee_start_date and
|
||||
:membership_fee_type in (member_fields_visible || []) do
|
||||
[f, :membership_fee_type]
|
||||
else
|
||||
[f]
|
||||
end
|
||||
end)
|
||||
|
||||
# If fee type is visible but start_date was not in the list, append it
|
||||
with_extras =
|
||||
if :membership_fee_type in (member_fields_visible || []) and
|
||||
:membership_fee_type not in with_extras do
|
||||
with_extras ++ [:membership_fee_type]
|
||||
else
|
||||
with_extras
|
||||
end
|
||||
|
||||
if :groups in (member_fields_visible || []), do: with_extras ++ [:groups], else: with_extras
|
||||
end
|
||||
|
||||
defp export_cycle_status_filter(nil), do: nil
|
||||
defp export_cycle_status_filter(:paid), do: "paid"
|
||||
defp export_cycle_status_filter(:unpaid), do: "unpaid"
|
||||
defp export_cycle_status_filter(_), do: nil
|
||||
|
||||
defp export_sort_field(nil), do: nil
|
||||
defp export_sort_field(f) when is_atom(f), do: Atom.to_string(f)
|
||||
defp export_sort_field(f) when is_binary(f), do: f
|
||||
|
||||
defp export_sort_order(nil), do: nil
|
||||
defp export_sort_order(:asc), do: "asc"
|
||||
defp export_sort_order(:desc), do: "desc"
|
||||
defp export_sort_order(o) when is_binary(o), do: o
|
||||
# Build a single ordered list that matches the table order:
|
||||
# - DB fields in Mv.Constants.member_fields() order (already pre-filtered as ordered_member_fields_db)
|
||||
# - membership_fee_type and membership_fee_status inserted after membership_fee_start_date when visible
|
||||
# - groups appended before custom fields when visible
|
||||
# - custom fields appended in the same order as table (already ordered_custom_field_ids)
|
||||
defp export_column_order(
|
||||
ordered_member_fields_db,
|
||||
ordered_computed_fields,
|
||||
ordered_custom_field_ids,
|
||||
membership_fee_type_visible,
|
||||
groups_visible
|
||||
) do
|
||||
db_strings = Enum.map(ordered_member_fields_db, &Atom.to_string/1)
|
||||
computed_strings = Enum.map(ordered_computed_fields, &Atom.to_string/1)
|
||||
|
||||
# Place membership_fee_type and membership_fee_status after membership_fee_start_date when present
|
||||
db_with_extras =
|
||||
Enum.flat_map(
|
||||
db_strings,
|
||||
&expand_db_string_for_export(&1, membership_fee_type_visible, computed_strings)
|
||||
)
|
||||
|
||||
# If fee type is visible but start_date was not in the list, append it before computed/groups
|
||||
db_with_extras =
|
||||
if membership_fee_type_visible and "membership_fee_type" not in db_with_extras do
|
||||
db_with_extras ++ ["membership_fee_type"]
|
||||
else
|
||||
db_with_extras
|
||||
end
|
||||
|
||||
# Any remaining computed fields not inserted above (future-proof)
|
||||
remaining_computed =
|
||||
computed_strings
|
||||
|> Enum.reject(&(&1 in db_with_extras))
|
||||
|
||||
result = db_with_extras ++ remaining_computed
|
||||
result = if groups_visible, do: result ++ ["groups"], else: result
|
||||
result ++ ordered_custom_field_ids
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue