diff --git a/src/api/rbac.api.ts b/src/api/rbac.api.ts index 160fc18..f14fea8 100644 --- a/src/api/rbac.api.ts +++ b/src/api/rbac.api.ts @@ -5,6 +5,7 @@ import type { GetRolesParams, CreateRoleRequest, CreateRoleResponse, + DeleteRoleResponse, SetRolePermissionsRequest, GetPermissionsResponse, } from "../types/rbac.types" @@ -26,3 +27,6 @@ export const setRolePermissions = (roleId: number, data: SetRolePermissionsReque export const getAllPermissions = () => http.get("/rbac/permissions") + +export const deleteRole = (roleId: number) => + http.delete(`/rbac/roles/${roleId}`) diff --git a/src/pages/role-management/RolesListPage.tsx b/src/pages/role-management/RolesListPage.tsx index 2d2107b..fc4140e 100644 --- a/src/pages/role-management/RolesListPage.tsx +++ b/src/pages/role-management/RolesListPage.tsx @@ -1,8 +1,18 @@ import { useEffect, useMemo, useState } from "react" import { useNavigate } from "react-router-dom" import { - Plus, Search, Shield, ShieldCheck, ChevronLeft, ChevronRight, - AlertCircle, Eye, X, Pencil, Check, + Plus, + Search, + Shield, + ShieldCheck, + ChevronLeft, + ChevronRight, + AlertCircle, + Eye, + X, + Pencil, + Check, + Trash2, } from "lucide-react" import { Button } from "../../components/ui/button" import { Card, CardContent } from "../../components/ui/card" @@ -12,7 +22,14 @@ import { Textarea } from "../../components/ui/textarea" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from "../../components/ui/dialog" -import { getRoles, getRoleDetail, getAllPermissions, setRolePermissions, updateRole } from "../../api/rbac.api" +import { + getRoles, + getRoleDetail, + getAllPermissions, + setRolePermissions, + updateRole, + deleteRole, +} from "../../api/rbac.api" import type { Role, RoleDetail, RolePermission } from "../../types/rbac.types" import { cn } from "../../lib/utils" import { toast } from "sonner" @@ -36,6 +53,11 @@ export function RolesListPage() { const [detailOpen, setDetailOpen] = useState(false) const [detailLoading, setDetailLoading] = useState(false) + // Delete modal state + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) + const [roleToDelete, setRoleToDelete] = useState(null) + const [deleteLoading, setDeleteLoading] = useState(false) + // Role info editing state const [editingRole, setEditingRole] = useState(false) const [editName, setEditName] = useState("") @@ -97,6 +119,45 @@ export function RolesListPage() { } } + const handleDeleteRoleClick = (role: Role) => { + setRoleToDelete(role) + setDeleteDialogOpen(true) + } + + const handleCancelDeleteRole = () => { + setDeleteDialogOpen(false) + setRoleToDelete(null) + } + + const handleConfirmDeleteRole = async () => { + if (!roleToDelete) return + setDeleteLoading(true) + try { + const res = await deleteRole(roleToDelete.id) + toast.success(res.data.message ?? "Role deleted successfully") + + // Close dialogs if the deleted role is currently opened. + if (selectedRole?.id === roleToDelete.id) { + setDetailOpen(false) + setSelectedRole(null) + setEditingPermissions(false) + setEditingRole(false) + setPermSearch("") + } + + setRoleToDelete(null) + setDeleteDialogOpen(false) + setPage(1) // trigger list refresh via the existing effect + } catch (err: unknown) { + const message = + (err as { response?: { data?: { message?: string } } })?.response?.data?.message ?? + "Failed to delete role." + toast.error(message) + } finally { + setDeleteLoading(false) + } + } + // Enter role info edit mode const handleEditRole = () => { if (!selectedRole) return @@ -360,16 +421,33 @@ export function RolesListPage() {
- Open details to view permissions - + + Open details to view permissions + +
+ {!role.is_system && ( + + )} + +
@@ -703,6 +781,55 @@ export function RolesListPage() { )} + + {/* Delete role dialog */} + { + setDeleteDialogOpen(open) + if (!open) handleCancelDeleteRole() + }} + > + + + + + Delete Role + + + Are you sure you want to delete this role? This action cannot be undone. + + + + {roleToDelete && ( +
+

{roleToDelete.name}

+

Role #{roleToDelete.id}

+
+ )} + +
+ + +
+
+
) } diff --git a/src/types/rbac.types.ts b/src/types/rbac.types.ts index 28721f1..b814aa0 100644 --- a/src/types/rbac.types.ts +++ b/src/types/rbac.types.ts @@ -60,6 +60,14 @@ export interface CreateRoleResponse { metadata: unknown } +export interface DeleteRoleResponse { + message: string + success: boolean + status_code: number + // Some backends may include extra fields; keep it optional for compatibility. + metadata?: unknown +} + export interface SetRolePermissionsRequest { permission_ids: number[] }