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:
Arnaud
2026-07-24 14:04:27 +02:00
parent 1dd8abfd52
commit 1fe379bcdc
15 changed files with 240 additions and 32 deletions
@@ -21,6 +21,8 @@ import {
} from "@/components/ui/dialog";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { VersionDiffView, type DiffSnapshot } from "@/components/recipe/version-diff-view";
import { UpgradeDialog } from "@/components/premium/upgrade-dialog";
import { ProBadge } from "@/components/premium/pro-badge";
type VersionSummary = {
id: string;
@@ -49,9 +51,14 @@ type VersionDetail = {
export function VersionHistoryButton({
recipeId,
currentSnapshot,
locked = false,
}: {
recipeId: string;
currentSnapshot: DiffSnapshot;
/** Available on some tier but not the viewer's — button still shows
* (with a "Pro" upsell) and opens an upgrade prompt instead of the
* version sheet. */
locked?: boolean;
}) {
const t = useTranslations("recipe");
const tForm = useTranslations("recipeForm");
@@ -68,8 +75,13 @@ export function VersionHistoryButton({
const [expandedData, setExpandedData] = useState<Record<string, VersionDetail>>({});
const [restoringId, setRestoringId] = useState<string | null>(null);
const [comparingId, setComparingId] = useState<string | null>(null);
const [upgradeOpen, setUpgradeOpen] = useState(false);
async function handleOpen(isOpen: boolean) {
if (isOpen && locked) {
setUpgradeOpen(true);
return;
}
setOpen(isOpen);
if (isOpen) {
setLoading(true);
@@ -137,7 +149,10 @@ export function VersionHistoryButton({
<History className="h-4 w-4" />
</Button>
} />
<TooltipContent>{t("historyTooltip")}</TooltipContent>
<TooltipContent className="flex items-center gap-1.5">
{t("historyTooltip")}
{locked && <ProBadge />}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Sheet open={open} onOpenChange={handleOpen}>
@@ -247,6 +262,12 @@ export function VersionHistoryButton({
)}
</DialogContent>
</Dialog>
<UpgradeDialog
open={upgradeOpen}
onOpenChange={setUpgradeOpen}
featureKey="version_history"
featureLabel="Recipe version history"
/>
</>
);
}