Merge branch 'main' into feat/299_plz
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is failing

This commit is contained in:
carla 2026-02-24 15:38:50 +01:00
commit c8d7dd3e55
36 changed files with 250 additions and 187 deletions

View file

@ -54,7 +54,7 @@ defmodule MvWeb.Layouts do
data-sidebar-expanded="true"
phx-hook="SidebarState"
>
<input id="mobile-drawer" type="checkbox" class="drawer-toggle" />
<input id="mobile-drawer" type="checkbox" class="drawer-toggle" phx-update="ignore" />
<div class="drawer-content flex flex-col relative z-0">
<!-- Mobile Header (only visible on mobile) -->

View file

@ -48,8 +48,8 @@ defmodule MvWeb.AuthController do
log_failure_safely(activity, reason)
case {activity, reason} do
{{:rauthy, _action}, reason} ->
handle_rauthy_failure(conn, reason)
{{:oidc, _action}, reason} ->
handle_oidc_failure(conn, reason)
{_, %AshAuthentication.Errors.AuthenticationFailed{caused_by: caused_by}} ->
handle_authentication_failed(conn, caused_by)
@ -61,8 +61,8 @@ defmodule MvWeb.AuthController do
end
end
# Log authentication failures safely, avoiding sensitive data for {:rauthy, _} activities
defp log_failure_safely({:rauthy, _action} = activity, reason) do
# Log authentication failures safely, avoiding sensitive data for {:oidc, _} activities
defp log_failure_safely({:oidc, _action} = activity, reason) do
# For Assent errors, use safe_assent_meta to avoid logging tokens/URLs with query params
case reason do
%Assent.ServerUnreachableError{} = err ->
@ -76,7 +76,7 @@ defmodule MvWeb.AuthController do
Logger.warning(message)
_ ->
# For other rauthy errors, log only error type, not full details
# For other OIDC errors, log only error type, not full details
error_type = get_error_type(reason)
Logger.warning(
@ -86,7 +86,7 @@ defmodule MvWeb.AuthController do
end
defp log_failure_safely(activity, reason) do
# For non-rauthy activities, safe to log full reason
# For non-OIDC activities, safe to log full reason
Logger.warning(
"Authentication failure - Activity: #{inspect(activity)}, Reason: #{inspect(reason)}"
)
@ -119,12 +119,12 @@ defmodule MvWeb.AuthController do
if Enum.empty?(parts), do: "", else: " - " <> Enum.join(parts, ", ")
end
# Handle all Rauthy (OIDC) authentication failures
defp handle_rauthy_failure(conn, %Ash.Error.Invalid{errors: errors}) do
# Handle all OIDC authentication failures
defp handle_oidc_failure(conn, %Ash.Error.Invalid{errors: errors}) do
handle_oidc_email_collision(conn, errors)
end
defp handle_rauthy_failure(conn, %AshAuthentication.Errors.AuthenticationFailed{
defp handle_oidc_failure(conn, %AshAuthentication.Errors.AuthenticationFailed{
caused_by: caused_by
}) do
case caused_by do
@ -139,7 +139,7 @@ defmodule MvWeb.AuthController do
end
# Handle Assent server unreachable errors (network/connectivity issues)
defp handle_rauthy_failure(conn, %Assent.ServerUnreachableError{} = _err) do
defp handle_oidc_failure(conn, %Assent.ServerUnreachableError{} = _err) do
# Logging already done safely in failure/3 via log_failure_safely/2
# No need to log again here to avoid duplicate logs
@ -152,7 +152,7 @@ defmodule MvWeb.AuthController do
end
# Handle Assent invalid response errors (configuration or malformed responses)
defp handle_rauthy_failure(conn, %Assent.InvalidResponseError{} = _err) do
defp handle_oidc_failure(conn, %Assent.InvalidResponseError{} = _err) do
# Logging already done safely in failure/3 via log_failure_safely/2
# No need to log again here to avoid duplicate logs
@ -165,7 +165,7 @@ defmodule MvWeb.AuthController do
end
# Catch-all clause for any other error types
defp handle_rauthy_failure(conn, _reason) do
defp handle_oidc_failure(conn, _reason) do
# Logging already done safely in failure/3 via log_failure_safely/2
# No need to log again here to avoid duplicate logs

View file

@ -84,7 +84,7 @@ defmodule MvWeb.LinkOidcAccountLive do
:info,
dgettext("auth", "Account activated! Redirecting to complete sign-in...")
)
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/rauthy")
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/oidc")
{:error, error} ->
Logger.warning(
@ -223,7 +223,7 @@ defmodule MvWeb.LinkOidcAccountLive do
"Your OIDC account has been successfully linked! Redirecting to complete sign-in..."
)
)
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/rauthy")}
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/oidc")}
{:error, error} ->
Logger.warning(

View file

@ -214,47 +214,49 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do
</:col>
<:action :let={cycle}>
<div class="flex gap-1">
<div class="flex gap-2">
<%= if @can_update_cycle do %>
<button
:if={cycle.status != :paid}
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="paid"
phx-target={@myself}
class="btn btn-sm btn-success"
title={gettext("Mark as paid")}
>
<.icon name="hero-check-circle" class="size-4" />
{gettext("Paid")}
</button>
<button
:if={cycle.status != :suspended}
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="suspended"
phx-target={@myself}
class="btn btn-sm btn-outline btn-warning"
title={gettext("Mark as suspended")}
>
<.icon name="hero-pause-circle" class="size-4" />
{gettext("Suspended")}
</button>
<button
:if={cycle.status != :unpaid}
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="unpaid"
phx-target={@myself}
class="btn btn-sm btn-error"
title={gettext("Mark as unpaid")}
>
<.icon name="hero-x-circle" class="size-4" />
{gettext("Unpaid")}
</button>
<div class="join">
<button
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="paid"
phx-target={@myself}
class={cycle_status_btn_class(cycle.status, :paid)}
aria-pressed={cycle.status == :paid}
title={gettext("Mark as paid")}
>
<.icon name="hero-check-circle" class="size-4" />
{gettext("Paid")}
</button>
<button
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="suspended"
phx-target={@myself}
class={cycle_status_btn_class(cycle.status, :suspended)}
aria-pressed={cycle.status == :suspended}
title={gettext("Mark as suspended")}
>
<.icon name="hero-pause-circle" class="size-4" />
{gettext("Suspended")}
</button>
<button
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="unpaid"
phx-target={@myself}
class={cycle_status_btn_class(cycle.status, :unpaid)}
aria-pressed={cycle.status == :unpaid}
title={gettext("Mark as unpaid")}
>
<.icon name="hero-x-circle" class="size-4" />
{gettext("Unpaid")}
</button>
</div>
<% end %>
<%= if @can_destroy_cycle do %>
<button
@ -1219,6 +1221,20 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do
defp translate_receipt_type("income"), do: gettext("Income")
defp translate_receipt_type(other), do: other
# Returns CSS classes for a cycle status button.
# Active (current) status is highlighted with color and non-interactive;
# inactive buttons are neutral gray. Matches the filter button pattern.
defp cycle_status_btn_class(current_status, btn_status) do
base = "join-item btn btn-sm"
case {current_status == btn_status, btn_status} do
{true, :paid} -> "#{base} btn-success btn-active pointer-events-none"
{true, :suspended} -> "#{base} btn-warning btn-active pointer-events-none"
{true, :unpaid} -> "#{base} btn-error btn-active pointer-events-none"
_ -> base
end
end
# Helper component for section box
attr :title, :string, required: true
slot :inner_block, required: true