feat: conistent danger zone delete flow
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
e5a6003ace
commit
91cf7cca6a
19 changed files with 499 additions and 287 deletions
|
|
@ -20,6 +20,7 @@ defmodule MvWeb.MemberLive.Form do
|
|||
"""
|
||||
use MvWeb, :live_view
|
||||
|
||||
require Logger
|
||||
import MvWeb.LiveHelpers, only: [current_actor: 1, submit_form: 3]
|
||||
|
||||
alias Mv.Membership
|
||||
|
|
@ -246,6 +247,42 @@ defmodule MvWeb.MemberLive.Form do
|
|||
{gettext("Save Member")}
|
||||
</.button>
|
||||
</div>
|
||||
|
||||
<%!-- Danger zone: same section pattern as MemberLive.Show (canonical) --%>
|
||||
<%= if @member && can?(@current_user, :destroy, @member) do %>
|
||||
<section class="mt-8 mb-6" aria-labelledby="danger-zone-heading">
|
||||
<h2 id="danger-zone-heading" class="text-lg font-semibold mb-3 text-error">
|
||||
{gettext("Danger zone")}
|
||||
</h2>
|
||||
<div class="border border-base-300 rounded-lg p-4 bg-base-100">
|
||||
<p class="text-base-content/70 mb-4">
|
||||
{gettext(
|
||||
"Deleting this member cannot be undone. All related data (e.g. membership fee cycles) will be removed."
|
||||
)}
|
||||
</p>
|
||||
<.button
|
||||
variant="danger"
|
||||
type="button"
|
||||
phx-click="delete"
|
||||
phx-value-id={@member.id}
|
||||
data-confirm={
|
||||
gettext("Are you sure you want to delete %{name}? This action cannot be undone.",
|
||||
name: MvWeb.Helpers.MemberHelpers.display_name(@member)
|
||||
)
|
||||
}
|
||||
data-testid="member-delete"
|
||||
aria-label={
|
||||
gettext("Delete member %{name}",
|
||||
name: MvWeb.Helpers.MemberHelpers.display_name(@member)
|
||||
)
|
||||
}
|
||||
>
|
||||
<.icon name="hero-trash" class="size-4" />
|
||||
{gettext("Delete member")}
|
||||
</.button>
|
||||
</div>
|
||||
</section>
|
||||
<% end %>
|
||||
</div>
|
||||
</.form>
|
||||
</Layouts.app>
|
||||
|
|
@ -366,6 +403,40 @@ defmodule MvWeb.MemberLive.Form do
|
|||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("delete", %{"id" => id}, socket) do
|
||||
member = socket.assigns.member
|
||||
actor = current_actor(socket)
|
||||
|
||||
if is_nil(member) do
|
||||
{:noreply, put_flash(socket, :error, gettext("Member not found"))}
|
||||
else
|
||||
if to_string(id) != to_string(member.id) do
|
||||
{:noreply, put_flash(socket, :error, gettext("Member not found"))}
|
||||
else
|
||||
case Ash.destroy(member, actor: actor) do
|
||||
:ok ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:success, gettext("Member deleted successfully"))
|
||||
|> push_navigate(to: ~p"/members")}
|
||||
|
||||
{:error, %Ash.Error.Forbidden{}} ->
|
||||
{:noreply,
|
||||
put_flash(
|
||||
socket,
|
||||
:error,
|
||||
gettext("You do not have permission to delete this member")
|
||||
)}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.warning("Member delete failed: member_id=#{member.id} error=#{inspect(error)}")
|
||||
{:noreply, put_flash(socket, :error, format_destroy_error(error))}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_save_success(socket, member) do
|
||||
notify_parent({:saved, member})
|
||||
|
||||
|
|
@ -413,6 +484,19 @@ defmodule MvWeb.MemberLive.Form do
|
|||
end
|
||||
end
|
||||
|
||||
defp format_destroy_error(%Ash.Error.Invalid{errors: errors}) do
|
||||
error_messages =
|
||||
Enum.map(errors, fn
|
||||
%{field: field, message: message} -> "#{field}: #{message}"
|
||||
%{message: message} -> message
|
||||
_ -> inspect(errors)
|
||||
end)
|
||||
|
||||
Enum.join(error_messages, ", ")
|
||||
end
|
||||
|
||||
defp format_destroy_error(error), do: inspect(error)
|
||||
|
||||
defp handle_save_error(socket, form) do
|
||||
# Always show a flash message when save fails
|
||||
# Field-level validation errors are displayed in form fields, but flash provides additional feedback
|
||||
|
|
|
|||
|
|
@ -418,6 +418,8 @@ defmodule MvWeb.MemberLive.Show do
|
|||
)}
|
||||
|
||||
{:error, error} ->
|
||||
require Logger
|
||||
Logger.warning("Member delete failed: member_id=#{member.id} error=#{inspect(error)}")
|
||||
{:noreply, put_flash(socket, :error, format_error(error))}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue