Packages

phoenix_kit

1.7.36
1.7.210 1.7.209 1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit_web live users permissions_matrix.ex
Raw

lib/phoenix_kit_web/live/users/permissions_matrix.ex

defmodule PhoenixKitWeb.Live.Users.PermissionsMatrix do
@moduledoc """
Interactive permissions matrix view for PhoenixKit admin panel.
Displays a matrix of roles vs module permission keys, showing which
roles have access to which sections. Owner column shows "always" badge.
Cells are clickable to toggle permissions directly.
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Admin.Events
alias PhoenixKit.Settings
alias PhoenixKit.Users.Auth.Scope
alias PhoenixKit.Users.Permissions
alias PhoenixKit.Users.Roles
def mount(_params, _session, socket) do
if connected?(socket) do
Events.subscribe_to_roles()
Events.subscribe_to_permissions()
Events.subscribe_to_modules()
end
project_title = Settings.get_project_title()
socket =
socket
|> assign(:page_title, "Permissions Matrix")
|> assign(:project_title, project_title)
|> load_matrix()
{:ok, socket}
end
# --- PubSub Handlers ---
def handle_info({:role_created, _role}, socket) do
{:noreply, load_matrix(socket)}
end
def handle_info({:role_updated, _role}, socket) do
{:noreply, load_matrix(socket)}
end
def handle_info({:role_deleted, _role}, socket) do
{:noreply, load_matrix(socket)}
end
def handle_info({:permission_granted, _role_id, _key}, socket) do
{:noreply, refresh_matrix(socket)}
end
def handle_info({:permission_revoked, _role_id, _key}, socket) do
{:noreply, refresh_matrix(socket)}
end
def handle_info({:permissions_synced, _role_id, _keys}, socket) do
{:noreply, refresh_matrix(socket)}
end
def handle_info({:module_enabled, _key}, socket) do
{:noreply, load_matrix(socket)}
end
def handle_info({:module_disabled, _key}, socket) do
{:noreply, load_matrix(socket)}
end
def handle_info(_msg, socket), do: {:noreply, socket}
# --- Events ---
def handle_event("toggle_permission", %{"role_id" => role_id_str, "key" => key}, socket) do
scope = socket.assigns[:phoenix_kit_current_scope]
with role when not is_nil(role) <-
Enum.find(socket.assigns.roles, &(to_string(&1.uuid) == role_id_str)),
false <- role.name == "Owner",
true <- scope != nil && Scope.has_module_access?(scope, "users") do
grantable =
if Scope.owner?(scope),
do: MapSet.new(Permissions.all_module_keys()),
else: Scope.accessible_modules(scope)
if MapSet.member?(grantable, key) do
granted_by_id = Scope.user_id(scope)
role_uuid = to_string(role.uuid)
role_keys = Map.get(socket.assigns.matrix, role_uuid, MapSet.new())
label = Permissions.module_label(key)
if MapSet.member?(role_keys, key) do
Permissions.revoke_permission(role_uuid, key)
{:noreply,
socket |> put_flash(:info, "Revoked #{label} from #{role.name}") |> refresh_matrix()}
else
Permissions.grant_permission(role_uuid, key, granted_by_id)
{:noreply,
socket |> put_flash(:info, "Granted #{label} to #{role.name}") |> refresh_matrix()}
end
else
{:noreply, put_flash(socket, :error, "You can only manage permissions you have")}
end
else
_ ->
{:noreply, socket}
end
end
# --- Helpers ---
defp load_matrix(socket) do
roles = Roles.list_roles()
matrix = Permissions.get_permissions_matrix()
all_count = length(Permissions.all_module_keys())
# Sort: Owner first, then by permission count descending, then name
sorted_roles =
Enum.sort_by(roles, fn role ->
count =
if role.name == "Owner",
do: all_count,
else: Map.get(matrix, to_string(role.uuid), MapSet.new()) |> MapSet.size()
{role.name != "Owner", -count, role.name}
end)
enabled = Permissions.enabled_module_keys()
enabled_feature_keys =
Enum.filter(Permissions.feature_module_keys(), &MapSet.member?(enabled, &1))
socket
|> assign(:roles, sorted_roles)
|> assign(:matrix, matrix)
|> assign(:core_keys, Permissions.core_section_keys())
|> assign(:feature_keys, enabled_feature_keys)
end
# Refresh matrix data only, keep existing role order stable
defp refresh_matrix(socket) do
matrix = Permissions.get_permissions_matrix()
assign(socket, :matrix, matrix)
end
end