- {/* Toolbar */}
-
- {canCreate && (
-
- )}
-
-
-
- {/* Tabelle */}
- {contracts.length === 0 ? (
-
-
Keine Verträge vorhanden.
-
- ) : (
-
-
-
- | Bezeichnung |
- Organisation |
- Status |
- Aktionen |
-
-
-
- {contracts.map((contract) => (
-
- | {contract.label} |
- {getLabelFast('organisations', contract.organisationId)} |
-
-
- {contract.enabled ? 'Aktiv' : 'Inaktiv'}
-
- |
-
- {canUpdate && (
-
- )}
- {canDelete && (
-
- )}
- |
-
- ))}
-
-
- )}
-
- {/* Create/Edit Modal */}
-
- {formError && (
-
- {formError}
-
- )}
-
- initialData={editingContract || { enabled: true }}
- fields={fields}
- onSave={onSave}
- onCancel={onCloseModal}
- isSaving={creatingItem}
- isEdit={!!editingContract}
- saveLabel={editingContract ? 'Aktualisieren' : 'Erstellen'}
- />
-
-
- );
-};
-
-export default TrusteeContractsView;
diff --git a/src/pages/views/trustee/TrusteeDashboardView.tsx b/src/pages/views/trustee/TrusteeDashboardView.tsx
index 384c1ea..af8a966 100644
--- a/src/pages/views/trustee/TrusteeDashboardView.tsx
+++ b/src/pages/views/trustee/TrusteeDashboardView.tsx
@@ -1,53 +1,73 @@
/**
* TrusteeDashboardView
*
- * Übersicht/Dashboard für eine Trustee-Instanz
+ * Übersicht/Dashboard für eine Trustee-Instanz.
+ * Zeigt Statistiken über Positionen, Dokumente und Verknüpfungen.
*/
import React from 'react';
import { useCurrentInstance } from '../../../hooks/useCurrentInstance';
-import { useTrusteeOrganisations } from '../../../hooks/useTrustee';
-import { useTrusteeContracts } from '../../../hooks/useTrustee';
+import { useTrusteePositions, useTrusteeDocuments, useTrusteePositionDocuments } from '../../../hooks/useTrustee';
import styles from './TrusteeViews.module.css';
export const TrusteeDashboardView: React.FC = () => {
const { instance } = useCurrentInstance();
- const { items: organisations, loading: orgsLoading } = useTrusteeOrganisations();
- const { items: contracts, loading: contractsLoading } = useTrusteeContracts();
+ const { items: positions, loading: posLoading } = useTrusteePositions();
+ const { items: documents, loading: docsLoading } = useTrusteeDocuments();
+ const { items: links, loading: linksLoading } = useTrusteePositionDocuments();
- const isLoading = orgsLoading || contractsLoading;
+ const isLoading = posLoading || docsLoading || linksLoading;
return (
- {/* Organisationen Card */}
+ {/* Positionen Card */}
-
🏢
+
📊
- {isLoading ? '...' : organisations.length}
+ {isLoading ? '...' : positions.length}
-
Organisationen
+
Positionen
- {/* Verträge Card */}
+ {/* Dokumente Card */}
📄
- {isLoading ? '...' : contracts.length}
+ {isLoading ? '...' : documents.length}
-
Verträge
+
Dokumente
- {/* Rolle Card */}
+ {/* Verknüpfungen Card */}
+
+
🔗
+
+
+ {isLoading ? '...' : links.length}
+
+
Zuordnungen
+
+
+
+ {/* Rollen Card */}
👤
-
{instance?.userRole || '-'}
-
Deine Rolle
+
+ {instance?.userRoles?.length ? (
+ instance.userRoles.map((role, idx) => (
+
{role}
+ ))
+ ) : '-'}
+
+
+ {(instance?.userRoles?.length || 0) === 1 ? 'Deine Rolle' : 'Deine Rollen'}
+
diff --git a/src/pages/views/trustee/TrusteeDocumentsView.tsx b/src/pages/views/trustee/TrusteeDocumentsView.tsx
index 664b46d..c816ceb 100644
--- a/src/pages/views/trustee/TrusteeDocumentsView.tsx
+++ b/src/pages/views/trustee/TrusteeDocumentsView.tsx
@@ -2,154 +2,129 @@
* TrusteeDocumentsView
*
* Dokument-Verwaltung für eine Trustee-Instanz.
- * Zeigt Belege und Dokumente mit Vertragszuordnung.
+ * Verwendet FormGeneratorTable für konsistentes UI.
*/
-import React, { useState, useMemo } from 'react';
+import React, { useState, useMemo, useEffect } from 'react';
import { useTrusteeDocuments, useTrusteeDocumentOperations, TrusteeDocument } from '../../../hooks/useTrustee';
-import { useTrusteeOptions } from '../../../hooks/useTrusteeOptions';
-import { useTablePermission } from '../../../hooks/useInstancePermissions';
import { useInstanceId } from '../../../hooks/useCurrentInstance';
-import { Popup } from '../../../components/UiComponents/Popup/Popup';
-import { TrusteeEditForm, FieldConfig } from './components';
+import { FormGeneratorTable } from '../../../components/FormGenerator/FormGeneratorTable';
+import { FormGeneratorForm } from '../../../components/FormGenerator/FormGeneratorForm';
+import { FaSync, FaFileAlt, FaDownload } from 'react-icons/fa';
import api from '../../../api';
-import styles from './TrusteeViews.module.css';
+import styles from '../../admin/Admin.module.css';
export const TrusteeDocumentsView: React.FC = () => {
const instanceId = useInstanceId();
- const { items: documents, loading, error, refetch } = useTrusteeDocuments();
- const { handleDelete, handleCreate, handleUpdate, deletingItems, creatingItem } = useTrusteeDocumentOperations();
- const { canCreate, canUpdate, canDelete } = useTablePermission('TrusteeDocument');
- // Options für Label-Auflösung
- const { getLabelFast, loading: optionsLoading, loadContractsForOrganisation } = useTrusteeOptions(['organisations', 'contracts']);
-
- // Modal State
- const [isModalOpen, setIsModalOpen] = useState(false);
- const [editingDoc, setEditingDoc] = useState
(null);
- const [formError, setFormError] = useState(null);
- const [downloading, setDownloading] = useState(null);
- const [contractOptions, setContractOptions] = useState>([]);
-
- // MIME-Type Options
- const mimeTypeOptions = [
- { value: 'application/pdf', label: 'PDF' },
- { value: 'image/jpeg', label: 'JPEG' },
- { value: 'image/png', label: 'PNG' },
- { value: 'application/octet-stream', label: 'Andere' },
- ];
-
- // Feld-Konfiguration für das Formular
- const fields: FieldConfig[] = useMemo(() => [
- {
- key: 'organisationId',
- label: 'Organisation',
- type: 'enum',
- required: true,
- optionsReference: 'organisations',
- },
- {
- key: 'contractId',
- label: 'Vertrag',
- type: 'enum',
- required: true,
- options: contractOptions,
- dependsOn: 'organisationId',
- },
- {
- key: 'documentName',
- label: 'Dokumentname',
- type: 'string',
- required: true,
- placeholder: 'z.B. Rechnung_2026.pdf',
- },
- {
- key: 'documentMimeType',
- label: 'Dateityp',
- type: 'enum',
- required: true,
- options: mimeTypeOptions,
- },
- ], [contractOptions]);
-
- if (loading || optionsLoading) {
- return Lade Dokumente...
;
- }
-
- if (error) {
- return Fehler: {error}
;
- }
-
- const onDelete = async (docId: string) => {
- if (window.confirm('Dokument wirklich löschen?')) {
- const success = await handleDelete(docId);
- if (success) {
+ // Entity hook
+ const {
+ items: documents,
+ attributes,
+ permissions,
+ pagination,
+ loading,
+ error,
+ refetch,
+ fetchById,
+ updateOptimistically,
+ removeOptimistically,
+ } = useTrusteeDocuments();
+
+ // Operations hook
+ const {
+ handleDelete,
+ handleCreate,
+ handleUpdate,
+ deletingItems,
+ } = useTrusteeDocumentOperations();
+
+ // Modal state
+ const [editingDocument, setEditingDocument] = useState(null);
+ const [isCreateMode, setIsCreateMode] = useState(false);
+ const [downloadingId, setDownloadingId] = useState(null);
+
+ // Initial fetch
+ useEffect(() => {
+ if (instanceId) {
+ refetch();
+ }
+ }, [instanceId]);
+
+ // Generate columns from attributes
+ const columns = useMemo(() => {
+ return (attributes || []).map(attr => ({
+ key: attr.name,
+ label: attr.label || attr.name,
+ type: attr.type as any,
+ sortable: attr.sortable !== false,
+ filterable: attr.filterable !== false,
+ searchable: attr.searchable !== false,
+ width: attr.width || 150,
+ minWidth: attr.minWidth || 100,
+ maxWidth: attr.maxWidth || 400,
+ }));
+ }, [attributes]);
+
+ // Check permissions
+ const canCreate = permissions?.create !== 'n';
+ const canUpdate = permissions?.update !== 'n';
+ const canDelete = permissions?.delete !== 'n';
+
+ // Handle edit click
+ const handleEditClick = async (doc: TrusteeDocument) => {
+ const fullDoc = await fetchById(doc.id);
+ if (fullDoc) {
+ setEditingDocument(fullDoc);
+ setIsCreateMode(false);
+ }
+ };
+
+ // Handle create click
+ const handleCreateClick = () => {
+ setEditingDocument(null);
+ setIsCreateMode(true);
+ };
+
+ // Handle form submit
+ const handleFormSubmit = async (data: Partial) => {
+ if (isCreateMode) {
+ const result = await handleCreate(data);
+ if (result.success) {
+ setIsCreateMode(false);
+ refetch();
+ }
+ } else if (editingDocument) {
+ const result = await handleUpdate(editingDocument.id, data);
+ if (result.success) {
+ setEditingDocument(null);
refetch();
}
}
};
-
- const onEdit = async (doc: TrusteeDocument) => {
- setEditingDoc(doc);
- setFormError(null);
- // Lade Contracts für die Organisation
- if (doc.organisationId) {
- const contracts = await loadContractsForOrganisation(doc.organisationId);
- setContractOptions(contracts);
- }
- setIsModalOpen(true);
- };
-
- const onCreate = () => {
- setEditingDoc(null);
- setFormError(null);
- setContractOptions([]);
- setIsModalOpen(true);
- };
-
- const onCloseModal = () => {
- setIsModalOpen(false);
- setEditingDoc(null);
- setFormError(null);
- setContractOptions([]);
- };
-
- const onSave = async (data: Partial) => {
- setFormError(null);
-
- try {
- if (editingDoc) {
- const result = await handleUpdate(editingDoc.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;
- }
+
+ // Handle delete
+ const handleDeleteDoc = async (doc: TrusteeDocument) => {
+ if (window.confirm(`Dokument "${doc.documentName}" wirklich löschen?`)) {
+ removeOptimistically(doc.id);
+ const success = await handleDelete(doc.id);
+ if (!success) {
+ refetch(); // Revert on error
}
-
- onCloseModal();
- refetch();
- } catch (err: any) {
- setFormError(err.message || 'Ein Fehler ist aufgetreten');
}
};
-
- const onDownload = async (doc: TrusteeDocument) => {
+
+ // Handle download
+ const handleDownload = async (doc: TrusteeDocument) => {
if (!instanceId) return;
- setDownloading(doc.id);
+ setDownloadingId(doc.id);
try {
const response = await api.get(
`/api/trustee/${instanceId}/documents/${doc.id}/data`,
{ responseType: 'blob' }
);
- // Blob-Download
const blob = new Blob([response.data], { type: doc.documentMimeType });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
@@ -163,112 +138,174 @@ export const TrusteeDocumentsView: React.FC = () => {
console.error('Download error:', err);
alert('Fehler beim Herunterladen des Dokuments.');
} finally {
- setDownloading(null);
+ setDownloadingId(null);
}
};
-
- // MIME-Type zu lesbarem Text
- const getMimeTypeLabel = (mimeType: string) => {
- const found = mimeTypeOptions.find(o => o.value === mimeType);
- return found?.label || mimeType?.split('/')[1]?.toUpperCase() || 'Unbekannt';
+
+ // Close modal
+ const handleCloseModal = () => {
+ setEditingDocument(null);
+ setIsCreateMode(false);
};
-
- return (
-
- {/* Toolbar */}
-
- {canCreate && (
-