// Atlas · Billing & plan management page const { useState: useBillState, useEffect: useBillEffect } = React; const TIER_META = { unverified: { label: "Unverified (Restricted)", price: "Restricted", blurb: "1 report limit until your email address is verified." }, researcher: { label: "Researcher", price: "Free", blurb: "2 reports per month, all report types, public research sources." }, professional: { label: "Professional", price: "$10/mo", blurb: "4 reports per month plus your own Internal Knowledge Repository." }, enterprise: { label: "Enterprise", price: "Custom", blurb: "Custom volume, shared team repository, unlimited users." }, }; function BillingPage({ user, setUser, onBack }) { const [me, setMe] = useBillState(user || null); const [busy, setBusy] = useBillState(false); const [error, setError] = useBillState(null); // Refresh on mount so a return from Stripe Checkout reflects the new tier. useBillEffect(() => { let live = true; (async () => { try { const res = await fetch("/api/me"); if (!res.ok) return; const data = await res.json(); if (!live) return; setMe(data); if (setUser) setUser((u) => (u ? { ...u, ...data } : u)); } catch {} })(); return () => { live = false; }; }, []); const tier = me?.tier || "researcher"; const meta = TIER_META[tier] || TIER_META.researcher; const used = me?.reports_used ?? 0; const limit = me?.reports_limit; // null = unlimited const status = me?.subscription_status; const go = async (path) => { setBusy(true); setError(null); try { const res = await fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.detail || "Something went wrong."); if (data.url) { window.location.href = data.url; return; } } catch (e) { setError(e.message || "Something went wrong."); } finally { setBusy(false); } }; const card = { background: "var(--bg-1)", border: "1px solid var(--line-2)", borderRadius: 12, padding: "24px 26px", maxWidth: 560, }; return (
Current plan
{meta.label}
{meta.price}
{meta.blurb}
Reports this month
{limit == null ? `${used} · unlimited` : `${used} / ${limit}`}
{status && (
Subscription status: {status}
)}
{error && (
{error}
)} {(tier === "unverified" || tier === "researcher") && (
{tier === "unverified" ? (
⚠️ Please verify your email address via the banner at the top of the page to unlock the standard Researcher plan (2 free reports/mo) or to upgrade to Professional.
) : (
Upgrade to Professional for 4 reports a month and your own Internal Knowledge Repository.
)}
)} {tier === "professional" && (
Update your card, view invoices, or cancel anytime through the billing portal.
)} {tier === "enterprise" && (
Your plan is managed by our team. For changes to volume, seats, or your shared repository, reach out at ramy@notedsource.com or{" "} contact us.
)}
); }