defmodule MvWeb.MemberFieldLive.IndexComponent do @moduledoc """ LiveComponent for managing member field visibility in overview (embedded in settings). ## Features - List all member fields from Mv.Constants.member_fields() - Display show_in_overview status as badge (Yes/No) - Display required status for required fields (first_name, last_name, email) - Toggle show_in_overview flag for each field - Updates Settings.member_field_visibility """ use MvWeb, :live_component alias Mv.Membership @required_fields [:first_name, :last_name, :email] @impl true def render(assigns) do assigns = assigns |> assign(:member_fields, get_member_fields_with_visibility(assigns.settings)) |> assign(:required?, &required?/1) ~H"""
<.form_section title={gettext("Memberdata")}>

{gettext("These fields are neccessary for MILA to handle member identification and payment calculations in the future. This you cannot delete these fields but hide them in the member overview.")}

<.table id="member_fields" rows={@member_fields}> <:col :let={{_field_name, field_data}} label={gettext("Field Name")}> {format_field_name(field_data.field)} <:col :let={{_field_name, field_data}} label={gettext("Required")} class="max-w-[9.375rem] text-center" > {gettext("Required")} {gettext("Optional")} <:col :let={{_field_name, field_data}} label={gettext("Show in overview")} class="max-w-[9.375rem] text-center" > {gettext("Yes")} {gettext("No")} <:action :let={{_field_name, field_data}}>
""" end @impl true def update(assigns, socket) do {:ok, socket |> assign(assigns) |> assign_new(:settings, fn -> get_settings() end)} end @impl true def handle_event("toggle_field_visibility", %{"field" => field_string}, socket) do # Validate that the field is a valid member field before converting to atom valid_fields = Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1) if field_string in valid_fields do {:ok, settings} = Membership.get_settings() # Get current visibility config current_visibility = settings.member_field_visibility || %{} # Normalize keys to strings normalized_visibility = Enum.reduce(current_visibility, %{}, fn {key, value}, acc when is_atom(key) -> Map.put(acc, Atom.to_string(key), value) {key, value}, acc when is_binary(key) -> Map.put(acc, key, value) end) # Toggle the field visibility current_value = Map.get(normalized_visibility, field_string, true) new_value = !current_value updated_visibility = Map.put(normalized_visibility, field_string, new_value) # Update settings case Membership.update_member_field_visibility(settings, updated_visibility) do {:ok, updated_settings} -> # Send message to parent LiveView send(self(), {:member_field_visibility_updated}) {:noreply, socket |> assign(:settings, updated_settings)} {:error, error} -> # Send error message to parent LiveView for user feedback send(self(), {:member_field_visibility_error, error}) {:noreply, socket} end else {:noreply, socket} end end # Helper functions defp get_settings do case Membership.get_settings() do {:ok, settings} -> settings {:error, _} -> # Return a minimal struct-like map for fallback # This is only used for initial rendering, actual settings will be loaded properly %{member_field_visibility: %{}} end end defp get_member_fields_with_visibility(settings) do member_fields = Mv.Constants.member_fields() visibility_config = settings.member_field_visibility || %{} # Normalize visibility config keys to atoms normalized_config = normalize_visibility_config(visibility_config) Enum.map(member_fields, fn field -> show_in_overview = Map.get(normalized_config, field, true) {Atom.to_string(field), %{field: field, show_in_overview: show_in_overview}} end) end defp normalize_visibility_config(config) when is_map(config) do Enum.reduce(config, %{}, fn {key, value}, acc when is_atom(key) -> Map.put(acc, key, value) {key, value}, acc when is_binary(key) -> try do atom_key = String.to_existing_atom(key) Map.put(acc, atom_key, value) rescue ArgumentError -> acc end _, acc -> acc end) end defp normalize_visibility_config(_), do: %{} defp required?(field) when field in @required_fields, do: true defp required?(_), do: false defp format_field_name(field) when is_atom(field) do field |> Atom.to_string() |> String.replace("_", " ") |> String.split() |> Enum.map_join(" ", &String.capitalize/1) end end