frontend_nyla/src/pages/views/trustee/TrusteeRolesView.tsx
2026-01-23 21:05:36 +01:00

197 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* TrusteeRolesView
*
* Rollen-Verwaltung für eine Trustee-Instanz.
* Rollen definieren Berechtigungen (admin, operate, userreport).
* Hinweis: Nur SysAdmin kann Rollen verwalten.
*/
import React, { useState, useMemo } from 'react';
import { useTrusteeRoles, useTrusteeRoleOperations, TrusteeRole } from '../../../hooks/useTrustee';
import { useTablePermission } from '../../../hooks/useInstancePermissions';
import { Popup } from '../../../components/UiComponents/Popup/Popup';
import { TrusteeEditForm, FieldConfig } from './components';
import styles from './TrusteeViews.module.css';
export const TrusteeRolesView: React.FC = () => {
const { items: roles, loading, error, refetch } = useTrusteeRoles();
const { handleDelete, handleCreate, handleUpdate, deletingItems, creatingItem } = useTrusteeRoleOperations();
const { canCreate, canUpdate, canDelete } = useTablePermission('TrusteeRole');
// Modal State
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingRole, setEditingRole] = useState<TrusteeRole | null>(null);
const [formError, setFormError] = useState<string | null>(null);
// Feld-Konfiguration für das Formular
const fields: FieldConfig[] = useMemo(() => [
{
key: 'id',
label: 'Rollen-ID',
type: 'string',
required: true,
editable: !editingRole, // Nur bei Create editierbar
placeholder: 'z.B. admin, operate, userreport',
helpText: 'Eindeutige Rollen-ID (nicht änderbar nach Erstellung)',
},
{
key: 'desc',
label: 'Beschreibung',
type: 'textarea',
required: true,
placeholder: 'Beschreibung der Rolle und ihrer Berechtigungen',
},
], [editingRole]);
if (loading) {
return <div className={styles.loading}>Lade Rollen...</div>;
}
if (error) {
return <div className={styles.error}>Fehler: {error}</div>;
}
const onDelete = async (roleId: string) => {
if (window.confirm('Rolle wirklich löschen? Dies ist nur möglich, wenn die Rolle nicht in Verwendung ist.')) {
const success = await handleDelete(roleId);
if (success) {
refetch();
}
}
};
const onEdit = (role: TrusteeRole) => {
setEditingRole(role);
setFormError(null);
setIsModalOpen(true);
};
const onCreate = () => {
setEditingRole(null);
setFormError(null);
setIsModalOpen(true);
};
const onCloseModal = () => {
setIsModalOpen(false);
setEditingRole(null);
setFormError(null);
};
const onSave = async (data: Partial<TrusteeRole>) => {
setFormError(null);
try {
if (editingRole) {
const result = await handleUpdate(editingRole.id, data);
if (!result.success) {
setFormError(result.error || 'Fehler beim Aktualisieren');
return;
}
} else {
const result = await handleCreate(data);
if (!result.success) {
setFormError(result.error || 'Fehler beim Erstellen');
return;
}
}
onCloseModal();
refetch();
} catch (err: any) {
setFormError(err.message || 'Ein Fehler ist aufgetreten');
}
};
return (
<div className={styles.listView}>
{/* Toolbar */}
<div className={styles.toolbar}>
{canCreate && (
<button className={styles.primaryButton} onClick={onCreate}>
+ Neue Rolle
</button>
)}
<button className={styles.secondaryButton} onClick={() => refetch()}>
Aktualisieren
</button>
</div>
{/* Info */}
<div className={styles.muted} style={{ fontSize: '0.8125rem', padding: '0.5rem 0' }}>
Rollen definieren Berechtigungen für Trustee-Zugriffe. Standard-Rollen: admin, operate, userreport.
</div>
{/* Tabelle */}
{roles.length === 0 ? (
<div className={styles.emptyState}>
<p>Keine Rollen vorhanden.</p>
</div>
) : (
<table className={styles.dataTable}>
<thead>
<tr>
<th>ID</th>
<th>Beschreibung</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{roles.map((role) => (
<tr key={role.id}>
<td className={styles.monospace}>{role.id}</td>
<td>{role.desc}</td>
<td className={styles.actions}>
{canUpdate && (
<button
className={styles.iconButton}
title="Bearbeiten"
onClick={() => onEdit(role)}
>
</button>
)}
{canDelete && (
<button
className={styles.iconButton}
title="Löschen"
onClick={() => onDelete(role.id)}
disabled={deletingItems.has(role.id)}
>
{deletingItems.has(role.id) ? '...' : '🗑️'}
</button>
)}
</td>
</tr>
))}
</tbody>
</table>
)}
{/* Create/Edit Modal */}
<Popup
isOpen={isModalOpen}
title={editingRole ? 'Rolle bearbeiten' : 'Neue Rolle'}
onClose={onCloseModal}
size="medium"
>
{formError && (
<div className={styles.formError} style={{ marginBottom: '1rem' }}>
{formError}
</div>
)}
<TrusteeEditForm<TrusteeRole>
initialData={editingRole || {}}
fields={fields}
onSave={onSave}
onCancel={onCloseModal}
isSaving={creatingItem}
isEdit={!!editingRole}
saveLabel={editingRole ? 'Aktualisieren' : 'Erstellen'}
/>
</Popup>
</div>
);
};
export default TrusteeRolesView;