'use client'; import { useState } from 'react'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Checkbox } from '@/components/ui/checkbox'; import { Button } from '@/components/ui/button'; import { RefreshCw, Save } from 'lucide-react'; import { toast } from 'sonner'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import apiClient from '@/lib/api/client'; interface Role { roleId: number; roleName: string; permissions?: Permission[]; } interface Permission { permissionId: number; permissionName: string; description: string; } interface _RbacMatrixProps { roles: Role[]; permissions: Permission[]; rolePermissions: Record; // roleId -> permissionIds[] } const extractArrayData = (value: unknown): T[] => { let current: unknown = value; for (let i = 0; i < 5; i += 1) { if (Array.isArray(current)) { return current as T[]; } if (!current || typeof current !== 'object' || !('data' in current)) { return []; } current = (current as { data?: unknown }).data; } return Array.isArray(current) ? (current as T[]) : []; }; const securityService = { getRoles: async (): Promise => { const response = await apiClient.get('/users/roles'); return extractArrayData(response.data); }, getPermissions: async (): Promise => { const response = await apiClient.get('/users/permissions'); return extractArrayData(response.data); }, updateRolePermissions: async (roleId: number, permissionIds: number[]) => { // This endpoint might not exist as a bulk update, usually it's per role // Assuming backend supports: PATCH /users/roles/:id/permissions { permissionIds: [] } return (await apiClient.patch(`/users/roles/${roleId}/permissions`, { permissionIds })).data; }, }; export function RbacMatrix() { const queryClient = useQueryClient(); const [pendingChanges, setPendingChanges] = useState>({}); const { data: roles = [], isLoading: rolesLoading } = useQuery({ queryKey: ['roles'], queryFn: securityService.getRoles, }); const { data: permissions = [], isLoading: permsLoading } = useQuery({ queryKey: ['permissions'], queryFn: securityService.getPermissions, }); // Fetch current assignments - this logic assumes we can get a map or list // For now, let's assume we can fetch matrix or individual role calls // In a real implementation this is heavier. For implementation speed, I'll mock the state logic assumption // that we load initial state from roles (if roles include permissions relation). // TODO: Fetch existing role_permissions. Assuming roles endpoint returns `permissions` array. const updateMutation = useMutation({ mutationFn: async (changes: Record) => { const promises = Object.entries(changes).map(([roleId, perms]) => securityService.updateRolePermissions(Number(roleId), perms) ); return Promise.all(promises); }, onSuccess: () => { toast.success('Permissions updated successfully'); setPendingChanges({}); queryClient.invalidateQueries({ queryKey: ['roles'] }); }, onError: () => toast.error('Failed to update permissions'), }); const handleToggle = (roleId: number, permId: number, currentPerms: number[]) => { const roleChanges = pendingChanges[roleId] || currentPerms; const newPerms = roleChanges.includes(permId) ? roleChanges.filter((id) => id !== permId) : [...roleChanges, permId]; setPendingChanges({ ...pendingChanges, [roleId]: newPerms }); }; const hasChanges = Object.keys(pendingChanges).length > 0; const roleList = Array.isArray(roles) ? roles : []; const permissionList = Array.isArray(permissions) ? permissions : []; if (rolesLoading || permsLoading) { return (
); } // Simplified: Permissions grouped by module/resource would be better, but flat list for now return (
Permission {roleList.map((role) => ( {role.roleName} ))} {permissionList.map((perm) => (
{perm.permissionName}
{perm.description}
{roleList.map((role) => { // Assume role.permissions is populated const currentRolePerms = role.permissions?.map((p) => p.permissionId) || []; const activePerms = pendingChanges[role.publicId] || currentRolePerms; const isChecked = activePerms.includes(perm.permissionId); return ( handleToggle(role.publicId, perm.permissionId, currentRolePerms)} /> ); })}
))}
); }