From 8cbd481709c61c6ec0e9fc49ac35daded69dcee8 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 12 Dec 2025 19:05:41 +0100 Subject: [PATCH] refactor: migrate MembershipFeeSettingsLive to AshPhoenix.Form --- lib/membership/setting.ex | 2 + .../changes/normalize_default_fee_type_id.ex | 19 ++++ .../live/membership_fee_settings_live.ex | 86 +++++++------------ 3 files changed, 51 insertions(+), 56 deletions(-) create mode 100644 lib/membership/setting/changes/normalize_default_fee_type_id.ex diff --git a/lib/membership/setting.ex b/lib/membership/setting.ex index 602eab9..eedc47c 100644 --- a/lib/membership/setting.ex +++ b/lib/membership/setting.ex @@ -95,6 +95,8 @@ defmodule Mv.Membership.Setting do description "Updates the membership fee configuration" require_atomic? false accept [:include_joining_cycle, :default_membership_fee_type_id] + + change Mv.Membership.Setting.Changes.NormalizeDefaultFeeTypeId end end diff --git a/lib/membership/setting/changes/normalize_default_fee_type_id.ex b/lib/membership/setting/changes/normalize_default_fee_type_id.ex new file mode 100644 index 0000000..fdbe1c8 --- /dev/null +++ b/lib/membership/setting/changes/normalize_default_fee_type_id.ex @@ -0,0 +1,19 @@ +defmodule Mv.Membership.Setting.Changes.NormalizeDefaultFeeTypeId do + @moduledoc """ + Ash change that normalizes empty strings to nil for default_membership_fee_type_id. + + HTML forms submit empty select values as empty strings (""), but the database + expects nil for optional UUID fields. This change converts "" to nil. + """ + use Ash.Resource.Change + + def change(changeset, _opts, _context) do + default_fee_type_id = Ash.Changeset.get_attribute(changeset, :default_membership_fee_type_id) + + if default_fee_type_id == "" do + Ash.Changeset.force_change_attribute(changeset, :default_membership_fee_type_id, nil) + else + changeset + end + end +end diff --git a/lib/mv_web/live/membership_fee_settings_live.ex b/lib/mv_web/live/membership_fee_settings_live.ex index 070b730..5ca32e9 100644 --- a/lib/mv_web/live/membership_fee_settings_live.ex +++ b/lib/mv_web/live/membership_fee_settings_live.ex @@ -25,37 +25,25 @@ defmodule MvWeb.MembershipFeeSettingsLive do |> assign(:page_title, gettext("Membership Fee Settings")) |> assign(:settings, settings) |> assign(:membership_fee_types, membership_fee_types) - |> assign(:selected_fee_type_id, settings.default_membership_fee_type_id) - |> assign(:include_joining_cycle, settings.include_joining_cycle) - |> assign(:changeset, to_form(%{}, as: :settings))} + |> assign_form()} end @impl true def handle_event("validate", %{"settings" => params}, socket) do - changeset = - %{} - |> validate_settings(params) - |> to_form(as: :settings) - - {:noreply, assign(socket, changeset: changeset)} + {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, params))} end def handle_event("save", %{"settings" => params}, socket) do - case update_settings(socket.assigns.settings, params) do + case AshPhoenix.Form.submit(socket.assigns.form, params: params) do {:ok, updated_settings} -> {:noreply, socket - |> put_flash(:info, gettext("Settings saved successfully.")) |> assign(:settings, updated_settings) - |> assign(:selected_fee_type_id, updated_settings.default_membership_fee_type_id) - |> assign(:include_joining_cycle, updated_settings.include_joining_cycle) - |> assign(:changeset, to_form(%{}, as: :settings))} + |> put_flash(:info, gettext("Settings saved successfully.")) + |> assign_form()} - {:error, changeset} -> - {:noreply, - socket - |> put_flash(:error, gettext("Failed to save settings. Please check the errors below.")) - |> assign(:changeset, to_form(changeset, as: :settings))} + {:error, form} -> + {:noreply, assign(socket, form: form)} end end @@ -80,7 +68,7 @@ defmodule MvWeb.MembershipFeeSettingsLive do <.form - for={@changeset} + for={@form} phx-change="validate" phx-submit="save" class="space-y-6" @@ -95,7 +83,10 @@ defmodule MvWeb.MembershipFeeSettingsLive do + <%= for {msg, _opts} <- @form.errors[:default_membership_fee_type_id] || [] do %> +

{msg}

+ <% end %>

{gettext( "This membership fee type is automatically assigned to all new members. Can be changed individually per member." @@ -124,13 +118,16 @@ defmodule MvWeb.MembershipFeeSettingsLive do type="checkbox" name="settings[include_joining_cycle]" class="checkbox checkbox-primary" - checked={@include_joining_cycle} + checked={@form[:include_joining_cycle].value} phx-debounce="blur" /> {gettext("Include joining cycle")} + <%= for {msg, _opts} <- @form.errors[:include_joining_cycle] || [] do %> +

{msg}

+ <% end %>

{gettext("When active: Members pay from the cycle of their joining.")} @@ -249,39 +246,16 @@ defmodule MvWeb.MembershipFeeSettingsLive do defp format_interval(:half_yearly), do: gettext("Half-yearly") defp format_interval(:yearly), do: gettext("Yearly") - defp validate_settings(attrs, params) do - attrs - |> Map.merge(params) - |> validate_default_fee_type() - end + defp assign_form(%{assigns: %{settings: settings}} = socket) do + form = + AshPhoenix.Form.for_update( + settings, + :update_membership_fee_settings, + api: Membership, + as: "settings", + forms: [auto?: true] + ) - defp validate_default_fee_type(%{"default_membership_fee_type_id" => ""} = attrs) do - Map.put(attrs, "default_membership_fee_type_id", nil) - end - - defp validate_default_fee_type(attrs), do: attrs - - defp update_settings(settings, params) do - # Convert empty string to nil for optional field - params = - if params["default_membership_fee_type_id"] == "" do - Map.put(params, "default_membership_fee_type_id", nil) - else - params - end - - # Convert checkbox value to boolean - params = - Map.update(params, "include_joining_cycle", false, fn - "true" -> true - "false" -> false - true -> true - false -> false - _ -> false - end) - - settings - |> Ash.Changeset.for_update(:update_membership_fee_settings, params) - |> Ash.update() + assign(socket, form: to_form(form)) end end