diff --git a/docs/onboarding-join-concept.md b/docs/onboarding-join-concept.md index 8083a7b..487256e 100644 --- a/docs/onboarding-join-concept.md +++ b/docs/onboarding-join-concept.md @@ -93,6 +93,7 @@ - **Placement:** Own section **"Onboarding / Join"** in global settings, **above** "Custom fields", **below** "Vereinsdaten" (club data). - **Join form enabled:** Checkbox (e.g. `join_form_enabled`). When set, the public `/join` page is active and the following config applies. +- **Copyable join link:** When the join form is enabled, a copyable full URL to the `/join` page is shown below the checkbox (above the field list), with a short hint so admins can share it with applicants. - **Field selection:** From **all existing** member fields (from `Mv.Constants.member_fields()`) and **custom fields**, the admin selects which fields appear on the join form. Stored as a list/set of field identifiers (no separate table); display in settings as a simple list, e.g. **badges with X to remove** (similar to the groups overview). Adding fields: e.g. dropdown or modal to pick from remaining fields. Detailed UX for this subsection is to be specified in a **separate subtask**. - **Technically required fields:** The only field that must always be required for the join flow is **email**. All other fields can be optional or marked as required per admin choice; implementation should support a "required" flag per selected join-form field. - **Other:** Which entry paths are enabled, approval workflow (who can approve) – to be detailed in Step 2 and later specs. @@ -115,7 +116,7 @@ Implementation spec for Subtask 5. #### Route and pages - **List:** **`/join_requests`** – list of join requests. Filter by status (default or primary view: status `submitted`); optional view for "all" or "approved/rejected" for audit. -- **Detail:** **`/join_requests/:id`** – single join request with all data (typed fields + `form_data`), actions Approve / Reject. +- **Detail:** **`/join_requests/:id`** – single join request. **Two blocks:** (1) **Applicant data** – all form fields (typed + `form_data`) merged and shown in join-form order; (2) **Status and review** – submitted_at, status, and when decided: approved_at/rejected_at, reviewed by. Actions Approve / Reject when status is `submitted`. #### Backend (JoinRequest) diff --git a/lib/mv_web/live/global_settings_live.ex b/lib/mv_web/live/global_settings_live.ex index ce3351a..84cf738 100644 --- a/lib/mv_web/live/global_settings_live.ex +++ b/lib/mv_web/live/global_settings_live.ex @@ -93,6 +93,7 @@ defmodule MvWeb.GlobalSettingsLive do |> assign(:smtp_test_result, nil) |> assign(:smtp_test_to_email, "") |> assign_join_form_state(settings, custom_fields) + |> assign(:join_url, url(socket.endpoint, ~p"/join")) |> assign_form() {:ok, socket} @@ -153,6 +154,33 @@ defmodule MvWeb.GlobalSettingsLive do
+ <%!-- Copyable join page link (below checkbox, above field list) --%> +
+

+ {gettext("Link to the public join page (share this with applicants):")} +

+
+ + <.button + variant="secondary" + size="sm" + id="copy-join-url-btn" + phx-hook="CopyToClipboard" + phx-click="copy_join_url" + aria-label={gettext("Copy join page URL")} + > + <.icon name="hero-clipboard-document" class="size-4" /> + {gettext("Copy")} + +
+
+ <%!-- Field list header + Add button (left-aligned) --%>

{gettext("Fields on the join form")}

@@ -796,6 +824,16 @@ defmodule MvWeb.GlobalSettingsLive do # ---- Join form event handlers ---- + @impl true + def handle_event("copy_join_url", _params, socket) do + socket = + socket + |> push_event("copy_to_clipboard", %{text: socket.assigns.join_url}) + |> put_flash(:success, gettext("Join page URL copied to clipboard.")) + + {:noreply, socket} + end + @impl true def handle_event("toggle_join_form_enabled", _params, socket) do socket = assign(socket, :join_form_enabled, not socket.assigns.join_form_enabled) diff --git a/lib/mv_web/live/join_request_live/show.ex b/lib/mv_web/live/join_request_live/show.ex index 138b433..d326f4f 100644 --- a/lib/mv_web/live/join_request_live/show.ex +++ b/lib/mv_web/live/join_request_live/show.ex @@ -128,20 +128,20 @@ defmodule MvWeb.JoinRequestLive.Show do <%= if @join_request do %>
+ <%!-- Single block: all applicant-provided data in join form order --%>
-

{gettext("Request data")}

+

{gettext("Applicant data")}

+
+ <%= for {label, value} <- applicant_data_rows(@join_request, @join_form_field_ids || []) do %> + <.field_row label={label} value={value} empty_text={gettext("Not specified")} /> + <% end %> +
+
+ + <%!-- Status and review (submitted_at, status; if decided: approved/rejected at, reviewed by) --%> +
+

