"use client"; import { useEffect, useState } from "react"; import { Autocomplete, Button, Chip, EmptyState, Input, Label, ListBox, Modal, SearchField, Select, Spinner, Table, TextField, useFilter, } from "@heroui/react"; import { CreditCard, Pencil, ArrowRotateRight } from "@gravity-ui/icons"; import { api, type IdTag, type UserRow } from "@/lib/api"; import { useSession } from "@/lib/auth-client"; import dayjs from "@/lib/dayjs"; type CreateForm = { name: string; email: string; username: string; password: string; role: string; }; type EditForm = { name: string; username: string; role: string; }; const emptyCreate: CreateForm = { name: "", email: "", username: "", password: "", role: "user" }; const ROLE_OPTIONS = [ { key: "user", label: "用户" }, { key: "admin", label: "管理员" }, ]; const statusColorMap: Record = { Accepted: "success", Blocked: "danger", Expired: "warning", Invalid: "danger", ConcurrentTx: "warning", }; function generateIdTag(): string { const chars = "0123456789ABCDEF"; let result = ""; for (let i = 0; i < 8; i++) { result += chars[Math.floor(Math.random() * chars.length)]; } return result; } function fenToYuan(fen: number): string { return (fen / 100).toFixed(2); } function FreeTagBinder({ freeTags, selected, onSelect, onBind, binding, }: { freeTags: IdTag[]; selected: string; onSelect: (v: string) => void; onBind: () => void; binding: boolean; }) { const { contains } = useFilter({ sensitivity: "base" }); return (
onSelect(key ? String(key) : "")} > {({ isPlaceholder, state }: any) => { if (isPlaceholder || !state.selectedItems?.length) return ( {freeTags.length === 0 ? "暂无无主卡" : "选择卡号"} ); const tag = freeTags.find((t) => t.idTag === state.selectedItems[0]?.key); return tag ? {tag.idTag} : null; }} 无匹配卡号}> {freeTags.map((t) => ( {t.idTag} ¥{(t.balance / 100).toFixed(2)} ))}
); } export default function UsersPage() { const { data: session } = useSession(); const currentUserId = session?.user?.id; const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [updating, setUpdating] = useState(null); const [createForm, setCreateForm] = useState(emptyCreate); const [editTarget, setEditTarget] = useState(null); const [editForm, setEditForm] = useState({ name: "", username: "", role: "user" }); const [saving, setSaving] = useState(false); const [cardsUser, setCardsUser] = useState(null); const [userTags, setUserTags] = useState([]); const [tagsLoading, setTagsLoading] = useState(false); const [newTagId, setNewTagId] = useState(""); const [creatingTag, setCreatingTag] = useState(false); const [unlinkingTag, setUnlinkingTag] = useState(null); const [deletingTag, setDeletingTag] = useState(null); const [freeTags, setFreeTags] = useState([]); const [selectedFreeTag, setSelectedFreeTag] = useState(""); const [bindingTag, setBindingTag] = useState(false); const load = async () => { setLoading(true); try { setUsers(await api.users.list()); } catch { // possibly not admin — show empty } finally { setLoading(false); } }; const handleRefresh = async () => { setRefreshing(true); try { setUsers(await api.users.list()); } catch { // ignore } finally { setRefreshing(false); } }; useEffect(() => { load(); }, []); const openEdit = (u: UserRow) => { setEditTarget(u); setEditForm({ name: u.name ?? "", username: u.username ?? "", role: u.role ?? "user" }); }; const handleCreate = async () => { setSaving(true); try { await api.users.create({ name: createForm.name, email: createForm.email, password: createForm.password, username: createForm.username || undefined, role: createForm.role, }); setCreateForm(emptyCreate); await load(); } finally { setSaving(false); } }; const handleEdit = async () => { if (!editTarget) return; setSaving(true); try { await api.users.update(editTarget.id, { name: editForm.name || undefined, username: editForm.username || null, role: editForm.role, }); await load(); } finally { setSaving(false); } }; const toggleBan = async (u: UserRow) => { setUpdating(u.id); try { await api.users.update(u.id, { banned: !u.banned, banReason: u.banned ? null : "管理员封禁", }); await load(); } finally { setUpdating(null); } }; const loadUserTags = async (userId: string) => { setTagsLoading(true); try { const all = await api.idTags.list(); setUserTags(all.filter((t) => t.userId === userId)); setFreeTags(all.filter((t) => !t.userId)); } finally { setTagsLoading(false); } }; const openCardsModal = (u: UserRow) => { setCardsUser(u); setSelectedFreeTag(""); setUserTags([]); setFreeTags([]); void loadUserTags(u.id); }; const handleCreateTag = async () => { if (!cardsUser || !newTagId) return; setCreatingTag(true); try { await api.idTags.create({ idTag: newTagId, userId: cardsUser.id }); setNewTagId(generateIdTag()); await loadUserTags(cardsUser.id); } finally { setCreatingTag(false); } }; const handleUnlinkTag = async (idTag: string) => { if (!cardsUser) return; setUnlinkingTag(idTag); try { await api.idTags.update(idTag, { userId: null }); await loadUserTags(cardsUser.id); } finally { setUnlinkingTag(null); } }; const handleBindFreeTag = async () => { if (!cardsUser || !selectedFreeTag) return; setBindingTag(true); try { await api.idTags.update(selectedFreeTag, { userId: cardsUser.id }); setSelectedFreeTag(""); await loadUserTags(cardsUser.id); } finally { setBindingTag(false); } }; const handleDeleteTag = async (idTag: string) => { if (!cardsUser) return; setDeletingTag(idTag); try { await api.idTags.delete(idTag); await loadUserTags(cardsUser.id); } finally { setDeletingTag(null); } }; const isEditingSelf = editTarget?.id === currentUserId; return (

用户管理

共 {users.length} 位用户(仅管理员可见)

新增用户 setCreateForm({ ...createForm, name: e.target.value })} /> setCreateForm({ ...createForm, email: e.target.value })} /> setCreateForm({ ...createForm, username: e.target.value })} /> setCreateForm({ ...createForm, password: e.target.value })} />
用户 邮箱 登录名 角色 状态 注册时间 操作 (
{loading ? "加载中…" : "暂无用户或无权限"}
)} > {users.map((u) => (
{u.name ?? "—"}
{u.email} {u.username ?? "—"} {u.role === "admin" ? "管理员" : "用户"} {u.banned ? ( 已封禁 ) : ( 正常 )} {dayjs(u.createdAt).format("YYYY/M/D HH:mm:ss")}
{/* Edit button */} 编辑用户 setEditForm({ ...editForm, name: e.target.value }) } /> setEditForm({ ...editForm, username: e.target.value }) } /> {!isEditingSelf && (
)}
{/* Cards management */} {cardsUser ? `${cardsUser.name ?? cardsUser.username ?? cardsUser.email} 的储值卡` : "储值卡管理"}
setNewTagId(e.target.value)} />
{tagsLoading ? (
) : userTags.length === 0 ? (

该用户暂无储值卡

) : (
卡号 状态 余额 操作 {userTags.map((tag) => ( {tag.idTag} {tag.status} ¥{fenToYuan(tag.balance)}
))}
)} {/* Ban / Unban button — disabled for current account */}
))} ); }