feat: per-tier feature toggles for recipe variations/pairings (v0.50.0)

Admins can now disable specific AI features per tier from Admin > Tier
Limits — new feature_flags table (feature x tier -> enabled, defaulting
to true so adding a new gated feature never needs a backfill).

Covers recipe variations, drink pairing, and meal pairing to start.
When disabled for a user's tier, the button stays visible (with a small
lock badge) but opens an upgrade dialog instead of running; the API
route rejects the call server-side either way (requireFeatureEnabled,
re-reads tier from the DB rather than trusting the session's cache,
same rationale as checkAndIncrementTierLimit).

The upgrade dialog is informational only — no Stripe checkout exists
yet (STRIPE_PLAN.md is still just a plan) — its CTA links to /support
prefilled as an upgrade-interest suggestion.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-18 23:35:12 +02:00
parent 12c2ec213a
commit 2f3ba14093
24 changed files with 5945 additions and 19 deletions
@@ -60,11 +60,17 @@ function isImage(contentType: string) {
return contentType.startsWith("image/");
}
export function SupportManager({ initialTickets }: { initialTickets: Ticket[] }) {
export function SupportManager({
initialTickets,
prefill,
}: {
initialTickets: Ticket[];
prefill?: { type: TicketType; title: string };
}) {
const t = useTranslations("support");
const [tickets, setTickets] = useState<Ticket[]>(initialTickets);
const [type, setType] = useState<TicketType>("bug");
const [title, setTitle] = useState("");
const [type, setType] = useState<TicketType>(prefill?.type ?? "bug");
const [title, setTitle] = useState(prefill?.title ?? "");
const [description, setDescription] = useState("");
const [submitting, setSubmitting] = useState(false);
const [pendingAttachments, setPendingAttachments] = useState<PendingAttachment[]>([]);