import { useState, useEffect, useCallback } from 'react'
import { useAuth } from '../contexts/AuthContext'
function apiHeaders(token) {
return { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }
}
async function apiFetch(url, options, token) {
const res = await fetch(`/api${url}`, { ...options, headers: { ...apiHeaders(token), ...options?.headers } })
if (!res.ok) {
const err = await res.json().catch(() => ({}))
throw new Error(err.detail || 'Request failed')
}
if (res.status === 204) return null
return res.json()
}
function UserModal({ user, onClose, onSave, token }) {
const isNew = !user.id
const [form, setForm] = useState({
username: user.username || '',
full_name: user.full_name || '',
email: user.email || '',
password: '',
is_admin: user.is_admin || false,
})
const [saving, setSaving] = useState(false)
const [error, setError] = useState(null)
const set = (k, v) => setForm(f => ({ ...f, [k]: v }))
const handleSave = async () => {
if (isNew && !form.password) { setError('Password is required for new users.'); return }
if (form.password && form.password.length < 6) { setError('Password must be at least 6 characters.'); return }
setSaving(true)
setError(null)
try {
if (isNew) {
await apiFetch('/admin/users', { method: 'POST', body: JSON.stringify(form) }, token)
} else {
const { password, username, ...updateData } = form
await apiFetch(`/admin/users/${user.id}`, { method: 'PUT', body: JSON.stringify(updateData) }, token)
}
onSave()
} catch (e) {
setError(e.message)
} finally {
setSaving(false)
}
}
return (
{isNew ? 'Add New User' : `Edit: ${user.username}`}
{error &&
{error}
}
)
}
function ResetPasswordModal({ user, onClose, token }) {
const [password, setPassword] = useState('')
const [confirm, setConfirm] = useState('')
const [saving, setSaving] = useState(false)
const [success, setSuccess] = useState(false)
const [error, setError] = useState(null)
const handleReset = async () => {
if (password.length < 6) { setError('Password must be at least 6 characters.'); return }
if (password !== confirm) { setError('Passwords do not match.'); return }
setSaving(true)
setError(null)
try {
await apiFetch(`/admin/users/${user.id}/reset-password`, { method: 'POST', body: JSON.stringify({ new_password: password }) }, token)
setSuccess(true)
} catch (e) {
setError(e.message)
} finally {
setSaving(false)
}
}
return (
Reset Password
Set a new password for {user.username}.
{success
?
✅ Password reset successfully!
: (
<>
{error &&
{error}
}
>
)}
{!success && (
)}
)
}
export default function Admin() {
const { token, user: currentUser } = useAuth()
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [modal, setModal] = useState(null) // { type: 'edit'|'reset', user }
const fetchUsers = useCallback(async () => {
setLoading(true)
try {
const data = await apiFetch('/admin/users', {}, token)
setUsers(data)
} catch (e) {
setError(e.message)
} finally {
setLoading(false)
}
}, [token])
useEffect(() => { fetchUsers() }, [fetchUsers])
const deleteUser = async (u) => {
if (!window.confirm(`Delete user "${u.username}"? This cannot be undone.`)) return
try {
await apiFetch(`/admin/users/${u.id}`, { method: 'DELETE' }, token)
fetchUsers()
} catch (e) { alert(e.message) }
}
const toggleActive = async (u) => {
try {
await apiFetch(`/admin/users/${u.id}`, { method: 'PUT', body: JSON.stringify({ is_active: !u.is_active }) }, token)
fetchUsers()
} catch (e) { alert(e.message) }
}
return (
{modal?.type === 'edit' && (
setModal(null)} onSave={() => { setModal(null); fetchUsers() }} />
)}
{modal?.type === 'reset' && (
{ setModal(null); fetchUsers() }} />
)}
Admin Panel — User Management
Tip: Make sure to change the default admin password after first login!
You are logged in as {currentUser?.username}.
{error && {error}
}
{loading ? (
Loading users...
) : (
| Username |
Full Name |
Email |
Role |
Status |
Created |
Actions |
{users.map(u => (
|
{u.username}
{u.id === currentUser?.id && (you)}
|
{u.full_name || '—'} |
{u.email || '—'} |
{u.is_admin ? '👑 Admin' : '👤 User'}
|
{u.is_active ? '● Active' : '○ Disabled'}
|
{u.created_at ? new Date(u.created_at).toLocaleDateString() : '—'}
|
|
))}
)}
)
}