# Phase 8 — Meal Planning & Waste Reduction **Goal**: Enable weekly meal planning with automatic nutrition tracking vs targets. The core feature is a **suggestion engine** that recommends recipes prioritizing ingredients already in the pantry (especially those expiring soon), reducing food waste while maintaining nutritional balance. **Depends on**: Phase 5 (products), Phase 6 (recipes), Phase 7 (pantry) --- ## Deliverables 1. `MealPlan` and `NutritionTarget` MongoDB schemas 2. Meal plan CRUD API with daily/weekly views 3. Recipe suggestion engine (algorithmic, not LLM-dependent) 4. Nutrition targets per user with daily tracking 5. Shopping gap analysis (what's needed beyond pantry) 6. Meal plan web UI with weekly calendar and suggestion panel --- ## Data Model ### MealPlan Schema ```typescript // packages/shared/src/types/meal-plan.ts export interface MealPlan { id: string; householdId: string; weekStartDate: Date; // Monday of the planning week days: MealPlanDay[]; status: MealPlanStatus; shoppingListId?: string; // Auto-generated shopping list (Phase 9 link) createdBy: string; createdAt: Date; updatedAt: Date; } export interface MealPlanDay { date: Date; meals: PlannedMeal[]; dailyNutritionTotal: NutritionInfo; // Computed } export interface PlannedMeal { id: string; // UUID for drag-and-drop reference type: MealType; recipeId?: string; // Linked recipe recipeName: string; // Denormalized servings: number; customName?: string; // For non-recipe meals customNutrition?: NutritionInfo; // Manual override for non-recipe meals perServingNutrition: NutritionInfo; // From recipe or custom notes?: string; } export enum MealType { BREAKFAST = 'breakfast', LUNCH = 'lunch', DINNER = 'dinner', SNACK = 'snack', } export enum MealPlanStatus { DRAFT = 'draft', ACTIVE = 'active', COMPLETED = 'completed', } ``` ### NutritionTarget Schema ```typescript // packages/shared/src/types/nutrition-target.ts export interface NutritionTarget { id: string; userId: string; householdId: string; dailyCalories: number; proteinG: number; carbsG: number; fatG: number; fiberG?: number; sodiumMg?: number; sugarG?: number; isActive: boolean; createdAt: Date; updatedAt: Date; } ``` ### MongoDB Indexes ```javascript { householdId: 1, weekStartDate: 1 } // unique per household per week { householdId: 1, status: 1 } { householdId: 1, 'days.meals.recipeId': 1 } // Find plans using a recipe ``` --- ## API Endpoints ### MealPlanModule | Method | Path | Description | Auth | | ------ | ------------------------------ | ---------------------------------------------- | ------ | | GET | `/meal-plans` | List meal plans (paginated) | member | | GET | `/meal-plans/current` | Get current week's plan | member | | GET | `/meal-plans/:id` | Get specific plan | member | | POST | `/meal-plans` | Create new week plan | member | | PATCH | `/meal-plans/:id` | Update plan (add/move/remove meals) | member | | DELETE | `/meal-plans/:id` | Delete plan (draft only) | admin | | POST | `/meal-plans/:id/activate` | Set plan as active | member | | GET | `/meal-plans/:id/shopping-gap` | What's needed beyond current pantry | member | | GET | `/meal-plans/suggestions` | Get recipe suggestions for current pantry | member | | POST | `/meal-plans/suggest-with-llm` | LLM-powered meal plan generation (placeholder) | member | ### NutritionTargetModule | Method | Path | Description | Auth | | ------ | ------------------------ | -------------------------------- | ------ | | GET | `/nutrition-targets` | Get current user's active target | member | | POST | `/nutrition-targets` | Set nutrition targets | member | | PATCH | `/nutrition-targets/:id` | Update targets | member | --- ## Tasks ### 8.1 — Shared Types & Validation - Add all types above to `packages/shared` - Zod schemas for create/update operations - Meal plan day validation (7 days per plan, valid dates) ### 8.2 — Meal Plan CRUD - Standard CRUD with computed `dailyNutritionTotal` per day: - Sum `perServingNutrition * servings` for all meals in each day - On meal plan creation: default to 7 empty days (Monday–Sunday) - On update: support granular operations: - `addMeal(dayIndex, meal)` - `removeMeal(dayIndex, mealId)` - `moveMeal(fromDay, toDay, mealId)` — for drag-and-drop - `updateMeal(dayIndex, mealId, updates)` ### 8.3 — Recipe Suggestion Engine (Core Algorithm) This is the **key differentiating feature** — algorithmic, no LLM required. ```typescript class RecipeSuggestionService { /** * Score and rank recipes based on pantry state and user preferences. * * Input: * - Current pantry items (with freshness urgency) * - Recipe catalog for the household * - User's nutrition targets (optional) * - Already planned meals this week (to avoid repetition) * * Scoring per recipe: * ingredientCoverageScore (0-40 pts): % of ingredients available in pantry * freshnessUrgencyScore (0-30 pts): bonus for using urgent/use-soon items * nutritionBalanceScore (0-20 pts): how well it complements the day's existing meals vs targets * varietyScore (0-10 pts): penalty for recently planned recipes * * Output per suggestion: * - recipe (id, name, perServingNutrition) * - score (total) * - availableIngredients[]: items from pantry that match * - missingIngredients[]: items not in pantry (with estimated cost from Phase 9 if available) * - urgentIngredients[]: pantry items with urgency=urgent that this recipe would use * - reasoning: human-readable explanation of why this recipe was suggested */ suggestRecipes(context: SuggestionContext): Promise; } ``` **Ingredient matching logic**: - Match recipe ingredient's `productId` against pantry items with `status: sealed|opened` - Check quantity: is there enough? (approximate — compare units, flag if unclear) - Prefer items with higher freshness urgency **Scoring weights** (configurable per household): ```typescript const DEFAULT_WEIGHTS = { ingredientCoverage: 40, freshnessUrgency: 30, nutritionBalance: 20, variety: 10, }; ``` ### 8.4 — Shopping Gap Analysis - `GET /meal-plans/:id/shopping-gap`: - For each recipe in the meal plan, list required ingredients - Cross-reference with current pantry (available quantity vs needed quantity) - Return: ```typescript interface ShoppingGap { coveredByPantry: ShoppingGapItem[]; // Already have enough needToBuy: ShoppingGapItem[]; // Partially or fully missing pantryItemsUsed: PantryItemUsage[]; // Which pantry items will be consumed } interface ShoppingGapItem { productId: string; productName: string; totalNeeded: { quantity: number; unit: ServingUnit }; availableInPantry: { quantity: number; unit: ServingUnit }; shortfall: { quantity: number; unit: ServingUnit }; usedInRecipes: string[]; // Recipe names } ``` - This output feeds directly into Phase 9's auto-generated shopping lists ### 8.5 — LLM Suggestion Placeholder - `POST /meal-plans/suggest-with-llm`: - Builds a context object: pantry summary, nutrition targets, dietary preferences - Calls `ILlmProvider.suggestMealPlan(context)` - With `NoOpLlmProvider`: returns `{ available: false }` - When wired (Phase 10): returns a full week meal plan draft ### 8.6 — Web UI: Meal Planning - `/meal-plans` page: - **Weekly calendar grid**: 7 columns (Mon–Sun) × 4 rows (Breakfast, Lunch, Dinner, Snack) - Each cell: drop zone for recipes, shows meal name + calorie badge - **Drag-and-drop**: drag recipes from suggestion panel or between cells - **Daily nutrition summary row** at bottom: calories, protein, carbs, fat bars - Color-coded vs user's nutrition targets (under = blue, on-target = green, over = red) - **Week navigation**: previous/next week arrows - **Suggestion panel** (sidebar or drawer): - "Suggestions based on your pantry" — ranked list from suggestion engine - Each suggestion shows: recipe name, match score, "Uses: [urgent items]", "Need to buy: [missing items]" - Click to expand: full ingredient match breakdown - "Add to plan" button → pick day + meal type - **Shopping gap tab**: shows what's needed beyond pantry, "Generate shopping list" button (Phase 9 integration) - `/nutrition-targets` settings: - Daily macro targets form (calories, protein, carbs, fat) - Preset templates: "Maintenance", "Weight loss", "Muscle gain", "Custom" - Visual preview: donut chart of macro ratios --- ## Acceptance Criteria - [ ] Can create a weekly meal plan and add meals to specific days/slots - [ ] Daily nutrition totals are computed and displayed - [ ] Suggestion engine returns ranked recipes based on pantry state - [ ] Suggestions prioritize recipes using soon-to-expire pantry items - [ ] Shopping gap analysis correctly identifies missing ingredients - [ ] Drag-and-drop works in the weekly calendar UI - [ ] Nutrition targets can be set per user - [ ] Daily nutrition bars show progress vs targets - [ ] Variety scoring penalizes recently used recipes --- ## Estimated Effort Large. The suggestion engine scoring algorithm, shopping gap analysis, and calendar UI with drag-and-drop are all significant features.