From 666d280a4c65f5481d627c31aabaaf00a2f96cb2 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 24 Jul 2026 13:16:47 +0200 Subject: [PATCH] fix: remove lock-icon overlay from variations button, show Pro badge in tooltip instead (v0.78.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lock icon overlapping the branch icon looked bad. Button is visible again when locked (not hidden) — click opens the upgrade dialog, tooltip shows a small "Pro" badge instead of an icon overlay. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 5 +++ FEATURE_AUDIT.md | 2 +- apps/web/app/(app)/recipes/[id]/page.tsx | 41 +++++++++---------- .../components/admin/feature-flags-form.tsx | 2 +- .../components/recipe/variations-button.tsx | 23 ++++++++++- apps/web/lib/changelog.ts | 9 +++- apps/web/package.json | 2 +- package.json | 2 +- 8 files changed, 58 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 271f73d..36dae6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to Epicure are documented here. This file is mirrored in-app at `/changelog` (and in the admin dashboard) via `apps/web/lib/changelog.ts` — update both together. +## 0.78.3 — 2026-07-24 15:00 + +### Fixed +- Recipe variations button: removed the small lock icon that overlapped the branch icon when the feature was locked for your tier. The button is visible again (clicking opens an upgrade prompt) and the tooltip now shows a "Pro" badge instead. + ## 0.78.2 — 2026-07-24 14:30 ### Fixed diff --git a/FEATURE_AUDIT.md b/FEATURE_AUDIT.md index b22c5a8..731112a 100644 --- a/FEATURE_AUDIT.md +++ b/FEATURE_AUDIT.md @@ -116,7 +116,7 @@ Status legend: **Exists** (fully working) · **Partial** (works but with a real | Stripe webhook (checkout/cancel) | Exists | Signature-verified, replay-protected, event-deduped — production quality | `apps/web/app/api/webhooks/stripe/route.ts` | | Stripe checkout + self-serve billing portal (2026-07-23) | Exists | `POST /api/v1/billing/checkout` creates a subscription Checkout Session (promotion codes enabled); `POST /api/v1/billing/portal` opens Stripe's hosted Customer Portal for self-serve cancel/upgrade/card-update. Webhook route rewritten with the real `stripe` SDK (`stripe.webhooks.constructEvent`, replacing the hand-rolled HMAC verifier) and now handles the full event set: `checkout.session.completed`, `customer.subscription.{updated,deleted}`, `invoice.{payment_failed,paid}` — `past_due` deliberately doesn't downgrade tier (Stripe retries the card first). `/settings/billing` shows plan cards, usage, and a "Manage billing" button; `/admin/billing` shows connection status, subscriber counts, past-due list, recent billing audit events. Cancel/downgrade is at period end (Stripe Portal default); no trial period. **Not yet built:** family-group multi-user sharing (Family tier is purchasable solo, but the plan's per-account member invite/join/tier-resolution piece is deliberately deferred — see `plans/STRIPE_PLAN.md` §1a, flagged there as the most novel/error-prone piece, intentionally shipped after solo billing is proven). | `apps/web/lib/stripe.ts`, `apps/web/app/api/webhooks/stripe/route.ts`, `apps/web/app/api/v1/billing/**`, `apps/web/app/(app)/settings/billing/page.tsx`, `apps/web/app/admin/billing/page.tsx` | | Admin dashboard | Exists | 15 sections (was 13, missing Billing until this pass): overview, insights/analytics, users, invites, recipe moderation, reports, support, tier limits, **billing**, webhooks, audit logs, storage, AI config, site settings, changelog | `apps/web/app/admin/layout.tsx` (`adminNav`) | -| Variations button lock-badge (closed 2026-07-24) | Exists | Was the one gated feature still shown disabled with a small lock-icon overlay + upgrade-dialog-on-click instead of hidden entirely; now hidden like every other gated feature, and the dead locked/`UpgradeDialog` code was removed from `VariationsButton`. | `apps/web/components/recipe/variations-button.tsx`, `apps/web/app/(app)/recipes/[id]/page.tsx` | +| Variations button locked treatment (revised 2026-07-24) | Exists | Only gated feature that stays visible-but-locked instead of hidden (deliberate, per explicit feedback: hiding it was tried first, then reverted). The lock-icon-overlapping-the-branch-icon look was removed; button renders normally, click opens `UpgradeDialog`, and the tooltip shows a small "Pro" badge instead of an icon overlay. | `apps/web/components/recipe/variations-button.tsx`, `apps/web/app/(app)/recipes/[id]/page.tsx` | | Feature flags (per-tier) vs feature prefs (per-user cosmetic) | Exists, two distinct systems | Flags now gate 9 capabilities by tier (was 3): recipe_variations/drink_pairing/meal_pairing (default enabled) plus recipe_import_url, recipe_import_photo, nutrition_estimation, markdown_export, weekly_nutrition, grocery_delivery (2026-07-24, default **disabled** for all tiers — admin turns on per tier from `/admin/tiers`). Each key's own `defaultEnabled` governs the fallback when no admin override row exists, not a blanket true. Prefs let users hide 7 nav sections, no billing implication, unrelated system. | `apps/web/lib/{feature-flags,feature-prefs}.ts` | | **Developer permission** (2026-07-22, split 2026-07-23) | Exists | Two separate, orthogonal permissions — not one. `users.isDeveloper` gates webhooks + self-serve API keys: admin-toggled always, *and* self-serve toggleable by the user themselves once on a paid tier (no added fee) via `PATCH /api/v1/users/me/developer-access`; free-tier users still need an admin grant. `users.isByokEnabled` gates BYOK separately, admin-only, no self-serve — routing real AI spend through Epicure on the user's own key warrants a manual check-in. Previously all three (webhooks/API keys/BYOK) shared one flag with zero self-serve path. Existing users with a webhook/API key were already grandfathered for `isDeveloper`; a second migration grandfathered existing BYOK users into `isByokEnabled` specifically. | `apps/web/lib/permissions.ts` (`hasDeveloperAccess`, `hasByokAccess`, `canSelfServeDeveloperAccess`), `apps/web/lib/api-auth.ts` (`requireDeveloper`, `requireByok`) | | User webhooks (personal automation) | Exists | 7 events, Zapier-style, requires developer access | `apps/web/lib/webhooks.ts` | diff --git a/apps/web/app/(app)/recipes/[id]/page.tsx b/apps/web/app/(app)/recipes/[id]/page.tsx index 836b4c1..fdafe05 100644 --- a/apps/web/app/(app)/recipes/[id]/page.tsx +++ b/apps/web/app/(app)/recipes/[id]/page.tsx @@ -232,27 +232,26 @@ export default async function RecipePage({ params }: Params) { ingredients={recipe.ingredients.map((ing) => ({ rawName: ing.rawName }))} /> )} - {!locked.variations && ( - ({ - rawName: ing.rawName, - quantity: ing.quantity, - unit: ing.unit, - note: ing.note, - order: ing.order, - }))} - steps={recipe.steps.map((s) => ({ - instruction: s.instruction, - timerSeconds: s.timerSeconds, - order: s.order, - }))} - /> - )} + ({ + rawName: ing.rawName, + quantity: ing.quantity, + unit: ing.unit, + note: ing.note, + order: ing.order, + }))} + steps={recipe.steps.map((s) => ({ + instruction: s.instruction, + timerSeconds: s.timerSeconds, + order: s.order, + }))} + locked={locked.variations} + /> diff --git a/apps/web/components/admin/feature-flags-form.tsx b/apps/web/components/admin/feature-flags-form.tsx index 49adf68..c02084c 100644 --- a/apps/web/components/admin/feature-flags-form.tsx +++ b/apps/web/components/admin/feature-flags-form.tsx @@ -48,7 +48,7 @@ export function FeatureFlagsForm({

Feature Toggles

- Disable a feature for a tier to hide it entirely for that tier's users. + Disable a feature for a tier to hide it for that tier's users (most features hide entirely; recipe variations instead shows a "Pro" badge in its tooltip and opens an upgrade prompt — see each feature's actual behavior in the app).

diff --git a/apps/web/components/recipe/variations-button.tsx b/apps/web/components/recipe/variations-button.tsx index 02a9c4c..b510b47 100644 --- a/apps/web/components/recipe/variations-button.tsx +++ b/apps/web/components/recipe/variations-button.tsx @@ -4,8 +4,10 @@ import { useState } from "react"; import { useTranslations } from "next-intl"; import { GitBranch } from "lucide-react"; import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { VariationsDialog } from "./variations-dialog"; +import { UpgradeDialog } from "@/components/premium/upgrade-dialog"; export function VariationsButton({ recipeId, @@ -15,6 +17,7 @@ export function VariationsButton({ cookMins, ingredients, steps, + locked = false, }: { recipeId: string; baseServings: number; @@ -23,20 +26,30 @@ export function VariationsButton({ cookMins?: number | null; ingredients: Array<{ rawName: string; quantity?: string | number | null; unit?: string | null; note?: string | null; order: number }>; steps: Array<{ instruction: string; timerSeconds?: number | null; order: number }>; + locked?: boolean; }) { const t = useTranslations("recipe"); const [open, setOpen] = useState(false); + const [upgradeOpen, setUpgradeOpen] = useState(false); return ( <> setOpen(true)} aria-label={t("variationsTooltip")}> + } /> - {t("variationsTooltip")} + + {t("variationsTooltip")} + {locked && Pro} + + ); } diff --git a/apps/web/lib/changelog.ts b/apps/web/lib/changelog.ts index 6920f26..e956d71 100644 --- a/apps/web/lib/changelog.ts +++ b/apps/web/lib/changelog.ts @@ -1,5 +1,5 @@ // Mirrors CHANGELOG.md at the repo root — update both together. -export const APP_VERSION = "0.78.2"; +export const APP_VERSION = "0.78.3"; export type ChangelogEntry = { version: string; @@ -11,6 +11,13 @@ export type ChangelogEntry = { }; export const CHANGELOG: ChangelogEntry[] = [ + { + version: "0.78.3", + date: "2026-07-24 15:00", + fixed: [ + "Recipe variations button: removed the small lock icon that overlapped the branch icon when the feature was locked for your tier. The button is visible again (clicking opens an upgrade prompt) and the tooltip now shows a \"Pro\" badge instead.", + ], + }, { version: "0.78.2", date: "2026-07-24 14:30", diff --git a/apps/web/package.json b/apps/web/package.json index 5a8a1f8..63c7d6d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@epicure/web", - "version": "0.78.2", + "version": "0.78.3", "private": true, "scripts": { "dev": "next dev", diff --git a/package.json b/package.json index 68a0ec0..aa0e10a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "epicure", - "version": "0.78.2", + "version": "0.78.3", "private": true, "scripts": { "dev": "pnpm --filter web dev",