Some checks failed
CI / Build & test backend (push) Failing after 14m56s
REQ-MOB-010: BarcodeScreen.tsx — barcode scanner via react-native-camera REQ-VIZ-001: WeeklyCalorieChart.tsx — 7-day bar chart on History screen REQ-VIZ-002: Streak tracker — GET /meals/streak + HomeScreen badge REQ-UX-001: Quick-add calories — POST /meals/quick-add + QuickAddScreen REQ-UX-002: Food favourites — UserFoodMemory.favourite + toggle endpoint + FoodRow star REQ-UX-003: GoalBanner.tsx — in-app slide-in when daily target hit REQ-EXP-001: ExportController — GET /export/meals CSV download REQ-WTR-001: Water tracking — WaterEntry entity + POST/GET /water + DailyDetails widget REQ-UX-004: Daily logging reminder — HomeScreen after-18:00 banner Also: Flyway V2 (favourite), V3 (water_entries), V4 (source constraints) Traceability, CHANGELOG, PLAN updated after each feature
399 lines
11 KiB
Markdown
399 lines
11 KiB
Markdown
# Calorie Counter App — Plan & Requirements
|
||
|
||
**Version**: 1.1
|
||
**Date**: 2026-05-19
|
||
**Status**: Phase 4 in progress
|
||
|
||
---
|
||
|
||
## 1. Product Vision
|
||
|
||
> "The easiest way to track calories with minimal effort and acceptable accuracy, using AI + smart defaults."
|
||
|
||
**Core principle**: Consistent estimation beats absolute precision. Users should trust the app enough to use it daily — not abandon it because it demands too much.
|
||
|
||
**KPI**: Log a meal in under 10 seconds.
|
||
|
||
---
|
||
|
||
## 2. Target Users
|
||
|
||
**Primary**: Busy professionals who eat a mix of home-cooked, restaurant, and packaged food. They want low friction, not lab-grade accuracy.
|
||
|
||
---
|
||
|
||
## 3. MVP Feature Scope
|
||
|
||
### IN scope
|
||
|
||
| Feature | Description |
|
||
|---|---|
|
||
| Manual food search | Search food DB, select portion, add to day |
|
||
| Barcode scan | Scan product → auto-fill nutrition |
|
||
| Photo logging (AI assist) | Snap photo → AI suggests items + portions → user confirms/edits |
|
||
| Daily calorie tracking | Consumed vs. target, remaining calories |
|
||
| Macro tracking | Protein / carbs / fat (optional display) |
|
||
| User profile | Age, weight, height, goal → auto-calculated daily target (BMR) |
|
||
| History view | Calorie totals per day |
|
||
| Repeat last meal | One-tap shortcut on home screen |
|
||
| AI correction loop | User edits AI result → stored to improve future suggestions |
|
||
|
||
### OUT of scope (MVP)
|
||
|
||
- Social features
|
||
- Meal plans
|
||
- Wearable integrations
|
||
- Deep health analytics
|
||
- Custom ML model training
|
||
|
||
---
|
||
|
||
## 4. Differentiation Strategy
|
||
|
||
Three features that separate this from MyFitnessPal etc:
|
||
|
||
1. **Confidence-aware calories** — show `500 kcal ± 80 kcal (confidence 85%)` instead of a false-precision single number
|
||
2. **Personal food memory** — app learns your typical portions, pre-fills next time
|
||
3. **AI correction loop** — every manual correction improves future suggestions, building a personalised model layer over time
|
||
|
||
---
|
||
|
||
## 5. Technical Architecture
|
||
|
||
### Stack decision
|
||
|
||
| Layer | Technology |
|
||
|---|---|
|
||
| Mobile | React Native |
|
||
| Backend | Spring Boot (Java)|
|
||
| Database | PostgreSQL |
|
||
| Food DB | Open Food Facts API (free, open) |
|
||
| AI service | OpenAI Vision API (MVP) → custom fine-tuned model (later) |
|
||
| Auth | JWT-based auth |
|
||
|
||
### Architecture diagram
|
||
|
||
```
|
||
Mobile App (React Native)
|
||
│
|
||
REST API
|
||
│
|
||
Backend (Spring Boot / FastAPI)
|
||
│
|
||
┌────────────────────────────────┐
|
||
│ Food DB (OpenFoodFacts cache) │
|
||
│ AI Service (Vision API) │
|
||
│ User Data (Postgres) │
|
||
└────────────────────────────────┘
|
||
```
|
||
|
||
**Key design decision**: Cache food DB locally for performance. Normalize all food entries to a common schema regardless of source (OpenFoodFacts / barcode / AI / manual).
|
||
|
||
---
|
||
|
||
## 6. Data Model
|
||
|
||
### User
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"email": "string",
|
||
"createdAt": "timestamp",
|
||
"profile": {
|
||
"age": 30,
|
||
"weightKg": 80,
|
||
"heightCm": 180,
|
||
"goal": "lose | maintain | gain",
|
||
"dailyCaloriesTarget": 2200
|
||
}
|
||
}
|
||
```
|
||
|
||
### FoodItem (normalised DB)
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"name": "Chicken breast",
|
||
"source": "openfoodfacts | custom | ai",
|
||
"caloriesPer100g": 165,
|
||
"macros": {
|
||
"proteinG": 31,
|
||
"fatG": 3.6,
|
||
"carbsG": 0
|
||
}
|
||
}
|
||
```
|
||
|
||
### MealEntry
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"userId": "uuid",
|
||
"date": "2026-05-16",
|
||
"mealType": "breakfast | lunch | dinner | snack",
|
||
"items": [
|
||
{
|
||
"foodItemId": "uuid",
|
||
"quantityGrams": 200,
|
||
"calories": 330
|
||
}
|
||
],
|
||
"source": "manual | barcode | photo",
|
||
"confidence": 0.82
|
||
}
|
||
```
|
||
|
||
### PhotoAnalysis (AI audit trail)
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"userId": "uuid",
|
||
"imageUrl": "string",
|
||
"detectedItems": [
|
||
{ "name": "rice", "estimatedGrams": 150, "confidence": 0.76 }
|
||
],
|
||
"userCorrections": [
|
||
{ "name": "rice", "correctedGrams": 180 }
|
||
]
|
||
}
|
||
```
|
||
|
||
### UserFoodMemory (personalisation layer)
|
||
|
||
```json
|
||
{
|
||
"userId": "uuid",
|
||
"foodName": "coffee with milk",
|
||
"avgPortionGrams": 250,
|
||
"lastUsed": "timestamp"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. API Design
|
||
|
||
### Auth
|
||
```
|
||
POST /auth/register
|
||
POST /auth/login
|
||
```
|
||
|
||
### User
|
||
```
|
||
GET /user/profile
|
||
PUT /user/profile
|
||
```
|
||
|
||
### Food
|
||
```
|
||
GET /foods?query=chicken
|
||
GET /foods/barcode/{code}
|
||
```
|
||
|
||
### Meals
|
||
```
|
||
POST /meals
|
||
GET /meals/daily?date=YYYY-MM-DD
|
||
GET /meals/{id}
|
||
PUT /meals/{id}
|
||
DELETE /meals/{id}
|
||
```
|
||
|
||
`GET /meals/daily` response:
|
||
```json
|
||
{
|
||
"totalCalories": 1800,
|
||
"target": 2200,
|
||
"remaining": 400,
|
||
"meals": [...]
|
||
}
|
||
```
|
||
|
||
### AI
|
||
```
|
||
POST /ai/analyze-meal ← multipart image upload
|
||
POST /ai/correction ← submit user correction
|
||
```
|
||
|
||
`POST /ai/analyze-meal` response:
|
||
```json
|
||
{
|
||
"analysisId": "uuid",
|
||
"suggestions": [
|
||
{ "name": "pasta", "grams": 250, "confidence": 0.78 }
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 8. UI / UX Requirements
|
||
|
||
### Screen map
|
||
|
||
```
|
||
Bottom Nav: [ Home ] [ History ] [ Profile ]
|
||
FAB: [ + Add Meal ] (accessible from Home)
|
||
```
|
||
|
||
### Screens
|
||
|
||
| Screen | Key elements |
|
||
|---|---|
|
||
| Home | Calorie progress card, meal list (Breakfast/Lunch/Dinner), repeat shortcut, FAB |
|
||
| Add Meal (bottom sheet) | Photo / Search / Barcode options |
|
||
| Camera | Full-screen preview, capture button |
|
||
| AI Result | Detected items with portions + confidence %, Edit and Confirm CTAs |
|
||
| Edit Meal | Per-item sliders (0–500g), real-time calorie total, Save button |
|
||
| Manual Search | Search input, results list with kcal/100g, portion selector |
|
||
| Daily Details | Calorie total, macro breakdown, meal list |
|
||
| History | Per-day calorie totals (scrollable list) |
|
||
| Profile | Weight / height / goal / daily target, Edit button |
|
||
|
||
### Critical UX rules (non-negotiable)
|
||
|
||
1. **Always require user confirmation** before saving AI-detected meals — never auto-save
|
||
2. **1-tap access** to Add Meal from Home screen
|
||
3. **Sliders over number inputs** for portion adjustment — faster, fewer errors
|
||
4. **Calories update in real-time** while adjusting portions
|
||
5. **Confidence score visible** on AI suggestions (supports honest accuracy framing)
|
||
|
||
### Accessibility
|
||
|
||
- All interactive elements keyboard/touch accessible
|
||
- Minimum touch target 48×48px
|
||
- Contrast ratio ≥ 4.5:1 (WCAG 2.2 AA)
|
||
- `alt` text on all food images / icons
|
||
|
||
---
|
||
|
||
## 9. Design System (summary)
|
||
|
||
### Colours
|
||
|
||
| Token | Value |
|
||
|---|---|
|
||
| Primary/Green | `#22C55E` |
|
||
| Primary/Dark | `#16A34A` |
|
||
| Error/Red | `#EF4444` |
|
||
| Warning/Yellow | `#F59E0B` |
|
||
| Gray/900 (text) | `#0F172A` |
|
||
| Background | `#FFFFFF` |
|
||
| Background/Muted | `#F8FAFC` |
|
||
|
||
### Typography (Inter / SF Pro)
|
||
- Heading/Large: 24px SemiBold
|
||
- Body/Large: 16px Regular
|
||
- Caption: 12px Regular
|
||
- Number/Kcal: 28px Bold
|
||
|
||
### Spacing: 8px grid (4 / 8 / 16 / 24 / 32 / 48px)
|
||
|
||
### Key components
|
||
`Button`, `MealItemRow`, `FoodRow`, `CalorieCard`, `AISuggestionCard`, `PortionSlider`, `ProgressBar`, `FAB`
|
||
|
||
---
|
||
|
||
## 10. Phased Delivery Plan
|
||
|
||
### Phase 1 — Core MVP ✅ Implemented
|
||
- [x] User auth (register / login)
|
||
- [x] User profile + BMR-based calorie target
|
||
- [x] Food search (OpenFoodFacts API)
|
||
- [x] Manual meal logging
|
||
- [x] Barcode scan backend endpoint
|
||
- [x] Daily calorie dashboard
|
||
- [x] Meal history
|
||
|
||
### Phase 2 — AI Layer ✅ Implemented
|
||
- [x] Photo capture screen
|
||
- [x] OpenAI Vision API integration (`/ai/analyze-meal`)
|
||
- [x] AI result confirmation screen
|
||
- [x] Per-item portion sliders (Edit Meal screen)
|
||
- [x] AI correction storage
|
||
|
||
### Phase 3 — Intelligence + Polish ✅ Implemented
|
||
- [x] Confidence-aware display (kcal ± range)
|
||
- [x] UserFoodMemory — personalised portion defaults
|
||
- [x] "Repeat last meal" shortcut
|
||
- [x] Macro tracking display (protein/carbs/fat)
|
||
- [x] Fine-tune AI suggestions based on user corrections
|
||
|
||
### Phase 4 — Enhanced Features (v1.1)
|
||
- [x] REQ-MOB-010: Barcode scanner mobile screen (HIGH — fix UI gap)
|
||
- [x] REQ-VIZ-001: Weekly calorie bar chart on History screen (HIGH)
|
||
- [x] REQ-VIZ-002: Streak tracker — consecutive days logged (HIGH)
|
||
- [x] REQ-UX-001: Quick-add calories without food search (MEDIUM)
|
||
- [x] REQ-UX-002: Food favourites — star items in search (MEDIUM)
|
||
- [x] REQ-UX-003: Goal achievement in-app notification (MEDIUM)
|
||
- [x] REQ-EXP-001: Data export as CSV (LOW)
|
||
- [x] REQ-WTR-001: Water intake tracking (LOW)
|
||
- [x] REQ-UX-004: Daily logging reminder banner (LOW)
|
||
|
||
---
|
||
|
||
## 11. Phase 4 Requirement Details
|
||
|
||
### REQ-MOB-010 — Barcode Scanner Screen (HIGH)
|
||
**Gap**: Backend and API client for barcode lookup exist; mobile UI omits the scan option.
|
||
- New `BarcodeScreen.tsx` using `react-native-camera` (already installed) — full-screen camera with barcode overlay
|
||
- Add "Scan Barcode" as third option in HomeScreen bottom sheet
|
||
- On successful scan → call `GET /foods/barcode/{code}` → navigate to portion selector → log meal
|
||
|
||
### REQ-VIZ-001 — Weekly Calorie Chart (HIGH)
|
||
- New `WeeklyCalorieChart` component: proportional-height bar chart for last 7 days (pure RN `View`, no extra deps)
|
||
- Rendered at top of History screen above the daily list
|
||
- Each bar shows day-of-week label + kcal value; target line drawn at user's daily goal
|
||
- Color-coded: green = at/under goal, amber = over goal
|
||
|
||
### REQ-VIZ-002 — Streak Tracker (HIGH)
|
||
- Backend: `GET /meals/streak` → returns `{ currentStreak: N, longestStreak: N }`
|
||
- Counts consecutive calendar days (ending today) where at least one meal was logged
|
||
- Mobile: streak badge on Home screen below CalorieCard
|
||
|
||
### REQ-UX-001 — Quick-Add Calories (MEDIUM)
|
||
- New `QuickAddScreen.tsx` — number-pad input for kcal + meal type picker
|
||
- Backend: `POST /meals/quick-add` → `{ date, mealType, calories, label? }` → creates system food "Quick Add" entry
|
||
- Accessible from Home bottom sheet as "⚡ Quick Add"
|
||
|
||
### REQ-UX-002 — Food Favourites (MEDIUM)
|
||
- Add `favourite` boolean column to `user_food_memories` (Flyway V3)
|
||
- Backend: `POST /foods/{id}/favourite` (toggle) → upserts UserFoodMemory with `favourite=true/false`
|
||
- Mobile: star icon on each `FoodRow`; Favourites section at top of Search screen
|
||
|
||
### REQ-UX-003 — Goal Achievement Notification (MEDIUM)
|
||
- In-app only (no native push required)
|
||
- When `remaining ≤ 0` after a meal is logged, show an in-app success banner on HomeScreen
|
||
- Banner auto-dismisses after 4 seconds
|
||
|
||
### REQ-EXP-001 — Data Export CSV (LOW)
|
||
- Backend: `GET /export/meals?from=YYYY-MM-DD&to=YYYY-MM-DD` → `Content-Type: text/csv`
|
||
- Columns: `date, mealType, foodName, grams, calories, source`
|
||
- Mobile: "Export Data" button in Profile screen → uses React Native `Share` API
|
||
|
||
### REQ-WTR-001 — Water Intake Tracking (LOW)
|
||
- Backend: `WaterEntry` entity + Flyway V4 migration; `POST /water`, `GET /water/daily?date=`
|
||
- Mobile: water counter widget on DailyDetails screen (+250ml / +500ml quick buttons, reset)
|
||
|
||
### REQ-UX-004 — Daily Logging Reminder (LOW)
|
||
- In-app banner (no native push)
|
||
- If it is after 18:00 local time and `totalCalories === 0` for today, show a reminder banner on HomeScreen
|
||
- Dismissible; does not re-appear once dismissed in the same session
|
||
|
||
---
|
||
|
||
## 12. Open Questions (to resolve before development)
|
||
|
||
1. **Backend language**: Spring Boot (Java — familiar) or FastAPI (Python — easier AI integration)?
|
||
2. **Auth provider**: Self-managed JWT, Firebase Auth, or Auth0?
|
||
3. **Database**: Postgres (more control) or Firestore (faster to start)?
|
||
4. **Image storage**: Firebase Storage or S3 for photo uploads?
|
||
5. **AI provider**: OpenAI Vision API only, or also evaluate Google Vision / custom model from day 1?
|
||
6. **Platforms**: iOS only, Android only, or both from day 1?
|
||
7. **Confidence display**: Show to users always, or only when below a threshold (e.g. < 80%)?
|