Files
leocrm/frontend/src/pages/SettingsRoles.tsx
T

191 lines
7.7 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
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 { useToast } from '@/components/ui/Toast';
import { Modal } from '@/components/ui/Modal';
interface Role {
id: string;
name: string;
permissions: string[];
}
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' },
];
export function SettingsRolesPage() {
const { t } = useTranslation();
const toast = useToast();
const [roles, setRoles] = useState<Role[]>([
{ 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<string[]>([]);
const [editingRole, setEditingRole] = useState<Role | null>(null);
const handleCreateRole = () => {
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);
};
const handleTogglePermission = (permKey: string, target: 'new' | 'edit') => {
if (target === 'new') {
setNewRolePermissions((prev) =>
prev.includes(permKey) ? prev.filter((p) => p !== permKey) : [...prev, permKey]
);
} else if (editingRole) {
setEditingRole({
...editingRole,
permissions: editingRole.permissions.includes(permKey)
? editingRole.permissions.filter((p) => p !== permKey)
: [...editingRole.permissions, permKey],
});
}
};
const handleSaveRole = () => {
if (!editingRole) return;
setRoles((prev) => prev.map((r) => (r.id === editingRole.id ? editingRole : r)));
toast.success(t('settings.roleUpdated'));
setEditingRole(null);
};
return (
<div className="p-6 max-w-4xl mx-auto" data-testid="settings-roles-page">
<div className="flex items-center justify-between mb-6">
<h1 className="text-2xl font-bold text-secondary-900">{t('settings.roles')}</h1>
<Button onClick={() => setCreateOpen(true)} data-testid="create-role-btn">
{t('settings.createRole')}
</Button>
</div>
{roles.length === 0 ? (
<EmptyState
title={t('settings.noRoles')}
action={<Button onClick={() => setCreateOpen(true)}>{t('settings.createRole')}</Button>}
/>
) : (
<div className="space-y-4">
{roles.map((role) => (
<Card key={role.id}>
<div className="flex items-center justify-between mb-3">
<h3 className="text-lg font-semibold text-secondary-900">{role.name}</h3>
<Button
variant="secondary"
size="sm"
onClick={() => setEditingRole(role)}
data-testid={`edit-role-${role.id}`}
>
{t('common.edit')}
</Button>
</div>
<div className="flex flex-wrap gap-2">
{role.permissions.length === 0 ? (
<span className="text-sm text-secondary-500"></span>
) : (
role.permissions.map((perm) => {
const label = ALL_PERMISSIONS.find((p) => p.key === perm)?.label || perm;
return <Badge key={perm} variant="primary">{label}</Badge>;
})
)}
</div>
</Card>
))}
</div>
)}
<Modal open={createOpen} onClose={() => setCreateOpen(false)} title={t('settings.createRole')}>
<div className="space-y-4" data-testid="create-role-form">
<Input
label={t('settings.roleName')}
value={newRoleName}
onChange={(e) => setNewRoleName(e.target.value)}
placeholder="z.B. Vertrieb"
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={newRolePermissions.includes(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 className="flex justify-end gap-3">
<Button variant="secondary" onClick={() => setCreateOpen(false)}>{t('common.cancel')}</Button>
<Button onClick={handleCreateRole} data-testid="save-role-btn">{t('common.save')}</Button>
</div>
</div>
</Modal>
<Modal open={!!editingRole} onClose={() => setEditingRole(null)} title={t('settings.roles') + ' — ' + (editingRole?.name || '')}>
{editingRole && (
<div className="space-y-4" data-testid="edit-role-form">
<Input
label={t('settings.roleName')}
value={editingRole.name}
onChange={(e) => setEditingRole({ ...editingRole, name: e.target.value })}
/>
<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={editingRole.permissions.includes(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 className="flex justify-end gap-3">
<Button variant="secondary" onClick={() => setEditingRole(null)}>{t('common.cancel')}</Button>
<Button onClick={handleSaveRole} data-testid="update-role-btn">{t('common.save')}</Button>
</div>
</div>
)}
</Modal>
</div>
);
}