mitgliederverwaltung/lib/mv_web/live/contribution_settings_live.ex
Moritz cd1af5aff5 feat: Add contribution management mock-up pages
Add non-functional preview pages for Contribution Types, Settings, and Member Contribution Periods with German translations
2025-12-03 14:32:09 +01:00

277 lines
9.3 KiB
Elixir
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

defmodule MvWeb.ContributionSettingsLive do
@moduledoc """
Mock-up LiveView for Contribution Settings (Admin).
This is a preview-only page that displays the planned UI for managing
global contribution settings. It shows static mock data and is not functional.
## Planned Features (Future Implementation)
- Set default contribution type for new members
- Configure whether joining period is included in contributions
- Explanatory text with examples
## Settings
- `default_contribution_type_id` - UUID of the default contribution type
- `include_joining_period` - Boolean whether to include joining period
## Note
This page is intentionally non-functional and serves as a UI mockup
for the upcoming Membership Contributions feature.
"""
use MvWeb, :live_view
@impl true
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:page_title, gettext("Contribution Settings"))
|> assign(:contribution_types, mock_contribution_types())
|> assign(:selected_type_id, "1")
|> assign(:include_joining_period, true)}
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_user={@current_user}>
<.mockup_warning />
<.header>
{gettext("Contribution Settings")}
<:subtitle>
{gettext("Configure global settings for membership contributions.")}
</:subtitle>
</.header>
<div class="grid gap-6 lg:grid-cols-2">
<%!-- Settings Form --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-cog-6-tooth" class="size-5" />
{gettext("Global Settings")}
</h2>
<form class="space-y-6">
<%!-- Default Contribution Type --%>
<fieldset class="fieldset">
<label class="label">
<span class="label-text font-semibold">
{gettext("Default Contribution Type")}
</span>
</label>
<select class="select select-bordered w-full" disabled>
<option :for={ct <- @contribution_types} selected={ct.id == @selected_type_id}>
{ct.name} ({format_currency(ct.amount)}, {format_interval(ct.interval)})
</option>
</select>
<p class="text-sm text-base-content/60 mt-2">
{gettext(
"This contribution type is automatically assigned to all new members. Can be changed individually per member."
)}
</p>
</fieldset>
<%!-- Include Joining Period --%>
<fieldset class="fieldset">
<label class="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
class="checkbox checkbox-primary"
checked={@include_joining_period}
disabled
/>
<span class="label-text font-semibold">
{gettext("Include joining period")}
</span>
</label>
<div class="ml-9 space-y-2">
<p class="text-sm text-base-content/60">
{gettext("When active: Members pay from the period of their joining.")}
</p>
<p class="text-sm text-base-content/60">
{gettext("When inactive: Members pay from the next full period after joining.")}
</p>
</div>
</fieldset>
<div class="divider"></div>
<button type="button" class="btn btn-primary w-full" disabled>
<.icon name="hero-check" class="size-5" />
{gettext("Save Settings")}
</button>
</form>
</div>
</div>
<%!-- Examples Card --%>
<div class="card bg-base-200">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-light-bulb" class="size-5" />
{gettext("Examples")}
</h2>
<.example_section
title={gettext("Yearly Interval - Joining Period 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")}
/>
<div class="divider"></div>
<.example_section
title={gettext("Yearly Interval - Joining Period 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")}
/>
<div class="divider"></div>
<.example_section
title={gettext("Quarterly Interval - Joining Period 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")}
/>
<div class="divider"></div>
<.example_section
title={gettext("Monthly Interval - Joining Period 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")}
/>
</div>
</div>
</div>
<.example_member_card />
</Layouts.app>
"""
end
# Example member card with link to period view
defp example_member_card(assigns) do
~H"""
<div class="card bg-base-100 shadow-xl mt-6">
<div class="card-body">
<h2 class="card-title">
<.icon name="hero-user" class="size-5" />
{gettext("Example: Member Contribution View")}
</h2>
<p class="text-base-content/70">
{gettext(
"See how the contribution periods will be displayed for an individual member. This example shows Maria Weber with multiple contribution periods."
)}
</p>
<div class="card-actions justify-end">
<.link navigate={~p"/contributions/member/example"} class="btn btn-primary btn-sm">
<.icon name="hero-eye" class="size-4" />
{gettext("View Example Member")}
</.link>
</div>
</div>
</div>
"""
end
# Mock-up warning banner component - subtle orange style
defp mockup_warning(assigns) do
~H"""
<div class="border border-warning text-warning bg-base-100 rounded-lg px-4 py-3 mb-6 flex items-center gap-3">
<.icon name="hero-exclamation-triangle" class="size-5 shrink-0" />
<div>
<span class="font-semibold">{gettext("Preview Mockup")}</span>
<span class="text-sm text-base-content/70 ml-2">
{gettext("This page is not functional and only displays the planned features.")}
</span>
</div>
</div>
"""
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"""
<div class="space-y-2">
<h3 class="font-semibold text-sm">{@title}</h3>
<div class="bg-base-300 rounded-lg p-3 text-sm space-y-1">
<p>
<span class="text-base-content/60">{gettext("Joining date")}:</span>
<span class="font-mono">{@joining_date}</span>
</p>
<p>
<span class="text-base-content/60">{gettext("Contribution start")}:</span>
<span class="font-mono font-semibold text-primary">{@start_date}</span>
</p>
<p>
<span class="text-base-content/60">{gettext("Generated periods")}:</span>
<span class="font-mono">
{Enum.join(@periods, ", ")}
</span>
</p>
</div>
<p class="text-xs text-base-content/60 italic">→ {@note}</p>
</div>
"""
end
# Mock data for demonstration
defp mock_contribution_types do
[
%{
id: "1",
name: gettext("Regular"),
amount: Decimal.new("60.00"),
interval: :yearly
},
%{
id: "2",
name: gettext("Reduced"),
amount: Decimal.new("30.00"),
interval: :yearly
},
%{
id: "3",
name: gettext("Student"),
amount: Decimal.new("5.00"),
interval: :monthly
},
%{
id: "4",
name: gettext("Family"),
amount: Decimal.new("25.00"),
interval: :quarterly
}
]
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")
end