Phases 6-7

This commit is contained in:
Aerilyn Weber 2026-05-14 14:47:23 +09:00
parent 76a516a417
commit 029940b079
111 changed files with 17247 additions and 447 deletions

View file

@ -26,33 +26,97 @@ MeshiTrack helps households manage medicines and food. Medicine tracking comes f
| **Monorepo** | Turborepo 2 |
| **Runtime** | Node.js 22+ (ESM-only) |
## Phase Completion Criteria (Mandatory)
Every phase MUST deliver ALL of the following before being considered complete. No phase may be marked done if any layer is missing.
### 1. Shared Package (`packages/shared`)
- [ ] Types defined in `src/types/`
- [ ] Enums defined in `src/enums/` with unit tests
- [ ] Zod validation schemas in `src/validation/` with unit tests
- [ ] Barrel exports updated in `index.ts`
- [ ] 100% line/function/statement coverage, 90% branch coverage
### 2. API Package (`packages/api`)
- [ ] Mongoose schema in `src/schemas/`
- [ ] Repository with `.lean().exec()` on all reads
- [ ] Service with business logic
- [ ] Route plugin registered in `main.ts`
- [ ] Unit tests for repository, service, and routes (100% coverage)
- [ ] All queries scoped to `householdId`
### 3. Web Package (`packages/web`)
- [ ] API client service in `src/services/` with unit tests
- [ ] Page(s) in `src/app/(dashboard)/` implementing the UI spec
- [ ] Component tests (React Testing Library) for every page and interactive component
- [ ] Server Components by default; `'use client'` only where interactivity is needed
### 4. CI Gate (must pass before phase sign-off)
```bash
npm run build # All packages compile
npm run test:cov # Coverage thresholds enforced (100/100/100/90)
npm run lint # Zero lint errors
```
### 5. Documentation
- [ ] Phase spec updated with `[x]` on completed tasks
- [ ] Acceptance criteria verified
---
## Phase Overview
### Medicine Tracking (Phases 1-4)
| Phase | Name | Description | Doc |
| ----- | ------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| 0 | Foundation & Infrastructure | Repo scaffolding, Docker, auth | [phase-0-foundation.md](phases/phase-0-foundation.md) |
| 1 | Medicine Library | Searchable medicine catalog with dosage/form info | [phase-1-medicine-library.md](phases/phase-1-medicine-library.md) |
| 2 | Medicine Cabinet | Inventory tracking with quantity, expiry, low-stock alerts | [phase-2-medicine-cabinet.md](phases/phase-2-medicine-cabinet.md) |
| 3 | Regimens & Pill Organizer | Daily medication schedules, batch-dispense, burn rate | [phase-3-regimens-pill-organizer.md](phases/phase-3-regimens-pill-organizer.md) |
| 4 | Pharmacies, Prices & Refills | Shared store infrastructure, price tracking, refill alerts | [phase-4-pharmacies-prices-refills.md](phases/phase-4-pharmacies-prices-refills.md) |
| Phase | Name | Description | Status | Doc |
| ----- | ------------------------------ | ---------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------- |
| 0 | Foundation & Infrastructure | Repo scaffolding, Docker, auth | DONE | [phase-0-foundation.md](phases/phase-0-foundation.md) |
| 1 | Medicine Library | Searchable medicine catalog with dosage/form info | DONE | [phase-1-medicine-library.md](phases/phase-1-medicine-library.md) |
| 2 | Medicine Cabinet | Inventory tracking with quantity, expiry, low-stock alerts | DONE | [phase-2-medicine-cabinet.md](phases/phase-2-medicine-cabinet.md) |
| 3 | Regimens & Pill Organizer | Daily medication schedules, batch-dispense, burn rate | DONE | [phase-3-regimens-pill-organizer.md](phases/phase-3-regimens-pill-organizer.md) |
| 4 | Pharmacies, Prices & Refills | Shared store infrastructure, price tracking, refill alerts | DONE | [phase-4-pharmacies-prices-refills.md](phases/phase-4-pharmacies-prices-refills.md) |
### Food Tracking (Phases 5-9)
| Phase | Name | Description | Doc |
| ----- | ------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| 5 | Product Library | Searchable food product catalog with nutrition data | [phase-5-product-library.md](phases/phase-5-product-library.md) |
| 6 | Recipe Management | Recipe CRUD, nutrition auto-calculation, import | [phase-6-recipes.md](phases/phase-6-recipes.md) |
| 7 | Pantry & Fridge Tracking | Track item lifecycle, freshness, spoilage estimation | [phase-7-pantry.md](phases/phase-7-pantry.md) |
| 8 | Meal Planning & Waste Reduction| Suggest meals from pantry, nutrition targets, weekly planning | [phase-8-meal-planning.md](phases/phase-8-meal-planning.md) |
| 9 | Grocery & Price Tracking | Shopping lists, price analytics, store comparison (reuses Phase 4 stores) | [phase-9-grocery.md](phases/phase-9-grocery.md) |
| Phase | Name | Description | Status | Doc |
| ----- | ------------------------------ | ---------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------- |
| 5 | Product Library | Searchable food product catalog with nutrition data | DONE | [phase-5-product-library.md](phases/phase-5-product-library.md) |
| 6 | Recipe Management | Recipe CRUD, nutrition auto-calculation, import | DONE | [phase-6-recipes.md](phases/phase-6-recipes.md) |
| 7 | Pantry & Fridge Tracking | Track item lifecycle, freshness, spoilage estimation | DONE | [phase-7-pantry.md](phases/phase-7-pantry.md) |
| 8 | Meal Planning & Waste Reduction| Suggest meals from pantry, nutrition targets, weekly planning | NOT STARTED | [phase-8-meal-planning.md](phases/phase-8-meal-planning.md) |
| 9 | Grocery & Price Tracking | Shopping lists, price analytics, store comparison (reuses Phase 4 stores) | NOT STARTED | [phase-9-grocery.md](phases/phase-9-grocery.md) |
### Shared (Phase 10)
| Phase | Name | Description | Doc |
| ----- | ------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| 10 | LLM Integration & Smart Features | Wire up LLM providers, enable smart features across both domains | [phase-10-llm.md](phases/phase-10-llm.md) |
| Phase | Name | Description | Status | Doc |
| ----- | ------------------------------ | ---------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------- |
| 10 | LLM Integration & Smart Features | Wire up LLM providers, enable smart features across both domains | PARTIAL | [phase-10-llm.md](phases/phase-10-llm.md) |
---
## Current Status & Gaps
### Phase 8 — Meal Planning (Next Priority)
The foundation for food tracking (Phases 5-7) is fully implemented and integrated. The next major milestone is Phase 8, which introduces the meal planning calendar and the algorithmic suggestion engine.
**Immediate Goals:**
- Implement `MealPlan` and `NutritionTarget` schemas.
- Build the `SuggestionEngineService` for pantry-based recipe ranking.
- Create the weekly calendar UI with drag-and-drop support.
### Phase 10 — LLM (PARTIAL)
The `ILlmProvider` interface and `NoOpLlmProvider` are implemented. Actual provider implementations and smart features are deferred.
### Lessons Learned
Previous phases (6 & 7) were completed without finishing Phase 5's full stack (API routes + web UI). This created a gap where the Product Library has no user-facing interface despite being consumed internally. **Going forward, every phase must deliver its complete vertical slice (shared -> API -> web -> tests) before the next phase starts.**
## Cross-Cutting Concerns
@ -121,8 +185,39 @@ MeshiTrack/
## Verification Strategy
- **Per-phase**: each phase ends with a working `docker compose up` that demos the new feature
- **Integration**: Postman/Bruno collection maintained alongside API development
- **E2E smoke test (medicine)**: automated script that creates a user, adds medicines, stocks cabinet, creates regimen, fills organizer, checks refill alerts
- **E2E smoke test (food)**: automated script that adds products, creates a recipe, stocks the pantry, generates a meal plan, and creates a shopping list
- **Performance**: MongoDB indexes reviewed per phase; query profiling before phase sign-off
### Per-Phase Gate (Blocking)
Every phase must pass these checks before sign-off:
1. **Build**: `npm run build` succeeds (all packages compile)
2. **Coverage**: `npm run test:cov` passes with thresholds:
- `packages/api`: 100% lines/functions/statements, 90% branches
- `packages/shared`: 100% lines/functions/statements, 90% branches
- `packages/web`: all component tests pass
3. **Lint**: `npm run lint` reports zero errors
4. **Web UI Exists**: every phase that adds API endpoints also adds corresponding web pages with component tests
5. **Services Tested**: every `*.service.ts` in web has a corresponding `*.test.ts`
### Per-Phase Deliverable Checklist
For each phase, the task breakdown must follow this template order:
```
X.1 — Shared Types & Validation (+ tests)
X.2 — Mongoose Schema & Repository (+ tests)
X.3 — Service (+ tests)
X.4 — Routes (+ tests)
X.5 — Web: API Client Service (+ tests)
X.6 — Web: Pages & Components (+ component tests)
X.7 — CI Verification (build + test:cov + lint)
```
### Integration Testing
- Postman/Bruno collection maintained alongside API development
- E2E smoke test (medicine): creates user, adds medicines, stocks cabinet, creates regimen, fills organizer, checks refill alerts
- E2E smoke test (food): adds products, creates recipe, stocks pantry, generates meal plan, creates shopping list
### Performance
- MongoDB indexes reviewed per phase; query profiling before phase sign-off

