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
# Normalize checkbox value: "on" -> true, missing -> false
normalized_params =
if Map.has_key?(params, "include_joining_cycle") do
params
|> Map.update("include_joining_cycle", false, fn
"on" -> true
"true" -> true
true -> true
_ -> false
end)
else
Map.put(params, "include_joining_cycle", false)
end
{:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, normalized_params))}
end
def handle_event("save", %{"settings" => params}, socket) do
# Normalize checkbox value: "on" -> true, missing -> false
normalized_params =
if Map.has_key?(params, "include_joining_cycle") do
params
|> Map.update("include_joining_cycle", false, fn
"on" -> true
"true" -> true
true -> true
_ -> false
end)
else
Map.put(params, "include_joining_cycle", false)
end
case AshPhoenix.Form.submit(socket.assigns.form, params: normalized_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"""
<.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 --%>
<%!-- Include Joining Cycle --%>
<.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")}
/>
{gettext("Joining date")}: {@joining_date}
{gettext("Membership fee start")}: {@start_date}
{gettext("Generated cycles")}: {Enum.join(@periods, ", ")}
→ {@note}