"use client"; import { useState } from "react"; import { toast } from "sonner"; import { Switch } from "@/components/ui/switch"; type Tier = "free" | "pro" | "family"; type FeatureDef = { key: string; label: string; description: string }; type Matrix = Record>; const TIER_LABELS: Record = { free: "Free", pro: "Pro", family: "Family" }; const TIERS: Tier[] = ["free", "pro", "family"]; export function FeatureFlagsForm({ features, initialMatrix, }: { features: FeatureDef[]; initialMatrix: Matrix; }) { const [matrix, setMatrix] = useState(initialMatrix); const [saving, setSaving] = useState(null); async function toggle(featureKey: string, tier: Tier, enabled: boolean) { const cellKey = `${featureKey}:${tier}`; setSaving(cellKey); const prev = matrix[featureKey]![tier]; setMatrix((m) => ({ ...m, [featureKey]: { ...m[featureKey]!, [tier]: enabled } })); try { const res = await fetch("/api/v1/admin/feature-flags", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ featureKey, tier, enabled }), }); if (!res.ok) throw new Error(); } catch { setMatrix((m) => ({ ...m, [featureKey]: { ...m[featureKey]!, [tier]: prev } })); toast.error("Failed to update feature flag"); } finally { setSaving(null); } } return (

Feature Toggles

Disable a feature for a tier to keep its button visible but gated — clicking it shows an upgrade prompt instead of running.

{TIERS.map((tier) => ( ))} {features.map((f) => ( {TIERS.map((tier) => ( ))} ))}
Feature{TIER_LABELS[tier]}

{f.label}

{f.description}

{ void toggle(f.key, tier, checked); }} />
); }