/** * 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, FaBuilding } from 'react-icons/fa'; import { useToast } from '../../contexts/ToastContext'; import { useApiRequest } from '../../hooks/useApi'; import { fetchAttributes } from '../../api/attributesApi'; import { resolveColumnTypes } from '../../utils/columnTypeResolver'; import type { ColumnConfig } from '../../components/FormGenerator/FormGeneratorTable'; import styles from './Admin.module.css'; import { useLanguage } from '../../providers/language/LanguageContext'; import { mandateDisplayLabel } from '../../utils/mandateDisplayUtils'; export const AdminUserMandatesPage: React.FC = () => { const { t } = useLanguage(); const { showError } = useToast(); const { request } = useApiRequest(); 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(); fetchAttributes(request, 'UserMandateView') .then(setBackendAttributes) .catch(() => setBackendAttributes([])); }, [fetchMandates, request]); // 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]); const _rawColumns: ColumnConfig[] = useMemo(() => [ { key: 'username', label: t('Benutzername'), sortable: true, filterable: true, searchable: true, width: 150, }, { key: 'email', label: t('E-Mail'), sortable: true, filterable: true, searchable: true, width: 200, }, { key: 'fullName', label: t('Vollständiger Name'), sortable: true, filterable: true, searchable: true, width: 180, }, { key: 'roleLabels', label: t('Rollen'), sortable: false, filterable: false, searchable: true, width: 200, formatter: (value: string[]) => { if (!value || value.length === 0) return '-'; return value.join(', '); }, }, { key: 'enabled', label: t('Aktiv'), sortable: true, filterable: true, searchable: false, width: 80, }, ], [t]); const columns = useMemo( () => resolveColumnTypes(_rawColumns, backendAttributes), [_rawColumns, backendAttributes], ); // 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 || t('Benutzer'), type: 'enum' as any, required: true, options: userOptions, }, { name: 'roleIds', label: roleIdsAttr?.label || t('Rollen'), type: 'multiselect' as any, required: true, options: roleOptions, } ]; }, [userOptions, roleOptions, backendAttributes, t]); // Form attributes for editing user roles const editRolesFields: AttributeDefinition[] = useMemo(() => { const roleIdsAttr = backendAttributes.find(a => a.name === 'roleIds'); return [{ name: 'roleIds', label: roleIdsAttr?.label || t('Rollen'), type: 'multiselect' as any, required: true, options: roleOptions, }]; }, [roleOptions, backendAttributes, t]); // 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(t('Fehler'), result.error || t('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(t('Fehler'), result.error || t('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(t('Fehler'), result.error || t('Fehler beim Entfernen des Benutzers')); } }; // Handle edit click const handleEditClick = (user: MandateUser) => { setEditingUser(user); }; if (error && !selectedMandateId) { return (
⚠️

{t('Fehler')}: {error}

); } return (

{t('Mandanten-Mitglieder')}

{t('Verwalten Sie, welche Benutzer Zugriff')}

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

{t('Kein Mandant ausgewählt')}

{t('Wählen Sie einen Mandanten aus, um dessen Mitglieder zu verwalten.')}

) : (
{ // 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={t('Keine Mitglieder gefunden')} />
)} {/* Add User Modal */} {showAddModal && (

{t('Benutzer zum Mandanten hinzufügen')}

{availableUsers.length === 0 ? (

{t('Alle Benutzer sind bereits diesem')}

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

{t('Rollen bearbeiten')}: {editingUser.username}

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