feat: V1 tracking endpoints + meal plan generation (PRD §5.1, §5.2, §5.3)
@ketopath/shared:
- tracking/schema.ts — Zod schemas for WeightEntry input (weight, optional
measurements/notes/energy/sleep/hunger, photo URLs) and FastEvent start /
update, plus PROTOCOL_DEFAULT_MINUTES table
- planner/macros.ts — macrosForPhase(): protein 1.6-1.8g/kg, netCarb 25/60/120g
by phase, fat = remainder (PRD §9.3)
- planner/matchmaking.ts — pure matchMeals() that filters by exclusion tags,
phase compatibility and meal category, then scores by euclidean distance
from the meal's macro target with a 1.5 penalty for recently-consumed recipes;
DEFAULT_MEAL_SHARE constant (25/35/10/30 %)
- 5 new unit tests cover exclusion, phase, recency, topN, category mismatch
apps/api:
- modules/tracking/weight.routes.ts — GET /me/weight-entries (last 60),
POST /me/weight-entries with upsert on (userId, date); encrypted fields
serialised to/from JSON strings
- modules/tracking/fast.routes.ts — GET, POST (start) and PATCH (update) on
fast events; symptoms stored as encrypted JSON
- modules/plan/plan.routes.ts — POST /me/meal-plans:
- reads profile + preferences, computes BMR (Mifflin-St Jeor), TDEE, daily
kcal target with the phase-dependent deficit, then macros via macrosForPhase
- upserts a MealPlan rooted at Monday-of-this-week, wipes prior slots, and
fills 28 slots running matchMeals per (day, meal) with a recent-2-day
rolling exclusion list to keep variety
- selected recipe + 4 alternatives per slot
- GET /me/meal-plans/current returns the active plan with selected/alternatives
@ketopath/db:
- prisma/seed-recipes.ts — 16 italian keto recipes (4 per meal type) with
estimated macros per serving and phase compatibility
- prisma/seed.ts — idempotent insertion (skip on existing name match)
Verified end-to-end with sign-up → create profile (Michele PRD persona) →
generate plan: 28 slots filled, daily target 1399 kcal / 137 P / 83 F / 25 C,
matchmaking distributes 4 different breakfasts across the first 4 days as
the recent-consumed penalty kicks in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
0d2d1f49c7
commit
4b6c3f6ae9
12 files changed
+854
-3
No files matched your search
@@ -0,0 +1,218 @@
|
||||
// Seed minimo del catalogo ricette italiane keto/low-carb (PRD §5.1).
|
||||
// 16 ricette distribuite su COLAZIONE/PRANZO/SPUNTINO/CENA, fasi 1/2/3.
|
||||
// Macros stimati per porzione singola.
|
||||
|
||||
import type { Difficulty, MealCategory } from '@prisma/client';
|
||||
|
||||
export interface SeedRecipe {
|
||||
name: string;
|
||||
category: MealCategory;
|
||||
description: string;
|
||||
prepMinutes: number;
|
||||
difficulty: Difficulty;
|
||||
kcal: number;
|
||||
proteinG: number;
|
||||
fatG: number;
|
||||
netCarbG: number;
|
||||
phases: number[];
|
||||
notesChef?: string;
|
||||
}
|
||||
|
||||
export const SEED_RECIPES: SeedRecipe[] = [
|
||||
// COLAZIONE
|
||||
{
|
||||
name: 'Frittata di spinaci e parmigiano',
|
||||
category: 'COLAZIONE',
|
||||
description: 'Tre uova con spinaci saltati e parmigiano grattugiato.',
|
||||
prepMinutes: 10,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 360,
|
||||
proteinG: 28,
|
||||
fatG: 26,
|
||||
netCarbG: 4,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Yogurt greco con noci e cannella',
|
||||
category: 'COLAZIONE',
|
||||
description: 'Yogurt greco intero con un cucchiaio di noci tritate.',
|
||||
prepMinutes: 3,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 320,
|
||||
proteinG: 18,
|
||||
fatG: 22,
|
||||
netCarbG: 8,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Avocado toast keto su pane di mandorle',
|
||||
category: 'COLAZIONE',
|
||||
description: 'Mezzo avocado schiacciato su una fetta di pane proteico.',
|
||||
prepMinutes: 5,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 380,
|
||||
proteinG: 12,
|
||||
fatG: 30,
|
||||
netCarbG: 6,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Uova strapazzate con pancetta',
|
||||
category: 'COLAZIONE',
|
||||
description: 'Due uova strapazzate al burro con pancetta croccante.',
|
||||
prepMinutes: 8,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 410,
|
||||
proteinG: 24,
|
||||
fatG: 33,
|
||||
netCarbG: 2,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
// PRANZO
|
||||
{
|
||||
name: 'Insalata di pollo, avocado e pomodorini',
|
||||
category: 'PRANZO',
|
||||
description: 'Petto di pollo grigliato, avocado, pomodorini, olio EVO.',
|
||||
prepMinutes: 15,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 540,
|
||||
proteinG: 38,
|
||||
fatG: 36,
|
||||
netCarbG: 9,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Salmone al forno con asparagi',
|
||||
category: 'PRANZO',
|
||||
description: 'Filetto di salmone con asparagi al forno e limone.',
|
||||
prepMinutes: 20,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 510,
|
||||
proteinG: 36,
|
||||
fatG: 35,
|
||||
netCarbG: 6,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Bistecca con zucchine grigliate',
|
||||
category: 'PRANZO',
|
||||
description: 'Tagliata di manzo con zucchine grigliate e olio EVO.',
|
||||
prepMinutes: 18,
|
||||
difficulty: 'MEDIA',
|
||||
kcal: 580,
|
||||
proteinG: 42,
|
||||
fatG: 38,
|
||||
netCarbG: 5,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Insalatona con tonno e olive',
|
||||
category: 'PRANZO',
|
||||
description: "Insalata mista, tonno sott'olio, olive nere, capperi.",
|
||||
prepMinutes: 10,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 470,
|
||||
proteinG: 32,
|
||||
fatG: 32,
|
||||
netCarbG: 7,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
// SPUNTINO
|
||||
{
|
||||
name: 'Mandorle al naturale',
|
||||
category: 'SPUNTINO',
|
||||
description: 'Una manciata di mandorle (~25 g).',
|
||||
prepMinutes: 1,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 150,
|
||||
proteinG: 5,
|
||||
fatG: 13,
|
||||
netCarbG: 3,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Cubetti di parmigiano',
|
||||
category: 'SPUNTINO',
|
||||
description: '30 g di parmigiano stagionato 24 mesi.',
|
||||
prepMinutes: 1,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 120,
|
||||
proteinG: 11,
|
||||
fatG: 8,
|
||||
netCarbG: 0,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Olive verdi marinate',
|
||||
category: 'SPUNTINO',
|
||||
description: 'Una porzione di olive verdi (60 g) con erbe aromatiche.',
|
||||
prepMinutes: 1,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 90,
|
||||
proteinG: 1,
|
||||
fatG: 9,
|
||||
netCarbG: 1,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Cetrioli con hummus di zucchine',
|
||||
category: 'SPUNTINO',
|
||||
description: 'Cetrioli a bastoncino con hummus light di zucchine.',
|
||||
prepMinutes: 5,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 110,
|
||||
proteinG: 3,
|
||||
fatG: 8,
|
||||
netCarbG: 5,
|
||||
phases: [2, 3],
|
||||
},
|
||||
// CENA
|
||||
{
|
||||
name: 'Branzino al cartoccio con verdure',
|
||||
category: 'CENA',
|
||||
description: 'Branzino al cartoccio con zucchine, carote e pomodorini.',
|
||||
prepMinutes: 25,
|
||||
difficulty: 'MEDIA',
|
||||
kcal: 460,
|
||||
proteinG: 38,
|
||||
fatG: 28,
|
||||
netCarbG: 7,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Polpette di pollo e ricotta',
|
||||
category: 'CENA',
|
||||
description: 'Polpette al forno con ricotta, basilico e parmigiano.',
|
||||
prepMinutes: 30,
|
||||
difficulty: 'MEDIA',
|
||||
kcal: 490,
|
||||
proteinG: 40,
|
||||
fatG: 32,
|
||||
netCarbG: 4,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Zuppa di cavolfiore e gorgonzola',
|
||||
category: 'CENA',
|
||||
description: 'Crema di cavolfiore con gorgonzola dolce DOP.',
|
||||
prepMinutes: 25,
|
||||
difficulty: 'FACILE',
|
||||
kcal: 420,
|
||||
proteinG: 18,
|
||||
fatG: 33,
|
||||
netCarbG: 9,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
{
|
||||
name: 'Tagliata di manzo con rucola e grana',
|
||||
category: 'CENA',
|
||||
description: 'Tagliata di manzo su letto di rucola con scaglie di grana.',
|
||||
prepMinutes: 15,
|
||||
difficulty: 'MEDIA',
|
||||
kcal: 540,
|
||||
proteinG: 44,
|
||||
fatG: 36,
|
||||
netCarbG: 3,
|
||||
phases: [1, 2, 3],
|
||||
},
|
||||
];
|
||||
Reference in new issue
Block a user