/** * AdminFeatureAccessPage * * Admin page for managing feature instances within mandates. * Allows creating, viewing, and managing feature instances. */ import React, { useState, useEffect, useMemo } from 'react'; import { useFeatureAccess, type FeatureInstance } from '../../hooks/useFeatureAccess'; import { useUserMandates, type Mandate } from '../../hooks/useUserMandates'; import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable'; import { FormGeneratorForm, type AttributeDefinition } from '../../components/FormGenerator/FormGeneratorForm'; import { FaPlus, FaSync, FaCube, FaBuilding, FaCogs, FaEdit } from 'react-icons/fa'; import { useToast } from '../../contexts/ToastContext'; import api from '../../api'; import styles from './Admin.module.css'; export const AdminFeatureAccessPage: React.FC = () => { const { features, instances, instancesPagination, loading, error, fetchFeatures, fetchInstances, createInstance, updateInstance, deleteInstance, syncInstanceRoles, } = useFeatureAccess(); const { fetchMandates } = useUserMandates(); const { showSuccess, showError } = useToast(); // State const [mandates, setMandates] = useState([]); const [selectedMandateId, setSelectedMandateId] = useState(''); const [showCreateModal, setShowCreateModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [editingInstance, setEditingInstance] = useState(null); const [, setIsSubmitting] = useState(false); const [syncingInstance, setSyncingInstance] = useState(null); const [backendAttributes, setBackendAttributes] = useState([]); // Load features, mandates, and attributes on mount useEffect(() => { fetchFeatures(); fetchMandates().then(setMandates); // Fetch FeatureInstance attributes from backend api.get('/api/attributes/FeatureInstance').then(response => { const attrs = response.data?.attributes || response.data || []; setBackendAttributes(Array.isArray(attrs) ? attrs : []); }).catch(() => setBackendAttributes([])); }, [fetchFeatures, fetchMandates]); // Load instances when mandate changes useEffect(() => { if (selectedMandateId) { fetchInstances(selectedMandateId); } }, [selectedMandateId, fetchInstances]); // Table columns const columns = useMemo(() => [ { key: 'label', label: 'Name', type: 'string' as const, sortable: true, filterable: true, searchable: true, width: 200 }, { key: 'featureCode', label: 'Feature', type: 'string' as const, sortable: true, filterable: true, width: 150, render: (value: string) => { const feature = features.find(f => f.code === value); if (feature) { const label = typeof feature.label === 'object' ? (feature.label.de || feature.label.en || value) : feature.label; return label; } return value; } }, { key: 'enabled', label: 'Aktiv', type: 'boolean' as const, sortable: true, filterable: true, width: 80 }, ], [features]); // Form attributes from backend - merge with dynamic feature options const createFields: AttributeDefinition[] = useMemo(() => { const excludedFields = ['id', 'mandateId']; const featureOptions = features.map(f => ({ value: f.code, label: typeof f.label === 'object' ? (f.label.de || f.label.en || f.code) : (f.label || f.code) })); return backendAttributes .filter(attr => !excludedFields.includes(attr.name)) .map(attr => ({ ...attr, // Override featureCode: make editable for create and add dynamic options readonly: attr.name === 'featureCode' ? false : attr.readonly, editable: attr.name === 'featureCode' || attr.name === 'enabled' ? true : attr.editable, options: attr.name === 'featureCode' ? featureOptions : attr.options, })) as AttributeDefinition[]; }, [features, backendAttributes]); // Handle create instance const handleCreateInstance = async (data: { featureCode: string; label: string; enabled?: boolean; copyTemplateRoles?: boolean }) => { if (!selectedMandateId) return; setIsSubmitting(true); try { const result = await createInstance(selectedMandateId, { featureCode: data.featureCode, label: data.label, enabled: data.enabled !== false, copyTemplateRoles: data.copyTemplateRoles !== false }); if (result.success) { setShowCreateModal(false); fetchInstances(selectedMandateId); showSuccess('Feature-Instanz erstellt', `Die Instanz "${data.label}" wurde erfolgreich erstellt.`); } else { showError('Fehler', result.error || 'Fehler beim Erstellen der Feature-Instanz'); } } finally { setIsSubmitting(false); } }; // Handle edit click const handleEditClick = (instance: FeatureInstance) => { setEditingInstance(instance); setShowEditModal(true); }; // Handle update instance const handleUpdateInstance = async (data: { label: string; enabled?: boolean }) => { if (!selectedMandateId || !editingInstance) return; setIsSubmitting(true); try { const result = await updateInstance(selectedMandateId, editingInstance.id, { label: data.label, enabled: data.enabled }); if (result.success) { setShowEditModal(false); setEditingInstance(null); fetchInstances(selectedMandateId); showSuccess('Feature-Instanz aktualisiert', `Die Instanz "${data.label}" wurde erfolgreich aktualisiert.`); } else { showError('Fehler', result.error || 'Fehler beim Aktualisieren der Feature-Instanz'); } } finally { setIsSubmitting(false); } }; // Handle delete instance - called by DeleteActionButton with instanceId const handleDeleteInstance = async (instanceId: string): Promise => { if (!selectedMandateId) return false; const result = await deleteInstance(selectedMandateId, instanceId); if (result.success) { showSuccess('Instanz gelöscht', 'Die Feature-Instanz wurde gelöscht.'); return true; } else { showError('Fehler', result.error || 'Fehler beim Löschen der Feature-Instanz'); return false; } }; // Handle sync roles const handleSyncRoles = async (instance: FeatureInstance) => { if (!selectedMandateId) return; setSyncingInstance(instance.id); try { const result = await syncInstanceRoles(selectedMandateId, instance.id, true); if (result.success && result.data) { showSuccess( 'Rollen synchronisiert', `Hinzugefügt: ${result.data.added}\nEntfernt: ${result.data.removed}\nUnverändert: ${result.data.unchanged}` ); } else { showError('Synchronisierung fehlgeschlagen', result.error || 'Fehler beim Synchronisieren der Rollen'); } } finally { setSyncingInstance(null); } }; // Get mandate name const getMandateName = (mandate: Mandate) => { if (typeof mandate.name === 'object') { return mandate.name.de || mandate.name.en || Object.values(mandate.name)[0] || mandate.id; } return mandate.name || mandate.id; }; // Get feature label const getFeatureLabel = (code: string) => { const feature = features.find(f => f.code === code); if (feature) { return typeof feature.label === 'object' ? (feature.label.de || feature.label.en || code) : (feature.label || code); } return code; }; if (error && !selectedMandateId) { return (
⚠️

