127 lines
5.1 KiB
TypeScript
127 lines
5.1 KiB
TypeScript
import { notFound } from "next/navigation";
|
|
import { getDayData, daysData } from "../../data/days";
|
|
import DayContent from "../../components/DayContent";
|
|
|
|
interface PageProps {
|
|
params: Promise<{ id: string }>;
|
|
}
|
|
|
|
export function generateStaticParams() {
|
|
return daysData.map((d) => ({ id: String(d.day) }));
|
|
}
|
|
|
|
export default async function DayPage({ params }: PageProps) {
|
|
const { id } = await params;
|
|
const dayId = parseInt(id, 10);
|
|
const data = getDayData(dayId);
|
|
|
|
if (!data) return notFound();
|
|
|
|
const prevDay = getDayData(dayId - 1);
|
|
const nextDay = getDayData(dayId + 1);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white">
|
|
{/* Top bar */}
|
|
<header className="sticky top-0 z-50 border-b border-slate-100 bg-white/80 backdrop-blur-lg">
|
|
<div className="mx-auto flex h-14 max-w-4xl items-center justify-between px-4 sm:px-6">
|
|
<a
|
|
href="/"
|
|
className="flex items-center gap-1.5 text-sm font-medium text-slate-600 transition-colors hover:text-sky-600"
|
|
>
|
|
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
</svg>
|
|
返回首页
|
|
</a>
|
|
<span className="text-sm text-slate-400">
|
|
Day {data.day} / 7
|
|
</span>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Article header */}
|
|
<div className="mx-auto max-w-4xl px-4 pt-10 pb-6 sm:px-6 sm:pt-16 sm:pb-10">
|
|
<div className="mb-6 flex flex-wrap items-center gap-3 text-sm text-slate-500">
|
|
<span className="rounded-full bg-slate-100 px-3 py-1 font-medium">
|
|
阅读时间{data.readingTime}
|
|
</span>
|
|
<span className="rounded-full bg-sky-50 px-3 py-1 font-medium text-sky-600">
|
|
第 {data.day} 天
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-500 to-cyan-500 text-3xl text-white shadow-lg shadow-sky-500/20 sm:h-20 sm:w-20 sm:text-4xl">
|
|
{data.icon}
|
|
</div>
|
|
<div>
|
|
<h1 className="text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl lg:text-5xl">
|
|
{data.title}
|
|
</h1>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mx-auto max-w-4xl px-4 sm:px-6">
|
|
<hr className="border-slate-100" />
|
|
</div>
|
|
|
|
{/* Markdown body */}
|
|
<article className="mx-auto max-w-4xl px-4 py-10 sm:px-6 sm:py-14">
|
|
<DayContent content={data.content} />
|
|
</article>
|
|
|
|
{/* Prev / Next navigation */}
|
|
<nav className="mx-auto max-w-4xl px-4 pb-20 sm:px-6">
|
|
<div className="flex flex-col gap-4 border-t border-slate-100 pt-8 sm:flex-row sm:justify-between">
|
|
{prevDay ? (
|
|
<a
|
|
href={`/day/${prevDay.day}`}
|
|
className="group flex items-center gap-3 rounded-xl border border-slate-100 px-5 py-4 transition-all hover:border-sky-200 hover:bg-sky-50/50 sm:flex-1"
|
|
>
|
|
<svg className="h-5 w-5 shrink-0 text-slate-400 transition-colors group-hover:text-sky-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
</svg>
|
|
<div>
|
|
<div className="text-xs text-slate-400">上一天</div>
|
|
<div className="text-sm font-semibold text-slate-700 group-hover:text-sky-700">
|
|
Day {prevDay.day}: {prevDay.title}
|
|
</div>
|
|
</div>
|
|
</a>
|
|
) : (
|
|
<div className="sm:flex-1" />
|
|
)}
|
|
{nextDay ? (
|
|
<a
|
|
href={`/day/${nextDay.day}`}
|
|
className="group flex items-center justify-end gap-3 rounded-xl border border-slate-100 px-5 py-4 text-right transition-all hover:border-sky-200 hover:bg-sky-50/50 sm:flex-1"
|
|
>
|
|
<div>
|
|
<div className="text-xs text-slate-400">下一天</div>
|
|
<div className="text-sm font-semibold text-slate-700 group-hover:text-sky-700">
|
|
Day {nextDay.day}: {nextDay.title}
|
|
</div>
|
|
</div>
|
|
<svg className="h-5 w-5 shrink-0 text-slate-400 transition-colors group-hover:text-sky-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
</svg>
|
|
</a>
|
|
) : (
|
|
<div className="sm:flex-1" />
|
|
)}
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Footer */}
|
|
<footer className="border-t border-slate-100 bg-slate-50 py-8 text-center text-sm text-slate-400">
|
|
<a href="/" className="font-medium text-slate-600 transition-colors hover:text-sky-600">
|
|
🌍 数字游民指南
|
|
</a>
|
|
<span className="mx-2">·</span>
|
|
开源免费 · 社区驱动
|
|
</footer>
|
|
</div>
|
|
);
|
|
}
|