From 635cdde0ebc02988a2c1ac5d4c2c83e26c9733c0 Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:45:53 +0530 Subject: [PATCH] refactor: enhance roles manager component with expanded role details and improved permissions display --- .../components/settings/roles-manager.tsx | 407 +++++++++--------- 1 file changed, 199 insertions(+), 208 deletions(-) diff --git a/surfsense_web/components/settings/roles-manager.tsx b/surfsense_web/components/settings/roles-manager.tsx index 5b30b5f60..b72a53854 100644 --- a/surfsense_web/components/settings/roles-manager.tsx +++ b/surfsense_web/components/settings/roles-manager.tsx @@ -4,6 +4,7 @@ import { useQuery } from "@tanstack/react-query"; import { useAtomValue } from "jotai"; import { Bot, + ChevronDown, Edit2, FileText, Globe, @@ -47,7 +48,6 @@ import { DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, } from "@/components/ui/dialog"; import { DropdownMenu, @@ -58,7 +58,6 @@ import { } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { ScrollArea } from "@/components/ui/scroll-area"; import { Spinner } from "@/components/ui/spinner"; import type { PermissionInfo } from "@/contracts/types/permissions.types"; import type { @@ -319,100 +318,6 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) { ); } -// ============ Role Permissions Display ============ - -function RolePermissionsDialog({ - permissions, - roleName, - children, -}: { - permissions: string[]; - roleName: string; - children: React.ReactNode; -}) { - const isFullAccess = permissions.includes("*"); - - const grouped: Record = {}; - if (!isFullAccess) { - for (const perm of permissions) { - const [category, action] = perm.split(":"); - if (!grouped[category]) grouped[category] = []; - grouped[category].push(action); - } - } - - const sortedCategories = Object.keys(grouped).sort((a, b) => { - const orderA = CATEGORY_CONFIG[a]?.order ?? 99; - const orderB = CATEGORY_CONFIG[b]?.order ?? 99; - return orderA - orderB; - }); - - const categoryCount = sortedCategories.length; - - return ( - - {children} - - - {roleName} — Permissions - - {isFullAccess - ? "This role has unrestricted access to all resources" - : `${permissions.length} permissions across ${categoryCount} categories`} - - - {isFullAccess ? ( -
-
- -
-
-

Full access

-

- All permissions granted across every category -

-
-
- ) : ( - -
- {sortedCategories.map((category) => { - const actions = grouped[category]; - const config = CATEGORY_CONFIG[category] || { - label: category, - icon: FileText, - }; - const IconComponent = config.icon; - return ( -
-
- - {config.label} -
-
- {actions.map((action) => ( - - {ACTION_LABELS[action] || action.replace(/_/g, " ")} - - ))} -
-
- ); - })} -
-
- )} -
-
- ); -} - function PermissionsBadge({ permissions }: { permissions: string[] }) { if (permissions.includes("*")) { return ( @@ -463,6 +368,7 @@ function RolesContent({ }) { const [showCreateRole, setShowCreateRole] = useState(false); const [editingRoleId, setEditingRoleId] = useState(null); + const [expandedRoleId, setExpandedRoleId] = useState(null); if (loading) { return ( @@ -508,91 +414,170 @@ function RolesContent({ )}
- {roles.map((role) => ( -
-
-
- - - + {role.is_default && ( + + Default + + )} +
+ {role.description && ( +

+ {role.description} +

+ )} + + +
+ +
+ + {!role.is_system_role && ( +
+ + + + + e.preventDefault()}> + {canUpdate && ( + setEditingRoleId(role.id)}> + + Edit Role + + )} + {canDelete && ( + <> + + + + e.preventDefault()}> + + Delete Role + + + + + Delete role? + + This will permanently delete the "{role.name}" role. + Members with this role will lose their permissions. + + + + Cancel + onDeleteRole(role.id)} + className="bg-destructive text-destructive-foreground hover:bg-destructive/90" + > + Delete + + + + + + )} + + +
+ )} + +
-
- -
- - {!role.is_system_role && ( -
- - - - - e.preventDefault()}> - {canUpdate && ( - setEditingRoleId(role.id)}> - - Edit Role - - )} - {canDelete && ( - <> - - - - e.preventDefault()}> - - Delete Role - - - - - Delete role? - - This will permanently delete the "{role.name}" role. - Members with this role will lose their permissions. - - - - Cancel - onDeleteRole(role.id)} - className="bg-destructive text-destructive-foreground hover:bg-destructive/90" + {isExpanded && ( +
+ {isFullAccess ? ( +
+ +

+ Full access — all permissions granted across every category +

+
+ ) : ( +
+ {sortedCategories.map((category) => { + const actions = grouped[category]; + const config = CATEGORY_CONFIG[category] || { + label: category, + icon: FileText, + }; + const IconComponent = config.icon; + return ( +
+
+ + + {config.label} + +
+
+ {actions.map((action) => ( + - Delete - - - - - - )} - - + {ACTION_LABELS[action] || action.replace(/_/g, " ")} + + ))} +
+
+ ); + })} +
+ )}
)}
-
- ))} + ); + })}
); @@ -676,46 +661,51 @@ function PermissionsEditor({ return (
-
+
onToggleCategory(category)} - onClick={(e) => e.stopPropagation()} aria-label={`Select all ${config.label} permissions`} /> -
toggleCategoryExpanded(category)} > - -
+ +
+ - + {isExpanded && (
@@ -726,28 +716,29 @@ function PermissionsEditor({ const isSelected = selectedPermissions.includes(perm.value); return ( -
+ onTogglePermission(perm.value)} - onClick={(e) => e.stopPropagation()} className="shrink-0" /> - + ); })}