diff --git a/lib/membership/member.ex b/lib/membership/member.ex index 6ae9307..d2ea07d 100644 --- a/lib/membership/member.ex +++ b/lib/membership/member.ex @@ -40,6 +40,8 @@ defmodule Mv.Membership.Member do import Ash.Expr require Logger + alias Mv.Membership.Helpers.VisibilityConfig + # Module constants @member_search_limit 10 @@ -600,18 +602,21 @@ defmodule Mv.Membership.Member do """ @spec show_in_overview?(atom()) :: boolean() def show_in_overview?(field) when is_atom(field) do + # exit_date defaults to false (hidden) instead of true + default_visibility = if field == :exit_date, do: false, else: true + case Mv.Membership.get_settings() do {:ok, settings} -> visibility_config = settings.member_field_visibility || %{} # Normalize map keys to atoms (JSONB may return string keys) - normalized_config = normalize_visibility_config(visibility_config) + normalized_config = VisibilityConfig.normalize(visibility_config) - # Get value from normalized config, default to true - Map.get(normalized_config, field, true) + # Get value from normalized config, use field-specific default + Map.get(normalized_config, field, default_visibility) {:error, _} -> - # If settings can't be loaded, default to visible - true + # If settings can't be loaded, use field-specific default + default_visibility end end @@ -956,29 +961,6 @@ defmodule Mv.Membership.Member do defp error_type(error) when is_atom(error), do: error defp error_type(_), do: :unknown - # Normalizes visibility config map keys from strings to atoms. - # JSONB in PostgreSQL converts atom keys to string keys when storing. - defp normalize_visibility_config(config) when is_map(config) do - Enum.reduce(config, %{}, fn - {key, value}, acc when is_atom(key) -> - Map.put(acc, key, value) - - {key, value}, acc when is_binary(key) -> - try do - atom_key = String.to_existing_atom(key) - Map.put(acc, atom_key, value) - rescue - ArgumentError -> - acc - end - - _, acc -> - acc - end) - end - - defp normalize_visibility_config(_), do: %{} - @doc """ Performs fuzzy search on members using PostgreSQL trigram similarity. diff --git a/lib/membership/membership.ex b/lib/membership/membership.ex index 4917c7c..982b837 100644 --- a/lib/membership/membership.ex +++ b/lib/membership/membership.ex @@ -57,6 +57,9 @@ defmodule Mv.Membership do # Settings should be created via seed script define :update_settings, action: :update define :update_member_field_visibility, action: :update_member_field_visibility + + define :update_single_member_field_visibility, + action: :update_single_member_field_visibility end end @@ -89,7 +92,10 @@ defmodule Mv.Membership do default_club_name = System.get_env("ASSOCIATION_NAME") || "Club Name" Mv.Membership.Setting - |> Ash.Changeset.for_create(:create, %{club_name: default_club_name}) + |> Ash.Changeset.for_create(:create, %{ + club_name: default_club_name, + member_field_visibility: %{"exit_date" => false} + }) |> Ash.create!(domain: __MODULE__) |> then(fn settings -> {:ok, settings} end) @@ -183,4 +189,42 @@ defmodule Mv.Membership do }) |> Ash.update(domain: __MODULE__) end + + @doc """ + Atomically updates a single field in the member field visibility configuration. + + This action uses PostgreSQL's jsonb_set function to atomically update a single key + in the JSONB map, preventing lost updates in concurrent scenarios. This is the + preferred method for updating individual field visibility settings. + + ## Parameters + + - `settings` - The settings record to update + - `field` - The member field name as a string (e.g., "street", "house_number") + - `show_in_overview` - Boolean value indicating visibility + + ## Returns + + - `{:ok, updated_settings}` - Successfully updated settings + - `{:error, error}` - Validation or update error + + ## Examples + + iex> {:ok, settings} = Mv.Membership.get_settings() + iex> {:ok, updated} = Mv.Membership.update_single_member_field_visibility(settings, field: "street", show_in_overview: false) + iex> updated.member_field_visibility["street"] + false + + """ + def update_single_member_field_visibility(settings, + field: field, + show_in_overview: show_in_overview + ) do + settings + |> Ash.Changeset.new() + |> Ash.Changeset.set_argument(:field, field) + |> Ash.Changeset.set_argument(:show_in_overview, show_in_overview) + |> Ash.Changeset.for_update(:update_single_member_field_visibility, %{}) + |> Ash.update(domain: __MODULE__) + end end diff --git a/lib/membership/setting.ex b/lib/membership/setting.ex index eedc47c..4ba0794 100644 --- a/lib/membership/setting.ex +++ b/lib/membership/setting.ex @@ -91,6 +91,16 @@ defmodule Mv.Membership.Setting do accept [:member_field_visibility] end + update :update_single_member_field_visibility do + description "Atomically updates a single field in the member_field_visibility JSONB map" + require_atomic? false + + argument :field, :string, allow_nil?: false + argument :show_in_overview, :boolean, allow_nil?: false + + change Mv.Membership.Setting.Changes.UpdateSingleMemberFieldVisibility + end + update :update_membership_fee_settings do description "Updates the membership fee configuration" require_atomic? false diff --git a/lib/membership/setting/changes/update_single_member_field_visibility.ex b/lib/membership/setting/changes/update_single_member_field_visibility.ex new file mode 100644 index 0000000..e047cdf --- /dev/null +++ b/lib/membership/setting/changes/update_single_member_field_visibility.ex @@ -0,0 +1,164 @@ +defmodule Mv.Membership.Setting.Changes.UpdateSingleMemberFieldVisibility do + @moduledoc """ + Ash change that atomically updates a single field in the member_field_visibility JSONB map. + + This change uses PostgreSQL's jsonb_set function to atomically update a single key + in the JSONB map, preventing lost updates in concurrent scenarios. + + ## Arguments + - `field` - The member field name as a string (e.g., "street", "house_number") + - `show_in_overview` - Boolean value indicating visibility + + ## Example + settings + |> Ash.Changeset.for_update(:update_single_member_field_visibility, + %{}, + arguments: %{field: "street", show_in_overview: false} + ) + |> Ash.update(domain: Mv.Membership) + """ + use Ash.Resource.Change + + alias Ash.Error.Invalid + alias Ecto.Adapters.SQL + require Logger + + def change(changeset, _opts, _context) do + with {:ok, field} <- get_and_validate_field(changeset), + {:ok, show_in_overview} <- get_and_validate_boolean(changeset, :show_in_overview) do + add_after_action(changeset, field, show_in_overview) + else + {:error, updated_changeset} -> updated_changeset + end + end + + defp get_and_validate_field(changeset) do + case Ash.Changeset.get_argument(changeset, :field) do + nil -> + {:error, + add_error(changeset, + field: :member_field_visibility, + message: "field argument is required" + )} + + field -> + valid_fields = Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1) + + if field in valid_fields do + {:ok, field} + else + {:error, + add_error( + changeset, + field: :member_field_visibility, + message: "Invalid member field: #{field}" + )} + end + end + end + + defp get_and_validate_boolean(changeset, arg_name) do + case Ash.Changeset.get_argument(changeset, arg_name) do + nil -> + {:error, + add_error( + changeset, + field: :member_field_visibility, + message: "#{arg_name} argument is required" + )} + + value when is_boolean(value) -> + {:ok, value} + + _ -> + {:error, + add_error( + changeset, + field: :member_field_visibility, + message: "#{arg_name} must be a boolean" + )} + end + end + + defp add_error(changeset, opts) do + Ash.Changeset.add_error(changeset, opts) + end + + defp add_after_action(changeset, field, show_in_overview) do + # Use after_action to execute atomic SQL update + Ash.Changeset.after_action(changeset, fn _changeset, settings -> + # Use PostgreSQL jsonb_set for atomic update + # jsonb_set(target, path, new_value, create_missing?) + # path is an array: ['field_name'] + # new_value must be JSON: to_jsonb(boolean) + sql = """ + UPDATE settings + SET member_field_visibility = jsonb_set( + COALESCE(member_field_visibility, '{}'::jsonb), + ARRAY[$1::text], + to_jsonb($2::boolean), + true + ) + WHERE id = $3 + RETURNING member_field_visibility + """ + + # Convert UUID string to binary for PostgreSQL + uuid_binary = Ecto.UUID.dump!(settings.id) + + case SQL.query(Mv.Repo, sql, [field, show_in_overview, uuid_binary]) do + {:ok, %{rows: [[updated_jsonb] | _]}} -> + updated_visibility = normalize_jsonb_result(updated_jsonb) + + # Update the settings struct with the new visibility + updated_settings = %{settings | member_field_visibility: updated_visibility} + {:ok, updated_settings} + + {:ok, %{rows: []}} -> + {:error, + Invalid.exception( + field: :member_field_visibility, + message: "Settings not found" + )} + + {:error, error} -> + Logger.error("Failed to atomically update member_field_visibility: #{inspect(error)}") + + {:error, + Invalid.exception( + field: :member_field_visibility, + message: "Failed to update visibility" + )} + end + end) + end + + defp normalize_jsonb_result(updated_jsonb) do + case updated_jsonb do + map when is_map(map) -> + # Convert atom keys to strings if needed + Enum.reduce(map, %{}, fn + {k, v}, acc when is_atom(k) -> Map.put(acc, Atom.to_string(k), v) + {k, v}, acc -> Map.put(acc, k, v) + end) + + binary when is_binary(binary) -> + case Jason.decode(binary) do + {:ok, decoded} when is_map(decoded) -> + decoded + + # Not a map after decode + {:ok, _} -> + %{} + + {:error, reason} -> + Logger.warning("Failed to decode JSONB: #{inspect(reason)}") + %{} + end + + _ -> + Logger.warning("Unexpected JSONB format: #{inspect(updated_jsonb)}") + %{} + end + end +end diff --git a/lib/mv/helpers/type_parsers.ex b/lib/mv/helpers/type_parsers.ex new file mode 100644 index 0000000..6c07e6e --- /dev/null +++ b/lib/mv/helpers/type_parsers.ex @@ -0,0 +1,49 @@ +defmodule Mv.Helpers.TypeParsers do + @moduledoc """ + Helper functions for parsing various input types to common Elixir types. + + Provides safe parsing functions for common type conversions, especially useful + when dealing with form data or external APIs. + """ + + @doc """ + Parses various input types to boolean. + + Handles: booleans, strings ("true"/"false"), integers (1/0), and other values (defaults to false). + + ## Parameters + + - `value` - The value to parse (boolean, string, integer, or other) + + ## Returns + + A boolean value + + ## Examples + + iex> parse_boolean(true) + true + + iex> parse_boolean("true") + true + + iex> parse_boolean("false") + false + + iex> parse_boolean(1) + true + + iex> parse_boolean(0) + false + + iex> parse_boolean(nil) + false + """ + @spec parse_boolean(any()) :: boolean() + def parse_boolean(value) when is_boolean(value), do: value + def parse_boolean("true"), do: true + def parse_boolean("false"), do: false + def parse_boolean(1), do: true + def parse_boolean(0), do: false + def parse_boolean(_), do: false +end diff --git a/lib/mv/membership/helpers/visibility_config.ex b/lib/mv/membership/helpers/visibility_config.ex new file mode 100644 index 0000000..886d575 --- /dev/null +++ b/lib/mv/membership/helpers/visibility_config.ex @@ -0,0 +1,55 @@ +defmodule Mv.Membership.Helpers.VisibilityConfig do + @moduledoc """ + Helper functions for normalizing member field visibility configuration. + + Handles conversion between string keys (from JSONB) and atom keys (Elixir convention). + JSONB in PostgreSQL converts atom keys to string keys when storing. + This module provides functions to normalize these back to atoms for Elixir usage. + """ + + @doc """ + Normalizes visibility config map keys from strings to atoms. + + JSONB in PostgreSQL converts atom keys to string keys when storing. + This function converts them back to atoms for Elixir usage. + + ## Parameters + + - `config` - A map with either string or atom keys + + ## Returns + + A map with atom keys (where possible) + + ## Examples + + iex> normalize(%{"first_name" => true, "email" => false}) + %{first_name: true, email: false} + + iex> normalize(%{first_name: true, email: false}) + %{first_name: true, email: false} + + iex> normalize(%{"invalid_field" => true}) + %{} + """ + @spec normalize(map()) :: map() + def normalize(config) when is_map(config) do + Enum.reduce(config, %{}, fn + {key, value}, acc when is_atom(key) -> + Map.put(acc, key, value) + + {key, value}, acc when is_binary(key) -> + try do + atom_key = String.to_existing_atom(key) + Map.put(acc, atom_key, value) + rescue + ArgumentError -> acc + end + + _, acc -> + acc + end) + end + + def normalize(_), do: %{} +end diff --git a/lib/mv_web/components/core_components.ex b/lib/mv_web/components/core_components.ex index ccec5a5..45bcae0 100644 --- a/lib/mv_web/components/core_components.ex +++ b/lib/mv_web/components/core_components.ex @@ -692,10 +692,11 @@ defmodule MvWeb.CoreComponents do """ attr :name, :string, required: true attr :class, :string, default: "size-4" + attr :rest, :global, include: ~w(aria-hidden) def icon(%{name: "hero-" <> _} = assigns) do ~H""" - + """ end diff --git a/lib/mv_web/components/layouts/navbar.ex b/lib/mv_web/components/layouts/navbar.ex index e3e9319..0bd1699 100644 --- a/lib/mv_web/components/layouts/navbar.ex +++ b/lib/mv_web/components/layouts/navbar.ex @@ -46,7 +46,7 @@ defmodule MvWeb.Layouts.Navbar do
  • {gettext("Contributions")} -
      +
      • <.link navigate="/membership_fee_types">{gettext("Membership Fee Types")}
      • diff --git a/lib/mv_web/controllers/auth_controller.ex b/lib/mv_web/controllers/auth_controller.ex index 9282903..20a8b20 100644 --- a/lib/mv_web/controllers/auth_controller.ex +++ b/lib/mv_web/controllers/auth_controller.ex @@ -78,6 +78,12 @@ defmodule MvWeb.AuthController do end end + # Catch-all clause for any other error types + defp handle_rauthy_failure(conn, reason) do + Logger.warning("Unhandled Rauthy failure reason: #{inspect(reason)}") + redirect_with_error(conn, gettext("Unable to authenticate with OIDC. Please try again.")) + end + # Handle generic AuthenticationFailed errors defp handle_authentication_failed(conn, %Ash.Error.Forbidden{errors: errors}) do if Enum.any?(errors, &match?(%AshAuthentication.Errors.CannotConfirmUnconfirmedUser{}, &1)) do diff --git a/lib/mv_web/helpers/field_type_formatter.ex b/lib/mv_web/helpers/field_type_formatter.ex new file mode 100644 index 0000000..6cc86e6 --- /dev/null +++ b/lib/mv_web/helpers/field_type_formatter.ex @@ -0,0 +1,59 @@ +defmodule MvWeb.Helpers.FieldTypeFormatter do + @moduledoc """ + Helper functions for formatting field types for display. + + Handles both Ash type modules (e.g., `Ash.Type.String`) and simple atoms (e.g., `:string`). + """ + + alias MvWeb.Translations.FieldTypes + + @doc """ + Formats an Ash type for display. + + Handles both Ash type modules (e.g., `Ash.Type.String`) and simple atoms (e.g., `:string`). + + ## Parameters + + - `type` - An atom or module representing the field type + + ## Returns + + A human-readable string representation of the type + + ## Examples + + iex> format(:string) + "String" + + iex> format(Ash.Type.String) + "String" + + iex> format(Ash.Type.Date) + "Date" + """ + @spec format(atom() | module()) :: String.t() + def format(type) when is_atom(type) do + type_string = to_string(type) + + if String.contains?(type_string, "Ash.Type.") do + type_string + |> String.split(".") + |> List.last() + |> String.downcase() + |> then(fn type_name -> + try do + type_atom = String.to_existing_atom(type_name) + FieldTypes.label(type_atom) + rescue + ArgumentError -> FieldTypes.label(:string) + end + end) + else + FieldTypes.label(type) + end + end + + def format(type) do + to_string(type) + end +end diff --git a/lib/mv_web/helpers/membership_fee_helpers.ex b/lib/mv_web/helpers/membership_fee_helpers.ex index 53d32c7..4986ca6 100644 --- a/lib/mv_web/helpers/membership_fee_helpers.ex +++ b/lib/mv_web/helpers/membership_fee_helpers.ex @@ -8,9 +8,9 @@ defmodule MvWeb.Helpers.MembershipFeeHelpers do use Gettext, backend: MvWeb.Gettext + alias Mv.Membership.Member alias Mv.MembershipFees.CalendarCycles alias Mv.MembershipFees.MembershipFeeCycle - alias Mv.Membership.Member @doc """ Formats a decimal amount as currency string. diff --git a/lib/mv_web/live/components/field_visibility_dropdown_component.ex b/lib/mv_web/live/components/field_visibility_dropdown_component.ex index 5fc0abf..426daed 100644 --- a/lib/mv_web/live/components/field_visibility_dropdown_component.ex +++ b/lib/mv_web/live/components/field_visibility_dropdown_component.ex @@ -18,6 +18,8 @@ defmodule MvWeb.Components.FieldVisibilityDropdownComponent do use MvWeb, :live_component + alias MvWeb.Translations.MemberFields + # --------------------------------------------------------------------------- # UPDATE # --------------------------------------------------------------------------- @@ -66,7 +68,7 @@ defmodule MvWeb.Components.FieldVisibilityDropdownComponent do <.dropdown_menu id="field-visibility-menu" icon="hero-adjustments-horizontal" - button_label={gettext("Columns")} + button_label={gettext("Show/Hide Columns")} items={@all_items} checkboxes={true} selected={@selected_fields} @@ -153,12 +155,12 @@ defmodule MvWeb.Components.FieldVisibilityDropdownComponent do defp field_to_string(field) when is_binary(field), do: field defp format_field_label(field) when is_atom(field) do - MvWeb.Translations.MemberFields.label(field) + MemberFields.label(field) end defp format_field_label(field) when is_binary(field) do case safe_to_existing_atom(field) do - {:ok, atom} -> MvWeb.Translations.MemberFields.label(atom) + {:ok, atom} -> MemberFields.label(atom) :error -> fallback_label(field) end end diff --git a/lib/mv_web/live/contribution_type_live/index.ex b/lib/mv_web/live/contribution_type_live/index.ex index 9a7b602..3e2f04c 100644 --- a/lib/mv_web/live/contribution_type_live/index.ex +++ b/lib/mv_web/live/contribution_type_live/index.ex @@ -115,7 +115,7 @@ defmodule MvWeb.ContributionTypeLive.Index do

        {gettext( - "Contribution types define different membership fee structures. Each type has a fixed interval (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation." + "Contribution types define different membership fee structures. Each type has a fixed cycle (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation." )}

          diff --git a/lib/mv_web/live/custom_field_live/form_component.ex b/lib/mv_web/live/custom_field_live/form_component.ex index 172cfd3..71bdd03 100644 --- a/lib/mv_web/live/custom_field_live/form_component.ex +++ b/lib/mv_web/live/custom_field_live/form_component.ex @@ -26,12 +26,12 @@ defmodule MvWeb.CustomFieldLive.FormComponent do type="button" phx-click="cancel" phx-target={@myself} - aria-label={gettext("Back to custom field overview")} + aria-label={gettext("Back to settings")} > <.icon name="hero-arrow-left" class="w-4 h-4" />

          - {if @custom_field, do: gettext("Edit Custom Field"), else: gettext("New Custom Field")} + {if @custom_field, do: gettext("Edit Data Field"), else: gettext("New Data Field")}

        @@ -66,7 +66,7 @@ defmodule MvWeb.CustomFieldLive.FormComponent do {gettext("Cancel")} <.button phx-disable-with={gettext("Saving...")} variant="primary"> - {gettext("Save Custom Field")} + {gettext("Save Data Field")} diff --git a/lib/mv_web/live/custom_field_live/index_component.ex b/lib/mv_web/live/custom_field_live/index_component.ex index ccae3c6..5f26f78 100644 --- a/lib/mv_web/live/custom_field_live/index_component.ex +++ b/lib/mv_web/live/custom_field_live/index_component.ex @@ -17,158 +17,170 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do assigns = assign(assigns, :field_type_label, &MvWeb.Translations.FieldTypes.label/1) ~H""" -
        - <.form_section title={gettext("Custom Fields")}> -
        -

        - {gettext("These will appear in addition to other data when adding new members.")} -

        -
        - <.button - class="ml-auto" - variant="primary" - phx-click="new_custom_field" - phx-target={@myself} - > - <.icon name="hero-plus" /> {gettext("New Custom Field")} - -
        -
        - <%!-- Show form when creating or editing --%> -
        - <.live_component - module={MvWeb.CustomFieldLive.FormComponent} - id={@form_id} - custom_field={@editing_custom_field} - on_save={ - fn custom_field, action -> send(self(), {:custom_field_saved, custom_field, action}) end - } - on_cancel={fn -> send_update(__MODULE__, id: @id, show_form: false) end} - /> -
        - - <%!-- Hide table when form is visible --%> - <.table - :if={!@show_form} - id="custom_fields" - rows={@streams.custom_fields} - row_click={ - fn {_id, custom_field} -> - JS.push("edit_custom_field", value: %{id: custom_field.id}, target: @myself) - end - } - > - <:col :let={{_id, custom_field}} label={gettext("Name")}>{custom_field.name} - - <:col :let={{_id, custom_field}} label={gettext("Value Type")}> - {@field_type_label.(custom_field.value_type)} - - - <:col :let={{_id, custom_field}} label={gettext("Description")}> - {custom_field.description} - - - <:col - :let={{_id, custom_field}} - label={gettext("Show in overview")} - class="max-w-[9.375rem] text-center" +
        +
        +

        + {gettext("These will appear in addition to other data when adding new members.")} +

        +
        + <.button + class="ml-auto" + variant="primary" + phx-click="new_custom_field" + phx-target={@myself} > - - {gettext("Yes")} - - - {gettext("No")} - - + <.icon name="hero-plus" /> {gettext("New Data Field")} + +
        +
        + <%!-- Show form when creating or editing --%> +
        + <.live_component + module={MvWeb.CustomFieldLive.FormComponent} + id={@form_id} + custom_field={@editing_custom_field} + on_save={ + fn custom_field, action -> send(self(), {:custom_field_saved, custom_field, action}) end + } + on_cancel={fn -> send_update(__MODULE__, id: @id, show_form: false) end} + /> +
        - <:action :let={{_id, custom_field}}> - <.link phx-click={ - JS.push("edit_custom_field", value: %{id: custom_field.id}, target: @myself) - }> - {gettext("Edit")} - - + <%!-- Hide table when form is visible --%> + <.table + :if={!@show_form} + id="custom_fields" + rows={@streams.custom_fields} + row_click={ + fn {_id, custom_field} -> + JS.push("edit_custom_field", value: %{id: custom_field.id}, target: @myself) + end + } + > + <:col :let={{_id, custom_field}} label={gettext("Name")}>{custom_field.name} - <:action :let={{_id, custom_field}}> - <.link phx-click={ - JS.push("prepare_delete", value: %{id: custom_field.id}, target: @myself) - }> - {gettext("Delete")} - - - + <:col :let={{_id, custom_field}} label={gettext("Value Type")}> + {@field_type_label.(custom_field.value_type)} + - <%!-- Delete Confirmation Modal --%> - -
        """ end @impl true def update(assigns, socket) do + # Track previous show_form state to detect when form is closed + previous_show_form = Map.get(socket.assigns, :show_form, false) + # If show_form is explicitly provided in assigns, reset editing state socket = if Map.has_key?(assigns, :show_form) and assigns.show_form == false do @@ -179,6 +191,13 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do socket end + # Detect when form is closed (show_form changes from true to false) + new_show_form = Map.get(assigns, :show_form, false) + + if previous_show_form and not new_show_form do + send(self(), {:editing_section_changed, nil}) + end + {:ok, socket |> assign(assigns) @@ -193,6 +212,11 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do @impl true def handle_event("new_custom_field", _params, socket) do + # Only send event if form was not already open + if not socket.assigns[:show_form] do + send(self(), {:editing_section_changed, :custom_fields}) + end + {:noreply, socket |> assign(:show_form, true) @@ -204,6 +228,11 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do def handle_event("edit_custom_field", %{"id" => id}, socket) do custom_field = Ash.get!(Mv.Membership.CustomField, id) + # Only send event if form was not already open + if not socket.assigns[:show_form] do + send(self(), {:editing_section_changed, :custom_fields}) + end + {:noreply, socket |> assign(:show_form, true) diff --git a/lib/mv_web/live/custom_field_value_live/show.ex b/lib/mv_web/live/custom_field_value_live/show.ex index 42e9f43..7a3f678 100644 --- a/lib/mv_web/live/custom_field_value_live/show.ex +++ b/lib/mv_web/live/custom_field_value_live/show.ex @@ -26,7 +26,7 @@ defmodule MvWeb.CustomFieldValueLive.Show do ~H""" <.header> - Custom field value {@custom_field_value.id} + Data field value {@custom_field_value.id} <:subtitle>This is a custom_field_value record from your database. <:actions> @@ -62,6 +62,6 @@ defmodule MvWeb.CustomFieldValueLive.Show do |> assign(:custom_field_value, Ash.get!(Mv.Membership.CustomFieldValue, id))} end - defp page_title(:show), do: "Show Custom field value" - defp page_title(:edit), do: "Edit Custom field value" + defp page_title(:show), do: "Show data field value" + defp page_title(:edit), do: "Edit data field value" end diff --git a/lib/mv_web/live/global_settings_live.ex b/lib/mv_web/live/global_settings_live.ex index 9bce04b..a4332d3 100644 --- a/lib/mv_web/live/global_settings_live.ex +++ b/lib/mv_web/live/global_settings_live.ex @@ -31,6 +31,7 @@ defmodule MvWeb.GlobalSettingsLive do socket |> assign(:page_title, gettext("Settings")) |> assign(:settings, settings) + |> assign(:active_editing_section, nil) |> assign_form()} end @@ -62,11 +63,21 @@ defmodule MvWeb.GlobalSettingsLive do - <%!-- Custom Fields Section --%> - <.live_component - module={MvWeb.CustomFieldLive.IndexComponent} - id="custom-fields-component" - /> + <%!-- Memberdata Section --%> + <.form_section title={gettext("Memberdata")}> + <.live_component + :if={@active_editing_section != :custom_fields} + module={MvWeb.MemberFieldLive.IndexComponent} + id="member-fields-component" + settings={@settings} + /> + <%!-- Custom Fields Section --%> + <.live_component + :if={@active_editing_section != :member_fields} + module={MvWeb.CustomFieldLive.IndexComponent} + id="custom-fields-component" + /> + """ end @@ -105,12 +116,14 @@ defmodule MvWeb.GlobalSettingsLive do ) {:noreply, - put_flash(socket, :info, gettext("Custom field %{action} successfully", action: action))} + socket + |> assign(:active_editing_section, nil) + |> put_flash(:info, gettext("Data field %{action} successfully", action: action))} end @impl true def handle_info({:custom_field_deleted, _custom_field}, socket) do - {:noreply, put_flash(socket, :info, gettext("Custom field deleted successfully"))} + {:noreply, put_flash(socket, :info, gettext("Data field deleted successfully"))} end @impl true @@ -119,7 +132,7 @@ defmodule MvWeb.GlobalSettingsLive do put_flash( socket, :error, - gettext("Failed to delete custom field: %{error}", error: inspect(error)) + gettext("Failed to delete data field: %{error}", error: inspect(error)) )} end @@ -128,6 +141,43 @@ defmodule MvWeb.GlobalSettingsLive do {:noreply, put_flash(socket, :error, gettext("Slug does not match. Deletion cancelled."))} end + @impl true + def handle_info({:editing_section_changed, section}, socket) do + {:noreply, assign(socket, :active_editing_section, section)} + end + + @impl true + def handle_info({:member_field_saved, _member_field, action}, socket) do + # Reload settings to get updated member_field_visibility + {:ok, updated_settings} = Membership.get_settings() + + # Send update to member fields component to close form + send_update(MvWeb.MemberFieldLive.IndexComponent, + id: "member-fields-component", + show_form: false, + settings: updated_settings + ) + + {:noreply, + socket + |> assign(:settings, updated_settings) + |> assign(:active_editing_section, nil) + |> put_flash(:info, gettext("Member field %{action} successfully", action: action))} + end + + @impl true + def handle_info({:member_field_visibility_updated}, socket) do + # Legacy event - reload settings and update component + {:ok, updated_settings} = Membership.get_settings() + + send_update(MvWeb.MemberFieldLive.IndexComponent, + id: "member-fields-component", + settings: updated_settings + ) + + {:noreply, assign(socket, :settings, updated_settings)} + end + defp assign_form(%{assigns: %{settings: settings}} = socket) do form = AshPhoenix.Form.for_update( diff --git a/lib/mv_web/live/member_field_live/form_component.ex b/lib/mv_web/live/member_field_live/form_component.ex new file mode 100644 index 0000000..1bba048 --- /dev/null +++ b/lib/mv_web/live/member_field_live/form_component.ex @@ -0,0 +1,338 @@ +defmodule MvWeb.MemberFieldLive.FormComponent do + @moduledoc """ + LiveComponent form for editing member field properties (embedded in settings). + + ## Features + - Edit member field visibility (show_in_overview) + - Display member field information from Member Resource (read-only) + - Restrict editing for email field (only show_in_overview can be changed) + - Real-time validation + - Updates Settings.member_field_visibility atomically + + ## Props + - `member_field` - The member field atom to edit (e.g., :first_name, :email) + - `settings` - The current Settings resource + - `on_save` - Callback function to call when form is saved + - `on_cancel` - Callback function to call when form is cancelled + + ## Note + Member fields are technical fields that cannot be changed (name, value_type, description, required). + Only the visibility (show_in_overview) can be modified. + """ + use MvWeb, :live_component + + alias Mv.Helpers.TypeParsers + alias Mv.Membership + alias Mv.Membership.Helpers.VisibilityConfig + alias MvWeb.Helpers.FieldTypeFormatter + alias MvWeb.Translations.MemberFields + + @required_fields [:first_name, :last_name, :email] + + @impl true + def render(assigns) do + assigns = + assigns + |> assign(:field_attributes, get_field_attributes(assigns.member_field)) + |> assign(:is_email_field?, assigns.member_field == :email) + |> assign(:field_label, MemberFields.label(assigns.member_field)) + + ~H""" +
        +
        +
        + <.button + type="button" + phx-click="cancel" + phx-target={@myself} + aria-label={gettext("Back to Settings")} + > + <.icon name="hero-arrow-left" class="w-4 h-4" /> + +

        + {gettext("Edit Field: %{field}", field: @field_label)} +

        +
        + + <.form + for={@form} + id={@id <> "-form"} + phx-change="validate" + phx-submit="save" + phx-target={@myself} + > +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + <.input + :if={not @is_email_field?} + field={@form[:description]} + type="text" + label={gettext("Description")} + disabled={@is_email_field?} + readonly={@is_email_field?} + /> + +
        +
        + +
        +
        + <.input + :if={not @is_email_field?} + field={@form[:required]} + type="checkbox" + label={gettext("Required")} + disabled={@is_email_field?} + readonly={@is_email_field?} + /> + + <.input + field={@form[:show_in_overview]} + type="checkbox" + label={gettext("Show in overview")} + /> + +
        + <.button type="button" phx-click="cancel" phx-target={@myself}> + {gettext("Cancel")} + + <.button phx-disable-with={gettext("Saving...")} variant="primary"> + {gettext("Save Field")} + +
        + +
        +
        + """ + end + + @impl true + def update(assigns, socket) do + {:ok, + socket + |> assign(assigns) + |> assign_form()} + end + + @impl true + def handle_event("validate", %{"member_field" => member_field_params}, socket) do + # For member fields, we only validate show_in_overview + # Other fields are read-only or derived from the Member Resource + form = socket.assigns.form + + updated_params = + member_field_params + |> Map.put( + "show_in_overview", + TypeParsers.parse_boolean(member_field_params["show_in_overview"]) + ) + |> Map.put("name", form.source["name"]) + |> Map.put("value_type", form.source["value_type"]) + |> Map.put("description", form.source["description"]) + |> Map.put("required", form.source["required"]) + + updated_form = + form + |> Map.put(:value, updated_params) + |> Map.put(:errors, []) + + {:noreply, assign(socket, form: updated_form)} + end + + @impl true + def handle_event("save", %{"member_field" => member_field_params}, socket) do + # Only show_in_overview can be changed for member fields + show_in_overview = TypeParsers.parse_boolean(member_field_params["show_in_overview"]) + field_string = Atom.to_string(socket.assigns.member_field) + + # Use atomic action to update only this single field + # This prevents lost updates in concurrent scenarios + case Membership.update_single_member_field_visibility( + socket.assigns.settings, + field: field_string, + show_in_overview: show_in_overview + ) do + {:ok, _updated_settings} -> + socket.assigns.on_save.(socket.assigns.member_field, "update") + {:noreply, socket} + + {:error, error} -> + # Add error to form + form = + socket.assigns.form + |> Map.put(:errors, [ + %{field: :show_in_overview, message: format_error(error)} + ]) + + {:noreply, assign(socket, form: form)} + end + end + + @impl true + def handle_event("cancel", _params, socket) do + socket.assigns.on_cancel.() + {:noreply, socket} + end + + # Helper functions + + defp assign_form(%{assigns: %{member_field: member_field, settings: settings}} = socket) do + field_attributes = get_field_attributes(member_field) + visibility_config = settings.member_field_visibility || %{} + normalized_config = VisibilityConfig.normalize(visibility_config) + show_in_overview = Map.get(normalized_config, member_field, true) + + # Create a manual form structure with string keys + # Note: immutable is not included as it's not editable for member fields + form_data = %{ + "name" => MemberFields.label(member_field), + "value_type" => FieldTypeFormatter.format(field_attributes.value_type), + "description" => field_attributes.description || "", + "required" => field_attributes.required, + "show_in_overview" => show_in_overview + } + + form = to_form(form_data, as: "member_field") + + assign(socket, form: form) + end + + defp get_field_attributes(field) when is_atom(field) do + # Get attribute info from Member Resource + alias Ash.Resource.Info + + case Info.attribute(Mv.Membership.Member, field) do + nil -> + # Fallback for fields not in resource (shouldn't happen with Constants) + %{ + value_type: :string, + description: nil, + required: field in @required_fields + } + + attribute -> + %{ + value_type: attribute.type, + description: nil, + required: not attribute.allow_nil? + } + end + end + + defp format_error(%Ash.Error.Invalid{} = error) do + Ash.ErrorKind.message(error) + end + + defp format_error(error) do + inspect(error) + end +end diff --git a/lib/mv_web/live/member_field_live/index_component.ex b/lib/mv_web/live/member_field_live/index_component.ex new file mode 100644 index 0000000..5204030 --- /dev/null +++ b/lib/mv_web/live/member_field_live/index_component.ex @@ -0,0 +1,219 @@ +defmodule MvWeb.MemberFieldLive.IndexComponent do + @moduledoc """ + LiveComponent for managing member field visibility in overview (embedded in settings). + + ## Features + - List all member fields from Mv.Constants.member_fields() + - Display show_in_overview status as badge (Yes/No) + - Display required status based on actual attribute definitions (allow_nil? false) + - Edit member field properties (expandable form like custom fields) + - Updates Settings.member_field_visibility + """ + use MvWeb, :live_component + + alias Ash.Resource.Info + alias Mv.Membership + alias Mv.Membership.Helpers.VisibilityConfig + alias MvWeb.Helpers.FieldTypeFormatter + alias MvWeb.Translations.MemberFields + + @impl true + def render(assigns) do + assigns = + assigns + |> assign(:member_fields, get_member_fields_with_visibility(assigns.settings)) + |> assign(:required?, &required?/1) + + ~H""" +
        +

        + {gettext( + "These fields are neccessary for MILA to handle member identification and payment calculations in the future. Thus you cannot delete these fields but hide them in the member overview." + )} +

        + + <%!-- Show form when editing --%> +
        + <.live_component + module={MvWeb.MemberFieldLive.FormComponent} + id={@form_id} + member_field={@editing_member_field} + settings={@settings} + on_save={ + fn member_field, action -> + send(self(), {:member_field_saved, member_field, action}) + end + } + on_cancel={fn -> send_update(__MODULE__, id: @id, show_form: false) end} + /> +
        + + <%!-- Hide table when form is visible --%> + <.table + :if={!@show_form} + id="member_fields" + rows={@member_fields} + > + <:col :let={{_field_name, field_data}} label={gettext("Name")}> + {MemberFields.label(field_data.field)} + + + <:col :let={{_field_name, field_data}} label={gettext("Value Type")}> + {format_value_type(field_data.field)} + + + <:col :let={{_field_name, field_data}} label={gettext("Description")}> + {field_data.description || ""} + + + <:col + :let={{_field_name, field_data}} + label={gettext("Required")} + class="max-w-[9.375rem] text-center" + > + + {gettext("Required")} + + + {gettext("Optional")} + + + + <:col + :let={{_field_name, field_data}} + label={gettext("Show in overview")} + class="max-w-[9.375rem] text-center" + > + + {gettext("Yes")} + + + {gettext("No")} + + + + <:action :let={{_field_name, field_data}}> + <.link + phx-click="edit_member_field" + phx-value-field={Atom.to_string(field_data.field)} + phx-target={@myself} + > + {gettext("Edit")} + + + +
        + """ + end + + @impl true + def update(assigns, socket) do + # Track previous show_form state to detect when form is closed + previous_show_form = Map.get(socket.assigns, :show_form, false) + + # If show_form is explicitly provided in assigns, reset editing state + socket = + if Map.has_key?(assigns, :show_form) and assigns.show_form == false do + socket + |> assign(:editing_member_field, nil) + |> assign(:form_id, "member-field-form-new") + else + socket + end + + # Detect when form is closed (show_form changes from true to false) + new_show_form = Map.get(assigns, :show_form, false) + + if previous_show_form and not new_show_form do + send(self(), {:editing_section_changed, nil}) + end + + {:ok, + socket + |> assign(assigns) + |> assign_new(:settings, fn -> get_settings() end) + |> assign_new(:show_form, fn -> false end) + |> assign_new(:form_id, fn -> "member-field-form-new" end) + |> assign_new(:editing_member_field, fn -> nil end)} + end + + @impl true + def handle_event("edit_member_field", %{"field" => field_string}, socket) do + # Validate that the field is a valid member field before converting to atom + valid_fields = Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1) + + if field_string in valid_fields do + field_atom = String.to_existing_atom(field_string) + + # Only send event if form was not already open + if not socket.assigns[:show_form] do + send(self(), {:editing_section_changed, :member_fields}) + end + + {:noreply, + socket + |> assign(:show_form, true) + |> assign(:editing_member_field, field_atom) + |> assign(:form_id, "member-field-form-#{field_string}")} + else + {:noreply, socket} + end + end + + # Helper functions + + defp get_settings do + case Membership.get_settings() do + {:ok, settings} -> + settings + + {:error, _} -> + # Return a minimal struct-like map for fallback + # This is only used for initial rendering, actual settings will be loaded properly + %{member_field_visibility: %{}} + end + end + + defp get_member_fields_with_visibility(settings) do + member_fields = Mv.Constants.member_fields() + visibility_config = settings.member_field_visibility || %{} + + # Normalize visibility config keys to atoms + normalized_config = VisibilityConfig.normalize(visibility_config) + + Enum.map(member_fields, fn field -> + show_in_overview = Map.get(normalized_config, field, true) + attribute = Info.attribute(Mv.Membership.Member, field) + + %{ + field: field, + show_in_overview: show_in_overview, + value_type: (attribute && attribute.type) || :string, + description: nil + } + end) + |> Enum.map(fn field_data -> + {Atom.to_string(field_data.field), field_data} + end) + end + + defp format_value_type(field) when is_atom(field) do + case Info.attribute(Mv.Membership.Member, field) do + nil -> FieldTypeFormatter.format(:string) + attribute -> FieldTypeFormatter.format(attribute.type) + end + end + + # Check if a field is required by checking the actual attribute definition + defp required?(field) when is_atom(field) do + case Info.attribute(Mv.Membership.Member, field) do + nil -> false + attribute -> not attribute.allow_nil? + end + end + + defp required?(_), do: false +end diff --git a/lib/mv_web/live/member_live/index.ex b/lib/mv_web/live/member_live/index.ex index fff5517..34928cd 100644 --- a/lib/mv_web/live/member_live/index.ex +++ b/lib/mv_web/live/member_live/index.ex @@ -31,10 +31,10 @@ defmodule MvWeb.MemberLive.Index do import Ash.Expr alias Mv.Membership - alias MvWeb.MemberLive.Index.Formatter alias MvWeb.Helpers.DateFormatter alias MvWeb.MemberLive.Index.FieldSelection alias MvWeb.MemberLive.Index.FieldVisibility + alias MvWeb.MemberLive.Index.Formatter alias MvWeb.MemberLive.Index.MembershipFeeStatus # Prefix used in sort field names for custom fields (e.g., "custom_field_") diff --git a/lib/mv_web/live/member_live/index.html.heex b/lib/mv_web/live/member_live/index.html.heex index 1557ed9..b2af205 100644 --- a/lib/mv_web/live/member_live/index.html.heex +++ b/lib/mv_web/live/member_live/index.html.heex @@ -257,6 +257,24 @@ > {MvWeb.MemberLive.Index.format_date(member.join_date)} + <:col + :let={member} + :if={:exit_date in @member_fields_visible} + label={ + ~H""" + <.live_component + module={MvWeb.Components.SortHeaderComponent} + id={:sort_exit_date} + field={:exit_date} + label={gettext("Exit Date")} + sort_field={@sort_field} + sort_order={@sort_order} + /> + """ + } + > + {MvWeb.MemberLive.Index.format_date(member.exit_date)} + <:col :let={member} label={gettext("Membership Fee Status")} diff --git a/lib/mv_web/live/member_live/index/field_visibility.ex b/lib/mv_web/live/member_live/index/field_visibility.ex index c9c8bd6..9ba9267 100644 --- a/lib/mv_web/live/member_live/index/field_visibility.ex +++ b/lib/mv_web/live/member_live/index/field_visibility.ex @@ -20,6 +20,8 @@ defmodule MvWeb.MemberLive.Index.FieldVisibility do 3. Default (all fields visible) """ + alias Mv.Membership.Helpers.VisibilityConfig + @doc """ Gets all available fields for selection. @@ -177,13 +179,15 @@ defmodule MvWeb.MemberLive.Index.FieldVisibility do # Gets member field visibility from settings defp get_member_field_visibility_from_settings(settings) do visibility_config = - normalize_visibility_config(Map.get(settings, :member_field_visibility, %{})) + VisibilityConfig.normalize(Map.get(settings, :member_field_visibility, %{})) member_fields = Mv.Constants.member_fields() Enum.reduce(member_fields, %{}, fn field, acc -> field_string = Atom.to_string(field) - show_in_overview = Map.get(visibility_config, field, true) + # exit_date defaults to false (hidden), all other fields default to true + default_visibility = if field == :exit_date, do: false, else: true + show_in_overview = Map.get(visibility_config, field, default_visibility) Map.put(acc, field_string, show_in_overview) end) end @@ -199,27 +203,6 @@ defmodule MvWeb.MemberLive.Index.FieldVisibility do end) end - # Normalizes visibility config map keys from strings to atoms - defp normalize_visibility_config(config) when is_map(config) do - Enum.reduce(config, %{}, fn - {key, value}, acc when is_atom(key) -> - Map.put(acc, key, value) - - {key, value}, acc when is_binary(key) -> - try do - atom_key = String.to_existing_atom(key) - Map.put(acc, atom_key, value) - rescue - ArgumentError -> acc - end - - _, acc -> - acc - end) - end - - defp normalize_visibility_config(_), do: %{} - # Converts field string to atom (for member fields) or keeps as string (for custom fields) defp to_field_identifier(field_string) when is_binary(field_string) do if String.starts_with?(field_string, Mv.Constants.custom_field_prefix()) do diff --git a/lib/mv_web/live/member_live/show/membership_fees_component.ex b/lib/mv_web/live/member_live/show/membership_fees_component.ex index f96fd73..d8c49eb 100644 --- a/lib/mv_web/live/member_live/show/membership_fees_component.ex +++ b/lib/mv_web/live/member_live/show/membership_fees_component.ex @@ -15,10 +15,10 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do require Ash.Query alias Mv.Membership - alias Mv.MembershipFees.MembershipFeeType - alias Mv.MembershipFees.MembershipFeeCycle - alias Mv.MembershipFees.CycleGenerator alias Mv.MembershipFees.CalendarCycles + alias Mv.MembershipFees.CycleGenerator + alias Mv.MembershipFees.MembershipFeeCycle + alias Mv.MembershipFees.MembershipFeeType alias MvWeb.Helpers.MembershipFeeHelpers @impl true @@ -63,7 +63,7 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do phx-click="delete_all_cycles" phx-target={@myself} class="btn btn-sm btn-error btn-outline" - title={gettext("Delete all cycles")} + title={gettext("Delete All Cycles")} > <.icon name="hero-trash" class="size-4" /> {gettext("Delete All Cycles")} @@ -168,7 +168,7 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do phx-value-cycle_id={cycle.id} phx-target={@myself} class="btn btn-sm btn-error btn-outline" - title={gettext("Delete cycle")} + title={gettext("Delete Cycle")} > <.icon name="hero-trash" class="size-4" /> {gettext("Delete")} @@ -329,16 +329,14 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do />
        <%= if @create_cycle_date do %>
        {format_create_cycle_period( diff --git a/lib/mv_web/live/membership_fee_type_live/form.ex b/lib/mv_web/live/membership_fee_type_live/form.ex index 5acb8c9..77a73af 100644 --- a/lib/mv_web/live/membership_fee_type_live/form.ex +++ b/lib/mv_web/live/membership_fee_type_live/form.ex @@ -15,9 +15,9 @@ defmodule MvWeb.MembershipFeeTypeLive.Form do require Ash.Query + alias Mv.Membership.Member alias Mv.MembershipFees alias Mv.MembershipFees.MembershipFeeType - alias Mv.Membership.Member alias MvWeb.Helpers.MembershipFeeHelpers @impl true diff --git a/lib/mv_web/live/membership_fee_type_live/index.ex b/lib/mv_web/live/membership_fee_type_live/index.ex index 176d4e1..f105058 100644 --- a/lib/mv_web/live/membership_fee_type_live/index.ex +++ b/lib/mv_web/live/membership_fee_type_live/index.ex @@ -16,10 +16,10 @@ defmodule MvWeb.MembershipFeeTypeLive.Index do require Ash.Query - alias Mv.MembershipFees - alias Mv.MembershipFees.MembershipFeeType alias Mv.Membership alias Mv.Membership.Member + alias Mv.MembershipFees + alias Mv.MembershipFees.MembershipFeeType alias MvWeb.Helpers.MembershipFeeHelpers @impl true @@ -115,7 +115,7 @@ defmodule MvWeb.MembershipFeeTypeLive.Index do phx-value-id={mft.id} data-confirm={gettext("Are you sure?")} class="btn btn-ghost btn-xs text-error" - aria-label={gettext("Delete membership fee type")} + aria-label={gettext("Delete Membership Fee Type")} > <.icon name="hero-trash" class="size-4" /> diff --git a/lib/mv_web/translations/member_fields.ex b/lib/mv_web/translations/member_fields.ex index 2d6834a..26f55ac 100644 --- a/lib/mv_web/translations/member_fields.ex +++ b/lib/mv_web/translations/member_fields.ex @@ -27,6 +27,7 @@ defmodule MvWeb.Translations.MemberFields do def label(:street), do: gettext("Street") def label(:house_number), do: gettext("House Number") def label(:postal_code), do: gettext("Postal Code") + def label(:membership_fee_start_date), do: gettext("Membership Fee Start Date") # Fallback for unknown fields def label(field) do diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index f1dc9e9..90f8de5 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -49,6 +49,7 @@ 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 #: lib/mv_web/live/role_live/form.ex #: lib/mv_web/live/role_live/index.html.heex @@ -127,6 +128,7 @@ msgid "close" msgstr "schließen" #: 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 #: lib/mv_web/translations/member_fields.ex #, elixir-autogen, elixir-format @@ -171,6 +173,7 @@ 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 #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/role_live/form.ex @@ -187,6 +190,7 @@ msgid "Street" msgstr "Straße" #: 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/formatter.ex #: lib/mv_web/live/member_live/show.ex #: lib/mv_web/live/role_live/show.ex @@ -200,6 +204,7 @@ msgid "Show Member" msgstr "Mitglied anzeigen" #: 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/formatter.ex #: lib/mv_web/live/member_live/show.ex #: lib/mv_web/live/member_live/show/membership_fees_component.ex @@ -260,6 +265,7 @@ 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 #: lib/mv_web/live/membership_fee_type_live/form.ex @@ -276,6 +282,8 @@ 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 +#: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/role_live/form.ex #: lib/mv_web/live/role_live/index.html.heex @@ -322,6 +330,8 @@ 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 +#: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/membership_fee_type_live/index.ex #: lib/mv_web/live/role_live/form.ex @@ -358,6 +368,9 @@ msgid "Profil" msgstr "Profil" #: 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 +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Required" msgstr "Erforderlich" @@ -415,6 +428,7 @@ 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 msgid "Value type" msgstr "Wertetyp" @@ -612,11 +626,6 @@ msgstr "Wähle ein Benutzerdefiniertes Feld" msgid "Custom field" msgstr "Benutzerdefinierte Felder" -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Custom field %{action} successfully" -msgstr "Benutzerdefiniertes Feld erfolgreich %{action}" - #: lib/mv_web/live/custom_field_value_live/form.ex #, elixir-autogen, elixir-format msgid "Custom field value %{action} successfully" @@ -627,7 +636,6 @@ msgstr "Benutzerdefinierter Feldwert erfolgreich %{action}" msgid "Please select a custom field first" msgstr "Bitte wähle zuerst ein Benutzerdefiniertes Feld" -#: lib/mv_web/live/custom_field_live/index_component.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format @@ -651,11 +659,6 @@ msgstr[1] "%{count} Mitglieder haben Werte für dieses benutzerdefinierte Feld z msgid "All custom field values will be permanently deleted when you delete this custom field." msgstr "Alle benutzerdefinierten Feldwerte werden beim Löschen dieses benutzerdefinierten Feldes dauerhaft gelöscht." -#: lib/mv_web/live/custom_field_live/index_component.ex -#, elixir-autogen, elixir-format -msgid "Delete Custom Field" -msgstr "Benutzerdefiniertes Feld löschen" - #: lib/mv_web/live/custom_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Delete Custom Field and All Values" @@ -673,6 +676,8 @@ msgstr "Um die Löschung zu bestätigen, gib bitte folgenden Text ein:" #: 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 +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Show in overview" msgstr "In Übersicht anzeigen" @@ -888,6 +893,7 @@ 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" @@ -923,11 +929,6 @@ msgstr "Beitragsarten" msgid "Contribution type" msgstr "Beitragsart" -#: 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 interval (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/components/layouts/navbar.ex #, elixir-autogen, elixir-format, fuzzy msgid "Contributions" @@ -998,7 +999,7 @@ msgstr "Ehrenamtlich" #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format msgid "Interval" -msgstr "Zyklus" +msgstr "Intervall" #: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format, fuzzy @@ -1226,11 +1227,6 @@ msgstr "Warum werden nicht alle Beitragsarten angezeigt?" msgid "Yearly" msgstr "jährlich" -#: lib/mv_web/live/components/field_visibility_dropdown_component.ex -#, elixir-autogen, elixir-format -msgid "Columns" -msgstr "Spalten" - #: lib/mv_web/live/components/field_visibility_dropdown_component.ex #, elixir-autogen, elixir-format msgid "Custom Field %{id}" @@ -1262,32 +1258,6 @@ msgstr "Alle auswählen" msgid "Select none" msgstr "Keine auswählen" -#: lib/mv_web/live/custom_field_live/form_component.ex -#, elixir-autogen, elixir-format -msgid "Back to custom field overview" -msgstr "Zurück zur Felderliste" - -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Custom field deleted successfully" -msgstr "Benutzerdefiniertes Feld erfolgreich gelöscht" - -#: lib/mv_web/live/custom_field_live/form_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Edit Custom Field" -msgstr "Benutzerdefiniertes Feld löschen" - -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Failed to delete custom field: %{error}" -msgstr "Konnte Feld nicht löschen: %{error}" - -#: lib/mv_web/live/custom_field_live/form_component.ex -#: lib/mv_web/live/custom_field_live/index_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "New Custom Field" -msgstr "Neues Benutzerdefiniertes Feld" - #: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format msgid "Slug does not match. Deletion cancelled." @@ -1299,6 +1269,7 @@ msgid "These will appear in addition to other data when adding new members." msgstr "Diese Felder können zusätzlich zu den normalen Daten ausgefüllt werden, wenn ein neues Mitglied angelegt wird." #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format, fuzzy msgid "Value Type" msgstr "Wertetyp" @@ -1329,107 +1300,42 @@ msgstr "Textfeld" msgid "Yes/No-Selection" msgstr "Ja/Nein-Auswahl" -#: lib/mv_web/live/member_live/index.html.heex +#: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Copy email addresses" -msgstr "E-Mail-Adressen kopieren" +msgid "Memberdata" +msgstr "Mitgliederdaten" -#: lib/mv_web/live/custom_field_live/form_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Save Custom Field" -msgstr "Benutzerdefiniertes Feld speichern" - -#: 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/components/core_components.ex +#: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format -msgid "This field is required" -msgstr "Dieses Feld ist erforderlich" +msgid "Optional" +msgstr "Optional" -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Configure global settings for membership fees." -msgstr "Globale Einstellungen für Mitgliedsbeiträge konfigurieren." - -#: lib/mv_web/live/membership_fee_settings_live.ex +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format -msgid "Default Membership Fee Type" -msgstr "Standard-Mitgliedsbeitragsart" +msgid "These fields are neccessary for MILA to handle member identification and payment calculations in the future. Thus you cannot delete these fields but hide them in the member overview." +msgstr "Diese Datenfelder sind für MILA notwendig um Mitglieder zu identifizieren und zukünftig Beitragszahlungen zu berechnen. Aus diesem Grund können sie nicht gelöscht, aber in der Übersicht ausgeblendet werden." -#: lib/mv_web/live/membership_fee_settings_live.ex +#: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Generated cycles" -msgstr "Generierte Zyklen" +msgid "Member field %{action} successfully" +msgstr "Mitglied wurde erfolgreich %{action}" -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Include joining cycle" -msgstr "Beitrittsdatum einbeziehen" - -#: lib/mv_web/components/layouts/navbar.ex -#: lib/mv_web/live/membership_fee_settings_live.ex +#: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format -msgid "Membership Fee Settings" -msgstr "Mitgliedsbeitragseinstellungen" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Membership fee start" -msgstr "Beitragsbeginn" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Monthly Interval - Joining Cycle Included" -msgstr "Monatliches Intervall – Beitrittszeitraum einbezogen" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "None (no default)" -msgstr "Keine (kein Standard)" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Quarterly Interval - Joining Cycle Excluded" -msgstr "Vierteljährliches Intervall – Beitrittszeitraum nicht einbezogen" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Settings saved successfully." -msgstr "Einstellungen erfolgreich gespeichert" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "This membership fee type is automatically assigned to all new members. Can be changed individually per member." -msgstr "Diese Mitgliedsbeitragsart wird automatisch allen neuen Mitgliedern zugewiesen. Kann individuell pro Mitglied geändert werden." - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "When active: Members pay from the cycle of their joining." -msgstr "Wenn aktiviert: Mitglieder zahlen ab dem Zeitraum ihres Beitritts." - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "When inactive: Members pay from the next full cycle after joining." -msgstr "Wenn deaktiviert: Mitglieder zahlen ab dem nächsten vollen Beitragszyklus nach dem Beitritt." - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Yearly Interval - Joining Cycle Excluded" -msgstr "Jährliches Intervall – Beitrittszeitraum nicht einbezogen" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Yearly Interval - Joining Cycle Included" -msgstr "Jährliches Intervall – Beitrittszeitraum einbezogen" +msgid "A cycle for this period already exists" +msgstr "Für dieses Intervall besteht bereits ein Zyklus." #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format msgid "About Membership Fee Types" msgstr "Über Mitgliedsbeitragsarten" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "All cycles deleted" +msgstr "Alle Zyklen gelöscht" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Already paid cycles will remain with the old amount." @@ -1462,16 +1368,56 @@ msgstr "Betrag ändern?" msgid "Changing the amount will affect %{count} member(s)." msgstr "Die Änderung des Betrags betrifft %{count} Mitglied(er)." +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Click to edit amount" +msgstr "Klicke um den Betrag zu ändern" + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Configure global settings for membership fees." +msgstr "Globale Einstellungen für Mitgliedsbeiträge konfigurieren." + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Confirm Change" msgstr "Änderung bestätigen" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Confirmation text does not match" +msgstr "Bestätigungstext stimmt nicht überein" + +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format, fuzzy +msgid "Copy email addresses" +msgstr "E-Mail-Adressen kopieren" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Create" +msgstr "erstellt" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Create Cycle" +msgstr "Aktueller Zyklus" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Create a new cycle manually" +msgstr "Erstelle manuell einen neuen Zyklus" + #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Current Cycle" msgstr "Aktueller Zyklus" +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Current Cycle Payment Status" +msgstr "Aktueller Zyklus Zahlungsstatus" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Current amount" @@ -1487,6 +1433,11 @@ msgstr "Zyklus" msgid "Cycle amount updated" msgstr "Zyklusbetrag aktualisiert" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Cycle created successfully" +msgstr "Zyklen erfolgreich regeneriert" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Cycle deleted" @@ -1502,6 +1453,21 @@ msgstr "Zyklenstatus aktualisiert" msgid "Cycles regenerated successfully" msgstr "Zyklen erfolgreich regeneriert" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Default Membership Fee Type" +msgstr "Standard-Mitgliedsbeitragsart" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Delete All" +msgstr "Löschen" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Delete All Cycles" +msgstr "Alle Zyklen löschen" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format, fuzzy msgid "Delete Cycle" @@ -1512,11 +1478,21 @@ msgstr "Zyklus löschen" msgid "Edit Cycle Amount" msgstr "Zyklusbetrag bearbeiten" +#: lib/mv_web/live/member_field_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Edit Field: %{field}" +msgstr "Mitglied bearbeiten" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Edit Membership Fee Type" msgstr "Mitgliedsbeitragsart bearbeiten" +#: lib/mv_web/live/membership_fee_type_live/index.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Edit membership fee type" +msgstr "Mitgliedsbeitragsart bearbeiten" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Failed to update cycle status: %{errors}" @@ -1532,6 +1508,16 @@ msgstr "Zukünftige unbezahlte Zyklen werden mit dem neuen Betrag regeneriert." msgid "Generate cycles from the last existing cycle to today" msgstr "Zyklen vom letzten existierenden Zyklus bis heute generieren" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Generated cycles" +msgstr "Generierte Zyklen" + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Include joining cycle" +msgstr "Beitrittsdatum einbeziehen" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Interval cannot be changed after creation." @@ -1542,11 +1528,21 @@ msgstr "Das Intervall kann nach der Erstellung nicht geändert werden." msgid "Invalid amount format" msgstr "Ungültiges Betragsformat" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Invalid date format" +msgstr "Ungültiges Betragsformat" + #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Last Cycle" msgstr "Letzter Zyklus" +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Last Cycle Payment Status" +msgstr "Letzter Zyklus Zahlungsstatus" + #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format msgid "Manage membership fee types for membership fees." @@ -1573,6 +1569,12 @@ msgstr "Als unbezahlt markieren" msgid "Membership Fee" msgstr "Mitgliedsbeitrag" +#: lib/mv_web/components/layouts/navbar.ex +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Membership Fee Settings" +msgstr "Mitgliedsbeitragseinstellungen" + #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format, fuzzy msgid "Membership Fee Status" @@ -1596,6 +1598,11 @@ msgstr "Mitgliedsbeitragsarten" msgid "Membership Fees" msgstr "Mitgliedsbeiträge" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Membership fee start" +msgstr "Beitragsbeginn" + #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format msgid "Membership fee type deleted" @@ -1621,6 +1628,11 @@ msgstr "Mitgliedsbeitragsart aktualisiert. Zyklen regeneriert." msgid "Membership fee types define different membership fee structures. Each type has a fixed interval (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation." msgstr "Mitgliedsbeitragsarten definieren verschiedene Mitgliedsbeitragsstrukturen. Jede Art hat ein festes Intervall (monatlich, vierteljährlich, halbjährlich, jährlich), das nach der Erstellung nicht geändert werden kann." +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Monthly Interval - Joining Cycle Included" +msgstr "Monatliches Intervall – Beitrittszeitraum einbezogen" + #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format @@ -1642,6 +1654,11 @@ msgstr "Kein Zyklus" msgid "No cycles" msgstr "Keine Zyklen" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "No cycles to delete" +msgstr "Keine Zyklen" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "No membership fee cycles found. Cycles will be generated automatically when a membership fee type is assigned." @@ -1658,11 +1675,31 @@ msgstr "Keine Mitgliedsbeitragsart zugewiesen" msgid "No status" msgstr "Kein Status" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "None (no default)" +msgstr "Keine (kein Standard)" + +#: lib/mv_web/live/member_live/show.ex +#, elixir-autogen, elixir-format +msgid "Not set" +msgstr "Nicht gesetzt" + +#: lib/mv_web/live/member_live/show.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Payment Interval" +msgstr "Zahlungsfilter" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Please confirm the amount change first" msgstr "Bitte bestätigen Sie zuerst die Betragsänderung" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Quarterly Interval - Joining Cycle Excluded" +msgstr "Vierteljährliches Intervall – Beitrittszeitraum nicht einbezogen" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Regenerate Cycles" @@ -1673,6 +1710,16 @@ 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" +msgstr "Benutzerdefiniertes Feld speichern" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Save Membership Fee Type" @@ -1689,193 +1736,198 @@ msgid "Select interval" msgstr "Intervall auswählen" #: lib/mv_web/live/member_live/show.ex -#: lib/mv_web/live/role_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "This action cannot be undone." +msgstr "Diese Aktion kann nicht rückgängig gemacht werden." + +#: lib/mv_web/components/core_components.ex +#, elixir-autogen, elixir-format +msgid "This field is required" +msgstr "Dieses Feld ist erforderlich" + +#: lib/mv_web/live/member_field_live/form_component.ex +#, elixir-autogen, elixir-format +msgid "This is a technical field and cannot be changed" +msgstr "Dies ist ein technisches Feld und kann nicht verändert werden." + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "This membership fee type is automatically assigned to all new members. Can be changed individually per member." +msgstr "Diese Mitgliedsbeitragsart wird automatisch allen neuen Mitgliedern zugewiesen. Kann individuell pro Mitglied geändert werden." + +#: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Type" msgstr "Art" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Type '%{confirmation}' to confirm" +msgstr "Trage '%{confirmation}' ein um zu bestätigen" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Use this form to manage membership fee types in your database." msgstr "Verwenden Sie dieses Formular, um Mitgliedsbeitragsarten in Ihrer Datenbank zu verwalten." +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Warning" +msgstr "Warnung" + #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Warning: Changing from %{old_interval} to %{new_interval} is not allowed. Please select a membership fee type with the same interval." msgstr "Warnung: Wechsel von %{old_interval} zu %{new_interval} ist nicht erlaubt. Bitte wählen Sie eine Mitgliedsbeitragsart mit demselben Intervall." -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "A cycle for this period already exists" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "All cycles deleted" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Click to edit amount" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Create" -msgstr "erstellt" +msgid "When active: Members pay from the cycle of their joining." +msgstr "Wenn aktiviert: Mitglieder zahlen ab dem Zeitraum ihres Beitritts." -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Create Cycle" -msgstr "Aktueller Zyklus" +msgid "When inactive: Members pay from the next full cycle after joining." +msgstr "Wenn deaktiviert: Mitglieder zahlen ab dem nächsten vollen Beitragszyklus nach dem Beitritt." -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Create a new cycle manually" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Cycle Period" -msgstr "Zyklus" +msgid "Yearly Interval - Joining Cycle Excluded" +msgstr "Jährliches Intervall – Beitrittszeitraum nicht einbezogen" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Cycle created successfully" -msgstr "Zyklen erfolgreich regeneriert" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Delete All" -msgstr "Löschen" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Delete All Cycles" -msgstr "Alle Zyklen löschen" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Delete all cycles" -msgstr "Zyklus löschen" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Delete cycle" -msgstr "Zyklus löschen" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Invalid date format" -msgstr "Ungültiges Betragsformat" - -#: lib/mv_web/live/member_live/show.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Payment Interval" -msgstr "Zahlungsfilter" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "The cycle period will be calculated based on this date and the interval." -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "This action cannot be undone." -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Type '%{confirmation}' to confirm" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Warning" -msgstr "" +msgid "Yearly Interval - Joining Cycle Included" +msgstr "Jährliches Intervall – Beitrittszeitraum einbezogen" #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "You are about to delete all %{count} cycles for this member." -msgstr "" +msgstr "Du bist dabei alle %{count} Zyklen für dieses Mitglied zu löschen." -#: lib/mv_web/live/member_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "Current Cycle Payment Status" -msgstr "Aktueller Zyklus Zahlungsstatus" - -#: lib/mv_web/live/member_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "Last Cycle Payment Status" -msgstr "Letzter Zyklus Zahlungsstatus" - -#: lib/mv_web/live/membership_fee_type_live/index.ex -#, elixir-autogen, elixir-format -msgid "Delete membership fee type" -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 "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 "Edit membership fee type" -msgstr "Mitgliedsbeitragsart bearbeiten" +msgid "Delete Membership Fee Type" +msgstr "Mitgliedsbeitragsart löschen" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/translations/member_fields.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Membership Fee Start Date" +msgstr "Mitgliedsbeitragsstatus" + +#: lib/mv_web/live/components/field_visibility_dropdown_component.ex #, elixir-autogen, elixir-format -msgid "Confirmation text does not match" -msgstr "" +msgid "Show/Hide Columns" +msgstr "Spalten ein-/ausblenden" #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format, fuzzy -msgid "No cycles to delete" -msgstr "Keine Zyklen" +msgid "The cycle 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/member_live/show.ex -#, elixir-autogen, elixir-format -msgid "Not set" -msgstr "Nicht gesetzt" +#: lib/mv_web/live/custom_field_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Back to settings" +msgstr "Zurück zu den Einstellungen" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Data field %{action} successfully" +msgstr "Datenfeld erfolgreich %{action}" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Data field deleted successfully" +msgstr "Datenfeld erfolgreich gelöscht" + +#: lib/mv_web/live/custom_field_live/index_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Delete Data Field" +msgstr "Datenfeld löschen" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Edit Data Field" +msgstr "Datenfeld bearbeiten" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Failed to delete data field: %{error}" +msgstr "Konnte Datenfeld nicht löschen: %{error}" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#: lib/mv_web/live/custom_field_live/index_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "New Data Field" +msgstr "Neues Datenfeld" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Save Data Field" +msgstr "Datenfeld speichern" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Show current cycle" +#~ msgstr "Aktuellen Zyklus anzeigen" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Unpaid in last cycle" +#~ msgstr "Unbezahlt im letzten Zyklus" + +#~ #: lib/mv_web/live/custom_field_live/index_component.ex +#~ #, elixir-autogen, elixir-format, fuzzy +#~ msgid "New Custom field" +#~ msgstr "Benutzerdefiniertes Feld speichern" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Show Last/Current Cycle Payment Status" +#~ msgstr "" #: lib/mv_web/live/role_live/show.ex #, elixir-autogen, elixir-format, fuzzy msgid "Back to roles list" msgstr "Zurück zur Rollen-Liste" -#: lib/mv_web/live/role_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "Cannot delete system role" -msgstr "System-Rolle kann nicht gelöscht werden" +#~ #: lib/mv_web/live/custom_field_live/show.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Auto-generated identifier (immutable)" +#~ msgstr "Automatisch generierter Bezeichner (unveränderlich)" -#: lib/mv_web/live/role_live/index.html.heex -#, elixir-autogen, elixir-format, fuzzy -msgid "Custom" -msgstr "Benutzerdefiniert" +#~ #: lib/mv_web/live/contribution_settings_live.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Configure global settings for membership contributions." +#~ msgstr "Globale Einstellungen für Mitgliedsbeiträge konfigurieren." -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Edit Role" -msgstr "Bearbeiten" +#~ #: lib/mv_web/live/member_live/form.ex +#~ #: lib/mv_web/live/member_live/show.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Contribution" +#~ msgstr "Beitrag" -#: lib/mv_web/live/role_live/index.ex -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Failed to delete role: %{error}" -msgstr "Rolle konnte nicht gelöscht werden: %{error}" +#~ #: lib/mv_web/components/layouts/navbar.ex +#~ #: lib/mv_web/live/contribution_settings_live.ex +#~ #, elixir-autogen, elixir-format, fuzzy +#~ msgid "Contribution Settings" +#~ msgstr "Beitragseinstellungen" -#: lib/mv_web/live/role_live/index.ex -#: lib/mv_web/live/role_live/index.html.heex -#, elixir-autogen, elixir-format, fuzzy -msgid "Listing Roles" -msgstr "Rollen auflisten" +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Copy emails" +#~ msgstr "E-Mails kopieren" -#: lib/mv_web/live/role_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "Manage user roles and their permission sets." -msgstr "Verwalte Benutzer*innen-Rollen und ihre Berechtigungssätze." - -#: lib/mv_web/live/role_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "New Role" -msgstr "Neue Rolle" +#~ #: lib/mv_web/live/contribution_settings_live.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Default Contribution Type" +#~ msgstr "Standard-Beitragsart" #: lib/mv_web/live/role_live/index.html.heex #: lib/mv_web/live/role_live/show.ex @@ -1883,119 +1935,40 @@ msgstr "Neue Rolle" msgid "No description" msgstr "Beschreibung" -#: lib/mv_web/live/role_live/form.ex -#: lib/mv_web/live/role_live/index.html.heex -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format -msgid "Permission Set" -msgstr "Berechtigungssatz" +#~ #: lib/mv_web/live/member_live/show/membership_fees_component.ex +#~ #, elixir-autogen, elixir-format, fuzzy +#~ msgid "Failed to delete some cycles: %{errors}" +#~ msgstr "Konnte Feld nicht löschen: %{error}" -#: lib/mv_web/live/role_live/form.ex -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format -msgid "Role" -msgstr "" +#~ #: lib/mv_web/live/custom_field_live/form_component.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Immutable" +#~ msgstr "Unveränderlich" -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format -msgid "Role details and permissions." -msgstr "Rollen-Details und Berechtigungen." +#~ #: lib/mv_web/live/contribution_settings_live.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Include joining period" +#~ msgstr "Beitrittsdatum einbeziehen" -#: lib/mv_web/live/role_live/form.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Save Role" -msgstr "Rolle speichern" +#~ #: lib/mv_web/live/custom_field_live/index_component.ex +#~ #, elixir-autogen, elixir-format, fuzzy +#~ msgid "New Custom field" +#~ msgstr "Benutzerdefiniertes Feld speichern" -#: lib/mv_web/live/role_live/form.ex -#, elixir-autogen, elixir-format -msgid "Select permission set" -msgstr "Berechtigungssatz auswählen" +#~ #: lib/mv_web/live/components/payment_filter_component.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Not paid" +#~ msgstr "Nicht bezahlt" -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Show Role" -msgstr "Anzeigen" +#~ #: lib/mv_web/live/member_live/show.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Payment Cycle" +#~ msgstr "Zahlungszyklus" -#: lib/mv_web/live/role_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "System" -msgstr "System" - -#: lib/mv_web/live/role_live/index.html.heex -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format -msgid "System Role" -msgstr "System-Rolle" - -#: lib/mv_web/live/role_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "System roles cannot be deleted" -msgstr "System-Rollen können nicht gelöscht werden" - -#: lib/mv_web/live/role_live/form.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Use this form to manage roles in your database." -msgstr "Verwenden Sie dieses Formular, um Rollen in Ihrer Datenbank zu verwalten." - -#: lib/mv_web/live/role_live/form.ex -#, elixir-autogen, elixir-format -msgid "admin - Unrestricted access" -msgstr "admin - Uneingeschränkter Zugriff" - -#: lib/mv_web/live/role_live/form.ex -#, elixir-autogen, elixir-format -msgid "normal_user - Create/Read/Update access" -msgstr "normal_user - Erstellen/Lesen/Aktualisieren Zugriff" - -#: lib/mv_web/live/role_live/form.ex -#, elixir-autogen, elixir-format -msgid "own_data - Access only to own data" -msgstr "own_data - Zugriff nur auf eigene Daten" - -#: lib/mv_web/live/role_live/form.ex -#, elixir-autogen, elixir-format -msgid "read_only - Read access to all data" -msgstr "read_only - Lesezugriff auf alle Daten" - -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format -msgid "Delete Role" -msgstr "Rolle löschen" - -#: lib/mv_web/live/role_live/index.ex -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format -msgid "Role deleted successfully." -msgstr "Rolle erfolgreich gelöscht." - -#: lib/mv_web/components/layouts/navbar.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Roles" -msgstr "Rollen" - -#: lib/mv_web/live/role_live/index.ex -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format -msgid "Cannot delete role. %{count} user(s) are still assigned to this role. Please assign them to another role first." -msgstr "Rolle kann nicht gelöscht werden. %{count} Benutzer*in(nen) sind dieser Rolle noch zugeordnet. Bitte weisen Sie sie zunächst einer anderen Rolle zu." - -#: lib/mv_web/live/role_live/form.ex -#: lib/mv_web/live/role_live/index.ex -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format -msgid "Role not found." -msgstr "Rolle nicht gefunden." - -#: lib/mv_web/live/role_live/form.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Role saved successfully." -msgstr "Rolle erfolgreich gespeichert" - -#: lib/mv_web/live/role_live/index.ex -#: lib/mv_web/live/role_live/show.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "System roles cannot be deleted." -msgstr "System-Rollen können nicht gelöscht werden." +#~ #: lib/mv_web/live/member_live/show.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Pending" +#~ msgstr "Ausstehend" #~ #: lib/mv_web/live/member_live/form.ex #~ #: lib/mv_web/live/member_live/show.ex @@ -2013,3 +1986,65 @@ msgstr "System-Rollen können nicht gelöscht werden." #~ #, elixir-autogen, elixir-format #~ msgid "Quarterly Interval - Joining Period Excluded" #~ msgstr "Vierteljährliches Intervall – Beitrittszeitraum nicht einbezogen" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Show Last/Current Cycle Payment Status" +#~ msgstr "" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Show current cycle" +#~ msgstr "Aktuellen Zyklus anzeigen" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Show last completed cycle" +#~ msgstr "Letzten abgeschlossenen Zyklus anzeigen" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Switch to current cycle" +#~ msgstr "Zum aktuellen Zyklus wechseln" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Switch to last completed cycle" +#~ msgstr "Zum letzten abgeschlossenen Zyklus wechseln" + +#~ #: lib/mv_web/live/member_live/form.ex +#~ #: lib/mv_web/live/member_live/show.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "This data is for demonstration purposes only (mockup)." +#~ msgstr "Diese Daten dienen nur zu Demonstrationszwecken (Mockup)." + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Unpaid in current cycle" +#~ msgstr "Unbezahlt im aktuellen Zyklus" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "Unpaid in last cycle" +#~ msgstr "Unbezahlt im letzten Zyklus" + +#~ #: lib/mv_web/live/contribution_settings_live.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "View Example Member" +#~ msgstr "Beispielmitglied anzeigen" + +#~ #: lib/mv_web/live/contribution_settings_live.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Yearly Interval - Joining Period Included" +#~ msgstr "Jährliches Intervall – Beitrittszeitraum einbezogen" + +#~ #: lib/mv_web/live/member_live/form.ex +#~ #: lib/mv_web/live/member_live/show.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "monthly" +#~ msgstr "monatlich" + +#~ #: lib/mv_web/live/member_live/index.html.heex +#~ #, elixir-autogen, elixir-format +#~ msgid "yearly" +#~ msgstr "jährlich" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 03dad7f..ba56b9d 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -50,6 +50,7 @@ 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 #: lib/mv_web/live/role_live/form.ex #: lib/mv_web/live/role_live/index.html.heex @@ -128,6 +129,7 @@ msgid "close" msgstr "" #: 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 #: lib/mv_web/translations/member_fields.ex #, elixir-autogen, elixir-format @@ -172,6 +174,7 @@ 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 #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/role_live/form.ex @@ -188,6 +191,7 @@ msgid "Street" msgstr "" #: 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/formatter.ex #: lib/mv_web/live/member_live/show.ex #: lib/mv_web/live/role_live/show.ex @@ -201,6 +205,7 @@ msgid "Show Member" msgstr "" #: 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/formatter.ex #: lib/mv_web/live/member_live/show.ex #: lib/mv_web/live/member_live/show/membership_fees_component.ex @@ -261,6 +266,7 @@ 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 #: lib/mv_web/live/membership_fee_type_live/form.ex @@ -277,6 +283,8 @@ 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 +#: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/role_live/form.ex #: lib/mv_web/live/role_live/index.html.heex @@ -323,6 +331,8 @@ 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 +#: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/membership_fee_type_live/index.ex #: lib/mv_web/live/role_live/form.ex @@ -359,6 +369,9 @@ msgid "Profil" 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 +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Required" msgstr "" @@ -416,6 +429,7 @@ 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 msgid "Value type" msgstr "" @@ -613,11 +627,6 @@ msgstr "" msgid "Custom field" msgstr "" -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Custom field %{action} successfully" -msgstr "" - #: lib/mv_web/live/custom_field_value_live/form.ex #, elixir-autogen, elixir-format msgid "Custom field value %{action} successfully" @@ -628,7 +637,6 @@ msgstr "" msgid "Please select a custom field first" msgstr "" -#: lib/mv_web/live/custom_field_live/index_component.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format @@ -652,11 +660,6 @@ msgstr[1] "" msgid "All custom field values will be permanently deleted when you delete this custom field." msgstr "" -#: lib/mv_web/live/custom_field_live/index_component.ex -#, elixir-autogen, elixir-format -msgid "Delete Custom Field" -msgstr "" - #: lib/mv_web/live/custom_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Delete Custom Field and All Values" @@ -674,6 +677,8 @@ 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 +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Show in overview" msgstr "" @@ -889,6 +894,7 @@ 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 "" @@ -924,11 +930,6 @@ msgstr "" msgid "Contribution type" 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 interval (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation." -msgstr "" - #: lib/mv_web/components/layouts/navbar.ex #, elixir-autogen, elixir-format msgid "Contributions" @@ -1227,11 +1228,6 @@ msgstr "" msgid "Yearly" msgstr "" -#: lib/mv_web/live/components/field_visibility_dropdown_component.ex -#, elixir-autogen, elixir-format -msgid "Columns" -msgstr "" - #: lib/mv_web/live/components/field_visibility_dropdown_component.ex #, elixir-autogen, elixir-format msgid "Custom Field %{id}" @@ -1263,32 +1259,6 @@ msgstr "" msgid "Select none" msgstr "" -#: lib/mv_web/live/custom_field_live/form_component.ex -#, elixir-autogen, elixir-format -msgid "Back to custom field overview" -msgstr "" - -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Custom field deleted successfully" -msgstr "" - -#: lib/mv_web/live/custom_field_live/form_component.ex -#, elixir-autogen, elixir-format -msgid "Edit Custom Field" -msgstr "" - -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Failed to delete custom field: %{error}" -msgstr "" - -#: lib/mv_web/live/custom_field_live/form_component.ex -#: lib/mv_web/live/custom_field_live/index_component.ex -#, elixir-autogen, elixir-format -msgid "New Custom Field" -msgstr "" - #: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format msgid "Slug does not match. Deletion cancelled." @@ -1300,6 +1270,7 @@ msgid "These will appear in addition to other data when adding new members." msgstr "" #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Value Type" msgstr "" @@ -1330,100 +1301,30 @@ msgstr "" msgid "Yes/No-Selection" msgstr "" -#: lib/mv_web/live/member_live/index.html.heex +#: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format -msgid "Copy email addresses" +msgid "Memberdata" 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/index_component.ex #, elixir-autogen, elixir-format -msgid "Save Custom Field" +msgid "Optional" msgstr "" -#: lib/mv_web/live/custom_field_value_live/form.ex +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format -msgid "Save Custom Field Value" +msgid "These fields are neccessary for MILA to handle member identification and payment calculations in the future. Thus you cannot delete these fields but hide them in the member overview." msgstr "" -#: lib/mv_web/components/core_components.ex +#: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format -msgid "This field is required" +msgid "Member field %{action} successfully" msgstr "" -#: lib/mv_web/live/membership_fee_settings_live.ex +#: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format -msgid "Configure global settings for membership fees." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Default Membership Fee Type" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Generated cycles" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Include joining cycle" -msgstr "" - -#: lib/mv_web/components/layouts/navbar.ex -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Membership Fee Settings" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Membership fee start" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Monthly Interval - Joining Cycle Included" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "None (no default)" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Quarterly Interval - Joining Cycle Excluded" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Settings saved successfully." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "This membership fee type is automatically assigned to all new members. Can be changed individually per member." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "When active: Members pay from the cycle of their joining." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "When inactive: Members pay from the next full cycle after joining." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Yearly Interval - Joining Cycle Excluded" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Yearly Interval - Joining Cycle Included" +msgid "A cycle for this period already exists" msgstr "" #: lib/mv_web/live/membership_fee_type_live/index.ex @@ -1431,6 +1332,11 @@ msgstr "" msgid "About Membership Fee Types" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "All cycles deleted" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Already paid cycles will remain with the old amount." @@ -1463,16 +1369,56 @@ msgstr "" msgid "Changing the amount will affect %{count} member(s)." msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Click to edit amount" +msgstr "" + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Configure global settings for membership fees." +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Confirm Change" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Confirmation text does not match" +msgstr "" + +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Copy email addresses" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Create" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Create Cycle" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Create a new cycle manually" +msgstr "" + #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Current Cycle" msgstr "" +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Current Cycle Payment Status" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Current amount" @@ -1488,6 +1434,11 @@ msgstr "" msgid "Cycle amount updated" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Cycle created successfully" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Cycle deleted" @@ -1503,6 +1454,21 @@ msgstr "" msgid "Cycles regenerated successfully" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Default Membership Fee Type" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Delete All" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Delete All Cycles" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Delete Cycle" @@ -1513,11 +1479,21 @@ msgstr "" msgid "Edit Cycle Amount" msgstr "" +#: lib/mv_web/live/member_field_live/form_component.ex +#, elixir-autogen, elixir-format +msgid "Edit Field: %{field}" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Edit Membership Fee Type" msgstr "" +#: lib/mv_web/live/membership_fee_type_live/index.ex +#, elixir-autogen, elixir-format +msgid "Edit membership fee type" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Failed to update cycle status: %{errors}" @@ -1533,6 +1509,16 @@ msgstr "" msgid "Generate cycles from the last existing cycle to today" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Generated cycles" +msgstr "" + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Include joining cycle" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Interval cannot be changed after creation." @@ -1543,11 +1529,21 @@ msgstr "" msgid "Invalid amount format" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Invalid date format" +msgstr "" + #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Last Cycle" msgstr "" +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Last Cycle Payment Status" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format msgid "Manage membership fee types for membership fees." @@ -1574,6 +1570,12 @@ msgstr "" msgid "Membership Fee" msgstr "" +#: lib/mv_web/components/layouts/navbar.ex +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Membership Fee Settings" +msgstr "" + #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format msgid "Membership Fee Status" @@ -1597,6 +1599,11 @@ msgstr "" msgid "Membership Fees" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Membership fee start" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format msgid "Membership fee type deleted" @@ -1622,6 +1629,11 @@ msgstr "" msgid "Membership fee types define different membership fee structures. Each type has a fixed interval (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation." msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Monthly Interval - Joining Cycle Included" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format @@ -1643,6 +1655,11 @@ msgstr "" msgid "No cycles" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "No cycles to delete" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "No membership fee cycles found. Cycles will be generated automatically when a membership fee type is assigned." @@ -1659,11 +1676,31 @@ msgstr "" msgid "No status" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "None (no default)" +msgstr "" + +#: lib/mv_web/live/member_live/show.ex +#, elixir-autogen, elixir-format +msgid "Not set" +msgstr "" + +#: lib/mv_web/live/member_live/show.ex +#, elixir-autogen, elixir-format +msgid "Payment Interval" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Please confirm the amount change first" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Quarterly Interval - Joining Cycle Excluded" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Regenerate Cycles" @@ -1674,6 +1711,16 @@ 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" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Save Membership Fee Type" @@ -1689,111 +1736,76 @@ msgstr "" msgid "Select interval" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Settings saved successfully." +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "This action cannot be undone." +msgstr "" + +#: lib/mv_web/components/core_components.ex +#, elixir-autogen, elixir-format +msgid "This field is required" +msgstr "" + +#: lib/mv_web/live/member_field_live/form_component.ex +#, elixir-autogen, elixir-format +msgid "This is a technical field and cannot be changed" +msgstr "" + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "This membership fee type is automatically assigned to all new members. Can be changed individually per member." +msgstr "" + #: lib/mv_web/live/member_live/show.ex #: lib/mv_web/live/role_live/index.html.heex #, elixir-autogen, elixir-format msgid "Type" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Type '%{confirmation}' to confirm" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Use this form to manage membership fee types in your database." msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Warning" +msgstr "" + #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Warning: Changing from %{old_interval} to %{new_interval} is not allowed. Please select a membership fee type with the same interval." msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format -msgid "A cycle for this period already exists" +msgid "When active: Members pay from the cycle of their joining." msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format -msgid "All cycles deleted" +msgid "When inactive: Members pay from the next full cycle after joining." msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format -msgid "Click to edit amount" +msgid "Yearly Interval - Joining Cycle Excluded" msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format -msgid "Create" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Create Cycle" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Create a new cycle manually" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Cycle Period" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Cycle created successfully" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Delete All" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Delete All Cycles" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Delete all cycles" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Delete cycle" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Invalid date format" -msgstr "" - -#: lib/mv_web/live/member_live/show.ex -#, elixir-autogen, elixir-format -msgid "Payment Interval" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "The cycle period will be calculated based on this date and the interval." -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "This action cannot be undone." -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Type '%{confirmation}' to confirm" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Warning" +msgid "Yearly Interval - Joining Cycle Included" msgstr "" #: lib/mv_web/live/member_live/show/membership_fees_component.ex @@ -1801,39 +1813,70 @@ msgstr "" msgid "You are about to delete all %{count} cycles for this member." msgstr "" -#: lib/mv_web/live/member_live/index.html.heex +#: lib/mv_web/live/contribution_type_live/index.ex #, elixir-autogen, elixir-format -msgid "Current Cycle Payment Status" -msgstr "" - -#: lib/mv_web/live/member_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "Last Cycle Payment Status" +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" +msgid "Delete Membership Fee Type" msgstr "" -#: lib/mv_web/live/membership_fee_type_live/index.ex +#: lib/mv_web/translations/member_fields.ex #, elixir-autogen, elixir-format -msgid "Edit membership fee type" +msgid "Membership Fee Start Date" +msgstr "" + +#: lib/mv_web/live/components/field_visibility_dropdown_component.ex +#, elixir-autogen, elixir-format +msgid "Show/Hide Columns" msgstr "" #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format -msgid "Confirmation text does not match" +msgid "The cycle will be calculated based on this date and the interval." msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/custom_field_live/form_component.ex #, elixir-autogen, elixir-format -msgid "No cycles to delete" +msgid "Back to settings" msgstr "" -#: lib/mv_web/live/member_live/show.ex +#: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format -msgid "Not set" +msgid "Data field %{action} successfully" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Data field deleted successfully" +msgstr "" + +#: lib/mv_web/live/custom_field_live/index_component.ex +#, elixir-autogen, elixir-format +msgid "Delete Data Field" +msgstr "" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#, elixir-autogen, elixir-format +msgid "Edit Data Field" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Failed to delete data field: %{error}" +msgstr "" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#: lib/mv_web/live/custom_field_live/index_component.ex +#, elixir-autogen, elixir-format +msgid "New Data Field" +msgstr "" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#, elixir-autogen, elixir-format +msgid "Save Data Field" msgstr "" #: lib/mv_web/live/role_live/show.ex diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index 8fd50a6..aa48030 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -50,6 +50,7 @@ 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 #: lib/mv_web/live/role_live/form.ex #: lib/mv_web/live/role_live/index.html.heex @@ -128,6 +129,7 @@ msgid "close" msgstr "" #: 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 #: lib/mv_web/translations/member_fields.ex #, elixir-autogen, elixir-format @@ -172,6 +174,7 @@ 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 #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/role_live/form.ex @@ -188,6 +191,7 @@ msgid "Street" msgstr "" #: 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/formatter.ex #: lib/mv_web/live/member_live/show.ex #: lib/mv_web/live/role_live/show.ex @@ -201,6 +205,7 @@ msgid "Show Member" msgstr "" #: 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/formatter.ex #: lib/mv_web/live/member_live/show.ex #: lib/mv_web/live/member_live/show/membership_fees_component.ex @@ -261,6 +266,7 @@ 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 #: lib/mv_web/live/membership_fee_type_live/form.ex @@ -277,6 +283,8 @@ 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 +#: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/role_live/form.ex #: lib/mv_web/live/role_live/index.html.heex @@ -323,6 +331,8 @@ 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 +#: lib/mv_web/live/member_field_live/index_component.ex #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/membership_fee_type_live/index.ex #: lib/mv_web/live/role_live/form.ex @@ -359,6 +369,9 @@ msgid "Profil" 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 +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Required" msgstr "" @@ -416,6 +429,7 @@ 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 msgid "Value type" msgstr "" @@ -613,11 +627,6 @@ msgstr "" msgid "Custom field" msgstr "" -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Custom field %{action} successfully" -msgstr "" - #: lib/mv_web/live/custom_field_value_live/form.ex #, elixir-autogen, elixir-format msgid "Custom field value %{action} successfully" @@ -628,7 +637,6 @@ msgstr "" msgid "Please select a custom field first" msgstr "" -#: lib/mv_web/live/custom_field_live/index_component.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format, fuzzy @@ -652,11 +660,6 @@ msgstr[1] "" msgid "All custom field values will be permanently deleted when you delete this custom field." msgstr "" -#: lib/mv_web/live/custom_field_live/index_component.ex -#, elixir-autogen, elixir-format -msgid "Delete Custom Field" -msgstr "" - #: lib/mv_web/live/custom_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Delete Custom Field and All Values" @@ -674,6 +677,8 @@ 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 +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format msgid "Show in overview" msgstr "" @@ -889,6 +894,7 @@ 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 "" @@ -924,11 +930,6 @@ msgstr "" msgid "Contribution type" 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 interval (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation." -msgstr "" - #: lib/mv_web/components/layouts/navbar.ex #, elixir-autogen, elixir-format msgid "Contributions" @@ -1227,11 +1228,6 @@ msgstr "" msgid "Yearly" msgstr "" -#: lib/mv_web/live/components/field_visibility_dropdown_component.ex -#, elixir-autogen, elixir-format -msgid "Columns" -msgstr "" - #: lib/mv_web/live/components/field_visibility_dropdown_component.ex #, elixir-autogen, elixir-format, fuzzy msgid "Custom Field %{id}" @@ -1263,32 +1259,6 @@ msgstr "" msgid "Select none" msgstr "" -#: lib/mv_web/live/custom_field_live/form_component.ex -#, elixir-autogen, elixir-format -msgid "Back to custom field overview" -msgstr "" - -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Custom field deleted successfully" -msgstr "" - -#: lib/mv_web/live/custom_field_live/form_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Edit Custom Field" -msgstr "" - -#: lib/mv_web/live/global_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Failed to delete custom field: %{error}" -msgstr "" - -#: lib/mv_web/live/custom_field_live/form_component.ex -#: lib/mv_web/live/custom_field_live/index_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "New Custom Field" -msgstr "" - #: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format msgid "Slug does not match. Deletion cancelled." @@ -1300,6 +1270,7 @@ msgid "These will appear in addition to other data when adding new members." msgstr "" #: lib/mv_web/live/custom_field_live/index_component.ex +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format, fuzzy msgid "Value Type" msgstr "" @@ -1330,100 +1301,30 @@ msgstr "" msgid "Yes/No-Selection" msgstr "" -#: lib/mv_web/live/member_live/index.html.heex +#: lib/mv_web/live/global_settings_live.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Copy email addresses" +msgid "Memberdata" 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/index_component.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Save Custom Field" +msgid "Optional" msgstr "" -#: lib/mv_web/live/custom_field_value_live/form.ex +#: lib/mv_web/live/member_field_live/index_component.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Save Custom Field Value" +msgid "These fields are neccessary for MILA to handle member identification and payment calculations in the future. Thus you cannot delete these fields but hide them in the member overview." msgstr "" -#: lib/mv_web/components/core_components.ex +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Member field %{action} successfully" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format -msgid "This field is required" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Configure global settings for membership fees." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Default Membership Fee Type" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Generated cycles" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Include joining cycle" -msgstr "" - -#: lib/mv_web/components/layouts/navbar.ex -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Membership Fee Settings" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "Membership fee start" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Monthly Interval - Joining Cycle Included" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "None (no default)" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Quarterly Interval - Joining Cycle Excluded" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Settings saved successfully." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format -msgid "This membership fee type is automatically assigned to all new members. Can be changed individually per member." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "When active: Members pay from the cycle of their joining." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "When inactive: Members pay from the next full cycle after joining." -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Yearly Interval - Joining Cycle Excluded" -msgstr "" - -#: lib/mv_web/live/membership_fee_settings_live.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Yearly Interval - Joining Cycle Included" +msgid "A cycle for this period already exists" msgstr "" #: lib/mv_web/live/membership_fee_type_live/index.ex @@ -1431,6 +1332,11 @@ msgstr "" msgid "About Membership Fee Types" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "All cycles deleted" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Already paid cycles will remain with the old amount." @@ -1463,16 +1369,56 @@ msgstr "" msgid "Changing the amount will affect %{count} member(s)." msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Click to edit amount" +msgstr "" + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Configure global settings for membership fees." +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Confirm Change" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Confirmation text does not match" +msgstr "" + +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format, fuzzy +msgid "Copy email addresses" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Create" +msgstr "created" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Create Cycle" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Create a new cycle manually" +msgstr "" + #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format, fuzzy msgid "Current Cycle" msgstr "" +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Current Cycle Payment Status" +msgstr "Current Cycle Payment Status" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format, fuzzy msgid "Current amount" @@ -1488,6 +1434,11 @@ msgstr "" msgid "Cycle amount updated" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Cycle created successfully" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Cycle deleted" @@ -1503,6 +1454,21 @@ msgstr "" msgid "Cycles regenerated successfully" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Default Membership Fee Type" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Delete All" +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Delete All Cycles" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format, fuzzy msgid "Delete Cycle" @@ -1513,11 +1479,21 @@ msgstr "" msgid "Edit Cycle Amount" msgstr "" +#: lib/mv_web/live/member_field_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Edit Field: %{field}" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format, fuzzy msgid "Edit Membership Fee Type" msgstr "" +#: lib/mv_web/live/membership_fee_type_live/index.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Edit membership fee type" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Failed to update cycle status: %{errors}" @@ -1533,6 +1509,16 @@ msgstr "" msgid "Generate cycles from the last existing cycle to today" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Generated cycles" +msgstr "" + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Include joining cycle" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Interval cannot be changed after creation." @@ -1543,11 +1529,21 @@ msgstr "" msgid "Invalid amount format" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Invalid date format" +msgstr "" + #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format msgid "Last Cycle" msgstr "" +#: lib/mv_web/live/member_live/index.html.heex +#, elixir-autogen, elixir-format +msgid "Last Cycle Payment Status" +msgstr "Last Cycle Payment Status" + #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format msgid "Manage membership fee types for membership fees." @@ -1574,6 +1570,12 @@ msgstr "" msgid "Membership Fee" msgstr "" +#: lib/mv_web/components/layouts/navbar.ex +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Membership Fee Settings" +msgstr "" + #: lib/mv_web/live/member_live/index.html.heex #, elixir-autogen, elixir-format, fuzzy msgid "Membership Fee Status" @@ -1597,6 +1599,11 @@ msgstr "" msgid "Membership Fees" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Membership fee start" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format, fuzzy msgid "Membership fee type deleted" @@ -1622,6 +1629,11 @@ msgstr "" msgid "Membership fee types define different membership fee structures. Each type has a fixed interval (monthly, quarterly, half-yearly, yearly) that cannot be changed after creation." msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "Monthly Interval - Joining Cycle Included" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #: lib/mv_web/live/membership_fee_type_live/index.ex #, elixir-autogen, elixir-format, fuzzy @@ -1643,6 +1655,11 @@ msgstr "" msgid "No cycles" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "No cycles to delete" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "No membership fee cycles found. Cycles will be generated automatically when a membership fee type is assigned." @@ -1659,11 +1676,31 @@ msgstr "" msgid "No status" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "None (no default)" +msgstr "" + +#: lib/mv_web/live/member_live/show.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Not set" +msgstr "" + +#: lib/mv_web/live/member_live/show.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Payment Interval" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format msgid "Please confirm the amount change first" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Quarterly Interval - Joining Cycle Excluded" +msgstr "" + #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Regenerate Cycles" @@ -1674,6 +1711,16 @@ 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" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format, fuzzy msgid "Save Membership Fee Type" @@ -1689,111 +1736,76 @@ msgstr "" msgid "Select interval" msgstr "" +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Settings saved successfully." +msgstr "" + +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "This action cannot be undone." +msgstr "" + +#: lib/mv_web/components/core_components.ex +#, elixir-autogen, elixir-format +msgid "This field is required" +msgstr "" + +#: lib/mv_web/live/member_field_live/form_component.ex +#, elixir-autogen, elixir-format +msgid "This is a technical field and cannot be changed" +msgstr "" + +#: lib/mv_web/live/membership_fee_settings_live.ex +#, elixir-autogen, elixir-format +msgid "This membership fee type is automatically assigned to all new members. Can be changed individually per member." +msgstr "" + #: lib/mv_web/live/member_live/show.ex #: lib/mv_web/live/role_live/index.html.heex #, elixir-autogen, elixir-format msgid "Type" msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Type '%{confirmation}' to confirm" +msgstr "" + #: lib/mv_web/live/membership_fee_type_live/form.ex #, elixir-autogen, elixir-format, fuzzy msgid "Use this form to manage membership fee types in your database." msgstr "" +#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#, elixir-autogen, elixir-format +msgid "Warning" +msgstr "" + #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format msgid "Warning: Changing from %{old_interval} to %{new_interval} is not allowed. Please select a membership fee type with the same interval." msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format -msgid "A cycle for this period already exists" +msgid "When active: Members pay from the cycle of their joining." msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format -msgid "All cycles deleted" +msgid "When inactive: Members pay from the next full cycle after joining." msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format -msgid "Click to edit amount" +msgid "Yearly Interval - Joining Cycle Excluded" msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Create" -msgstr "created" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Create Cycle" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/live/membership_fee_settings_live.ex #, elixir-autogen, elixir-format -msgid "Create a new cycle manually" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Cycle Period" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Cycle created successfully" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Delete All" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Delete All Cycles" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Delete all cycles" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Delete cycle" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Invalid date format" -msgstr "" - -#: lib/mv_web/live/member_live/show.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Payment Interval" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "The cycle period will be calculated based on this date and the interval." -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "This action cannot be undone." -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Type '%{confirmation}' to confirm" -msgstr "" - -#: lib/mv_web/live/member_live/show/membership_fees_component.ex -#, elixir-autogen, elixir-format -msgid "Warning" +msgid "Yearly Interval - Joining Cycle Included" msgstr "" #: lib/mv_web/live/member_live/show/membership_fees_component.ex @@ -1801,39 +1813,70 @@ msgstr "" msgid "You are about to delete all %{count} cycles for this member." msgstr "" -#: lib/mv_web/live/member_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "Current Cycle Payment Status" -msgstr "Current Cycle Payment Status" - -#: lib/mv_web/live/member_live/index.html.heex -#, elixir-autogen, elixir-format -msgid "Last Cycle Payment Status" -msgstr "Last Cycle Payment Status" - -#: lib/mv_web/live/membership_fee_type_live/index.ex -#, elixir-autogen, elixir-format -msgid "Delete membership fee type" +#: 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 "Edit membership fee type" +msgid "Delete Membership Fee Type" msgstr "" -#: lib/mv_web/live/member_live/show/membership_fees_component.ex +#: lib/mv_web/translations/member_fields.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Membership Fee Start Date" +msgstr "" + +#: lib/mv_web/live/components/field_visibility_dropdown_component.ex #, elixir-autogen, elixir-format -msgid "Confirmation text does not match" +msgid "Show/Hide Columns" msgstr "" #: lib/mv_web/live/member_live/show/membership_fees_component.ex #, elixir-autogen, elixir-format, fuzzy -msgid "No cycles to delete" +msgid "The cycle will be calculated based on this date and the interval." msgstr "" -#: lib/mv_web/live/member_live/show.ex +#: lib/mv_web/live/custom_field_live/form_component.ex #, elixir-autogen, elixir-format, fuzzy -msgid "Not set" +msgid "Back to settings" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Data field %{action} successfully" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Data field deleted successfully" +msgstr "" + +#: lib/mv_web/live/custom_field_live/index_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Delete Data Field" +msgstr "" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Edit Data Field" +msgstr "" + +#: lib/mv_web/live/global_settings_live.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Failed to delete data field: %{error}" +msgstr "" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#: lib/mv_web/live/custom_field_live/index_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "New Data Field" +msgstr "" + +#: lib/mv_web/live/custom_field_live/form_component.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Save Data Field" msgstr "" #: lib/mv_web/live/role_live/show.ex @@ -2005,7 +2048,12 @@ msgstr "" #~ #: lib/mv_web/live/custom_field_live/show.ex #~ #, elixir-autogen, elixir-format -#~ msgid "Auto-generated identifier (immutable)" +#~ msgstr "" + +#~ #: lib/mv_web/live/member_field_live/form_component.ex +#~ #: lib/mv_web/live/member_field_live/index_component.ex +#~ #, elixir-autogen, elixir-format, fuzzy +#~ msgid "String" #~ msgstr "" #~ #: lib/mv_web/live/contribution_settings_live.ex @@ -2013,6 +2061,11 @@ msgstr "" #~ msgid "Configure global settings for membership contributions." #~ msgstr "" +#~ #: lib/mv_web/live/global_settings_live.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Failed to update member field visibility: %{error}" +#~ msgstr "" + #~ #: lib/mv_web/live/member_live/form.ex #~ #: lib/mv_web/live/member_live/show.ex #~ #, elixir-autogen, elixir-format @@ -2025,22 +2078,6 @@ msgstr "" #~ msgid "Contribution Settings" #~ msgstr "" -#~ #: lib/mv_web/live/contribution_settings_live.ex -#~ #, elixir-autogen, elixir-format -#~ msgid "Contribution start" -#~ msgstr "" - -#~ #: lib/mv_web/live/member_live/index.html.heex -#~ #, elixir-autogen, elixir-format -#~ msgid "Copy emails" -#~ msgstr "" - -#~ #: lib/mv_web/live/contribution_settings_live.ex -#~ #, elixir-autogen, elixir-format -#~ msgid "Default Contribution Type" -#~ msgstr "" - -#~ #: lib/mv_web/live/contribution_settings_live.ex #~ #, elixir-autogen, elixir-format #~ msgid "Example: Member Contribution View" #~ msgstr "" @@ -2061,12 +2098,6 @@ msgstr "" #~ msgid "Immutable" #~ msgstr "" -#~ #: lib/mv_web/live/custom_field_live/index_component.ex -#~ #, elixir-autogen, elixir-format, fuzzy -#~ msgid "New Custom field" -#~ msgstr "" - -#~ #: lib/mv_web/live/components/payment_filter_component.ex #~ #, elixir-autogen, elixir-format #~ msgid "Not paid" #~ msgstr "" @@ -2081,6 +2112,11 @@ msgstr "" #~ msgid "Pending" #~ msgstr "" +#~ #: lib/mv_web/live/custom_field_live/form_component.ex +#~ #, elixir-autogen, elixir-format, fuzzy +#~ msgid "Save Custom Field" +#~ msgstr "" + #~ #: lib/mv_web/live/member_live/form.ex #~ #: lib/mv_web/live/member_live/show.ex #~ #: lib/mv_web/translations/member_fields.ex diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 6b23cce..3fb2bad 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -525,10 +525,39 @@ default_club_name = System.get_env("ASSOCIATION_NAME") || "Club Name" case Membership.get_settings() do {:ok, existing_settings} -> # Settings exist, update if club_name is different from env var - if existing_settings.club_name != default_club_name do - {:ok, _updated} = - Membership.update_settings(existing_settings, %{club_name: default_club_name}) + # Also ensure exit_date is set to false by default if not already configured + updates = + %{} + |> then(fn acc -> + if existing_settings.club_name != default_club_name, + do: Map.put(acc, :club_name, default_club_name), + else: acc + end) + |> then(fn acc -> + visibility_config = existing_settings.member_field_visibility || %{} + # Ensure exit_date is set to false if not already configured + if not Map.has_key?(visibility_config, "exit_date") and + not Map.has_key?(visibility_config, :exit_date) do + updated_visibility = Map.put(visibility_config, "exit_date", false) + Map.put(acc, :member_field_visibility, updated_visibility) + else + acc + end + end) + + if map_size(updates) > 0 do + {:ok, _updated} = Membership.update_settings(existing_settings, updates) end + + {:ok, nil} -> + # Settings don't exist yet, create with exit_date defaulting to false + {:ok, _settings} = + Membership.Setting + |> Ash.Changeset.for_create(:create, %{ + club_name: default_club_name, + member_field_visibility: %{"exit_date" => false} + }) + |> Ash.create!() end IO.puts("✅ Seeds completed successfully!") diff --git a/test/membership/member_field_visibility_test.exs b/test/membership/member_field_visibility_test.exs index 9c7e5e0..47ab5bd 100644 --- a/test/membership/member_field_visibility_test.exs +++ b/test/membership/member_field_visibility_test.exs @@ -13,14 +13,17 @@ defmodule Mv.Membership.MemberFieldVisibilityTest do alias Mv.Membership.Member describe "show_in_overview?/1" do - test "returns true for all member fields by default" do + test "returns true for all member fields by default, except exit_date" do # When no settings exist or member_field_visibility is not configured # Test with fields from constants + # Note: exit_date defaults to false (hidden) by design member_fields = Mv.Constants.member_fields() Enum.each(member_fields, fn field -> - assert Member.show_in_overview?(field) == true, - "Field #{field} should be visible by default" + expected_visibility = if field == :exit_date, do: false, else: true + + assert Member.show_in_overview?(field) == expected_visibility, + "Field #{field} should be #{if expected_visibility, do: "visible", else: "hidden"} by default" end) end @@ -77,4 +80,72 @@ defmodule Mv.Membership.MemberFieldVisibilityTest do end) end end + + describe "update_single_member_field_visibility/3" do + test "atomically updates a single field in member_field_visibility" do + {:ok, settings} = Mv.Membership.get_settings() + field_string = "street" + + # Update single field + {:ok, updated_settings} = + Mv.Membership.update_single_member_field_visibility( + settings, + field: field_string, + show_in_overview: false + ) + + # Verify the field was updated + assert updated_settings.member_field_visibility[field_string] == false + + # Verify other fields are not affected + other_fields = + Mv.Constants.member_fields() + |> Enum.reject(&(&1 == String.to_existing_atom(field_string))) + + Enum.each(other_fields, fn field -> + field_string = Atom.to_string(field) + # Fields not explicitly set should default to true (except exit_date) + expected = if field == :exit_date, do: false, else: true + + assert Map.get(updated_settings.member_field_visibility, field_string, expected) == + expected + end) + end + + test "returns error for invalid field name" do + {:ok, settings} = Mv.Membership.get_settings() + + assert {:error, %Ash.Error.Invalid{errors: [%{field: :member_field_visibility}]}} = + Mv.Membership.update_single_member_field_visibility( + settings, + field: "invalid_field", + show_in_overview: false + ) + end + + test "handles concurrent updates atomically" do + {:ok, settings} = Mv.Membership.get_settings() + field1 = "street" + field2 = "house_number" + + # Simulate concurrent updates by updating different fields + {:ok, updated1} = + Mv.Membership.update_single_member_field_visibility( + settings, + field: field1, + show_in_overview: false + ) + + {:ok, updated2} = + Mv.Membership.update_single_member_field_visibility( + updated1, + field: field2, + show_in_overview: true + ) + + # Both fields should be correctly updated + assert updated2.member_field_visibility[field1] == false + assert updated2.member_field_visibility[field2] == true + end + end end diff --git a/test/mv_web/live/custom_field_live/deletion_test.exs b/test/mv_web/live/custom_field_live/deletion_test.exs index 322cf38..a35c06c 100644 --- a/test/mv_web/live/custom_field_live/deletion_test.exs +++ b/test/mv_web/live/custom_field_live/deletion_test.exs @@ -154,7 +154,7 @@ defmodule MvWeb.CustomFieldLive.DeletionTest do |> render_click() # Should show success message - assert render(view) =~ "Custom field deleted successfully" + assert render(view) =~ "Data field deleted successfully" # Custom field should be gone from database assert {:error, _} = Ash.get(CustomField, custom_field.id) diff --git a/test/mv_web/live/global_settings_live_test.exs b/test/mv_web/live/global_settings_live_test.exs index 6a739b5..86680f3 100644 --- a/test/mv_web/live/global_settings_live_test.exs +++ b/test/mv_web/live/global_settings_live_test.exs @@ -64,5 +64,21 @@ defmodule MvWeb.GlobalSettingsLiveTest do assert html =~ "must be present" end + + test "displays Memberdata section", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + assert html =~ "Memberdata" or html =~ "Member Data" + end + + test "displays flash message after member field visibility update", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/settings") + + # Simulate member field visibility update + send(view.pid, {:member_field_visibility_updated}) + + # Check for flash message + assert render(view) =~ "updated" or render(view) =~ "success" + end end end diff --git a/test/mv_web/live/member_field_live/index_component_test.exs b/test/mv_web/live/member_field_live/index_component_test.exs new file mode 100644 index 0000000..037a77c --- /dev/null +++ b/test/mv_web/live/member_field_live/index_component_test.exs @@ -0,0 +1,124 @@ +defmodule MvWeb.MemberFieldLive.IndexComponentTest do + @moduledoc """ + Tests for MemberFieldLive.IndexComponent. + + Tests cover: + - Rendering all member fields from Mv.Constants.member_fields() + - Displaying show_in_overview status as badge (Yes/No) + - Displaying required status for required fields (first_name, last_name, email) + - Current status is displayed based on settings.member_field_visibility + - Default status is "Yes" (visible) when not configured in settings + """ + use MvWeb.ConnCase, async: false + + import Phoenix.LiveViewTest + + alias Mv.Membership + + setup %{conn: conn} do + user = create_test_user(%{email: "admin@example.com"}) + conn = conn_with_oidc_user(conn, user) + {:ok, conn: conn, user: user} + end + + describe "rendering" do + test "renders all member fields from Constants", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + # Check that all member fields are displayed + member_fields = Mv.Constants.member_fields() + + for field <- member_fields do + field_name = String.replace(Atom.to_string(field), "_", " ") |> String.capitalize() + # Field name should appear in the table (either as label or in some form) + assert html =~ field_name or html =~ Atom.to_string(field) + end + end + + test "displays show_in_overview status as badge", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + # Should have "Show in overview" column header + assert html =~ "Show in overview" or html =~ "Show in Overview" + + # Should have badge elements (Yes/No) + assert html =~ "badge" or html =~ "Yes" or html =~ "No" + end + + test "displays required status for required fields", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + # Required fields: first_name, last_name, email + # Should have "Required" column or indicator + assert html =~ "Required" or html =~ "required" + end + + test "shows default status as Yes when not configured", %{conn: conn} do + # Ensure settings have no member_field_visibility configured + {:ok, settings} = Membership.get_settings() + + {:ok, _updated} = + Membership.update_settings(settings, %{member_field_visibility: %{}}) + + {:ok, _view, html} = live(conn, ~p"/settings") + + # All fields should show as visible (Yes) by default + # Check for "Yes" badge or similar indicator + assert html =~ "Yes" or html =~ "badge-success" + end + + test "shows configured visibility status from settings", %{conn: conn} do + # Configure some fields as hidden + {:ok, settings} = Membership.get_settings() + visibility_config = %{"street" => false, "house_number" => false} + + {:ok, _updated} = + Membership.update_member_field_visibility(settings, visibility_config) + + {:ok, _view, html} = live(conn, ~p"/settings") + + # Street and house_number should show as hidden (No) + # Other fields should show as visible (Yes) + assert html =~ "street" or html =~ "Street" + assert html =~ "house_number" or html =~ "House number" + end + end + + describe "required fields" do + test "marks first_name as required", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + # first_name should be marked as required + assert html =~ "first_name" or html =~ "First name" + # Should have required indicator + assert html =~ "required" or html =~ "Required" + end + + test "marks last_name as required", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + # last_name should be marked as required + assert html =~ "last_name" or html =~ "Last name" + # Should have required indicator + assert html =~ "required" or html =~ "Required" + end + + test "marks email as required", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + # email should be marked as required + assert html =~ "email" or html =~ "Email" + # Should have required indicator + assert html =~ "required" or html =~ "Required" + end + + test "does not mark optional fields as required", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + # Optional fields should not have required indicator + # Check that street (optional) doesn't have required badge + # This test verifies that only required fields show the indicator + assert html =~ "street" or html =~ "Street" + end + end +end