'use client' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { authFetch, clearToken, getApiBase, getToken } from '../lib/auth' import AdminShell from '../ui/AdminShell' type AdminUser = { username: string role: string authProvider?: string | null lastLoginAt?: string | null isBlocked?: boolean } const formatLastLogin = (value?: string | null) => { if (!value) return 'Never' const date = new Date(value) if (Number.isNaN(date.valueOf())) return value return date.toLocaleString() } export default function UsersPage() { const router = useRouter() const [users, setUsers] = useState([]) const [error, setError] = useState(null) const [loading, setLoading] = useState(true) const [passwordInputs, setPasswordInputs] = useState>({}) const [passwordStatus, setPasswordStatus] = useState>({}) const loadUsers = async () => { try { const baseUrl = getApiBase() const response = await authFetch(`${baseUrl}/admin/users`) if (!response.ok) { if (response.status === 401) { clearToken() router.push('/login') return } if (response.status === 403) { router.push('/') return } throw new Error('Could not load users.') } const data = await response.json() if (Array.isArray(data?.users)) { setUsers( data.users.map((user: any) => ({ username: user.username ?? 'Unknown', role: user.role ?? 'user', authProvider: user.auth_provider ?? 'local', lastLoginAt: user.last_login_at ?? null, isBlocked: Boolean(user.is_blocked), })) ) } else { setUsers([]) } setError(null) } catch (err) { console.error(err) setError('Could not load user list.') } finally { setLoading(false) } } const toggleUserBlock = async (username: string, blocked: boolean) => { try { const baseUrl = getApiBase() const response = await authFetch( `${baseUrl}/admin/users/${encodeURIComponent(username)}/${blocked ? 'block' : 'unblock'}`, { method: 'POST' } ) if (!response.ok) { throw new Error('Update failed') } await loadUsers() } catch (err) { console.error(err) setError('Could not update user access.') } } const updateUserRole = async (username: string, role: string) => { try { const baseUrl = getApiBase() const response = await authFetch( `${baseUrl}/admin/users/${encodeURIComponent(username)}/role`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ role }), } ) if (!response.ok) { throw new Error('Update failed') } await loadUsers() } catch (err) { console.error(err) setError('Could not update user role.') } } const updateUserPassword = async (username: string) => { const newPassword = passwordInputs[username] || '' if (!newPassword || newPassword.length < 8) { setPasswordStatus((current) => ({ ...current, [username]: 'Password must be at least 8 characters.', })) return } try { const baseUrl = getApiBase() const response = await authFetch( `${baseUrl}/admin/users/${encodeURIComponent(username)}/password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: newPassword }), } ) if (!response.ok) { const text = await response.text() throw new Error(text || 'Update failed') } setPasswordInputs((current) => ({ ...current, [username]: '' })) setPasswordStatus((current) => ({ ...current, [username]: 'Password updated.', })) } catch (err) { console.error(err) setPasswordStatus((current) => ({ ...current, [username]: 'Could not update password.', })) } } useEffect(() => { if (!getToken()) { router.push('/login') return } void loadUsers() }, [router]) if (loading) { return
Loading users...
} return ( Reload list } >
{error &&
{error}
} {users.length === 0 ? (
No users found yet.
) : (
{users.map((user) => (
{user.username} Role: {user.role} Login type: {user.authProvider || 'local'} Last login: {formatLastLogin(user.lastLoginAt)}
{user.authProvider === 'local' && (
setPasswordInputs((current) => ({ ...current, [user.username]: event.target.value, })) } />
)} {passwordStatus[user.username] && (
{passwordStatus[user.username]}
)}
))}
)}
) }