# Workout Planner — Project Overview

_Last updated: February 18, 2026_

## What This Is

A self-hosted workout logging and planning app built for personal use. The core idea is a mobile-first tool for tracking strength training workouts — what exercises you did, how many sets/reps, at what weight — with a dark, minimal UI that stays out of your way.

The app runs on a local server (Raspberry Pi, NAS, or any Docker host) with no dependency on third-party services. All data lives in a local SQLite database.

## Tech Stack

- **Framework**: Next.js 14 (App Router, server components + client components)
- **Database**: SQLite via Prisma ORM (`prisma/data/app.db`)
- **Styling**: Tailwind CSS, dark mode only (zinc palette, `bg-[#0A0A0A]` base)
- **Auth**: Cookie-based sessions with bcrypt password hashing, 30-day token expiry
- **Icons**: Lucide React
- **Validation**: Zod schemas on all API routes
- **PWA**: Service worker + manifest for mobile install
- **Deployment**: Docker + docker-compose, with shell scripts for start/stop/rebuild

## Current Features (What's Built)

### Workout Logging
The main feature. You log a workout by picking exercises from your library, entering sets with weight/reps, and saving. The app records the date, all set data, optional notes, difficulty rating, and duration.

Sets are displayed in a compact grouped format: consecutive sets at the same weight are collapsed (e.g., "245 x 3/3/3" instead of three separate lines). This is handled by the shared `formatSetsSummary()` helper in `lib/formatSets.ts`.

Each set supports: reps, weight (lbs or kg), RPE (1-10), duration (seconds), distance, calories, and notes. Not every field is shown — the exercise's `inputFields` array controls which fields appear in the logging UI.

### Exercise Library
~101 exercises currently in the database, each with equipment type, muscle group tags, and configurable tracked fields. Exercises are per-user and can be custom.

Equipment types include: barbell, dumbbell, bodyweight, cable, kettlebell, machine, cardio, eq bar, hex bar, grippers, ring, and other. These are extensible — the API accepts any string for type, muscle groups, and input fields (the "+" button feature).

Each exercise detail page shows a history of all logged workouts for that exercise, displayed as a clean list with date, set count, and grouped weight summary.

### CSV Import
A pipeline for importing historical workout data from CSV files. The workflow was designed around the specific use case of photographing handwritten workout logs, having an LLM convert them to CSV format, then importing into the app.

**Import flow**: Upload CSV → API parses and maps exercise names → review each workout one-at-a-time in an editable form → approve to save to DB, or skip/delete. No data touches the database until explicitly approved.

The parser (`app/api/import/parse/route.ts`) includes a 26-entry name mapping table (`NAME_MAP`) that translates CSV shorthand to database exercise names (e.g., "BB Row" → "Barbell Row", "CoC" → "Captains of Crush"). It handles M/D/YYYY and ISO date formats, detects kg units from notes, and returns a list of unmapped exercise names so they can be created before import.

### Dashboard
Shows summary stats: total workouts, total volume, recent workouts. The dashboard queries are in `lib/db/stats.ts`.

### Workout History
Paginated list of past workouts, each rendered as an expandable card showing exercises and their set summaries. Filter by date range. Total set count displayed per workout. Upload button in the header links to the import page.

### Exercise Search & Filtering
Exercises list supports real-time search and filter pills for equipment type. The filter tags are dynamic — derived from the actual exercise data rather than a hardcoded list.

### Settings
Theme preference (light/dark/system), default weight unit (lbs/kg), optional Claude AI integration toggle with API key field. The rest timer setting was removed as unused.

### Custom Values ("+" Buttons)
Exercise types, tracked fields, and muscle groups all support adding custom values via inline "+" buttons. The API validates with `z.string()` rather than `z.enum()`, so any value is accepted and persisted.

### PWA Support
Service worker and manifest for installing as a mobile app. Icons generated at multiple sizes (72px through 512px, including maskable variants).

## Data Model (Key Entities)