View file

@ -97,6 +97,19 @@ Separate each group with a blank line.
| React hook | camelCase | `useProducts`, `usePantryItems` |
| CSS class (Tailwind) | kebab-case | via Tailwind utilities |
## Implementation Workflow (Vertical Slice)
Every feature or phase implementation must follow this order to ensure consistency and type safety across the monorepo:
1. **Shared Layer (`packages/shared`)**: Define types, enums, and Zod schemas. Add unit tests.
2. **Database Layer (`packages/api`)**: Create Mongoose schema and Repository. Use `.lean().exec()` on all reads. Add integration tests.
3. **Service Layer (`packages/api`)**: Implement business logic using Awilix DI. Add unit tests.
4. **Route Layer (`packages/api`)**: Create Fastify route plugin. Add route tests.
5. **Web API Client (`packages/web`)**: Implement frontend service. Add unit tests.
6. **Web UI (`packages/web`)**: Create Next.js pages/components (Server Components by default). Add component tests.
**Mandatory Verification**: Every task must end with `npm run build`, `npm run test:cov`, and `npm run lint` all passing with the defined coverage thresholds.
## Code Organization Rules
### API (NestJS)

View file

@ -181,6 +181,8 @@ interface PaginatedResponse<T> {
- `ImportProductsSchema` — array of `CreateProductSchema` (for JSON import)
- All schemas imported from `'zod/v4'`; enums use `z.enum(Object.values(...))`.
**Status**: DONE (types, enums, validation schemas all exist with tests)
### 5.2 — Mongoose Schema & Repository
- `packages/api/src/modules/products/schemas/product.schema.ts` — Mongoose schema with `timestamps: true`, `deletedAt` index, partial unique index on `(householdId, barcode)`.
@ -195,7 +197,35 @@ interface PaginatedResponse<T> {
- `bulkCreate(householdId, items[])` — uses `insertMany` with `ordered: false`
- All read queries use `.lean().exec()`.
### 5.3 — Barcode Lookup Service
**Status**: DONE (repository exists with tests)
### 5.3 — Products Service
- `ProductsService` with business logic:
- Dedup check on create (same name + brand within household)
- Barcode collision check (409 ConflictError)
- Soft-delete with `deletedAt` timestamp
- Coordinate barcode lookup (call BarcodeService)
- Coordinate import (validate + bulkCreate)
- Smart-add (call LLM provider)
**Status**: NOT STARTED
### 5.4 — Products Routes
- Route plugin registered via `fp()`:
- `GET /products` — list/search (paginated)
- `GET /products/:id` — get single
- `POST /products` — create
- `PATCH /products/:id` — update
- `DELETE /products/:id` — soft-delete (admin)
- `GET /products/barcode/:code` — barcode lookup
- `POST /products/import` — bulk import
- `POST /products/smart-add` — LLM placeholder
**Status**: NOT STARTED
### 5.5 — Barcode Lookup Service
- `BarcodeService`:
- First check local DB for matching barcode (per household)
@ -208,7 +238,9 @@ interface PaginatedResponse<T> {
- Failures (network, 404, malformed): return `{ found: false }`; do not throw
- Outbound HTTP via `undici` with a 5s timeout and a configurable User-Agent (`MeshiTrack/<version> (+self-hosted)`)
### 5.4 — LLM Provider Interface
**Status**: NOT STARTED
### 5.6 — LLM Provider Interface
- `packages/api/src/modules/llm/interfaces/llm-provider.interface.ts`:
@ -231,14 +263,18 @@ export const LLM_PROVIDER = Symbol('LLM_PROVIDER');
- `NoOpLlmProvider`: implements interface, returns `null` for all methods, logs a warning
- `LlmModule`: provides `LLM_PROVIDER` via factory, selectable by env var `LLM_PROVIDER_TYPE`
### 5.5 — Smart Add Endpoint
**Status**: DONE (interface + NoOp provider exist with tests)
### 5.7 — Smart Add Endpoint
- `POST /products/smart-add` accepts `{ text?: string, image?: file }`
- Calls `ILlmProvider.extractNutrition()`
- If LLM returns data, pre-fill a product and return to client for review (not auto-saved)
- If LLM unavailable (`NoOpLlmProvider`), return `{ available: false, message: 'LLM not configured' }`
### 5.6 — Import Endpoint
**Status**: NOT STARTED (blocked on 5.3/5.4)
### 5.8 — Import Endpoint
- `POST /products/import` accepts multipart CSV or JSON file (max 5 MB, 5000 rows)
- Validate each row against `CreateProductSchema`; reject rows with imperial `servingUnit` values with a clear error message
@ -248,7 +284,16 @@ export const LLM_PROVIDER = Symbol('LLM_PROVIDER');
- `tags` is a `;`-separated list
- `servingUnit``{g, ml, piece, slice}`
### 5.7 — Web UI: Product Library
**Status**: NOT STARTED (blocked on 5.3/5.4)
### 5.9 — Web: API Client Service
- `packages/web/src/services/products.ts` — fetch functions for all product endpoints
- Unit tests in `packages/web/src/services/__tests__/products.test.ts`
**Status**: NOT STARTED
### 5.10 — Web UI: Product Library
- `/products` page (Server Component for initial fetch; client island for filters):
- Search bar with debounced full-text search (300ms)
@ -263,6 +308,15 @@ export const LLM_PROVIDER = Symbol('LLM_PROVIDER');
- Barcode field with "Lookup" button (calls `/products/barcode/:code`)
- "Smart Add" tab (text input or image upload)
- Import dialog: file upload with preview, row count, and error display
- Component tests for page and interactive components
**Status**: NOT STARTED
### 5.11 — CI Verification
- `npm run build` passes
- `npm run test:cov` passes (100% lines/functions/statements, 90% branches)
- `npm run lint` passes
---
@ -276,10 +330,12 @@ export const LLM_PROVIDER = Symbol('LLM_PROVIDER');
- [ ] Barcode collisions within a household return 409
- [ ] Bulk import processes a CSV with 100+ products and reports per-row errors
- [ ] Web UI allows searching, filtering, adding, and editing products
- [ ] Web UI has component tests for all pages and interactive components
- [ ] `ILlmProvider` interface is defined and injectable
- [ ] Smart Add endpoint returns graceful `{ available: false }` with the NoOp provider
- [ ] All product queries are scoped to `householdId`
- [ ] Unit + integration tests meet coverage targets (100% lines/functions/statements, 90% branches)
- [ ] `npm run build`, `npm run test:cov`, and `npm run lint` all pass
---

View file

@ -6,264 +6,99 @@
---
## Deliverables
## Implementation Workflow (Vertical Slice)
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
Following the project's standard workflow, Phase 8 will be implemented in the following order:
### 8.1 — Shared Layer: Types & Validation
- **Types**: Define `MealPlan` and `NutritionTarget` in `packages/shared/src/types/`.
- **Enums**: Define `MealType` and `MealPlanStatus` in `packages/shared/src/enums/`.
- **Validation**: Create Zod schemas in `packages/shared/src/validation/`:
- `CreateMealPlanSchema`: Ensures 7 days, valid `weekStartDate`.
- `UpdateMealPlanSchema`: Supports granular updates (add/move/remove meal).
- `NutritionTargetSchema`: Validates daily macros and calorie goals.
- **Unit Tests**: Test Zod schemas for all edge cases (e.g., negative calories, overlapping weeks).
### 8.2 — Database Layer: Repositories
- **MealPlanRepository**:
- `findByHousehold(householdId, weekStartDate)`: Unique constraint per household per week.
- `findCurrent(householdId)`: Returns the plan for the current calendar week.
- `updateMeal(id, dayIndex, mealId, data)`: Granular update for a specific meal slot.
- **NutritionTargetRepository**:
- `findByUser(userId, householdId)`: Get active targets for a specific user.
- `upsert(userId, householdId, data)`: Update or create targets.
- **Integration Tests**: Verify MongoDB unique indexes and household scoping.
### 8.3 — Service Layer: Suggestion Engine & Planning
- **MealPlanService**:
- Manage CRUD operations and status transitions (Draft -> Active).
- Compute `dailyNutritionTotal` dynamically using the `NutritionCalculatorService` from Phase 6.
- **NutritionTargetService**:
- Manage user-specific nutritional goals and presets (Maintenance, Loss, Gain).
- **SuggestionEngineService** (Core Algorithm):
- **Scoring Algorithm**:
- `IngredientCoverage` (40%): Matches recipe ingredients vs current pantry items.
- `FreshnessUrgency` (30%): Bonus for using items with `urgency: urgent` or `expiringSoon`.
- `NutritionBalance` (20%): Complements existing meals in the day vs user targets.
- `Variety` (10%): Penalizes recipes used in the last 14 days.
- Uses `RecipesService` to fetch catalog and `PantryService` to fetch current inventory.
- **ShoppingGapService**:
- Analyze meal plans vs pantry to identify missing ingredients for Phase 9 integration.
- **Unit Tests**: Test scoring weights and ingredient matching logic (100% coverage required).
### 8.4 — Route Layer: API Endpoints
- **MealPlanRoutes**:
- `GET /api/v1/households/:householdId/meal-plans`: List plans.
- `GET /api/v1/households/:householdId/meal-plans/current`: Get current week.
- `POST /api/v1/households/:householdId/meal-plans`: Create plan.
- `PATCH /api/v1/households/:householdId/meal-plans/:id`: Update structure.
- `GET /api/v1/households/:householdId/meal-plans/suggestions`: Get algorithmic suggestions.
- **NutritionTargetRoutes**:
- `GET /api/v1/households/:householdId/nutrition-targets`: Get user targets.
- `POST /api/v1/households/:householdId/nutrition-targets`: Set targets.
- **Integration Tests**: Verify all endpoints return correctly shaped responses (using `toProductResponse` style mappers).
### 8.5 — Web Layer: API Client & Components
- **API Client**: Implement `meal-plans.ts` and `nutrition-targets.ts` in `packages/web/src/services/`.
- **Hooks**: Create `useMealPlan` and `useSuggestions` hooks for state management.
- **Components**:
- `CalendarGrid`: Weekly view using CSS Grid and drag-and-drop.
- `NutritionProgress`: Daily progress bars vs targets.
- `SuggestionPanel`: Sidebar showing ranked recipes with "reasoning" tooltips.
- `ShoppingGapView`: Breakdown of missing items.
- **Component Tests**: Use React Testing Library to verify drag-and-drop and progress bar calculations.
### 8.6 — Verification Gate
- `npm run build`
- `npm run test:cov` (100% API/Shared, 90% Web thresholds)
- `npm run lint`
---
## Data Model
### MealPlan Schema
## Technical Details
### Scoring Algorithm Constants
```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 (MondaySunday)
- 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<RecipeSuggestion[]>;
}
```
**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,
const WEIGHTS = {
COVERAGE: 0.4, // Do I have the items?
URGENCY: 0.3, // Should I use these items now?
NUTRITION: 0.2, // Does it fit my macros?
VARIETY: 0.1 // Have I eaten this too much?
};
```
### 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 (MonSun) × 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
### Integration Points
- **PantryService**: Fetch items where `status` is `sealed` or `opened`.
- **RecipesService**: Fetch recipes and their computed nutrition/ingredients.
- **NutritionCalculator**: Reuse for summing daily plan totals.
---
## 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.
- [ ] Weekly meal plans can be created and managed via a calendar UI.
- [ ] Drag-and-drop allows moving recipes between days and meal types.
- [ ] The suggestion engine prioritizes recipes using urgent pantry items.
- [ ] Daily nutrition bars reflect progress against user-defined macro targets.
- [ ] Shopping gap analysis identifies missing ingredients for the planned week.
- [ ] Full unit and integration test coverage as per project standards.
- [ ] Build, Lint, and Coverage checks all pass.

View file

@ -315,7 +315,14 @@ interface StoreComparisonResult {
- Match items to products (fuzzy), match store to stores
- With `NoOpLlmProvider`: returns `{ available: false }`
### 9.10 — Web UI: Grocery Management
### 9.10 — Web: API Client Services
- `packages/web/src/services/shopping-lists.ts` — fetch functions for shopping list endpoints
- `packages/web/src/services/prices.ts` — fetch functions for price record endpoints
- Unit tests in `packages/web/src/services/__tests__/shopping-lists.test.ts`
- Unit tests in `packages/web/src/services/__tests__/prices.test.ts`
### 9.11 — Web UI: Grocery Management
- `/shopping-lists` page:
- Active lists at top, completed/archived below
@ -326,16 +333,23 @@ interface StoreComparisonResult {
- Each item: checkbox, name, quantity, estimated price
- Check off: expand to enter actual price (optional)
- Real-time sync indicator ("2 members shopping")
- "Done Shopping" button prompts "Add items to pantry?"
- "Done Shopping" button prompts "Add items to pantry?"
- `/stores` page:
- Store list with CRUD
- Store list with CRUD (already exists from Phase 4, extend if needed)
- Per-store: total spent, last visit, product count
- `/prices` page (analytics):
- Product search price history line chart (per store, color-coded)
- Product search price history line chart (per store, color-coded)
- Store comparison table
- Spending over time bar chart
- Price alerts panel
- **Shopping list widget on dashboard**: shows active lists with quick-check functionality
- Component tests for all pages and interactive components
### 9.12 — CI Verification
- `npm run build` passes
- `npm run test:cov` passes (100% lines/functions/statements, 90% branches)
- `npm run lint` passes
---
@ -349,6 +363,8 @@ interface StoreComparisonResult {
- [ ] Price analytics show spending trends and alerts
- [ ] Auto-generated lists from meal plans correctly reflect shopping gap
- [ ] Receipt parsing endpoint delegates to LLM provider
- [ ] Web UI has component tests for all pages and interactive components
- [ ] `npm run build`, `npm run test:cov`, and `npm run lint` all pass
---