VIEW
Macro Tracker
← BACK TO PROJECTS

MACRO TRACKER

Daily-use nutrition PWA. Type meals in natural language, Claude Sonnet parses them into structured macro data. Batch cook system, receipt OCR, AI meal suggestions scored against remaining daily targets. 90+ consecutive days of personal use.

ROLE Sole Developer
YEAR 2025
TYPE PWA / Full-Stack
STATUS Live (personal use)

What I Built

A nutrition tracking progressive web app built for daily use. The core problem: logging food accurately is tedious if you have to search a database for every ingredient. The solution is a conversational input layer: type "150g chicken breast with 200g rice and a tbsp of olive oil" and the Claude Sonnet API parses that into structured macro data. You confirm, edit if needed, and save. No searching, no drop-downs.

The app has been my primary nutrition tool for over 90 consecutive days. That sustained real-world use has driven every iteration — the batch cook system, the pantry tracker, the receipt scanner all came from hitting actual friction points.

Database Architecture

PostgreSQL — 12+ tables daily_logs ← one row per day per user meal_entries ← individual logged meals, linked to daily_log meal_micros ← micronutrient breakdown per meal entry saved_meals ← reusable meal templates with macro totals food_items ← verified database of ingredients + macros recipes ← batch cook recipes with serving count recipe_ingredients ← ingredients per recipe, linked to food_items pantry_state ← current stock: quantity, category, expiry receipts ← scanned receipt records receipt_items ← parsed line items from receipts micro_targets ← per-user NHS RDA micronutrient targets targets ← per-user daily macro targets

Core Features

💬

Conversational Food Logging

Type meals in plain English. The Claude Sonnet API parses natural language into protein, carbs, fat, calories, and fibre. You see the parsed result and confirm, edit individual values, or reject before anything is saved to the database.

🍳

Batch Cook System

Describe a recipe in natural language. The API creates it with per-portion macros calculated from the verified food item database. Raw ingredients are deducted from pantry on cook. Remaining portions are tracked — log a portion and pantry updates accordingly.

🧾

Receipt OCR

Photograph a shopping receipt. The AI extracts line items, matches each against the verified food database, and auto-populates the pantry with quantities and expiry estimates. Reduces pantry entry from minutes to seconds.

🤖

AI Meal Suggestions

Scores all 26+ saved meals against remaining daily macro targets. Then generates contextual suggestions using pantry contents, food exclusions, meal history, time of day, and the remaining macro gap. Surfaces what to eat next, not just what you've eaten.

🥦

Micronutrient Tracking

Tracks intake against NHS RDA targets for key micronutrients. Traffic-light status per nutrient. Separate from macros — you can hit your protein target and still see where you're consistently under on vitamins or minerals.

🏪

Pantry Management

Tracks quantities, categories, and expiry dates. Usage rates update automatically as meals are logged. The meal suggester draws from pantry contents to recommend meals you can actually cook right now.

The App

The AI Works at Two Levels

Claude is involved in two completely separate ways in this project. First, it's the development tool: the entire codebase was built through AI-assisted pair programming, with each feature passing through structured engineering, product, design, QA, and operations review gates before being committed.

Second, it's the runtime engine: the Claude Sonnet API runs live in production, parsing user input into structured data on every food log, receipt scan, and meal suggestion request. The user never touches a database search field; the AI handles the unstructured-to-structured conversion.

When It Went Wrong

DEBUGGING STORY

During a build session, an AI-written Python script hit a unicode encoding error and silently wrote an empty file over the 260-line MealSuggester React component. The app went white. The error message pointed to .length on undefined — not the real problem.

Traced back to a 0-byte file. The component couldn't be recovered from the Docker container because only backend files are volume-mounted; frontend build files aren't. The component had to be rewritten from scratch.

The first rewrite attempt failed because the AI assumed different prop names than the parent component actually passes. Caught from the error output, corrected against the actual parent, deployed clean. Total recovery time: one session.

This incident changed how backend scripts are run during builds: temp files write to a separate staging path first, and the component swap only happens after a byte-count validation check on the output.

Deployment

Runs on a self-managed Ubuntu VPS (217.154.41.233) via Docker Compose. The backend Node.js/Express service and PostgreSQL database run as separate containers with a shared network. Backend source is volume-mounted for rapid iteration; the React PWA builds to a static dist folder served via Nginx.

version: '3.8' services: api: build: ./server volumes: - ./server:/app environment: - DATABASE_URL=postgresql://... - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} depends_on: - db db: image: postgres:15 volumes: - pgdata:/var/lib/postgresql/data nginx: image: nginx:alpine volumes: - ./client/dist:/usr/share/nginx/html ports: - "80:80"

Tech Stack

REACT (PWA) NODE.JS / EXPRESS POSTGRESQL CLAUDE SONNET API DOCKER COMPOSE NGINX SELF-HOSTED VPS REST API JWT AUTH

NEXT PROJECT

BRIEFCRAFT →