/** * useRoles Hook * * Hook für die Verwaltung von globalen RBAC-Rollen im Admin-Bereich. * Folgt dem gleichen Pattern wie useOrgUsers. */ import { useState, useEffect, useCallback } from 'react'; import { useApiRequest } from './useApi'; import api from '../api'; import { usePermissions, type UserPermissions } from './usePermissions'; import { fetchRoles as fetchRolesApi, fetchRoleById as fetchRoleByIdApi, createRole as createRoleApi, updateRole as updateRoleApi, deleteRole as deleteRoleApi, type Role, type RoleUpdateData, type PaginationParams } from '../api/roleApi'; // Re-export types export type { Role, RoleUpdateData, PaginationParams }; export interface AttributeDefinition { name: string; type: string; label: string; description?: string; required?: boolean; default?: any; options?: Array<{ value: string | number; label: string | { [key: string]: string } }> | string; sortable?: boolean; filterable?: boolean; searchable?: boolean; width?: number; minWidth?: number; maxWidth?: number; readonly?: boolean; editable?: boolean; } /** * Hook for managing RBAC roles in admin panel */ export function useAdminRoles() { const [roles, setRoles] = useState([]); const [attributes, setAttributes] = useState([]); const [permissions, setPermissions] = useState(null); const [pagination, setPagination] = useState<{ currentPage: number; pageSize: number; totalItems: number; totalPages: number; } | null>(null); const { request, isLoading: loading, error } = useApiRequest(); const { checkPermission } = usePermissions(); // Fetch attributes from backend const fetchAttributes = useCallback(async () => { try { const response = await api.get('/api/attributes/Role'); let attrs: AttributeDefinition[] = []; if (response.data?.attributes && Array.isArray(response.data.attributes)) { attrs = response.data.attributes; } else if (Array.isArray(response.data)) { attrs = response.data; } else if (response.data && typeof response.data === 'object') { const keys = Object.keys(response.data); for (const key of keys) { if (Array.isArray(response.data[key])) { attrs = response.data[key]; break; } } } setAttributes(attrs); return attrs; } catch (error: any) { if (error.response?.status === 429) { console.warn('Rate limit exceeded while fetching role attributes.'); } else if (error.response?.status !== 401) { console.error('Error fetching role attributes:', error); } setAttributes([]); return []; } }, []); // Fetch permissions const fetchPermissions = useCallback(async () => { try { const perms = await checkPermission('DATA', 'Role'); setPermissions(perms); return perms; } catch (error: any) { console.error('Error fetching role permissions:', error); const defaultPerms: UserPermissions = { view: false, read: 'n', create: 'n', update: 'n', delete: 'n', }; setPermissions(defaultPerms); return defaultPerms; } }, [checkPermission]); // Fetch roles const fetchRoles = useCallback(async (params?: PaginationParams) => { try { const data = await fetchRolesApi(request, params); if (data && typeof data === 'object' && 'items' in data) { const items = Array.isArray(data.items) ? data.items : []; setRoles(items); if (data.pagination) { setPagination(data.pagination); } } else { const items = Array.isArray(data) ? data : []; setRoles(items); setPagination(null); } } catch (error: any) { setRoles([]); setPagination(null); } }, [request]); // Optimistic updates const removeOptimistically = (roleId: string) => { setRoles(prev => prev.filter(r => r.id !== roleId)); }; const updateOptimistically = (roleId: string, updateData: Partial) => { setRoles(prev => prev.map(r => r.id === roleId ? { ...r, ...updateData } : r) ); }; // Fetch single role const fetchRoleById = useCallback(async (roleId: string): Promise => { return await fetchRoleByIdApi(request, roleId); }, [request]); // Generate columns from attributes (including fkSource/fkDisplayField for FK resolution) const columns = attributes.map(attr => ({ key: attr.name, label: attr.label || attr.name, type: attr.type as any, sortable: attr.sortable !== false, filterable: attr.filterable !== false, searchable: attr.searchable !== false, width: attr.width || 150, minWidth: attr.minWidth || 100, maxWidth: attr.maxWidth || 400, fkSource: (attr as any).fkSource, // API endpoint for FK data fkDisplayField: (attr as any).fkDisplayField, // Which field of FK target to display })); // Create role const handleCreate = useCallback(async (roleData: Partial): Promise => { try { await createRoleApi(request, roleData); await fetchRoles(); return true; } catch (error: any) { console.error('Error creating role:', error); return false; } }, [request, fetchRoles]); // Update role const handleUpdate = useCallback(async (roleId: string, updateData: RoleUpdateData): Promise => { try { updateOptimistically(roleId, updateData); await updateRoleApi(request, roleId, updateData); return true; } catch (error: any) { console.error('Error updating role:', error); await fetchRoles(); return false; } }, [request, fetchRoles]); // Delete role const handleDelete = useCallback(async (roleId: string): Promise => { try { removeOptimistically(roleId); await deleteRoleApi(request, roleId); return true; } catch (error: any) { console.error('Error deleting role:', error); await fetchRoles(); return false; } }, [request, fetchRoles]); // Inline update const handleInlineUpdate = useCallback(async ( roleId: string, updateData: Partial ): Promise => { await handleUpdate(roleId, updateData); }, [handleUpdate]); // Load data on mount useEffect(() => { fetchAttributes(); fetchPermissions(); fetchRoles(); }, []); return { roles, attributes, columns, permissions, pagination, loading, error, refetch: fetchRoles, fetchRoleById, handleCreate, handleUpdate, handleDelete, handleInlineUpdate, updateOptimistically, }; } export default useAdminRoles;