237 lines
6.7 KiB
TypeScript
237 lines
6.7 KiB
TypeScript
/**
|
|
* 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<Role[]>([]);
|
|
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
|
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
|
const [pagination, setPagination] = useState<{
|
|
currentPage: number;
|
|
pageSize: number;
|
|
totalItems: number;
|
|
totalPages: number;
|
|
} | null>(null);
|
|
const { request, isLoading: loading, error } = useApiRequest<null, Role[]>();
|
|
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<Role>) => {
|
|
setRoles(prev =>
|
|
prev.map(r => r.id === roleId ? { ...r, ...updateData } : r)
|
|
);
|
|
};
|
|
|
|
// Fetch single role
|
|
const fetchRoleById = useCallback(async (roleId: string): Promise<Role | null> => {
|
|
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<Role>): Promise<boolean> => {
|
|
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<boolean> => {
|
|
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<boolean> => {
|
|
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<Role>
|
|
): Promise<void> => {
|
|
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;
|