Loading portfolio
All Work

Intake

A nutrition and habit tracker that grades your day against your own targets, transparently and deterministically, and never guesses at data you didn't give it.

Design + Build · SoloVisit live site
RLS policies
0
Automated tests
0
AI calls
0
Every score is reproducible offline.
Metrics scored
0
02(Context)

Mainstream apps guess at missing data. I built Intake to give users deterministic, uncompromising accountability.

Product film · play with sound
03(Design)

Unknown stays unknown, never zero, enforced the same way in the database, the domain code, and the copy. A day's total returns null the moment any contributing log is incomplete, rather than defaulting to zero; the scoring engine excludes any metric that isn't available and renormalizes the remaining weights instead of scoring the gap as failure; and the dashboard marks a metric “No target configured” or “Not tracked yet” instead of quietly grading it. An imported USDA lookup follows the same rule: it fills the form but stays fully editable, because an estimate is not a confirmed value. Refusing to guess is what makes each score explainable, and it is also what makes the app rigid: with no free-text nutrition inference and no barcode scanner, anything the user will not type stays unrecorded.

Logging is built to survive being done one-handed, mid-meal. The meal form's submit button sits in a sticky, blurred bar pinned above the fixed mobile tab bar, so it stays in thumb reach no matter how far down the nutrition fields someone has scrolled. Water logging skips the form entirely for the common case: three one-tap +250/+500/+750 ml buttons fire straight from the dashboard, and the shell adds safe-area padding for notched phones.

Two choices back the “transparent, deterministic” claim instead of just asserting it. Every score carries a “Why this score?” dialog listing each metric's actual and target values, its own 0–100 score, its weight, and the normalized result, so the grade is never a black box even though the underlying math is not trivial. Deleting a logged entry runs through a confirmation dialog that names the real consequence: a finished day's review is recalculated immediately, instead of a generic “Are you sure?”

04(Engineering)

Intake runs on Next.js App Router with React Server Components for authenticated reads and Server Actions for every mutation. There's no separate API server, and the browser never holds a Supabase service-role key. Supabase is the whole backend: Postgres holds the data, Auth owns sessions via SSR cookies, and Row Level Security is the real authorization boundary. The center of gravity is the deterministic scoring engine, framework-free TypeScript unit-tested apart from the database and UI, reused both to grade real days and to generate the app's own demo dataset.

Log
Server Actions + USDA search
Manual entry or import, always editable
→
→
Persist
daily_reviews upsert
Immutable snapshot; edits flip is_stale and re-run
stack   Next.js · TypeScript · Supabase · PostgreSQL · Zod · Tailwind · shadcn/ui
05(Why end-to-end)
Because I owned both the Postgres schema and RLS policies and the scoring engine that reads them, I could change what a metric's “missing” state means in one pass, updating the check constraint, the score math, and the dashboard copy together. The alternative is what mainstream trackers do: default the gap to zero, or infer it, and let the day show a number either way.
The case for one person, end to end
06(Outcome)

Public and live at food-intake-app.vercel.app. Ships the full loop: meal, water, and activity logging with optional USDA lookup, a daily score with a “Why this score?” breakdown, history and weekly/monthly review, and Supabase-authenticated accounts, all secured by Postgres row-level security.

0
Postgres tables
0
Demo days generated
07(What I'd do differently)

Three concrete gaps, not humility filler. First, package-lock.json is out of sync with package.json: npm ci fails a clean install over missing @emnapi packages, despite the README's own claim that the lockfile is pinned and committed. Second, the documented verification commands (lint, typecheck, unit tests, coverage, build, e2e) have no CI workflow running them, so a bad push can't be caught automatically. Third, the README's own known-limitations section admits v1 is email-and-password only, has no barcode scanner or free-text nutrition inference, and ships a period_reviews table that's fully migrated but never written to. I'd keep the deterministic, no-AI scoring engine regardless: it's rigid, but it's why every score is explainable and testable offline.

Next project
AXIS ↗
Personal OS · Full-stack / Next.js · Supabase · Recharts
← Back to selected work