.husky Loading last commit info...
public
src
.dockerignore
.editorconfig
.env.example
.gitignore
.oxlintrc.json
.prettierignore
.prettierrc
Dockerfile
PROJECT_CONTEXT.md
README.md
components.json
docker-compose.yml
eslint.config.js
index.html
nginx.conf
package-lock.json
package.json
tsconfig.app.json
tsconfig.json
tsconfig.node.json
vite.config.ts
README.md

Gym Workouts

A React workout library app: browse exercises by muscle group, open detailed instructions, and play demo videos. Built on a Vite + TanStack stack with Light / Dark / System theming.

Features

  • Exercise library with search and filters (body part, category, difficulty, equipment)
  • Exercise detail pages with metadata, step-by-step instructions, and video demos
  • Prev / next navigation between exercises
  • Weekly custom schedule — assign exercises per day of the week (persisted in localStorage)
  • Today / day workout — view today’s plan and preview other days
  • Theme switcher: Light, Dark, or System (persisted in localStorage)
  • Responsive layout for desktop and mobile
  • Graceful empty states when filters match nothing, a day is empty, or a video is missing

Tech stack

  • Vite + React + TypeScript
  • TanStack Router — type-safe file-based routing (@tanstack/router-plugin)
  • TanStack Query — async state / caching
  • Axios — HTTP client
  • Tailwind CSS — utility-first styling (CSS variables for light/dark)
  • shadcn/ui + Lucide React — accessible UI primitives
  • ESLint + Prettier + Husky + lint-staged

Getting started

npm install
cp .env.example .env
npm run dev

Open the URL printed in the terminal (usually http://localhost:5173).

Scripts

ScriptDescription
npm run devStart the Vite dev server
npm run buildType-check and build for production
npm run previewPreview the production build
npm run lintRun ESLint
npm run lint:fixRun ESLint and auto-fix
npm run formatFormat with Prettier
npm run format:checkCheck Prettier formatting
npm run typecheckRun TypeScript without emitting

Routing

Routing uses TanStack Router file-based routes via @tanstack/router-plugin. Route modules live in src/routes/; the plugin generates src/routeTree.gen.ts (do not edit by hand).

FilePathDescription
routes/__root.tsx(layout)App providers, shell, and not-found UI
routes/index.tsx/Exercise library (search + filters)
routes/today.tsx/todayToday / day-wise workout view
routes/schedule.tsx/scheduleWeekly schedule editor
routes/exercises.$exerciseId.tsx/exercises/$exerciseIdExercise detail, video, and instructions

The router instance is created in src/router.tsx.

Weekly schedule

Custom schedules are stored under gym-workout-schedule as a versioned payload. Each day keeps workout-group refs (for labels) plus concrete exercise IDs from gym-workouts.json.

Default plan (first visit or after Reset Schedule):

DayWorkout groupsFocus
Monday4Back
Tuesday7, 8Biceps + Abs
Wednesday2, 3Chest + Tricep
Thursday6Shoulder
Friday1Legs
Saturday5Cardio
SundayRest

Workout groups resolve to exercise IDs via matchers in src/features/schedule/workout-groups.ts (no duplicated exercise objects). The library has no dedicated biceps/abs primaries, so those groups use the closest tags available in the JSON.

Reference plan file: src/assets/gym/default-weekly-schedule.json.

Changes save immediately. Reset Schedule (on My Schedule) restores the default plan after confirmation and does not clear theme preference.

Workout data & videos

  • Exercise metadata lives in src/assets/gym/gym-workouts.json (50 exercises, IDs 00510100).
  • Demo videos live in src/assets/gym/workouts/ as {id}.mp4.
  • Videos are resolved at build time via Vite import.meta.glob and matched by exercise ID — the JSON is not mutated for video paths.
  • If an exercise has no matching file, the detail page shows a “No demo video” state.

Fields available per exercise: id, name, bodyPart, equipment, target, secondaryMuscles, instructions, description, difficulty, category.

Theme

Theme preference is stored under the workout-app-theme key (light | dark | system). System mode follows prefers-color-scheme. An inline script in index.html applies the class before React loads to avoid a flash of the wrong theme. Dark mode uses the existing .dark CSS variables in src/styles/index.css.

Folder structure

src/
├── assets/gym/           # Workout JSON + demo videos
├── components/           # Shared UI (theme switcher, shadcn primitives)
├── config/               # App configuration (env, etc.)
├── features/
│   ├── schedule/         # Weekly schedule storage, UI, pages
│   └── workouts/         # Exercise library data, components, pages
├── hooks/                # Shared hooks (theme context helpers)
├── layouts/              # App shell
├── lib/                  # Axios, query client, utils
├── providers/            # Theme + schedule + React Query providers
├── router.tsx            # createRouter + type registration
├── routeTree.gen.ts      # Auto-generated route tree (plugin)
├── routes/               # File-based TanStack Router routes
└── styles/               # Global styles and design tokens

Path alias

@/* resolves to src/*.

Environment

Copy .env.example to .env and adjust values as needed:

cp .env.example .env
VariableDescription
VITE_APP_NAMEDisplay name in the app header
VITE_API_BASE_URLAPI base URL for Axios (default /api)
Please wait...
Connection lost or session expired, reload to recover
Page is in error, reload to recover