defmodule MvWeb.CustomFieldLive.Form do @moduledoc """ LiveView form for creating and editing custom fields (admin). ## Features - Create new custom field definitions - Edit existing custom fields - Select value type from supported types - Set immutable and required flags - Real-time validation ## Form Fields **Required:** - name - Unique identifier (e.g., "phone_mobile", "emergency_contact") - value_type - Data type (:string, :integer, :boolean, :date, :email) **Optional:** - description - Human-readable explanation - immutable - If true, values cannot be changed after creation (default: false) - required - If true, all members must have this custom field (default: false) ## Value Type Selection - `:string` - Text data (unlimited length) - `:integer` - Numeric data - `:boolean` - True/false flags - `:date` - Date values - `:email` - Validated email addresses ## Events - `validate` - Real-time form validation - `save` - Submit form (create or update custom field) ## Security Custom field management is restricted to admin users. """ use MvWeb, :live_view @impl true def render(assigns) do ~H""" <.header> {@page_title} <:subtitle> {gettext("Use this form to manage custom_field records in your database.")} <.form for={@form} id="custom_field-form" phx-change="validate" phx-submit="save"> <.input field={@form[:name]} type="text" label={gettext("Name")} /> <.input field={@form[:value_type]} type="select" label={gettext("Value type")} options={ Ash.Resource.Info.attribute(Mv.Membership.CustomField, :value_type).constraints[:one_of] } /> <.input field={@form[:description]} type="text" label={gettext("Description")} /> <.input field={@form[:immutable]} type="checkbox" label={gettext("Immutable")} /> <.input field={@form[:required]} type="checkbox" label={gettext("Required")} /> <.button phx-disable-with={gettext("Saving...")} variant="primary"> {gettext("Save Custom field")} <.button navigate={return_path(@return_to, @custom_field)}>{gettext("Cancel")} """ end @impl true def mount(params, _session, socket) do custom_field = case params["id"] do nil -> nil id -> Ash.get!(Mv.Membership.CustomField, id) end action = if is_nil(custom_field), do: "New", else: "Edit" page_title = action <> " " <> "Custom field" {:ok, socket |> assign(:return_to, return_to(params["return_to"])) |> assign(custom_field: custom_field) |> assign(:page_title, page_title) |> assign_form()} end defp return_to("show"), do: "show" defp return_to(_), do: "index" @impl true def handle_event("validate", %{"custom_field" => custom_field_params}, socket) do {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, custom_field_params))} end def handle_event("save", %{"custom_field" => custom_field_params}, socket) do case AshPhoenix.Form.submit(socket.assigns.form, params: custom_field_params) do {:ok, custom_field} -> notify_parent({:saved, custom_field}) action = case socket.assigns.form.source.type do :create -> gettext("create") :update -> gettext("update") other -> to_string(other) end socket = socket |> put_flash(:info, gettext("Custom field %{action} successfully", action: action)) |> push_navigate(to: return_path(socket.assigns.return_to, custom_field)) {:noreply, socket} {:error, form} -> {:noreply, assign(socket, form: form)} end end defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) defp assign_form(%{assigns: %{custom_field: custom_field}} = socket) do form = if custom_field do AshPhoenix.Form.for_update(custom_field, :update, as: "custom_field") else AshPhoenix.Form.for_create(Mv.Membership.CustomField, :create, as: "custom_field") end assign(socket, form: to_form(form)) end defp return_path("index", _custom_field), do: ~p"/custom_fields" defp return_path("show", custom_field), do: ~p"/custom_fields/#{custom_field.id}" end