- **User** — email/password auth, one user per instance in practice
- **Exercise** — name, type (equipment), muscleGroups (JSON array), inputFields (JSON array), per-user
- **Workout** — date, optional name/notes/duration/difficulty/calories
- **SetLog** — links a workout to an exercise, stores reps/weight/weightUnit/RPE/duration/distance/calories/notes
- **UserPreferences** — theme, default weight unit, Claude AI settings
- **Program/ProgramWeek/ProgramDay/ProgramExercise** — schema exists but not yet implemented in UI
- **Equipment** — schema exists but not yet implemented in UI
- **ContentItem/ContentChunk** — schema exists for future knowledge base feature
- **AISuggestion** — schema exists for future AI coaching feature

## Future Phases (Planned)

### Phase 1: More Historical Data Import
Continue photographing and importing handwritten workout logs. The CSV format and import flow are ready; just need more pages transcribed.

### Phase 2: Training Programs
The database schema already supports structured programs (Program → Weeks → Days → Exercises with sets/reps/RPE targets). The UI for creating, viewing, and following programs has not been built yet.

### Phase 3: Analytics & Progress Tracking
Visualizations for tracking progress over time — charts for weight progression per exercise, volume trends, frequency heatmaps, personal records timeline. The data is all there; just needs frontend visualization.

### Phase 4: AI Coaching (Claude Integration)
The settings already have an "Enable Claude AI" toggle and API key field. The plan is to use Claude to analyze workout history and provide suggestions — exercise recommendations, program adjustments, identifying plateaus, recovery recommendations. The `AISuggestion` model and `ContentItem`/`ContentChunk` models support this.

### Phase 5: Equipment Inventory
Track what equipment is available in the home gym. The `Equipment` model exists with name, type, quantity, weight fields. Could inform exercise recommendations and program generation.

### Phase 6: Content Library
Upload training PDFs, link YouTube videos, store training knowledge. The `ContentItem` and `ContentChunk` models support chunked text storage with page numbers and video timestamps for future RAG-style search.

## Design Philosophy

- **Dark mode only in practice** — the zinc/black palette is the primary design language. Light mode exists in the theme toggle but the app is designed dark-first.
- **Mobile-first** — bottom navigation bar on mobile, sidebar on desktop. Touch-friendly tap targets.
- **Minimal chrome** — data-dense views, compact set summaries, no unnecessary decoration.
- **Client-side state for in-progress work** — workout forms and import review queue keep data in React state until explicitly saved. No auto-save to DB.
- **Flexible schema** — exercise types, muscle groups, and tracked fields are open strings, not enums. New values can be added without code changes.

## Key Conventions

- **Weight units**: Most exercises default to lbs. Kettlebell exercises, Turkish Get Ups, and windmills default to kg. The `defaultWeightUnit` field on Exercise controls this.
- **Set summary format**: `formatSetsSummary()` is the single source of truth for displaying sets compactly. Used in workout cards, exercise history, and import review.
- **Date display**: Dates show year (e.g., "Jan 27, 2026") to avoid ambiguity across year boundaries.
- **Enter key behavior**: In set logging forms, Enter advances to the next field rather than submitting the form.
- **API patterns**: All API routes use Zod validation, return JSON, and follow REST conventions. Auth is via `getCurrentUser()` which reads the session cookie.

## Running the App

```bash
# Development
npm run dev

# Production (Docker)
docker-compose up -d

# Or via scripts
./scripts/start.sh    # start the server
./scripts/stop.sh     # stop it
./scripts/rebuild.sh  # rebuild and restart
```

Database lives at `prisma/data/app.db`. Environment variables in `.env` / `.env.local` set the `DATABASE_URL` connection string.

## Important Notes for Handoff

- The actual database file is `prisma/data/app.db`, NOT `prisma/dev.db` (which exists but is empty/stale).
- SQLite on some mounted filesystems (Docker volumes, network mounts) can have journal mode issues. If you get "disk I/O error", try copying the DB locally, modifying with `PRAGMA journal_mode=OFF`, then copying back.
- The import CSV file at `import-data/proof-of-work-feb2026.csv` contains parsed data from handwritten logs covering January-February 2026. More pages will be added over time.
- Exercise name mapping in the import parser (`NAME_MAP` in `app/api/import/parse/route.ts`) should be updated as new shorthand names are encountered in CSV data.
