"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)} )) )}
); }