diff --git a/frontend/src/pages/SettingsRoles.tsx b/frontend/src/pages/SettingsRoles.tsx index 7b99e72..09dfa98 100644 --- a/frontend/src/pages/SettingsRoles.tsx +++ b/frontend/src/pages/SettingsRoles.tsx @@ -1,17 +1,21 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useRoles, useCreateRole, useUpdateRole, useDeleteRole } from '@/api/hooks'; import { Input } from '@/components/ui/Input'; import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import { Badge } from '@/components/ui/Badge'; import { EmptyState } from '@/components/ui/EmptyState'; +import { Skeleton } from '@/components/ui/Skeleton'; import { useToast } from '@/components/ui/Toast'; import { Modal } from '@/components/ui/Modal'; +import { ConfirmDialog } from '@/components/ui/ConfirmDialog'; interface Role { id: string; name: string; - permissions: string[]; + permissions: Record; + field_permissions?: Record; } const ALL_PERMISSIONS = [ @@ -25,59 +29,142 @@ const ALL_PERMISSIONS = [ { key: 'settings:write', label: 'Einstellungen bearbeiten' }, ]; +function isPermissionGranted(permissions: Record, permKey: string): boolean { + if (!permissions) return false; + if (typeof permissions === 'boolean') return permissions; + if (Array.isArray(permissions)) return permissions.includes(permKey); + if (typeof permissions === 'object') { + if (permKey in permissions) { + const val = permissions[permKey]; + return val === true || val === 'true' || val === 1; + } + if ('all' in permissions && permissions.all === true) return true; + } + return false; +} + +function togglePermission( + permissions: Record, + permKey: string +): Record { + const current = isPermissionGranted(permissions, permKey); + return { ...permissions, [permKey]: !current }; +} + +function getGrantedPermissionKeys(permissions: Record): string[] { + if (!permissions) return []; + if (Array.isArray(permissions)) return permissions as string[]; + if (typeof permissions === 'object') { + return Object.keys(permissions).filter( + (k) => k !== 'all' && permissions[k] === true + ); + } + return []; +} + export function SettingsRolesPage() { const { t } = useTranslation(); const toast = useToast(); + const { data, isLoading, isError, error, refetch } = useRoles(); + const createRoleMutation = useCreateRole(); + const updateRoleMutation = useUpdateRole(); + const deleteRoleMutation = useDeleteRole(); - const [roles, setRoles] = useState([ - { id: '1', name: 'Administrator', permissions: ALL_PERMISSIONS.map((p) => p.key) }, - { id: '2', name: 'Mitarbeiter', permissions: ['companies:read', 'companies:write', 'contacts:read', 'contacts:write'] }, - { id: '3', name: 'Gast', permissions: ['companies:read', 'contacts:read'] }, - ]); const [createOpen, setCreateOpen] = useState(false); const [newRoleName, setNewRoleName] = useState(''); - const [newRolePermissions, setNewRolePermissions] = useState([]); + const [newRolePermissions, setNewRolePermissions] = useState>({}); const [editingRole, setEditingRole] = useState(null); + const [confirmDelete, setConfirmDelete] = useState(null); - const handleCreateRole = () => { + const roles: Role[] = data?.items ?? []; + + const handleCreateRole = async () => { if (!newRoleName.trim()) { toast.error(t('validation.required')); return; } - const role: Role = { - id: Date.now().toString(), - name: newRoleName.trim(), - permissions: newRolePermissions, - }; - setRoles((prev) => [...prev, role]); - toast.success(t('settings.roleCreated')); - setNewRoleName(''); - setNewRolePermissions([]); - setCreateOpen(false); + try { + await createRoleMutation.mutateAsync({ + name: newRoleName.trim(), + permissions: newRolePermissions, + field_permissions: {}, + }); + toast.success(t('settings.roleCreated')); + setNewRoleName(''); + setNewRolePermissions({}); + setCreateOpen(false); + } catch (err: any) { + toast.error(err.message || t('common.error')); + } }; const handleTogglePermission = (permKey: string, target: 'new' | 'edit') => { if (target === 'new') { - setNewRolePermissions((prev) => - prev.includes(permKey) ? prev.filter((p) => p !== permKey) : [...prev, permKey] - ); + setNewRolePermissions((prev) => togglePermission(prev, permKey)); } else if (editingRole) { setEditingRole({ ...editingRole, - permissions: editingRole.permissions.includes(permKey) - ? editingRole.permissions.filter((p) => p !== permKey) - : [...editingRole.permissions, permKey], + permissions: togglePermission(editingRole.permissions, permKey), }); } }; - const handleSaveRole = () => { + const handleSaveRole = async () => { if (!editingRole) return; - setRoles((prev) => prev.map((r) => (r.id === editingRole.id ? editingRole : r))); - toast.success(t('settings.roleUpdated')); - setEditingRole(null); + try { + await updateRoleMutation.mutateAsync({ + id: editingRole.id, + data: { + name: editingRole.name, + permissions: editingRole.permissions, + field_permissions: editingRole.field_permissions || {}, + }, + }); + toast.success(t('settings.roleUpdated')); + setEditingRole(null); + } catch (err: any) { + toast.error(err.message || t('common.error')); + } }; + const handleDeleteRole = async () => { + if (!confirmDelete) return; + try { + await deleteRoleMutation.mutateAsync(confirmDelete.id); + toast.success(t('settings.roleDeleted')); + setConfirmDelete(null); + } catch (err: any) { + toast.error(err.message || t('common.error')); + } + }; + + if (isLoading) { + return ( +
+ +
+ + + +
+
+ ); + } + + if (isError) { + return ( +
+

{t('settings.roles')}

+ refetch()}>{t('common.reset')}} + > +

{error?.message || t('common.error')}

+
+
+ ); + } + return (
@@ -94,31 +181,44 @@ export function SettingsRolesPage() { /> ) : (
- {roles.map((role) => ( - -
-

{role.name}

- -
-
- {role.permissions.length === 0 ? ( - - ) : ( - role.permissions.map((perm) => { - const label = ALL_PERMISSIONS.find((p) => p.key === perm)?.label || perm; - return {label}; - }) - )} -
-
- ))} + {roles.map((role) => { + const grantedKeys = getGrantedPermissionKeys(role.permissions); + return ( + +
+

{role.name}

+
+ + +
+
+
+ {grantedKeys.length === 0 ? ( + + ) : ( + grantedKeys.map((perm) => { + const label = ALL_PERMISSIONS.find((p) => p.key === perm)?.label || perm; + return {label}; + }) + )} +
+
+ ); + })}
)} @@ -138,7 +238,7 @@ export function SettingsRolesPage() {
- +
@@ -169,7 +275,7 @@ export function SettingsRolesPage() {