A mobile-first, single-user workout tracker for replacing a paper gym notebook. The app tracks workouts, exercises, sets, and flexible metrics such as reps, weight, time, distance, and laps.
- Password-protected single-user login.
- Recent workout list with one-tap workout creation.
- Active workout screen for adding exercises and quick set entries.
- Exercise suggestions based on the last 90 days of workout history.
- Editable exercise names and set metrics while a workout is active.
- Completed workouts are read-only.
- Workouts cannot be finished until every exercise has at least one set.
- Next.js 16 App Router
- React 19
- TypeScript
- Tailwind CSS 4
- HyperUI-inspired copied Tailwind patterns
- Prisma 7
- PostgreSQL 18
- Docker Compose
- Node.js
- npm
- Docker Desktop or a compatible Docker runtime
This project uses a checked-in .env.development for local development defaults. These values are intentionally non-production credentials for the local Docker database.
Database connection variables are split by purpose:
DATABASE_POOL_URLis used by the running app and seed script for normal database operations.DATABASE_DIRECT_URLis used by Prisma migrations.
For local development, both variables point at the same Docker Postgres database.
npm run dev starts the local PostgreSQL 18 container before starting Next.js.
Use npm run pwa:dev when testing installability, service worker behavior, or offline sync locally. It starts the same database container, enables service worker registration, and runs Next.js with --experimental-https at https://localhost:3000.
On first setup, the app creates the password holder automatically and prints a strong initial password in the server logs. The password is only logged when no auth user exists.
- Install dependencies.
npm install- Start the development server and database.
npm run dev- In a second terminal, run the initial database migration.
npm run prisma:migrate- Open the app.
http://localhost:3000
The development database runs in Docker using PostgreSQL 18.
Prisma migrations use DATABASE_DIRECT_URL; the app runtime and seed script use DATABASE_POOL_URL.
Useful commands:
npm run db:up
npm run db:down
npm run pwa:dev
npm run prisma:migrate
npm run prisma:generate
npm run prisma:studio
npm run db:seednpm run prisma:generate writes the generated Prisma client to lib/generated/prisma as configured in prisma/schema.prisma.
The app is single-user and password protected.
The auth user is created automatically on first visit to the login page when no user exists. A strong generated password is printed once in the server logs. After signing in, use Settings to change the password.
npm run db:seed is optional and uses the same behavior: it creates and logs a generated password only when no auth user exists.
The app uses a flexible metric model rather than fixed set fields.
A workout contains ordered workout exercise entries. Each entry points at a reusable exercise name and contains ordered sets. Each set contains one or more metrics.
Supported metric types and units are defined in prisma/schema.prisma:
REPSwithCOUNTWEIGHTwithLBorKGTIMEwithSECONDSorMINUTESDISTANCEwithMETERS,KM, orMILESLAPSwithLAPS
Example metric combinations:
- reps + weight
- reps only
- time only
- distance + time
- laps + time
The app is mobile-first because it is primarily used during workouts at the gym. UI patterns should prioritize fast data entry, large tap targets, and low-friction editing.
HyperUI is used as design inspiration through copied Tailwind patterns, not as an installed UI package.
The app is installable as a PWA and includes offline support for gym dead zones.
- The web app manifest, icons, theme color, and service worker make the app installable from supported browsers.
- Recently visited same-origin pages are cached with a network-first strategy, so previously opened workout pages can be viewed offline.
- Active workout edits use an IndexedDB-backed mutation queue for add/edit/delete set and exercise changes while offline.
- Queued workout changes sync back to Postgres through
/api/workouts/[workoutId]/syncwhen connectivity returns. - Offline sync is intentionally scoped to active workout entry. Login, password changes, and first-time auth bootstrap still require the server.
- Logging out or changing the password redirects to
/login, where the client asks the service worker to clear cached authenticated pages.
Operational notes:
- Install from a production HTTPS deployment for the best Android/iOS PWA behavior.
- For local PWA testing, use
npm run pwa:devand openhttps://localhost:3000so browser PWA APIs run in a secure context. - Normal
npm run devdoes not register the service worker; this avoids stale development caches while working on non-PWA features. - If a browser keeps an old service worker, close all app tabs and reopen the installed app after deploying a new service worker version.
- If sync fails, queued changes remain in IndexedDB and retry when the app is online again.
- Push notifications are intentionally not implemented; the app does not request notification permission or require VAPID keys.
Set NEXT_PUBLIC_DEMO_MODE=true or APP_MODE=demo to run branch previews without auth or Postgres access.
/renders a local preview home page instead of requiring a signed-in user.- Workouts are saved to the browser's IndexedDB using the same snapshot shape as offline workout entry.
/workouts/[workoutId]reuses the active workout UI, but changes stay local and never call the sync API./loginand/settingsredirect back to/in demo mode.prisma generateuses a dummy datasource URL in demo mode, so preview builds do not needDATABASE_DIRECT_URL.npm run prisma:migrateexits without touching a database in demo mode.- Demo data is per browser/device and can be cleared by the browser at any time.
Vercel deployments are intentionally restricted by vercel.json:
mainis allowed for production deployments.devis allowed for development deployments.- All other branches are blocked from automatic Vercel deployments.
The Vercel build command should be npm run vercel:build. This runs prisma generate, skips prisma migrate deploy in demo mode, and runs migrations only for database-backed deployments.
app/page.tsxlists recent workouts and starts new workouts.app/logincontains the sign-in page and login/logout server actions.app/workouts/actions.tscontains workout, exercise, and set server actions.app/workouts/[workoutId]contains the active/completed workout screen and its client-side editors.lib/auth.tsmanages DB-backed sessions and the HTTP-only session cookie.lib/prisma.tscreates the Prisma client using@prisma/adapter-pg.prisma/schema.prismacontains the PostgreSQL data model.prisma/seed.tsoptionally creates the auth user with a generated password.
This project is a full-stack rebuild based on two older repositories:
Those projects used a separate frontend/backend architecture with Next.js, Kotlin Spring Boot, and MongoDB. This app keeps the same core purpose of replacing a paper gym notebook, but merges the concept into one full-stack Next.js application backed by Prisma and PostgreSQL.