"use client"; import { useEffect, useState } from "react"; import { Eye, Users, LogIn, CalendarDays } from "lucide-react"; import { AdminStatCard } from "@/components/admin"; import { AdminChartCard, NamedBarChart, NamedPieChart, VisitTrendChart } from "@/components/admin/charts"; import { apiStatsOverview, type VisitOverview, type VisitPageRow } from "@/lib/api"; import { formatInt, RangeSegmented, StatsState, TableEmpty, deviceLabel, refKindLabel } from "./ui"; export default function OverviewClient({ initial }: { initial: VisitOverview | null }) { const [range, setRange] = useState<"7d" | "30d">("7d"); const [data, setData] = useState(initial); const [loading, setLoading] = useState(!initial); const [error, setError] = useState(""); useEffect(() => { let alive = true; setLoading(true); apiStatsOverview(range) .then((d) => { if (alive) { setData(d); setError(""); } }) .catch(() => alive && setError("加载失败,请稍后重试")) .finally(() => alive && setLoading(false)); return () => { alive = false; }; }, [range]); const delta = (today: number, yesterday: number) => { if (yesterday <= 0) return today > 0 ? "+100%" : "—"; const pct = Math.round(((today - yesterday) / yesterday) * 100); return `${pct >= 0 ? "+" : ""}${pct}%`; }; if (!data) return ; const devices = data.devices.map((d) => ({ label: deviceLabel(d.key), count: d.count })); const sources = data.sources.map((d) => ({ label: refKindLabel(d.key), count: d.count })); return (

今日 PV {formatInt(data.today_pv)} · UV {formatInt(data.today_uv)}

{error ? : null}
} /> } color="var(--ok)" soft="var(--ok-soft, var(--panel-2))" /> 0 ? `占今日 PV ${Math.round((data.logged_in_pv / data.today_pv) * 100)}%` : undefined } icon={} color="var(--gold)" soft="var(--gold-soft)" /> } color="var(--ink-2)" soft="var(--panel-2)" />
{devices.length ? : } {sources.length ? : }
{data.top_pages.length ? (
{data.top_pages.map((p: VisitPageRow, i) => (
{i + 1} {p.path || "/"} {formatInt(p.pv)} UV {formatInt(p.uv)}
))}
) : ( )}
); }