feat: locked-feature upgrade prompts route to billing + track upgrade interest (v0.81.0)

UpgradeDialog's CTA now sends users to Settings > Billing (with a feature-specific banner) instead of a generic support-ticket prefill, and fires a best-effort tracking beacon recording which feature triggered the click. New feature_upgrade_clicks table + Admin > Insights chart ("Most-requested locked features") gives admins visibility into which locked features actually drive upgrade interest.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-24 14:12:12 +02:00
parent 1fe379bcdc
commit ce7741c0b6
14 changed files with 6392 additions and 17 deletions
+13 -2
View File
@@ -8,6 +8,8 @@ import { ManageBillingButton } from "@/components/settings/manage-billing-button
import { UsageQuotaSection } from "@/components/settings/usage-quota-section";
import { BillingDetailsForm } from "@/components/settings/billing-details-form";
import { Badge } from "@/components/ui/badge";
import { FEATURE_DEFINITIONS } from "@/lib/feature-flags";
import { Sparkles } from "lucide-react";
export const metadata: Metadata = {};
@@ -17,10 +19,11 @@ function describeLimit(n: number, unit: string): string {
return n === UNLIMITED ? `Unlimited ${unit}` : `${n} ${unit}`;
}
export default async function BillingPage({ searchParams }: { searchParams: Promise<{ checkout?: string }> }) {
export default async function BillingPage({ searchParams }: { searchParams: Promise<{ checkout?: string; upgrade?: string }> }) {
const session = await auth.api.getSession({ headers: await headers() });
if (!session) return null;
const { checkout } = await searchParams;
const { checkout, upgrade } = await searchParams;
const upgradeFeature = FEATURE_DEFINITIONS.find((f) => f.key === upgrade);
const currentMonth = new Date().toISOString().slice(0, 7);
@@ -66,6 +69,14 @@ export default async function BillingPage({ searchParams }: { searchParams: Prom
return (
<div className="space-y-8">
{upgradeFeature && (
<div className="flex items-start gap-3 rounded-lg border border-primary/40 bg-primary/5 p-4 text-sm">
<Sparkles className="h-4 w-4 text-primary shrink-0 mt-0.5" />
<p>
Upgrading unlocks <strong>{upgradeFeature.label}</strong> {upgradeFeature.description}
</p>
</div>
)}
{checkout === "success" && (
<div className="rounded-lg border border-primary/40 bg-primary/5 p-4 text-sm">
Payment received your plan updates within a few seconds as Stripe confirms it. Refresh if it doesn&apos;t show up right away.