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:
Moritz 2026-01-08 11:42:29 +01:00
parent 36858db97c
commit 9c8cdb5e17
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 33 additions and 1 deletions

View file

@ -17,16 +17,21 @@ defmodule MvWeb.RoleLive.Index do
use MvWeb, :live_view
alias Mv.Authorization
alias Mv.Accounts
require Ash.Query
@impl true
def mount(_params, _session, socket) do
socket = ensure_user_role_loaded(socket)
roles = load_roles()
user_counts = load_user_counts(roles)
{:ok,
socket
|> assign(:page_title, gettext("Listing Roles"))
|> assign(:roles, roles)}
|> assign(:roles, roles)
|> assign(:user_counts, user_counts)}
end
defp ensure_user_role_loaded(socket) do
@ -86,6 +91,29 @@ defmodule MvWeb.RoleLive.Index do
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
Enum.map_join(error.errors, ", ", fn e -> e.message end)
end