defmodule MvWeb.MembershipFeeSettingsLive do @moduledoc """ LiveView for managing membership fee settings (Admin). Allows administrators to configure: - Default membership fee type for new members - Whether to include the joining cycle in membership fee generation """ use MvWeb, :live_view alias Mv.Membership alias Mv.MembershipFees.MembershipFeeType @impl true def mount(_params, _session, socket) do {:ok, settings} = Membership.get_settings() membership_fee_types = MembershipFeeType |> Ash.Query.sort(name: :asc) |> Ash.read!() {:ok, socket |> assign(:page_title, gettext("Membership Fee Settings")) |> assign(:settings, settings) |> assign(:membership_fee_types, membership_fee_types) |> assign_form()} end @impl true def handle_event("validate", %{"settings" => params}, socket) do {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, params))} end def handle_event("save", %{"settings" => params}, socket) do case AshPhoenix.Form.submit(socket.assigns.form, params: params) do {:ok, updated_settings} -> {:noreply, socket |> assign(:settings, updated_settings) |> put_flash(:info, gettext("Settings saved successfully.")) |> assign_form()} {:error, form} -> {:noreply, assign(socket, form: form)} end end @impl true def render(assigns) do ~H""" <.header> {gettext("Membership Fee Settings")} <:subtitle> {gettext("Configure global settings for membership fees.")}
<%!-- Settings Form --%>

<.icon name="hero-cog-6-tooth" class="size-5" /> {gettext("Global Settings")}

<.form for={@form} phx-change="validate" phx-submit="save" class="space-y-6" > <%!-- Default Membership Fee Type --%>
<%= 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." )}

<%!-- 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.")}

{gettext("When inactive: Members pay from the next full cycle after joining.")}

<%!-- Examples Card --%>

<.icon name="hero-light-bulb" class="size-5" /> {gettext("Examples")}

<.example_section title={gettext("Yearly Interval - Joining Cycle Included")} joining_date="15.03.2023" include_joining={true} start_date="01.01.2023" periods={["2023", "2024", "2025"]} note={gettext("Member pays for the year they joined")} />
<.example_section title={gettext("Yearly Interval - Joining Cycle Excluded")} joining_date="15.03.2023" include_joining={false} start_date="01.01.2024" periods={["2024", "2025"]} note={gettext("Member pays from the next full year")} />
<.example_section title={gettext("Quarterly Interval - Joining Cycle Excluded")} joining_date="15.05.2024" include_joining={false} start_date="01.07.2024" periods={["Q3/2024", "Q4/2024", "Q1/2025"]} note={gettext("Member pays from the next full quarter")} />
<.example_section title={gettext("Monthly Interval - Joining Cycle Included")} joining_date="15.03.2024" include_joining={true} start_date="01.03.2024" periods={["03/2024", "04/2024", "05/2024", "..."]} note={gettext("Member pays from the joining month")} />
""" end # Example section component attr :title, :string, required: true attr :joining_date, :string, required: true attr :include_joining, :boolean, required: true attr :start_date, :string, required: true attr :periods, :list, required: true attr :note, :string, required: true defp example_section(assigns) do ~H"""

{@title}

{gettext("Joining date")}: {@joining_date}

{gettext("Membership fee start")}: {@start_date}

{gettext("Generated cycles")}: {Enum.join(@periods, ", ")}

→ {@note}

""" end defp format_currency(%Decimal{} = amount) do "#{Decimal.to_string(amount)} €" end defp format_interval(:monthly), do: gettext("Monthly") defp format_interval(:quarterly), do: gettext("Quarterly") defp format_interval(:half_yearly), do: gettext("Half-yearly") defp format_interval(:yearly), do: gettext("Yearly") 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] ) assign(socket, form: to_form(form)) end end