From 7c821193fc67e823a43fc31527273275f733b0d3 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 9 Jan 2026 05:26:11 +0100 Subject: [PATCH] Fix error handling and actor access in MemberLive.Index Replace bang calls with proper error handling and use current_actor/1 helper for consistent actor access. --- lib/mv_web/live/member_live/index.ex | 64 ++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/lib/mv_web/live/member_live/index.ex b/lib/mv_web/live/member_live/index.ex index 07398af..f7eddfb 100644 --- a/lib/mv_web/live/member_live/index.ex +++ b/lib/mv_web/live/member_live/index.ex @@ -31,6 +31,7 @@ defmodule MvWeb.MemberLive.Index do require Ash.Query import Ash.Expr + import MvWeb.LiveHelpers, only: [current_actor: 1] alias Mv.Membership alias MvWeb.MemberLive.Index.Formatter @@ -60,7 +61,7 @@ defmodule MvWeb.MemberLive.Index do # Note: Using Ash.read! (bang version) - errors will be handled by Phoenix LiveView # and result in a 500 error page. This is appropriate for LiveViews where errors # should be visible to the user rather than silently failing. - actor = socket.assigns[:current_user] + actor = current_actor(socket) custom_fields_visible = Mv.Membership.CustomField @@ -134,14 +135,41 @@ defmodule MvWeb.MemberLive.Index do """ @impl true def handle_event("delete", %{"id" => id}, socket) do - # Note: Using bang versions (!) - errors will be handled by Phoenix LiveView - # This ensures users see error messages if deletion fails (e.g., permission denied) - actor = socket.assigns[:current_user] - member = Ash.get!(Mv.Membership.Member, id, actor: actor) - Ash.destroy!(member, actor: actor) + actor = current_actor(socket) - updated_members = Enum.reject(socket.assigns.members, &(&1.id == id)) - {:noreply, assign(socket, :members, updated_members)} + case Ash.get(Mv.Membership.Member, id, actor: actor) do + {:ok, member} -> + case Ash.destroy(member, actor: actor) do + :ok -> + updated_members = Enum.reject(socket.assigns.members, &(&1.id == id)) + + {:noreply, + socket + |> assign(:members, updated_members) + |> put_flash(:info, gettext("Member deleted successfully"))} + + {:error, %Ash.Error.Forbidden{}} -> + {:noreply, + put_flash( + socket, + :error, + gettext("You do not have permission to delete this member") + )} + + {:error, error} -> + {:noreply, put_flash(socket, :error, format_error(error))} + end + + {:error, %Ash.Error.Query.NotFound{}} -> + {:noreply, put_flash(socket, :error, gettext("Member not found"))} + + {:error, %Ash.Error.Forbidden{} = _error} -> + {:noreply, + put_flash(socket, :error, gettext("You do not have permission to access this member"))} + + {:error, error} -> + {:noreply, put_flash(socket, :error, format_error(error))} + end end @impl true @@ -241,6 +269,24 @@ defmodule MvWeb.MemberLive.Index do end end + # Helper to format errors for display + defp format_error(%Ash.Error.Invalid{errors: errors}) do + error_messages = + Enum.map(errors, fn error -> + case error do + %{field: field, message: message} -> "#{field}: #{message}" + %{message: message} -> message + _ -> inspect(error) + end + end) + + Enum.join(error_messages, ", ") + end + + defp format_error(error) do + inspect(error) + end + # ----------------------------------------------------------------- # Handle Infos from Child Components # ----------------------------------------------------------------- @@ -683,7 +729,7 @@ defmodule MvWeb.MemberLive.Index do # Note: Using Ash.read! - errors will be handled by Phoenix LiveView # This is appropriate for data loading in LiveViews - actor = socket.assigns[:current_user] + actor = current_actor(socket) members = Ash.read!(query, actor: actor) # Custom field values are already filtered at the database level in load_custom_field_values/2