Commit Graph

4 Commits

Author SHA1 Message Date
Mike Kell 747d917aed feat(pantry): complete Phase 5 pantry delta engine and auto-shopping generator
Domain (pure Dart):
- Add PantryItem and ShoppingListItem entities plus the PantryCategory aisle
  enum (produce/meat_seafood/dairy/dry_goods/spices/other) with a keyword
  infer() heuristic and ExpirationStatus warning buckets (expired /
  expiringSoon within 3 days)
- Add measurement_units value object (mass/volume/count dimensions with
  lb/oz/g/kg and ml/l/tsp/tbsp/cup conversion onto a shared base scale) and
  ingredient_name canonicalizer (lowercase, parenthetical stripping,
  plural folding: eggs -> egg, lbs -> lb)
- Define IPantryRepository contract covering inventory and checklist CRUD
- Add CalculateShoppingDelta use case: aggregates the active 7-day meal-plan
  slot window's recipe ingredient manifests by canonical name, computes
  deficit = totalRequired - currentPantryQuantity in base units (dominant
  recipe unit wins display, 2-decimal rounding), upserts isAutoGenerated
  rows with stable ids and source recipe titles, clears stale auto rows once
  stock covers the plan, and never modifies or removes manual rows

Data:
- Bump AppDatabase to schema v4 (migration for v1-v3 installs) with
  PantryItemsTable (UUID PK, canonical name, quantity/unit, aisle key,
  nullable expirationDate, lastUpdated) and ShoppingListItemsTable (UUID PK,
  required quantity/unit, purchased/auto flags, JSON sourceNotes for recipe
  titles) plus watch/get/upsert/delete helpers
- Implement DriftPantryRepository (record <-> entity mapping, enum keys,
  JSON recipe-title round-trip)

Presentation:
- Add PantryInventoryController (live watch stream, quick +/- stock steps
  clamped at zero, aisle-grouping vs expiration-sort modes) and
  ShoppingListController (manual add, All/To Buy/Checked filter, check-off
  with "Add to Pantry?" unit-converting stock merge or aisle-inferred row
  creation, syncWithMealPlan reading the planner's Monday-Sunday window and
  recipe catalog)
- Refactor PantryScreen: Inventory/Shopping tab header with remaining-count
  badge, Aisles/Expiration segmented control, categorized tiles with +/-
  counters and expired/expiring badges, swipe-to-delete on both lists,
  prominent "Sync with Meal Plan" banner with run summary + snackbar,
  "From: <recipe>" origin chips on auto rows, checklist filters, check-off
  -> pantry prompt, and Add Pantry Item / Add Shopping Item dialogs

Testing:
- 10 new tests in delta_calculator_test.dart: 300 g on hand vs 800 g
  requirement -> +500 g entry, sufficient stock -> no entry, manual rows
  preserved across repeated syncs, stale auto cleanup, cross-recipe
  plural/unit normalization (4 eggs + 2 egg - 2 -> +4 egg), lb/oz scale
  unification (0.75 lb), check-off -> pantry transfer (merge +
  aisle-inferred create), end-to-end syncWithMealPlan, and inventory
  canonicalization / quick-adjust clamp; full suite green (99/99)
- flutter analyze clean (No issues found!) after codegen; BUILD_STATE.md:
  mark Phase 5 complete and record all verification gates
2026-10-16 10:33:14 -04:00
Mike Kell bdee2de35f feat(meal_planner): complete Phase 4 recipe scraper and meal planning calendar
Domain (pure Dart):
- Add Recipe, MacroProfile, and RecipeIngredient entities (quantity/unit/
  item parsing: decimals, ASCII/unicode/mixed fractions, known-unit
  vocabulary) plus MealPlanSlot reusing the nutrition MealType enum
- Define IRecipeRepository and IMealPlanRepository contracts
- Add ScrapeRecipeFromUrl use case: package:http download with injectable
  fetcher, package:html DOM parse, JSON-LD @graph/root/array/list-@type
  navigation, HowToStep/HowToSection flattening, ISO-8601 prep/cook times,
  recipeYield, regex nutrition parsing, OpenGraph fallback, and typed
  RecipeScrapeException
