2026-04-15 11:07:19 +08:00

204 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Link from "next/link";
import { ArrowUpRight, Clock3, Smartphone, Sparkles } from "lucide-react";
import { DashboardCharts } from "@/components/admin/dashboard-charts";
import { getDashboardData, parseDashboardQuery } from "@/lib/analytics/server";
import { formatDateTime, formatNumber } from "@/lib/utils";
export const dynamic = "force-dynamic";
const ranges = [
{ label: "24 小时", value: "1" },
{ label: "7 天", value: "7" },
{ label: "30 天", value: "30" },
{ label: "90 天", value: "90" },
] as const;
type AdminPageProps = {
searchParams: Promise<{
range?: string;
}>;
};
export default async function AdminPage({ searchParams }: AdminPageProps) {
const rawParams = await searchParams;
const query = parseDashboardQuery(rawParams);
const data = await getDashboardData(query);
return (
<main className="relative overflow-hidden px-6 py-8 sm:px-8 lg:px-10">
<div className="hero-orb left-[-120px] top-[10%]" />
<div className="hero-orb right-[-120px] top-[-40px] bg-[radial-gradient(circle,_rgba(29,111,104,0.16)_0%,_rgba(29,111,104,0)_72%)]" />
<div className="mx-auto flex max-w-7xl flex-col gap-6">
<section className="soft-card p-7 sm:p-8">
<div className="flex flex-col gap-6 xl:flex-row xl:items-end xl:justify-between">
<div className="space-y-4">
<div className="inline-flex items-center gap-2 rounded-full bg-[var(--surface-2)] px-4 py-2 text-sm text-[var(--ink-2)]">
<Sparkles className="h-4 w-4 text-[var(--accent-1)]" />
Android
</div>
<div>
<p className="eyebrow">使</p>
<h1 className="font-[var(--font-display)] text-4xl leading-tight text-[var(--ink-1)] sm:text-5xl">
使
</h1>
</div>
<p className="max-w-3xl text-base leading-8 text-[var(--ink-2)]">
Android 使便使
</p>
</div>
<div className="flex flex-col gap-3 xl:items-end">
<Link
href="/"
className="inline-flex items-center gap-2 rounded-full border border-[var(--line-soft)] bg-white/75 px-5 py-3 text-sm font-semibold text-[var(--ink-1)]"
>
<ArrowUpRight className="h-4 w-4" />
</Link>
<div className="flex flex-wrap gap-2 text-sm text-[var(--ink-2)]">
<span className="pill-tag">Android </span>
<span className="pill-tag"></span>
</div>
</div>
</div>
<div className="mt-8 flex flex-col gap-5">
<div className="space-y-2">
<p className="text-sm font-medium text-[var(--ink-3)]"></p>
<div className="flex flex-wrap gap-2">
{ranges.map((item) => (
<Link
key={item.value}
href={`?range=${item.value}`}
className={query.days === Number(item.value) ? "filter-pill filter-pill-active" : "filter-pill"}
>
{item.label}
</Link>
))}
</div>
</div>
</div>
</section>
<section className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
{data.stats.map((stat) => (
<article key={stat.label} className="stat-card">
<p className="stat-label">{stat.label}</p>
<strong className="stat-value">{formatNumber(stat.value)}</strong>
<p className="stat-hint">{stat.hint}</p>
</article>
))}
</section>
<DashboardCharts activityTrend={data.activityTrend} topEvents={data.topEvents} />
<section className="grid gap-6 xl:grid-cols-[1.05fr_0.95fr]">
<article className="soft-card p-6">
<div className="mb-5 flex items-center justify-between gap-4">
<div>
<p className="eyebrow"></p>
<h2 className="text-2xl font-semibold text-[var(--ink-1)]"></h2>
</div>
<div className="pill-tag">
<Clock3 className="h-4 w-4" />
{query.days}
</div>
</div>
<div className="space-y-3">
{data.topPaths.map((item, index) => (
<div key={item.label} className="list-row">
<div className="flex items-center gap-4">
<span className="index-badge">{index + 1}</span>
<div>
<p className="text-sm text-[var(--ink-3)]"></p>
<p className="text-base font-semibold text-[var(--ink-1)]">{item.label}</p>
</div>
</div>
<strong className="text-lg font-semibold text-[var(--ink-1)]">{formatNumber(item.total)}</strong>
</div>
))}
</div>
</article>
<article className="soft-card p-6">
<div className="mb-5 flex items-center justify-between gap-4">
<div>
<p className="eyebrow"></p>
<h2 className="text-2xl font-semibold text-[var(--ink-1)]">使</h2>
</div>
</div>
<div className="space-y-3">
{data.recentSessions.map((session) => (
<div key={session.sessionKey} className="rounded-[24px] border border-[var(--line-soft)] bg-white/80 px-4 py-4">
<div className="mb-2 flex items-center justify-between gap-3">
<div className="flex items-center gap-2 text-sm text-[var(--ink-2)]">
<Smartphone className="h-4 w-4 text-[var(--accent-2)]" />
Android
</div>
<span className="text-xs text-[var(--ink-3)]">{formatDateTime(session.lastSeenAt)}</span>
</div>
<div className="space-y-1">
<p className="text-base font-semibold text-[var(--ink-1)]">{session.deviceLabel}</p>
<p className="text-sm text-[var(--ink-2)]"> {session.sessionKey.slice(0, 18)}...</p>
<p className="text-sm text-[var(--ink-3)]">
{formatNumber(session.eventCount)}
{session.appVersion ? ` · 版本 ${session.appVersion}` : ""}
{session.locale ? ` · ${session.locale}` : ""}
</p>
</div>
</div>
))}
</div>
</article>
</section>
<section className="soft-card p-6">
<div className="mb-5 flex items-center justify-between gap-4">
<div>
<p className="eyebrow"></p>
<h2 className="text-2xl font-semibold text-[var(--ink-1)]"></h2>
</div>
</div>
<div className="overflow-x-auto">
<table className="min-w-full border-separate border-spacing-y-3">
<thead>
<tr className="text-left text-sm text-[var(--ink-3)]">
<th className="px-4"></th>
<th className="px-4"></th>
<th className="px-4"></th>
<th className="px-4"> / </th>
<th className="px-4"></th>
</tr>
</thead>
<tbody>
{data.recentEvents.map((event) => (
<tr key={event.id} className="rounded-[24px] bg-white/80 text-sm text-[var(--ink-2)] shadow-[var(--shadow-card)]">
<td className="rounded-l-[20px] px-4 py-4">{formatDateTime(event.occurredAt)}</td>
<td className="px-4 py-4">Android</td>
<td className="px-4 py-4">
<span className="inline-flex rounded-full bg-[var(--surface-2)] px-3 py-1 text-[var(--ink-1)]">
{event.name}
</span>
<p className="mt-2 text-xs tracking-[0.18em] text-[var(--ink-3)]">{event.type}</p>
</td>
<td className="px-4 py-4">
<p className="font-medium text-[var(--ink-1)]">{event.pathname ?? "未标记功能"}</p>
<p className="mt-1 text-xs text-[var(--ink-3)]">{event.component ?? "无额外标签"}</p>
</td>
<td className="rounded-r-[20px] px-4 py-4">
<p>{event.elementText ?? "无额外说明"}</p>
<p className="mt-1 text-xs text-[var(--ink-3)]"> {event.sessionKey.slice(0, 18)}...</p>
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
</div>
</main>
);
}