refactor: Simplify UserLive.Form handle_event and improve error handling
- Extract handle_member_linking, perform_member_link_action helpers - Extract handle_save_success, get_action_name, handle_member_link_error - Replace hardcoded strings with gettext translations - Use submit_form wrapper for consistent actor handling - Group all handle_event/3 clauses together - Add early return in load_members_for_linking if actor is nil
This commit is contained in:
parent
a22081f288
commit
eb81d5f7cb
1 changed files with 78 additions and 56 deletions
|
|
@ -34,7 +34,7 @@ defmodule MvWeb.UserLive.Form do
|
|||
use MvWeb, :live_view
|
||||
|
||||
on_mount {MvWeb.LiveHelpers, :ensure_user_role_loaded}
|
||||
import MvWeb.LiveHelpers, only: [current_actor: 1]
|
||||
import MvWeb.LiveHelpers, only: [current_actor: 1, submit_form: 3]
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
|
|
@ -305,6 +305,7 @@ defmodule MvWeb.UserLive.Form do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("validate", %{"user" => user_params}, socket) do
|
||||
validated_form = AshPhoenix.Form.validate(socket.assigns.form, user_params)
|
||||
|
||||
|
|
@ -322,70 +323,30 @@ defmodule MvWeb.UserLive.Form do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("save", %{"user" => user_params}, socket) do
|
||||
actor = current_actor(socket)
|
||||
# First save the user without member changes
|
||||
case AshPhoenix.Form.submit(socket.assigns.form,
|
||||
params: user_params,
|
||||
action_opts: [actor: actor]
|
||||
) do
|
||||
case submit_form(socket.assigns.form, user_params, actor) do
|
||||
{:ok, user} ->
|
||||
# Then handle member linking/unlinking as a separate step
|
||||
actor = current_actor(socket)
|
||||
|
||||
result =
|
||||
cond do
|
||||
# Selected member ID takes precedence (new link)
|
||||
socket.assigns.selected_member_id ->
|
||||
Mv.Accounts.update_user(user, %{member: %{id: socket.assigns.selected_member_id}},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Unlink flag is set
|
||||
socket.assigns[:unlink_member] ->
|
||||
Mv.Accounts.update_user(user, %{member: nil}, actor: actor)
|
||||
|
||||
# No changes to member relationship
|
||||
true ->
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, updated_user} ->
|
||||
notify_parent({:saved, updated_user})
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> put_flash(:info, "User #{socket.assigns.form.source.type}d successfully")
|
||||
|> push_navigate(to: return_path(socket.assigns.return_to, updated_user))
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, error} ->
|
||||
# Show user-friendly error from member linking/unlinking
|
||||
error_message = extract_error_message(error)
|
||||
|
||||
{:noreply,
|
||||
put_flash(
|
||||
socket,
|
||||
:error,
|
||||
gettext("Failed to link member: %{error}", error: error_message)
|
||||
)}
|
||||
end
|
||||
handle_member_linking(socket, user, actor)
|
||||
|
||||
{:error, form} ->
|
||||
{:noreply, assign(socket, form: form)}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("show_member_dropdown", _params, socket) do
|
||||
{:noreply, assign(socket, show_member_dropdown: true)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("hide_member_dropdown", _params, socket) do
|
||||
{:noreply, assign(socket, show_member_dropdown: false, focused_member_index: nil)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("member_dropdown_keydown", %{"key" => "ArrowDown"}, socket) do
|
||||
return_if_dropdown_closed(socket, fn ->
|
||||
max_index = length(socket.assigns.available_members) - 1
|
||||
|
|
@ -402,6 +363,7 @@ defmodule MvWeb.UserLive.Form do
|
|||
end)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("member_dropdown_keydown", %{"key" => "ArrowUp"}, socket) do
|
||||
return_if_dropdown_closed(socket, fn ->
|
||||
current = socket.assigns.focused_member_index
|
||||
|
|
@ -417,23 +379,27 @@ defmodule MvWeb.UserLive.Form do
|
|||
end)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("member_dropdown_keydown", %{"key" => "Enter"}, socket) do
|
||||
return_if_dropdown_closed(socket, fn ->
|
||||
select_focused_member(socket)
|
||||
end)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("member_dropdown_keydown", %{"key" => "Escape"}, socket) do
|
||||
return_if_dropdown_closed(socket, fn ->
|
||||
{:noreply, assign(socket, show_member_dropdown: false, focused_member_index: nil)}
|
||||
end)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("member_dropdown_keydown", _params, socket) do
|
||||
# Ignore other keys
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("search_members", %{"member_search" => query}, socket) do
|
||||
socket =
|
||||
socket
|
||||
|
|
@ -445,6 +411,7 @@ defmodule MvWeb.UserLive.Form do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("select_member", %{"id" => member_id}, socket) do
|
||||
# Find the selected member to get their name
|
||||
selected_member = Enum.find(socket.assigns.available_members, &(&1.id == member_id))
|
||||
|
|
@ -461,27 +428,82 @@ defmodule MvWeb.UserLive.Form do
|
|||
|> assign(:selected_member_name, member_name)
|
||||
|> assign(:unlink_member, false)
|
||||
|> assign(:show_member_dropdown, false)
|
||||
|> assign(:member_search_query, member_name)
|
||||
|> push_event("set-input-value", %{id: "member-search-input", value: member_name})
|
||||
|> assign(:focused_member_index, nil)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("unlink_member", _params, socket) do
|
||||
# Set flag to unlink member on save
|
||||
# Clear all member selection state and keep dropdown hidden
|
||||
socket =
|
||||
socket
|
||||
|> assign(:unlink_member, true)
|
||||
|> assign(:selected_member_id, nil)
|
||||
|> assign(:selected_member_name, nil)
|
||||
|> assign(:member_search_query, "")
|
||||
|> assign(:unlink_member, true)
|
||||
|> assign(:show_member_dropdown, false)
|
||||
|> load_initial_members()
|
||||
|> assign(:focused_member_index, nil)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp handle_member_linking(socket, user, actor) do
|
||||
result = perform_member_link_action(socket, user, actor)
|
||||
|
||||
case result do
|
||||
{:ok, updated_user} ->
|
||||
handle_save_success(socket, updated_user)
|
||||
|
||||
{:error, error} ->
|
||||
handle_member_link_error(socket, error)
|
||||
end
|
||||
end
|
||||
|
||||
defp perform_member_link_action(socket, user, actor) do
|
||||
cond do
|
||||
# Selected member ID takes precedence (new link)
|
||||
socket.assigns.selected_member_id ->
|
||||
Mv.Accounts.update_user(user, %{member: %{id: socket.assigns.selected_member_id}},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Unlink flag is set
|
||||
socket.assigns[:unlink_member] ->
|
||||
Mv.Accounts.update_user(user, %{member: nil}, actor: actor)
|
||||
|
||||
# No changes to member relationship
|
||||
true ->
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_save_success(socket, updated_user) do
|
||||
notify_parent({:saved, updated_user})
|
||||
|
||||
action = get_action_name(socket.assigns.form.source.type)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> put_flash(:info, gettext("User %{action} successfully", action: action))
|
||||
|> push_navigate(to: return_path(socket.assigns.return_to, updated_user))
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp get_action_name(:create), do: gettext("created")
|
||||
defp get_action_name(:update), do: gettext("updated")
|
||||
defp get_action_name(other), do: to_string(other)
|
||||
|
||||
defp handle_member_link_error(socket, error) do
|
||||
error_message = extract_error_message(error)
|
||||
|
||||
{:noreply,
|
||||
put_flash(
|
||||
socket,
|
||||
:error,
|
||||
gettext("Failed to link member: %{error}", error: error_message)
|
||||
)}
|
||||
end
|
||||
|
||||
@spec notify_parent(any()) :: any()
|
||||
defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
|
||||
|
||||
|
|
@ -597,10 +619,10 @@ defmodule MvWeb.UserLive.Form do
|
|||
case List.first(errors) do
|
||||
%{message: message} when is_binary(message) -> message
|
||||
%{field: field, message: message} -> "#{field}: #{message}"
|
||||
_ -> "Unknown error"
|
||||
_ -> gettext("Unknown error")
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_error_message(error) when is_binary(error), do: error
|
||||
defp extract_error_message(_), do: "Unknown error"
|
||||
defp extract_error_message(_), do: gettext("Unknown error")
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue