All projects

Case study · Live

PCOD Tracker

AI-parsed health logging for women with PCOD

Role

Solo — product, design, full-stack

Period

Mar 2025 · live at pcod-tracker.vercel.app

The Pitch

Type about your day in plain language. Claude extracts symptoms, mood, diet, medications, sleep, and energy into structured data — no forms, no severity sliders.

The Problem

PCOD (Polycystic Ovarian Disease) affects roughly 1 in 10 women. Managing it requires tracking a complex web of symptoms, hormones, medications, diet, exercise, mood, and menstrual cycles. Most health apps make this a chore — endless forms, dropdowns, and manual data entry.

The goal was to flip the friction: let users talk about their day in plain language and let the app figure out the rest. Also: parse lab-report PDFs automatically so nobody has to type hormone values by hand.

The Approach

Rant-first health logging

The central feature is a chat-style "Rant & Vent" screen. Users type freely — "had terrible cramps today, skipped my metformin, ate pizza for lunch, couldn't sleep until 2 a.m., feeling really anxious about everything" — no forms.

Behind the scenes, Claude parses the text and extracts structured data: symptoms (cramps, severity 4/5; insomnia, severity 3/5), medications (metformin — skipped), diet (pizza, lunch), mood (anxious), sleep (estimated 5 hours), energy (low). All of it gets saved to the right database tables.

Prompt engineering as the bottleneck

The trickiest piece was getting Claude to return strict JSON without hallucinating symptoms that weren't mentioned, while still being smart enough to infer things like sleep duration from "couldn't sleep until 2 a.m."

The system prompt is a tight schema contract with worked examples, explicit anti-patterns ("do not infer a symptom from the absence of one"), and a JSON-parseable response format. Every response is validated against a Zod schema before hitting the database. Anything that fails validation falls back to a manual entry form so the user never loses the rant.

Lab report parsing

Users upload a PDF — usually a dense hormone panel (LH, FSH, testosterone, insulin, thyroid). PDF text is extracted locally with `pdf-parse`, then Claude extracts every lab value, identifies the normal range, flags abnormals, and detects any medications mentioned in the report.

The extracted medications automatically merge into the user's existing medication list with case-insensitive deduplication. No re-entry.

Visualising patterns

Raw data is useless without patterns. The app shows: a calendar heatmap of symptom frequency and intensity by day, Recharts line graphs for hormone levels over time with normal ranges as reference bands, a 7-day medication adherence streak, and cycle history with gap analysis.

Key Decisions

Local PDF extraction, not cloud

Lab reports are sensitive. PDF text extraction happens on the server but PDFs themselves are never stored — only the structured lab values persist. The Claude API call sees the extracted text, not the PDF. Nothing is retained by the AI provider.

Fallback form on every AI call

If Claude returns malformed JSON, the user still sees their rant pre-filled into a manual form — they never lose the data. This was the difference between a demo and a tool people could actually rely on.

Rant UI over dashboards as the primary surface

Most health apps lead with a dashboard full of charts. That's backwards — users need to log before they can see patterns. Leading with the lowest-friction log flow (typing in plain language) fixes the dropout at step one.

Metrics

Prisma models

15

API routes

14

Auth

Google OAuth + credentials

AI provider

Claude

Privacy

PDFs never stored

Open source

MIT

free to self-host

Stack

App

Next.js 16React 19TypeScriptTailwind CSSRecharts

Data & auth

PostgreSQLPrisma (15 models)NextAuth.jsJWT sessionsbcryptjs

AI & PDF

Anthropic Claude APIpdf-parse (local PDF text extraction)