Files
Epicure/apps/web/app/admin/tiers/page.tsx
T
Arnaud 2f3ba14093 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>
2026-07-18 23:35:12 +02:00

35 lines
1.2 KiB
TypeScript

import type { Metadata } from "next";
import { db, tierDefinitions } from "@epicure/db";
import { TierLimitsForm } from "@/components/admin/tier-limits-form";
import { FeatureFlagsForm } from "@/components/admin/feature-flags-form";
import { getFeatureFlagMatrix, FEATURE_DEFINITIONS } from "@/lib/feature-flags";
export const metadata: Metadata = {};
export default async function AdminTiersPage() {
const [tiers, featureFlagMatrix] = await Promise.all([
db.select().from(tierDefinitions),
getFeatureFlagMatrix(),
]);
return (
<div className="space-y-8">
<div>
<h1 className="text-2xl font-bold tracking-tight">Tier Limits</h1>
<p className="text-muted-foreground text-sm mt-1">
Change monthly quotas per tier. Takes effect immediately for all users on that tier.
</p>
</div>
{tiers.map((tierDefinition) => (
<TierLimitsForm key={tierDefinition.tier} tierDefinition={tierDefinition} />
))}
<FeatureFlagsForm
features={FEATURE_DEFINITIONS.map((f) => ({ key: f.key, label: f.label, description: f.description }))}
initialMatrix={featureFlagMatrix}
/>
</div>
);
}