'use client'; import { useCallback, useEffect, useState } from 'react'; import { useTranslations } from 'next-intl'; import { useAuthStore } from '@/lib/stores/auth-store'; import { UserAccessModal } from './components/UserAccessModal'; const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'; interface User { id: string; username: string; // WINDOWS #15: ein Konto ohne Adresse (kollidierte AD-Adresse) ist // moeglich, seit User.email optional ist. Die Handanlage (UserFormData) // verlangt weiterhin eine Adresse. email: string | null; displayName: string | null; role: 'SUPER_ADMIN' | 'ADMIN' | 'USER'; isActive: boolean; tenantId: string; createdAt: string; } interface UserFormData { username: string; email: string; password: string; displayName: string; role: 'SUPER_ADMIN' | 'ADMIN' | 'USER'; } /** * Admin users page -- User CRUD management (D-12). * ADMIN and SUPER_ADMIN can access. ADMIN sees only own-tenant users. */ export default function AdminUsersPage() { const t = useTranslations('admin.users'); const tCommon = useTranslations('common'); const tHeader = useTranslations('header'); const currentUser = useAuthStore((s) => s.user); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editingUser, setEditingUser] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); const [detailsUser, setDetailsUser] = useState(null); const [formData, setFormData] = useState({ username: '', email: '', password: '', displayName: '', role: 'USER', }); // Access check: only ADMIN and SUPER_ADMIN const hasAccess = currentUser?.role === 'ADMIN' || currentUser?.role === 'SUPER_ADMIN'; const fetchUsers = useCallback(async () => { try { const res = await fetch(`${API_URL}/users`, { credentials: 'include', }); if (res.ok) { setUsers(await res.json()); } } catch { // silently fail } finally { setLoading(false); } }, []); useEffect(() => { if (hasAccess) { fetchUsers(); } else { setLoading(false); } }, [hasAccess, fetchUsers]); const openCreate = () => { setEditingUser(null); setFormData({ username: '', email: '', password: '', displayName: '', role: 'USER', }); setShowForm(true); }; const openEdit = (user: User) => { setEditingUser(user); setFormData({ username: user.username, email: user.email ?? '', password: '', displayName: user.displayName ?? '', role: user.role, }); setShowForm(true); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const url = editingUser ? `${API_URL}/users/${editingUser.id}` : `${API_URL}/users`; const method = editingUser ? 'PATCH' : 'POST'; const body: Record = { username: formData.username, email: formData.email, displayName: formData.displayName || undefined, role: formData.role, }; // Only include password if provided if (formData.password) { body.password = formData.password; } try { const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(body), }); if (res.ok) { setShowForm(false); fetchUsers(); } } catch { // silently fail } }; const handleDelete = async (id: string) => { try { const res = await fetch(`${API_URL}/users/${id}`, { method: 'DELETE', credentials: 'include', }); if (res.ok) { setDeleteConfirm(null); fetchUsers(); } } catch { // silently fail } }; if (!hasAccess) { return (

{tCommon('accessDenied')}

); } const roleBadgeClass = (role: string) => { switch (role) { case 'SUPER_ADMIN': return 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400'; case 'ADMIN': return 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400'; default: return 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400'; } }; return (
{/* Page header */}

{t('title')}

{/* Users table */} {loading ? (

{tCommon('loading')}

) : users.length === 0 ? (

{t('noUsers')}

) : (
{users.map((user) => ( ))}
{t('username')} {t('email')} {t('displayName')} {t('role')} {t('status')} {t('actions')}
{user.username} {user.email ?? '–'} {user.displayName ?? '-'} {tHeader(`role.${user.role}`)} {user.isActive ? tCommon('active') : tCommon('inactive')}
)} {/* Create/Edit modal */} {showForm && (

{editingUser ? t('edit') : t('create')}

setFormData({ ...formData, username: e.target.value }) } className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
setFormData({ ...formData, email: e.target.value }) } className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
setFormData({ ...formData, password: e.target.value }) } className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
setFormData({ ...formData, displayName: e.target.value }) } className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
)} {/* Delete confirmation modal */} {deleteConfirm && (

{t('deleteConfirm')}

)} {/* User-Detail-Zugriff (D-16): geerbte + direkte Modulfreigaben */} {detailsUser && ( setDetailsUser(null)} /> )}
); }