{gettext("Status and review")}

- <.field_row label={gettext("Email")} value={@join_request.email} /> - <.field_row - label={gettext("First name")} - value={@join_request.first_name} - empty_text={gettext("Not specified")} - /> - <.field_row - label={gettext("Last name")} - value={@join_request.last_name} - empty_text={gettext("Not specified")} - /> <.field_row label={gettext("Submitted at")} value={DateFormatter.format_datetime(@join_request.submitted_at)} @@ -154,24 +154,7 @@ defmodule MvWeb.JoinRequestLive.Show do
-
-
- - <%= if map_size(@join_request.form_data || %{}) > 0 do %> -
-

{gettext("Additional form data")}

-
- <%= for {key, value} <- format_form_data(@join_request.form_data, @join_form_field_ids || []) do %> - <.field_row label={key} value={to_string(value)} /> - <% end %> -
-
- <% end %> - - <%= if @join_request.status in [:approved, :rejected] do %> -
-

{gettext("Review information")}

-
+ <%= if @join_request.status in [:approved, :rejected] do %> <%= if @join_request.approved_at do %> <.field_row label={gettext("Approved at")} @@ -189,9 +172,9 @@ defmodule MvWeb.JoinRequestLive.Show do value={JoinRequestHelpers.reviewer_display(@join_request)} empty_text="-" /> -
+ <% end %>
- <% end %> +
<%= if @join_request.status == :submitted do %>
@@ -240,40 +223,71 @@ defmodule MvWeb.JoinRequestLive.Show do """ end - # Formats form_data for display in join-form order; legacy keys (not in current - # join_form_field_ids) are appended at the end, sorted by label for stability. - # Labels: member field keys → human-readable; UUID keys kept as-is (custom field IDs). - defp format_form_data(nil, _ordered_field_ids), do: [] - - defp format_form_data(form_data, ordered_field_ids) when is_map(form_data) 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 member_field_strings = Constants.member_fields() |> Enum.map(&Atom.to_string/1) + form_data = join_request.form_data || %{} + + typed = %{ + "email" => join_request.email, + "first_name" => join_request.first_name, + "last_name" => join_request.last_name + } - # First: entries in current join form order (only keys present in form_data) in_order = ordered_field_ids - |> Enum.filter(&Map.has_key?(form_data, &1)) |> Enum.map(fn key -> - value = form_data[key] + value = Map.get(typed, key) || Map.get(form_data, key) label = field_key_to_label(key, member_field_strings) - {label, value} + {label, format_applicant_value(value)} end) - # Then: keys in form_data that are not in current settings (e.g. removed fields on old requests) legacy_keys = form_data |> Map.keys() - |> Enum.reject(&(&1 in ordered_field_ids)) + |> Enum.reject(fn k -> + k in ordered_field_ids or k in ["email", "first_name", "last_name"] + end) |> Enum.sort() legacy_entries = Enum.map(legacy_keys, fn key -> label = field_key_to_label(key, member_field_strings) - {label, form_data[key]} + {label, format_applicant_value(form_data[key])} end) in_order ++ legacy_entries end + defp format_applicant_value(nil), do: nil + defp format_applicant_value(""), do: nil + defp format_applicant_value(%Date{} = date), do: DateFormatter.format_date(date) + defp format_applicant_value(value) when is_map(value), do: format_applicant_value_from_map(value) + defp format_applicant_value(value) when is_boolean(value), + do: if(value, do: gettext("Yes"), else: gettext("No")) + defp format_applicant_value(value) when is_binary(value) or is_number(value), + do: to_string(value) + defp format_applicant_value(value), do: to_string(value) + + defp format_applicant_value_from_map(value) do + raw = Map.get(value, "_union_value") || Map.get(value, "value") + type = Map.get(value, "_union_type") || Map.get(value, "type") + + if raw && type in ["date", :date] do + format_applicant_value(raw) + else + format_applicant_value_simple(raw, value) + end + end + + defp format_applicant_value_simple(raw, _value) when is_binary(raw), do: raw + defp format_applicant_value_simple(raw, _value) when is_boolean(raw), + do: if(raw, do: gettext("Yes"), else: gettext("No")) + 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)), diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index e99aa0d..1b163d4 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -110,11 +110,6 @@ msgstr "Feld hinzufügen" msgid "Add members" msgstr "Mitglieder hinzufügen" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Additional form data" -msgstr "Weitere Formulardaten" - #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Address" @@ -1121,7 +1116,6 @@ msgstr "Rolle bearbeiten" #: lib/mv_web/live/group_live/show.ex #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/index.html.heex #: lib/mv_web/live/member_live/show.ex @@ -1374,7 +1368,6 @@ msgid "First Name" msgstr "Vorname" #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format msgid "First name" @@ -1792,7 +1785,6 @@ msgid "Last Name" msgstr "Nachname" #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format msgid "Last name" @@ -2178,6 +2170,7 @@ msgstr "Neuer Betrag" #: lib/mv_web/live/components/member_filter_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/member_live/index/formatter.ex #: lib/mv_web/live/member_live/show.ex @@ -2681,11 +2674,6 @@ msgstr "Mitglied aus Gruppe entfernen" msgid "Reorder" msgstr "Umordnen" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Request data" -msgstr "Antragsdaten" - #: lib/mv_web/live/custom_field_live/form_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex #: lib/mv_web/live/global_settings_live.ex @@ -2711,11 +2699,6 @@ msgstr "Passwort zurücksetzen" msgid "Review by" msgstr "Geprüft von" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Review information" -msgstr "Bearbeitungsinformationen" - #: lib/mv_web/live/join_request_live/index.ex #, elixir-autogen, elixir-format msgid "Reviewed at" @@ -3575,6 +3558,7 @@ msgstr "Jährliches Intervall – Beitrittszeitraum einbezogen" #: lib/mv_web/live/components/member_filter_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/member_live/index/formatter.ex #: lib/mv_web/live/member_live/show.ex @@ -3776,3 +3760,38 @@ msgstr "aktualisiert" #, elixir-autogen, elixir-format msgid "without %{name}" msgstr "ohne %{name}" + +#: lib/mv_web/live/join_request_live/show.ex +#, elixir-autogen, elixir-format +msgid "Applicant data" +msgstr "Angaben des Antragstellers" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Copy" +msgstr "Kopieren" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Copy join page URL" +msgstr "URL der Beitrittsseite kopieren" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Join page URL" +msgstr "URL der Beitrittsseite" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Join page URL copied to clipboard." +msgstr "URL der Beitrittsseite in die Zwischenablage kopiert." + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Link to the public join page (share this with applicants):" +msgstr "Link zur öffentlichen Beitrittsseite (diesen Link mit Interessent*innen teilen):" + +#: lib/mv_web/live/join_request_live/show.ex +#, elixir-autogen, elixir-format +msgid "Status and review" +msgstr "Status und Prüfung" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 1679228..60e77c1 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -111,11 +111,6 @@ msgstr "" msgid "Add members" msgstr "" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Additional form data" -msgstr "" - #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Address" @@ -1122,7 +1117,6 @@ msgstr "" #: lib/mv_web/live/group_live/show.ex #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/index.html.heex #: lib/mv_web/live/member_live/show.ex @@ -1375,7 +1369,6 @@ msgid "First Name" msgstr "" #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format msgid "First name" @@ -1793,7 +1786,6 @@ msgid "Last Name" msgstr "" #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format msgid "Last name" @@ -2179,6 +2171,7 @@ msgstr "" #: lib/mv_web/live/components/member_filter_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/member_live/index/formatter.ex #: lib/mv_web/live/member_live/show.ex @@ -2682,11 +2675,6 @@ msgstr "" msgid "Reorder" msgstr "" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Request data" -msgstr "" - #: lib/mv_web/live/custom_field_live/form_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex #: lib/mv_web/live/global_settings_live.ex @@ -2712,11 +2700,6 @@ msgstr "" msgid "Review by" msgstr "" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Review information" -msgstr "" - #: lib/mv_web/live/join_request_live/index.ex #, elixir-autogen, elixir-format msgid "Reviewed at" @@ -3575,6 +3558,7 @@ msgstr "" #: lib/mv_web/live/components/member_filter_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/member_live/index/formatter.ex #: lib/mv_web/live/member_live/show.ex @@ -3776,3 +3760,38 @@ msgstr "" #, elixir-autogen, elixir-format msgid "without %{name}" msgstr "" + +#: lib/mv_web/live/join_request_live/show.ex +#, elixir-autogen, elixir-format +msgid "Applicant data" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Copy" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Copy join page URL" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Join page URL" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Join page URL copied to clipboard." +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Link to the public join page (share this with applicants):" +msgstr "" + +#: lib/mv_web/live/join_request_live/show.ex +#, elixir-autogen, elixir-format +msgid "Status and review" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index 8a016ed..4e4f87b 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -111,11 +111,6 @@ msgstr "" msgid "Add members" msgstr "" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Additional form data" -msgstr "Additional form data" - #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Address" @@ -1122,7 +1117,6 @@ msgstr "" #: lib/mv_web/live/group_live/show.ex #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/index.html.heex #: lib/mv_web/live/member_live/show.ex @@ -1375,7 +1369,6 @@ msgid "First Name" msgstr "" #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format, fuzzy msgid "First name" @@ -1793,7 +1786,6 @@ msgid "Last Name" msgstr "" #: lib/mv_web/live/join_request_live/index.ex -#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format, fuzzy msgid "Last name" @@ -2179,6 +2171,7 @@ msgstr "" #: lib/mv_web/live/components/member_filter_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/member_live/index/formatter.ex #: lib/mv_web/live/member_live/show.ex @@ -2682,11 +2675,6 @@ msgstr "" msgid "Reorder" msgstr "Reorder" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Request data" -msgstr "Request data" - #: lib/mv_web/live/custom_field_live/form_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex #: lib/mv_web/live/global_settings_live.ex @@ -2712,11 +2700,6 @@ msgstr "Reset your password" msgid "Review by" msgstr "Review by" -#: lib/mv_web/live/join_request_live/show.ex -#, elixir-autogen, elixir-format -msgid "Review information" -msgstr "Review information" - #: lib/mv_web/live/join_request_live/index.ex #, elixir-autogen, elixir-format msgid "Reviewed at" @@ -3575,6 +3558,7 @@ msgstr "" #: lib/mv_web/live/components/member_filter_component.ex #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/join_request_live/show.ex #: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/member_live/index/formatter.ex #: lib/mv_web/live/member_live/show.ex @@ -3776,3 +3760,38 @@ msgstr "" #, elixir-autogen, elixir-format msgid "without %{name}" msgstr "without %{name}" + +#: lib/mv_web/live/join_request_live/show.ex +#, elixir-autogen, elixir-format +msgid "Applicant data" +msgstr "Applicant data" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Copy" +msgstr "Copy" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Copy join page URL" +msgstr "Copy join page URL" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Join page URL" +msgstr "Join page URL" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Join page URL copied to clipboard." +msgstr "Join page URL copied to clipboard." + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Link to the public join page (share this with applicants):" +msgstr "Link to the public join page (share this with applicants):" + +#: lib/mv_web/live/join_request_live/show.ex +#, elixir-autogen, elixir-format +msgid "Status and review" +msgstr "Status and review" diff --git a/priv/repo/seeds_bootstrap.exs b/priv/repo/seeds_bootstrap.exs index 7aafaac..9947704 100644 --- a/priv/repo/seeds_bootstrap.exs +++ b/priv/repo/seeds_bootstrap.exs @@ -263,6 +263,21 @@ default_hidden_in_overview = %{ "membership_fee_start_date" => false } +# Default join form field selection (email + name + address + join_date); join form stays disabled. +default_join_form_field_ids = [ + "email", + "first_name", + "last_name", + "street", + "house_number", + "postal_code", + "city", + "country", + "join_date" +] + +default_join_form_field_required = %{"email" => true} + case Membership.get_settings() do {:ok, existing_settings} -> updates = @@ -304,7 +319,9 @@ case Membership.get_settings() do |> Ash.Changeset.for_create(:create, %{ club_name: default_club_name, member_field_visibility: default_hidden_in_overview, - default_membership_fee_type_id: default_fee_type.id + default_membership_fee_type_id: default_fee_type.id, + join_form_field_ids: default_join_form_field_ids, + join_form_field_required: default_join_form_field_required }) |> Ash.create!() end diff --git a/priv/repo/seeds_dev.exs b/priv/repo/seeds_dev.exs index 436507f..5b3de9f 100644 --- a/priv/repo/seeds_dev.exs +++ b/priv/repo/seeds_dev.exs @@ -481,19 +481,28 @@ for {email, values} <- custom_value_assignments do end end -# Join form: enable so membership application list is visible in dev +# Join form: enable so membership application list is visible in dev; default field list includes address + join_date +default_join_form_field_ids = [ + "email", + "first_name", + "last_name", + "street", + "house_number", + "postal_code", + "city", + "country", + "join_date" +] + +default_join_form_field_required = %{"email" => true} + case Membership.get_settings() do {:ok, settings} -> unless settings.join_form_enabled do Membership.update_settings(settings, %{ join_form_enabled: true, - join_form_field_ids: settings.join_form_field_ids || ["email", "first_name", "last_name", "city"], - join_form_field_required: settings.join_form_field_required || %{ - "email" => true, - "first_name" => false, - "last_name" => false, - "city" => false - } + join_form_field_ids: settings.join_form_field_ids || default_join_form_field_ids, + join_form_field_required: settings.join_form_field_required || default_join_form_field_required }) end _ ->