← back to Trademarks Copyright
src/app/drops/archive/[id]/page.tsx
83 lines
import Link from "next/link";
import { notFound } from "next/navigation";
import { query } from "@/lib/db";
type Params = { id: string };
export const dynamic = "force-dynamic";
export async function generateMetadata({ params }: { params: Promise<Params> }) {
const { id } = await params;
const { rows } = await query<{ subject: string; intro: string | null }>(
`SELECT subject, intro FROM drops WHERE id = $1 AND status IN ('published','sent')`, [Number(id)]
);
if (!rows.length) return { title: "Not found" };
return { title: rows[0].subject, description: rows[0].intro ?? undefined };
}
export default async function ArchivedDrop({ params }: { params: Promise<Params> }) {
const { id } = await params;
const numId = Number(id);
if (!Number.isFinite(numId)) notFound();
const { rows: drops } = await query<{
id: number; drop_date: string; subject: string; intro: string | null; outro: string | null;
}>(
`SELECT id, drop_date, subject, intro, outro FROM drops
WHERE id = $1 AND status IN ('published','sent')`,
[numId]
);
if (!drops.length) notFound();
const drop = drops[0];
const { rows: items } = await query<{
position: number; headline: string; blurb: string;
}>(`SELECT position, headline, blurb FROM drop_items WHERE drop_id = $1 ORDER BY position`, [numId]);
const teased = items.slice(0, 1); // free readers see headline + blurb of first item only
const hidden = items.slice(1);
const date = new Date(drop.drop_date);
return (
<article className="mx-auto max-w-3xl">
<div className="mb-3 text-sm"><Link href="/drops/archive" className="text-ink/60 hover:text-brass">← Archive</Link></div>
<div className="card overflow-hidden">
<div className="bg-ledger text-parchment px-6 py-5">
<div className="text-xs tracking-widest text-brass">
{date.toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric", year: "numeric" }).toUpperCase()}
</div>
<h1 className="mt-1 text-3xl font-semibold">{drop.subject}</h1>
{drop.intro && <p className="mt-2 italic text-parchment/80">{drop.intro}</p>}
</div>
{teased.map((it) => (
<div key={it.position} className="border-t border-ink/10 p-5">
<div className="text-xs text-ink/50">#{it.position}</div>
<h3 className="mt-1 text-xl font-semibold">{it.headline}</h3>
<p className="mt-2 text-ink/80">{it.blurb}</p>
</div>
))}
{hidden.length > 0 && (
<div className="border-t border-ink/10 p-6 bg-brass/5">
{hidden.slice(0, 2).map((it) => (
<div key={it.position} className="mb-4 opacity-60 select-none">
<div className="text-xs text-ink/40">#{it.position}</div>
<h3 className="text-lg font-semibold blur-sm">{it.headline}</h3>
<p className="text-sm text-ink/60 blur-sm">{it.blurb.slice(0, 160)}…</p>
</div>
))}
<div className="text-center mt-4">
<div className="text-xs uppercase tracking-wide text-brass mb-2">
{hidden.length} item{hidden.length === 1 ? "" : "s"} gated for subscribers
</div>
<Link href="/drops" className="btn">Start free trial to read the rest</Link>
</div>
</div>
)}
</div>
</article>
);
}