diff --git a/ui/src/pages/admin/rbac/RolesTab.tsx b/ui/src/pages/admin/rbac/RolesTab.tsx index 8ba2517d..f1a9a335 100644 --- a/ui/src/pages/admin/rbac/RolesTab.tsx +++ b/ui/src/pages/admin/rbac/RolesTab.tsx @@ -1,6 +1,7 @@ import { useState, useMemo } from 'react'; -import { useRoles, useRole } from '../../../api/queries/admin/rbac'; +import { useRoles, useRole, useCreateRole, useDeleteRole } from '../../../api/queries/admin/rbac'; import type { RoleDetail } from '../../../api/queries/admin/rbac'; +import { ConfirmDeleteDialog } from '../../../components/admin/ConfirmDeleteDialog'; import styles from './RbacPage.module.css'; function getInitials(name: string): string { @@ -19,6 +20,12 @@ export function RolesTab() { const roles = useRoles(); const [selectedId, setSelectedId] = useState(null); const [filter, setFilter] = useState(''); + const [showCreateForm, setShowCreateForm] = useState(false); + const [newName, setNewName] = useState(''); + const [newDesc, setNewDesc] = useState(''); + const [newScope, setNewScope] = useState('custom'); + const [createError, setCreateError] = useState(''); + const createRole = useCreateRole(); const roleDetail = useRole(selectedId); @@ -48,6 +55,7 @@ export function RolesTab() { Define permission scopes; assign to users or groups +
@@ -59,6 +67,39 @@ export function RolesTab() { onChange={(e) => setFilter(e.target.value)} />
+ {showCreateForm && ( +
+
+ + { setNewName(e.target.value); setCreateError(''); }} + placeholder="Role name" autoFocus /> +
+
+ + setNewDesc(e.target.value)} placeholder="Optional description" /> +
+
+ + setNewScope(e.target.value)} placeholder="custom" /> +
+ {createError &&
{createError}
} +
+ + +
+
+ )}
{filtered.map((role) => { const isSelected = role.id === selectedId; @@ -101,7 +142,7 @@ export function RolesTab() { Select a role to view details
) : ( - + setSelectedId(null)} /> )}
@@ -109,15 +150,26 @@ export function RolesTab() { ); } -function RoleDetailView({ role }: { role: RoleDetail }) { +function RoleDetailView({ role, onDeselect }: { role: RoleDetail; onDeselect: () => void }) { + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const deleteRole = useDeleteRole(); + return ( <> -
- {getInitials(role.name)} -
-
- {role.name} - {role.system && 🔒} +
+
+
+ {getInitials(role.name)} +
+
+ {role.name} + {role.system && 🔒} +
+
+ {!role.system && ( + + )}
{role.description || 'No description'}
@@ -196,6 +248,12 @@ function RoleDetailView({ role }: { role: RoleDetail }) { )}
+ + {!role.system && ( + setShowDeleteDialog(false)} + onConfirm={() => { deleteRole.mutate(role.id, { onSuccess: () => { setShowDeleteDialog(false); onDeselect(); } }); }} + resourceName={role.name} resourceType="role" /> + )} ); }