defmodule MvWeb.CustomFieldLive.IndexComponent do @moduledoc """ LiveComponent for managing custom field definitions (embedded in settings). ## Features - List all custom fields - Display type information (name, value type, description) - Show immutable and required flags - Create new custom fields - Edit existing custom fields - Delete custom fields with confirmation (cascades to all custom field values) """ use MvWeb, :live_component @impl true def render(assigns) do ~H"""
<.header> {gettext("Custom Fields")} <:subtitle> {gettext("These will appear in addition to other data when adding new members.")} <:actions> <.button variant="primary" phx-click="new_custom_field" phx-target={@myself}> <.icon name="hero-plus" /> {gettext("New Custom field")} <%!-- Show form when creating or editing --%>
<.live_component module={MvWeb.CustomFieldLive.FormComponent} id={@form_id} custom_field={@editing_custom_field} on_save={ fn custom_field, action -> send(self(), {:custom_field_saved, custom_field, action}) end } on_cancel={fn -> send_update(__MODULE__, id: @id, show_form: false) end} />
<%!-- Hide table when form is visible --%> <.table :if={!@show_form} id="custom_fields" rows={@streams.custom_fields} row_click={ fn {_id, custom_field} -> JS.push("edit_custom_field", value: %{id: custom_field.id}, target: @myself) end } > <:col :let={{_id, custom_field}} label={gettext("Name")}>{custom_field.name} <:col :let={{_id, custom_field}} label={gettext("Value Type")}> {custom_field.value_type} <:col :let={{_id, custom_field}} label={gettext("Description")}> {custom_field.description} <:col :let={{_id, custom_field}} label={gettext("Show in Overview")}> {gettext("Yes")} {gettext("No")} <:action :let={{_id, custom_field}}> <.link phx-click={ JS.push("edit_custom_field", value: %{id: custom_field.id}, target: @myself) }> {gettext("Edit")} <:action :let={{_id, custom_field}}> <.link phx-click={JS.push("prepare_delete", value: %{id: custom_field.id}, target: @myself)}> {gettext("Delete")} <%!-- Delete Confirmation Modal --%>
""" end @impl true def update(assigns, socket) do # If show_form is explicitly provided in assigns, reset editing state socket = if Map.has_key?(assigns, :show_form) and assigns.show_form == false do socket |> assign(:editing_custom_field, nil) |> assign(:form_id, "custom-field-form-new") else socket end {:ok, socket |> assign(assigns) |> assign_new(:show_form, fn -> false end) |> assign_new(:form_id, fn -> "custom-field-form-new" end) |> assign_new(:editing_custom_field, fn -> nil end) |> assign_new(:show_delete_modal, fn -> false end) |> assign_new(:custom_field_to_delete, fn -> nil end) |> assign_new(:slug_confirmation, fn -> "" end) |> stream(:custom_fields, Ash.read!(Mv.Membership.CustomField), reset: true)} end @impl true def handle_event("new_custom_field", _params, socket) do {:noreply, socket |> assign(:show_form, true) |> assign(:editing_custom_field, nil) |> assign(:form_id, "custom-field-form-new")} end @impl true def handle_event("edit_custom_field", %{"id" => id}, socket) do custom_field = Ash.get!(Mv.Membership.CustomField, id) {:noreply, socket |> assign(:show_form, true) |> assign(:editing_custom_field, custom_field) |> assign(:form_id, "custom-field-form-#{id}")} end @impl true def handle_event("prepare_delete", %{"id" => id}, socket) do custom_field = Ash.get!(Mv.Membership.CustomField, id, load: [:assigned_members_count]) {:noreply, socket |> assign(:custom_field_to_delete, custom_field) |> assign(:show_delete_modal, true) |> assign(:slug_confirmation, "")} end @impl true def handle_event("update_slug_confirmation", %{"slug" => slug}, socket) do {:noreply, assign(socket, :slug_confirmation, slug)} end @impl true def handle_event("confirm_delete", _params, socket) do custom_field = socket.assigns.custom_field_to_delete if socket.assigns.slug_confirmation == custom_field.slug do case Ash.destroy(custom_field) do :ok -> send(self(), {:custom_field_deleted, custom_field}) {:noreply, socket |> assign(:show_delete_modal, false) |> assign(:custom_field_to_delete, nil) |> assign(:slug_confirmation, "") |> stream_delete(:custom_fields, custom_field)} {:error, error} -> send(self(), {:custom_field_delete_error, error}) {:noreply, socket |> assign(:show_delete_modal, false) |> assign(:custom_field_to_delete, nil) |> assign(:slug_confirmation, "")} end else send(self(), :custom_field_slug_mismatch) {:noreply, socket |> assign(:show_delete_modal, false) |> assign(:custom_field_to_delete, nil) |> assign(:slug_confirmation, "")} end end @impl true def handle_event("cancel_delete", _params, socket) do {:noreply, socket |> assign(:show_delete_modal, false) |> assign(:custom_field_to_delete, nil) |> assign(:slug_confirmation, "")} end end