← back to Govarbitrage

src/app/pricing/page.tsx

119 lines

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

export const dynamic = "force-dynamic";

export const metadata: Metadata = {
  title: "Pricing",
  description:
    "GovArbitrage plans: Free (delayed deals), Standard ($12/mo — real-time + hot-deal alerts), and Premium ($29/mo — unlimited, confidence bands, priority alerts). Cancel anytime.",
  alternates: { canonical: "/pricing" },
  openGraph: {
    title: "Pricing — GovArbitrage",
    description:
      "GovArbitrage plans: Free (delayed deals), Standard ($12/mo — real-time + hot-deal alerts), and Premium ($29/mo — unlimited, confidence bands, priority alerts). Cancel anytime.",
    url: "/pricing",
    type: "website",
  },
};

const pricingJsonLd = {
  "@context": "https://schema.org",
  "@type": "ItemList",
  name: "GovArbitrage Pricing Plans",
  itemListElement: TIER_ORDER.map((key, idx) => {
    const t = TIERS[key];
    return {
      "@type": "ListItem",
      position: idx + 1,
      item: {
        "@type": "Offer",
        name: t.label,
        description: t.blurb,
        price: t.priceUsd.toString(),
        priceCurrency: "USD",
        eligibleDuration: {
          "@type": "QuantitativeValue",
          value: 1,
          unitCode: "MON",
        },
      },
    };
  }),
};

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">Find the deals before everyone else</h1>
        <p className="text-base text-muted-foreground">
          8 government-surplus auction networks, de-duped and scored — with honest,
          confidence-labeled max-bid math. Cancel anytime.
        </p>
        {STRIPE_MODE !== "test" && (
          <span className="inline-block bg-warning/20 text-warning text-xs px-3 py-1 rounded-full font-medium">
            TEST MODE — no real charges (checkout is simulated)
          </span>
        )}
      </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;
          const featured = key === "STANDARD";
          return (
            <div
              key={key}
              className={`relative rounded-xl bg-card text-card-foreground p-5 ${
                featured ? "border-2 border-foreground" : "border"
              }`}
            >
              {featured && (
                <div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-foreground text-background text-[11px] font-bold px-3 py-0.5 rounded-full">
                  MOST POPULAR
                </div>
              )}
              <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>
              <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.
        Prices shown are introductory and may change.
      </p>
    </main>
  );
}