Merge pull request 'Small refactoring' (#360) from refactor into main
All checks were successful
continuous-integration/drone/push Build is passing

Reviewed-on: #360
This commit is contained in:
moritz 2026-01-20 17:59:22 +01:00
commit 264323504f
23 changed files with 1230 additions and 2219 deletions

View file

@ -1,116 +0,0 @@
# Documentation Sync - Code Adjustments Todo List
**Created:** 2026-01-13
**Purpose:** List of all code adjustments identified based on documentation synchronization
## Code Adjustments (Priority: Low)
### 1. Domain Public API Documentation Incomplete
**Problem:** The `@moduledoc` in domain modules does not list all public functions.
**Affected Files:**
- `lib/membership/membership.ex` - Missing functions in Public API:
- `list_required_custom_fields/0`
- `update_member_field_visibility/2`
- `update_single_member_field_visibility/3`
- `lib/accounts/accounts.ex` - Very short Public API documentation, could be more detailed
- `lib/membership_fees/membership_fees.ex` - Public API is complete, but could more clearly document that LiveViews use direct Ash calls
**Priority:** Low (Documentation, no functionality affected)
**Recommendation:** Update Public API sections in all domain modules to list all public functions.
### 2. Outdated Comments in MemberLive.Form
**Problem:** `@moduledoc` in `lib/mv_web/live/member_live/form.ex` still mentions "Payment Data: Mockup section (not editable)", but Membership Fees are now fully implemented.
**Affected File:**
- `lib/mv_web/live/member_live/form.ex` (Line 16)
**Priority:** Low (Documentation, no functionality affected)
**Recommendation:** Update `@moduledoc` to reflect the current status.
### 3. Mv.Accounts Domain Public API Missing Completely
**Problem:** The `@moduledoc` in `lib/accounts/accounts.ex` does not mention any Public API functions, although several are defined.
**Affected File:**
- `lib/accounts/accounts.ex` - Missing Public API documentation for:
- `create_user/1`
- `list_users/0`
- `update_user/2`
- `destroy_user/1`
- `create_register_with_rauthy/1`
- `read_sign_in_with_rauthy/1`
**Priority:** Low (Documentation, no functionality affected)
**Recommendation:** Add Public API section to `@moduledoc`, similar to other domain modules.
### 4. Mv.Authorization Domain Public API Missing get_role/1
**Problem:** The `@moduledoc` in `lib/mv/authorization/authorization.ex` does not list `get_role/1` in the Public API, although it is defined.
**Affected File:**
- `lib/mv/authorization/authorization.ex` - Missing function in Public API:
- `get_role/1` (is defined, but not mentioned in Public API)
**Priority:** Low (Documentation, no functionality affected)
**Recommendation:** Add `get_role/1` to the Public API list.
### 5. Remove Deprecated Implementations
**Problem:** `lib/mv_web/live/custom_field_value_live/show.ex` `MvWeb.ContributionTypeLive.Index` and `MvWeb.ContributionPeriodLive.Show` are deprecated, they should be removed.
### 6. Missing Tests for Some LiveViews
**Problem:** Some LiveViews do not have corresponding test files.
**Affected LiveViews:**
- `MvWeb.UserLive.Show` - No test present
- `MvWeb.RoleLive.Show` - No test present
**Priority:** Medium (Test coverage could be improved)
**Recommendation:** Add tests for the three Show LiveViews to ensure complete test coverage.
### 7. Mv.Accounts.Token @moduledoc Too Short
**Problem:** The `@moduledoc` in `lib/accounts/token.ex` is very short and not informative.
**Affected File:**
- `lib/accounts/token.ex` - Currently only: "AshAuthentication specific ressource"
**Priority:** Low (Documentation, no functionality affected)
**Recommendation:** Expand @moduledoc to explain that this is an AshAuthentication Token Resource and is used for session management.
### 8. PageController Missing @moduledoc
**Problem:** The `@moduledoc` in `lib/mv_web/controllers/page_controller.ex` is completely missing.
**Affected File:**
- `lib/mv_web/controllers/page_controller.ex` - No @moduledoc present
**Priority:** Low (Documentation, no functionality affected)
**Recommendation:** Add @moduledoc to explain that this controller renders the homepage.
**Note:** Other controller modules (Router, Endpoint, Telemetry) also do not have @moduledoc, but this is common and acceptable for standard Phoenix modules.
## Analysis Summary
### Found Inconsistencies
**1. Domain Public API Documentation Incomplete** (see Code Adjustments #1)
**2. Outdated Comments in MemberLive.Form** (see Code Adjustments #2)
**3. Mv.Accounts Domain Public API Missing Completely** (see Code Adjustments #3)
**4. Mv.Authorization Domain Public API Missing get_role/1** (see Code Adjustments #4)
**5. CustomFieldValueLive.Show is Deprecated** (see Code Adjustments #5)
**6. Missing Tests for Some LiveViews** (see Code Adjustments #6)
**7. Mv.Accounts.Token @moduledoc Too Short** (see Code Adjustments #7)
**8. PageController Missing @moduledoc** (see Code Adjustments #8)

View file

@ -1,6 +1,15 @@
defmodule Mv.Accounts do
@moduledoc """
AshAuthentication specific domain to handle Authentication for users.
## Resources
- `User` - User accounts with authentication methods (password, OIDC)
- `Token` - Session tokens for authentication
## Public API
The domain exposes these main actions:
- User CRUD: `create_user/1`, `list_users/0`, `update_user/2`, `destroy_user/1`
- Authentication: `create_register_with_rauthy/1`, `read_sign_in_with_rauthy/1`
"""
use Ash.Domain,
extensions: [AshAdmin.Domain, AshPhoenix]

View file

@ -1,6 +1,10 @@
defmodule Mv.Accounts.Token do
@moduledoc """
AshAuthentication specific ressource
AshAuthentication Token Resource for session management.
This resource is used by AshAuthentication to manage authentication tokens
for user sessions. Tokens are automatically created and managed by the
authentication system.
"""
use Ash.Resource,
data_layer: AshPostgres.DataLayer,

View file

@ -12,8 +12,8 @@ defmodule Mv.Membership do
The domain exposes these main actions:
- Member CRUD: `create_member/1`, `list_members/0`, `update_member/2`, `destroy_member/1`
- Custom field value management: `create_custom_field_value/1`, `list_custom_field_values/0`, etc.
- Custom field management: `create_custom_field/1`, `list_custom_fields/0`, etc.
- Settings management: `get_settings/0`, `update_settings/2`
- Custom field management: `create_custom_field/1`, `list_custom_fields/0`, `list_required_custom_fields/0`, etc.
- Settings management: `get_settings/0`, `update_settings/2`, `update_member_field_visibility/2`, `update_single_member_field_visibility/3`
## Admin Interface
The domain is configured with AshAdmin for management UI.

View file

@ -11,6 +11,8 @@ defmodule Mv.MembershipFees do
- MembershipFeeType CRUD: `create_membership_fee_type/1`, `list_membership_fee_types/0`, `update_membership_fee_type/2`, `destroy_membership_fee_type/1`
- MembershipFeeCycle CRUD: `create_membership_fee_cycle/1`, `list_membership_fee_cycles/0`, `update_membership_fee_cycle/2`, `destroy_membership_fee_cycle/1`
Note: LiveViews may use direct Ash calls instead of these domain functions for performance or flexibility.
## Overview
This domain handles the complete membership fee lifecycle including:
- Fee type definitions (monthly, quarterly, half-yearly, yearly)

View file

@ -7,7 +7,7 @@ defmodule Mv.Authorization do
## Public API
The domain exposes these main actions:
- Role CRUD: `create_role/1`, `list_roles/0`, `update_role/2`, `destroy_role/1`
- Role CRUD: `create_role/1`, `list_roles/0`, `get_role/1`, `update_role/2`, `destroy_role/1`
## Admin Interface
The domain is configured with AshAdmin for management UI.

View file

@ -302,30 +302,25 @@ defmodule Mv.Membership.Import.MemberCSV do
max_errors = Keyword.get(opts, :max_errors, 50)
{inserted, failed, errors, _collected_error_count, truncated?} =
Enum.reduce(chunk_rows_with_lines, {0, 0, [], 0, false}, fn {line_number, row_map},
{acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?} ->
current_error_count = existing_error_count + acc_error_count
Enum.reduce(chunk_rows_with_lines, {0, 0, [], 0, false}, fn {line_number, row_map}, acc ->
current_error_count = existing_error_count + elem(acc, 3)
case process_row(row_map, line_number, custom_field_lookup) do
{:ok, _member} ->
{acc_inserted + 1, acc_failed, acc_errors, acc_error_count, acc_truncated?}
update_inserted(acc)
{:error, error} ->
new_acc_failed = acc_failed + 1
# Only collect errors if under limit
{new_acc_errors, new_error_count, new_truncated?} =
if current_error_count < max_errors do
{[error | acc_errors], acc_error_count + 1, acc_truncated?}
else
{acc_errors, acc_error_count, true}
end
{acc_inserted, new_acc_failed, new_acc_errors, new_error_count, new_truncated?}
handle_row_error(acc, error, current_error_count, max_errors)
end
end)
{:ok, %{inserted: inserted, failed: failed, errors: Enum.reverse(errors), errors_truncated?: truncated?}}
{:ok,
%{
inserted: inserted,
failed: failed,
errors: Enum.reverse(errors),
errors_truncated?: truncated?
}}
end
@doc """
@ -389,11 +384,9 @@ defmodule Mv.Membership.Import.MemberCSV do
# Extracts the first error from a changeset and converts it to a MemberCSV.Error struct
defp extract_changeset_error(changeset, csv_line_number) do
case Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end) do
errors = Ecto.Changeset.traverse_errors(changeset, &format_error_message/1)
case errors do
%{email: [message | _]} ->
# Email-specific error
%Error{
@ -422,6 +415,56 @@ defmodule Mv.Membership.Import.MemberCSV do
end
end
# Helper function to update accumulator when row is successfully inserted
defp update_inserted({acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?}) do
{acc_inserted + 1, acc_failed, acc_errors, acc_error_count, acc_truncated?}
end
# Helper function to handle row error with error count limit checking
defp handle_row_error(
{acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?},
error,
current_error_count,
max_errors
) do
new_acc_failed = acc_failed + 1
{new_acc_errors, new_error_count, new_truncated?} =
collect_error_if_under_limit(
error,
acc_errors,
acc_error_count,
acc_truncated?,
current_error_count,
max_errors
)
{acc_inserted, new_acc_failed, new_acc_errors, new_error_count, new_truncated?}
end
# Helper function to collect error only if under limit
defp collect_error_if_under_limit(
error,
acc_errors,
acc_error_count,
acc_truncated?,
current_error_count,
max_errors
) do
if current_error_count < max_errors do
{[error | acc_errors], acc_error_count + 1, acc_truncated?}
else
{acc_errors, acc_error_count, true}
end
end
# Formats error message by replacing placeholders
defp format_error_message({msg, opts}) do
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end
# Maps changeset error messages to appropriate Gettext messages
defp gettext_error_message(message) when is_binary(message) do
cond do

View file

@ -1,4 +1,10 @@
defmodule MvWeb.PageController do
@moduledoc """
Controller for rendering the homepage.
This controller handles the root route and renders the application's
homepage view.
"""
use MvWeb, :controller
def home(conn, _params) do

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -13,7 +13,7 @@ defmodule MvWeb.MemberLive.Form do
## Form Sections
- Personal Data: Name, address, contact information, membership dates, notes
- Custom Fields: Dynamic fields in uniform grid layout (displayed sorted by name)
- Payment Data: Mockup section (not editable)
- Membership Fee: Selection of membership fee type with interval validation
## Events
- `validate` - Real-time form validation
@ -355,55 +355,72 @@ defmodule MvWeb.MemberLive.Form do
# Extracts a user-friendly error message from form errors
defp extract_error_message(form) do
# Try to extract message from source errors first
source_errors = get_source_errors(form)
case source_errors do
[%Ash.Error.Invalid{errors: errors} | _] when is_list(errors) ->
# Extract first error message
case List.first(errors) do
%{message: message} when is_binary(message) ->
gettext("Validation failed: %{message}", message: message)
cond do
has_invalid_error?(source_errors) ->
extract_invalid_error_message(source_errors)
%{field: field, message: message} when is_binary(message) ->
gettext("Validation failed: %{field} %{message}", field: field, message: message)
has_other_error?(source_errors) ->
extract_other_error_message(source_errors)
_ ->
gettext("Validation failed. Please check your input.")
end
has_form_errors?(form) ->
gettext("Please correct the errors in the form and try again.")
[error | _] ->
# Try to extract message from other error types
case error do
%{message: message} when is_binary(message) ->
message
true ->
gettext("Failed to save member. Please try again.")
end
end
error when is_struct(error) ->
# Try to use Ash.ErrorKind protocol if available
try do
Ash.ErrorKind.message(error)
rescue
Protocol.UndefinedError -> gettext("Failed to save member. Please try again.")
end
# Checks if source errors contain an Ash.Error.Invalid
defp has_invalid_error?([%Ash.Error.Invalid{errors: errors} | _]) when is_list(errors), do: true
defp has_invalid_error?(_), do: false
_ ->
gettext("Failed to save member. Please try again.")
end
# Extracts message from Ash.Error.Invalid
defp extract_invalid_error_message([%Ash.Error.Invalid{errors: errors} | _]) do
case List.first(errors) do
%{message: message} when is_binary(message) ->
gettext("Validation failed: %{message}", message: message)
%{field: field, message: message} when is_binary(message) ->
gettext("Validation failed: %{field} %{message}", field: field, message: message)
_ ->
# Check if there are any field errors in the form
if has_form_errors?(form) do
gettext("Please correct the errors in the form and try again.")
else
gettext("Failed to save member. Please try again.")
end
gettext("Validation failed. Please check your input.")
end
end
# Checks if source errors contain other error types
defp has_other_error?([_ | _]), do: true
defp has_other_error?(_), do: false
# Extracts message from other error types
defp extract_other_error_message([error | _]) do
cond do
Map.has_key?(error, :message) and is_binary(error.message) ->
error.message
is_struct(error) ->
extract_struct_error_message(error)
true ->
gettext("Failed to save member. Please try again.")
end
end
# Extracts message from struct error using Ash.ErrorKind protocol
defp extract_struct_error_message(error) do
try do
Ash.ErrorKind.message(error)
rescue
Protocol.UndefinedError -> gettext("Failed to save member. Please try again.")
end
end
# Checks if form has any errors
defp has_form_errors?(form) do
case Map.get(form, :errors) do
errors when is_list(errors) and length(errors) > 0 -> true
errors when is_list(errors) and errors != [] -> true
_ -> false
end
end

View file

@ -58,12 +58,6 @@ defmodule MvWeb.Router do
live "/members/:id", MemberLive.Show, :show
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/new", UserLive.Form, :new
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/: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)
live "/admin/roles", RoleLive.Index, :index
live "/admin/roles/new", RoleLive.Form, :new

View file

@ -11,7 +11,6 @@ msgstr ""
"Language: de\n"
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr "Aktionen"
@ -37,7 +36,6 @@ msgstr "Verbindung wird wiederhergestellt"
msgid "City"
msgstr "Stadt"
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
@ -47,7 +45,6 @@ msgstr "Stadt"
msgid "Delete"
msgstr "Löschen"
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
@ -65,7 +62,6 @@ msgstr "Bearbeiten"
msgid "Edit Member"
msgstr "Mitglied bearbeiten"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show.ex
@ -141,7 +137,6 @@ msgstr "Austrittsdatum"
msgid "House Number"
msgstr "Hausnummer"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/translations/member_fields.ex
@ -150,7 +145,6 @@ msgid "Notes"
msgstr "Notizen"
#: lib/mv_web/live/components/payment_filter_component.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -171,7 +165,6 @@ msgid "Save Member"
msgstr "Mitglied speichern"
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/global_settings_live.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
@ -214,14 +207,12 @@ msgid "Yes"
msgstr "Ja"
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "create"
msgstr "erstellt"
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "update"
@ -264,7 +255,6 @@ msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt"
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
@ -275,11 +265,6 @@ msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt"
msgid "Cancel"
msgstr "Abbrechen"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Choose a member"
msgstr "Mitglied auswählen"
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
@ -313,13 +298,7 @@ msgstr "Abmelden"
msgid "Listing Users"
msgstr "Benutzer*innen auflisten"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Member"
msgstr "Mitglied"
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/index.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -327,7 +306,6 @@ msgstr "Mitglied"
msgid "Members"
msgstr "Mitglieder"
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
@ -351,7 +329,6 @@ msgstr "Neue*r Benutzer*in"
msgid "Not enabled"
msgstr "Nicht aktiviert"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format
msgid "Note"
@ -401,11 +378,6 @@ msgstr "Benutzer*in anzeigen"
msgid "This is a user record from your database."
msgstr "Dies ist ein Benutzer*innen-Datensatz aus Ihrer Datenbank."
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Unsupported value type: %{type}"
msgstr "Nicht unterstützter Wertetyp: %{type}"
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format
msgid "Use this form to manage user records in your database."
@ -417,11 +389,6 @@ msgstr "Verwenden Sie dieses Formular, um Benutzer*innen-Datensätze zu verwalte
msgid "User"
msgstr "Benutzer*in"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Value"
msgstr "Wert"
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format
@ -611,37 +578,12 @@ msgstr "E-Mail kann nicht aktualisiert werden: Diese E-Mail-Adresse ist bereits
msgid "This email is already linked to a different OIDC account. Cannot link multiple OIDC providers to the same account."
msgstr "Diese E-Mail-Adresse ist bereits mit einem anderen OIDC-Konto verknüpft. Es können nicht mehrere OIDC-Provider mit demselben Konto verknüpft werden."
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Choose a custom field"
msgstr "Wähle ein Benutzerdefiniertes Feld"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Custom field"
msgstr "Benutzerdefinierte Felder"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Custom field value %{action} successfully"
msgstr "Benutzerdefinierter Feldwert erfolgreich %{action}"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Please select a custom field first"
msgstr "Bitte wähle zuerst ein Benutzerdefiniertes Feld"
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Custom Fields"
msgstr "Benutzerdefinierte Felder"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Use this form to manage Custom Field Value records in your database."
msgstr "Verwende dieses Formular, um Benutzerdefinierte Feldwerte in deiner Datenbank zu verwalten."
#: lib/mv_web/live/custom_field_live/index_component.ex
#, elixir-autogen, elixir-format
msgid "%{count} member has a value assigned for this custom field."
@ -866,20 +808,6 @@ msgstr "Speichern"
msgid "Create Member"
msgstr "Mitglied erstellen"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "%{count} period selected"
msgid_plural "%{count} periods selected"
msgstr[0] "%{count} Zyklus ausgewählt"
msgstr[1] "%{count} Zyklen ausgewählt"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "About Contribution Types"
msgstr "Über Beitragsarten"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -887,54 +815,16 @@ msgstr "Über Beitragsarten"
msgid "Amount"
msgstr "Betrag"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Back to Settings"
msgstr "Zurück zu den Einstellungen"
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Can be changed at any time. Amount changes affect future periods only."
msgstr "Kann jederzeit geändert werden. Änderungen des Betrags betreffen nur zukünftige Zyklen."
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Cannot delete - members assigned"
msgstr "Löschen nicht möglich es sind Mitglieder zugewiesen"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Change Contribution Type"
msgstr "Beitragsart ändern"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Contribution Start"
msgstr "Beitragsbeginn"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Contribution Types"
msgstr "Beitragsarten"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Contribution type"
msgstr "Beitragsart"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Contributions for %{name}"
msgstr "Beiträge für %{name}"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Current"
msgstr "Aktuell"
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Deletion"
@ -945,12 +835,6 @@ msgstr "Löschen"
msgid "Examples"
msgstr "Beispiele"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Family"
msgstr "Familie"
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Fixed after creation. Members can only switch between types with the same interval."
@ -962,27 +846,12 @@ msgid "Global Settings"
msgstr "Globale Einstellungen"
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "Half-yearly"
msgstr "Halbjährlich"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Half-yearly contribution for supporting members"
msgstr "Halbjährlicher Beitrag für Fördermitglieder"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Honorary"
msgstr "Ehrenamtlich"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -995,36 +864,6 @@ msgstr "Intervall"
msgid "Joining date"
msgstr "Beitrittsdatum"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Joining year - reduced to 0"
msgstr "Beitrittsjahr auf 0 reduziert"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Manage contribution types for membership fees."
msgstr "Beitragsarten für Mitgliedsbeiträge verwalten."
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Paid"
msgstr "Als bezahlt markieren"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Suspended"
msgstr "Als pausiert markieren"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Unpaid"
msgstr "Als unbezahlt markieren"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Member Contributions"
msgstr "Mitgliedsbeiträge"
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Member pays for the year they joined"
@ -1045,131 +884,35 @@ msgstr "Mitglied zahlt ab dem nächsten vollständigen Quartal"
msgid "Member pays from the next full year"
msgstr "Mitglied zahlt ab dem nächsten vollständigen Jahr"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Member since"
msgstr "Mitglied seit"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Members can only switch between contribution types with the same payment interval (e.g., yearly to yearly). This prevents complex period overlaps."
msgstr "Mitglieder können nur zwischen Beitragsarten mit demselben Zahlungszyklus wechseln (z.B. jährlich zu jährlich). Dadurch werden komplexe Überlappungen vermieden."
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Monthly"
msgstr "Monatlich"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Monthly fee for students and trainees"
msgstr "Monatlicher Beitrag für Studierende und Auszubildende"
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Name & Amount"
msgstr "Name & Betrag"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "New Contribution Type"
msgstr "Neue Beitragsart"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "No fee for honorary members"
msgstr "Kein Beitrag für ehrenamtliche Mitglieder"
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Only possible if no members are assigned to this type."
msgstr "Nur möglich, wenn diesem Typ keine Mitglieder zugewiesen sind."
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Open Contributions"
msgstr "Offene Beiträge"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Paid via bank transfer"
msgstr "Bezahlt durch Überweisung"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Preview Mockup"
msgstr "Vorschau"
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "Quarterly"
msgstr "Vierteljährlich"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Quarterly fee for family memberships"
msgstr "Vierteljährlicher Beitrag für Familienmitgliedschaften"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Reduced"
msgstr "Reduziert"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Reduced fee for unemployed, pensioners, or low income"
msgstr "Ermäßigter Beitrag für Arbeitslose, Rentner*innen oder Geringverdienende"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Regular"
msgstr "Regulär"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Reopen"
msgstr "Wieder öffnen"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Standard membership fee for regular members"
msgstr "Regulärer Mitgliedsbeitrag für Vollmitglieder"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Status"
msgstr "Status"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Student"
msgstr "Student"
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Supporting Member"
msgstr "Fördermitglied"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Suspend"
msgstr "Pausieren"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -1177,24 +920,7 @@ msgstr "Pausieren"
msgid "Suspended"
msgstr "Pausiert"
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "This page is not functional and only displays the planned features."
msgstr "Diese Seite ist nicht funktionsfähig und zeigt nur geplante Funktionen."
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Time Period"
msgstr "Zeitraum"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Total Contributions"
msgstr "Gesamtbeiträge"
#: lib/mv_web/live/components/payment_filter_component.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -1202,14 +928,7 @@ msgstr "Gesamtbeiträge"
msgid "Unpaid"
msgstr "Unbezahlt"
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Why are not all contribution types shown?"
msgstr "Warum werden nicht alle Beitragsarten angezeigt?"
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
@ -1697,11 +1416,6 @@ msgstr "Zyklen regenerieren"
msgid "Regenerating..."
msgstr "Regeneriere..."
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field Value"
msgstr "Benutzerdefinierten Feldwert speichern"
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Field"
@ -1799,11 +1513,6 @@ msgstr "Jährliches Intervall Beitrittszeitraum einbezogen"
msgid "You are about to delete all %{count} cycles for this member."
msgstr "Du bist dabei alle %{count} Zyklen für dieses Mitglied zu löschen."
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Contribution types define different membership fee structures. Each type has a fixed cycle (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation."
msgstr "Beitragsarten definieren verschiedene Beitragsmodelle. Jede Art hat einen festen Zyklus (monatlich, vierteljährlich, halbjährlich, jährlich), der nach Erstellung nicht mehr geändert werden kann."
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Delete Membership Fee Type"
@ -2072,16 +1781,6 @@ msgstr "Zyklus löschen"
msgid "The cycle period will be calculated based on this date and the interval."
msgstr "Der Zyklus wird basierend auf diesem Datum und dem Intervall berechnet."
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "Custom field value deleted successfully"
msgstr "Benutzerdefinierter Feldwert erfolgreich gelöscht"
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "Custom field value not found"
msgstr "Benutzerdefinierter Feldwert nicht gefunden"
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Membership fee type not found"
@ -2102,11 +1801,6 @@ msgstr "Benutzer*in erfolgreich gelöscht"
msgid "User not found"
msgstr "Benutzer*in nicht gefunden"
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to access this custom field value"
msgstr "Sie haben keine Berechtigung, auf diesen benutzerdefinierten Feldwert zuzugreifen"
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to access this membership fee type"
@ -2117,11 +1811,6 @@ msgstr "Sie haben keine Berechtigung, auf diese Mitgliedsbeitragsart zuzugreifen
msgid "You do not have permission to access this user"
msgstr "Sie haben keine Berechtigung, auf diese*n Benutzer*in zuzugreifen"
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to delete this custom field value"
msgstr "Sie haben keine Berechtigung, diesen benutzerdefinierten Feldwert zu löschen"
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to delete this membership fee type"
@ -2167,11 +1856,6 @@ msgstr "Sie haben keine Berechtigung, auf dieses Mitglied zuzugreifen"
msgid "You do not have permission to delete this member"
msgstr "Sie haben keine Berechtigung, dieses Mitglied zu löschen"
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to view custom field values"
msgstr "Sie haben keine Berechtigung, benutzerdefinierte Feldwerte anzuzeigen"
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Member created successfully"
@ -2212,7 +1896,319 @@ msgstr "Beitragstypen"
msgid "Administration"
msgstr "Administration"
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Failed to %{action} member."
msgstr "Fehler beim %{action} des Mitglieds."
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Failed to save member. Please try again."
msgstr "Fehler beim Speichern des Mitglieds. Bitte versuchen Sie es erneut."
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Please correct the errors in the form and try again."
msgstr "Bitte korrigieren Sie die Fehler im Formular und versuchen Sie es erneut."
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed. Please check your input."
msgstr "Validierung fehlgeschlagen. Bitte überprüfen Sie Ihre Eingabe."
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed: %{field} %{message}"
msgstr "Validierung fehlgeschlagen: %{field} %{message}"
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed: %{message}"
msgstr "Validierung fehlgeschlagen: %{message}"
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Use this form to manage Custom Field Value records in your database."
#~ msgstr "Verwende dieses Formular, um Benutzerdefinierte Feldwerte in deiner Datenbank zu verwalten."
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Member"
#~ msgstr "Mitglied"
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Choose a custom field"
#~ msgstr "Wähle ein Benutzerdefiniertes Feld"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Joining year - reduced to 0"
#~ msgstr "Beitrittsjahr auf 0 reduziert"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Regular"
#~ msgstr "Regulär"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Current"
#~ msgstr "Aktuell"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Paid via bank transfer"
#~ msgstr "Bezahlt durch Überweisung"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Mark as Unpaid"
#~ msgstr "Als unbezahlt markieren"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Half-yearly contribution for supporting members"
#~ msgstr "Halbjährlicher Beitrag für Fördermitglieder"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Reduced fee for unemployed, pensioners, or low income"
#~ msgstr "Ermäßigter Beitrag für Arbeitslose, Rentner*innen oder Geringverdienende"
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Custom field value not found"
#~ msgstr "Benutzerdefinierter Feldwert nicht gefunden"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Supporting Member"
#~ msgstr "Fördermitglied"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Monthly fee for students and trainees"
#~ msgstr "Monatlicher Beitrag für Studierende und Auszubildende"
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Custom field value %{action} successfully"
#~ msgstr "Benutzerdefinierter Feldwert erfolgreich %{action}"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Total Contributions"
#~ msgstr "Gesamtbeiträge"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Manage contribution types for membership fees."
#~ msgstr "Beitragsarten für Mitgliedsbeiträge verwalten."
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Change Contribution Type"
#~ msgstr "Beitragsart ändern"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "New Contribution Type"
#~ msgstr "Neue Beitragsart"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Time Period"
#~ msgstr "Zeitraum"
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Custom field value deleted successfully"
#~ msgstr "Benutzerdefinierter Feldwert erfolgreich gelöscht"
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "You do not have permission to access this custom field value"
#~ msgstr "Sie haben keine Berechtigung, auf diesen benutzerdefinierten Feldwert zuzugreifen"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Cannot delete - members assigned"
#~ msgstr "Löschen nicht möglich es sind Mitglieder zugewiesen"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Preview Mockup"
#~ msgstr "Vorschau"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Contribution Types"
#~ msgstr "Beitragsarten"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "This page is not functional and only displays the planned features."
#~ msgstr "Diese Seite ist nicht funktionsfähig und zeigt nur geplante Funktionen."
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Member since"
#~ msgstr "Mitglied seit"
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Unsupported value type: %{type}"
#~ msgstr "Nicht unterstützter Wertetyp: %{type}"
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Custom field"
#~ msgstr "Benutzerdefinierte Felder"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Mark as Paid"
#~ msgstr "Als bezahlt markieren"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Contribution type"
#~ msgstr "Beitragsart"
#~ #: lib/mv_web/components/layouts/sidebar.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Contributions"
#~ msgstr "Beiträge"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Reduced"
#~ msgstr "Reduziert"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "No fee for honorary members"
#~ msgstr "Kein Beitrag für ehrenamtliche Mitglieder"
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "You do not have permission to delete this custom field value"
#~ msgstr "Sie haben keine Berechtigung, diesen benutzerdefinierten Feldwert zu löschen"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "%{count} period selected"
#~ msgid_plural "%{count} periods selected"
#~ msgstr[0] "%{count} Zyklus ausgewählt"
#~ msgstr[1] "%{count} Zyklen ausgewählt"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Mark as Suspended"
#~ msgstr "Als pausiert markieren"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Contribution types define different membership fee structures. Each type has a fixed cycle (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation."
#~ msgstr "Beitragsarten definieren verschiedene Beitragsmodelle. Jede Art hat einen festen Zyklus (monatlich, vierteljährlich, halbjährlich, jährlich), der nach Erstellung nicht mehr geändert werden kann."
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Choose a member"
#~ msgstr "Mitglied auswählen"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Suspend"
#~ msgstr "Pausieren"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Reopen"
#~ msgstr "Wieder öffnen"
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Value"
#~ msgstr "Wert"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Why are not all contribution types shown?"
#~ msgstr "Warum werden nicht alle Beitragsarten angezeigt?"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Contribution Start"
#~ msgstr "Beitragsbeginn"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Standard membership fee for regular members"
#~ msgstr "Regulärer Mitgliedsbeitrag für Vollmitglieder"
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Save Custom Field Value"
#~ msgstr "Benutzerdefinierten Feldwert speichern"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Honorary"
#~ msgstr "Ehrenamtlich"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Contributions for %{name}"
#~ msgstr "Beiträge für %{name}"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Family"
#~ msgstr "Familie"
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "You do not have permission to view custom field values"
#~ msgstr "Sie haben keine Berechtigung, benutzerdefinierte Feldwerte anzuzeigen"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Student"
#~ msgstr "Student"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Quarterly fee for family memberships"
#~ msgstr "Vierteljährlicher Beitrag für Familienmitgliedschaften"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Members can only switch between contribution types with the same payment interval (e.g., yearly to yearly). This prevents complex period overlaps."
#~ msgstr "Mitglieder können nur zwischen Beitragsarten mit demselben Zahlungszyklus wechseln (z.B. jährlich zu jährlich). Dadurch werden komplexe Überlappungen vermieden."
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Please select a custom field first"
#~ msgstr "Bitte wähle zuerst ein Benutzerdefiniertes Feld"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Open Contributions"
#~ msgstr "Offene Beiträge"
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Member Contributions"
#~ msgstr "Mitgliedsbeiträge"
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "About Contribution Types"
#~ msgstr "Über Beitragsarten"

View file

@ -12,7 +12,6 @@ msgid ""
msgstr ""
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr ""
@ -38,7 +37,6 @@ msgstr ""
msgid "City"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
@ -48,7 +46,6 @@ msgstr ""
msgid "Delete"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
@ -66,7 +63,6 @@ msgstr ""
msgid "Edit Member"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show.ex
@ -142,7 +138,6 @@ msgstr ""
msgid "House Number"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/translations/member_fields.ex
@ -151,7 +146,6 @@ msgid "Notes"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -172,7 +166,6 @@ msgid "Save Member"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/global_settings_live.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
@ -215,14 +208,12 @@ msgid "Yes"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "create"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "update"
@ -265,7 +256,6 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
@ -276,11 +266,6 @@ msgstr ""
msgid "Cancel"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Choose a member"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
@ -314,13 +299,7 @@ msgstr ""
msgid "Listing Users"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Member"
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/index.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -328,7 +307,6 @@ msgstr ""
msgid "Members"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
@ -352,7 +330,6 @@ msgstr ""
msgid "Not enabled"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format
msgid "Note"
@ -402,11 +379,6 @@ msgstr ""
msgid "This is a user record from your database."
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Unsupported value type: %{type}"
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format
msgid "Use this form to manage user records in your database."
@ -418,11 +390,6 @@ msgstr ""
msgid "User"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Value"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format
@ -612,37 +579,12 @@ msgstr ""
msgid "This email is already linked to a different OIDC account. Cannot link multiple OIDC providers to the same account."
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Choose a custom field"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Custom field"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Custom field value %{action} successfully"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Please select a custom field first"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Custom Fields"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Use this form to manage Custom Field Value records in your database."
msgstr ""
#: lib/mv_web/live/custom_field_live/index_component.ex
#, elixir-autogen, elixir-format
msgid "%{count} member has a value assigned for this custom field."
@ -867,20 +809,6 @@ msgstr ""
msgid "Create Member"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "%{count} period selected"
msgid_plural "%{count} periods selected"
msgstr[0] ""
msgstr[1] ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "About Contribution Types"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -888,54 +816,16 @@ msgstr ""
msgid "Amount"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Back to Settings"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Can be changed at any time. Amount changes affect future periods only."
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Cannot delete - members assigned"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Change Contribution Type"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Contribution Start"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Contribution Types"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Contribution type"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Contributions for %{name}"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Current"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Deletion"
@ -946,12 +836,6 @@ msgstr ""
msgid "Examples"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Family"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Fixed after creation. Members can only switch between types with the same interval."
@ -963,27 +847,12 @@ msgid "Global Settings"
msgstr ""
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "Half-yearly"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Half-yearly contribution for supporting members"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Honorary"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -996,36 +865,6 @@ msgstr ""
msgid "Joining date"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Joining year - reduced to 0"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Manage contribution types for membership fees."
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Paid"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Suspended"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Unpaid"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Member Contributions"
msgstr ""
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Member pays for the year they joined"
@ -1046,131 +885,35 @@ msgstr ""
msgid "Member pays from the next full year"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Member since"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Members can only switch between contribution types with the same payment interval (e.g., yearly to yearly). This prevents complex period overlaps."
msgstr ""
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "Monthly"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Monthly fee for students and trainees"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Name & Amount"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "New Contribution Type"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "No fee for honorary members"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Only possible if no members are assigned to this type."
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Open Contributions"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Paid via bank transfer"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Preview Mockup"
msgstr ""
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "Quarterly"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Quarterly fee for family memberships"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Reduced"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Reduced fee for unemployed, pensioners, or low income"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Regular"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Reopen"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Standard membership fee for regular members"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Status"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Student"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Supporting Member"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Suspend"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -1178,24 +921,7 @@ msgstr ""
msgid "Suspended"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "This page is not functional and only displays the planned features."
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Time Period"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Total Contributions"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -1203,14 +929,7 @@ msgstr ""
msgid "Unpaid"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Why are not all contribution types shown?"
msgstr ""
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
@ -1698,11 +1417,6 @@ msgstr ""
msgid "Regenerating..."
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Save Custom Field Value"
msgstr ""
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Save Field"
@ -1800,11 +1514,6 @@ msgstr ""
msgid "You are about to delete all %{count} cycles for this member."
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Contribution types define different membership fee structures. Each type has a fixed cycle (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation."
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Delete Membership Fee Type"
@ -2073,16 +1782,6 @@ msgstr ""
msgid "The cycle period will be calculated based on this date and the interval."
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "Custom field value deleted successfully"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "Custom field value not found"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Membership fee type not found"
@ -2103,11 +1802,6 @@ msgstr ""
msgid "User not found"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to access this custom field value"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to access this membership fee type"
@ -2118,11 +1812,6 @@ msgstr ""
msgid "You do not have permission to access this user"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to delete this custom field value"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to delete this membership fee type"
@ -2168,11 +1857,6 @@ msgstr ""
msgid "You do not have permission to delete this member"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format
msgid "You do not have permission to view custom field values"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Member created successfully"
@ -2191,6 +1875,7 @@ msgstr ""
#: lib/mv/membership/import/member_csv.ex
#, elixir-autogen, elixir-format
msgid "Email is required."
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format
@ -2211,3 +1896,33 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Administration"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Failed to %{action} member."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Failed to save member. Please try again."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Please correct the errors in the form and try again."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed. Please check your input."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed: %{field} %{message}"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed: %{message}"
msgstr ""

View file

@ -12,7 +12,6 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr ""
@ -38,7 +37,6 @@ msgstr ""
msgid "City"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
@ -48,7 +46,6 @@ msgstr ""
msgid "Delete"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
@ -66,7 +63,6 @@ msgstr ""
msgid "Edit Member"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show.ex
@ -142,7 +138,6 @@ msgstr ""
msgid "House Number"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/translations/member_fields.ex
@ -151,7 +146,6 @@ msgid "Notes"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -172,7 +166,6 @@ msgid "Save Member"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/global_settings_live.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
@ -215,14 +208,12 @@ msgid "Yes"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "create"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "update"
@ -265,7 +256,6 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/custom_field_value_live/form.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
@ -276,11 +266,6 @@ msgstr ""
msgid "Cancel"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Choose a member"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
@ -314,13 +299,7 @@ msgstr ""
msgid "Listing Users"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Member"
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/index.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -328,7 +307,6 @@ msgstr ""
msgid "Members"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
@ -352,7 +330,6 @@ msgstr ""
msgid "Not enabled"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Note"
@ -402,11 +379,6 @@ msgstr ""
msgid "This is a user record from your database."
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Unsupported value type: %{type}"
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Use this form to manage user records in your database."
@ -418,11 +390,6 @@ msgstr ""
msgid "User"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Value"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format
@ -612,37 +579,12 @@ msgstr ""
msgid "This email is already linked to a different OIDC account. Cannot link multiple OIDC providers to the same account."
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Choose a custom field"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Custom field"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Custom field value %{action} successfully"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Please select a custom field first"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Custom Fields"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Use this form to manage Custom Field Value records in your database."
msgstr ""
#: lib/mv_web/live/custom_field_live/index_component.ex
#, elixir-autogen, elixir-format
msgid "%{count} member has a value assigned for this custom field."
@ -867,20 +809,6 @@ msgstr ""
msgid "Create Member"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "%{count} period selected"
msgid_plural "%{count} periods selected"
msgstr[0] ""
msgstr[1] ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "About Contribution Types"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -888,54 +816,16 @@ msgstr ""
msgid "Amount"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Back to Settings"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Can be changed at any time. Amount changes affect future periods only."
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Cannot delete - members assigned"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Change Contribution Type"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Contribution Start"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Contribution Types"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Contribution type"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Contributions for %{name}"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Current"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Deletion"
@ -946,12 +836,6 @@ msgstr ""
msgid "Examples"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Family"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Fixed after creation. Members can only switch between types with the same interval."
@ -963,27 +847,12 @@ msgid "Global Settings"
msgstr ""
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "Half-yearly"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Half-yearly contribution for supporting members"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Honorary"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
@ -996,36 +865,6 @@ msgstr ""
msgid "Joining date"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Joining year - reduced to 0"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Manage contribution types for membership fees."
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Paid"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Suspended"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Mark as Unpaid"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Member Contributions"
msgstr ""
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Member pays for the year they joined"
@ -1046,131 +885,35 @@ msgstr ""
msgid "Member pays from the next full year"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Member since"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Members can only switch between contribution types with the same payment interval (e.g., yearly to yearly). This prevents complex period overlaps."
msgstr ""
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "Monthly"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Monthly fee for students and trainees"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Name & Amount"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "New Contribution Type"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "No fee for honorary members"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Only possible if no members are assigned to this type."
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Open Contributions"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Paid via bank transfer"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Preview Mockup"
msgstr ""
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "Quarterly"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Quarterly fee for family memberships"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Reduced"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Reduced fee for unemployed, pensioners, or low income"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Regular"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Reopen"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Standard membership fee for regular members"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Status"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Student"
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Supporting Member"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Suspend"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -1178,24 +921,7 @@ msgstr ""
msgid "Suspended"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "This page is not functional and only displays the planned features."
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Time Period"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Total Contributions"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/member_live/index/membership_fee_status.ex
@ -1203,14 +929,7 @@ msgstr ""
msgid "Unpaid"
msgstr ""
#: lib/mv_web/live/contribution_period_live/show.ex
#, elixir-autogen, elixir-format
msgid "Why are not all contribution types shown?"
msgstr ""
#: lib/mv_web/helpers/membership_fee_helpers.ex
#: lib/mv_web/live/contribution_period_live/show.ex
#: lib/mv_web/live/contribution_type_live/index.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
@ -1698,11 +1417,6 @@ msgstr ""
msgid "Regenerating..."
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field Value"
msgstr ""
#: lib/mv_web/live/member_field_live/form_component.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Field"
@ -1800,11 +1514,6 @@ msgstr ""
msgid "You are about to delete all %{count} cycles for this member."
msgstr ""
#: lib/mv_web/live/contribution_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Contribution types define different membership fee structures. Each type has a fixed cycle (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation."
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Delete Membership Fee Type"
@ -2073,16 +1782,6 @@ msgstr ""
msgid "The cycle period will be calculated based on this date and the interval."
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Custom field value deleted successfully"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Custom field value not found"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Membership fee type not found"
@ -2103,11 +1802,6 @@ msgstr ""
msgid "User not found"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "You do not have permission to access this custom field value"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "You do not have permission to access this membership fee type"
@ -2118,11 +1812,6 @@ msgstr ""
msgid "You do not have permission to access this user"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "You do not have permission to delete this custom field value"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "You do not have permission to delete this membership fee type"
@ -2168,11 +1857,6 @@ msgstr ""
msgid "You do not have permission to delete this member"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "You do not have permission to view custom field values"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Member created successfully"
@ -2213,12 +1897,324 @@ msgstr ""
msgid "Administration"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Failed to %{action} member."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Failed to save member. Please try again."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Please correct the errors in the form and try again."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed. Please check your input."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed: %{field} %{message}"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Validation failed: %{message}"
msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Use this form to manage Custom Field Value records in your database."
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Member"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Choose a custom field"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Joining year - reduced to 0"
#~ msgstr ""
#~ #: lib/mv_web/components/layouts/sidebar.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Admin"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Regular"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Current"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Paid via bank transfer"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Mark as Unpaid"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Half-yearly contribution for supporting members"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Reduced fee for unemployed, pensioners, or low income"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Custom field value not found"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Supporting Member"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Monthly fee for students and trainees"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Custom field value %{action} successfully"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Total Contributions"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Manage contribution types for membership fees."
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Change Contribution Type"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "New Contribution Type"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Time Period"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Custom field value deleted successfully"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "You do not have permission to access this custom field value"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Cannot delete - members assigned"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Preview Mockup"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Contribution Types"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "This page is not functional and only displays the planned features."
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Member since"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Unsupported value type: %{type}"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Custom field"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Mark as Paid"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Contribution type"
#~ msgstr ""
#~ #: lib/mv_web/components/layouts/sidebar.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Contributions"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Reduced"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "No fee for honorary members"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "You do not have permission to delete this custom field value"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "%{count} period selected"
#~ msgid_plural "%{count} periods selected"
#~ msgstr[0] ""
#~ msgstr[1] ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Mark as Suspended"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Contribution types define different membership fee structures. Each type has a fixed cycle (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation."
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Choose a member"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Suspend"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Reopen"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Value"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Why are not all contribution types shown?"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Contribution Start"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Standard membership fee for regular members"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Save Custom Field Value"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Honorary"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Contributions for %{name}"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Family"
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/index.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "You do not have permission to view custom field values"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Student"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Quarterly fee for family memberships"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Members can only switch between contribution types with the same payment interval (e.g., yearly to yearly). This prevents complex period overlaps."
#~ msgstr ""
#~ #: lib/mv_web/live/custom_field_value_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Please select a custom field first"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Open Contributions"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_period_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Member Contributions"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_type_live/index.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "About Contribution Types"
#~ msgstr ""

View file

@ -158,10 +158,8 @@ defmodule Mv.MembershipFees.MembershipFeeTypeIntegrationTest do
|> Ash.update!()
# Create a member without explicitly setting membership_fee_type_id
# Note: This test assumes that the Member resource automatically assigns
# the default_membership_fee_type_id during creation. If this is not yet
# implemented, this test will fail initially (which is expected in TDD).
# For now, we skip this test as the auto-assignment feature is not yet implemented.
# The Member resource automatically assigns the default_membership_fee_type_id
# during creation via SetDefaultMembershipFeeType change.
{:ok, member} =
Ash.create(Member, %{
first_name: "Test",
@ -169,10 +167,8 @@ defmodule Mv.MembershipFees.MembershipFeeTypeIntegrationTest do
email: "test.member.#{System.unique_integer([:positive])}@example.com"
})
# TODO: When auto-assignment is implemented, uncomment this assertion
# assert member.membership_fee_type_id == fee_type.id
# For now, we just verify the member was created successfully
assert %Member{} = member
# Verify that the default membership fee type was automatically assigned
assert member.membership_fee_type_id == fee_type.id
end
test "include_joining_cycle is used during cycle generation" do

View file

@ -404,7 +404,7 @@ defmodule Mv.Membership.Import.MemberCSVTest do
assert chunk_result.inserted == 0
assert chunk_result.failed == 10
assert length(chunk_result.errors) == 0
assert chunk_result.errors == []
end
test "error capping with mixed success and failure" do

View file

@ -146,8 +146,6 @@ defmodule MvWeb.ProfileNavigationTest do
"/",
"/members",
"/members/new",
"/custom_field_values",
"/custom_field_values/new",
"/users",
"/users/new"
]

View file

@ -0,0 +1,274 @@
defmodule MvWeb.RoleLive.ShowTest do
@moduledoc """
Tests for the role show page.
Tests cover:
- Displaying role information
- System role badge display
- User count display
- Navigation
- Error handling
- Delete functionality
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
require Ash.Query
use Gettext, backend: MvWeb.Gettext
alias Mv.Authorization
alias Mv.Authorization.Role
# Helper to create a role
defp create_role(attrs \\ %{}) do
default_attrs = %{
name: "Test Role #{System.unique_integer([:positive])}",
description: "Test description",
permission_set_name: "read_only"
}
attrs = Map.merge(default_attrs, attrs)
case Authorization.create_role(attrs) do
{:ok, role} -> role
{:error, error} -> raise "Failed to create role: #{inspect(error)}"
end
end
# Helper to create admin user with admin role
defp create_admin_user(conn) do
# Create admin role
admin_role =
case Authorization.list_roles() do
{:ok, roles} ->
case Enum.find(roles, &(&1.name == "Admin")) do
nil ->
# Create admin role if it doesn't exist
create_role(%{
name: "Admin",
description: "Administrator with full access",
permission_set_name: "admin"
})
role ->
role
end
_ ->
# Create admin role if list_roles fails
create_role(%{
name: "Admin",
description: "Administrator with full access",
permission_set_name: "admin"
})
end
# Create user
{:ok, user} =
Mv.Accounts.User
|> Ash.Changeset.for_create(:register_with_password, %{
email: "admin#{System.unique_integer([:positive])}@mv.local",
password: "testpassword123"
})
|> Ash.create()
# Assign admin role using manage_relationship
{:ok, user} =
user
|> Ash.Changeset.for_update(:update, %{})
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|> Ash.update()
# Load role for authorization checks (must be loaded for can?/3 to work)
user_with_role = Ash.load!(user, :role, domain: Mv.Accounts)
# Store user with role in session for LiveView
conn = conn_with_password_user(conn, user_with_role)
{conn, user_with_role, admin_role}
end
describe "mount and display" do
setup %{conn: conn} do
{conn, _user, _admin_role} = create_admin_user(conn)
%{conn: conn}
end
test "mounts successfully with valid role ID", %{conn: conn} do
role = create_role()
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
assert html =~ role.name
end
test "displays role name", %{conn: conn} do
role = create_role(%{name: "Test Role Name"})
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
assert html =~ "Test Role Name"
assert html =~ gettext("Name")
end
test "displays role description when present", %{conn: conn} do
role = create_role(%{description: "This is a test description"})
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
assert html =~ "This is a test description"
assert html =~ gettext("Description")
end
test "displays 'No description' when description is missing", %{conn: conn} do
role = create_role(%{description: nil})
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
assert html =~ gettext("No description")
end
test "displays permission set name", %{conn: conn} do
role = create_role(%{permission_set_name: "read_only"})
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
assert html =~ "read_only"
assert html =~ gettext("Permission Set")
end
test "displays system role badge when is_system_role is true", %{conn: conn} do
system_role =
Role
|> Ash.Changeset.for_create(:create_role, %{
name: "System Role",
permission_set_name: "own_data"
})
|> Ash.Changeset.force_change_attribute(:is_system_role, true)
|> Ash.create!()
{:ok, _view, html} = live(conn, "/admin/roles/#{system_role.id}")
assert html =~ gettext("System Role")
assert html =~ gettext("Yes")
end
test "displays non-system role badge when is_system_role is false", %{conn: conn} do
role = create_role()
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
assert html =~ gettext("System Role")
assert html =~ gettext("No")
end
test "displays user count", %{conn: conn} do
role = create_role()
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
# User count should be displayed (might be 0 or more)
assert html =~ gettext("User") || html =~ "0" || html =~ "users"
end
end
describe "navigation" do
setup %{conn: conn} do
{conn, _user, _admin_role} = create_admin_user(conn)
%{conn: conn}
end
test "back button navigates to role list", %{conn: conn} do
role = create_role()
{:ok, view, _html} = live(conn, "/admin/roles/#{role.id}")
assert {:error, {:live_redirect, %{to: to}}} =
view
|> element(
"a[aria-label='#{gettext("Back to roles list")}'], button[aria-label='#{gettext("Back to roles list")}']"
)
|> render_click()
assert to == "/admin/roles"
end
test "edit button navigates to edit form", %{conn: conn} do
role = create_role()
{:ok, view, _html} = live(conn, "/admin/roles/#{role.id}")
assert {:error, {:live_redirect, %{to: to}}} =
view
|> element(
"a[href='/admin/roles/#{role.id}/edit'], button[href='/admin/roles/#{role.id}/edit']"
)
|> render_click()
assert to == "/admin/roles/#{role.id}/edit"
end
end
describe "error handling" do
setup %{conn: conn} do
{conn, _user, _admin_role} = create_admin_user(conn)
%{conn: conn}
end
test "redirects to role list with error for invalid role ID", %{conn: conn} do
invalid_id = Ecto.UUID.generate()
# Should redirect to index with error message
result = live(conn, "/admin/roles/#{invalid_id}")
assert match?({:error, {:redirect, %{to: "/admin/roles"}}}, result) or
match?({:error, {:live_redirect, %{to: "/admin/roles"}}}, result)
end
end
describe "delete functionality" do
setup %{conn: conn} do
{conn, _user, _admin_role} = create_admin_user(conn)
%{conn: conn}
end
test "delete button is not shown for system roles", %{conn: conn} do
system_role =
Role
|> Ash.Changeset.for_create(:create_role, %{
name: "System Role",
permission_set_name: "own_data"
})
|> Ash.Changeset.force_change_attribute(:is_system_role, true)
|> Ash.create!()
{:ok, _view, html} = live(conn, "/admin/roles/#{system_role.id}")
# Delete button should not be visible for system roles
refute html =~ ~r/Delete.*Role.*#{system_role.id}/i
end
test "delete button is shown for non-system roles", %{conn: conn} do
role = create_role()
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
# Delete button should be visible for non-system roles
assert html =~ gettext("Delete Role") || html =~ "delete"
end
end
describe "page title" do
setup %{conn: conn} do
{conn, _user, _admin_role} = create_admin_user(conn)
%{conn: conn}
end
test "sets correct page title", %{conn: conn} do
role = create_role()
{:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")
# Check that page title is set (might be in title tag or header)
assert html =~ gettext("Show Role") || html =~ role.name
end
end
end

View file

@ -0,0 +1,155 @@
defmodule MvWeb.UserLive.ShowTest do
@moduledoc """
Tests for the user show page.
Tests cover:
- Displaying user information
- Authentication status display
- Linked member display
- Navigation
- Error handling
"""
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
use Gettext, backend: MvWeb.Gettext
alias Mv.Membership.Member
setup do
# Create test user
user = create_test_user(%{email: "test@example.com", oidc_id: "test123"})
%{user: user}
end
describe "mount and display" do
test "mounts successfully with valid user ID", %{conn: conn, user: user} do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/users/#{user.id}")
assert html =~ to_string(user.email)
end
test "displays user email", %{conn: conn, user: user} do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/users/#{user.id}")
assert html =~ to_string(user.email)
assert html =~ gettext("Email")
end
test "displays password authentication status when enabled", %{conn: conn} do
user = create_test_user(%{email: "password-user@example.com", password: "test123"})
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/users/#{user.id}")
assert html =~ gettext("Password Authentication")
assert html =~ gettext("Enabled")
end
test "displays password authentication status when not enabled", %{conn: conn} do
# User without password (only OIDC) - create user with OIDC only
user =
create_test_user(%{
email: "oidc-only#{System.unique_integer([:positive])}@example.com",
oidc_id: "oidc#{System.unique_integer([:positive])}",
hashed_password: nil
})
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/users/#{user.id}")
assert html =~ gettext("Password Authentication")
assert html =~ gettext("Not enabled")
end
test "displays linked member when present", %{conn: conn} do
# Create member
{:ok, member} =
Member
|> Ash.Changeset.for_create(:create_member, %{
first_name: "Alice",
last_name: "Smith",
email: "alice@example.com"
})
|> Ash.create()
# Create user and link to member
user = create_test_user(%{email: "user@example.com"})
{:ok, _updated_user} =
user
|> Ash.Changeset.for_update(:update, %{})
|> Ash.Changeset.manage_relationship(:member, member, type: :append_and_remove)
|> Ash.update()
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/users/#{user.id}")
assert html =~ gettext("Linked Member")
assert html =~ "Alice Smith"
assert html =~ ~r/href="[^"]*\/members\/#{member.id}"/
end
test "displays 'No member linked' when no member is linked", %{conn: conn, user: user} do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/users/#{user.id}")
assert html =~ gettext("Linked Member")
assert html =~ gettext("No member linked")
end
end
describe "navigation" do
test "back button navigates to user list", %{conn: conn, user: user} do
conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, ~p"/users/#{user.id}")
assert {:error, {:live_redirect, %{to: to}}} =
view
|> element(
"a[aria-label='#{gettext("Back to users list")}'], button[aria-label='#{gettext("Back to users list")}']"
)
|> render_click()
assert to == "/users"
end
test "edit button navigates to edit form", %{conn: conn, user: user} do
conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, ~p"/users/#{user.id}")
assert {:error, {:live_redirect, %{to: to}}} =
view
|> element(
"a[href='/users/#{user.id}/edit?return_to=show'], button[href='/users/#{user.id}/edit?return_to=show']"
)
|> render_click()
assert to == "/users/#{user.id}/edit?return_to=show"
end
end
describe "error handling" do
test "raises exception for invalid user ID", %{conn: conn} do
invalid_id = Ecto.UUID.generate()
conn = conn_with_oidc_user(conn)
# The mount function uses Ash.get! which will raise an exception
# This is expected behavior - the LiveView doesn't handle this case
assert_raise Ash.Error.Invalid, fn ->
live(conn, ~p"/users/#{invalid_id}")
end
end
end
describe "page title" do
test "sets correct page title", %{conn: conn, user: user} do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/users/#{user.id}")
# Check that page title is set (might be in title tag or header)
assert html =~ gettext("Show User") || html =~ to_string(user.email)
end
end
end