"use client";
import { useState, useTransition } from "react";
import Link from "next/link";
import { ChevronRight, Eye, UserPlus, Users } from "lucide-react";
import {
apiAdminAnalytics,
type AnalyticsOverview,
type NewUser,
} from "@/lib/api";
import { postTypeLabel } from "@/lib/postMeta";
import { formatRelative } from "@/lib/format";
import { postPath, userPath } from "@/lib/urlStyle";
import Avatar from "@/components/Avatar";
import { useUrlStyle } from "@/components/SiteBrandProvider";
import { AdminSegmented } from "@/components/admin";
import {
AdminChartCard,
DualLineChart,
NamedBarChart,
NamedPieChart,
SingleAreaChart,
VisitTrendChart,
} from "@/components/admin/charts";
type RangeKey = "7d" | "30d";
const fmt = (n: number) => n.toLocaleString("zh-CN");
function PanelHead({ title, extra }: { title: string; extra?: string }) {
return (
{title}
{extra && (
{extra}
)}
);
}
function mergePostCommentSeries(data: AnalyticsOverview) {
const map = new Map();
for (const row of data.posts_series) {
map.set(row.date, { date: row.date, posts: row.count, comments: 0 });
}
for (const row of data.comments_series) {
const prev = map.get(row.date);
if (prev) prev.comments = row.count;
else map.set(row.date, { date: row.date, posts: 0, comments: row.count });
}
return [...map.values()].sort((a, b) => a.date.localeCompare(b.date));
}
export default function DashboardClient({
initialAnalytics,
newUsers,
totalUsers,
canUsers,
}: {
initialAnalytics: AnalyticsOverview;
newUsers: NewUser[];
totalUsers?: number;
canUsers: boolean;
}) {
const urlStyle = useUrlStyle();
const [range, setRange] = useState("7d");
const [data, setData] = useState(initialAnalytics);
const [error, setError] = useState(null);
const [pending, startTransition] = useTransition();
const switchRange = (next: RangeKey) => {
if (next === range || pending) return;
setRange(next);
setError(null);
startTransition(async () => {
try {
const res = await apiAdminAnalytics(next);
setData(res);
} catch {
setError("趋势数据加载失败,请稍后重试");
}
});
};
const activitySeries = mergePostCommentSeries(data);
const registerSeries = data.registers_series.map((r) => ({
date: r.date,
count: r.count,
}));
const boardBars = data.boards.map((b) => ({
label: b.label || b.key,
count: b.count,
}));
const typeSlices = data.post_types.map((t) => ({
label: postTypeLabel(t.key),
count: t.count,
}));
return (
{pending ? "正在切换区间…" : `近 ${data.range_days} 日趋势`}
ariaLabel="分析时间范围"
size="sm"
role="group"
value={range}
onChange={switchRange}
options={[
{ key: "7d", label: "近 7 日" },
{ key: "30d", label: "近 30 日" },
]}
/>
{error && (
{error}
)}
{newUsers.length === 0 ? (
暂无成员数据
) : (
newUsers.slice(0, 8).map((u) => (
{u.nickname}
@{u.username}
{formatRelative(u.created_at)}加入
))
)}
{canUsers && (
前往用户管理
)}
{data.top_viewed.length === 0 ? (
暂无浏览数据
) : (
data.top_viewed.map((p, i) => (
{i + 1}
{p.title}
{fmt(p.view_count)}
))
)}
);
}