import { useEffect, useMemo, useState } from "react" import { ArrowLeft, Loader2, Search, X, Check } from "lucide-react" import { useNavigate } from "react-router-dom" import { Button } from "../../components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card" import { Input } from "../../components/ui/input" import { Textarea } from "../../components/ui/textarea" import { Badge } from "../../components/ui/badge" import { createRole, setRolePermissions, getAllPermissions } from "../../api/rbac.api" import type { RolePermission } from "../../types/rbac.types" import { cn } from "../../lib/utils" import { toast } from "sonner" export function AddRolePage() { const navigate = useNavigate() const [roleName, setRoleName] = useState("") const [roleDescription, setRoleDescription] = useState("") const [selectedPermissionIds, setSelectedPermissionIds] = useState>(new Set()) // Permissions from API (already grouped by group_name) const [permissionsMap, setPermissionsMap] = useState>({}) const [permLoading, setPermLoading] = useState(true) const [permSearch, setPermSearch] = useState("") const [saving, setSaving] = useState(false) // Load all available permissions useEffect(() => { const fetch = async () => { setPermLoading(true) try { const res = await getAllPermissions() setPermissionsMap(res.data.data ?? {}) } catch { toast.error("Failed to load permissions.") } finally { setPermLoading(false) } } fetch() }, []) // Flat list of all permissions (for select-all / count) const allPermissions = useMemo( () => Object.values(permissionsMap).flat(), [permissionsMap], ) // Filtered & sorted groups const permissionGroups = useMemo(() => { const q = permSearch.toLowerCase() const entries: [string, RolePermission[]][] = [] for (const [groupName, perms] of Object.entries(permissionsMap)) { const filtered = q ? perms.filter( (p) => p.name.toLowerCase().includes(q) || p.key.toLowerCase().includes(q) || groupName.toLowerCase().includes(q), ) : perms if (filtered.length > 0) entries.push([groupName, filtered]) } return entries.sort(([a], [b]) => a.localeCompare(b)) }, [permissionsMap, permSearch]) const togglePermission = (id: number) => { setSelectedPermissionIds((prev) => { const next = new Set(prev) if (next.has(id)) next.delete(id) else next.add(id) return next }) } const toggleGroup = (perms: RolePermission[]) => { const allSelected = perms.every((p) => selectedPermissionIds.has(p.id)) setSelectedPermissionIds((prev) => { const next = new Set(prev) for (const p of perms) { if (allSelected) next.delete(p.id) else next.add(p.id) } return next }) } const selectAll = () => { setSelectedPermissionIds(new Set(allPermissions.map((p) => p.id))) } const clearAll = () => { setSelectedPermissionIds(new Set()) } const handleSubmit = async () => { if (!roleName.trim()) { toast.error("Role name is required.") return } setSaving(true) try { // 1. Create the role const res = await createRole({ name: roleName.trim(), description: roleDescription.trim(), }) const newRoleId = res.data.data.id // 2. Assign permissions if any selected if (selectedPermissionIds.size > 0) { await setRolePermissions(newRoleId, { permission_ids: Array.from(selectedPermissionIds), }) } toast.success(`Role "${res.data.data.name}" created successfully.`) navigate("/roles") } catch (err: unknown) { const message = (err as { response?: { data?: { message?: string } } })?.response?.data?.message ?? "Failed to create role." toast.error(message) } finally { setSaving(false) } } return (
{/* Header */}

Add New Role

Create a role and assign permissions.

{/* Left – Role info */} Role Information
setRoleName(e.target.value)} placeholder="e.g. CONTENT_MANAGER" />