/** * AdminUsersPage * * Admin page for managing Users using FormGeneratorTable. */ import React, { useState, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { useOrgUsers, useUserOperations } from '../../hooks/useUsers'; import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable'; import { FormGeneratorForm } from '../../components/FormGenerator/FormGeneratorForm'; import { FaPlus, FaSync, FaKey, FaEnvelopeOpenText, FaUserShield } from 'react-icons/fa'; import styles from './Admin.module.css'; import { getUserDataCache } from '../../utils/userCache'; import { useLanguage } from '../../providers/language/LanguageContext'; const _PRIVILEGED_FLAGS = ['isSysAdmin', 'isPlatformAdmin'] as const; interface User { id: string; username: string; email: string; fullName: string; enabled: boolean; isSysAdmin?: boolean; isPlatformAdmin?: boolean; [key: string]: any; } export const AdminUsersPage: React.FC = () => { const { t } = useLanguage(); const navigate = useNavigate(); // Use two hooks: one for data, one for operations const { data: users, attributes, permissions, pagination, loading, error, refetch, fetchUserById, updateOptimistically, } = useOrgUsers(); const { handleUserCreate: createUser, handleUserUpdate: updateUser, handleUserDelete: deleteUser, handleSendPasswordLink, handleInlineUpdate, sendingPasswordLink: sendingPasswordLinkState, } = useUserOperations(); const [showCreateModal, setShowCreateModal] = useState(false); const [editingUser, setEditingUser] = useState(null); // 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, fkSource: (attr as any).fkSource, fkDisplayField: (attr as any).fkDisplayField, })); }, [attributes]); // Check permissions const canCreate = permissions?.create !== 'n'; const canUpdate = permissions?.update !== 'n'; const canDelete = permissions?.delete !== 'n'; // Handle edit click const handleEditClick = async (user: User) => { const fullUser = await fetchUserById(user.id); if (fullUser) { setEditingUser(fullUser as User); } }; // Handle create submit const handleCreateSubmit = async (data: Partial) => { const result = await createUser(data as Omit); if (result.success) { setShowCreateModal(false); refetch(); // Refresh the list } }; // Handle edit submit const handleEditSubmit = async (data: Partial) => { if (!editingUser) return; const result = await updateUser(editingUser.id, data); if (result.success) { setEditingUser(null); refetch(); // Refresh the list } }; // Handle delete (confirmation handled by DeleteActionButton) const handleDeleteUser = async (user: User) => { const success = await deleteUser(user.id); if (success) { refetch(); // Refresh the list } }; // Handle send password link const handleSendPassword = async (user: User) => { await handleSendPasswordLink(user.id); }; // Privileged-flag gating mirrors the backend rules in routeDataUsers.update_user // and create_user: only a Platform-Admin may set isSysAdmin / isPlatformAdmin, // and even then never on themselves (Self-Protection). const currentUserCache = getUserDataCache(); const callerIsPlatformAdmin = currentUserCache?.isPlatformAdmin === true; const callerId = currentUserCache?.id; const _buildFormAttributes = (mode: 'create' | 'edit', targetUserId?: string) => { const excludedFields = ['id', 'hashedPassword', 'authenticationAuthority']; const isSelfEdit = mode === 'edit' && targetUserId !== undefined && targetUserId === callerId; // Caller may flip flags only when PlatformAdmin AND not editing themselves. const flagsEditable = callerIsPlatformAdmin && !isSelfEdit; return (attributes || []) .filter(attr => !excludedFields.includes(attr.name)) .map(attr => { if (_PRIVILEGED_FLAGS.includes(attr.name as any) && !flagsEditable) { return { ...attr, editable: false }; } if (attr.name === 'username') { return { ...attr, editable: false }; } return attr; }); }; const formAttributesCreate = useMemo( () => _buildFormAttributes('create'), [attributes, callerIsPlatformAdmin], ); const formAttributesEdit = useMemo( () => _buildFormAttributes('edit', editingUser?.id), [attributes, callerIsPlatformAdmin, callerId, editingUser?.id], ); if (error) { return (
⚠️

{t('Fehler beim Laden der Benutzer')}: {error}

); } return (

{t('Benutzer')}

{t('Verwalten Sie alle Benutzer im')}

{canCreate && ( )}
, onClick: handleSendPassword, title: t('Passwort-Link senden'), loading: (row: User) => sendingPasswordLinkState.has(row.id), } ] : []} onDelete={handleDeleteUser} hookData={{ refetch, permissions, pagination, handleDelete: deleteUser, handleInlineUpdate, updateOptimistically, }} emptyMessage={t('Keine Benutzer gefunden')} />
{/* Create Modal */} {showCreateModal && (

{t('Neuer Benutzer')}

{formAttributesCreate.length === 0 ? (
{t('Lade Formular')}
) : ( setShowCreateModal(false)} submitButtonText={t('Erstellen')} cancelButtonText={t('Abbrechen')} /> )}
)} {/* Edit Modal */} {editingUser && (

{t('Benutzer bearbeiten')}

{formAttributesEdit.length === 0 ? (
{t('Lade Formular')}
) : ( setEditingUser(null)} submitButtonText={t('Speichern')} cancelButtonText={t('Abbrechen')} /> )}
)}
); }; export default AdminUsersPage;