← back to Trademarks Copyright

src/app/drops/view/[token]/page.tsx

87 lines

import Link from "next/link";
import { notFound } from "next/navigation";
import { query } from "@/lib/db";
import ReferralWidget from "@/components/ReferralWidget";

type Params = { token: string };

export default async function SubscriberPortal({ params }: { params: Promise<Params> }) {
  const { token } = await params;

  const { rows: subs } = await query<{
    id: number; email: string; name: string | null; tier: string; status: string;
    trial_drops_left: number; signed_up_at: string; last_delivered_at: string | null;
  }>(
    `SELECT id, email, name, tier, status, trial_drops_left, signed_up_at, last_delivered_at
     FROM subscribers WHERE token = $1`, [token]
  );
  if (!subs.length) notFound();
  const sub = subs[0];

  const { rows: drops } = await query<{
    id: number; drop_date: string; subject: string;
    sent_at: string | null; intro: string | null;
  }>(
    `SELECT d.id, d.drop_date, d.subject, d.sent_at, d.intro
     FROM drops d
     JOIN deliveries dv ON dv.drop_id = d.id
     WHERE dv.subscriber_id = $1
     ORDER BY d.drop_date DESC`, [sub.id]
  );

  const { rows: refStats } = await query<{ total: string; rewarded: string }>(
    `SELECT COUNT(*)::text AS total,
            COUNT(*) FILTER (WHERE reward_given = TRUE)::text AS rewarded
     FROM referrals WHERE referrer_token = $1`,
    [token]
  );
  const totalRefs = Number(refStats[0]?.total ?? 0);
  const rewardedRefs = Number(refStats[0]?.rewarded ?? 0);
  const untilNext = 3 - (totalRefs - rewardedRefs * 3);

  return (
    <div>
      <div className="mb-4 card p-5">
        <div className="text-xs uppercase tracking-widest text-brass">Your portal</div>
        <h1 className="mt-1 text-3xl font-semibold">{sub.name || sub.email}</h1>
        <div className="mt-2 flex flex-wrap items-center gap-3 text-sm">
          <span className="tag">Tier: {sub.tier}</span>
          <span className="tag">Status: {sub.status}</span>
          {sub.tier === "trial" && <span className="tag">{sub.trial_drops_left} trial drops left</span>}
          <span className="text-ink/60">Signed up {new Date(sub.signed_up_at).toLocaleDateString()}</span>
          <span className="ml-auto text-xs">
            <Link href={`/drops/unsubscribe/${token}`} className="text-ink/50 hover:text-red-600">Unsubscribe</Link>
          </span>
        </div>
      </div>

      <ReferralWidget
        token={token}
        totalRefs={totalRefs}
        untilNext={Math.max(0, untilNext)}
        rewardedRefs={rewardedRefs}
      />

      <h2 className="text-xl font-semibold mt-6">Drop archive</h2>
      {drops.length === 0 ? (
        <div className="mt-3 text-ink/60">No drops delivered yet. Your first arrives at 9am PT.</div>
      ) : (
        <ul className="mt-3 space-y-2">
          {drops.map((d) => (
            <li key={d.id} className="card p-4">
              <div className="text-xs text-brass">
                {new Date(d.drop_date).toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric" }).toUpperCase()}
              </div>
              <h3 className="font-semibold">{d.subject}</h3>
              {d.intro && <p className="text-sm text-ink/70"><em>{d.intro}</em></p>}
              <div className="mt-2">
                <Link href={`/drops/view/${token}/${d.id}`} className="text-sm text-brass hover:underline">Read drop →</Link>
              </div>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}