refactor: Remove deprecated LiveViews
- Remove CustomFieldValueLive (Index, Form, Show) - Remove ContributionTypeLive.Index - Remove ContributionPeriodLive.Show - Remove corresponding routes from router - Remove references in CustomFieldValueLive.Index
This commit is contained in:
parent
d72bb8869f
commit
4154296b54
7 changed files with 0 additions and 1086 deletions
|
|
@ -1,345 +0,0 @@
|
||||||
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"""
|
|
||||||
<Layouts.app flash={@flash} current_user={@current_user}>
|
|
||||||
<.mockup_warning />
|
|
||||||
|
|
||||||
<.header>
|
|
||||||
{gettext("Contributions for %{name}", name: MvWeb.Helpers.MemberHelpers.display_name(@member))}
|
|
||||||
<:subtitle>
|
|
||||||
{gettext("Contribution type")}:
|
|
||||||
<span class="font-semibold">{@member.contribution_type}</span>
|
|
||||||
· {gettext("Member since")}: <span class="font-mono">{@member.joined_at}</span>
|
|
||||||
</:subtitle>
|
|
||||||
<:actions>
|
|
||||||
<.link navigate={~p"/membership_fee_settings"} class="btn btn-ghost btn-sm">
|
|
||||||
<.icon name="hero-arrow-left" class="size-4" />
|
|
||||||
{gettext("Back to Settings")}
|
|
||||||
</.link>
|
|
||||||
</:actions>
|
|
||||||
</.header>
|
|
||||||
|
|
||||||
<%!-- Member Info Card --%>
|
|
||||||
<div class="mb-6 shadow card bg-base-100">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="grid grid-cols-2 gap-4 md:grid-cols-4">
|
|
||||||
<div>
|
|
||||||
<span class="text-sm text-base-content/60">{gettext("Email")}</span>
|
|
||||||
<p class="font-medium">{@member.email}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="text-sm text-base-content/60">{gettext("Contribution Start")}</span>
|
|
||||||
<p class="font-mono">{@member.contribution_start}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="text-sm text-base-content/60">{gettext("Total Contributions")}</span>
|
|
||||||
<p class="font-semibold">{length(@periods)}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="text-sm text-base-content/60">{gettext("Open Contributions")}</span>
|
|
||||||
<p class="font-semibold text-error">
|
|
||||||
{Enum.count(@periods, &(&1.status == :unpaid))}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%!-- Contribution Type Change --%>
|
|
||||||
<div class="mb-6 card bg-base-200">
|
|
||||||
<div class="py-4 card-body">
|
|
||||||
<div class="flex flex-wrap items-center gap-4">
|
|
||||||
<span class="font-semibold">{gettext("Change Contribution Type")}:</span>
|
|
||||||
<select class="w-64 select select-bordered select-sm" disabled>
|
|
||||||
<option selected>{@member.contribution_type} (60,00 €, {gettext("Yearly")})</option>
|
|
||||||
<option>{gettext("Reduced")} (30,00 €, {gettext("Yearly")})</option>
|
|
||||||
<option>{gettext("Honorary")} (0,00 €, {gettext("Yearly")})</option>
|
|
||||||
</select>
|
|
||||||
<span
|
|
||||||
class="text-sm text-base-content/60 cursor-help tooltip tooltip-bottom"
|
|
||||||
data-tip={
|
|
||||||
gettext(
|
|
||||||
"Members can only switch between contribution types with the same payment interval (e.g., yearly to yearly). This prevents complex period overlaps."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<.icon name="hero-question-mark-circle" class="inline size-4" />
|
|
||||||
{gettext("Why are not all contribution types shown?")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%!-- Bulk Actions --%>
|
|
||||||
<div class="flex flex-wrap items-center gap-4 mb-4">
|
|
||||||
<span class="text-sm text-base-content/60">
|
|
||||||
{ngettext(
|
|
||||||
"%{count} period selected",
|
|
||||||
"%{count} periods selected",
|
|
||||||
MapSet.size(@selected_periods),
|
|
||||||
count: MapSet.size(@selected_periods)
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<button class="btn btn-sm btn-success" disabled>
|
|
||||||
<.icon name="hero-check" class="size-4" />
|
|
||||||
{gettext("Mark as Paid")}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-ghost" disabled>
|
|
||||||
<.icon name="hero-minus-circle" class="size-4" />
|
|
||||||
{gettext("Mark as Suspended")}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-ghost" disabled>
|
|
||||||
<.icon name="hero-x-circle" class="size-4" />
|
|
||||||
{gettext("Mark as Unpaid")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%!-- Periods Table --%>
|
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<table class="table table-zebra">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<input type="checkbox" class="checkbox checkbox-sm" disabled />
|
|
||||||
</th>
|
|
||||||
<th>{gettext("Time Period")}</th>
|
|
||||||
<th>{gettext("Interval")}</th>
|
|
||||||
<th>{gettext("Amount")}</th>
|
|
||||||
<th>{gettext("Status")}</th>
|
|
||||||
<th>{gettext("Notes")}</th>
|
|
||||||
<th>{gettext("Actions")}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr :for={period <- @periods} class={period_row_class(period.status)}>
|
|
||||||
<td>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class="checkbox checkbox-sm"
|
|
||||||
checked={MapSet.member?(@selected_periods, period.id)}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="font-mono">
|
|
||||||
{period.period_start} – {period.period_end}
|
|
||||||
</div>
|
|
||||||
<div :if={period.is_current} class="mt-1 badge badge-info badge-sm">
|
|
||||||
{gettext("Current")}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge badge-outline badge-sm">{format_interval(period.interval)}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="font-mono">{format_currency(period.amount)}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<.status_badge status={period.status} />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span :if={period.notes} class="text-sm italic text-base-content/60">
|
|
||||||
{period.notes}
|
|
||||||
</span>
|
|
||||||
<span :if={!period.notes} class="text-base-content/30">—</span>
|
|
||||||
</td>
|
|
||||||
<td class="w-0 font-semibold whitespace-nowrap">
|
|
||||||
<div class="flex gap-4">
|
|
||||||
<.link
|
|
||||||
href="#"
|
|
||||||
class={[
|
|
||||||
"cursor-not-allowed",
|
|
||||||
if(period.status == :paid, do: "invisible", else: "opacity-50")
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{gettext("Paid")}
|
|
||||||
</.link>
|
|
||||||
<.link
|
|
||||||
href="#"
|
|
||||||
class={[
|
|
||||||
"cursor-not-allowed",
|
|
||||||
if(period.status == :suspended, do: "invisible", else: "opacity-50")
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{gettext("Suspend")}
|
|
||||||
</.link>
|
|
||||||
<.link
|
|
||||||
href="#"
|
|
||||||
class={[
|
|
||||||
"cursor-not-allowed",
|
|
||||||
if(period.status != :paid, do: "invisible", else: "opacity-50")
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{gettext("Reopen")}
|
|
||||||
</.link>
|
|
||||||
<.link href="#" class="opacity-50 cursor-not-allowed">
|
|
||||||
{gettext("Note")}
|
|
||||||
</.link>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</Layouts.app>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Mock-up warning banner component - subtle orange style
|
|
||||||
defp mockup_warning(assigns) do
|
|
||||||
~H"""
|
|
||||||
<div class="flex items-center gap-3 px-4 py-3 mb-6 border rounded-lg border-warning text-warning bg-base-100">
|
|
||||||
<.icon name="hero-exclamation-triangle" class="size-5 shrink-0" />
|
|
||||||
<div>
|
|
||||||
<span class="font-semibold">{gettext("Preview Mockup")}</span>
|
|
||||||
<span class="ml-2 text-sm text-base-content/70">
|
|
||||||
– {gettext("This page is not functional and only displays the planned features.")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Status badge component
|
|
||||||
attr :status, :atom, required: true
|
|
||||||
|
|
||||||
defp status_badge(%{status: :paid} = assigns) do
|
|
||||||
~H"""
|
|
||||||
<span class="gap-1 badge badge-success">
|
|
||||||
<.icon name="hero-check-circle-mini" class="size-3" />
|
|
||||||
{gettext("Paid")}
|
|
||||||
</span>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
defp status_badge(%{status: :unpaid} = assigns) do
|
|
||||||
~H"""
|
|
||||||
<span class="gap-1 badge badge-error">
|
|
||||||
<.icon name="hero-x-circle-mini" class="size-3" />
|
|
||||||
{gettext("Unpaid")}
|
|
||||||
</span>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
defp status_badge(%{status: :suspended} = assigns) do
|
|
||||||
~H"""
|
|
||||||
<span class="gap-1 badge badge-neutral">
|
|
||||||
<.icon name="hero-pause-circle-mini" class="size-3" />
|
|
||||||
{gettext("Suspended")}
|
|
||||||
</span>
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
|
|
@ -1,205 +0,0 @@
|
||||||
defmodule MvWeb.ContributionTypeLive.Index do
|
|
||||||
@moduledoc """
|
|
||||||
Mock-up LiveView for Contribution Types Management (Admin).
|
|
||||||
|
|
||||||
This is a preview-only page that displays the planned UI for managing
|
|
||||||
contribution types. It shows static mock data and is not functional.
|
|
||||||
|
|
||||||
## Planned Features (Future Implementation)
|
|
||||||
- List all contribution types
|
|
||||||
- Display: Name, Amount, Interval, Member count
|
|
||||||
- Create new contribution types
|
|
||||||
- Edit existing contribution types (name, amount, description - NOT interval)
|
|
||||||
- Delete contribution types (if no members assigned)
|
|
||||||
|
|
||||||
## 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 Types"))
|
|
||||||
|> assign(:contribution_types, mock_contribution_types())}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def render(assigns) do
|
|
||||||
~H"""
|
|
||||||
<Layouts.app flash={@flash} current_user={@current_user}>
|
|
||||||
<.mockup_warning />
|
|
||||||
|
|
||||||
<.header>
|
|
||||||
{gettext("Contribution Types")}
|
|
||||||
<:subtitle>
|
|
||||||
{gettext("Manage contribution types for membership fees.")}
|
|
||||||
</:subtitle>
|
|
||||||
<:actions>
|
|
||||||
<button class="btn btn-primary" disabled>
|
|
||||||
<.icon name="hero-plus" /> {gettext("New Contribution Type")}
|
|
||||||
</button>
|
|
||||||
</:actions>
|
|
||||||
</.header>
|
|
||||||
|
|
||||||
<.table id="contribution_types" rows={@contribution_types} row_id={fn ct -> "ct-#{ct.id}" end}>
|
|
||||||
<:col :let={ct} label={gettext("Name")}>
|
|
||||||
<span class="font-medium">{ct.name}</span>
|
|
||||||
<p :if={ct.description} class="text-sm text-base-content/60">{ct.description}</p>
|
|
||||||
</:col>
|
|
||||||
|
|
||||||
<:col :let={ct} label={gettext("Amount")}>
|
|
||||||
<span class="font-mono">{format_currency(ct.amount)}</span>
|
|
||||||
</:col>
|
|
||||||
|
|
||||||
<:col :let={ct} label={gettext("Interval")}>
|
|
||||||
<span class="badge badge-outline">{format_interval(ct.interval)}</span>
|
|
||||||
</:col>
|
|
||||||
|
|
||||||
<:col :let={ct} label={gettext("Members")}>
|
|
||||||
<span class="badge badge-ghost">{ct.member_count}</span>
|
|
||||||
</:col>
|
|
||||||
|
|
||||||
<:action :let={_ct}>
|
|
||||||
<button class="btn btn-ghost btn-xs" disabled title={gettext("Edit")}>
|
|
||||||
<.icon name="hero-pencil" class="size-4" />
|
|
||||||
</button>
|
|
||||||
</:action>
|
|
||||||
|
|
||||||
<:action :let={ct}>
|
|
||||||
<button
|
|
||||||
class="btn btn-ghost btn-xs text-error"
|
|
||||||
disabled
|
|
||||||
title={
|
|
||||||
if ct.member_count > 0,
|
|
||||||
do: gettext("Cannot delete - members assigned"),
|
|
||||||
else: gettext("Delete")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<.icon name="hero-trash" class="size-4" />
|
|
||||||
</button>
|
|
||||||
</:action>
|
|
||||||
</.table>
|
|
||||||
|
|
||||||
<.info_card />
|
|
||||||
</Layouts.app>
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
|
|
||||||
# Info card explaining the contribution type concept
|
|
||||||
defp info_card(assigns) do
|
|
||||||
~H"""
|
|
||||||
<div class="card bg-base-200 mt-6">
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 class="card-title">
|
|
||||||
<.icon name="hero-information-circle" class="size-5" />
|
|
||||||
{gettext("About Contribution Types")}
|
|
||||||
</h2>
|
|
||||||
<div class="prose prose-sm max-w-none">
|
|
||||||
<p>
|
|
||||||
{gettext(
|
|
||||||
"Contribution types define different membership fee structures. Each type has a fixed cycle (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation."
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>{gettext("Name & Amount")}</strong>
|
|
||||||
- {gettext("Can be changed at any time. Amount changes affect future periods only.")}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>{gettext("Interval")}</strong>
|
|
||||||
- {gettext(
|
|
||||||
"Fixed after creation. Members can only switch between types with the same interval."
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>{gettext("Deletion")}</strong>
|
|
||||||
- {gettext("Only possible if no members are assigned to this type.")}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Mock data for demonstration
|
|
||||||
defp mock_contribution_types do
|
|
||||||
[
|
|
||||||
%{
|
|
||||||
id: "1",
|
|
||||||
name: gettext("Regular"),
|
|
||||||
description: gettext("Standard membership fee for regular members"),
|
|
||||||
amount: Decimal.new("60.00"),
|
|
||||||
interval: :yearly,
|
|
||||||
member_count: 45
|
|
||||||
},
|
|
||||||
%{
|
|
||||||
id: "2",
|
|
||||||
name: gettext("Reduced"),
|
|
||||||
description: gettext("Reduced fee for unemployed, pensioners, or low income"),
|
|
||||||
amount: Decimal.new("30.00"),
|
|
||||||
interval: :yearly,
|
|
||||||
member_count: 12
|
|
||||||
},
|
|
||||||
%{
|
|
||||||
id: "3",
|
|
||||||
name: gettext("Student"),
|
|
||||||
description: gettext("Monthly fee for students and trainees"),
|
|
||||||
amount: Decimal.new("5.00"),
|
|
||||||
interval: :monthly,
|
|
||||||
member_count: 8
|
|
||||||
},
|
|
||||||
%{
|
|
||||||
id: "4",
|
|
||||||
name: gettext("Family"),
|
|
||||||
description: gettext("Quarterly fee for family memberships"),
|
|
||||||
amount: Decimal.new("25.00"),
|
|
||||||
interval: :quarterly,
|
|
||||||
member_count: 15
|
|
||||||
},
|
|
||||||
%{
|
|
||||||
id: "5",
|
|
||||||
name: gettext("Supporting Member"),
|
|
||||||
description: gettext("Half-yearly contribution for supporting members"),
|
|
||||||
amount: Decimal.new("100.00"),
|
|
||||||
interval: :half_yearly,
|
|
||||||
member_count: 3
|
|
||||||
},
|
|
||||||
%{
|
|
||||||
id: "6",
|
|
||||||
name: gettext("Honorary"),
|
|
||||||
description: gettext("No fee for honorary members"),
|
|
||||||
amount: Decimal.new("0.00"),
|
|
||||||
interval: :yearly,
|
|
||||||
member_count: 2
|
|
||||||
}
|
|
||||||
]
|
|
||||||
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
|
|
||||||
|
|
@ -1,300 +0,0 @@
|
||||||
defmodule MvWeb.CustomFieldValueLive.Form do
|
|
||||||
@moduledoc """
|
|
||||||
LiveView form for creating and editing custom field values.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
- Create new custom field values with member and type selection
|
|
||||||
- Edit existing custom field values
|
|
||||||
- Value input adapts to custom field type (string, integer, boolean, date, email)
|
|
||||||
- Real-time validation
|
|
||||||
|
|
||||||
## Form Fields
|
|
||||||
**Required:**
|
|
||||||
- member - Select which member owns this custom field value
|
|
||||||
- custom_field - Select the type (defines value type)
|
|
||||||
- value - The actual value (input type depends on custom field type)
|
|
||||||
|
|
||||||
## Value Types
|
|
||||||
The form dynamically renders appropriate inputs based on custom field type:
|
|
||||||
- String: text input
|
|
||||||
- Integer: number input
|
|
||||||
- Boolean: checkbox
|
|
||||||
- Date: date picker
|
|
||||||
- Email: email input with validation
|
|
||||||
|
|
||||||
## Events
|
|
||||||
- `validate` - Real-time form validation
|
|
||||||
- `save` - Submit form (create or update custom field value)
|
|
||||||
|
|
||||||
## Note
|
|
||||||
Custom field values are typically managed through the member edit form,
|
|
||||||
not through this standalone form.
|
|
||||||
"""
|
|
||||||
use MvWeb, :live_view
|
|
||||||
|
|
||||||
on_mount {MvWeb.LiveHelpers, :ensure_user_role_loaded}
|
|
||||||
import MvWeb.LiveHelpers, only: [current_actor: 1, submit_form: 3]
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def render(assigns) do
|
|
||||||
~H"""
|
|
||||||
<Layouts.app flash={@flash} current_user={@current_user}>
|
|
||||||
<.header>
|
|
||||||
{@page_title}
|
|
||||||
<:subtitle>
|
|
||||||
{gettext("Use this form to manage Custom Field Value records in your database.")}
|
|
||||||
</:subtitle>
|
|
||||||
</.header>
|
|
||||||
|
|
||||||
<.form for={@form} id="custom_field_value-form" phx-change="validate" phx-submit="save">
|
|
||||||
<!-- Custom Field Selection -->
|
|
||||||
<.input
|
|
||||||
field={@form[:custom_field_id]}
|
|
||||||
type="select"
|
|
||||||
label={gettext("Custom field")}
|
|
||||||
options={custom_field_options(@custom_fields)}
|
|
||||||
prompt={gettext("Choose a custom field")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Member Selection -->
|
|
||||||
<.input
|
|
||||||
field={@form[:member_id]}
|
|
||||||
type="select"
|
|
||||||
label={gettext("Member")}
|
|
||||||
options={member_options(@members)}
|
|
||||||
prompt={gettext("Choose a member")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Value Input - handles Union type -->
|
|
||||||
<%= if @selected_custom_field do %>
|
|
||||||
<.union_value_input form={@form} custom_field={@selected_custom_field} />
|
|
||||||
<% else %>
|
|
||||||
<div class="text-sm text-gray-600">
|
|
||||||
{gettext("Please select a custom field first")}
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<.button phx-disable-with={gettext("Saving...")} variant="primary">
|
|
||||||
{gettext("Save Custom Field Value")}
|
|
||||||
</.button>
|
|
||||||
<.button navigate={return_path(@return_to, @custom_field_value)}>{gettext("Cancel")}</.button>
|
|
||||||
</.form>
|
|
||||||
</Layouts.app>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Helper function for Union-Value Input
|
|
||||||
defp union_value_input(assigns) do
|
|
||||||
# Extract the current value from the CustomFieldValue
|
|
||||||
current_value = extract_current_value(assigns.form.data, assigns.custom_field.value_type)
|
|
||||||
assigns = assign(assigns, :current_value, current_value)
|
|
||||||
|
|
||||||
~H"""
|
|
||||||
<div class="space-y-2">
|
|
||||||
<label class="block text-sm font-medium text-gray-700">
|
|
||||||
{gettext("Value")}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<%= case @custom_field.value_type do %>
|
|
||||||
<% :string -> %>
|
|
||||||
<.inputs_for :let={value_form} field={@form[:value]}>
|
|
||||||
<.input field={value_form[:value]} type="text" label="" value={@current_value} />
|
|
||||||
<input type="hidden" name={value_form[:_union_type].name} value="string" />
|
|
||||||
</.inputs_for>
|
|
||||||
<% :integer -> %>
|
|
||||||
<.inputs_for :let={value_form} field={@form[:value]}>
|
|
||||||
<.input field={value_form[:value]} type="number" label="" value={@current_value} />
|
|
||||||
<input type="hidden" name={value_form[:_union_type].name} value="integer" />
|
|
||||||
</.inputs_for>
|
|
||||||
<% :boolean -> %>
|
|
||||||
<.inputs_for :let={value_form} field={@form[:value]}>
|
|
||||||
<.input field={value_form[:value]} type="checkbox" label="" checked={@current_value} />
|
|
||||||
<input type="hidden" name={value_form[:_union_type].name} value="boolean" />
|
|
||||||
</.inputs_for>
|
|
||||||
<% :date -> %>
|
|
||||||
<.inputs_for :let={value_form} field={@form[:value]}>
|
|
||||||
<.input
|
|
||||||
field={value_form[:value]}
|
|
||||||
type="date"
|
|
||||||
label=""
|
|
||||||
value={format_date_value(@current_value)}
|
|
||||||
/>
|
|
||||||
<input type="hidden" name={value_form[:_union_type].name} value="date" />
|
|
||||||
</.inputs_for>
|
|
||||||
<% :email -> %>
|
|
||||||
<.inputs_for :let={value_form} field={@form[:value]}>
|
|
||||||
<.input field={value_form[:value]} type="email" label="" value={@current_value} />
|
|
||||||
<input type="hidden" name={value_form[:_union_type].name} value="email" />
|
|
||||||
</.inputs_for>
|
|
||||||
<% _ -> %>
|
|
||||||
<div class="text-sm text-red-600">
|
|
||||||
{gettext("Unsupported value type: %{type}", type: @custom_field.value_type)}
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Helper function to extract the current value from the CustomFieldValue
|
|
||||||
defp extract_current_value(
|
|
||||||
%Mv.Membership.CustomFieldValue{value: %Ash.Union{value: value}},
|
|
||||||
_value_type
|
|
||||||
) do
|
|
||||||
value
|
|
||||||
end
|
|
||||||
|
|
||||||
defp extract_current_value(_data, _value_type) do
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Helper function to format Date values for HTML input
|
|
||||||
defp format_date_value(%Date{} = date) do
|
|
||||||
Date.to_iso8601(date)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_date_value(nil), do: ""
|
|
||||||
|
|
||||||
defp format_date_value(date) when is_binary(date) do
|
|
||||||
case Date.from_iso8601(date) do
|
|
||||||
{:ok, parsed_date} -> Date.to_iso8601(parsed_date)
|
|
||||||
_ -> ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_date_value(_), do: ""
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def mount(params, _session, socket) do
|
|
||||||
custom_field_value =
|
|
||||||
case params["id"] do
|
|
||||||
nil -> nil
|
|
||||||
id -> Ash.get!(Mv.Membership.CustomFieldValue, id) |> Ash.load!([:custom_field])
|
|
||||||
end
|
|
||||||
|
|
||||||
action = if is_nil(custom_field_value), do: "New", else: "Edit"
|
|
||||||
page_title = action <> " " <> "Custom field value"
|
|
||||||
|
|
||||||
# Load all CustomFields and Members for the selection fields
|
|
||||||
actor = current_actor(socket)
|
|
||||||
custom_fields = Ash.read!(Mv.Membership.CustomField, actor: actor)
|
|
||||||
members = Ash.read!(Mv.Membership.Member, actor: actor)
|
|
||||||
|
|
||||||
{:ok,
|
|
||||||
socket
|
|
||||||
|> assign(:return_to, return_to(params["return_to"]))
|
|
||||||
|> assign(custom_field_value: custom_field_value)
|
|
||||||
|> assign(:page_title, page_title)
|
|
||||||
|> assign(:custom_fields, custom_fields)
|
|
||||||
|> assign(:members, members)
|
|
||||||
|> assign(:selected_custom_field, custom_field_value && custom_field_value.custom_field)
|
|
||||||
|> assign_form()}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp return_to("show"), do: "show"
|
|
||||||
defp return_to(_), do: "index"
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("validate", %{"custom_field_value" => custom_field_value_params}, socket) do
|
|
||||||
# Find the selected CustomField
|
|
||||||
selected_custom_field =
|
|
||||||
case custom_field_value_params["custom_field_id"] do
|
|
||||||
"" -> nil
|
|
||||||
nil -> nil
|
|
||||||
id -> Enum.find(socket.assigns.custom_fields, &(&1.id == id))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set the Union type based on the selected CustomField
|
|
||||||
updated_params =
|
|
||||||
if selected_custom_field do
|
|
||||||
union_type = to_string(selected_custom_field.value_type)
|
|
||||||
put_in(custom_field_value_params, ["value", "_union_type"], union_type)
|
|
||||||
else
|
|
||||||
custom_field_value_params
|
|
||||||
end
|
|
||||||
|
|
||||||
{:noreply,
|
|
||||||
socket
|
|
||||||
|> assign(:selected_custom_field, selected_custom_field)
|
|
||||||
|> assign(form: AshPhoenix.Form.validate(socket.assigns.form, updated_params))}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("save", %{"custom_field_value" => custom_field_value_params}, socket) do
|
|
||||||
# Set the Union type based on the selected CustomField
|
|
||||||
updated_params =
|
|
||||||
if socket.assigns.selected_custom_field do
|
|
||||||
union_type = to_string(socket.assigns.selected_custom_field.value_type)
|
|
||||||
put_in(custom_field_value_params, ["value", "_union_type"], union_type)
|
|
||||||
else
|
|
||||||
custom_field_value_params
|
|
||||||
end
|
|
||||||
|
|
||||||
actor = current_actor(socket)
|
|
||||||
|
|
||||||
case submit_form(socket.assigns.form, updated_params, actor) do
|
|
||||||
{:ok, custom_field_value} ->
|
|
||||||
notify_parent({:saved, custom_field_value})
|
|
||||||
|
|
||||||
action =
|
|
||||||
case socket.assigns.form.source.type do
|
|
||||||
:create -> gettext("create")
|
|
||||||
:update -> gettext("update")
|
|
||||||
other -> to_string(other)
|
|
||||||
end
|
|
||||||
|
|
||||||
socket =
|
|
||||||
socket
|
|
||||||
|> put_flash(
|
|
||||||
:info,
|
|
||||||
gettext("Custom field value %{action} successfully", action: action)
|
|
||||||
)
|
|
||||||
|> push_navigate(to: return_path(socket.assigns.return_to, custom_field_value))
|
|
||||||
|
|
||||||
{:noreply, socket}
|
|
||||||
|
|
||||||
{:error, form} ->
|
|
||||||
{:noreply, assign(socket, form: form)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
|
|
||||||
|
|
||||||
defp assign_form(%{assigns: %{custom_field_value: custom_field_value}} = socket) do
|
|
||||||
form =
|
|
||||||
if custom_field_value do
|
|
||||||
# Determine the Union type based on the custom_field
|
|
||||||
union_type = custom_field_value.custom_field && custom_field_value.custom_field.value_type
|
|
||||||
|
|
||||||
params =
|
|
||||||
if union_type do
|
|
||||||
%{"value" => %{"_union_type" => to_string(union_type)}}
|
|
||||||
else
|
|
||||||
%{}
|
|
||||||
end
|
|
||||||
|
|
||||||
AshPhoenix.Form.for_update(custom_field_value, :update,
|
|
||||||
as: "custom_field_value",
|
|
||||||
params: params
|
|
||||||
)
|
|
||||||
else
|
|
||||||
AshPhoenix.Form.for_create(Mv.Membership.CustomFieldValue, :create,
|
|
||||||
as: "custom_field_value"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
assign(socket, form: to_form(form))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp return_path("index", _custom_field_value), do: ~p"/custom_field_values"
|
|
||||||
|
|
||||||
defp return_path("show", custom_field_value),
|
|
||||||
do: ~p"/custom_field_values/#{custom_field_value.id}"
|
|
||||||
|
|
||||||
# Helper functions for selection options
|
|
||||||
defp custom_field_options(custom_fields) do
|
|
||||||
Enum.map(custom_fields, &{&1.name, &1.id})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp member_options(members) do
|
|
||||||
Enum.map(members, &{MvWeb.Helpers.MemberHelpers.display_name(&1), &1.id})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
defmodule MvWeb.CustomFieldValueLive.Index do
|
|
||||||
@moduledoc """
|
|
||||||
LiveView for displaying and managing custom field values.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
- List all custom field values with their values and types
|
|
||||||
- Show which member each custom field value belongs to
|
|
||||||
- Display custom field information
|
|
||||||
- Navigate to custom field value details and edit forms
|
|
||||||
- Delete custom field values
|
|
||||||
|
|
||||||
## Relationships
|
|
||||||
Each custom field value is linked to:
|
|
||||||
- A member (the custom field value owner)
|
|
||||||
- A custom field (defining value type and behavior)
|
|
||||||
|
|
||||||
## Events
|
|
||||||
- `delete` - Remove a custom field value from the database
|
|
||||||
|
|
||||||
## Note
|
|
||||||
Custom field values are typically managed through the member edit form.
|
|
||||||
This view provides a global overview of all custom field values.
|
|
||||||
"""
|
|
||||||
use MvWeb, :live_view
|
|
||||||
|
|
||||||
on_mount {MvWeb.LiveHelpers, :ensure_user_role_loaded}
|
|
||||||
import MvWeb.LiveHelpers, only: [current_actor: 1]
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def render(assigns) do
|
|
||||||
~H"""
|
|
||||||
<Layouts.app flash={@flash} current_user={@current_user}>
|
|
||||||
<.header>
|
|
||||||
Listing Custom field values
|
|
||||||
<:actions>
|
|
||||||
<.button variant="primary" navigate={~p"/custom_field_values/new"}>
|
|
||||||
<.icon name="hero-plus" /> New Custom field value
|
|
||||||
</.button>
|
|
||||||
</:actions>
|
|
||||||
</.header>
|
|
||||||
|
|
||||||
<.table
|
|
||||||
id="custom_field_values"
|
|
||||||
rows={@streams.custom_field_values}
|
|
||||||
row_click={
|
|
||||||
fn {_id, custom_field_value} ->
|
|
||||||
JS.navigate(~p"/custom_field_values/#{custom_field_value}")
|
|
||||||
end
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<:col :let={{_id, custom_field_value}} label="Id">{custom_field_value.id}</:col>
|
|
||||||
|
|
||||||
<:action :let={{_id, custom_field_value}}>
|
|
||||||
<div class="sr-only">
|
|
||||||
<.link navigate={~p"/custom_field_values/#{custom_field_value}"}>Show</.link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<.link navigate={~p"/custom_field_values/#{custom_field_value}/edit"}>Edit</.link>
|
|
||||||
</:action>
|
|
||||||
|
|
||||||
<:action :let={{id, custom_field_value}}>
|
|
||||||
<.link
|
|
||||||
phx-click={JS.push("delete", value: %{id: custom_field_value.id}) |> hide("##{id}")}
|
|
||||||
data-confirm="Are you sure?"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</.link>
|
|
||||||
</:action>
|
|
||||||
</.table>
|
|
||||||
</Layouts.app>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def mount(_params, _session, socket) do
|
|
||||||
actor = current_actor(socket)
|
|
||||||
|
|
||||||
# Early return if no actor (prevents exceptions in unauthenticated tests)
|
|
||||||
if is_nil(actor) do
|
|
||||||
{:ok,
|
|
||||||
socket
|
|
||||||
|> assign(:page_title, "Listing Custom field values")
|
|
||||||
|> stream(:custom_field_values, [])}
|
|
||||||
else
|
|
||||||
case Ash.read(Mv.Membership.CustomFieldValue, actor: actor) do
|
|
||||||
{:ok, custom_field_values} ->
|
|
||||||
{:ok,
|
|
||||||
socket
|
|
||||||
|> assign(:page_title, "Listing Custom field values")
|
|
||||||
|> stream(:custom_field_values, custom_field_values)}
|
|
||||||
|
|
||||||
{:error, %Ash.Error.Forbidden{}} ->
|
|
||||||
{:ok,
|
|
||||||
socket
|
|
||||||
|> assign(:page_title, "Listing Custom field values")
|
|
||||||
|> stream(:custom_field_values, [])
|
|
||||||
|> put_flash(:error, gettext("You do not have permission to view custom field values"))}
|
|
||||||
|
|
||||||
{:error, error} ->
|
|
||||||
{:ok,
|
|
||||||
socket
|
|
||||||
|> assign(:page_title, "Listing Custom field values")
|
|
||||||
|> stream(:custom_field_values, [])
|
|
||||||
|> put_flash(:error, format_error(error))}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("delete", %{"id" => id}, socket) do
|
|
||||||
actor = MvWeb.LiveHelpers.current_actor(socket)
|
|
||||||
|
|
||||||
case Ash.get(Mv.Membership.CustomFieldValue, id, actor: actor) do
|
|
||||||
{:ok, custom_field_value} ->
|
|
||||||
case Ash.destroy(custom_field_value, actor: actor) do
|
|
||||||
:ok ->
|
|
||||||
{:noreply,
|
|
||||||
socket
|
|
||||||
|> stream_delete(:custom_field_values, custom_field_value)
|
|
||||||
|> put_flash(:info, gettext("Custom field value deleted successfully"))}
|
|
||||||
|
|
||||||
{:error, %Ash.Error.Forbidden{}} ->
|
|
||||||
{:noreply,
|
|
||||||
put_flash(
|
|
||||||
socket,
|
|
||||||
:error,
|
|
||||||
gettext("You do not have permission to delete this custom field value")
|
|
||||||
)}
|
|
||||||
|
|
||||||
{:error, error} ->
|
|
||||||
{:noreply, put_flash(socket, :error, format_error(error))}
|
|
||||||
end
|
|
||||||
|
|
||||||
{:error, %Ash.Error.Query.NotFound{}} ->
|
|
||||||
{:noreply, put_flash(socket, :error, gettext("Custom field value not found"))}
|
|
||||||
|
|
||||||
{:error, %Ash.Error.Forbidden{} = _error} ->
|
|
||||||
{:noreply,
|
|
||||||
put_flash(
|
|
||||||
socket,
|
|
||||||
:error,
|
|
||||||
gettext("You do not have permission to access this custom field value")
|
|
||||||
)}
|
|
||||||
|
|
||||||
{:error, error} ->
|
|
||||||
{:noreply, put_flash(socket, :error, format_error(error))}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_error(%Ash.Error.Invalid{errors: errors}) do
|
|
||||||
Enum.map_join(errors, ", ", fn %{message: message} -> message end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_error(error) do
|
|
||||||
inspect(error)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
defmodule MvWeb.CustomFieldValueLive.Show do
|
|
||||||
@moduledoc """
|
|
||||||
LiveView for displaying a single custom field value's details.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
- Display custom field value and type
|
|
||||||
- Show linked member
|
|
||||||
- Show custom field definition
|
|
||||||
- Navigate to edit form
|
|
||||||
- Return to custom field value list
|
|
||||||
|
|
||||||
## Displayed Information
|
|
||||||
- Custom field value (formatted based on type)
|
|
||||||
- Custom field name and description
|
|
||||||
- Member information (who owns this custom field value)
|
|
||||||
- Custom field value metadata (ID, timestamps if added)
|
|
||||||
|
|
||||||
## Navigation
|
|
||||||
- Back to custom field value list
|
|
||||||
- Edit custom field value
|
|
||||||
"""
|
|
||||||
use MvWeb, :live_view
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def render(assigns) do
|
|
||||||
~H"""
|
|
||||||
<Layouts.app flash={@flash} current_user={@current_user}>
|
|
||||||
<.header>
|
|
||||||
Data field value {@custom_field_value.id}
|
|
||||||
<:subtitle>This is a custom_field_value record from your database.</:subtitle>
|
|
||||||
|
|
||||||
<:actions>
|
|
||||||
<.button navigate={~p"/custom_field_values"}>
|
|
||||||
<.icon name="hero-arrow-left" />
|
|
||||||
</.button>
|
|
||||||
<.button
|
|
||||||
variant="primary"
|
|
||||||
navigate={~p"/custom_field_values/#{@custom_field_value}/edit?return_to=show"}
|
|
||||||
>
|
|
||||||
<.icon name="hero-pencil-square" /> Edit Custom field value
|
|
||||||
</.button>
|
|
||||||
</:actions>
|
|
||||||
</.header>
|
|
||||||
|
|
||||||
<.list>
|
|
||||||
<:item title="Id">{@custom_field_value.id}</:item>
|
|
||||||
</.list>
|
|
||||||
</Layouts.app>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def mount(_params, _session, socket) do
|
|
||||||
{:ok, socket}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_params(%{"id" => id}, _, socket) do
|
|
||||||
{:noreply,
|
|
||||||
socket
|
|
||||||
|> assign(:page_title, page_title(socket.assigns.live_action))
|
|
||||||
|> assign(:custom_field_value, Ash.get!(Mv.Membership.CustomFieldValue, id))}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp page_title(:show), do: "Show data field value"
|
|
||||||
defp page_title(:edit), do: "Edit data field value"
|
|
||||||
end
|
|
||||||
|
|
@ -58,12 +58,6 @@ defmodule MvWeb.Router do
|
||||||
live "/members/:id", MemberLive.Show, :show
|
live "/members/:id", MemberLive.Show, :show
|
||||||
live "/members/:id/show/edit", MemberLive.Show, :edit
|
live "/members/:id/show/edit", MemberLive.Show, :edit
|
||||||
|
|
||||||
live "/custom_field_values", CustomFieldValueLive.Index, :index
|
|
||||||
live "/custom_field_values/new", CustomFieldValueLive.Form, :new
|
|
||||||
live "/custom_field_values/:id/edit", CustomFieldValueLive.Form, :edit
|
|
||||||
live "/custom_field_values/:id", CustomFieldValueLive.Show, :show
|
|
||||||
live "/custom_field_values/:id/show/edit", CustomFieldValueLive.Show, :edit
|
|
||||||
|
|
||||||
live "/users", UserLive.Index, :index
|
live "/users", UserLive.Index, :index
|
||||||
live "/users/new", UserLive.Form, :new
|
live "/users/new", UserLive.Form, :new
|
||||||
live "/users/:id/edit", UserLive.Form, :edit
|
live "/users/:id/edit", UserLive.Form, :edit
|
||||||
|
|
@ -80,10 +74,6 @@ defmodule MvWeb.Router do
|
||||||
live "/membership_fee_types/new", MembershipFeeTypeLive.Form, :new
|
live "/membership_fee_types/new", MembershipFeeTypeLive.Form, :new
|
||||||
live "/membership_fee_types/:id/edit", MembershipFeeTypeLive.Form, :edit
|
live "/membership_fee_types/:id/edit", MembershipFeeTypeLive.Form, :edit
|
||||||
|
|
||||||
# Contribution Management (Mock-ups)
|
|
||||||
live "/contribution_types", ContributionTypeLive.Index, :index
|
|
||||||
live "/contributions/member/:id", ContributionPeriodLive.Show, :show
|
|
||||||
|
|
||||||
# Role Management (Admin only)
|
# Role Management (Admin only)
|
||||||
live "/admin/roles", RoleLive.Index, :index
|
live "/admin/roles", RoleLive.Index, :index
|
||||||
live "/admin/roles/new", RoleLive.Form, :new
|
live "/admin/roles/new", RoleLive.Form, :new
|
||||||
|
|
|
||||||
|
|
@ -146,8 +146,6 @@ defmodule MvWeb.ProfileNavigationTest do
|
||||||
"/",
|
"/",
|
||||||
"/members",
|
"/members",
|
||||||
"/members/new",
|
"/members/new",
|
||||||
"/custom_field_values",
|
|
||||||
"/custom_field_values/new",
|
|
||||||
"/users",
|
"/users",
|
||||||
"/users/new"
|
"/users/new"
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue