From d41d13d1220d659545d80a2b3052ebe380c920c8 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 24 Feb 2026 01:06:41 +0100 Subject: [PATCH] fix(members): restore column visibility from URL on reload Read 'fields' from URI when conn.params has no query (e.g. full page load). When ?fields=... is present use URL-only selection so columns are not merged with global settings. Fall back to session+global when URL has only invalid field names. --- lib/mv_web/live/member_live/index.ex | 82 +++++++++++++++---- .../member_live/index/field_visibility.ex | 19 +++++ 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/lib/mv_web/live/member_live/index.ex b/lib/mv_web/live/member_live/index.ex index e7c0fd7..889a818 100644 --- a/lib/mv_web/live/member_live/index.ex +++ b/lib/mv_web/live/member_live/index.ex @@ -615,7 +615,9 @@ defmodule MvWeb.MemberLive.Index do # ----------------------------------------------------------------- @impl true - def handle_params(params, _url, socket) do + def handle_params(params, url, socket) do + url = url || request_url_from_socket(socket) + params = merge_fields_param_from_uri(params, url) prev_sig = build_signature(socket) fields_in_url? = @@ -625,20 +627,7 @@ defmodule MvWeb.MemberLive.Index do end url_selection = FieldSelection.parse_from_url(params) - - merged_selection = - FieldSelection.merge_sources( - url_selection, - socket.assigns.user_field_selection, - %{} - ) - - final_selection = - FieldVisibility.merge_with_global_settings( - merged_selection, - socket.assigns.settings, - socket.assigns.all_custom_fields - ) + final_selection = compute_final_field_selection(fields_in_url?, url_selection, socket) visible_member_fields = final_selection @@ -828,6 +817,69 @@ defmodule MvWeb.MemberLive.Index do add_boolean_filters(base_params, boolean_filters) end + defp compute_final_field_selection(true, url_selection, socket) do + only_url = + FieldVisibility.selection_from_url_only(url_selection, socket.assigns.all_custom_fields) + + visible = FieldVisibility.get_visible_member_fields(only_url) + + if visible == [] do + # URL had only invalid field names; fall back to session + global. + compute_final_field_selection(false, url_selection, socket) + else + only_url + end + end + + defp compute_final_field_selection(false, url_selection, socket) do + merged = + FieldSelection.merge_sources( + url_selection, + socket.assigns.user_field_selection, + %{} + ) + + FieldVisibility.merge_with_global_settings( + merged, + socket.assigns.settings, + socket.assigns.all_custom_fields + ) + end + + # On full page load conn.params has no query string; read "fields" from URI so column visibility is restored. + defp request_url_from_socket(socket) do + case socket.private[:connect_info] do + %Plug.Conn{} = conn -> Plug.Conn.request_url(conn) + _ -> nil + end + end + + defp merge_fields_param_from_uri(params, nil), do: params + + defp merge_fields_param_from_uri(params, %URI{query: query}) when is_binary(query) do + case URI.decode_query(query)["fields"] do + nil -> params + value -> Map.put(params, "fields", value) + end + end + + defp merge_fields_param_from_uri(params, %URI{}), do: params + + defp merge_fields_param_from_uri(params, url) when is_binary(url) do + case URI.parse(url).query do + nil -> + params + + q -> + case URI.decode_query(q)["fields"] do + nil -> params + value -> Map.put(params, "fields", value) + end + end + end + + defp merge_fields_param_from_uri(params, _), do: params + defp build_base_params(query, sort_field, sort_order) do %{ "query" => query || "", diff --git a/lib/mv_web/live/member_live/index/field_visibility.ex b/lib/mv_web/live/member_live/index/field_visibility.ex index 7b54bba..df20d25 100644 --- a/lib/mv_web/live/member_live/index/field_visibility.ex +++ b/lib/mv_web/live/member_live/index/field_visibility.ex @@ -64,6 +64,25 @@ defmodule MvWeb.MemberLive.Index.FieldVisibility do member_fields ++ custom_field_names end + @doc """ + Builds field selection from URL only: fields in `url_selection` are visible, all others false. + Use when `?fields=...` is in the URL so column visibility is not merged with global settings. + """ + @spec selection_from_url_only(%{String.t() => boolean()}, [struct()]) :: %{ + String.t() => boolean() + } + def selection_from_url_only(url_selection, custom_fields) when is_map(url_selection) do + all_fields = get_all_available_fields(custom_fields) + + Enum.reduce(all_fields, %{}, fn field, acc -> + field_string = field_to_string(field) + visible = Map.get(url_selection, field_string, false) + Map.put(acc, field_string, visible) + end) + end + + def selection_from_url_only(_, _), do: %{} + @doc """ Merges user field selection with global settings.