Configurable member field "required" flag and Vereinfacht-required fields closes #440 #441

Merged
moritz merged 13 commits from fix/required_fields into main 2026-02-23 23:28:36 +01:00
Showing only changes of commit 27b9cbe814 - Show all commits

View file

@ -23,6 +23,8 @@ defmodule MvWeb.MemberLive.Form do
import MvWeb.LiveHelpers, only: [current_actor: 1, submit_form: 3]
alias Mv.Membership
alias Mv.Membership.Helpers.VisibilityConfig
alias Mv.MembershipFees
alias Mv.MembershipFees.MembershipFeeType
alias MvWeb.Helpers.MembershipFeeHelpers
@ -84,30 +86,54 @@ defmodule MvWeb.MemberLive.Form do
<%!-- Name Row --%>
<div class="flex gap-4">
<div class="w-48">
<.input field={@form[:first_name]} label={gettext("First Name")} />
<.input
field={@form[:first_name]}
label={gettext("First Name")}
required={@member_field_required_map[:first_name]}
/>
</div>
<div class="w-48">
<.input field={@form[:last_name]} label={gettext("Last Name")} />
<.input
field={@form[:last_name]}
label={gettext("Last Name")}
required={@member_field_required_map[:last_name]}
/>
</div>
</div>
<%!-- Address Row --%>
<div class="flex gap-4">
<div class="flex-1">
<.input field={@form[:street]} label={gettext("Street")} />
<.input
field={@form[:street]}
label={gettext("Street")}
required={@member_field_required_map[:street]}
/>
</div>
<div class="w-16">
<.input field={@form[:house_number]} label={gettext("Nr.")} />
<.input
field={@form[:house_number]}
label={gettext("Nr.")}
required={@member_field_required_map[:house_number]}
/>
</div>
<div class="w-24">
<.input field={@form[:postal_code]} label={gettext("Postal Code")} />
<.input
field={@form[:postal_code]}
label={gettext("Postal Code")}
required={@member_field_required_map[:postal_code]}
/>
</div>
<div class="w-32">
<.input field={@form[:city]} label={gettext("City")} />
<.input
field={@form[:city]}
label={gettext("City")}
required={@member_field_required_map[:city]}
/>
</div>
</div>
<%!-- Email --%>
<%!-- Email (always required) --%>
<div>
<.input field={@form[:email]} label={gettext("Email")} required type="email" />
</div>
@ -115,16 +141,31 @@ defmodule MvWeb.MemberLive.Form do
<%!-- Membership Dates Row --%>
<div class="flex gap-4">
<div class="w-36">
<.input field={@form[:join_date]} label={gettext("Join Date")} type="date" />
<.input
field={@form[:join_date]}
label={gettext("Join Date")}
type="date"
required={@member_field_required_map[:join_date]}
/>
</div>
<div class="w-36">
<.input field={@form[:exit_date]} label={gettext("Exit Date")} type="date" />
<.input
field={@form[:exit_date]}
label={gettext("Exit Date")}
type="date"
required={@member_field_required_map[:exit_date]}
/>
</div>
</div>
<%!-- Notes --%>
<div>
<.input field={@form[:notes]} label={gettext("Notes")} type="textarea" />
<.input
field={@form[:notes]}
label={gettext("Notes")}
type="textarea"
required={@member_field_required_map[:notes]}
/>
</div>
</div>
</.form_section>
@ -254,6 +295,9 @@ defmodule MvWeb.MemberLive.Form do
# Load available membership fee types
available_fee_types = load_available_fee_types(member, actor)
# Load settings to know which member fields are required (for asterisk/tooltip)
member_field_required_map = get_member_field_required_map()
{:ok,
socket
|> assign(:return_to, return_to(params["return_to"]))
@ -263,9 +307,38 @@ defmodule MvWeb.MemberLive.Form do
|> assign(:page_title, page_title)
|> assign(:available_fee_types, available_fee_types)
|> assign(:interval_warning, nil)
|> assign(:member_field_required_map, member_field_required_map)
|> assign_form()}
end
defp get_member_field_required_map do
vereinfacht_required? = Mv.Config.vereinfacht_configured?()
case Membership.get_settings() do
{:ok, settings} ->
required_config = settings.member_field_required || %{}
normalized = VisibilityConfig.normalize(required_config)
Mv.Constants.member_fields()
|> Enum.map(fn field ->
required =
field == :email ||
(vereinfacht_required? && Mv.Constants.vereinfacht_required_field?(field)) ||
Map.get(normalized, field, false)
{field, required}
end)
|> Map.new()
{:error, _} ->
# Email always required; Vereinfacht fields when integration active
Map.new(Mv.Constants.member_fields(), fn f ->
{f,
f == :email || (vereinfacht_required? && Mv.Constants.vereinfacht_required_field?(f))}
end)
end
end
defp return_to("show"), do: "show"
defp return_to(_), do: "index"