Packages

phoenix_kit

1.2.5
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 roles_live.html.heex
Raw

lib/phoenix_kit_web/live/roles_live.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="Role Management"
current_path={@current_path}
project_title={@project_title}
>
<div class="container flex-col mx-auto px-4 py-6">
<%!-- Header Section --%>
<header class="w-full relative mb-6">
<%!-- Back Button (Left aligned) --%>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/dashboard")}
class="btn btn-outline btn-primary btn-sm absolute left-0 top-0 -mb-12"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 19l-7-7m0 0l7-7m-7 7h18"
>
</path>
</svg>
Back to Dashboard
</.link>
<%!-- Title Section --%>
<div class="text-center">
<h1 class="text-4xl font-bold text-base-content mb-3">Role Management</h1>
<p class="text-lg text-base-content">Create and manage user roles</p>
</div>
</header>
<%!-- Action Button --%>
<div class="flex justify-center mb-6">
<button
phx-click="show_create_form"
class="btn btn-primary btn-lg"
>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
>
</path>
</svg>
Create New Role
</button>
</div>
<%!-- Role Creation/Edit Form Modal --%>
<%= if @show_form do %>
<div class="modal modal-open">
<div class="modal-box max-w-2xl">
<h3 class="font-bold text-xl mb-4">
{if @editing_role, do: "Edit Role", else: "Create New Role"}
</h3>
<form
phx-submit={if @editing_role, do: "update_role", else: "create_role"}
class="space-y-4"
>
<div class="form-control">
<label class="label">
<span class="label-text">Role Name *</span>
</label>
<input
type="text"
name="role[name]"
value={@form_data["name"]}
placeholder="Enter role name (e.g., Manager, Editor)"
class="input input-bordered w-full"
required
maxlength="50"
/>
<label class="label">
<span class="label-text-alt">Must be unique, 1-50 characters</span>
</label>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Description</span>
</label>
<textarea
name="role[description]"
placeholder="Describe what this role can do (optional)"
class="textarea textarea-bordered w-full h-24"
maxlength="500"
><%= @form_data["description"] %></textarea>
<label class="label">
<span class="label-text-alt">Optional, up to 500 characters</span>
</label>
</div>
<div class="modal-action">
<button type="button" phx-click="hide_form" class="btn btn-outline">
Cancel
</button>
<button type="submit" class="btn btn-primary">
{if @editing_role, do: "Update Role", else: "Create Role"}
</button>
</div>
</form>
</div>
</div>
<% end %>
<%!-- Roles List --%>
<div class="bg-base-100 rounded-lg shadow-md overflow-hidden">
<%= if length(@roles) > 0 do %>
<div class="overflow-x-auto">
<table class="table table-zebra w-full">
<thead>
<tr>
<th>Role</th>
<th>Type</th>
<th>Description</th>
<th>Users</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<%= for role <- @roles do %>
<tr>
<td>
<div class="flex items-center gap-2">
<span class={"badge #{role_badge_class(role)}"}>
{role.name}
</span>
</div>
</td>
<td>
<%= if role.is_system_role do %>
<span class="badge badge-outline badge-sm">System Role</span>
<% else %>
<span class="badge badge-success badge-sm">Custom Role</span>
<% end %>
</td>
<td class="max-w-xs">
<div class="text-sm">
<%= if role.description do %>
{role.description}
<% else %>
<span class="text-base-content/50 italic">No description</span>
<% end %>
</div>
</td>
<td>
<div class="flex items-center gap-2">
<span class="font-medium">{users_count_for_role(role, @role_stats)}</span>
<span class="text-sm text-base-content/70">users</span>
</div>
</td>
<td>
<div class="flex flex-wrap gap-1">
<%= if !role.is_system_role do %>
<%!-- Edit Button --%>
<button
class="btn btn-xs btn-outline btn-primary"
phx-click="edit_role"
phx-value-role_id={role.id}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 mr-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
Edit
</button>
<%!-- Delete Button --%>
<button
class="btn btn-xs btn-outline btn-error"
phx-click="delete_role"
phx-value-role_id={role.id}
data-confirm="Are you sure you want to delete this role? This action cannot be undone."
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 mr-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
Delete
</button>
<% else %>
<span class="text-xs text-base-content/50">Protected</span>
<% end %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% else %>
<%!-- Empty state --%>
<div class="text-center py-12">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-16 w-16 mx-auto text-base-content/40 mb-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"
/>
</svg>
<h3 class="text-lg font-medium text-base-content mb-2">
No roles found
</h3>
<p class="text-base-content/70 mb-4">
Start by creating your first custom role.
</p>
<button
phx-click="show_create_form"
class="btn btn-primary"
>
Create First Role
</button>
</div>
<% end %>
</div>
<%!-- Role Statistics --%>
<div class="mt-8">
<h3 class="text-lg font-semibold text-base-content mb-6">Role Statistics</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<%!-- System Roles Count --%>
<div class="bg-gradient-to-br from-blue-500 to-blue-600 text-white rounded-xl p-4 shadow-lg">
<div class="flex items-center gap-3">
<div class="p-2 bg-white/20 rounded-lg">
🔒
</div>
<div>
<div class="text-2xl font-bold">
{Enum.count(@roles, & &1.is_system_role)}
</div>
<div class="text-sm opacity-90">System Roles</div>
</div>
</div>
</div>
<%!-- Custom Roles Count --%>
<div class="bg-gradient-to-br from-purple-500 to-purple-600 text-white rounded-xl p-4 shadow-lg">
<div class="flex items-center gap-3">
<div class="p-2 bg-white/20 rounded-lg">
⚙️
</div>
<div>
<div class="text-2xl font-bold">
{Enum.count(@roles, &(!&1.is_system_role))}
</div>
<div class="text-sm opacity-90">Custom Roles</div>
</div>
</div>
</div>
<%!-- Total Roles Count --%>
<div class="bg-gradient-to-br from-green-500 to-green-600 text-white rounded-xl p-4 shadow-lg">
<div class="flex items-center gap-3">
<div class="p-2 bg-white/20 rounded-lg">
📊
</div>
<div>
<div class="text-2xl font-bold">{length(@roles)}</div>
<div class="text-sm opacity-90">Total Roles</div>
</div>
</div>
</div>
</div>
</div>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>