import type { Metadata } from "next"; import { DollarSign, TrendingUp, Users, CreditCard, UserPlus, Mic2, Wallet, CheckCircle2 } from "lucide-react"; import { getOverview } from "@/lib/admin/metrics"; import { getSignupSeries, getRevenueSeries } from "@/lib/admin/series"; import { parseRange } from "@/lib/admin/range"; import { PLANS, PLAN_ORDER } from "@/lib/billing/plans"; import { formatPrice } from "@/lib/utils"; import { PageHeader } from "@/components/app/page-header"; import { StatCard } from "@/components/admin/ui/stat-card"; import { ChartCard } from "@/components/admin/ui/chart-card"; import { BarSeries, Donut } from "@/components/admin/ui/charts"; import { RangePicker } from "@/components/admin/ui/table-controls"; import { CHART, TIER_COLORS } from "@/components/admin/ui/chart-theme"; import { requireAdmin } from "@/lib/auth/guards"; export const metadata: Metadata = { title: "Admin · Overview" }; export default async function AdminOverviewPage({ searchParams, }: { searchParams: Promise<{ range?: string }>; }) { // Defence in depth: the (admin) layout also guards, but a layout is not an // authorization boundary — pages render concurrently with it, and a route moved // out of the group would silently lose its only check. await requireAdmin(); const range = parseRange((await searchParams).range); const [m, signups, revenue] = await Promise.all([ getOverview(range), getSignupSeries(range), getRevenueSeries(range), ]); const tierData = PLAN_ORDER.map((k) => ({ name: PLANS[k].name, value: m.tierCounts[k] ?? 0, color: TIER_COLORS[k], })).filter((d) => d.value > 0); const usd = (n: number) => `$${n.toFixed(2)}`; return ( <> } />
`$${v}`} series={[ { key: "newMrr", name: "New MRR", color: CHART.brand }, { key: "churnedMrr", name: "Churned", color: CHART.warning }, ]} /> {tierData.length > 0 ? ( d.color)} /> ) : (

No active subscriptions yet.

)}
); }