Groups Admin UI closes #372 #382

Merged
simon merged 8 commits from feature/372-groups-management into main 2026-01-28 10:51:45 +01:00
Showing only changes of commit 59aefe9521 - Show all commits

View file

@ -172,7 +172,8 @@ defmodule MvWeb.GroupLive.Show do
{ngettext( {ngettext(
"This group has %{count} member. All member-group associations will be permanently deleted.", "This group has %{count} member. All member-group associations will be permanently deleted.",
"This group has %{count} members. All member-group associations will be permanently deleted.", "This group has %{count} members. All member-group associations will be permanently deleted.",
@group.member_count @group.member_count,
count: @group.member_count
)} )}
</span> </span>
</div> </div>
@ -186,7 +187,7 @@ defmodule MvWeb.GroupLive.Show do
<div class="p-2 mb-2 font-mono text-lg font-bold break-all rounded bg-base-200"> <div class="p-2 mb-2 font-mono text-lg font-bold break-all rounded bg-base-200">
{@group.name} {@group.name}
</div> </div>
<form phx-change="update_name_confirmation" phx-debounce="200"> <form phx-change="update_name_confirmation">
<input <input
id="group-name-confirmation" id="group-name-confirmation"
name="name" name="name"
@ -194,6 +195,7 @@ defmodule MvWeb.GroupLive.Show do
value={@name_confirmation || ""} value={@name_confirmation || ""}
placeholder={gettext("Enter the group name to confirm")} placeholder={gettext("Enter the group name to confirm")}
autocomplete="off" autocomplete="off"
phx-debounce="200"
class="w-full input input-bordered" class="w-full input input-bordered"
/> />
</form> </form>
@ -243,29 +245,23 @@ defmodule MvWeb.GroupLive.Show do
def handle_event("confirm_delete", %{"slug" => slug}, socket) do def handle_event("confirm_delete", %{"slug" => slug}, socket) do
actor = current_actor(socket) actor = current_actor(socket)
group = socket.assigns.group
# Server-side authorization check to prevent unauthorized delete attempts # Verify slug matches the group in assigns (prevents tampering)
if can?(actor, :destroy, Mv.Membership.Group) do if group.slug == slug do
case Membership.get_group_by_slug(slug, actor: actor, load: []) do # Server-side authorization check on the specific group record
{:ok, nil} -> if can?(actor, :destroy, group) do
{:noreply, handle_delete_confirmation(socket, group, actor)
socket else
|> put_flash(:error, gettext("Group not found.")) {:noreply,
|> redirect(to: ~p"/groups")} socket
|> put_flash(:error, gettext("Not authorized."))
{:ok, group} -> |> redirect(to: ~p"/groups")}
handle_delete_confirmation(socket, group, actor)
{:error, _error} ->
{:noreply,
socket
|> put_flash(:error, gettext("Failed to load group."))
|> redirect(to: ~p"/groups")}
end end
else else
{:noreply, {:noreply,
socket socket
|> put_flash(:error, gettext("Not authorized.")) |> put_flash(:error, gettext("Group not found."))
|> redirect(to: ~p"/groups")} |> redirect(to: ~p"/groups")}
end end
end end