A mobile-first, offline-capable PWA that replaces a 52-sheet P90X Excel workbook (P90Xcel v2.05) with a fast, client-only web app — workout logging, rescheduling, a body-metrics dashboard, and the workbook's exact scoring engine.
Live: https://pablomatchspace.github.io/p90x-webapp/
Your data stays on your device. There is no account and no analytics. Everything lives in your browser's
localStorageand in the JSON file you export. The one exception is cloud sync — off by default, end-to-end encrypted, and running on a backend you host. The public app and its tests use a fabricated sample dataset — no real personal data is in this repository (PRD decision D3).
The Excel workbook works but is painful on a phone mid-workout: rescheduling means unprotecting sheets and running VBA macros, the charts are dated, and data entry is slow. This app keeps the workbook's proven data model and scoring math, and adds first-class rescheduling, a real dashboard, fast entry (ghost prefill, steppers, focus mode, rest timer), and a Classic⇄Lean toggle.
- Start in one step — pick the date of your first workout and the whole 13-week schedule builds itself. No spreadsheet, no import, no account.
- Today & Schedule — 13-week calendar with per-day status, phase bands, and one-tap logging; four rescheduling modes (skip/shift, move/swap, pull-forward, weekly template remap) with preview, undo, and an audit trail.
- Workout logging — the workbook's scoring engine computed live (adjusted reps, penalties, R×W), a familiar week grid, and a one-exercise-at-a-time focus mode with per-exercise history and a forward-looking target per exercise (beat your last net score — or your last round's on day 1 of a new round).
- Voice rep entry — a push-to-talk mic in focus mode: say "reps 22, knee 8", "next" or "finish workout" and it happens; an opt-in hands-free mode re-arms the mic between sets so a whole session logs without touching the screen. English recognition, feature-detected — browsers without speech recognition simply don't show the button.
- Guided play mode — press Play and the workout runs itself: authored video timelines for Plyometrics, Kenpo X, X Stretch, Cardio X and Yoga (classic or P90X3 30-min), plus a per-step timer in focus mode for strength days. Beeps distinguish rest from work, spoken announcements name each exercise (toggleable), and your video/audio deeplinks stay one tap away mid-session.
- Rounds — complete a 90-day round, archive it with one tap and start the next: an end-of-round report (adherence, strength, body deltas), a round-over-round comparison, and archived history that feeds the next round's targets. Later Settings changes never rewrite an archived round.
- Body log — daily scale entries with derived BMI / lean mass / FFMI + category, colour-coded against your SETUP targets and limits.
- Dashboard & charts — body-vs-target KPIs with an FFMI feasibility "reality check", adherence & pace with a cumulative trend, strength progression and body-composition charts (hand-rolled SVG, no chart library) with crosshair read-outs, phase bands and trend overlays, and a deterministic daily quote.
- Nutrition targets — an evidence-based, recomposition-aware calorie + macro recommendation derived from your own targets (balanced/low-carb diet styles), with the published P90X Nutrition Plan numbers (phase-aware calories + macro split) one disclosure away. Recommendations only — no food logging.
- Settings & extras — every SETUP field editable with guardrails, unit toggle, per-workout video/audio deeplinks, editable quote pack, free-form notes, Navy/3-site/7-site body-fat calculators, and a Help/About page.
- Resilience — a global error boundary, corrupted-storage quarantine + recovery, a one-slot backup written before every destructive action, and a storage-full warning.
- Cloud sync (optional) — keep two devices in step through a Cloudflare Worker you deploy yourself. Encrypted on your device; the server cannot read it.
The app never auto-loads data, so nothing happens until you choose one of three ways in:
- Start a program (no import) — open the app, hit Start a program, pick the date of your first workout and choose Classic or Lean. That's it: the schedule is derived from the start date, so you're logging immediately. Height, weight and targets are optional and can be filled in later under More → Settings.
- Import your workbook — if you already tracked in Excel, convert and import it (below); your history comes with you.
- Try the sample — More → Data → Try sample data loads the fabricated demo dataset so you can look around.
Run the converter locally and import the resulting file:
python tools/convert_xlsm.py "P90X Classic ….xlsm" -o p90x-data.jsonThen More → Data → Choose file and confirm the preview. The converter output
(p90x-data*.json) is gitignored and never leaves your machine. You can export a
backup at any time from the same screen; import→export→import is lossless.
Off by default. Turn it on and two devices stay in step — log on the phone, review on the desktop, no files to shuffle.
You run the backend. There is no shared service and no account system: you
deploy worker/ to your own free Cloudflare account, and the app
asks only for that endpoint's URL and a passphrase. It never asks for a Cloudflare
credential. Your data is encrypted on your device with a key derived from the
passphrase, so the Worker stores ciphertext it cannot read — and neither can
anyone else, including whoever runs this repo.
- Deploy the Worker — paste one file into the Cloudflare dashboard, or use
wrangler. Both paths are written out inworker/README.md. - In the app: More → Cloud sync → your Worker's URL + a passphrase.
- Copy the
SYNC_TOKENthe page shows onto the Worker, and sync. - On your second device: same URL, same passphrase.
A stale device can never silently overwrite a newer copy — the Worker rejects the write and the app asks which one wins. Every download saves the replaced document to the backup slot first. Losing the passphrase orphans the cloud copy only; the data on your devices is untouched.
Vite · React 19 + TypeScript · React Router (HashRouter) · Zustand + Immer ·
Zod (import validation) · Tailwind CSS · vite-plugin-pwa (Workbox) · Vitest +
Testing Library · Playwright · oxlint · Prettier · Lighthouse CI.
Design principles: only raw inputs are stored — every derived number (scores,
penalties, BMI, FFMI, adherence) is a pure function in src/lib, mirroring the
workbook's formula design. Dates are local-calendar ISO strings (YYYY-MM-DD,
no UTC conversion).
npm install
npm run dev # Vite dev server
npm run build # tsc -b + vite build (production bundle)
npm run preview # serve the built app
npm run lint # oxlint (zero warnings)
npm run typecheck # tsc -b
npm run test # Vitest unit/logic suites
npm run test:coverage # + coverage on src/lib and src/state
npm run e2e # Playwright (build first) — desktop chromium + realme 16 Pro+
npm run lhci # Lighthouse CI budget (build first)
npm run format # Prettiernpm run e2e and npm run lhci serve the built app, so run npm run build
first if the source changed.
src/lib/ pure logic — scoring, schedule, body/setup math, adherence, charts (fully unit-tested)
src/state/ Zustand store, actions, localStorage persistence + recovery
src/features/ screens by area (start, today, schedule, workouts, body, dashboard, more)
src/components/ shared UI (Layout, Page, NoProgramCard, ErrorBoundary, SystemBanners, LineChart)
src/data/ generated static assets — Classic/Lean templates + exercise catalog (no personal data)
public/ icons, favicon, and the fabricated sample-data.json
e2e/ Playwright specs — per-feature + cross-feature journeys
tools/ xlsm→JSON converter and the program/catalog generator
worker/ optional self-hosted cloud-sync Worker (never deployed by CI)
docs/ sanitized PRD.md, the story index, and per-epic write-ups
Every PR and push to main runs the CI workflow, whose three jobs go in
parallel:
- validate — lint, format check, typecheck, unit tests (+ coverage), build.
- e2e — the full Playwright suite on the desktop chromium and realme 16 Pro+ mobile profiles.
- lighthouse — Lighthouse CI asserting performance / accessibility / best-practices ≥ 90 on the built app.
Two more workflows run alongside it: CodeQL (security scanning) and, on
main only, Deploy to GitHub Pages. Dependabot is enabled.
Data model, schedule, exercise names and scoring rules are derived as factual data from the P90Xcel v2.05 workbook (workoutsoft.com). This is a personal, non-commercial tool. "P90X" and related marks belong to their respective owners; this project ships no Beachbody/P90X branding or logo assets and is not affiliated with or endorsed by them.
docs/PRD.md— the product requirements (sanitized).docs/stories/— the delivered user-story index.docs/epics/— per-epic write-ups for work after v1.0.0.docs/requirements/— sourced requirement notes (workout timelines, nutrition targets, FFMI feasibility) with evidence tiers.worker/README.md— deploy your own cloud-sync backend.CLAUDE.md— conventions and the story-execution protocol for contributors (human or AI agent).