# SAGE - COMPLETE PROJECT DOCUMENTATION ## Smart Kitchen Management System ๐ŸŒฟ **Version:** 1.0.0 **Created:** October 3, 2025 **Platform:** Flutter (Android & iOS) **Status:** Active Development ๐Ÿ”ฅ --- ## ๐Ÿ“– TABLE OF CONTENTS 1. [Project Overview](#project-overview) 2. [Core Features](#core-features) 3. [Technical Architecture](#technical-architecture) 4. [Data Models](#data-models) 5. [API Integrations](#api-integrations) 6. [User Flows](#user-flows) 7. [Notification System](#notification-system) 8. [Multi-User & Sync](#multi-user--sync) 9. [Security & Privacy](#security--privacy) 10. [Testing Strategy](#testing-strategy) 11. [Deployment](#deployment) --- ## ๐Ÿ“ฑ PROJECT OVERVIEW ### The Problem - People forget what's in their fridge/pantry - Food expires and gets wasted โ†’ money wasted - Constant grocery store trips forgetting items - End up ordering takeout when food at home expires - No organization = kitchen chaos ### The Solution: SAGE A comprehensive kitchen management app that: 1. **Tracks** all your food inventory with expiration dates 2. **Alerts** you before food expires (with Discord integration!) 3. **Manages** your recipes and connects them to inventory 4. **Generates** smart shopping lists 5. **Shares** with household members 6. **Works offline-first** with optional cloud sync ### Why "Sage"? - ๐ŸŒฟ The herb (kitchen theme) - ๐Ÿง  Wisdom and smart decisions - ๐Ÿ’š Simple, clean, memorable ### Target Users - Busy individuals who want to save money - Families managing shared kitchens - Anyone tired of wasting food - People who hate grocery shopping inefficiency - Budget-conscious cooks --- ## ๐ŸŽฏ CORE FEATURES ### 1. INVENTORY TRACKER ๐Ÿ“ฆ **The Foundation:** Track everything in your kitchen **Key Capabilities:** - Add items via barcode scan OR manual entry - Auto-populate from Open Food Facts (2M+ products!) - Track quantity, expiration date, location - Organize by location (fridge, freezer, pantry, etc.) - Categorize automatically or manually - Attach photos (cached locally) - Search and filter inventory - Quick edit/delete items **Barcode Scanning:** ``` User scans barcode โ†“ Query Open Food Facts API โ†“ Product found? โ”œโ”€ YES โ†’ Auto-fill: name, photo, category, typical shelf life โ”‚ User adds: quantity, actual expiration, location โ”‚ โ†’ Save to local database โ”‚ โ””โ”€ NO โ†’ Manual entry form User fills everything โ†’ Save to local database ``` **Data Tracked Per Item:** - Name - Barcode (if scanned) - Quantity & unit - Purchase date - Expiration date - Location (fridge/freezer/pantry/other) - Category (auto or manual) - Photo URL (cached) - Notes (optional) - Last modified timestamp - Sync status --- ### 2. SMART ALERTS ๐Ÿ”” **The Brain:** Never let food go to waste **Alert Timeline:** | Time Before Expiration | Alert Type | Message | |------------------------|------------|---------| | 1 month | FYI notification | "Ranch dressing expires in 30 days" | | 2 weeks | Heads up | "Sour cream expires in 14 days" | | 1 week | Use soon! | "Milk expires in 7 days - Tap for recipes" | | Before next shopping day | Shopping alert | "3 items expire before next week!" | **Shopping Day Intelligence:** ``` User sets shopping frequency: - Weekly (every Tuesday) - Bi-weekly (every other Monday) - Monthly (first Saturday) - Pay cycle (15th and 30th) - Custom days System calculates next shopping day โ†“ For each item expiring before next shopping day: โ†’ Send notification โ†’ Add to "Add to Shopping List" quick action โ†’ Send to Discord channel (if enabled) ``` **Notification Types:** 1. **Local Push Notifications** - Scheduled in advance - Tappable (opens relevant screen) - Customizable (can disable certain types) 2. **Discord Webhooks** - Critical alerts only (shopping day, urgent expirations) - Formatted with emojis and priority - @everyone tag for household **Example Discord Message:** ```markdown ๐ŸŒฟ **Sage Alert** ๐ŸŒฟ @everyone Shopping day is tomorrow! **Expiring before next week:** ๐Ÿ”ด Ranch dressing (expires Oct 10) - 3 days ๐Ÿ”ด Croutons (expires Oct 12) - 5 days ๐ŸŸก Milk (expires Oct 15) - 8 days **Suggested Actions:** โ€ข Add to shopping list โ€ข Check "Use It Up" recipes โ€ข Update quantities if already replaced Don't forget to restock! ๐Ÿ›’ ``` --- ### 3. RECIPE BOOK ๐Ÿ“– **The Inspiration:** Never wonder "what's for dinner?" again **Core Functions:** - Add recipes manually or copy/paste from websites - Store ingredients with quantities - Write/paste instructions - Add optional photos - Tag recipes (Quick, Vegetarian, Fancy, etc.) - Share recipes with community (optional) - Mark favorites **Smart Features:** - **"What Can I Make?"** - Shows recipes you can make with current inventory - Displays % of ingredients you have - Sorts by completeness - Shows what's missing - **"Use It Up!"** - Suggests recipes using expiring items - Prioritizes items expiring soonest - Helps prevent waste **Recipe Data Structure:** ```dart Recipe { name: "Caesar Salad" ingredients: [ {name: "Romaine lettuce", quantity: 1, unit: "head"}, {name: "Caesar dressing", quantity: 0.5, unit: "cup"}, {name: "Croutons", quantity: 1, unit: "cup"}, {name: "Parmesan", quantity: 0.25, unit: "cup", optional: true} ] instructions: "Chop lettuce. Toss with dressing. Top with croutons and parmesan." prepTime: 10 minutes servings: 2 tags: ["Quick", "Salad", "Vegetarian"] photo: "url_or_local_path" isPublic: true // Share with community createdBy: user_id } ``` **Copy/Paste from Websites:** User pastes URL or text block โ†’ Parser extracts: - Recipe title - Ingredient list (with quantities) - Instructions - Optional: photo, prep time, servings Common formats supported: - Recipe schema (schema.org/Recipe) - Plain text with smart parsing - Popular recipe sites (AllRecipes, Food Network, etc.) --- ### 4. SHOPPING LISTS ๐Ÿ›’ **The Planner:** Efficient grocery trips, every time **Key Features:** - **Multiple Lists** - Separate lists for different stores - "Costco" - "Trader Joe's" - "Farmers Market" - Custom names - **Smart List Building** - Add items manually - One-click add from recipes - Suggested items based on expiring inventory - **While Shopping** - Check off items as you shop - Uncheck if needed - Sort by store section (optional) - Quick quantity adjust - **After Shopping** - "Add All to Inventory" quick action - Batch-add checked items - Auto-populate likely expiration dates - Clear completed items **Sharing:** - Share lists with household members - Real-time updates (if cloud sync enabled) - See who added what - Collaborative shopping **List Organization:** ``` Shopping List: "Costco" Items: โ˜ Milk (2 gallons) - Added from: Ranch expires alert โ˜‘ Chicken breast (2 lbs) - Added from: Recipe "Chicken Tacos" โ˜ Romaine lettuce (2 heads) - Added manually โ˜ Croutons (1 bag) - Added from: Recipe "Caesar Salad" Quick Actions: - Add recipe ingredients - Add expiring items - Share list - Sort by aisle ``` --- ### 5. MULTI-USER & SYNC โ˜๏ธ **The Connector:** Share with your household **Three Sync Modes (User's Choice):** #### Mode 1: Cloud Sync (Supabase) โ˜๏ธ **Best for:** Most users, easy setup, reliable **Features:** - Real-time sync across devices - User authentication - Household groups (shared inventory/lists) - Recipe community sharing - Automatic backups **Technical:** - PostgreSQL database (Supabase) - Row-level security - Real-time subscriptions - Free tier: 500MB DB, 2GB bandwidth/month **User Flow:** ``` Sign up with email โ†“ Create or join household โ†“ All devices sync automatically โ†“ Changes propagate in real-time ``` #### Mode 2: Completely Free (Self-Hosted) ๐Ÿ  **Best for:** Privacy-conscious users, no cloud dependency **Options:** **A. Export/Import** - Export inventory/recipes to JSON - Share file via any method (email, cloud storage, etc.) - Import on other device - Manual sync process **B. Local WiFi Sync** - Devices on same network - Peer-to-peer sync (no internet needed!) - Automatic discovery - One device = "host" **C. Self-Host Backend** - Docker Compose setup (we provide!) - Run on Raspberry Pi or home server - Full control of data - Zero recurring costs **Self-Hosted Setup:** ```bash # We provide this in docs! git clone https://github.com/sage-app/backend cd backend docker-compose up -d # Configure app to point to your server # http://192.168.1.100:3000 ``` #### Mode 3: Local Only ๐Ÿ“ฑ **Best for:** Solo users, maximum privacy, no sharing needed **Features:** - Everything on device - Zero external dependencies - Export for backup - Fast and simple **Offline-First Philosophy (ALL MODES):** ``` Every action saves locally FIRST โ†“ If online & sync enabled: Queue for sync โ†“ Sync when possible โ†“ Update local cache App works PERFECTLY offline Never wait for network Graceful sync when available ``` **Conflict Resolution:** - Last-write-wins (timestamp-based) - Show notification if conflict detected - User can view both versions and choose --- ## ๐Ÿ—๏ธ TECHNICAL ARCHITECTURE ### Tech Stack Overview ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ FLUTTER APP (Dart) โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ UI Layer (Widgets) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ State Management (Riverpod) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Business Logic Layer โ”‚ โ”‚ โ”‚ โ”‚ - Controllers โ”‚ โ”‚ โ”‚ โ”‚ - Services โ”‚ โ”‚ โ”‚ โ”‚ - Repositories โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Data Layer โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Isar โ”‚ โ”‚ Supabase โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (Local) โ”‚ โ”‚ (Cloud) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ External APIs โ”‚ โ”‚ - Open Food Facts โ”‚ โ”‚ - Discord Webhook โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### Key Technologies | Component | Technology | Purpose | |-----------|-----------|---------| | Framework | Flutter 3.x | Cross-platform UI | | Language | Dart 3.x | Type-safe, fast, AOT compiled | | State Management | Riverpod 2.x | Reactive state, dependency injection | | Local Database | Isar 3.x | Fast NoSQL, offline-first | | Cloud Database | Supabase | PostgreSQL, real-time, auth | | Barcode Scanner | mobile_scanner | Camera-based scanning | | Notifications | flutter_local_notifications | Scheduled alerts | | Image Caching | cached_network_image | Performance optimization | | HTTP Client | http | API requests | ### Project Structure ``` sage/ โ”œโ”€โ”€ lib/ โ”‚ โ”œโ”€โ”€ main.dart # App entry point โ”‚ โ”œโ”€โ”€ app.dart # App widget, theme, routing โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ core/ # Core utilities โ”‚ โ”‚ โ”œโ”€โ”€ constants/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ app_constants.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ api_constants.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ colors.dart โ”‚ โ”‚ โ”œโ”€โ”€ utils/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ date_utils.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ validators.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ formatters.dart โ”‚ โ”‚ โ””โ”€โ”€ extensions/ โ”‚ โ”‚ โ”œโ”€โ”€ date_extensions.dart โ”‚ โ”‚ โ””โ”€โ”€ string_extensions.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ features/ # Feature modules โ”‚ โ”‚ โ”œโ”€โ”€ inventory/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ models/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ food_item.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ inventory_repository.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ controllers/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ inventory_controller.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ screens/ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ inventory_screen.dart โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ add_item_screen.dart โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ item_detail_screen.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ widgets/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ inventory_list.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ expiration_badge.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ barcode_scanner.dart โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ recipes/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ models/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ recipe.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ recipe_repository.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ controllers/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ recipe_controller.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ screens/ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ recipes_screen.dart โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ add_recipe_screen.dart โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ recipe_detail_screen.dart โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ what_can_i_make_screen.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ widgets/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ recipe_card.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ingredient_list.dart โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ shopping/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ models/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ shopping_list.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ shopping_repository.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ controllers/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ shopping_controller.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ screens/ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ shopping_lists_screen.dart โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ list_detail_screen.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ widgets/ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ shopping_item.dart โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ home/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ screens/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ home_screen.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ widgets/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ expiring_soon_carousel.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ quick_stats.dart โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ settings/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ models/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ user_settings.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ settings_repository.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ controllers/ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ settings_controller.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ screens/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ settings_screen.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ notifications_settings.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ sync_settings.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ discord_settings.dart โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ auth/ โ”‚ โ”‚ โ”œโ”€โ”€ controllers/ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ auth_controller.dart โ”‚ โ”‚ โ””โ”€โ”€ screens/ โ”‚ โ”‚ โ”œโ”€โ”€ login_screen.dart โ”‚ โ”‚ โ””โ”€โ”€ signup_screen.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ services/ # Business logic services โ”‚ โ”‚ โ”œโ”€โ”€ notification_service.dart โ”‚ โ”‚ โ”œโ”€โ”€ sync_service.dart โ”‚ โ”‚ โ”œโ”€โ”€ barcode_service.dart โ”‚ โ”‚ โ”œโ”€โ”€ discord_service.dart โ”‚ โ”‚ โ”œโ”€โ”€ openfoodfacts_service.dart โ”‚ โ”‚ โ””โ”€โ”€ expiration_tracker_service.dart โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ data/ # Data layer โ”‚ โ”‚ โ”œโ”€โ”€ local/ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ isar_database.dart โ”‚ โ”‚ โ””โ”€โ”€ remote/ โ”‚ โ”‚ โ””โ”€โ”€ supabase_client.dart โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ shared/ # Shared widgets & components โ”‚ โ”œโ”€โ”€ widgets/ โ”‚ โ”‚ โ”œโ”€โ”€ custom_button.dart โ”‚ โ”‚ โ”œโ”€โ”€ custom_text_field.dart โ”‚ โ”‚ โ””โ”€โ”€ loading_indicator.dart โ”‚ โ””โ”€โ”€ navigation/ โ”‚ โ””โ”€โ”€ app_router.dart โ”‚ โ”œโ”€โ”€ assets/ # Static assets โ”‚ โ”œโ”€โ”€ images/ โ”‚ โ”œโ”€โ”€ icons/ โ”‚ โ””โ”€โ”€ fonts/ โ”‚ โ”œโ”€โ”€ test/ # Tests โ”‚ โ”œโ”€โ”€ unit/ โ”‚ โ”œโ”€โ”€ widget/ โ”‚ โ””โ”€โ”€ integration/ โ”‚ โ”œโ”€โ”€ android/ # Android-specific โ”œโ”€โ”€ ios/ # iOS-specific โ”œโ”€โ”€ pubspec.yaml # Dependencies โ””โ”€โ”€ README.md ``` --- ## ๐Ÿ’พ DATA MODELS (DETAILED) ### FoodItem Model ```dart import 'package:isar/isar.dart'; part 'food_item.g.dart'; @collection class FoodItem { Id id = Isar.autoIncrement; // Basic Info late String name; String? barcode; late int quantity; String? unit; // "bottles", "lbs", "oz", "items" // Dates late DateTime purchaseDate; late DateTime expirationDate; // Organization @enumerated late Location location; String? category; // Auto from barcode or manual // Media & Notes String? photoUrl; // Cached from API or user uploaded String? notes; // Multi-user support String? userId; String? householdId; // Sync tracking DateTime? lastModified; bool syncedToCloud = false; // Computed properties (not stored) @ignore int get daysUntilExpiration { return expirationDate.difference(DateTime.now()).inDays; } @ignore ExpirationStatus get expirationStatus { final days = daysUntilExpiration; if (days < 0) return ExpirationStatus.expired; if (days <= 3) return ExpirationStatus.critical; if (days <= 7) return ExpirationStatus.warning; if (days <= 14) return ExpirationStatus.caution; return ExpirationStatus.fresh; } } enum Location { fridge, freezer, pantry, spiceRack, countertop, other } enum ExpirationStatus { fresh, // > 14 days caution, // 8-14 days warning, // 4-7 days critical, // 1-3 days expired // 0 or negative days } ``` ### Recipe Model ```dart import 'package:isar/isar.dart'; part 'recipe.g.dart'; @collection class Recipe { Id id = Isar.autoIncrement; // Basic Info late String name; late String instructions; // Ingredients (embedded objects) late List ingredients; // Media String? photoUrl; // Metadata List tags = []; int? prepTimeMinutes; int? cookTimeMinutes; int? servings; @enumerated DifficultyLevel? difficulty; // Sharing String? createdBy; // user_id bool isPublic = false; bool isFavorite = false; // Timestamps DateTime? created; DateTime? lastModified; // Sync bool syncedToCloud = false; // Computed @ignore int get totalTimeMinutes { return (prepTimeMinutes ?? 0) + (cookTimeMinutes ?? 0); } } @embedded class Ingredient { late String name; double? quantity; String? unit; bool optional = false; // For matching against inventory String? matchedItemId; // Link to FoodItem } enum DifficultyLevel { easy, medium, hard } ``` ### ShoppingList Model ```dart import 'package:isar/isar.dart'; part 'shopping_list.g.dart'; @collection class ShoppingList { Id id = Isar.autoIncrement; late String name; // "Costco", "Trader Joe's" List items = []; // Sharing String? householdId; List sharedWith = []; // Metadata DateTime? created; DateTime? lastModified; // Sync bool syncedToCloud = false; // Computed @ignore int get totalItems => items.length; @ignore int get checkedItems => items.where((i) => i.checked).length; @ignore double get completionPercentage { if (totalItems == 0) return 0; return (checkedItems / totalItems) * 100; } } @embedded class ShoppingItem { late String name; double? quantity; String? unit; bool checked = false; // Metadata @enumerated Priority priority = Priority.normal; String? addedFrom; // "recipe:123", "inventory:456", "manual" String? addedBy; // user_id DateTime? addedAt; String? notes; } enum Priority { low, normal, high } ``` ### UserSettings Model ```dart import 'package:isar/isar.dart'; part 'user_settings.g.dart'; @collection class UserSettings { Id id = Isar.autoIncrement; // Shopping Schedule @enumerated ShoppingFrequency frequency = ShoppingFrequency.weekly; // For weekly/biweekly int? dayOfWeek; // 1-7 (Monday-Sunday) // For monthly int? dayOfMonth; // 1-31 // For pay cycle List? payCycleDays; // [15, 30] // For custom List? customDays; // [1, 4, 6] = Mon, Thu, Sat // Calculated DateTime? nextShoppingDay; // Notifications bool enableNotifications = true; bool notify1Month = true; bool notify2Weeks = true; bool notify1Week = true; bool notifyShoppingDay = true; // Discord bool enableDiscord = false; String? discordWebhookUrl; bool discordCriticalOnly = true; // Sync @enumerated SyncMode syncMode = SyncMode.localOnly; String? supabaseUserId; String? householdId; // Display bool showPhotos = true; bool showExpirationBadges = true; @enumerated ThemeMode themeMode = ThemeMode.system; @enumerated SortOrder inventorySortOrder = SortOrder.expirationDate; } enum ShoppingFrequency { weekly, // Every X day of week biweekly, // Every 2 weeks on X day monthly, // Every month on X date payCycle, // Specific dates (15th, 30th) custom // User-selected days } enum SyncMode { localOnly, cloudSync, wifiSync, selfHosted } enum ThemeMode { light, dark, system } enum SortOrder { name, expirationDate, purchaseDate, location, quantity } ``` --- ## ๐Ÿ”Œ API INTEGRATIONS ### 1. Open Food Facts API **Purpose:** Auto-populate product info from barcodes **Base URL:** `https://world.openfoodfacts.org/api/v2` **Endpoint:** `GET /product/{barcode}` **Example Request:** ```dart final response = await http.get( Uri.parse('https://world.openfoodfacts.org/api/v2/product/0041220576500') ); // Returns product data including: // - product_name // - brands // - categories // - image_url // - ingredients // - nutriscore ``` **Example Response:** ```json { "code": "0041220576500", "product": { "product_name": "Hidden Valley Ranch Dressing", "brands": "Hidden Valley", "categories": "Dressings, Ranch dressing", "image_url": "https://images.openfoodfacts.org/...", "quantity": "16 fl oz (473 mL)" }, "status": 1, "status_verbose": "product found" } ``` **Service Implementation:** ```dart class OpenFoodFactsService { static const baseUrl = 'https://world.openfoodfacts.org/api/v2'; Future getProductByBarcode(String barcode) async { try { final response = await http.get( Uri.parse('$baseUrl/product/$barcode') ); if (response.statusCode == 200) { final data = json.decode(response.body); if (data['status'] == 1) { return ProductInfo.fromJson(data['product']); } } return null; // Product not found } catch (e) { print('Error fetching product: $e'); return null; } } } ``` **Fallback Strategy:** 1. Try Open Food Facts 2. If not found, try UPC Database API (backup) 3. If still not found, manual entry --- ### 2. Discord Webhooks **Purpose:** Send expiration alerts to Discord channel **Setup:** 1. User creates webhook in Discord server settings 2. Copies webhook URL 3. Pastes in Sage settings 4. App sends JSON POST requests to webhook **Example Webhook URL:** ``` https://discord.com/api/webhooks/123456789/abcdef... ``` **Sending a Message:** ```dart class DiscordService { Future sendAlert(String webhookUrl, List expiringItems) async { final message = _buildAlertMessage(expiringItems); final response = await http.post( Uri.parse(webhookUrl), headers: {'Content-Type': 'application/json'}, body: json.encode({ 'username': 'Sage Kitchen Alert', 'avatar_url': 'https://sage-app.com/icon.png', 'embeds': [ { 'title': '๐ŸŒฟ Sage Alert ๐ŸŒฟ', 'description': message, 'color': 0x4CAF50, // Green 'footer': { 'text': 'Sage Kitchen Management' }, 'timestamp': DateTime.now().toIso8601String() } ] }) ); if (response.statusCode != 204) { throw Exception('Failed to send Discord alert'); } } String _buildAlertMessage(List items) { final buffer = StringBuffer(); buffer.writeln('@everyone Shopping day is tomorrow!\n'); buffer.writeln('**Expiring before next week:**'); for (final item in items) { final emoji = _getEmoji(item.daysUntilExpiration); buffer.writeln('$emoji ${item.name} (expires in ${item.daysUntilExpiration} days)'); } buffer.writeln('\nDon\'t forget to restock! ๐Ÿ›’'); return buffer.toString(); } String _getEmoji(int days) { if (days <= 3) return '๐Ÿ”ด'; if (days <= 7) return '๐ŸŸก'; return '๐ŸŸข'; } } ``` **Rate Limiting:** - Discord allows ~5 requests per second per webhook - Batch alerts together - Use exponential backoff if rate limited --- ## ๐Ÿ“ฑ USER FLOWS ### Flow 1: First Time Setup ``` User downloads Sage โ†“ Welcome screen โ†“ Choose sync mode: - Local only (skip to next) - Cloud sync (sign up/login) - Self-hosted (enter URL) โ†“ Set shopping frequency: "How often do you shop?" - Weekly โ†’ Pick day - Bi-weekly โ†’ Pick day - Monthly โ†’ Pick date - Pay cycle โ†’ Enter dates - Custom โ†’ Select days โ†“ Enable notifications: "Get alerts for expiring items?" - Yes (request permission) - No (can enable later) โ†“ Optional: Discord setup "Send alerts to Discord?" - Yes โ†’ Paste webhook URL โ†’ Test - Skip โ†“ Tutorial: "Let's add your first item!" - Scan barcode demo - OR manual entry โ†“ Ready to use! ๐ŸŽ‰ ``` --- ### Flow 2: Adding Item via Barcode ``` User taps "+ Add Item" โ†“ Choose method: [๐Ÿ“ท Scan Barcode] or [โœ๏ธ Manual Entry] โ†“ User taps "Scan Barcode" โ†“ Camera opens with barcode overlay โ†“ User scans barcode โ†“ Query Open Food Facts API โ†“ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Product Found? โ”‚ โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜ โ”‚ โ”‚ YES NO โ”‚ โ”‚ โ–ผ โ–ผ Auto-fill: Manual Entry: - Name - Type name - Photo - Optional photo - Category - Select category โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ–ผ User adds: - Quantity (1, 2, 3...) - Unit (bottles, lbs, etc.) - Expiration date (calendar picker) - Location (dropdown: Fridge/Freezer/Pantry) - Optional notes โ†“ [Save] button โ†“ Save to local database โ†“ If cloud sync enabled: Queue for sync โ†“ Show success message Navigate to inventory screen ``` --- ### Flow 3: Meal Planning with Recipes ``` User opens Recipes tab โ†“ Browses recipes OR taps "What Can I Make?" โ†“ IF "What Can I Make?": System matches recipes to current inventory Shows % of ingredients available "Caesar Salad: 75% (missing croutons)" โ†“ User selects recipe โ†“ Recipe details screen shows: - Photo - Ingredients with checkmarks: โœ… Romaine lettuce (in fridge) โœ… Ranch dressing (in pantry) โŒ Croutons (not in inventory) - Instructions - Prep time โ†“ User taps "Add Missing Items to Shopping List" โ†“ Dialog: "Add to which list?" - Select existing list - OR create new list โ†“ Croutons added to shopping list โ†“ User cooks meal! โ†“ Optional: Mark ingredients as used (Reduces quantity in inventory) ``` --- ### Flow 4: Shopping Trip ``` User opens Shopping tab โ†“ Selects "Costco" shopping list โ†“ List shows: โ˜ Milk (2 gallons) โ˜ Chicken (2 lbs) โ˜ Croutons (1 bag) โ˜‘ Eggs (already checked off yesterday) โ†“ At store, user checks off items: โ˜‘ Milk โ˜‘ Chicken โ˜‘ Croutons โ†“ All items checked! โ†“ Banner appears: "Shopping complete! Add items to inventory?" [Yes] [Not yet] โ†“ User taps [Yes] โ†“ Bulk add screen: Pre-filled with checked items User adds: - Expiration dates (smart defaults shown) - Locations (remembered from last time) Quick swipe interface โ†“ [Save All] button โ†“ All items added to inventory Shopping list cleared (or moved to archive) โ†“ Success! ๐ŸŽ‰ ``` --- ### Flow 5: Responding to Expiration Alert ``` Morning notification: "๐ŸŒฟ Sage: Use soon! Ranch dressing expires in 3 days" โ†“ User taps notification โ†“ App opens to "Use It Up" screen โ†“ Shows recipes featuring ranch: - Buffalo Chicken Wrap - Veggie Dip Platter - Ranch Chicken Tacos โ†“ User selects "Buffalo Chicken Wrap" โ†“ Checks ingredients: โœ… Ranch dressing (expiring!) โœ… Tortillas โœ… Lettuce โŒ Chicken (needs to buy) โ†“ Adds chicken to shopping list โ†“ Makes dinner, ranch dressing used! โ†“ User marks ranch as "used up" in app OR reduces quantity to 0 โ†“ Item removed from inventory Food waste prevented! ๐Ÿ’ช ``` --- ## ๐Ÿ”” NOTIFICATION SYSTEM (DETAILED) ### Background Job Architecture **Strategy:** Scheduled daily check + immediate calculations ```dart class ExpirationTrackerService { final InventoryRepository _inventory; final SettingsRepository _settings; final NotificationService _notifications; Future runDailyCheck() async { // Run every morning at 9 AM final items = await _inventory.getAllItems(); final settings = await _settings.getSettings(); final nextShoppingDay = settings.nextShoppingDay; for (final item in items) { await _checkItemAndNotify(item, nextShoppingDay); } } Future _checkItemAndNotify(FoodItem item, DateTime? nextShopping) async { final daysUntil = item.daysUntilExpiration; // 1 month notification if (daysUntil == 30) { await _notifications.schedule( title: '๐ŸŒฟ Sage: FYI', body: '${item.name} expires in 30 days', payload: 'item:${item.id}', ); } // 2 weeks notification if (daysUntil == 14) { await _notifications.schedule( title: '๐ŸŒฟ Sage: Heads up!', body: '${item.name} expires in 2 weeks', payload: 'item:${item.id}', ); } // 1 week notification if (daysUntil == 7) { await _notifications.schedule( title: '๐ŸŒฟ Sage: Use soon!', body: '${item.name} expires in 1 week - Tap for recipes', payload: 'useItUp:${item.id}', ); } // Shopping day check if (nextShopping != null) { if (item.expirationDate.isBefore(nextShopping) && daysUntil > 0) { await _notifications.schedule( title: '๐ŸŒฟ Sage: Add to shopping list!', body: '${item.name} expires before your next shopping trip', payload: 'addToList:${item.id}', ); } } } } ``` ### Shopping Day Calculation ```dart class ShoppingDayCalculator { DateTime? calculateNextShoppingDay(UserSettings settings) { final now = DateTime.now(); switch (settings.frequency) { case ShoppingFrequency.weekly: return _nextWeekday(now, settings.dayOfWeek!); case ShoppingFrequency.biweekly: return _nextBiweekly(now, settings.dayOfWeek!); case ShoppingFrequency.monthly: return _nextMonthlyDate(now, settings.dayOfMonth!); case ShoppingFrequency.payCycle: return _nextPayCycleDate(now, settings.payCycleDays!); case ShoppingFrequency.custom: return _nextCustomDay(now, settings.customDays!); } } DateTime _nextWeekday(DateTime from, int targetDay) { // targetDay: 1 = Monday, 7 = Sunday final currentDay = from.weekday; int daysToAdd = (targetDay - currentDay) % 7; if (daysToAdd == 0) daysToAdd = 7; // Next week return from.add(Duration(days: daysToAdd)); } DateTime _nextBiweekly(DateTime from, int targetDay) { final nextWeek = _nextWeekday(from, targetDay); // Check if last shopping was less than a week ago // If yes, return 2 weeks from last shopping // If no, return next week // (Would need to store last shopping date) return nextWeek.add(Duration(days: 7)); // Simplified } DateTime _nextMonthlyDate(DateTime from, int targetDate) { var next = DateTime(from.year, from.month, targetDate); if (next.isBefore(from) || next.isAtSameMomentAs(from)) { // Next month next = DateTime(from.year, from.month + 1, targetDate); } return next; } DateTime _nextPayCycleDate(DateTime from, List dates) { // dates like [15, 30] final upcoming = dates .map((d) => DateTime(from.year, from.month, d)) .where((date) => date.isAfter(from)) .toList() ..sort(); if (upcoming.isEmpty) { // Next month final firstDate = dates.first; return DateTime(from.year, from.month + 1, firstDate); } return upcoming.first; } DateTime _nextCustomDay(DateTime from, List days) { // days like [1, 4, 6] = Monday, Thursday, Saturday final upcoming = days .map((d) => _nextWeekday(from, d)) .toList() ..sort(); return upcoming.first; } } ``` --- ## ๐Ÿ” SECURITY & PRIVACY ### Data Privacy Principles 1. **Local-First:** All data stored locally first, cloud is optional 2. **User Control:** Users choose sync mode and what to share 3. **No Tracking:** Zero analytics, no user tracking 4. **Open Source:** Code is public, verifiable 5. **Self-Hostable:** Users can run own backend ### Cloud Security (Supabase) **Row-Level Security (RLS) Policies:** ```sql -- Users can only see their own items CREATE POLICY "Users see own items" ON food_items FOR SELECT USING (auth.uid() = user_id); -- Household members see shared items CREATE POLICY "Household access" ON food_items FOR SELECT USING ( household_id IN ( SELECT household_id FROM household_members WHERE user_id = auth.uid() ) ); -- Users can only modify their own items CREATE POLICY "Users modify own items" ON food_items FOR UPDATE USING (auth.uid() = user_id); -- Similar policies for recipes, shopping lists, etc. ``` **Authentication:** - Email/password (Supabase Auth) - Optional: OAuth (Google, Apple) in future - JWT tokens for API requests - Refresh tokens for sessions ### Local Security **Database Encryption:** ```dart // Isar supports encryption final isar = await Isar.open( [FoodItemSchema, RecipeSchema, ...], directory: dir.path, inspector: false, encryptionKey: _getOrCreateEncryptionKey(), ); ``` **Secure Storage:** - User settings encrypted - Discord webhook URL secured - Auth tokens in secure storage (flutter_secure_storage) --- ## ๐Ÿงช TESTING STRATEGY ### Unit Tests Test individual functions and business logic ```dart // Example: Test expiration calculation test('daysUntilExpiration calculates correctly', () { final item = FoodItem() ..name = 'Milk' ..expirationDate = DateTime.now().add(Duration(days: 5)); expect(item.daysUntilExpiration, equals(5)); }); test('expirationStatus returns correct status', () { final item = FoodItem() ..name = 'Milk' ..expirationDate = DateTime.now().add(Duration(days: 2)); expect(item.expirationStatus, equals(ExpirationStatus.critical)); }); ``` ### Widget Tests Test UI components ```dart testWidgets('ExpirationBadge shows correct color', (tester) async { await tester.pumpWidget( MaterialApp( home: ExpirationBadge(daysUntil: 2), ), ); expect(find.text('2 days'), findsOneWidget); final badge = tester.widget(find.byType(Container)); expect(badge.color, equals(Colors.red)); // Critical = red }); ``` ### Integration Tests Test complete user flows ```dart testWidgets('Add item flow works end-to-end', (tester) async { await tester.pumpWidget(MyApp()); // Tap add button await tester.tap(find.byIcon(Icons.add)); await tester.pumpAndSettle(); // Choose manual entry await tester.tap(find.text('Manual Entry')); await tester.pumpAndSettle(); // Fill form await tester.enterText(find.byKey(Key('name_field')), 'Milk'); await tester.enterText(find.byKey(Key('quantity_field')), '1'); // Select expiration date await tester.tap(find.byKey(Key('expiration_picker'))); // ... date picker interaction // Save await tester.tap(find.text('Save')); await tester.pumpAndSettle(); // Verify item appears in inventory expect(find.text('Milk'), findsOneWidget); }); ``` --- ## ๐Ÿš€ DEPLOYMENT ### Android Release Process 1. **Build Release APK:** ```bash flutter build apk --release ``` 2. **Build App Bundle (for Play Store):** ```bash flutter build appbundle --release ``` 3. **Sign the Build:** - Configure signing in `android/app/build.gradle` - Store keystore securely - Add to `.gitignore` 4. **Upload to Play Store:** - Internal testing โ†’ Alpha โ†’ Beta โ†’ Production - Gradual rollout recommended ### iOS Release Process 1. **Prerequisites:** - Apple Developer account ($99/year) - Xcode installed - Provisioning profiles configured 2. **Build:** ```bash flutter build ios --release ``` 3. **Archive in Xcode:** - Open `ios/Runner.xcworkspace` - Product โ†’ Archive - Upload to App Store Connect 4. **TestFlight:** - Internal testing - External beta testing - Submit for review ### Self-Hosted Backend Deployment **Docker Compose Setup:** ```yaml version: '3.8' services: postgres: image: postgres:15 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: sage volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" api: image: sage-backend:latest depends_on: - postgres environment: DATABASE_URL: postgresql://postgres:${DB_PASSWORD}@postgres:5432/sage JWT_SECRET: ${JWT_SECRET} ports: - "3000:3000" volumes: postgres_data: ``` **Deploy on Raspberry Pi:** ```bash # On Raspberry Pi git clone https://github.com/sage-app/backend cd backend cp .env.example .env # Edit .env with your settings docker-compose up -d ``` --- ## ๐Ÿ“Š METRICS & ANALYTICS ### Privacy-Respecting Metrics **What we DON'T track:** - Personal data - Specific food items - User behavior - Location data **What we CAN track (locally, opt-in):** - Items saved (count only, for user's stats) - Food waste prevented (estimated) - Money saved (estimated from item costs) - App usage (locally, for user dashboard) **User Dashboard:** ``` Your Stats (This Month): - ๐Ÿ›’ 24 items tracked - ๐Ÿ’ฐ Estimated savings: $47 - ๐ŸŒฑ Food waste prevented: 3 lbs - ๐Ÿ“– Recipes tried: 5 - โญ Most used recipe: Caesar Salad ``` --- ## ๐ŸŽจ DESIGN SYSTEM ### Color Palette ```dart class AppColors { // Primary - Sage Green theme static const primary = Color(0xFF4CAF50); static const primaryDark = Color(0xFF388E3C); static const primaryLight = Color(0xFF81C784); // Expiration Status Colors static const fresh = Color(0xFF4CAF50); // Green static const caution = Color(0xFFFFEB3B); // Yellow static const warning = Color(0xFFFF9800); // Orange static const critical = Color(0xFFF44336); // Red static const expired = Color(0xFF9E9E9E); // Gray // UI static const background = Color(0xFFFAFAFA); static const surface = Color(0xFFFFFFFF); static const text = Color(0xFF212121); static const textSecondary = Color(0xFF757575); } ``` ### Typography ```dart class AppTextStyles { static const headline1 = TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: AppColors.text, ); static const headline2 = TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: AppColors.text, ); static const body = TextStyle( fontSize: 16, color: AppColors.text, ); static const caption = TextStyle( fontSize: 12, color: AppColors.textSecondary, ); } ``` --- ## ๐Ÿ”ฎ FUTURE FEATURES ### Phase 8+ (Post-MVP) 1. **Meal Planning Calendar** - Plan meals for the week - Auto-generate shopping list - Track what you ate 2. **Nutrition Tracking** - From Open Food Facts nutrition data - Track calories, macros (optional) - Health insights 3. **Price Tracking** - Log item costs - Track spending over time - Price comparison between stores 4. **Voice Input** - "Hey Sage, add milk to my shopping list" - Voice-activated barcode scanning 5. **AI Recipe Suggestions** - "What can I make with chicken and rice?" - Generate recipes from ingredients - Dietary restrictions 6. **Household Gamification** - Compete to waste less food - Achievements & badges - Family leaderboard 7. **Integration with Smart Appliances** - Samsung Family Hub fridge - Smart scales for quantity tracking --- **Built with ๐Ÿ’š by enthusiastic developers who hate wasting food!** **Let's make kitchens smarter, one scan at a time! ๐ŸŒฟ**