← 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>
  );
}