import type { Metadata } from "next";
import Link from "next/link";
import { cookies } from "next/headers";
import {
Users,
PenLine,
MessageCircle,
Activity,
Megaphone,
Palette,
ChevronRight,
UserPlus,
LayoutGrid,
} from "lucide-react";
import { fetchOverview, type OverviewResponse } from "@/lib/api";
import { authCookieHeader } from "@/lib/cookies";
import { getMeCached } from "@/lib/serverData";
import Avatar from "@/components/Avatar";
import { formatRelative } from "@/lib/format";
export const metadata: Metadata = {
title: "管理仪表盘",
};
// 精确千分位(管理员视角需要真实数值,不用首页的紧凑 K/M)
const fmt = (n: number) => n.toLocaleString("zh-CN");
function greeting(hour: number) {
if (hour < 6) return "凌晨好";
if (hour < 12) return "上午好";
if (hour < 14) return "中午好";
if (hour < 18) return "下午好";
return "晚上好";
}
// 统计 Bento 卡:总量 + 今日增量,语义色与全站令牌一致
function StatCard({
label,
value,
foot,
icon,
color,
soft,
}: {
label: string;
value: string;
foot: string;
icon: React.ReactNode;
color: string;
soft: string;
}) {
return (
);
}
// 快捷管理入口:整卡可点(stretch-link),图标 + 标题 + 描述 + 箭头
function QuickLink({
href,
icon,
title,
desc,
soft,
color,
}: {
href: string;
icon: React.ReactNode;
title: string;
desc: string;
soft: string;
color: string;
}) {
return (
);
}
function PanelHead({ title, extra }: { title: string; extra?: string }) {
return (
{title}
{extra && (
{extra}
)}
);
}
export default async function AdminDashboardPage() {
const cookie = authCookieHeader(await cookies());
// 与 layout 的 /me 请求共享 React cache;概览失败降级,不阻塞仪表盘骨架
const [me, overview] = await Promise.all([
getMeCached(cookie || undefined),
fetchOverview(cookie).catch(() => null),
]);
const now = new Date();
const dateLabel = new Intl.DateTimeFormat("zh-CN", {
year: "numeric",
month: "long",
day: "numeric",
weekday: "long",
}).format(now);
const stats = overview?.stats;
const boards = overview?.boards ?? [];
const newUsers = overview?.new_users ?? [];
const topBoards = [...boards].sort((a, b) => b.post_count - a.post_count).slice(0, 6);
const maxBoardCount = Math.max(1, ...topBoards.map((b) => b.post_count));
return (
{/* 页头 */}
{greeting(now.getHours())},{me.user?.nickname}
{dateLabel} · 这里是站点运营全貌
{/* 统计 Bento */}
}
color="var(--accent)"
soft="var(--accent-soft)"
/>
}
color="var(--clay)"
soft="var(--clay-soft)"
/>
}
color="var(--ok)"
soft="var(--ok-soft)"
/>
}
color="var(--gold)"
soft="var(--gold-soft)"
/>
{/* 快捷入口 */}
快捷管理
}
title="用户管理"
desc="查看全站成员,授予管理员权限或封禁违规账号"
soft="var(--accent-soft)"
color="var(--accent)"
/>
}
title="公告管理"
desc="发布、编辑与下线站点公告,支持标签颜色与草稿状态"
soft="var(--clay-soft)"
color="var(--clay)"
/>
}
title="外观设置"
desc="配置全站主题强调色,实时预览后保存并对所有用户生效"
soft="var(--gold-soft)"
color="var(--gold)"
/>
{/* 成员与板块 */}
{/* 最新注册成员 */}
{newUsers.length === 0 ? (
暂无成员数据
) : (
newUsers.slice(0, 6).map((u) => (
{u.nickname}
@{u.username}
{formatRelative(u.created_at)}加入
))
)}
{/* 板块分布 */}
{topBoards.length === 0 ? (
暂无板块数据
) : (
topBoards.map((b) => (
{b.name}
{fmt(b.post_count)}
{/* 相对占比迷你条(纯装饰) */}
))
)}
);
}