feat: unify page titles
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/production Build is failing

This commit is contained in:
Simon 2026-03-13 19:01:50 +01:00
parent e8ec620d57
commit c933144920
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
37 changed files with 309 additions and 200 deletions

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