rights system

This commit is contained in:
Matthias Hochmeister
2026-03-23 12:35:28 +01:00
parent 725d4d1729
commit 83b84664ce
2 changed files with 75 additions and 32 deletions

View File

@@ -10,6 +10,11 @@ import {
Chip,
CircularProgress,
Collapse,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
FormControlLabel,
IconButton,
Switch,
@@ -109,6 +114,7 @@ function PermissionMatrixTab() {
const [expandedGroups, setExpandedGroups] = useState<Record<string, boolean>>({});
const [showDepEditor, setShowDepEditor] = useState(false);
const [deleteGroupConfirm, setDeleteGroupConfirm] = useState<string | null>(null);
const toggleGroup = (groupId: string) => {
setExpandedGroups(prev => ({ ...prev, [groupId]: !prev[groupId] }));
@@ -137,16 +143,6 @@ function PermissionMatrixTab() {
onError: () => showError('Fehler beim Speichern der Berechtigungen'),
});
const addGroupMutation = useMutation({
mutationFn: (groupName: string) => permissionsApi.setGroupPermissions(groupName, []),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin-permission-matrix'] });
queryClient.invalidateQueries({ queryKey: ['admin-unknown-groups'] });
showSuccess('Gruppe hinzugefügt');
},
onError: () => showError('Fehler beim Hinzufügen der Gruppe'),
});
const deleteGroupMutation = useMutation({
mutationFn: (groupName: string) => permissionsApi.deleteGroup(groupName),
onSuccess: () => {
@@ -290,18 +286,10 @@ function PermissionMatrixTab() {
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
{/* Unknown Groups Alert */}
{unknownGroups && unknownGroups.length > 0 && (
<Alert severity="warning" sx={{ alignItems: 'center' }}>
<Typography variant="body2" sx={{ mb: 1 }}>
Folgende Gruppen wurden in der Benutzertabelle gefunden, sind aber noch nicht in der Berechtigungsmatrix:
<Alert severity="info" sx={{ alignItems: 'center' }}>
<Typography variant="body2">
Folgende Gruppen haben noch keine Berechtigungen zugewiesen: <strong>{unknownGroups.join(', ')}</strong>
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{unknownGroups.map(g => (
<Button key={g} variant="outlined" size="small" startIcon={<AddIcon />}
onClick={() => addGroupMutation.mutate(g)} disabled={addGroupMutation.isPending}>
{g} hinzufügen
</Button>
))}
</Box>
</Alert>
)}
@@ -339,7 +327,7 @@ function PermissionMatrixTab() {
<DependencyEditor
groupHierarchy={groupHierarchy}
permissionDeps={permissionDeps}
allGroups={[...nonAdminGroups, ...groups.filter(g => g === 'dashboard_admin')]}
allGroups={nonAdminGroups}
allPermissions={permissions}
onSave={(config) => depConfigMutation.mutate(config)}
isSaving={depConfigMutation.isPending}
@@ -376,11 +364,8 @@ function PermissionMatrixTab() {
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 0.5 }}>
{g.replace('dashboard_', '')}
<Tooltip title={`Gruppe "${g}" entfernen`} placement="top">
<IconButton size="small" onClick={() => {
if (window.confirm(`Gruppe "${g}" und alle zugehörigen Berechtigungen wirklich entfernen?`)) {
deleteGroupMutation.mutate(g);
}
}} sx={{ opacity: 0.4, '&:hover': { opacity: 1, color: 'error.main' } }}>
<IconButton size="small" onClick={() => setDeleteGroupConfirm(g)}
sx={{ opacity: 0.4, '&:hover': { opacity: 1, color: 'error.main' } }}>
<DeleteIcon sx={{ fontSize: 14 }} />
</IconButton>
</Tooltip>
@@ -471,6 +456,33 @@ function PermissionMatrixTab() {
</TableContainer>
</CardContent>
</Card>
{/* Delete Group Confirmation Dialog */}
<Dialog open={!!deleteGroupConfirm} onClose={() => setDeleteGroupConfirm(null)}>
<DialogTitle>Gruppe entfernen</DialogTitle>
<DialogContent>
<DialogContentText>
Soll die Gruppe &quot;{deleteGroupConfirm}&quot; und alle zugehörigen Berechtigungen
wirklich entfernt werden? Diese Aktion kann nicht rückgängig gemacht werden.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => setDeleteGroupConfirm(null)}>Abbrechen</Button>
<Button
variant="contained"
color="error"
onClick={() => {
if (deleteGroupConfirm) {
deleteGroupMutation.mutate(deleteGroupConfirm);
setDeleteGroupConfirm(null);
}
}}
disabled={deleteGroupMutation.isPending}
>
{deleteGroupMutation.isPending ? <CircularProgress size={20} /> : 'Entfernen'}
</Button>
</DialogActions>
</Dialog>
</Box>
);
}
@@ -487,8 +499,28 @@ interface DependencyEditorProps {
}
function DependencyEditor({ groupHierarchy, permissionDeps, allGroups, allPermissions, onSave, isSaving }: DependencyEditorProps) {
const [editHierarchy, setEditHierarchy] = useState<Record<string, string[]>>(() => ({ ...groupHierarchy }));
const [editDeps, setEditDeps] = useState<Record<string, string[]>>(() => ({ ...permissionDeps }));
const groupSet = useMemo(() => new Set(allGroups), [allGroups]);
const permIdSet = useMemo(() => new Set(allPermissions.map(p => p.id)), [allPermissions]);
// Filter saved config to only include groups/permissions that actually exist
const [editHierarchy, setEditHierarchy] = useState<Record<string, string[]>>(() => {
const filtered: Record<string, string[]> = {};
for (const [g, inheritors] of Object.entries(groupHierarchy)) {
if (groupSet.has(g)) {
filtered[g] = inheritors.filter(i => groupSet.has(i));
}
}
return filtered;
});
const [editDeps, setEditDeps] = useState<Record<string, string[]>>(() => {
const filtered: Record<string, string[]> = {};
for (const [p, deps] of Object.entries(permissionDeps)) {
if (permIdSet.has(p)) {
filtered[p] = deps.filter(d => permIdSet.has(d));
}
}
return filtered;
});
const nonAdminGroups = allGroups.filter(g => g !== 'dashboard_admin');
const permOptions = allPermissions.map(p => p.id);