← back to Govarbitrage

src/app/pricing/page.tsx

95 lines

import type { Metadata } from "next";
import { TIERS, TIER_ORDER } from "@/lib/tiers";
import { getCurrentTier } from "@/lib/current-user";
import UpgradeButton from "./UpgradeButton";

export const dynamic = "force-dynamic";

export const metadata: Metadata = {
  title: "Pricing",
  description:
    "Explore GovArbitrage beta access and preview plans. Current listings and full money-math analysis are available on every plan. Checkout uses test or simulated payments.",
  alternates: { canonical: "/pricing" },
  openGraph: {
    title: "Pricing — GovArbitrage",
    description:
      "Explore GovArbitrage beta access and preview plans. Current listings and full money-math analysis are available on every plan. Checkout uses test or simulated payments.",
    url: "/pricing",
    type: "website",
  },
};

const pricingJsonLd = {
  "@context": "https://schema.org",
  "@type": "WebPage",
  name: "GovArbitrage beta access and preview plans",
  description: metadata.description,
};

export default async function PricingPage() {
  const current = await getCurrentTier();

  return (
    <main className="mx-auto max-w-4xl space-y-6 p-5 pt-14 text-foreground">
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(pricingJsonLd) }}
      />
      <div className="text-center space-y-2">
        <h1 className="text-3xl font-semibold tracking-tight">Explore government-surplus deals</h1>
        <p className="text-base text-muted-foreground">
          Browse current auction listings and full money-math analysis. Paid-plan
          features and prices are previews during beta.
        </p>
        <p className="rounded-lg bg-warning/20 text-foreground text-sm px-4 py-3" data-testid="beta-notice">
          Beta access: listing freshness, source access, and money-math analysis are
          currently available on every plan. Daily newsletter access depends on
          confirmed subscription, not plan. Checkout uses test or simulated payments;
          no real charge is made.
        </p>
      </div>

      <div className="grid grid-cols-1 gap-4 md:grid-cols-3 mt-7">
        {TIER_ORDER.map((key) => {
          const t = TIERS[key];
          const isCurrent = current === key;
          return (
            <div
              key={key}
              className="relative rounded-xl border bg-card text-card-foreground p-5"
              data-testid={`plan-${key.toLowerCase()}`}
            >
              <div className="font-bold text-lg">{t.label}</div>
              <div className="my-1.5">
                <span className="text-4xl font-extrabold">${t.priceUsd}</span>
                <span className="text-muted-foreground">{t.priceUsd > 0 ? "/mo" : ""}</span>
              </div>
              {t.priceUsd > 0 && <div className="text-xs text-muted-foreground">preview price</div>}
              <div className="text-muted-foreground text-sm min-h-9">{t.blurb}</div>
              <ul className="list-none p-0 my-3.5 space-y-1 text-sm leading-relaxed">
                {t.features.map((f) => (
                  <li key={f}>
                    <span className="text-positive mr-1.5">✓</span>{f}
                  </li>
                ))}
              </ul>
              {t.priceUsd === 0 ? (
                <div className={`py-2.5 font-bold ${isCurrent ? "text-positive" : "text-muted-foreground"}`}>
                  {isCurrent ? "✓ Your plan" : "Default plan"}
                </div>
              ) : (
                <UpgradeButton tier={key as "STANDARD" | "PREMIUM"} label={t.label} current={isCurrent} />
              )}
            </div>
          );
        })}
      </div>

      <p className="text-center text-muted-foreground text-xs leading-relaxed">
        Estimates are heuristic and confidence-labeled — always verify comps before bidding.
        Paid-plan features and prices are previews and may change before launch.
      </p>
    </main>
  );
}