From 99a8d643449a93edbee81792356e5e5e8c81506a Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 13 Mar 2026 14:11:54 +0100 Subject: [PATCH] fix: translation of login page --- DESIGN_GUIDELINES.md | 15 ++ Justfile | 1 + docs/feature-roadmap.md | 6 +- lib/mv_web/auth_overrides.ex | 59 ++++--- lib/mv_web/components/layouts.ex | 63 ++++++- lib/mv_web/components/layouts/sidebar.ex | 4 +- .../controllers/join_confirm_controller.ex | 33 +++- lib/mv_web/controllers/join_confirm_html.ex | 9 + .../join_confirm_html/confirm.html.heex | 65 ++++++++ lib/mv_web/live/auth/sign_in_live.ex | 118 ++++++------- lib/mv_web/live/join_live.ex | 156 +++++++++--------- priv/gettext/auth.pot | 6 +- priv/gettext/de/LC_MESSAGES/auth.po | 8 +- priv/gettext/de/LC_MESSAGES/default.po | 42 ++++- priv/gettext/default.pot | 42 ++++- priv/gettext/en/LC_MESSAGES/auth.po | 8 +- priv/gettext/en/LC_MESSAGES/default.po | 42 ++++- .../controllers/auth_controller_test.exs | 10 ++ 18 files changed, 487 insertions(+), 200 deletions(-) create mode 100644 lib/mv_web/controllers/join_confirm_html.ex create mode 100644 lib/mv_web/controllers/join_confirm_html/confirm.html.heex diff --git a/DESIGN_GUIDELINES.md b/DESIGN_GUIDELINES.md index 92f7a90..6e8ca40 100644 --- a/DESIGN_GUIDELINES.md +++ b/DESIGN_GUIDELINES.md @@ -76,6 +76,21 @@ For LiveViews that render an edit or new form (e.g. member, group, role, user, c If the `<.header>` is outside the `<.form>`, the submit button must reference the form via the `form` attribute (e.g. `form="user-form"`). +### 2.3 Public / unauthenticated pages (Join, Sign-in, Join Confirm) + +Pages that do not require authentication (e.g. `/join`, `/sign-in`, `/confirm_join/:token`) use a unified layout via the **`Layouts.public_page`** component: + +- **Component:** `Layouts.public_page` renders: + - **Header:** Logo + "Mitgliederverwaltung" (left) | Club name centered via absolute positioning | Language selector (right) + - Main content slot, Flash group. No sidebar, no authenticated-layout logic. +- **Content:** DaisyUI **hero** section (`hero`, `hero-content`) for the main message or form, so all public pages share the same visual structure. The hero is constrained in width (`max-w-4xl mx-auto`) and content is left-aligned (`hero-content flex-col items-start text-left`). +- **Locale handling:** The language selector uses `Gettext.get_locale(MvWeb.Gettext)` (backend-specific) to correctly reflect the active locale. `SignInLive` sets both `Gettext.put_locale(MvWeb.Gettext, locale)` and `Gettext.put_locale(locale)` to keep global and backend locales in sync. +- **Translations for AshAuthentication components:** AshAuthentication’s `_gettext` mechanism translates button labels (e.g. “Sign in” → “Anmelden”, “Register” → “Registrieren”) at runtime via `gettext_fn: {MvWeb.Gettext, "auth"}`. Components that do NOT use `_gettext` (e.g. `HorizontalRule`) receive static German overrides via **`MvWeb.AuthOverridesDE`**, which is prepended to the overrides list in `SignInLive` when the locale is `"de"`. +- **Implementation:** + - **Sign-in** (`SignInLive`): Uses `use Phoenix.LiveView` (not `use MvWeb, :live_view`) so AshAuthentication’s sign_in_route live_session on_mount chain is not mixed with LiveHelpers hooks. Renders `` with the SignIn component inside a hero. Displays a locale-aware `

` title (“Anmelden” / “Registrieren”) above the AshAuthentication component (the library’s Banner is hidden via `show_banner: false`). + - **Join** (`JoinLive`): Uses `use MvWeb, :live_view` and wraps content in `` with a hero for the form. + - **Join Confirm** (controller): Uses `JoinConfirmHTML` with a template that repeats the same header markup and a hero block for the result (no component call from controller templates). + ## 3) Typography (system) Use these standard roles: diff --git a/Justfile b/Justfile index f3ad5a3..d2c51e5 100644 --- a/Justfile +++ b/Justfile @@ -10,6 +10,7 @@ install-dependencies: mix deps.get migrate-database: + mix compile mix ash.setup reset-database: diff --git a/docs/feature-roadmap.md b/docs/feature-roadmap.md index 03f1cce..6383660 100644 --- a/docs/feature-roadmap.md +++ b/docs/feature-roadmap.md @@ -36,10 +36,10 @@ **Closed Issues:** - ✅ [#171](https://git.local-it.org/local-it/mitgliederverwaltung/issues/171) - OIDC handling and linking (closed 2025-11-13) +- ✅ [#146](https://git.local-it.org/local-it/mitgliederverwaltung/issues/146) - Translate "or" in the login screen — fixed via `MvWeb.AuthOverridesDE` locale-specific module (2026-03-13) +- ✅ [#144](https://git.local-it.org/local-it/mitgliederverwaltung/issues/144) - Add language switch dropdown to login screen — fixed locale selector bug with `Gettext.get_locale(MvWeb.Gettext)` (2026-03-13) -**Open Issues:** -- [#146](https://git.local-it.org/local-it/mitgliederverwaltung/issues/146) - Translate "or" in the login screen (Low) -- [#144](https://git.local-it.org/local-it/mitgliederverwaltung/issues/144) - Add language switch dropdown to login screen (Low) +**Open Issues:** (none remaining for Authentication UI) **Current State:** - ✅ **Role-based access control (RBAC)** - Implemented (2026-01-08, PR #346, closes #345) diff --git a/lib/mv_web/auth_overrides.ex b/lib/mv_web/auth_overrides.ex index 5cab4d2..44b3408 100644 --- a/lib/mv_web/auth_overrides.ex +++ b/lib/mv_web/auth_overrides.ex @@ -3,52 +3,57 @@ defmodule MvWeb.AuthOverrides do UI customizations for AshAuthentication Phoenix components. ## Overrides - - `SignIn` - Restricts form width to prevent full-width display - - `Banner` - Replaces default logo with "Mitgliederverwaltung" text - - `HorizontalRule` - Translates "or" text to German + - `SignIn` - Restricts form width and hides the library banner (title is rendered in SignInLive) + - `Banner` - Replaces default logo with text for reset/confirm pages + - `Flash` - Hides library flash (we use flash_group in root layout) ## Documentation For complete reference on available overrides, see: https://hexdocs.pm/ash_authentication_phoenix/ui-overrides.html """ use AshAuthentication.Phoenix.Overrides - use Gettext, backend: MvWeb.Gettext - # configure your UI overrides here - - # First argument to `override` is the component name you are overriding. - # The body contains any number of configurations you wish to override - # Below are some examples - - # For a complete reference, see https://hexdocs.pm/ash_authentication_phoenix/ui-overrides.html - - # override AshAuthentication.Phoenix.Components.Banner do - # set :image_url, "https://media.giphy.com/media/g7GKcSzwQfugw/giphy.gif" - # set :text_class, "bg-red-500" - # end - - # Avoid full-width for the Sign In Form + # Avoid full-width for the Sign In Form. + # Banner is hidden because SignInLive renders its own locale-aware title. override AshAuthentication.Phoenix.Components.SignIn do set :root_class, "md:min-w-md" + set :show_banner, false end - # Replace banner logo with text (no image in light or dark so link has discernible text) + # Replace banner logo with text for reset/confirm pages (no image so link has discernible text). override AshAuthentication.Phoenix.Components.Banner do set :text, "Mitgliederverwaltung" set :image_url, nil set :dark_image_url, nil end - # Translate the "or" in the horizontal rule (between password form and SSO). - # Uses auth domain so it respects the current locale (e.g. "oder" in German). - override AshAuthentication.Phoenix.Components.HorizontalRule do - set :text, dgettext("auth", "or") - end - - # Hide AshAuthentication's Flash component since we use flash_group in root layout - # This prevents duplicate flash messages + # Hide AshAuthentication's Flash component since we use flash_group in root layout. + # This prevents duplicate flash messages. override AshAuthentication.Phoenix.Components.Flash do set :message_class_info, "hidden" set :message_class_error, "hidden" end end + +defmodule MvWeb.AuthOverridesDE do + @moduledoc """ + German locale-specific overrides for AshAuthentication Phoenix components. + + Prepended to the overrides list in SignInLive when the locale is "de". + Provides runtime-static German text for components that do not use + the `_gettext` mechanism (e.g. HorizontalRule renders its text directly), + and for submit buttons whose disable_text bypasses the POT extraction pipeline. + """ + use AshAuthentication.Phoenix.Overrides + + # HorizontalRule renders text without `_gettext`, so we need a static German string. + override AshAuthentication.Phoenix.Components.HorizontalRule do + set :text, "oder" + end + + # Registering ... disable-text is passed through _gettext but "Registering ..." + # has no dgettext source reference, so we supply the German string directly. + override AshAuthentication.Phoenix.Components.Password.RegisterForm do + set :disable_button_text, "Registrieren..." + end +end diff --git a/lib/mv_web/components/layouts.ex b/lib/mv_web/components/layouts.ex index 2979eb4..22408c7 100644 --- a/lib/mv_web/components/layouts.ex +++ b/lib/mv_web/components/layouts.ex @@ -13,6 +13,54 @@ defmodule MvWeb.Layouts do embed_templates "layouts/*" + @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 + share the same chrome without the sidebar or authenticated layout logic. + """ + attr :flash, :map, required: true, doc: "the map of flash messages" + slot :inner_block, required: true + + def public_page(assigns) do + club_name = + case Mv.Membership.get_settings() do + {:ok, s} -> s.club_name || "Mitgliederverwaltung" + _ -> "Mitgliederverwaltung" + end + + assigns = assign(assigns, :club_name, club_name) + + ~H""" +
+
+ Mila Logo + Mitgliederverwaltung +
+ + {@club_name} + +
+ + +
+
+
+
+ {render_slot(@inner_block)} +
+
+ <.flash_group flash={@flash} /> + """ + end + @doc """ Renders the app layout. Can be used with or without a current_user. When current_user is present, it will show the navigation bar. @@ -99,10 +147,13 @@ defmodule MvWeb.Layouts do <% else %> - -
- Mila Logo - + +
+
+ Mila Logo + Mitgliederverwaltung +
+ {@club_name}
@@ -113,8 +164,8 @@ defmodule MvWeb.Layouts do class="select select-sm focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2" aria-label={gettext("Select language")} > - - + +
diff --git a/lib/mv_web/components/layouts/sidebar.ex b/lib/mv_web/components/layouts/sidebar.ex index 49d9cae..4a90543 100644 --- a/lib/mv_web/components/layouts/sidebar.ex +++ b/lib/mv_web/components/layouts/sidebar.ex @@ -260,8 +260,8 @@ defmodule MvWeb.Layouts.Sidebar do class="select select-sm w-full focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2" aria-label={gettext("Select language")} > - - + + diff --git a/lib/mv_web/controllers/join_confirm_controller.ex b/lib/mv_web/controllers/join_confirm_controller.ex index a1247f3..38a3263 100644 --- a/lib/mv_web/controllers/join_confirm_controller.ex +++ b/lib/mv_web/controllers/join_confirm_controller.ex @@ -2,8 +2,9 @@ defmodule MvWeb.JoinConfirmController do @moduledoc """ Handles GET /confirm_join/:token for the public join flow (double opt-in). - Calls a configurable callback (default Mv.Membership) so tests can stub the - dependency. Public route; no authentication required. + Renders a full HTML page with public header and hero layout (success, expired, + or invalid). Calls a configurable callback (default Mv.Membership) so tests can + stub the dependency. Public route; no authentication required. """ use MvWeb, :controller @@ -26,20 +27,36 @@ defmodule MvWeb.JoinConfirmController do defp success_response(conn) do conn - |> put_resp_content_type("text/html") - |> send_resp(200, gettext("Thank you, we have received your request.")) + |> assign_confirm_assigns(:success) + |> put_view(MvWeb.JoinConfirmHTML) + |> render("confirm.html") end defp expired_response(conn) do conn - |> put_resp_content_type("text/html") - |> send_resp(200, gettext("This link has expired. Please submit the form again.")) + |> assign_confirm_assigns(:expired) + |> put_view(MvWeb.JoinConfirmHTML) + |> render("confirm.html") end defp invalid_response(conn) do conn - |> put_resp_content_type("text/html") |> put_status(404) - |> send_resp(404, gettext("Invalid or expired link.")) + |> assign_confirm_assigns(:invalid) + |> put_view(MvWeb.JoinConfirmHTML) + |> render("confirm.html") + end + + defp assign_confirm_assigns(conn, result) do + club_name = + case Mv.Membership.get_settings() do + {:ok, settings} -> settings.club_name || "Mitgliederverwaltung" + _ -> "Mitgliederverwaltung" + end + + conn + |> assign(:result, result) + |> assign(:club_name, club_name) + |> assign(:csrf_token, Plug.CSRFProtection.get_csrf_token()) end end diff --git a/lib/mv_web/controllers/join_confirm_html.ex b/lib/mv_web/controllers/join_confirm_html.ex new file mode 100644 index 0000000..052f197 --- /dev/null +++ b/lib/mv_web/controllers/join_confirm_html.ex @@ -0,0 +1,9 @@ +defmodule MvWeb.JoinConfirmHTML do + @moduledoc """ + Renders join confirmation result pages (success, expired, invalid) with + public header and hero layout. Used by JoinConfirmController. + """ + use MvWeb, :html + + embed_templates "join_confirm_html/*" +end diff --git a/lib/mv_web/controllers/join_confirm_html/confirm.html.heex b/lib/mv_web/controllers/join_confirm_html/confirm.html.heex new file mode 100644 index 0000000..8789607 --- /dev/null +++ b/lib/mv_web/controllers/join_confirm_html/confirm.html.heex @@ -0,0 +1,65 @@ +<%!-- Public header (same structure as Layouts.app unauthenticated branch) --%> +
+ Mila Logo + + {@club_name} + +
+ + +
+
+ +
+
+
+
+
+ <%= case @result do %> + <% :success -> %> +

+ {gettext("Thank you")} +

+

+ {gettext("Thank you, we have received your request.")} +

+

+ {gettext("You will receive an email once your application has been reviewed.")} +

+ + {gettext("Back to join form")} + + <% :expired -> %> +

+ {gettext("Link expired")} +

+

+ {gettext("This link has expired. Please submit the form again.")} +

+ + {gettext("Submit new request")} + + <% :invalid -> %> +

+ {gettext("Invalid or expired link")} +

+

+ {gettext("Invalid or expired link.")} +

+ + {gettext("Go to join form")} + + <% end %> +
+
+
+
+
diff --git a/lib/mv_web/live/auth/sign_in_live.ex b/lib/mv_web/live/auth/sign_in_live.ex index 7ef330b..96bf62b 100644 --- a/lib/mv_web/live/auth/sign_in_live.ex +++ b/lib/mv_web/live/auth/sign_in_live.ex @@ -1,28 +1,42 @@ defmodule MvWeb.SignInLive do @moduledoc """ - Custom sign-in page with language selector and conditional Single Sign-On button. + Custom sign-in page with public header and hero layout (same as Join/Join Confirm). - - Renders a language selector (same pattern as LinkOidcAccountLive). - - Wraps the default AshAuthentication SignIn component in a container with - `data-oidc-configured` so that CSS can hide the SSO button when OIDC is not configured. + Uses Layouts.public_page (no sidebar, no app-layout hooks). Wraps the AshAuthentication + SignIn component in a hero section. Container has data-oidc-configured so CSS can hide + the SSO button when OIDC is not configured. + + Keeps `use Phoenix.LiveView` (not MvWeb :live_view) so AshAuthentication's sign_in_route + live_session on_mount chain is not mixed with LiveHelpers hooks. + + ## Locale overrides + `MvWeb.AuthOverridesDE` is prepended to the overrides list when the locale is "de", + providing static German strings for components that do not use `_gettext` internally + (e.g. HorizontalRule renders its `:text` override directly). """ use Phoenix.LiveView use Gettext, backend: MvWeb.Gettext alias AshAuthentication.Phoenix.Components alias Mv.Config + alias MvWeb.{AuthOverridesDE, Layouts} @impl true def mount(_params, session, socket) do - overrides = - session - |> Map.get("overrides", [AshAuthentication.Phoenix.Overrides.Default]) - # Locale: same fallback as LiveUserAuth so config :default_locale (e.g. "en" in test) is respected - locale = - session["locale"] || Application.get_env(:mv, :default_locale, "de") + locale = session["locale"] || Application.get_env(:mv, :default_locale, "de") + # Set both backend-specific and global locale so Gettext.get_locale/0 and + # Gettext.get_locale/1 both return the correct value (important for the + # language-selector `selected` attribute in Layouts.public_page). Gettext.put_locale(MvWeb.Gettext, locale) + Gettext.put_locale(locale) + + # Prepend DE-specific overrides when locale is German so that components + # without _gettext support (e.g. HorizontalRule) still render in German. + base_overrides = Map.get(session, "overrides", [AshAuthentication.Phoenix.Overrides.Default]) + locale_overrides = if locale == "de", do: [AuthOverridesDE], else: [] + overrides = locale_overrides ++ base_overrides socket = socket @@ -36,10 +50,9 @@ defmodule MvWeb.SignInLive do |> assign(:context, session["context"] || %{}) |> assign(:auth_routes_prefix, session["auth_routes_prefix"]) |> assign(:gettext_fn, session["gettext_fn"]) - |> assign(:live_action, :sign_in) + |> assign_new(:live_action, fn -> :sign_in end) |> assign(:oidc_configured, Config.oidc_configured?()) |> assign(:oidc_only, Config.oidc_only?()) - |> assign(:root_class, "grid h-screen place-items-center bg-base-100") |> assign(:sign_in_id, "sign-in") |> assign(:locale, locale) @@ -54,50 +67,43 @@ defmodule MvWeb.SignInLive do @impl true def render(assigns) do ~H""" -
-

{dgettext("auth", "Sign in")}

- <%!-- Language selector --%> - - - <.live_component - module={Components.SignIn} - otp_app={@otp_app} - live_action={@live_action} - path={@path} - auth_routes_prefix={@auth_routes_prefix} - resources={@resources} - reset_path={@reset_path} - register_path={@register_path} - id={@sign_in_id} - overrides={@overrides} - current_tenant={@current_tenant} - context={@context} - gettext_fn={@gettext_fn} - /> -
+ +
+
+
+
+

+ {if @live_action == :register, + do: dgettext("auth", "Register"), + else: dgettext("auth", "Sign in")} +

+ <.live_component + module={Components.SignIn} + otp_app={@otp_app} + live_action={@live_action} + path={@path} + auth_routes_prefix={@auth_routes_prefix} + resources={@resources} + reset_path={@reset_path} + register_path={@register_path} + id={@sign_in_id} + overrides={@overrides} + current_tenant={@current_tenant} + context={@context} + gettext_fn={@gettext_fn} + /> +
+
+
+
+
""" end end diff --git a/lib/mv_web/live/join_live.ex b/lib/mv_web/live/join_live.ex index 7489331..4716cf8 100644 --- a/lib/mv_web/live/join_live.ex +++ b/lib/mv_web/live/join_live.ex @@ -33,91 +33,97 @@ defmodule MvWeb.JoinLive do @impl true def render(assigns) do ~H""" - -
- <.header> - {gettext("Become a member")} - + +
+
+
+
+ <.header> + {gettext("Become a member")} + -

- {gettext("Please enter your details for the membership application here.")} -

+

+ {gettext("Please enter your details for the membership application here.")} +

- <%= if @submitted do %> -
-

- {gettext( - "We have saved your details. To complete your request, please click the link we sent to your email." - )} -

-
- <% else %> - <.form - for={@form} - id="join-form" - phx-submit="submit" - class="space-y-4" - > - <%= if @rate_limit_error do %> -
- {@rate_limit_error} -
- <% end %> + <%= if @submitted do %> +
+

+ {gettext( + "We have saved your details. To complete your request, please click the link we sent to your email." + )} +

+
+ <% else %> + <.form + for={@form} + id="join-form" + phx-submit="submit" + class="space-y-4" + > + <%= if @rate_limit_error do %> +
+ {@rate_limit_error} +
+ <% end %> - <%= for field <- @join_fields do %> -
- - -
- <% end %> + <%= for field <- @join_fields do %> +
+ + +
+ <% end %> - <%!-- + <%!-- Honeypot (best practice): legit field name "website", type="text", no inline CSS, hidden via class in app.css (off-screen + 1px). tabindex=-1, autocomplete=off, aria-hidden so screen readers skip. If filled → silent failure (same success UI). --%> - - -

- {gettext( - "By submitting your application you will receive an email with a confirmation link. Once you have confirmed your email address, your application will be reviewed." - )} -

- -

- {gettext( - "Your details are only used to process your membership application and to contact you. To prevent abuse we also process technical data (e.g. IP address) only as necessary." - )} -

- -
- -
- - <% end %> +
+
- + """ end diff --git a/priv/gettext/auth.pot b/priv/gettext/auth.pot index a81a82b..cd46c56 100644 --- a/priv/gettext/auth.pot +++ b/priv/gettext/auth.pot @@ -139,18 +139,16 @@ msgid "This OIDC account is already linked to another user. Please contact suppo msgstr "" #: lib/mv_web/live/auth/link_oidc_account_live.ex -#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format msgid "Language selection" msgstr "" #: lib/mv_web/live/auth/link_oidc_account_live.ex -#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format msgid "Select language" msgstr "" -#: lib/mv_web/auth_overrides.ex +#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format -msgid "or" +msgid "Register" msgstr "" diff --git a/priv/gettext/de/LC_MESSAGES/auth.po b/priv/gettext/de/LC_MESSAGES/auth.po index 2aa5e6a..07583be 100644 --- a/priv/gettext/de/LC_MESSAGES/auth.po +++ b/priv/gettext/de/LC_MESSAGES/auth.po @@ -135,18 +135,16 @@ msgid "This OIDC account is already linked to another user. Please contact suppo msgstr "Dieses OIDC-Konto ist bereits mit einer*m anderen Benutzer*in verknüpft. Bitte kontaktiere den Support." #: lib/mv_web/live/auth/link_oidc_account_live.ex -#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format msgid "Language selection" msgstr "Sprachauswahl" #: lib/mv_web/live/auth/link_oidc_account_live.ex -#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format msgid "Select language" msgstr "Sprache auswählen" -#: lib/mv_web/auth_overrides.ex +#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format -msgid "or" -msgstr "oder" +msgid "Register" +msgstr "Registrieren" diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index 4c824f0..a96e6c9 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -1688,7 +1688,7 @@ msgstr "Ungültiges Datumsformat" msgid "Invalid email address. Please enter a valid recipient address." msgstr "Ungültige E-Mail-Adresse. Bitte gib eine gültige Empfängeradresse ein." -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "Invalid or expired link." msgstr "Ungültiger oder abgelaufener Link." @@ -2897,6 +2897,7 @@ msgstr "Intervall auswählen" #: lib/mv_web/components/layouts.ex #: lib/mv_web/components/layouts/sidebar.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "Select language" msgstr "Sprache auswählen" @@ -3197,7 +3198,7 @@ msgstr "Wird getestet..." msgid "Text" msgstr "Textfeld" -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "Thank you, we have received your request." msgstr "Vielen Dank, wir haben deine Anfrage erhalten." @@ -3270,7 +3271,7 @@ msgstr "Dies ist ein technisches Feld und kann nicht verändert werden." msgid "This is a test email sent from Mila. If you received this, your SMTP configuration is working correctly." msgstr "Dies ist eine Test-E-Mail von Mila. Wenn du diese erhalten hast, funktioniert deine SMTP-Konfiguration korrekt." -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "This link has expired. Please submit the form again." msgstr "Dieser Link ist abgelaufen. Bitte sende das Formular erneut ab." @@ -3831,3 +3832,38 @@ msgstr "Wir haben deine Anfrage erhalten. Du hast bereits einen Mitgliedsantrag, #, elixir-autogen, elixir-format msgid "You already had a pending request. Here is a new confirmation link." msgstr "Du hattest bereits einen offenen Antrag. Hier ist ein neuer Bestätigungslink." + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Back to join form" +msgstr "Zurück zu den Mitgliedsanträgen" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Go to join form" +msgstr "Zum Antragsformular" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Invalid or expired link" +msgstr "Ungültiger oder abgelaufener Link." + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Link expired" +msgstr "Link abgelaufen" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Submit new request" +msgstr "Antrag absenden" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Thank you" +msgstr "Vielen Dank" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "You will receive an email once your application has been reviewed." +msgstr "Du erhältst eine E-Mail, sobald dein Antrag geprüft wurde." diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 8796553..6945957 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -1689,7 +1689,7 @@ msgstr "" msgid "Invalid email address. Please enter a valid recipient address." msgstr "" -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "Invalid or expired link." msgstr "" @@ -2898,6 +2898,7 @@ msgstr "" #: lib/mv_web/components/layouts.ex #: lib/mv_web/components/layouts/sidebar.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "Select language" msgstr "" @@ -3198,7 +3199,7 @@ msgstr "" msgid "Text" msgstr "" -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "Thank you, we have received your request." msgstr "" @@ -3271,7 +3272,7 @@ msgstr "" msgid "This is a test email sent from Mila. If you received this, your SMTP configuration is working correctly." msgstr "" -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "This link has expired. Please submit the form again." msgstr "" @@ -3831,3 +3832,38 @@ msgstr "" #, elixir-autogen, elixir-format msgid "You already had a pending request. Here is a new confirmation link." msgstr "" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Back to join form" +msgstr "" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Go to join form" +msgstr "" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Invalid or expired link" +msgstr "" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Link expired" +msgstr "" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Submit new request" +msgstr "" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Thank you" +msgstr "" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "You will receive an email once your application has been reviewed." +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/auth.po b/priv/gettext/en/LC_MESSAGES/auth.po index 764ea1d..564e640 100644 --- a/priv/gettext/en/LC_MESSAGES/auth.po +++ b/priv/gettext/en/LC_MESSAGES/auth.po @@ -132,18 +132,16 @@ msgid "This OIDC account is already linked to another user. Please contact suppo msgstr "" #: lib/mv_web/live/auth/link_oidc_account_live.ex -#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format msgid "Language selection" msgstr "" #: lib/mv_web/live/auth/link_oidc_account_live.ex -#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format msgid "Select language" msgstr "" -#: lib/mv_web/auth_overrides.ex +#: lib/mv_web/live/auth/sign_in_live.ex #, elixir-autogen, elixir-format -msgid "or" -msgstr "or" +msgid "Register" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index 22c6363..827290b 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -1689,7 +1689,7 @@ msgstr "" msgid "Invalid email address. Please enter a valid recipient address." msgstr "" -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "Invalid or expired link." msgstr "Invalid or expired link." @@ -2898,6 +2898,7 @@ msgstr "" #: lib/mv_web/components/layouts.ex #: lib/mv_web/components/layouts/sidebar.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format, fuzzy msgid "Select language" msgstr "" @@ -3198,7 +3199,7 @@ msgstr "" msgid "Text" msgstr "" -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "Thank you, we have received your request." msgstr "Thank you, we have received your request." @@ -3271,7 +3272,7 @@ msgstr "" msgid "This is a test email sent from Mila. If you received this, your SMTP configuration is working correctly." msgstr "" -#: lib/mv_web/controllers/join_confirm_controller.ex +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex #, elixir-autogen, elixir-format msgid "This link has expired. Please submit the form again." msgstr "This link has expired. Please submit the form again." @@ -3831,3 +3832,38 @@ msgstr "We have received your request. You already have a membership application #, elixir-autogen, elixir-format msgid "You already had a pending request. Here is a new confirmation link." msgstr "You already had a pending request. Here is a new confirmation link." + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Back to join form" +msgstr "Back to membership applications" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Go to join form" +msgstr "Go to join form" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Invalid or expired link" +msgstr "Invalid or expired link." + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Link expired" +msgstr "Link expired" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Submit new request" +msgstr "Submit new request" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "Thank you" +msgstr "Thank you" + +#: lib/mv_web/controllers/join_confirm_html/confirm.html.heex +#, elixir-autogen, elixir-format +msgid "You will receive an email once your application has been reviewed." +msgstr "You will receive an email once your application has been reviewed." diff --git a/test/mv_web/controllers/auth_controller_test.exs b/test/mv_web/controllers/auth_controller_test.exs index 0841e68..328a9f4 100644 --- a/test/mv_web/controllers/auth_controller_test.exs +++ b/test/mv_web/controllers/auth_controller_test.exs @@ -28,6 +28,16 @@ defmodule MvWeb.AuthControllerTest do assert html_response(conn, 200) =~ "Sign in" end + @tag role: :unauthenticated + test "GET /sign-in returns 200 and renders page (exercises AuthOverrides and layout)", %{ + conn: conn + } do + {:ok, _view, html} = live(conn, ~p"/sign-in") + assert html =~ "Sign in" + # Public header (logo) from Layouts.app unauthenticated branch + assert html =~ "mila.svg" or html =~ "Mila Logo" + end + test "GET /sign-out redirects to home", %{conn: authenticated_conn} do conn = conn_with_oidc_user(authenticated_conn) conn = get(conn, ~p"/sign-out")