Problem 3/4: Replace hardcoded ALL_PERMISSIONS with usePermissions API call, group by category

This commit is contained in:
2026-07-03 20:07:53 +00:00
parent 68b5153139
commit 493d99aff2
+70 -36
View File
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useRoles, useCreateRole, useUpdateRole, useDeleteRole } from '@/api/hooks';
import { useRoles, useCreateRole, useUpdateRole, useDeleteRole, usePermissions } from '@/api/hooks';
import type { PermissionItem } from '@/api/hooks';
import { Input } from '@/components/ui/Input';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
@@ -18,17 +19,6 @@ interface Role {
field_permissions?: Record<string, any>;
}
const ALL_PERMISSIONS = [
{ key: 'companies:read', label: 'Firmen lesen' },
{ key: 'companies:write', label: 'Firmen bearbeiten' },
{ key: 'contacts:read', label: 'Kontakte lesen' },
{ key: 'contacts:write', label: 'Kontakte bearbeiten' },
{ key: 'users:read', label: 'Benutzer lesen' },
{ key: 'users:write', label: 'Benutzer bearbeiten' },
{ key: 'audit:read', label: 'Audit-Log lesen' },
{ key: 'settings:write', label: 'Einstellungen bearbeiten' },
];
function isPermissionGranted(permissions: Record<string, any>, permKey: string): boolean {
if (!permissions) return false;
if (typeof permissions === 'boolean') return permissions;
@@ -66,6 +56,7 @@ export function SettingsRolesPage() {
const { t } = useTranslation();
const toast = useToast();
const { data, isLoading, isError, error, refetch } = useRoles();
const { data: permissionsData, isLoading: permissionsLoading } = usePermissions();
const createRoleMutation = useCreateRole();
const updateRoleMutation = useUpdateRole();
const deleteRoleMutation = useDeleteRole();
@@ -78,6 +69,29 @@ export function SettingsRolesPage() {
const roles: Role[] = data?.items ?? [];
const allPermissions: PermissionItem[] = useMemo(() => {
return permissionsData?.all ?? [];
}, [permissionsData]);
const permissionLabelMap = useMemo(() => {
const map = new Map<string, string>();
for (const perm of allPermissions) {
map.set(perm.key, perm.label);
}
return map;
}, [allPermissions]);
const groupedPermissions = useMemo(() => {
const groups: Record<string, PermissionItem[]> = {};
for (const perm of allPermissions) {
if (!groups[perm.category]) {
groups[perm.category] = [];
}
groups[perm.category].push(perm);
}
return groups;
}, [allPermissions]);
const handleCreateRole = async () => {
if (!newRoleName.trim()) {
toast.error(t('validation.required'));
@@ -138,7 +152,7 @@ export function SettingsRolesPage() {
}
};
if (isLoading) {
if (isLoading || permissionsLoading) {
return (
<div className="p-6 max-w-4xl mx-auto" data-testid="settings-roles-page">
<Skeleton className="h-8 w-48 mb-6" />
@@ -211,7 +225,7 @@ export function SettingsRolesPage() {
<span className="text-sm text-secondary-500"></span>
) : (
grantedKeys.map((perm) => {
const label = ALL_PERMISSIONS.find((p) => p.key === perm)?.label || perm;
const label = permissionLabelMap.get(perm) || perm;
return <Badge key={perm} variant="primary">{label}</Badge>;
})
)}
@@ -228,22 +242,32 @@ export function SettingsRolesPage() {
label={t('settings.roleName')}
value={newRoleName}
onChange={(e) => setNewRoleName(e.target.value)}
placeholder="z.B. Vertrieb"
placeholder={t('settings.roleName')}
data-testid="new-role-name"
/>
<div>
<label className="block text-sm font-medium text-secondary-700 mb-2">{t('settings.permissions')}</label>
<div className="space-y-2">
{ALL_PERMISSIONS.map((perm) => (
<label key={perm.key} className="flex items-center gap-2 p-2 rounded-md hover:bg-secondary-50 cursor-pointer min-h-touch">
<input
type="checkbox"
checked={isPermissionGranted(newRolePermissions, perm.key)}
onChange={() => handleTogglePermission(perm.key, 'new')}
className="w-4 h-4 rounded border-secondary-300 text-primary-600 focus:ring-primary-500"
/>
<span className="text-sm text-secondary-900">{perm.label}</span>
</label>
{allPermissions.length === 0 && (
<p className="text-sm text-secondary-500">{t('settings.noPermissions')}</p>
)}
{Object.entries(groupedPermissions).map(([category, perms]) => (
<div key={category} className="mb-3">
<p className="text-xs font-semibold text-secondary-500 uppercase mb-1">
{category === 'system' ? t('settings.systemPermissions') : t('settings.pluginPermissions')}
</p>
{perms.map((perm) => (
<label key={perm.key} className="flex items-center gap-2 p-2 rounded-md hover:bg-secondary-50 cursor-pointer min-h-touch">
<input
type="checkbox"
checked={isPermissionGranted(newRolePermissions, perm.key)}
onChange={() => handleTogglePermission(perm.key, 'new')}
className="w-4 h-4 rounded border-secondary-300 text-primary-600 focus:ring-primary-500"
/>
<span className="text-sm text-secondary-900">{perm.label}</span>
</label>
))}
</div>
))}
</div>
</div>
@@ -271,16 +295,26 @@ export function SettingsRolesPage() {
<div>
<label className="block text-sm font-medium text-secondary-700 mb-2">{t('settings.permissions')}</label>
<div className="space-y-2">
{ALL_PERMISSIONS.map((perm) => (
<label key={perm.key} className="flex items-center gap-2 p-2 rounded-md hover:bg-secondary-50 cursor-pointer min-h-touch">
<input
type="checkbox"
checked={isPermissionGranted(editingRole.permissions, perm.key)}
onChange={() => handleTogglePermission(perm.key, 'edit')}
className="w-4 h-4 rounded border-secondary-300 text-primary-600 focus:ring-primary-500"
/>
<span className="text-sm text-secondary-900">{perm.label}</span>
</label>
{allPermissions.length === 0 && (
<p className="text-sm text-secondary-500">{t('settings.noPermissions')}</p>
)}
{Object.entries(groupedPermissions).map(([category, perms]) => (
<div key={category} className="mb-3">
<p className="text-xs font-semibold text-secondary-500 uppercase mb-1">
{category === 'system' ? t('settings.systemPermissions') : t('settings.pluginPermissions')}
</p>
{perms.map((perm) => (
<label key={perm.key} className="flex items-center gap-2 p-2 rounded-md hover:bg-secondary-50 cursor-pointer min-h-touch">
<input
type="checkbox"
checked={isPermissionGranted(editingRole.permissions, perm.key)}
onChange={() => handleTogglePermission(perm.key, 'edit')}
className="w-4 h-4 rounded border-secondary-300 text-primary-600 focus:ring-primary-500"
/>
<span className="text-sm text-secondary-900">{perm.label}</span>
</label>
))}
</div>
))}
</div>
</div>