- {gettext("Contribution")}
+ <.form_section title={gettext("Membership Fee")}>
+
+
+
+ {gettext("Membership Fee Type")}
-
-
-
-
- <.input field={@form[:paid]} label={gettext("Paid")} type="checkbox" />
+
+ {gettext("None")}
+ <%= for fee_type <- @available_fee_types do %>
+
+ {fee_type.name} ({MembershipFeeHelpers.format_currency(fee_type.amount)}, {MembershipFeeHelpers.format_interval(
+ fee_type.interval
+ )})
+
+ <% end %>
+
+ <%= for {msg, _opts} <- @form.errors[:membership_fee_type_id] || [] do %>
+
{msg}
+ <% end %>
+ <%= if @interval_warning do %>
+
+ <.icon name="hero-exclamation-triangle" class="size-5" />
+ {@interval_warning}
+
+ <% end %>
+
+ {gettext(
+ "Select a membership fee type for this member. Members can only switch between types with the same interval."
+ )}
+
@@ -235,12 +243,15 @@ defmodule MvWeb.MemberLive.Form do
member =
case params["id"] do
nil -> nil
- id -> Ash.get!(Mv.Membership.Member, id)
+ id -> Ash.get!(Mv.Membership.Member, id, load: [:membership_fee_type])
end
page_title =
if is_nil(member), do: gettext("Create Member"), else: gettext("Edit Member")
+ # Load available membership fee types
+ available_fee_types = load_available_fee_types(member)
+
{:ok,
socket
|> assign(:return_to, return_to(params["return_to"]))
@@ -248,6 +259,8 @@ defmodule MvWeb.MemberLive.Form do
|> assign(:initial_custom_field_values, initial_custom_field_values)
|> assign(member: member)
|> assign(:page_title, page_title)
+ |> assign(:available_fee_types, available_fee_types)
+ |> assign(:interval_warning, nil)
|> assign_form()}
end
@@ -256,7 +269,53 @@ defmodule MvWeb.MemberLive.Form do
@impl true
def handle_event("validate", %{"member" => member_params}, socket) do
- {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, member_params))}
+ validated_form = AshPhoenix.Form.validate(socket.assigns.form, member_params)
+
+ # Check for interval mismatch if membership_fee_type_id changed
+ socket =
+ if Map.has_key?(member_params, "membership_fee_type_id") &&
+ socket.assigns.member &&
+ socket.assigns.member.membership_fee_type do
+ new_fee_type_id = member_params["membership_fee_type_id"]
+
+ if new_fee_type_id != "" &&
+ new_fee_type_id != socket.assigns.member.membership_fee_type_id do
+ new_fee_type = find_fee_type(socket.assigns.available_fee_types, new_fee_type_id)
+
+ if new_fee_type &&
+ new_fee_type.interval != socket.assigns.member.membership_fee_type.interval do
+ assign(
+ socket,
+ :interval_warning,
+ gettext(
+ "Warning: Changing from %{old_interval} to %{new_interval} is not allowed. Please select a membership fee type with the same interval.",
+ old_interval:
+ MembershipFeeHelpers.format_interval(
+ socket.assigns.member.membership_fee_type.interval
+ ),
+ new_interval: MembershipFeeHelpers.format_interval(new_fee_type.interval)
+ )
+ )
+ else
+ assign(socket, :interval_warning, nil)
+ end
+ else
+ assign(socket, :interval_warning, nil)
+ end
+ else
+ socket
+ end
+
+ {:noreply, assign(socket, form: validated_form)}
+ end
+
+ def handle_event(
+ "validate_membership_fee_type",
+ %{"member" => %{"membership_fee_type_id" => fee_type_id}},
+ socket
+ ) do
+ # Same validation as above, but triggered by select change
+ handle_event("validate", %{"member" => %{"membership_fee_type_id" => fee_type_id}}, socket)
end
def handle_event("save", %{"member" => member_params}, socket) do
@@ -348,6 +407,30 @@ defmodule MvWeb.MemberLive.Form do
defp return_path("show", nil), do: ~p"/members"
defp return_path("show", member), do: ~p"/members/#{member.id}"
+ # -----------------------------------------------------------------
+ # Helper Functions
+ # -----------------------------------------------------------------
+
+ defp load_available_fee_types(member) do
+ all_types =
+ MembershipFeeType
+ |> Ash.Query.sort(name: :asc)
+ |> Ash.read!(domain: MembershipFees)
+
+ # If member has a fee type, filter to same interval
+ if member && member.membership_fee_type do
+ Enum.filter(all_types, fn type ->
+ type.interval == member.membership_fee_type.interval
+ end)
+ else
+ all_types
+ end
+ end
+
+ defp find_fee_type(fee_types, fee_type_id) do
+ Enum.find(fee_types, &(&1.id == fee_type_id))
+ end
+
# -----------------------------------------------------------------
# Helper Functions for Custom Fields
# -----------------------------------------------------------------