feat: add user count display for each role
- Add Users column showing number of users assigned to each role - Load user counts efficiently in single query to avoid N+1 - Similar implementation to membership fee types member count
This commit is contained in:
parent
36858db97c
commit
9c8cdb5e17
2 changed files with 33 additions and 1 deletions
|
|
@ -17,16 +17,21 @@ defmodule MvWeb.RoleLive.Index do
|
||||||
use MvWeb, :live_view
|
use MvWeb, :live_view
|
||||||
|
|
||||||
alias Mv.Authorization
|
alias Mv.Authorization
|
||||||
|
alias Mv.Accounts
|
||||||
|
|
||||||
|
require Ash.Query
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, _session, socket) do
|
def mount(_params, _session, socket) do
|
||||||
socket = ensure_user_role_loaded(socket)
|
socket = ensure_user_role_loaded(socket)
|
||||||
roles = load_roles()
|
roles = load_roles()
|
||||||
|
user_counts = load_user_counts(roles)
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
socket
|
socket
|
||||||
|> assign(:page_title, gettext("Listing Roles"))
|
|> assign(:page_title, gettext("Listing Roles"))
|
||||||
|> assign(:roles, roles)}
|
|> assign(:roles, roles)
|
||||||
|
|> assign(:user_counts, user_counts)}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_user_role_loaded(socket) do
|
defp ensure_user_role_loaded(socket) do
|
||||||
|
|
@ -86,6 +91,29 @@ defmodule MvWeb.RoleLive.Index do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Loads all user counts for roles in a single query to avoid N+1 queries
|
||||||
|
defp load_user_counts(roles) do
|
||||||
|
role_ids = Enum.map(roles, & &1.id)
|
||||||
|
|
||||||
|
# Load all users with role_id in a single query
|
||||||
|
users =
|
||||||
|
Accounts.User
|
||||||
|
|> Ash.Query.filter(role_id in ^role_ids)
|
||||||
|
|> Ash.Query.select([:role_id])
|
||||||
|
|> Ash.read!(domain: Mv.Accounts)
|
||||||
|
|
||||||
|
# Group by role_id and count
|
||||||
|
users
|
||||||
|
|> Enum.group_by(& &1.role_id)
|
||||||
|
|> Enum.map(fn {role_id, users_list} -> {role_id, length(users_list)} end)
|
||||||
|
|> Map.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
# Gets user count from preloaded assigns map
|
||||||
|
defp get_user_count(role, user_counts) do
|
||||||
|
Map.get(user_counts, role.id, 0)
|
||||||
|
end
|
||||||
|
|
||||||
defp format_error(%Ash.Error.Invalid{} = error) do
|
defp format_error(%Ash.Error.Invalid{} = error) do
|
||||||
Enum.map_join(error.errors, ", ", fn e -> e.message end)
|
Enum.map_join(error.errors, ", ", fn e -> e.message end)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,10 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
</:col>
|
</:col>
|
||||||
|
|
||||||
|
<:col :let={role} label={gettext("Users")}>
|
||||||
|
<span class="badge badge-ghost">{get_user_count(role, @user_counts)}</span>
|
||||||
|
</:col>
|
||||||
|
|
||||||
<:action :let={role}>
|
<:action :let={role}>
|
||||||
<div class="sr-only">
|
<div class="sr-only">
|
||||||
<.link navigate={~p"/admin/roles/#{role}"}>{gettext("Show")}</.link>
|
<.link navigate={~p"/admin/roles/#{role}"}>{gettext("Show")}</.link>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue