Finalize join request feature #472

Merged
simon merged 18 commits from feature/308-web-form into main 2026-03-13 20:51:11 +01:00
37 changed files with 309 additions and 200 deletions
Showing only changes of commit c933144920 - Show all commits

View file

@ -13,6 +13,39 @@ defmodule MvWeb.Layouts do
embed_templates "layouts/*"
@doc """
Builds the full browser tab title: "Mila", "Mila · Page", or "Mila · Page · Club".
Order is always: Mila · page title · club name.
Uses assigns[:club_name] and the short page label from assigns[:content_title] or
assigns[:page_title]. LiveViews should set content_title (same gettext as sidebar)
and then assign page_title to the result of this function so the client receives
the full title.
"""
def page_title_string(assigns) do
club = assigns[:club_name]
page = assigns[:content_title] || assigns[:page_title]
parts =
[page, club]
|> Enum.filter(&(is_binary(&1) and String.trim(&1) != ""))
if parts == [] do
"Mila"
else
"Mila · " <> Enum.join(parts, " · ")
end
end
@doc """
Assigns content_title (short label for heading; same gettext as sidebar) and
page_title (full browser tab title). Call from LiveView mount after club_name
is set (e.g. from on_mount). Returns the socket.
"""
def assign_page_title(socket, content_title) do
socket = assign(socket, :content_title, content_title)
assign(socket, :page_title, page_title_string(socket.assigns))
end
@doc """
Renders the public (unauthenticated) page layout: header with logo + "Mitgliederverwaltung" left,
club name centered, language selector right; plus main content and flash group. Use for sign-in, join, and join-confirm pages so they

View file

@ -7,8 +7,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={get_csrf_token()} />
<link phx-track-static rel="icon" type="image/svg+xml" href={~p"/images/mila.svg"} />
<.live_title default="Mv" suffix=" · Phoenix Framework">
{assigns[:page_title]}
<.live_title default="Mila">
{page_title_string(assigns)}
</.live_title>
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/js/app.js"}>

View file

@ -8,6 +8,8 @@ defmodule MvWeb.JoinConfirmController do
"""
use MvWeb, :controller
use Gettext, backend: MvWeb.Gettext
def confirm(conn, %{"token" => token}) when is_binary(token) do
callback = Application.get_env(:mv, :join_confirm_callback, Mv.Membership)
@ -48,8 +50,15 @@ defmodule MvWeb.JoinConfirmController do
end
defp assign_confirm_assigns(conn, result) do
page_title = page_title_for_result(result)
conn
|> assign(:result, result)
|> assign(:page_title, page_title)
|> assign(:flash, conn.assigns[:flash] || conn.flash || %{})
end
defp page_title_for_result(:success), do: gettext("Join confirmation")
defp page_title_for_result(:expired), do: gettext("Link expired")
defp page_title_for_result(:invalid), do: gettext("Invalid link")
end

View file

@ -7,7 +7,11 @@ defmodule MvWeb.PageController do
"""
use MvWeb, :controller
use Gettext, backend: MvWeb.Gettext
def home(conn, _params) do
render(conn, :home)
conn
|> assign(:page_title, gettext("Home"))
|> render(:home)
end
end

View file

@ -19,6 +19,7 @@ defmodule MvWeb.SignInLive do
alias AshAuthentication.Phoenix.Components
alias Mv.Config
alias Mv.Membership
alias MvWeb.{AuthOverridesDE, AuthOverridesRegistrationDisabled, Layouts}
@impl true
@ -49,6 +50,13 @@ defmodule MvWeb.SignInLive do
register_path =
if session["registration_enabled"] == false, do: nil, else: session["register_path"]
# Club name and page title for browser tab (root layout: Mila · Club · Page)
club_name =
case Membership.get_settings() do
{:ok, settings} when is_binary(settings.club_name) -> settings.club_name
_ -> nil
end
socket =
socket
|> assign(overrides: overrides)
@ -66,6 +74,8 @@ defmodule MvWeb.SignInLive do
|> assign(:oidc_only, Config.oidc_only?())
|> assign(:sign_in_id, "sign-in")
|> assign(:locale, locale)
|> assign(:club_name, club_name)
|> Layouts.assign_page_title(gettext("Sign in"))
{:ok, socket}
end

View file

@ -17,7 +17,7 @@ defmodule MvWeb.DatafieldsLive do
{:ok,
socket
|> assign(:page_title, gettext("Datafields"))
|> Layouts.assign_page_title(gettext("Datafields"))
|> assign(:settings, settings)
|> assign(:active_editing_section, nil)
|> assign(:custom_field_delete_modal_open, false)}
@ -50,7 +50,7 @@ defmodule MvWeb.DatafieldsLive do
~H"""
<Layouts.app flash={@flash} current_user={@current_user} club_name={@settings.club_name}>
<.header>
{gettext("Datafields")}
{@content_title}
<:subtitle>
{gettext(
"Configure which data you want to save for your members. Define individual datafields."

View file

@ -60,7 +60,7 @@ defmodule MvWeb.GlobalSettingsLive do
socket =
socket
|> assign(:page_title, gettext("Settings"))
|> Layouts.assign_page_title(gettext("Basic settings"))
|> assign(:settings, settings)
|> assign(:locale, locale)
|> assign(:environment, environment)
@ -112,7 +112,7 @@ defmodule MvWeb.GlobalSettingsLive do
~H"""
<Layouts.app flash={@flash} current_user={@current_user} club_name={@settings.club_name}>
<.header>
{gettext("Settings")}
{gettext("Basic settings")}
<:subtitle>
{gettext("Manage global settings for the association.")}
</:subtitle>

View file

@ -32,7 +32,7 @@ defmodule MvWeb.GroupLive.Form do
socket
|> assign(:actor, actor)
|> assign(:group, nil)
|> assign(:page_title, page_title_for_params(params))
|> Layouts.assign_page_title(page_title_for_params(params))
|> assign(:return_to, return_to_for_params(params))}
else
{:ok, redirect(socket, to: ~p"/groups")}
@ -56,7 +56,7 @@ defmodule MvWeb.GroupLive.Form do
{:noreply,
socket
|> assign(:group, group)
|> assign(:page_title, gettext("Edit Group"))
|> Layouts.assign_page_title(gettext("Edit Group"))
|> assign(:return_to, :show)
|> assign_form(actor)}
@ -85,7 +85,7 @@ defmodule MvWeb.GroupLive.Form do
{gettext("Back")}
</.button>
</:leading>
{@page_title}
{@content_title}
<:actions>
<.button phx-disable-with={gettext("Saving...")} variant="primary" type="submit">
{gettext("Save")}

View file

@ -28,7 +28,7 @@ defmodule MvWeb.GroupLive.Index do
{:ok,
socket
|> assign(:page_title, gettext("Groups"))
|> Layouts.assign_page_title(gettext("Groups"))
|> assign(:groups, groups)}
else
{:ok, redirect(socket, to: ~p"/members")}
@ -40,7 +40,7 @@ defmodule MvWeb.GroupLive.Index do
~H"""
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Groups")}
{@content_title}
<:actions>
<%= if can?(@current_user, :create, Mv.Membership.Group) do %>
<.button navigate={~p"/groups/new"} variant="primary">

View file

@ -70,9 +70,11 @@ defmodule MvWeb.GroupLive.Show do
{:ok, group} ->
open_delete = params["confirm_delete"] == "1" && can?(actor, :destroy, group)
content_title = gettext("Group %{name}", name: group.name)
socket =
socket
|> assign(:page_title, group.name)
|> Layouts.assign_page_title(content_title)
|> assign(:group, group)
|> assign(:show_delete_modal, open_delete)
|> assign(:name_confirmation, "")
@ -102,7 +104,7 @@ defmodule MvWeb.GroupLive.Show do
{gettext("Back")}
</.button>
</:leading>
{@group.name}
{@content_title}
<:actions>
<%= if can?(@current_user, :update, @group) do %>
<.button

View file

@ -65,7 +65,7 @@ defmodule MvWeb.ImportLive do
socket =
socket
|> assign(:page_title, gettext("Import"))
|> Layouts.assign_page_title(gettext("Import"))
|> assign(:club_name, club_name)
|> assign(:import_state, nil)
|> assign(:import_progress, nil)
@ -94,7 +94,7 @@ defmodule MvWeb.ImportLive do
<%!-- CSV Import Section --%>
<div data-testid="import-page">
<.header>
{gettext("Import Members")}
{@content_title}
<:subtitle>
{gettext("Import members from CSV files.")}
</:subtitle>

View file

@ -36,6 +36,7 @@ defmodule MvWeb.JoinLive do
|> assign(:client_ip, client_ip)
|> assign(:honeypot_field, @honeypot_field)
|> assign(:club_name, club_name)
|> Layouts.assign_page_title(gettext("Join"))
|> assign(:form, to_form(initial_form_params(join_fields)))
{:ok, socket}

View file

@ -43,7 +43,7 @@ defmodule MvWeb.JoinRequestLive.Index do
~H"""
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Join requests")}
{@content_title}
</.header>
<div class="mt-6 space-y-8 max-w-4xl">
@ -159,7 +159,7 @@ defmodule MvWeb.JoinRequestLive.Index do
assign(socket, :join_requests_history, [])
end
assign(socket, :page_title, gettext("Join requests"))
Layouts.assign_page_title(socket, gettext("Join requests"))
end
defp review_date(req, timezone) do

View file

@ -32,7 +32,7 @@ defmodule MvWeb.JoinRequestLive.Show do
socket
|> assign(:join_request, nil)
|> assign(:join_form_field_ids, [])
|> assign(:page_title, gettext("Join request"))}
|> Layouts.assign_page_title(gettext("Join request"))}
else
{:ok, redirect(socket, to: ~p"/members")}
end
@ -57,7 +57,7 @@ defmodule MvWeb.JoinRequestLive.Show do
socket
|> assign(:join_request, request)
|> assign(:join_form_field_ids, field_ids)
|> assign(:page_title, gettext("Join request %{email}", email: request.email))}
|> Layouts.assign_page_title(gettext("Join request %{email}", email: request.email))}
{:error, _error} ->
{:noreply,
@ -123,7 +123,7 @@ defmodule MvWeb.JoinRequestLive.Show do
{gettext("Back")}
</.button>
</:leading>
{gettext("Join request")}
{@content_title}
</.header>
<%= if @join_request do %>

View file

@ -374,7 +374,7 @@ defmodule MvWeb.MemberLive.Form do
id -> Ash.get!(MemberResource, id, load: [:membership_fee_type], actor: actor)
end
page_title =
content_title =
if is_nil(member), do: gettext("Create Member"), else: gettext("Edit Member")
# Load available membership fee types
@ -389,7 +389,7 @@ defmodule MvWeb.MemberLive.Form do
|> assign(:custom_fields, custom_fields)
|> assign(:initial_custom_field_values, initial_custom_field_values)
|> assign(member: member)
|> assign(:page_title, page_title)
|> Layouts.assign_page_title(content_title)
|> assign(:available_fee_types, available_fee_types)
|> assign(:interval_warning, nil)
|> assign(:member_field_required_map, member_field_required_map)

View file

@ -127,7 +127,7 @@ defmodule MvWeb.MemberLive.Index do
socket =
socket
|> assign(:page_title, gettext("Members"))
|> Layouts.assign_page_title(gettext("Members"))
|> assign(:query, "")
|> assign_new(:sort_field, fn -> :first_name end)
|> assign_new(:sort_order, fn -> :asc end)

View file

@ -1,6 +1,6 @@
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Members")}
{@content_title}
<:actions>
<.live_component
module={MvWeb.Components.ExportDropdown}

View file

@ -47,7 +47,7 @@ defmodule MvWeb.MemberLive.Show do
{gettext("Back")}
</.button>
</:leading>
{MemberHelpers.display_name(@member)}
{@content_title}
<:actions>
<%= if can?(@current_user, :update, @member) do %>
<.button
@ -435,9 +435,12 @@ defmodule MvWeb.MemberLive.Show do
|> Map.put(:last_cycle_status, last_cycle_status)
|> Map.put(:current_cycle_status, current_cycle_status)
content_title =
gettext("Member %{name}", name: MemberHelpers.display_name(member))
{:noreply,
socket
|> assign(:page_title, page_title(socket.assigns.live_action))
|> Layouts.assign_page_title(content_title)
|> assign(:member, member)}
end
@ -565,9 +568,6 @@ defmodule MvWeb.MemberLive.Show do
{:noreply, assign(socket, :member, member)}
end
defp page_title(:show), do: gettext("Show Member")
defp page_title(:edit), do: gettext("Edit Member")
defp format_error(%Ash.Error.Invalid{errors: errors}) do
error_messages =
Enum.map(errors, fn

View file

@ -33,7 +33,7 @@ defmodule MvWeb.MembershipFeeSettingsLive do
{:ok,
socket
|> assign(:page_title, gettext("Membership Fee Settings"))
|> Layouts.assign_page_title(gettext("Membership fee settings"))
|> assign(:settings, settings)
|> assign(:membership_fee_types, membership_fee_types)
|> assign(:member_counts, member_counts)
@ -140,7 +140,7 @@ defmodule MvWeb.MembershipFeeSettingsLive do
~H"""
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Membership Fee Settings")}
{@content_title}
<:subtitle>
{gettext("Configure fee types for membership fees.")}
</:subtitle>

View file

@ -33,7 +33,7 @@ defmodule MvWeb.MembershipFeeTypeLive.Form do
{gettext("Back")}
</.button>
</:leading>
{@page_title}
{@content_title}
<:actions>
<.button
form="membership-fee-type-form"
@ -221,7 +221,7 @@ defmodule MvWeb.MembershipFeeTypeLive.Form do
id -> Ash.get!(MembershipFeeType, id, domain: MembershipFees, actor: actor)
end
page_title =
content_title =
if is_nil(membership_fee_type),
do: gettext("New Membership Fee Type"),
else: gettext("Edit Membership Fee Type")
@ -230,7 +230,7 @@ defmodule MvWeb.MembershipFeeTypeLive.Form do
socket
|> assign(:return_to, return_to(params["return_to"]))
|> assign(:membership_fee_type, membership_fee_type)
|> assign(:page_title, page_title)
|> Layouts.assign_page_title(content_title)
|> assign(:show_amount_warning, false)
|> assign(:old_amount, nil)
|> assign(:new_amount, nil)

View file

@ -32,7 +32,7 @@ defmodule MvWeb.MembershipFeeTypeLive.Index do
{:ok,
socket
|> assign(:page_title, gettext("Membership Fee Types"))
|> Layouts.assign_page_title(gettext("Membership fee settings"))
|> assign(:membership_fee_types, fee_types)
|> assign(:member_counts, member_counts)}
end
@ -42,7 +42,7 @@ defmodule MvWeb.MembershipFeeTypeLive.Index do
~H"""
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Membership Fee Types")}
{@content_title}
<:subtitle>
{gettext("Manage membership fee types for membership fees.")}
</:subtitle>

View file

@ -29,7 +29,7 @@ defmodule MvWeb.RoleLive.Form do
{gettext("Back")}
</.button>
</:leading>
{@page_title}
{@content_title}
<:actions>
<.button phx-disable-with={gettext("Saving...")} variant="primary" type="submit">
{gettext("Save")}
@ -94,14 +94,13 @@ defmodule MvWeb.RoleLive.Form do
def mount(params, _session, socket) do
case params["id"] do
nil ->
action = gettext("New")
page_title = action <> " " <> gettext("Role")
content_title = gettext("New") <> " " <> gettext("Role")
{:ok,
socket
|> assign(:return_to, return_to(params["return_to"]))
|> assign(:role, nil)
|> assign(:page_title, page_title)
|> Layouts.assign_page_title(content_title)
|> assign_form()}
id ->
@ -113,14 +112,13 @@ defmodule MvWeb.RoleLive.Form do
actor: socket.assigns[:current_user]
) do
{:ok, role} ->
action = gettext("Edit")
page_title = action <> " " <> gettext("Role")
content_title = gettext("Edit") <> " " <> gettext("Role")
{:ok,
socket
|> assign(:return_to, return_to(params["return_to"]))
|> assign(:role, role)
|> assign(:page_title, page_title)
|> Layouts.assign_page_title(content_title)
|> assign_form()}
{:error, %Ash.Error.Invalid{errors: [%Ash.Error.Query.NotFound{} | _]}} ->

View file

@ -28,7 +28,7 @@ defmodule MvWeb.RoleLive.Index do
{:ok,
socket
|> assign(:page_title, gettext("Listing Roles"))
|> Layouts.assign_page_title(gettext("Roles"))
|> assign(:roles, roles)
|> assign(:user_counts, user_counts)}
end

View file

@ -1,6 +1,6 @@
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Listing Roles")}
{@content_title}
<:subtitle>
{gettext("Manage roles and their permission sets.")}
</:subtitle>

View file

@ -30,9 +30,11 @@ defmodule MvWeb.RoleLive.Show do
{:ok, role} ->
user_count = load_user_count(role, socket.assigns[:current_user])
content_title = gettext("Role %{name}", name: role.name)
{:ok,
socket
|> assign(:page_title, gettext("Show Role"))
|> Layouts.assign_page_title(content_title)
|> assign(:role, role)
|> assign(:user_count, user_count)
|> assign(:show_delete_modal, false)}
@ -202,7 +204,7 @@ defmodule MvWeb.RoleLive.Show do
{gettext("Back")}
</.button>
</:leading>
{gettext("Role")} {@role.name}
{@content_title}
<:subtitle>{gettext("Role details and permissions.")}</:subtitle>
<:actions>

View file

@ -18,7 +18,7 @@ defmodule MvWeb.StatisticsLive do
# Only static assigns and fee types here; load_statistics runs once in handle_params
socket =
socket
|> assign(:page_title, gettext("Statistics"))
|> Layouts.assign_page_title(gettext("Statistics"))
|> assign(:selected_fee_type_id, nil)
|> load_fee_types()
@ -58,7 +58,7 @@ defmodule MvWeb.StatisticsLive do
~H"""
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Statistics")}
{@content_title}
</.header>
<section class="mb-8" aria-labelledby="members-heading">

View file

@ -59,7 +59,7 @@ defmodule MvWeb.UserLive.Form do
{gettext("Back")}
</.button>
</:leading>
{@page_title}
{@content_title}
<:actions>
<.button
form="user-form"
@ -423,8 +423,9 @@ defmodule MvWeb.UserLive.Form do
defp mount_continue(user, params, socket) do
actor = current_actor(socket)
action = if is_nil(user), do: gettext("New"), else: gettext("Edit")
page_title = action <> " " <> gettext("User")
content_title =
if(is_nil(user), do: gettext("New"), else: gettext("Edit")) <> " " <> gettext("User")
# Only admins can link/unlink users to members (permission docs; prevents privilege escalation).
can_manage_member_linking = can?(actor, :destroy, UserResource)
@ -436,7 +437,7 @@ defmodule MvWeb.UserLive.Form do
socket
|> assign(:return_to, return_to(params["return_to"]))
|> assign(user: user)
|> assign(:page_title, page_title)
|> Layouts.assign_page_title(content_title)
|> assign(:can_manage_member_linking, can_manage_member_linking)
|> assign(:can_assign_role, can_assign_role)
|> assign(:roles, roles)

View file

@ -38,7 +38,7 @@ defmodule MvWeb.UserLive.Index do
{:ok,
socket
|> assign(:page_title, gettext("Listing Users"))
|> Layouts.assign_page_title(gettext("Users"))
|> assign(:sort_field, :email)
|> assign(:sort_order, :asc)
|> assign(:users, sorted)}

View file

@ -1,6 +1,6 @@
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Users")}
{@content_title}
<:subtitle>{gettext("Manage users and their permissions.")}</:subtitle>
<:actions>
<%= if can?(@current_user, :create, Mv.Accounts.User) do %>

View file

@ -48,7 +48,7 @@ defmodule MvWeb.UserLive.Show do
{gettext("Back")}
</.button>
</:leading>
{gettext("User")} {@user.email}
{@content_title}
<:actions>
<%= if can?(@current_user, :update, @user) do %>
<.button
@ -179,9 +179,11 @@ defmodule MvWeb.UserLive.Show do
|> put_flash(:error, gettext("This user cannot be viewed."))
|> push_navigate(to: ~p"/users")}
else
content_title = gettext("User %{email}", email: user.email)
{:ok,
socket
|> assign(:page_title, gettext("Show User"))
|> Layouts.assign_page_title(content_title)
|> assign(:user, user)
|> assign(:show_delete_modal, false)}
end

View file

@ -17,6 +17,7 @@ defmodule MvWeb.LiveHelpers do
"""
import Phoenix.Component
alias Mv.Authorization.Actor
alias Mv.Membership
alias MvWeb.Plugs.CheckPagePermission
def on_mount(:default, _params, session, socket) do
@ -27,9 +28,17 @@ defmodule MvWeb.LiveHelpers do
connect_params = socket.private[:connect_params] || %{}
timezone = connect_params["timezone"] || connect_params[:timezone]
# Club name for browser tab title (Mila · Club · Page)
club_name =
case Membership.get_settings() do
{:ok, settings} when is_binary(settings.club_name) -> settings.club_name
_ -> nil
end
socket =
socket
|> assign(:browser_timezone, timezone)
|> assign(:club_name, club_name)
{:cont, socket}
end

View file

@ -0,0 +1,22 @@
defmodule MvWeb.Plugs.AssignClubName do
@moduledoc """
Assigns :club_name from settings for controller-rendered pages.
Used by the root layout to build the browser tab title (Mila · Club · Page).
LiveViews set club_name in on_mount instead.
"""
import Plug.Conn
alias Mv.Membership
def init(opts), do: opts
def call(conn, _opts) do
club_name =
case Membership.get_settings() do
{:ok, settings} when is_binary(settings.club_name) -> settings.club_name
_ -> nil
end
assign(conn, :club_name, club_name)
end
end

View file

@ -14,6 +14,7 @@ defmodule MvWeb.Router do
plug :put_secure_browser_headers
plug :load_from_session
plug :set_locale
plug MvWeb.Plugs.AssignClubName
plug MvWeb.Plugs.CheckPagePermission
plug MvWeb.Plugs.JoinFormEnabled
plug MvWeb.Plugs.RegistrationEnabled

View file

@ -351,6 +351,7 @@ msgid "Base URL"
msgstr "Basis-URL"
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Basic settings"
msgstr "Grundeinstellungen"
@ -1079,7 +1080,6 @@ msgid "Edit Group"
msgstr "Gruppe bearbeiten"
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Edit Member"
msgstr "Mitglied bearbeiten"
@ -1574,11 +1574,6 @@ msgstr "Wenn du diese Anfrage nicht gestellt hast, kannst du diese E-Mail ignori
msgid "Import"
msgstr "Import"
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Import Members"
msgstr "Mitglieder importieren (CSV)"
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format
msgid "Import Results"
@ -1828,17 +1823,6 @@ msgstr "Verknüpftes Mitglied"
msgid "Linked User"
msgstr "Verknüpfte*r Benutzer*in"
#: lib/mv_web/live/role_live/index.ex
#: lib/mv_web/live/role_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Listing Roles"
msgstr "Rollen auflisten"
#: lib/mv_web/live/user_live/index.ex
#, elixir-autogen, elixir-format
msgid "Listing Users"
msgstr "Benutzer*innen auflisten"
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format
msgid "Logout"
@ -1991,7 +1975,6 @@ msgstr "Die Verknüpfung des Mitglieds wird beim Speichern aufgehoben. Ein neues
#: lib/mv_web/live/group_live/index.ex
#: lib/mv_web/live/group_live/show.ex
#: lib/mv_web/live/member_live/index.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#: lib/mv_web/live/statistics_live.ex
@ -2010,11 +1993,6 @@ msgstr "Mitgliedertabelle"
msgid "Membership Fee"
msgstr "Mitgliedsbeitrag"
#: 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
#: lib/mv_web/translations/member_fields.ex
#, elixir-autogen, elixir-format, fuzzy
@ -2034,7 +2012,6 @@ msgid "Membership Fee Type"
msgstr "Mitgliedsbeitragsart"
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Membership Fee Types"
msgstr "Mitgliedsbeitragsarten"
@ -2046,6 +2023,8 @@ msgid "Membership Fees"
msgstr "Mitgliedsbeiträge"
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Membership fee settings"
msgstr "Beitragseinstellungen"
@ -2705,7 +2684,6 @@ msgid "Reviewed at"
msgstr "Geprüft am"
#: lib/mv_web/live/role_live/form.ex
#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
@ -2735,6 +2713,7 @@ msgid "Role saved successfully."
msgstr "Rolle erfolgreich gespeichert."
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Roles"
msgstr "Rollen"
@ -2971,11 +2950,6 @@ msgstr "Passwort setzen"
msgid "Sets whether the payment status filter and the membership fee status column use the last completed or the current payment cycle."
msgstr "Legt fest, ob Bezahlstatusfilter und Mitgliedsbeitragsstatus-Spalte den letzten abgeschlossenen oder den aktuellen Zahlungszyklus verwenden."
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Settings"
msgstr "Einstellungen"
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Settings saved successfully."
@ -2991,21 +2965,6 @@ msgstr "Einstellungen erfolgreich gespeichert"
msgid "Show"
msgstr "Anzeigen"
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Show Member"
msgstr "Mitglied anzeigen"
#: lib/mv_web/live/role_live/show.ex
#, elixir-autogen, elixir-format
msgid "Show Role"
msgstr "Rolle anzeigen"
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Show User"
msgstr "Benutzer*in anzeigen"
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Show bookings/receipts from Vereinfacht"
@ -3390,7 +3349,6 @@ msgid "Use the data field name as the CSV column header in your file. Data field
msgstr "Verwende die Namen der Datenfelder als Spaltennamen in der CSV-Datei. Datenfelder müssen in Mila bereits angelegt sein, da unbekannte Spaltennamen ignoriert werden. Gruppen und Beitragsstatus können nicht importiert werden."
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "User"
msgstr "Benutzer*in"
@ -3429,7 +3387,7 @@ msgstr "Benutzername"
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.html.heex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/index.ex
#, elixir-autogen, elixir-format
msgid "Users"
msgstr "Benutzer*innen"
@ -3847,6 +3805,7 @@ msgstr "Zum Antragsformular"
msgid "Invalid or expired link"
msgstr "Ungültiger oder abgelaufener Link."
#: lib/mv_web/controllers/join_confirm_controller.ex
#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex
#, elixir-autogen, elixir-format
msgid "Link expired"
@ -3891,3 +3850,48 @@ msgstr "Einstellung konnte nicht gespeichert werden."
#, elixir-autogen, elixir-format
msgid "If disabled, users cannot sign up via /register; sign-in and the join form remain available."
msgstr "Wenn deaktiviert, können sich Nutzer*innen nicht über /register anmelden; Anmeldung und Beitrittsformular bleiben verfügbar."
#: lib/mv_web/controllers/page_controller.ex
#, elixir-autogen, elixir-format
msgid "Home"
msgstr ""
#: lib/mv_web/controllers/join_confirm_controller.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Invalid link"
msgstr "Ungültiger oder abgelaufener Link."
#: lib/mv_web/live/join_live.ex
#, elixir-autogen, elixir-format
msgid "Join"
msgstr "Beitritt"
#: lib/mv_web/controllers/join_confirm_controller.ex
#, elixir-autogen, elixir-format
msgid "Join confirmation"
msgstr "Beitrittsbestätigung"
#: lib/mv_web/live/auth/sign_in_live.ex
#, elixir-autogen, elixir-format
msgid "Sign in"
msgstr "Anmelden"
#: lib/mv_web/live/group_live/show.ex
#, elixir-autogen, elixir-format
msgid "Group %{name}"
msgstr "Gruppe %{name}"
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Member %{name}"
msgstr "Mitglied %{name}"
#: lib/mv_web/live/role_live/show.ex
#, elixir-autogen, elixir-format
msgid "Role %{name}"
msgstr "Rolle %{name}"
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "User %{email}"
msgstr "Benutzer*in %{email}"

View file

@ -352,6 +352,7 @@ msgid "Base URL"
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Basic settings"
msgstr ""
@ -1080,7 +1081,6 @@ msgid "Edit Group"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Edit Member"
msgstr ""
@ -1575,11 +1575,6 @@ msgstr ""
msgid "Import"
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format
msgid "Import Members"
msgstr ""
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format
msgid "Import Results"
@ -1829,17 +1824,6 @@ msgstr ""
msgid "Linked User"
msgstr ""
#: lib/mv_web/live/role_live/index.ex
#: lib/mv_web/live/role_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Listing Roles"
msgstr ""
#: lib/mv_web/live/user_live/index.ex
#, elixir-autogen, elixir-format
msgid "Listing Users"
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format
msgid "Logout"
@ -1992,7 +1976,6 @@ msgstr ""
#: lib/mv_web/live/group_live/index.ex
#: lib/mv_web/live/group_live/show.ex
#: lib/mv_web/live/member_live/index.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#: lib/mv_web/live/statistics_live.ex
@ -2011,11 +1994,6 @@ msgstr ""
msgid "Membership Fee"
msgstr ""
#: 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
#: lib/mv_web/translations/member_fields.ex
#, elixir-autogen, elixir-format
@ -2035,7 +2013,6 @@ msgid "Membership Fee Type"
msgstr ""
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Membership Fee Types"
msgstr ""
@ -2047,6 +2024,8 @@ msgid "Membership Fees"
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Membership fee settings"
msgstr ""
@ -2706,7 +2685,6 @@ msgid "Reviewed at"
msgstr ""
#: lib/mv_web/live/role_live/form.ex
#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
@ -2736,6 +2714,7 @@ msgid "Role saved successfully."
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.ex
#, elixir-autogen, elixir-format
msgid "Roles"
msgstr ""
@ -2972,11 +2951,6 @@ msgstr ""
msgid "Sets whether the payment status filter and the membership fee status column use the last completed or the current payment cycle."
msgstr ""
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Settings"
msgstr ""
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Settings saved successfully."
@ -2992,21 +2966,6 @@ msgstr ""
msgid "Show"
msgstr ""
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Show Member"
msgstr ""
#: lib/mv_web/live/role_live/show.ex
#, elixir-autogen, elixir-format
msgid "Show Role"
msgstr ""
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Show User"
msgstr ""
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Show bookings/receipts from Vereinfacht"
@ -3391,7 +3350,6 @@ msgid "Use the data field name as the CSV column header in your file. Data field
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "User"
msgstr ""
@ -3430,7 +3388,7 @@ msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.html.heex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/index.ex
#, elixir-autogen, elixir-format
msgid "Users"
msgstr ""
@ -3847,6 +3805,7 @@ msgstr ""
msgid "Invalid or expired link"
msgstr ""
#: lib/mv_web/controllers/join_confirm_controller.ex
#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex
#, elixir-autogen, elixir-format
msgid "Link expired"
@ -3891,3 +3850,48 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "If disabled, users cannot sign up via /register; sign-in and the join form remain available."
msgstr ""
#: lib/mv_web/controllers/page_controller.ex
#, elixir-autogen, elixir-format
msgid "Home"
msgstr ""
#: lib/mv_web/controllers/join_confirm_controller.ex
#, elixir-autogen, elixir-format
msgid "Invalid link"
msgstr ""
#: lib/mv_web/live/join_live.ex
#, elixir-autogen, elixir-format
msgid "Join"
msgstr ""
#: lib/mv_web/controllers/join_confirm_controller.ex
#, elixir-autogen, elixir-format
msgid "Join confirmation"
msgstr ""
#: lib/mv_web/live/auth/sign_in_live.ex
#, elixir-autogen, elixir-format
msgid "Sign in"
msgstr ""
#: lib/mv_web/live/group_live/show.ex
#, elixir-autogen, elixir-format
msgid "Group %{name}"
msgstr ""
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Member %{name}"
msgstr ""
#: lib/mv_web/live/role_live/show.ex
#, elixir-autogen, elixir-format
msgid "Role %{name}"
msgstr ""
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "User %{email}"
msgstr ""

View file

@ -352,6 +352,7 @@ msgid "Base URL"
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Basic settings"
msgstr ""
@ -1080,7 +1081,6 @@ msgid "Edit Group"
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Edit Member"
msgstr ""
@ -1575,11 +1575,6 @@ msgstr "If you did not submit this request, you can ignore this email."
msgid "Import"
msgstr ""
#: lib/mv_web/live/import_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Import Members"
msgstr ""
#: lib/mv_web/live/import_live/components.ex
#, elixir-autogen, elixir-format
msgid "Import Results"
@ -1829,17 +1824,6 @@ msgstr ""
msgid "Linked User"
msgstr ""
#: 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 ""
#: lib/mv_web/live/user_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Listing Users"
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format
msgid "Logout"
@ -1992,7 +1976,6 @@ msgstr ""
#: lib/mv_web/live/group_live/index.ex
#: lib/mv_web/live/group_live/show.ex
#: lib/mv_web/live/member_live/index.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#: lib/mv_web/live/statistics_live.ex
@ -2011,11 +1994,6 @@ msgstr ""
msgid "Membership Fee"
msgstr ""
#: 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
#: lib/mv_web/translations/member_fields.ex
#, elixir-autogen, elixir-format, fuzzy
@ -2035,7 +2013,6 @@ msgid "Membership Fee Type"
msgstr ""
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Membership Fee Types"
msgstr ""
@ -2047,6 +2024,8 @@ msgid "Membership Fees"
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/membership_fee_settings_live.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Membership fee settings"
msgstr ""
@ -2706,7 +2685,6 @@ msgid "Reviewed at"
msgstr "Review date"
#: lib/mv_web/live/role_live/form.ex
#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
@ -2736,6 +2714,7 @@ msgid "Role saved successfully."
msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Roles"
msgstr ""
@ -2972,11 +2951,6 @@ msgstr ""
msgid "Sets whether the payment status filter and the membership fee status column use the last completed or the current payment cycle."
msgstr "Sets whether the payment status filter and the membership fee status column use the last completed or the current payment cycle."
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Settings"
msgstr ""
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Settings saved successfully."
@ -2992,21 +2966,6 @@ msgstr ""
msgid "Show"
msgstr ""
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Show Member"
msgstr ""
#: lib/mv_web/live/role_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Show Role"
msgstr ""
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Show User"
msgstr ""
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Show bookings/receipts from Vereinfacht"
@ -3391,7 +3350,6 @@ msgid "Use the data field name as the CSV column header in your file. Data field
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "User"
msgstr ""
@ -3430,7 +3388,7 @@ msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.html.heex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Users"
msgstr ""
@ -3847,6 +3805,7 @@ msgstr "Go to join form"
msgid "Invalid or expired link"
msgstr "Invalid or expired link."
#: lib/mv_web/controllers/join_confirm_controller.ex
#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex
#, elixir-autogen, elixir-format
msgid "Link expired"
@ -3891,3 +3850,48 @@ msgstr "Failed to update setting."
#, elixir-autogen, elixir-format
msgid "If disabled, users cannot sign up via /register; sign-in and the join form remain available."
msgstr "If disabled, users cannot sign up via /register; sign-in and the join form remain available."
#: lib/mv_web/controllers/page_controller.ex
#, elixir-autogen, elixir-format
msgid "Home"
msgstr ""
#: lib/mv_web/controllers/join_confirm_controller.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Invalid link"
msgstr "Invalid or expired link."
#: lib/mv_web/live/join_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Join"
msgstr ""
#: lib/mv_web/controllers/join_confirm_controller.ex
#, elixir-autogen, elixir-format
msgid "Join confirmation"
msgstr ""
#: lib/mv_web/live/auth/sign_in_live.ex
#, elixir-autogen, elixir-format
msgid "Sign in"
msgstr ""
#: lib/mv_web/live/group_live/show.ex
#, elixir-autogen, elixir-format
msgid "Group %{name}"
msgstr "Group %{name}"
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
msgid "Member %{name}"
msgstr "Member %{name}"
#: lib/mv_web/live/role_live/show.ex
#, elixir-autogen, elixir-format
msgid "Role %{name}"
msgstr "Role %{name}"
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "User %{email}"
msgstr "User %{email}"

View file

@ -38,7 +38,10 @@ defmodule MvWeb.JoinLiveEmailFailureTest do
|> render_submit()
html = render(view)
assert html =~ "could not send" or html =~ "confirmation email"
# Error message is translated; accept English or German wording
assert html =~ "could not send" or html =~ "confirmation email" or
html =~ "konnte nicht" or html =~ "Bestätigungs-E-Mail"
refute view |> element("[data-testid='join-success-message']") |> has_element?()
end