"use client"; import { useState } from "react"; import type { AdminSnapshot, AdminUserSummary, SettingsSnapshot, UserRole, } from "@/lib/types"; interface SettingsResponse { ok?: boolean; error?: string; settings?: SettingsSnapshot; } interface UserResponse { ok?: boolean; error?: string; user?: AdminUserSummary; } interface SettingsFormState { defaultMonthlyQuota: string; } interface CreateUserDraft { username: string; password: string; role: UserRole; monthlyQuota: string; } type EditableAdminUser = Omit & { monthlyQuota: string | null; passwordDraft: string; }; function toEditableUser(user: AdminUserSummary): EditableAdminUser { return { ...user, monthlyQuota: user.monthlyQuota === null ? null : String(user.monthlyQuota), passwordDraft: "", }; } function getErrorMessage(error: unknown, fallbackMessage: string): string { return error instanceof Error ? error.message : fallbackMessage; } export default function AdminDashboard({ initialSnapshot, }: { initialSnapshot: AdminSnapshot; }) { const [settings, setSettings] = useState({ defaultMonthlyQuota: String(initialSnapshot.settings.defaultMonthlyQuota), }); const [users, setUsers] = useState( initialSnapshot.users.map(toEditableUser), ); const [feedback, setFeedback] = useState(""); const [errorMessage, setErrorMessage] = useState(""); const [isSavingSettings, setIsSavingSettings] = useState(false); const [createDraft, setCreateDraft] = useState({ username: "", password: "", role: "user", monthlyQuota: settings.defaultMonthlyQuota, }); async function saveSettings(): Promise { setFeedback(""); setErrorMessage(""); setIsSavingSettings(true); try { const response = await fetch("/api/admin/settings", { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ defaultMonthlyQuota: settings.defaultMonthlyQuota, }), }); const payload = (await response.json()) as SettingsResponse; if (!response.ok || payload.ok !== true || !payload.settings) { throw new Error(payload.error || "保存默认额度失败。"); } setSettings({ defaultMonthlyQuota: String(payload.settings.defaultMonthlyQuota), }); setFeedback("默认额度已更新。"); } catch (error) { setErrorMessage(getErrorMessage(error, "保存默认额度失败。")); } finally { setIsSavingSettings(false); } } async function createUser(): Promise { setFeedback(""); setErrorMessage(""); try { const response = await fetch("/api/admin/users", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(createDraft), }); const payload = (await response.json()) as UserResponse; if (!response.ok || payload.ok !== true || !payload.user) { throw new Error(payload.error || "创建用户失败。"); } const createdUser = payload.user; setUsers((currentUsers) => [ ...currentUsers, toEditableUser(createdUser), ]); setCreateDraft({ username: "", password: "", role: "user", monthlyQuota: settings.defaultMonthlyQuota, }); setFeedback("用户已创建。"); } catch (error) { setErrorMessage(getErrorMessage(error, "创建用户失败。")); } } async function saveUser(user: EditableAdminUser): Promise { setFeedback(""); setErrorMessage(""); try { const response = await fetch(`/api/admin/users/${user.id}`, { method: "PATCH", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ role: user.role, monthlyQuota: user.role === "admin" ? null : user.monthlyQuota, isActive: user.isActive, password: user.passwordDraft || undefined, }), }); const payload = (await response.json()) as UserResponse; if (!response.ok || payload.ok !== true || !payload.user) { throw new Error(payload.error || "保存用户失败。"); } const savedUser = payload.user; setUsers((currentUsers) => currentUsers.map((item) => item.id === user.id ? toEditableUser(savedUser) : item, ), ); setFeedback(`已保存 ${savedUser.username}。`); } catch (error) { setErrorMessage(getErrorMessage(error, "保存用户失败。")); } } return (

Policy

默认额度设置

{initialSnapshot.periodKey}

没有单独指定额度的普通用户,将继承这里的月额度。

Users

创建用户

{(feedback || errorMessage) && (
{errorMessage || feedback}
)}

Manage

用户管理

用户 角色 额度 本月已用 状态 重置密码 操作
{users.map((user) => (
{user.username} {user.isUnlimited ? "无限额" : `剩余 ${user.remaining}`}
setUsers((currentUsers) => currentUsers.map((item) => item.id === user.id ? { ...item, monthlyQuota: event.target.value === "" ? null : event.target.value, } : item, ), ) } /> {user.usedThisPeriod} setUsers((currentUsers) => currentUsers.map((item) => item.id === user.id ? { ...item, passwordDraft: event.target.value } : item, ), ) } />
))}
); }