/** * AdminUserMandatesPage * * Admin page for managing user-mandate memberships. * Allows assigning users to mandates and managing their roles within mandates. */ import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'; import { useUserMandates, type MandateUser, type Mandate, type Role, type PaginationParams } from '../../hooks/useUserMandates'; import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable'; import { FormGeneratorForm, type AttributeDefinition } from '../../components/FormGenerator/FormGeneratorForm'; import { FaPlus, FaSync, FaUsers, FaBuilding } from 'react-icons/fa'; import { useToast } from '../../contexts/ToastContext'; import api from '../../api'; import styles from './Admin.module.css'; export const AdminUserMandatesPage: React.FC = () => { const { showError } = useToast(); const { users, loading, error, pagination, fetchMandateUsers, addUserToMandate, removeUserFromMandate, updateUserRoles, fetchMandates, fetchRoles, fetchAllUsers, } = useUserMandates(); // Store current mandateId for refetch const currentMandateIdRef = useRef(''); // State const [mandates, setMandates] = useState([]); const [selectedMandateId, setSelectedMandateId] = useState(''); const [roles, setRoles] = useState([]); const [allUsers, setAllUsers] = useState>([]); const [showAddModal, setShowAddModal] = useState(false); const [editingUser, setEditingUser] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const [backendAttributes, setBackendAttributes] = useState([]); // Load mandates and attributes on mount useEffect(() => { const loadMandates = async () => { const data = await fetchMandates(); setMandates(data); // Auto-select first mandate if available if (data.length > 0 && !selectedMandateId) { setSelectedMandateId(data[0].id); } }; loadMandates(); // Fetch UserMandate attributes from backend (for table columns) api.get('/api/attributes/UserMandate').then(response => { const attrs = response.data?.attributes || response.data || []; setBackendAttributes(Array.isArray(attrs) ? attrs : []); }).catch(() => setBackendAttributes([])); }, [fetchMandates]); // Load users when mandate changes useEffect(() => { if (selectedMandateId) { currentMandateIdRef.current = selectedMandateId; fetchMandateUsers(selectedMandateId); fetchRoles(selectedMandateId).then(setRoles); } }, [selectedMandateId, fetchMandateUsers, fetchRoles]); // Refetch wrapper that accepts pagination params from FormGeneratorTable const refetchWithParams = useCallback(async (paginationParams?: PaginationParams) => { const mandateId = currentMandateIdRef.current; if (!mandateId) return; // If pagination params provided, pass them; otherwise just use mandateId if (paginationParams && Object.keys(paginationParams).length > 0) { return fetchMandateUsers(paginationParams); } return fetchMandateUsers(mandateId); }, [fetchMandateUsers]); // Load all users for the add modal useEffect(() => { fetchAllUsers().then(setAllUsers); }, [fetchAllUsers]); // Get users not yet in the mandate const availableUsers = useMemo(() => { const existingUserIds = new Set(users.map(u => u.userId)); return allUsers.filter(u => !existingUserIds.has(u.id)); }, [allUsers, users]); // Table columns - based on MandateUserInfo response structure const columns = useMemo(() => { return [ { key: 'username', label: 'Benutzername', type: 'text' as any, sortable: true, filterable: true, searchable: true, width: 150, }, { key: 'email', label: 'E-Mail', type: 'text' as any, sortable: true, filterable: true, searchable: true, width: 200, }, { key: 'fullName', label: 'Vollständiger Name', type: 'text' as any, sortable: true, filterable: true, searchable: true, width: 180, }, { key: 'roleLabels', label: 'Rollen', type: 'text' as any, sortable: false, filterable: false, searchable: true, width: 200, render: (value: string[]) => { if (!value || value.length === 0) return '-'; return value.join(', '); }, }, { key: 'enabled', label: 'Aktiv', type: 'boolean' as any, sortable: true, filterable: true, searchable: false, width: 80, }, ]; }, []); // No dependencies - columns are static, roleLabels come from backend // Dynamic options for forms (users and roles) const userOptions = useMemo(() => availableUsers.map(u => ({ value: u.id, label: `${u.username} ${u.email ? `(${u.email})` : ''}` })), [availableUsers]); const roleOptions = useMemo(() => roles.filter(r => !r.featureInstanceId).map(r => ({ value: r.id, label: r.roleLabel })), [roles]); // Form attributes for adding a user - uses dynamic options // Note: This is an operational form for junction table, not direct model editing const addUserFields: AttributeDefinition[] = useMemo(() => { // Check if backend has userId attribute to get label/description const userIdAttr = backendAttributes.find(a => a.name === 'userId'); const roleIdsAttr = backendAttributes.find(a => a.name === 'roleIds'); return [ { name: 'targetUserId', label: userIdAttr?.label || 'Benutzer', type: 'enum' as any, required: true, options: userOptions, }, { name: 'roleIds', label: roleIdsAttr?.label || 'Rollen', type: 'multiselect' as any, required: true, options: roleOptions, } ]; }, [userOptions, roleOptions, backendAttributes]); // Form attributes for editing user roles const editRolesFields: AttributeDefinition[] = useMemo(() => { const roleIdsAttr = backendAttributes.find(a => a.name === 'roleIds'); return [{ name: 'roleIds', label: roleIdsAttr?.label || 'Rollen', type: 'multiselect' as any, required: true, options: roleOptions, }]; }, [roleOptions, backendAttributes]); // Handle add user submit const handleAddUser = async (data: { targetUserId: string; roleIds: string[] }) => { if (!selectedMandateId) return; setIsSubmitting(true); try { const result = await addUserToMandate(selectedMandateId, data); if (result.success) { setShowAddModal(false); fetchMandateUsers(selectedMandateId); } else { showError('Fehler', result.error || 'Fehler beim Hinzufügen des Benutzers'); } } finally { setIsSubmitting(false); } }; // Handle edit roles submit const handleEditRoles = async (data: { roleIds: string[] }) => { if (!selectedMandateId || !editingUser) return; setIsSubmitting(true); try { const result = await updateUserRoles(selectedMandateId, editingUser.userId, data.roleIds); if (result.success) { setEditingUser(null); fetchMandateUsers(selectedMandateId); } else { showError('Fehler', result.error || 'Fehler beim Aktualisieren der Rollen'); } } finally { setIsSubmitting(false); } }; // Handle remove user (confirmation handled by DeleteActionButton) const handleRemoveUser = async (user: MandateUser) => { if (!selectedMandateId) return; const result = await removeUserFromMandate(selectedMandateId, user.userId); if (!result.success) { showError('Fehler', result.error || 'Fehler beim Entfernen des Benutzers'); } }; // Handle edit click const handleEditClick = (user: MandateUser) => { setEditingUser(user); }; // Get mandate name const getMandateName = (mandate: Mandate) => { if (mandate.label) return mandate.label; if (typeof mandate.name === 'object') { return mandate.name.de || mandate.name.en || Object.values(mandate.name)[0] || mandate.id; } return mandate.name || mandate.id; }; if (error && !selectedMandateId) { return (
⚠️

Fehler: {error}

); } return (

Mandanten-Mitglieder

Verwalten Sie, welche Benutzer Zugriff auf welche Mandanten haben

{/* Mandate Selector */}
{selectedMandateId && (
)}
{/* Content */} {!selectedMandateId ? (

Kein Mandant ausgewählt

Wählen Sie einen Mandanten aus, um dessen Mitglieder zu verwalten.

) : loading && users.length === 0 ? (
Lade Mandanten-Mitglieder...
) : users.length === 0 ? (

Keine Mitglieder

Diesem Mandanten sind noch keine Benutzer zugewiesen.

) : (
{ // Find user by UserMandate ID to get userId for API call const user = users.find(u => u.id === userMandateId); if (user) { const result = await removeUserFromMandate(selectedMandateId, user.userId); return result.success; } return false; }, }} emptyMessage="Keine Mitglieder gefunden" />
)} {/* Add User Modal */} {showAddModal && (
setShowAddModal(false)}>
e.stopPropagation()}>

Benutzer zum Mandanten hinzufügen

{availableUsers.length === 0 ? (

Alle Benutzer sind bereits diesem Mandanten zugewiesen.

) : roleOptions.length === 0 ? (
Lade Rollen...
) : ( setShowAddModal(false)} submitButtonText={isSubmitting ? 'Hinzufügen...' : 'Hinzufügen'} cancelButtonText="Abbrechen" /> )}
)} {/* Edit Roles Modal */} {editingUser && (
setEditingUser(null)}>
e.stopPropagation()}>

Rollen bearbeiten: {editingUser.username}

setEditingUser(null)} submitButtonText={isSubmitting ? 'Speichern...' : 'Speichern'} cancelButtonText="Abbrechen" />
)}
); }; export default AdminUserMandatesPage;