- Add core/utils/uuid.dart RFC-4122 v4 generator (no new dependency)

Data:
- Bump AppDatabase to schema v3 (migration for v1/v2 installs) with
  RecipesTable (UUID PK, JSON ingredient/instruction columns, macros) and
  MealPlanSlotsTable (UUID PK, nullable recipeId FK ON DELETE SET NULL)
- Implement DriftRecipeRepository (URL-deduplicated importRecipe, createdAt
  preserved on re-import) and DriftMealPlanRepository (half-open [from, to)
  window, upsert by id)

Presentation:
- Add RecipeImportController (URL submit -> loading -> preview modal state,
  live catalog watch stream, confirm-save/dismiss/delete) and
  MealPlanCalendarController (Monday-Sunday window navigation, slot
  assign/upsert/clear, prepared toggles, per-day protein rollup against the
  100 g target, deleted-recipe slot normalization)
- Rework MealPlannerScreen: URL import bar with spinner, RecipePreviewDialog
  (macros, ingredients, steps, save with error surface), draggable
  SavedRecipesTray with protein badges, responsive WeeklyCalendarView
  (>=900 dp: 7-column grid, below: tabbed pager) with DragTarget slots and
  daily protein indicators, AssignSlotDialog quick-assign modal

Fixes:
- Eliminate desktop resize overflow (RenderFlex hatched stripes): wide-mode
  day columns scroll vertically, and a LayoutBuilder collapses the page
  into a single scrollable column with a fixed-height calendar when the
  window body is shorter than ~520 dp

Testing:
- 29 new tests: recipe scraper (JSON-LD variants, macro/ingredient/
  instruction/duration parsing, OG fallback, execute error mapping),
  meal-plan repositories (window filter, upsert, dedup), calendar
  controller (rollup, navigation, prepared/custom entries), and screen
  widget tests pumping short-compact/short-wide/tall/extremely-short
  window sizes to prove no overflow; full suite green (89/89)
- BUILD_STATE.md: mark Phase 4 complete and record all verification gates
2026-10-16 09:07:41 -04:00
Mike Kell 094cf08f25 feat(nutrition,health_sync): complete Phase 3 nutrition tracking and HealthGateway sync
- Domain: FoodLogEntry/MealType, HydrationLogEntry, BiometricEntry/BiometricSource entities plus INutritionRepository and IHealthSyncRepository contracts (pure Dart).

- Data: AppDatabase schema v2 (FoodLogs/HydrationLogs/BiometricLogs tables, v1 migration, range-filtered CRUD) with drift-backed NutritionRepository and HealthSyncRepository; startOfDay/endOfDay date utils.

- Platform: HealthGateway conditional-import factory - mobile impl wires package:health (WEIGHT kg->lb, ACTIVE_ENERGY_BURNED J->kcal, permissions, write-back) with guarded fallbacks; web/desktop impl never touches platform channels (manual entry + drift cache).

- Presentation: NutritionController and BiometricController AsyncNotifiers (100g protein / 96oz water targets, meal grouping, 7/30-day weight deltas, idempotent 90-day native sync with per-day active-energy aggregation); LogFoodDialog and LogWeightDialog.

- UI: NutritionScreen progress meters, +8/+16/+24oz quick-add, meal-grouped food diary, weight tracker card with Log Weight / Sync (native sync on Android/iOS, manual dialog on Windows/Web) and status snackbars.

- Tests: 23 new tests (in-memory drift repos, controller fakes, fake health gateway covering fallback, permission denial, and duplicate-free re-sync); 60/60 passing, flutter analyze clean, flutter build web succeeds. BUILD_STATE.md updated to Phase 4.
2026-10-16 07:02:28 -04:00
Mike Kell 7b6b646d2c feat(auth): complete Phase 1 passwordless identity and encrypted DB foundation 2026-10-16 05:01:54 -04:00