refactor: improve groups LiveView based on code review feedback
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Simon 2026-01-28 10:33:27 +01:00
parent 3eb4cde0b7
commit ddc8335cc0
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
8 changed files with 109 additions and 104 deletions

View file

@ -23,78 +23,29 @@ defmodule MvWeb.GroupLive.Form do
def mount(params, _session, socket) do
actor = current_actor(socket)
# Check authorization
# Check authorization based on whether we are creating or updating
action = if params["slug"], do: :update, else: :create
resource = Mv.Membership.Group
if can?(actor, action, resource) do
case load_group_for_form(socket, params, actor) do
{:redirect, socket} ->
{:ok, socket}
{:ok, socket} ->
{:ok, assign_form(socket)}
end
{:ok,
socket
|> assign(:actor, actor)
|> assign(:group, nil)
|> assign(:page_title, page_title_for_params(params))
|> assign(:return_to, return_to_for_params(params))}
else
{:ok, redirect(socket, to: ~p"/groups")}
end
end
defp load_group_for_form(socket, params, actor) do
case params["slug"] do
nil ->
# New group
socket =
socket
|> assign(:group, nil)
|> assign(:page_title, gettext("Create Group"))
|> assign(:return_to, "index")
{:ok, socket}
slug ->
# Edit existing group
load_existing_group(socket, slug, actor)
end
end
defp load_existing_group(socket, slug, actor) do
case Membership.get_group_by_slug(slug, actor: actor) do
{:ok, nil} ->
socket =
socket
|> put_flash(:error, gettext("Group not found."))
|> redirect(to: ~p"/groups")
{:redirect, socket}
{:ok, group} ->
socket =
socket
|> assign(:group, group)
|> assign(:page_title, gettext("Edit Group"))
|> assign(:return_to, "show")
{:ok, socket}
{:error, _error} ->
socket =
socket
|> put_flash(:error, gettext("Failed to load group."))
|> redirect(to: ~p"/groups")
{:redirect, socket}
end
end
@impl true
def handle_params(params, _url, socket) do
# Handle slug-based routing for edit
actor = socket.assigns.actor
case params do
%{"slug" => slug} when is_binary(slug) ->
actor = current_actor(socket)
case Membership.get_group_by_slug(slug, actor: actor) do
case Membership.get_group_by_slug(slug, actor: actor, load: []) do
{:ok, nil} ->
{:noreply,
socket
@ -106,8 +57,8 @@ defmodule MvWeb.GroupLive.Form do
socket
|> assign(:group, group)
|> assign(:page_title, gettext("Edit Group"))
|> assign(:return_to, "show")
|> assign_form()}
|> assign(:return_to, :show)
|> assign_form(actor)}
{:error, _error} ->
{:noreply,
@ -117,7 +68,8 @@ defmodule MvWeb.GroupLive.Form do
end
_ ->
{:noreply, socket}
# New group - ensure form is initialized for create
{:noreply, assign_form(socket, actor)}
end
end
@ -163,17 +115,16 @@ defmodule MvWeb.GroupLive.Form do
end
def handle_event("save", %{"group" => group_params}, socket) do
actor = current_actor(socket)
actor = socket.assigns.actor
case submit_form(socket.assigns.form, group_params, actor) do
{:ok, group} ->
notify_parent({:saved, group})
redirect_path =
if socket.assigns.return_to == "show" do
~p"/groups/#{group.slug}"
else
~p"/groups"
case socket.assigns.return_to do
:show -> ~p"/groups/#{group.slug}"
_ -> ~p"/groups"
end
socket =
@ -190,9 +141,8 @@ defmodule MvWeb.GroupLive.Form do
defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
defp assign_form(%{assigns: assigns} = socket) do
defp assign_form(%{assigns: assigns} = socket, actor) do
group = Map.get(assigns, :group)
actor = assigns.current_user
form =
if group do
@ -216,9 +166,19 @@ defmodule MvWeb.GroupLive.Form do
assign(socket, form: to_form(form))
end
defp return_path("index", _group), do: ~p"/groups"
defp return_path("show", group) when not is_nil(group), do: ~p"/groups/#{group.slug}"
defp return_path("show", _group), do: ~p"/groups"
defp page_title_for_params(%{"slug" => _slug}), do: gettext("Edit Group")
defp page_title_for_params(_params), do: gettext("Create Group")
defp return_to_for_params(%{"slug" => _slug}), do: :show
defp return_to_for_params(_params), do: :index
defp return_path(:index, _group), do: ~p"/groups"
defp return_path(:show, group) when not is_nil(group), do: ~p"/groups/#{group.slug}"
defp return_path(:show, _group), do: ~p"/groups"
defp return_path(_, group) when not is_nil(group), do: ~p"/groups/#{group.slug}"
defp return_path(_, _group), do: ~p"/groups"
end

View file

@ -116,14 +116,17 @@ defmodule MvWeb.GroupLive.Index do
query =
Mv.Membership.Group
|> Ash.Query.load(:member_count)
|> Ash.Query.sort(:name)
opts = ash_actor_opts(actor)
case Ash.read(query, opts) do
{:ok, groups} ->
Enum.sort_by(groups, & &1.name)
groups
{:error, _} ->
{:error, _error} ->
require Logger
Logger.warning("Failed to load groups in GroupLive.Index")
[]
end
end

View file

@ -186,7 +186,7 @@ defmodule MvWeb.GroupLive.Show do
<div class="p-2 mb-2 font-mono text-lg font-bold break-all rounded bg-base-200">
{@group.name}
</div>
<form phx-change="update_name_confirmation">
<form phx-change="update_name_confirmation" phx-debounce="200">
<input
id="group-name-confirmation"
name="name"
@ -244,21 +244,29 @@ defmodule MvWeb.GroupLive.Show do
def handle_event("confirm_delete", %{"slug" => slug}, socket) do
actor = current_actor(socket)
case Membership.get_group_by_slug(slug, actor: actor) do
{:ok, nil} ->
{:noreply,
socket
|> put_flash(:error, gettext("Group not found."))
|> redirect(to: ~p"/groups")}
# Server-side authorization check to prevent unauthorized delete attempts
if can?(actor, :destroy, Mv.Membership.Group) do
case Membership.get_group_by_slug(slug, actor: actor, load: []) do
{:ok, nil} ->
{:noreply,
socket
|> put_flash(:error, gettext("Group not found."))
|> redirect(to: ~p"/groups")}
{:ok, group} ->
handle_delete_confirmation(socket, group, actor)
{:ok, group} ->
handle_delete_confirmation(socket, group, actor)
{:error, _error} ->
{:noreply,
socket
|> put_flash(:error, gettext("Failed to load group."))
|> redirect(to: ~p"/groups")}
{:error, _error} ->
{:noreply,
socket
|> put_flash(:error, gettext("Failed to load group."))
|> redirect(to: ~p"/groups")}
end
else
{:noreply,
socket
|> put_flash(:error, gettext("Not authorized."))
|> redirect(to: ~p"/groups")}
end
end
@ -269,8 +277,7 @@ defmodule MvWeb.GroupLive.Show do
{:noreply,
socket
|> put_flash(:error, gettext("Group name does not match."))
|> assign(:show_delete_modal, false)
|> assign(:name_confirmation, "")}
|> assign(:show_delete_modal, true)}
end
end