From 0a7bbc7fa6d135c8fb1b6230098aa16524d1113c Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 6 May 2026 11:05:28 +0200 Subject: [PATCH] fix: labels for custom fields in join requests --- lib/mv_web/components/layouts.ex | 2 +- lib/mv_web/live/join_live.ex | 31 +++++++++++- lib/mv_web/live/join_request_live/show.ex | 61 +++++++++++++++++++---- test/mv_web/live/join_live_test.exs | 6 ++- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/lib/mv_web/components/layouts.ex b/lib/mv_web/components/layouts.ex index 54f589d..9aff23c 100644 --- a/lib/mv_web/components/layouts.ex +++ b/lib/mv_web/components/layouts.ex @@ -138,7 +138,7 @@ defmodule MvWeb.Layouts do # Single get_settings() for layout; derive club_name and join_form_enabled to avoid duplicate query. %{club_name: club_name, join_form_enabled: join_form_enabled} = get_layout_settings() - # TODO: unprocessed count runs on every page load when join form enabled; consider + # NOTE: Unprocessed count runs on every page load when join form is enabled; consider # loading only on navigation or caching briefly if performance becomes an issue. unprocessed_join_requests_count = get_unprocessed_join_requests_count(assigns.current_user, join_form_enabled) diff --git a/lib/mv_web/live/join_live.ex b/lib/mv_web/live/join_live.ex index 3b8db05..d3d66f0 100644 --- a/lib/mv_web/live/join_live.ex +++ b/lib/mv_web/live/join_live.ex @@ -216,19 +216,48 @@ defmodule MvWeb.JoinLive do defp build_join_fields_with_labels(allowlist) do member_field_strings = Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1) + custom_field_name_by_id = custom_field_name_map(allowlist, member_field_strings) Enum.map(allowlist, fn %{id: id, required: required} -> label = if id in member_field_strings do MemberFields.label(String.to_existing_atom(id)) else - gettext("Field") + Map.get(custom_field_name_by_id, id, gettext("Field")) end %{id: id, label: label, required: required} end) end + defp custom_field_name_map(allowlist, member_field_strings) do + custom_field_ids = + allowlist + |> Enum.map(& &1.id) + |> Enum.reject(&(&1 in member_field_strings)) + + case custom_field_ids do + [] -> + %{} + + ids -> + Mv.Membership.CustomField + |> Ash.Query.select([:id, :name]) + |> Ash.read(domain: Mv.Membership, authorize?: false) + |> case do + {:ok, fields} -> + allowed_ids = MapSet.new(ids) + + fields + |> Enum.filter(&MapSet.member?(allowed_ids, &1.id)) + |> Map.new(&{&1.id, &1.name}) + + {:error, _} -> + %{} + end + end + end + defp initial_form_params(join_fields) do join_fields |> Enum.map(fn f -> {f.id, ""} end) diff --git a/lib/mv_web/live/join_request_live/show.ex b/lib/mv_web/live/join_request_live/show.ex index 304cb6a..2aab87d 100644 --- a/lib/mv_web/live/join_request_live/show.ex +++ b/lib/mv_web/live/join_request_live/show.ex @@ -31,6 +31,7 @@ defmodule MvWeb.JoinRequestLive.Show do {:ok, socket |> assign(:join_request, nil) + |> assign(:custom_field_name_by_id, %{}) |> assign(:join_form_field_ids, []) |> Layouts.assign_page_title(gettext("Join request"))} else @@ -53,9 +54,13 @@ defmodule MvWeb.JoinRequestLive.Show do {:ok, request} -> field_ids = Membership.get_join_form_allowlist() |> Enum.map(& &1.id) + custom_field_name_by_id = + custom_field_name_map(field_ids ++ Map.keys(request.form_data || %{}), actor) + {:noreply, socket |> assign(:join_request, request) + |> assign(:custom_field_name_by_id, custom_field_name_by_id) |> assign(:join_form_field_ids, field_ids) |> Layouts.assign_page_title(gettext("Join request – %{email}", email: request.email))} @@ -132,7 +137,12 @@ defmodule MvWeb.JoinRequestLive.Show do

{gettext("Applicant data")}

- <%= for {label, value} <- applicant_data_rows(@join_request, @join_form_field_ids || []) do %> + <%= for {label, value} <- + applicant_data_rows( + @join_request, + @join_form_field_ids || [], + @custom_field_name_by_id || %{} + ) do %> <.field_row label={label} value={value} empty_text={gettext("Not specified")} /> <% end %>
@@ -230,7 +240,7 @@ defmodule MvWeb.JoinRequestLive.Show do # Builds a single list of {label, display_value} for all applicant-provided data in join form # order. Typed fields (email, first_name, last_name) and form_data are merged; legacy # form_data keys (not in current join form config) are appended at the end. - defp applicant_data_rows(join_request, ordered_field_ids) do + defp applicant_data_rows(join_request, ordered_field_ids, custom_field_name_by_id) do member_field_strings = Constants.member_fields() |> Enum.map(&Atom.to_string/1) form_data = join_request.form_data || %{} @@ -244,7 +254,7 @@ defmodule MvWeb.JoinRequestLive.Show do ordered_field_ids |> Enum.map(fn key -> value = Map.get(typed, key) || Map.get(form_data, key) - label = field_key_to_label(key, member_field_strings) + label = field_key_to_label(key, member_field_strings, custom_field_name_by_id) {label, format_applicant_value(value)} end) @@ -258,7 +268,7 @@ defmodule MvWeb.JoinRequestLive.Show do legacy_entries = Enum.map(legacy_keys, fn key -> - label = field_key_to_label(key, member_field_strings) + label = field_key_to_label(key, member_field_strings, custom_field_name_by_id) {label, format_applicant_value(form_data[key])} end) @@ -299,11 +309,44 @@ defmodule MvWeb.JoinRequestLive.Show do defp format_applicant_value_simple(raw, _value) when is_integer(raw), do: to_string(raw) defp format_applicant_value_simple(_raw, value), do: to_string(value) - defp field_key_to_label(key, member_field_strings) when is_binary(key) do - if key in member_field_strings, - do: MemberFieldsTranslations.label(String.to_existing_atom(key)), - else: key + defp field_key_to_label(key, member_field_strings, custom_field_name_by_id) + when is_binary(key) do + if key in member_field_strings do + MemberFieldsTranslations.label(String.to_existing_atom(key)) + else + Map.get(custom_field_name_by_id, key, key) + end end - defp field_key_to_label(key, _), do: to_string(key) + defp field_key_to_label(key, _, _), do: to_string(key) + + defp custom_field_name_map(field_keys, actor) do + member_field_strings = Constants.member_fields() |> Enum.map(&Atom.to_string/1) + + custom_field_ids = + field_keys + |> Enum.uniq() + |> Enum.reject(&(&1 in member_field_strings)) + + case custom_field_ids do + [] -> + %{} + + ids -> + Mv.Membership.CustomField + |> Ash.Query.select([:id, :name]) + |> Ash.read(actor: actor, domain: Mv.Membership) + |> case do + {:ok, fields} -> + allowed_ids = MapSet.new(ids) + + fields + |> Enum.filter(&MapSet.member?(allowed_ids, &1.id)) + |> Map.new(&{&1.id, &1.name}) + + {:error, _} -> + %{} + end + end + end end diff --git a/test/mv_web/live/join_live_test.exs b/test/mv_web/live/join_live_test.exs index 383b413..273e786 100644 --- a/test/mv_web/live/join_live_test.exs +++ b/test/mv_web/live/join_live_test.exs @@ -159,7 +159,11 @@ defmodule MvWeb.JoinLiveTest do {:ok, view, _html} = live(conn, "/join") - assert has_element?(view, "label[for='join-field-#{custom_field.id}'] .label-text", custom_field.name) + assert has_element?( + view, + "label[for='join-field-#{custom_field.id}'] .label-text", + custom_field.name + ) end end