refactor: fold batch-cooking into the main recipes list and Generate dialog

Batch-cook recipes no longer live in a separate /batch-cooking section:
- Removed the dedicated page/route and its nav button.
- /recipes shows both kinds together, with a chef-hat badge marking
  batch sessions, and a filter to isolate/hide them.
- "Batch cooking" is now a third tab in the existing "Generate recipe
  with AI" dialog (alongside Describe/Photo) instead of a separate
  button — one AI entry point instead of three.
- Extracted the counters/difficulty/dietary form into BatchCookFields,
  shared between that dialog and the meal-planner's batch-cooking
  dialog.
- Also fixed a dialog resize issue (progress bar mounting mid-generate
  grew the dialog's height, which the positioning library didn't
  always handle) by reserving space up front and capping dialog height
  with a scroll fallback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-12 09:31:04 +02:00
parent f29fa531a1
commit 646c97128d
10 changed files with 238 additions and 200 deletions
+16 -6
View File
@@ -3,7 +3,7 @@
import { useState, useCallback, useEffect } from "react";
import Image from "next/image";
import { useTranslations } from "next-intl";
import { Trash2, Globe, Lock, Link2, X, Check, ListChecks, LayoutGrid, List, Rows3, FolderPlus } from "lucide-react";
import { Trash2, Globe, Lock, Link2, X, Check, ListChecks, LayoutGrid, List, Rows3, FolderPlus, ChefHat } from "lucide-react";
import { AddToCollectionDialog } from "@/components/recipe/add-to-collection-dialog";
import { Button, buttonVariants } from "@/components/ui/button";
import {
@@ -45,6 +45,7 @@ type Recipe = {
updatedAt: Date;
photos?: Array<{ storageKey: string; isCover: boolean }>;
isFavorited?: boolean;
isBatchCook?: boolean;
};
/** Stops the click from bubbling to the card's own Link/select handler. */
@@ -114,9 +115,12 @@ function GridCard({ recipe, selected, selectMode }: { recipe: Recipe; selected:
>
<RecipeThumb recipe={recipe} selectMode={selectMode} selected={selected} className="aspect-video" />
<div className="flex flex-col flex-1 p-3 gap-1">
<h3 className={cn("font-semibold leading-tight line-clamp-2 text-sm transition-colors", !selectMode && "group-hover:text-primary")}>
{recipe.title}
</h3>
<div className="flex items-center gap-1.5">
<h3 className={cn("font-semibold leading-tight line-clamp-2 text-sm transition-colors", !selectMode && "group-hover:text-primary")}>
{recipe.title}
</h3>
{recipe.isBatchCook && <ChefHat className="h-3.5 w-3.5 text-primary shrink-0" />}
</div>
{recipe.description && (
<p className="text-xs text-muted-foreground line-clamp-2 leading-relaxed">{recipe.description}</p>
)}
@@ -171,7 +175,10 @@ function ListRow({ recipe, selected, selectMode }: { recipe: Recipe; selected: b
<RecipeThumb recipe={recipe} selectMode={selectMode} selected={selected} className="w-28 h-20 rounded-lg" />
<div className="flex-1 min-w-0 flex flex-col gap-1">
<div className="flex items-start justify-between gap-2">
<h3 className={cn("font-semibold leading-tight line-clamp-1 text-sm", !selectMode && "group-hover:text-primary")}>{recipe.title}</h3>
<h3 className={cn("font-semibold leading-tight line-clamp-1 text-sm flex items-center gap-1.5", !selectMode && "group-hover:text-primary")}>
{recipe.title}
{recipe.isBatchCook && <ChefHat className="h-3.5 w-3.5 text-primary shrink-0" />}
</h3>
<div className="flex items-center gap-1 shrink-0">
{!selectMode && (
<StopPropagation>
@@ -211,7 +218,10 @@ function CompactRow({ recipe, selected, selectMode }: { recipe: Recipe; selected
)}
>
<RecipeThumb recipe={recipe} selectMode={selectMode} selected={selected} className="w-10 h-10 rounded-md shrink-0" />
<h3 className={cn("font-medium text-sm truncate flex-1 min-w-0", !selectMode && "group-hover:text-primary")}>{recipe.title}</h3>
<h3 className={cn("font-medium text-sm truncate flex-1 min-w-0 flex items-center gap-1.5", !selectMode && "group-hover:text-primary")}>
<span className="truncate">{recipe.title}</span>
{recipe.isBatchCook && <ChefHat className="h-3.5 w-3.5 text-primary shrink-0" />}
</h3>
<span className="hidden sm:flex items-center gap-1 text-xs text-muted-foreground shrink-0"><Users className="h-3 w-3" />{recipe.baseServings}</span>
{totalMins > 0 && (
<span className="hidden sm:flex items-center gap-1 text-xs text-muted-foreground shrink-0"><Clock className="h-3 w-3" />{t("total", { mins: totalMins })}</span>