defmodule MvWeb.ContributionPeriodLive.Show do @moduledoc """ Mock-up LiveView for Member Contribution Periods (Admin/Treasurer View). This is a preview-only page that displays the planned UI for viewing and managing contribution periods for a specific member. It shows static mock data and is not functional. ## Planned Features (Future Implementation) - Display all contribution periods for a member - Show period dates, interval, amount, and status - Quick status change (paid/unpaid/suspended) - Bulk marking of multiple periods - Notes per 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("Member Contributions")) |> assign(:member, mock_member()) |> assign(:periods, mock_periods()) |> assign(:selected_periods, MapSet.new())} end @impl true def render(assigns) do ~H""" <.mockup_warning /> <.header> {gettext("Contributions for %{name}", name: "#{@member.first_name} #{@member.last_name}")} <:subtitle> {gettext("Contribution type")}: {@member.contribution_type} · {gettext("Member since")}: {@member.joined_at} <:actions> <.link navigate={~p"/contribution_settings"} class="btn btn-ghost btn-sm"> <.icon name="hero-arrow-left" class="size-4" /> {gettext("Back to Settings")} <%!-- Member Info Card --%>
{gettext("Email")}

{@member.email}

{gettext("Contribution Start")}

{@member.contribution_start}

{gettext("Total Contributions")}

{length(@periods)}

{gettext("Open Contributions")}

{Enum.count(@periods, &(&1.status == :unpaid))}

<%!-- Contribution Type Change --%>
{gettext("Change Contribution Type")}: <.icon name="hero-question-mark-circle" class="inline size-4" /> {gettext("Why are not all contribution types shown?")}
<%!-- Bulk Actions --%>
{ngettext( "%{count} period selected", "%{count} periods selected", MapSet.size(@selected_periods), count: MapSet.size(@selected_periods) )}
<%!-- Periods Table --%>
{gettext("Time Period")} {gettext("Interval")} {gettext("Amount")} {gettext("Status")} {gettext("Notes")} {gettext("Actions")}
{period.period_start} – {period.period_end}
{gettext("Current")}
{format_interval(period.interval)} {format_currency(period.amount)} <.status_badge status={period.status} /> {period.notes}
<.link href="#" class={[ "cursor-not-allowed", if(period.status == :paid, do: "invisible", else: "opacity-50") ]} > {gettext("Paid")} <.link href="#" class={[ "cursor-not-allowed", if(period.status == :suspended, do: "invisible", else: "opacity-50") ]} > {gettext("Suspend")} <.link href="#" class={[ "cursor-not-allowed", if(period.status != :paid, do: "invisible", else: "opacity-50") ]} > {gettext("Reopen")} <.link href="#" class="opacity-50 cursor-not-allowed"> {gettext("Note")}
""" end # Mock-up warning banner component - subtle orange style defp mockup_warning(assigns) do ~H"""
<.icon name="hero-exclamation-triangle" class="size-5 shrink-0" />
{gettext("Preview Mockup")} – {gettext("This page is not functional and only displays the planned features.")}
""" end # Status badge component attr :status, :atom, required: true defp status_badge(%{status: :paid} = assigns) do ~H""" <.icon name="hero-check-circle-mini" class="size-3" /> {gettext("Paid")} """ end defp status_badge(%{status: :unpaid} = assigns) do ~H""" <.icon name="hero-x-circle-mini" class="size-3" /> {gettext("Unpaid")} """ end defp status_badge(%{status: :suspended} = assigns) do ~H""" <.icon name="hero-pause-circle-mini" class="size-3" /> {gettext("Suspended")} """ end defp period_row_class(:unpaid), do: "bg-error/5" defp period_row_class(:suspended), do: "bg-base-200/50" defp period_row_class(_), do: "" # Mock member data defp mock_member do %{ id: "123", first_name: "Maria", last_name: "Weber", email: "maria.weber@example.de", contribution_type: gettext("Regular"), joined_at: "15.03.2021", contribution_start: "01.01.2021" } end # Mock periods data defp mock_periods do [ %{ id: "p1", period_start: "01.01.2025", period_end: "31.12.2025", interval: :yearly, amount: Decimal.new("60.00"), status: :unpaid, notes: nil, is_current: true }, %{ id: "p2", period_start: "01.01.2024", period_end: "31.12.2024", interval: :yearly, amount: Decimal.new("60.00"), status: :paid, notes: gettext("Paid via bank transfer"), is_current: false }, %{ id: "p3", period_start: "01.01.2023", period_end: "31.12.2023", interval: :yearly, amount: Decimal.new("50.00"), status: :paid, notes: nil, is_current: false }, %{ id: "p4", period_start: "01.01.2022", period_end: "31.12.2022", interval: :yearly, amount: Decimal.new("50.00"), status: :paid, notes: nil, is_current: false }, %{ id: "p5", period_start: "01.01.2021", period_end: "31.12.2021", interval: :yearly, amount: Decimal.new("50.00"), status: :suspended, notes: gettext("Joining year - reduced to 0"), is_current: false } ] 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