Project

GainsTracker

A workout-tracking web app for building training plans, logging sessions on a calendar, and browsing exercise recommendations by muscle group. Built with vanilla JavaScript and Supabase — no framework, no build step, same stack as the rest of this site.

JavaScript HTML & CSS Supabase PostgreSQL

Overview

GainsTracker is a static web app that keeps a whole training routine in one place: what to train, what you actually did, and ideas for what to try next. Everything is plain HTML, CSS, and ES-module JavaScript served as static files, with Supabase providing authentication and a PostgreSQL database at runtime. Each user signs in with email and password and only ever sees their own plans and logs.

Open the app →

Plans

The Plans tab is where routines get built. A plan is organized by muscle group, and each exercise in a group carries its own notes, target reps, and target weight. For lifts where a single target isn't enough, per-set tracking can be switched on to record reps and weight for every individual set. Plans are stored in a plans table with the muscle groups and exercises kept as JSONB, so the structure can stay flexible without schema changes.

Log

The Log tab shows a monthly calendar where every day is marked as Logged, Rest day, or Missed — a quick visual read on consistency. A “Log Today's Session” button records the day's workout, and each entry lands in a logs table with the exercises stored as JSONB alongside the date and a rest-day flag.

Recommendations

The Recommendations tab is a built-in exercise library organized by muscle group — chest, back, shoulders, arms, core, legs, and glutes — and drills down into specialized sub-muscles like upper, mid, and lower chest, lats and traps, biceps and triceps, or quads and hamstrings. It's a reference for filling out a plan when a muscle group needs new ideas.

Data & Auth

Authentication is an email/password gate through Supabase. The database has two tables, plans and logs, both protected by PostgreSQL Row Level Security so every query is scoped to the signed-in user — no user can read or write anyone else's rows. A database trigger keeps plans.updated_at fresh on every change.