feat: version history as 10th feature flag, richer upgrade prompts, close unguarded photo-import tab (v0.80.0)
Recipe version history is now gated like the other 9 per-tier features (enabled by default, locked-vs-hidden treatment via isFeatureAvailableAnyTier). UpgradeDialog now shows a tagline + concrete bullet list per feature instead of one generic sentence. Also closes a real gap: the AI-generate dialog's Photo tab hit /api/v1/ai/import-photo directly with no tier check in the UI (server route was already correctly gated) — a locked-out user could fill it in and hit a dead-end error instead of an upgrade prompt. Now threaded through the same lock/hide props as the dedicated PhotoImportButton. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -117,6 +117,7 @@ export default async function RecipePage({ params }: Params) {
|
||||
mealPairing: !featureFlags.meal_pairing[viewerTier],
|
||||
nutritionEstimation: !featureFlags.nutrition_estimation[viewerTier],
|
||||
markdownExport: !featureFlags.markdown_export[viewerTier],
|
||||
versionHistory: !featureFlags.version_history[viewerTier],
|
||||
};
|
||||
// A feature disabled for every tier has no upgrade path, so it hides
|
||||
// outright; one enabled for at least one tier still shows (locked, with a
|
||||
@@ -127,6 +128,7 @@ export default async function RecipePage({ params }: Params) {
|
||||
mealPairing: isFeatureAvailableAnyTier(featureFlags, "meal_pairing"),
|
||||
nutritionEstimation: isFeatureAvailableAnyTier(featureFlags, "nutrition_estimation"),
|
||||
markdownExport: isFeatureAvailableAnyTier(featureFlags, "markdown_export"),
|
||||
versionHistory: isFeatureAvailableAnyTier(featureFlags, "version_history"),
|
||||
};
|
||||
|
||||
const isOwner = recipe.authorId === session.user.id;
|
||||
@@ -289,23 +291,26 @@ export default async function RecipePage({ params }: Params) {
|
||||
)}
|
||||
{isOwner && (
|
||||
<>
|
||||
<VersionHistoryButton
|
||||
recipeId={id}
|
||||
currentSnapshot={{
|
||||
title: recipe.title,
|
||||
description: recipe.description,
|
||||
ingredients: recipe.ingredients.map((ing) => ({
|
||||
rawName: ing.rawName,
|
||||
quantity: ing.quantity,
|
||||
unit: ing.unit,
|
||||
note: ing.note,
|
||||
})),
|
||||
steps: recipe.steps.map((s) => ({
|
||||
instruction: s.instruction,
|
||||
timerSeconds: s.timerSeconds,
|
||||
})),
|
||||
}}
|
||||
/>
|
||||
{available.versionHistory && (
|
||||
<VersionHistoryButton
|
||||
recipeId={id}
|
||||
currentSnapshot={{
|
||||
title: recipe.title,
|
||||
description: recipe.description,
|
||||
ingredients: recipe.ingredients.map((ing) => ({
|
||||
rawName: ing.rawName,
|
||||
quantity: ing.quantity,
|
||||
unit: ing.unit,
|
||||
note: ing.note,
|
||||
})),
|
||||
steps: recipe.steps.map((s) => ({
|
||||
instruction: s.instruction,
|
||||
timerSeconds: s.timerSeconds,
|
||||
})),
|
||||
}}
|
||||
locked={locked.versionHistory}
|
||||
/>
|
||||
)}
|
||||
<Tooltip>
|
||||
<TooltipTrigger render={
|
||||
<Link href={`/recipes/${id}/edit`} className={cn(buttonVariants({ variant: "ghost", size: "icon" }))}>
|
||||
|
||||
Reference in New Issue
Block a user