import type { Metadata } from "next";
import Link from "next/link";
import { cookies } from "next/headers";
import {
ClipboardCheck,
ChevronRight,
Eye,
FileText,
MessageSquareText,
LayoutDashboard,
Coins,
Users,
Activity,
} from "lucide-react";
import {
fetchOverview,
fetchAdminPendingCounts,
fetchAdminAnalytics,
type PendingCounts,
type AnalyticsOverview,
} from "@/lib/api";
import { authCookieHeader } from "@/lib/cookies";
import { getMeCached } from "@/lib/serverData";
import { canModerateAny, isSuperOrOwner, isAdminOrAbove, ROLES } from "@/lib/roles";
import { AdminPageHeader, AdminStatCard } from "@/components/admin";
import DashboardClient from "./DashboardClient";
export const metadata: Metadata = {
title: "管理仪表盘",
};
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 "晚上好";
}
/** 紧凑待审条:跳转内容管理待审筛选 */
function PendingStrip({ counts }: { counts: PendingCounts }) {
const total = counts.posts + counts.comments;
const idle = total === 0;
const href =
counts.posts > 0 || counts.comments === 0
? "/admin/content?status=pending"
: "/admin/content?status=pending&kind=comments";
return (
待审内容
{idle ? (
暂无待审
) : (
{fmt(total)} 项
)}
帖子
{fmt(counts.posts)}
评论
{fmt(counts.comments)}
);
}
const emptyAnalytics = (): AnalyticsOverview => ({
range_days: 7,
today_pv: 0,
today_uv: 0,
visits: [],
posts_series: [],
comments_series: [],
registers_series: [],
boards: [],
post_types: [],
top_viewed: [],
});
export default async function AdminDashboardPage() {
const cookie = authCookieHeader(await cookies());
const me = await getMeCached(cookie || undefined);
const canMod = canModerateAny(me.user);
const canUsers = isSuperOrOwner(me.user?.role);
const canEconomy = isAdminOrAbove(me.user?.role);
const boardScoped = me.user?.role === ROLES.BOARD_ADMIN;
const [overview, pendingCounts, analytics] = await Promise.all([
boardScoped
? Promise.resolve(null)
: fetchOverview(cookie).catch(() => null),
canMod
? fetchAdminPendingCounts(cookie).catch(() => ({ posts: 0, comments: 0 }))
: Promise.resolve(null),
fetchAdminAnalytics(cookie, "7d").catch(() => emptyAnalytics()),
]);
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 newUsers = boardScoped ? [] : overview?.new_users ?? [];
// 板管:今日内容用授权板 analytics 序列今日点;隐藏全站注册/在线卡的误导数字
const todayPosts = boardScoped
? analytics.posts_series[analytics.posts_series.length - 1]?.count ?? 0
: stats?.today_posts ?? 0;
const todayComments = boardScoped
? analytics.comments_series[analytics.comments_series.length - 1]?.count ?? 0
: stats?.today_comments ?? 0;
return (
积分管理
) : undefined
}
/>
}
/>
}
color="var(--ok)"
soft="var(--ok-soft)"
/>
}
color="var(--gold)"
soft="var(--gold-soft)"
/>
}
color="var(--clay)"
soft="var(--clay-soft)"
/>
{canMod && pendingCounts && }
);
}