Add non-functional preview pages for Contribution Types, Settings, and Member Contribution Periods with German translations
277 lines
9.3 KiB
Elixir
277 lines
9.3 KiB
Elixir
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
|