import EditOutlined from "@mui/icons-material/EditOutlined";
import GppBadOutlined from "@mui/icons-material/GppBadOutlined";
import TrendingUpOutlined from "@mui/icons-material/TrendingUpOutlined";
import Avatar from "@mui/material/Avatar";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import { useCallback, useMemo, useState } from "react";
import { useToast } from "@/shared/ui/ToastProvider.jsx";
import { banUser, grantUserVip, listUsers, updateUser, updateUserLevels } from "../api/business.js";
import { useExternalAuth } from "../auth/ExternalAuthProvider.jsx";
import { UserBanDialog, UserEditDialog, UserLevelDialog } from "../components/UserDialogs.jsx";
import { hasExternalCapability } from "../config/capabilities.js";
import { ExternalPage, ExternalPageState, PagePagination, ResponsiveDataList, StatusChip } from "../shared/PageComponents.jsx";
import { usePagedResource } from "../shared/usePagedResource.js";
export default function UsersPage() {
const { session } = useExternalAuth();
const { showToast } = useToast();
const [filters, setFilters] = useState({ keyword: "", status: "" });
const [query, setQuery] = useState({ keyword: "", page: 1, status: "" });
const [dialog, setDialog] = useState({ type: "", user: null });
const [submitting, setSubmitting] = useState(false);
const load = useCallback(() => listUsers({ keyword: query.keyword, page: query.page, page_size: 20, status: query.status }), [query]);
const { data, error, loading, reload } = usePagedResource(load);
const canEdit = hasExternalCapability(session, "user:update");
const canBan = hasExternalCapability(session, "user:ban");
const canGrantLevel = hasExternalCapability(session, "user-level:grant");
const closeDialog = () => setDialog({ type: "", user: null });
const execute = async (request, successMessage) => {
setSubmitting(true);
try {
await request();
showToast(successMessage, "success");
closeDialog();
reload();
} catch (requestError) {
showToast(requestError.message || "用户操作失败", "error");
} finally {
setSubmitting(false);
}
};
const columns = useMemo(() => [
{ key: "user", label: "用户", render: (user) => {user.username.slice(0, 1)}{user.username || "-"}ID {user.id} },
{ key: "prettyId", label: "短 ID(靓号)", render: (user) => user.prettyId || "-" },
{ key: "country", label: "国家/性别", render: (user) => `${user.country || "-"} / ${user.gender || "-"}` },
{ key: "level", label: "等级", render: (user) => `财富 ${user.wealthLevel} / 魅力 ${user.charmLevel} / VIP ${user.vipLevel}` },
{ key: "status", label: "状态", render: (user) => },
{ key: "actions", label: "操作", render: (user) => {canEdit ? : null}{canGrantLevel ? : null}{canBan && !user.banned ? : null} }
], [canBan, canEdit, canGrantLevel]);
return (
{ event.preventDefault(); setQuery({ ...filters, page: 1 }); }}>
setFilters({ ...filters, keyword: event.target.value })} />
setFilters({ ...filters, status: event.target.value })}>
{!loading && !error ? <> item.id} /> setQuery({ ...query, page })} />> : null}
execute(() => updateUser(dialog.user.id, form), "用户信息已更新")} />
execute(() => banUser(dialog.user.id, { expires_at_ms: Number(form.days) === 0 ? 0 : Date.now() + Number(form.days) * 86400000, reason: form.reason.trim() }), "用户已封禁")} />
execute(() => form.track === "vip"
? grantUserVip(dialog.user.id, { commandId: createLevelCommandId(), durationMs: Number(form.days) * 86400000, level: Number(form.level), reason: form.reason.trim() })
: updateUserLevels(dialog.user.id, { adjustments: [{ duration_days: Number(form.days), level: Number(form.level), track: form.track }], reason: form.reason.trim() }), "用户等级已下发")} />
);
}
function createLevelCommandId() {
return `external-level-grant-${globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`}`;
}