"use client" import { FormEvent, useEffect, useMemo, useState } from "react" import { useRouter } from "next/navigation" type UserDoc = Record type UserFormState = { email: string username: string name: string wallet_address: string role: string password: string } const API_BASE = "http://127.0.0.1:5000" const initialUserForm: UserFormState = { email: "", username: "", name: "", wallet_address: "", role: "student", password: "", } const getUserId = (user: UserDoc) => String(user._id || user.id || "") export default function AdminUsersPage() { const router = useRouter() const [ready, setReady] = useState(false) const [loading, setLoading] = useState(false) const [saving, setSaving] = useState(false) const [actionLoadingId, setActionLoadingId] = useState("") const [users, setUsers] = useState([]) const [selectedUser, setSelectedUser] = useState(null) const [search, setSearch] = useState("") const [statusFilter, setStatusFilter] = useState("all") const [roleFilter, setRoleFilter] = useState("all") const [pagination, setPagination] = useState({ page: 1, limit: 25, total: 0, pages: 1 }) const [message, setMessage] = useState("") const [createForm, setCreateForm] = useState(initialUserForm) const [editMode, setEditMode] = useState(false) const [editId, setEditId] = useState("") const [editForm, setEditForm] = useState(initialUserForm) const getToken = () => localStorage.getItem("admin_token") const headers = () => { const token = getToken() return token ? { "Content-Type": "application/json", Authorization: `Bearer ${token}` } : { "Content-Type": "application/json" } } const ensureAuth = async () => { const token = getToken() if (!token) { router.push("/admin/login") return false } const resp = await fetch(`${API_BASE}/api/admin/test`, { headers: headers() }) if (!resp.ok) { localStorage.removeItem("admin_token") router.push("/admin/login") return false } return true } const fetchUsers = async (page = 1, nextSearch = search, nextStatus = statusFilter, nextRole = roleFilter) => { setLoading(true) try { const params = new URLSearchParams({ page: String(page), limit: String(pagination.limit), }) if (nextSearch.trim()) params.set("search", nextSearch.trim()) if (nextStatus !== "all") params.set("status", nextStatus) if (nextRole !== "all") params.set("role", nextRole) const resp = await fetch(`${API_BASE}/api/admin/users?${params.toString()}`, { headers: headers() }) if (!resp.ok) { setUsers([]) setMessage("Failed to load users.") return } const data = await resp.json() setUsers(Array.isArray(data.users) ? data.users : []) if (data.pagination) setPagination(data.pagination) setMessage("") } catch { setUsers([]) setMessage("Network error while loading users.") } finally { setLoading(false) } } const handleCreateUser = async (e: FormEvent) => { e.preventDefault() setSaving(true) setMessage("") try { const payload: Record = { email: createForm.email.trim(), username: createForm.username.trim(), name: createForm.name.trim(), wallet_address: createForm.wallet_address.trim(), role: createForm.role, } if (createForm.password.trim()) payload.password = createForm.password const resp = await fetch(`${API_BASE}/api/admin/users`, { method: "POST", headers: headers(), body: JSON.stringify(payload), }) const data = await resp.json().catch(() => ({})) if (!resp.ok) { setMessage(String(data.error || "Failed to create user.")) return } setCreateForm(initialUserForm) setMessage("User created successfully.") await fetchUsers(1) } catch { setMessage("Network error while creating user.") } finally { setSaving(false) } } const startEdit = (user: UserDoc) => { setEditMode(true) setEditId(getUserId(user)) setEditForm({ email: String(user.email || ""), username: String(user.username || ""), name: String(user.name || ""), wallet_address: String(user.wallet_address || ""), role: String(user.role || "student"), password: "", }) } const submitEdit = async (e: FormEvent) => { e.preventDefault() if (!editId) return setSaving(true) setMessage("") try { const payload: Record = { email: editForm.email.trim(), username: editForm.username.trim(), name: editForm.name.trim(), wallet_address: editForm.wallet_address.trim(), role: editForm.role, } if (editForm.password.trim()) payload.password = editForm.password const resp = await fetch(`${API_BASE}/api/admin/users/${editId}`, { method: "PUT", headers: headers(), body: JSON.stringify(payload), }) const data = await resp.json().catch(() => ({})) if (!resp.ok) { setMessage(String(data.error || "Failed to update user.")) return } setEditMode(false) setEditId("") setEditForm(initialUserForm) setMessage("User updated successfully.") await fetchUsers(pagination.page) } catch { setMessage("Network error while updating user.") } finally { setSaving(false) } } const quickAction = async ( userId: string, action: "suspend" | "ban" | "activate" | "delete" | "reset-password", role?: string, ) => { if (!userId) return setActionLoadingId(`${userId}:${action}`) setMessage("") try { let endpoint = `${API_BASE}/api/admin/users/${userId}/${action}` let method: "POST" | "DELETE" = "POST" let body: string | undefined if (action === "delete") method = "DELETE" if (action === "reset-password") body = JSON.stringify({ new_password: "TempPass@123" }) if (action === "suspend" || action === "ban" || action === "activate") { endpoint = `${API_BASE}/api/admin/users/${userId}/status` const statusMap: Record = { suspend: "suspended", ban: "banned", activate: "active" } body = JSON.stringify({ status: statusMap[action] }) } if (role) { endpoint = `${API_BASE}/api/admin/users/${userId}/role` body = JSON.stringify({ role }) } const resp = await fetch(endpoint, { method, headers: headers(), body, }) const data = await resp.json().catch(() => ({})) if (!resp.ok) { setMessage(String(data.error || "Action failed.")) return } setMessage(role ? `Role updated to ${role}.` : `User action ${action} completed.`) await fetchUsers(pagination.page) } catch { setMessage("Network error while running action.") } finally { setActionLoadingId("") } } const roleSet = useMemo(() => { const roles = new Set() for (const user of users) { const value = String(user.role || "").trim() if (value) roles.add(value) } return Array.from(roles) }, [users]) useEffect(() => { const init = async () => { const ok = await ensureAuth() if (!ok) return setReady(true) await fetchUsers(1) } init() }, []) if (!ready) { return (

Loading users...

) } return (

User Management

Manage accounts, roles, access status, and student progress from real database records.

Create User

setCreateForm((p) => ({ ...p, email: e.target.value }))} placeholder="Email" required className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" /> setCreateForm((p) => ({ ...p, username: e.target.value }))} placeholder="Username" required className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" /> setCreateForm((p) => ({ ...p, name: e.target.value }))} placeholder="Full name" className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" /> setCreateForm((p) => ({ ...p, wallet_address: e.target.value }))} placeholder="Wallet address" className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" /> setCreateForm((p) => ({ ...p, password: e.target.value }))} placeholder="Password (optional)" type="password" className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" />