Fehler: {error}

); } return (

Feature-Instanzen

Verwalten Sie Feature-Instanzen für jeden Mandanten

{/* Mandate Selector */}
{selectedMandateId && (
)}
{/* Available Features Info */} {features.length > 0 && (
Verfügbare Features: {features.map((f, i) => ( {i > 0 && ', '} {getFeatureLabel(f.code)} ))}
)} {/* Content */} {!selectedMandateId ? (

Kein Mandant ausgewählt

Wählen Sie einen Mandanten aus, um dessen Feature-Instanzen zu verwalten.

) : loading && instances.length === 0 ? (
Lade Feature-Instanzen...
) : instances.length === 0 ? (

Keine Feature-Instanzen

Für diesen Mandanten wurden noch keine Feature-Instanzen erstellt.

) : (
, onClick: handleEditClick, title: 'Instanz bearbeiten', }, { id: 'syncRoles', icon: , onClick: handleSyncRoles, title: 'Rollen synchronisieren', loading: (row: FeatureInstance) => syncingInstance === row.id, disabled: (row: FeatureInstance) => !row.enabled, } ]} hookData={{ refetch: fetchInstances, pagination: instancesPagination, handleDelete: handleDeleteInstance, }} emptyMessage="Keine Feature-Instanzen gefunden" />
)} {/* Create Instance Modal */} {showCreateModal && (
setShowCreateModal(false)}>
e.stopPropagation()}>

Neue Feature-Instanz erstellen

{features.length === 0 ? (

Keine Features verfügbar. Bitte wenden Sie sich an den System-Administrator.

) : createFields.length === 0 ? (
Lade Formular...
) : ( setShowCreateModal(false)} submitButtonText="Erstellen" cancelButtonText="Abbrechen" /> )}
)} {/* Edit Instance Modal */} {showEditModal && editingInstance && (
{ setShowEditModal(false); setEditingInstance(null); }}>
e.stopPropagation()}>

Feature-Instanz bearbeiten

{ setShowEditModal(false); setEditingInstance(null); }} submitButtonText="Speichern" cancelButtonText="Abbrechen" />
)}
); }; export default AdminFeatureAccessPage;