From 274c50c2f6c9706e69384ae6c6e3c98428f9aaa5 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 20 Jul 2026 23:23:01 +0200 Subject: [PATCH] fix: desktop nav ignored feature toggles; add chatbots toggle (v0.62.0) Feature toggles (Settings -> Features) were filtered into visibleNavItems but the desktop horizontal nav still mapped over the raw NAV_ITEMS list, so hiding a feature only worked on mobile. Both nav renders now read the same filtered list. Also adds a Chatbots toggle covering the recipe cooking-chat panel and the recipe-list cooking assistant, wired end-to-end (schema, migration, feature-prefs lib, API route, settings form, i18n, openapi). Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 8 + apps/web/app/(app)/recipes/[id]/page.tsx | 6 +- apps/web/app/(app)/recipes/page.tsx | 4 +- .../api/v1/users/me/feature-prefs/route.ts | 1 + apps/web/components/layout/nav.tsx | 4 +- .../settings/feature-toggles-form.tsx | 2 +- apps/web/lib/changelog.ts | 12 +- apps/web/lib/feature-prefs.ts | 5 +- apps/web/lib/openapi.ts | 3 +- apps/web/messages/en.json | 4 +- apps/web/messages/fr.json | 4 +- apps/web/package.json | 2 +- package.json | 2 +- .../db/src/migrations/0057_sudden_kree.sql | 1 + .../db/src/migrations/meta/0057_snapshot.json | 5854 +++++++++++++++++ packages/db/src/migrations/meta/_journal.json | 7 + packages/db/src/schema/users.ts | 1 + 17 files changed, 5906 insertions(+), 14 deletions(-) create mode 100644 packages/db/src/migrations/0057_sudden_kree.sql create mode 100644 packages/db/src/migrations/meta/0057_snapshot.json diff --git a/CHANGELOG.md b/CHANGELOG.md index f5c27c8..1e19098 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ 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.62.0 — 2026-07-20 23:45 + +### Added +- Settings → Features: new Chatbots toggle — hides both the recipe cooking-chat panel and the recipe-list cooking assistant when off. + +### Fixed +- Feature toggles (Settings → Features) were only respected by the mobile hamburger menu — the desktop horizontal nav still showed every item regardless of your saved preferences. Both now read the same visibility list. + ## 0.61.0 — 2026-07-20 23:15 ### Added diff --git a/apps/web/app/(app)/recipes/[id]/page.tsx b/apps/web/app/(app)/recipes/[id]/page.tsx index a8d59e5..3c1b875 100644 --- a/apps/web/app/(app)/recipes/[id]/page.tsx +++ b/apps/web/app/(app)/recipes/[id]/page.tsx @@ -35,6 +35,7 @@ import { CommentsSection } from "@/components/social/comments-section"; import { getPublicUrl } from "@/lib/storage"; import { cn, stripMarkdown } from "@/lib/utils"; import { RecipeChatPanel } from "@/components/recipe/recipe-chat-panel"; +import { getFeaturePrefs } from "@/lib/feature-prefs"; import { BatchCookSteps } from "@/components/recipe/batch-cook-steps"; import { BatchCookDishes } from "@/components/recipe/batch-cook-dishes"; import { KeepScreenAwake } from "@/components/recipe/keep-screen-awake"; @@ -71,7 +72,7 @@ export default async function RecipePage({ params }: Params) { const DIETARY_LABELS = m.recipe.dietary; const unitPref = (session.user as { unitPref?: string }).unitPref === "imperial" ? "imperial" : "metric"; - const [recipe, ratingData, favoriteData, myRating, forkedFrom, myNote, dishCookLog, featureFlags] = await Promise.all([ + const [recipe, ratingData, favoriteData, myRating, forkedFrom, myNote, dishCookLog, featureFlags, featurePrefs] = await Promise.all([ db.query.recipes.findFirst({ where: and( eq(recipes.id, id), @@ -102,6 +103,7 @@ export default async function RecipePage({ params }: Params) { columns: { batchDishId: true, cookedAt: true }, }), getFeatureFlagMatrix(), + getFeaturePrefs(session.user.id), ]); if (!recipe) notFound(); @@ -507,7 +509,7 @@ export default async function RecipePage({ params }: Params) { - + {featurePrefs.chatbots && } ); } diff --git a/apps/web/app/(app)/recipes/page.tsx b/apps/web/app/(app)/recipes/page.tsx index 186aa2b..ad8c549 100644 --- a/apps/web/app/(app)/recipes/page.tsx +++ b/apps/web/app/(app)/recipes/page.tsx @@ -9,6 +9,7 @@ import { RecipesEmptyState } from "@/components/recipe/recipes-empty-state"; import { RecipesGrid } from "@/components/recipe/recipes-grid"; import { CookingAssistantPanel } from "@/components/recipe/cooking-assistant-panel"; import { getMessages } from "@/lib/i18n/server"; +import { getFeaturePrefs } from "@/lib/feature-prefs"; export const metadata: Metadata = {}; @@ -40,6 +41,7 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear const session = await auth.api.getSession({ headers: await headers() }); if (!session) return null; const m = getMessages((session.user as { locale?: string }).locale); + const featurePrefs = await getFeaturePrefs(session.user.id); const { q, sort, visibility, difficulty, tag, page: pageParam, batchCook, recipeType } = await searchParams; const query = (q ?? "").trim().slice(0, 200); @@ -160,7 +162,7 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear )} - + {featurePrefs.chatbots && } ); } diff --git a/apps/web/app/api/v1/users/me/feature-prefs/route.ts b/apps/web/app/api/v1/users/me/feature-prefs/route.ts index 2e0e123..1df3ab0 100644 --- a/apps/web/app/api/v1/users/me/feature-prefs/route.ts +++ b/apps/web/app/api/v1/users/me/feature-prefs/route.ts @@ -11,6 +11,7 @@ const PutSchema = z.object({ shoppingLists: z.boolean().optional(), collections: z.boolean().optional(), messages: z.boolean().optional(), + chatbots: z.boolean().optional(), }); export async function GET() { diff --git a/apps/web/components/layout/nav.tsx b/apps/web/components/layout/nav.tsx index d14b2ea..75ba73d 100644 --- a/apps/web/components/layout/nav.tsx +++ b/apps/web/components/layout/nav.tsx @@ -52,7 +52,7 @@ export function Nav() { // backend default — avoids a flash of items disappearing on load for the // (much more common) case where a user hasn't hidden anything. const [featurePrefs, setFeaturePrefs] = useState({ - nutrition: true, pantry: true, mealPlan: true, shoppingLists: true, collections: true, messages: true, + nutrition: true, pantry: true, mealPlan: true, shoppingLists: true, collections: true, messages: true, chatbots: true, }); useEffect(() => { @@ -121,7 +121,7 @@ export function Nav() { Epicure