Edit User

{!editMode ? (

Select a user from the table to edit details.

) : ( <> setEditForm((p) => ({ ...p, email: e.target.value }))} placeholder="Email" required className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" /> setEditForm((p) => ({ ...p, username: e.target.value }))} placeholder="Username" required className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" /> setEditForm((p) => ({ ...p, name: e.target.value }))} placeholder="Full name" className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" /> setEditForm((p) => ({ ...p, wallet_address: e.target.value }))} placeholder="Wallet address" className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" /> setEditForm((p) => ({ ...p, password: e.target.value }))} placeholder="Set new password (optional)" type="password" className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" />
)}
setSearch(e.target.value)} placeholder="Search email, username, full name" className="min-w-[220px] flex-1 rounded-md border border-gray-300 px-3 py-2 text-sm dark:border-gray-700 dark:bg-gray-800" />
{message ? (
{message}
) : null}
{loading ? ( ) : users.length === 0 ? ( ) : ( users.map((user, idx) => { const userId = getUserId(user) const status = String(user.status || "active") const progress = Number(user.progress_percent || user.progress || 0) return ( ) }) )}
Email Username Role Status Progress Actions
Loading users...
No users found.
{String(user.email || "-")} {String(user.username || user.name || "-")} {String(user.role || "student")} {status} {Number.isFinite(progress) ? `${progress}%` : "0%"}
Page {pagination.page} of {pagination.pages} | Total {pagination.total}
{selectedUser ? (

Full User Document

              {JSON.stringify(selectedUser, null, 2)}
            
) : null}
) }