"use client"; import { useCallback, useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { Button, Card, Input } from "@/shared/components"; import Modal, { ConfirmModal } from "@/shared/components/Modal"; import useUserStore from "@/store/userStore"; import { formatVietnamDateTime } from "@/shared/utils/dateTime"; const EMPTY_FORM = { username: "", password: "", role: "user", isActive: true }; function formatDate(value) { if (!value) return "—"; return formatVietnamDateTime(value, { dateStyle: "medium", timeStyle: "short" }) || "—"; } export default function UsersPage() { const router = useRouter(); const user = useUserStore((state) => state.user); const fetchCurrentUser = useUserStore((state) => state.fetchCurrentUser); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const [editor, setEditor] = useState(null); const [form, setForm] = useState(EMPTY_FORM); const [deleteTarget, setDeleteTarget] = useState(null); const loadUsers = useCallback(async () => { setLoading(true); setError(""); try { const response = await fetch("/api/users", { cache: "no-store" }); if (response.status === 403) { router.replace("/dashboard"); return; } const data = await response.json(); if (!response.ok) throw new Error(data.error || "Failed to load users"); setUsers(data.users || []); } catch (requestError) { setError(requestError.message || "Failed to load users"); } finally { setLoading(false); } }, [router]); useEffect(() => { if (!user) fetchCurrentUser(); }, [fetchCurrentUser, user]); useEffect(() => { if (!user) return undefined; if (user.role !== "admin") { router.replace("/dashboard"); return undefined; } const frameId = window.requestAnimationFrame(() => { void loadUsers(); }); return () => window.cancelAnimationFrame(frameId); }, [loadUsers, router, user]); const openCreate = () => { setError(""); setForm(EMPTY_FORM); setEditor({ mode: "create" }); }; const openEdit = (target) => { setError(""); setForm({ username: target.username, password: "", role: target.role, isActive: target.isActive }); setEditor({ mode: "edit", user: target }); }; const saveUser = async (event) => { event.preventDefault(); setSaving(true); setError(""); try { const isCreate = editor.mode === "create"; const payload = { ...form }; if (!payload.password) delete payload.password; const response = await fetch(isCreate ? "/api/users" : `/api/users/${editor.user.id}`, { method: isCreate ? "POST" : "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); const data = await response.json(); if (!response.ok) throw new Error(data.error || "Failed to save user"); setEditor(null); await loadUsers(); } catch (requestError) { setError(requestError.message || "Failed to save user"); } finally { setSaving(false); } }; const deleteUser = async () => { if (!deleteTarget) return; setSaving(true); setError(""); try { const response = await fetch(`/api/users/${deleteTarget.id}`, { method: "DELETE" }); const data = await response.json(); if (!response.ok) throw new Error(data.error || "Failed to delete user"); setDeleteTarget(null); await loadUsers(); } catch (requestError) { setError(requestError.message || "Failed to delete user"); } finally { setSaving(false); } }; if (!user || user.role !== "admin") { return
Administration
Manage dashboard accounts and access roles.
{error}
: null}| Username | Role | Status | Created | Actions |
|---|---|---|---|---|
| Loading users… | ||||
| No users found. | ||||
| {entry.username}{entry.id === user.id ? (you) : null} | {entry.role} | {entry.isActive ? "Active" : "Disabled"} | {formatDate(entry.createdAt)} |
|