feat: recipe detail + weekly shopping list (PRD §5.1, §5.4)

Recipe detail
- Seed catalog of 33 italian ingredients with macros, allergens, avg price.
- Each seed recipe now declares its RecipeIngredient links (qty + unit).
- /recipes/[id] page: editorial layout with prep meta, ingredients list,
  macros block, preparation, chef's note. Plan-week meal names linkable.

Shopping list
- GET /me/shopping-list aggregates RecipeIngredient quantities of the
  ACTIVE plan, grouped by ingredient category, sorted alphabetically.
- /shopping page: total items, estimated cost, per-category checklist
  with check-off persisted in localStorage per plan id.
- Home gets a "Spesa" nav item; /plan links the list when a plan exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucianoandClaude Opus 4.7 committed 2026-04-29 21:13:00 +02:00
1 parent b79340efb7
commit ad1e4ecaeb
13 files changed
+1331 -14

No files matched your search

+10 -1
View File
@@ -1,4 +1,5 @@
import { prisma } from '@ketopath/db';
import Link from 'next/link';
import { redirect } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { setRequestLocale } from 'next-intl/server';
@@ -62,12 +63,20 @@ function PlanPageContent({ plan }: { plan: Awaited<ReturnType<typeof fetchCurren
{plan ? formatWeek(plan.weekStart) : t('noPlan')}
</p>
</div>
<div className="animate-fade-up [animation-delay:360ms] md:col-span-4 md:text-right">
<div className="animate-fade-up flex flex-col items-stretch gap-3 [animation-delay:360ms] md:col-span-4 md:items-end">
<form action={regeneratePlan}>
<Button type="submit" variant={plan ? 'outline' : 'default'} size="lg">
{plan ? t('regenerate') : t('generate')}
</Button>
</form>
{plan ? (
<Link
href="/shopping"
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
>
Lista della spesa →
</Link>
) : null}
</div>
</div>