feat: add batch-cooking sessions (single "mega recipe" with dish sections)
New AI-generated batch-cooking flow: pick N dinners/lunches + servings, get one unified recipe covering a full prep session — merged shopping list, steps tagged per-dish (a step can advance several dishes at once, e.g. a shared oven bake), and per-dish storage (fridge/freezer) + exact day-of reheat/finishing instructions. Schema: recipes.isBatchCook, recipeSteps.appliesTo (dish names), new recipeBatchDishes table. Batch recipes are excluded from the normal /recipes list and live under their own /batch-cooking section. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useTranslations, useLocale } from "next-intl";
|
||||
import { ChefHat, PlusCircle, Utensils } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { BatchCookGenerateDialog } from "@/components/recipe/batch-cook-generate-dialog";
|
||||
|
||||
type Session = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
baseServings: number;
|
||||
createdAt: string;
|
||||
dishCount: number;
|
||||
};
|
||||
|
||||
export function BatchCookingPageContent({ sessions }: { sessions: Session[] }) {
|
||||
const t = useTranslations("batchCooking");
|
||||
const locale = useLocale();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">{t("title")}</h1>
|
||||
<p className="text-muted-foreground mt-1">{t("subtitle")}</p>
|
||||
</div>
|
||||
<Button size="sm" onClick={() => setOpen(true)}>
|
||||
<PlusCircle className="h-4 w-4" />
|
||||
{t("newSession")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{sessions.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center h-64 border-2 border-dashed rounded-xl gap-4">
|
||||
<ChefHat className="h-12 w-12 text-muted-foreground/40" />
|
||||
<p className="text-muted-foreground text-sm">{t("empty")}</p>
|
||||
<Button size="sm" onClick={() => setOpen(true)}>
|
||||
<PlusCircle className="h-4 w-4" />
|
||||
{t("newSession")}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{sessions.map((s) => (
|
||||
<Link
|
||||
key={s.id}
|
||||
href={`/recipes/${s.id}`}
|
||||
className="group rounded-xl border p-4 hover:shadow-sm transition-shadow space-y-2"
|
||||
>
|
||||
<h2 className="font-semibold group-hover:text-primary transition-colors line-clamp-1">{s.title}</h2>
|
||||
{s.description && <p className="text-sm text-muted-foreground line-clamp-2">{s.description}</p>}
|
||||
<div className="flex items-center gap-3 text-xs text-muted-foreground pt-1">
|
||||
<span className="flex items-center gap-1">
|
||||
<Utensils className="h-3.5 w-3.5" />
|
||||
{t("dishCount", { count: s.dishCount })}
|
||||
</span>
|
||||
<span>{new Date(s.createdAt).toLocaleDateString(locale)}</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<BatchCookGenerateDialog open={open} onOpenChange={setOpen} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user