defmodule MvWeb.RoleLive.Index do @moduledoc """ LiveView for displaying and managing the role list. ## Features - List all roles with name, description, permission_set_name, is_system_role - Create new roles - Navigate to role details (row click) and edit from details header - Delete only via Danger zone on role show page ## Security Only admins can access this page (enforced by authorization). """ use MvWeb, :live_view alias Mv.Accounts alias Mv.Authorization require Ash.Query import MvWeb.RoleLive.Helpers, only: [format_error: 1, permission_set_badge_variant: 1, opts_with_actor: 3] @impl true def mount(_params, _session, socket) do actor = socket.assigns[:current_user] roles = load_roles(actor) user_counts = load_user_counts(roles, actor) {:ok, socket |> assign(:page_title, gettext("Listing Roles")) |> assign(:roles, roles) |> assign(:user_counts, user_counts)} end @spec load_roles(map() | nil) :: [Mv.Authorization.Role.t()] defp load_roles(actor) do opts = MvWeb.LiveHelpers.ash_actor_opts(actor) case Authorization.list_roles(opts) do {:ok, roles} -> Enum.sort_by(roles, & &1.name) {:error, _} -> [] end end # Loads all user counts for roles using DB-side aggregation for better performance @spec load_user_counts([Mv.Authorization.Role.t()], map() | nil) :: %{ Ecto.UUID.t() => non_neg_integer() } defp load_user_counts(roles, _actor) do role_ids = Enum.map(roles, & &1.id) # Use Ecto directly for efficient GROUP BY COUNT query # This is much more performant than loading all users and counting in Elixir # Note: We bypass Ash here for performance, but this is a simple read-only query import Ecto.Query query = from u in Accounts.User, where: u.role_id in ^role_ids, group_by: u.role_id, select: {u.role_id, count(u.id)} results = Mv.Repo.all(query) results |> Enum.into(%{}, fn {role_id, count} -> {role_id, count} end) end # Gets user count from preloaded assigns map @spec get_user_count(Mv.Authorization.Role.t(), %{Ecto.UUID.t() => non_neg_integer()}) :: non_neg_integer() defp get_user_count(role, user_counts) do Map.get(user_counts, role.id, 0) end end