Skip to content
CutGrass88Public

About

An exercise library and workout planner built with Express and vanilla JavaScript.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

RepShelf

A simple place to collect exercises and build reusable workouts.

Tests

RepShelf is a small full-stack web app built with vanilla JavaScript and Express. Add exercises with optional notes and images, choose the exercises for a workout, and keep your collection between server restarts using a local JSON file.

The app focuses on planning workouts. It does not track completed sessions, repetitions, weights, or progress.

Features

  • Create exercises with a name, an optional description of up to 350 characters, and an optional HTTP(S) image URL.
  • Build named workouts by selecting exercises from your collection.
  • Edit exercise details and change a workout's name or selected exercises using the existing forms.
  • Filter the workout builder's exercise list by the beginning of an exercise name, ignoring letter case.
  • Browse exercise cards and workouts with exercise names, descriptions, and images.
  • See exercise and workout counts, and refresh the displayed data.
  • Confirm deletion before removing an exercise or workout.
  • Automatically remove a deleted exercise from every workout that references it.
  • Save changes to a local JSON file, with no database service required.

Quick start

Use Node.js 22 and npm. Local verification uses Node.js 22.20.0 and npm 11.6.4.

Clone the repository and install the locked dependencies:

git clone https://github.com/CutGrass88/repshelf.git
cd repshelf
npm ci

Create your local data file from the included examples. This is required before starting the app; tests create their own isolated fixtures.

Windows PowerShell:

Copy-Item data.example.json data.json

macOS or Linux:

cp data.example.json data.json

Run the server:

npm start

Open http://localhost:3000. Stop the server with Ctrl+C.

Run the npm commands from the repository root. Opening public/index.html directly does not start the API.

The page loads Bootstrap and SweetAlert2 from a CDN. Internet access is needed for those resources and for any remote exercise images.

Using RepShelf

  1. Add an exercise in Create exercise. Only its name is required.
  2. Enter a workout name and select one or more exercises in Create workout.
  3. Submit the workout to see its exercise cards in the workouts list.
  4. Use Refresh to fetch the latest data from the server.
  5. Choose Edit on an exercise or workout to load it into its form. Make your changes and select Save changes, or Cancel to return to creating a new item.
  6. Use a delete button and confirm the dialog to remove an item.

Selections are retained when you search for another exercise, so a workout can include exercises chosen across different search results.

Editing an exercise keeps its ID, so its updated details appear in workouts that already use it. Editing a workout lets you rename it or change the selected exercises. Cancel does not save changes, and a failed save leaves your entered values in the form so you can retry.

Deleting an exercise also removes it from existing workouts. A workout can remain in the list with no exercises after its last exercise is deleted; select at least one exercise when saving an edit to that workout.

Technology

Part Implementation
Frontend HTML, CSS, vanilla JavaScript, Fetch API
Styling Bootstrap 4.1.3 from jsDelivr, plus local CSS
Confirmation dialogs SweetAlert2 11 from jsDelivr
Server Node.js, Express 5, CommonJS modules
Storage Local data.json, read into memory at startup
Tests Jest 30 and Supertest 7
Dependency installation npm with a committed package-lock.json

There is no frontend build step. Express serves the page and the JSON API from the same server.

How it works

flowchart LR
    Browser[Browser: HTML, CSS, JavaScript]
    Server[Express server]
    Assets[public/ assets]
    State[In-memory exercises and workouts]
    File[(Local data.json)]
    CDN[Bootstrap and SweetAlert2 CDN]

    Browser -->|Page and asset requests| Server
    Server --> Assets
    Browser -->|Fetch /api requests| Server
    Server --> State
    File -->|Read on startup| State
    State -->|Save after create, edit or delete| File
    Browser -->|Styles and dialogs| CDN
Loading

server/server.js starts the HTTP listener. server/serverApp.js creates the Express app, serves static files, validates requests, and handles storage. The browser fetches exercises and workouts, stores them in client-side state, and renders the lists.

Project structure

repshelf/
├── .github/workflows/test.yml # Linux and Windows checks
├── docs/
│   └── api.md              # Endpoint reference and request examples
├── public/
│   ├── index.html          # Page structure and forms
│   ├── app.js              # Fetch requests, rendering, and interactions
│   └── style.css           # App-specific styles
├── server/
│   ├── server.js           # Server entry point
│   └── serverApp.js        # Express routes and JSON persistence
├── tests/
│   ├── client.test.js      # Client behavior regression tests
│   └── server.test.js      # API tests
├── data.example.json       # Committed sample collection
├── data.json               # Local runtime data; created during setup, ignored by Git
├── .gitignore
├── LICENSE
├── package.json
├── package-lock.json
└── README.md

The repository keeps application code, tests, documentation, and neutral example data. Installed dependencies and runtime data belong in your local working copy.

Commands and configuration

Command Purpose
npm ci Install dependencies from the lockfile
npm start Start server/server.js
npm test Run the Jest API and client regression suites
npm test -- --runInBand Run the suite in one process
Environment variable Default Purpose
PORT 3000 HTTP listener port
DATA_PATH data.json in the app root Path to an existing JSON collection; relative overrides resolve from the working directory

Windows PowerShell:

$env:PORT = "3001"
npm start

macOS or Linux:

PORT=3001 npm start

The app does not load .env files automatically. Set environment variables in your shell or process configuration. If you use DATA_PATH, keep the file outside the repository or explicitly ignore it so personal data is not committed.

API overview

All routes are relative to the running server. Send JSON request bodies with Content-Type: application/json.

Method Endpoint Result
GET /api/exercises List exercises
GET /api/exercises/:id Get one exercise
POST /api/exercises Create an exercise
PUT /api/exercises/:id Update an exercise while keeping its ID
DELETE /api/exercises/:id Delete an exercise and remove its workout references
GET /api/workouts List workouts with their exercise IDs
GET /api/workouts/:id Get a workout with its exercise names resolved
POST /api/workouts Create a workout from existing exercise IDs
PUT /api/workouts/:id Update a workout's name and selected exercises
DELETE /api/workouts/:id Delete a workout

See the complete API reference for schemas, examples, validation rules, status codes, and current edge cases.

Data, backups, and resets

data.example.json includes four exercises—Bench Press, Squat, Row, and Plank—and two workouts: Full Body and Core. Its image fields are empty, so the sample collection does not depend on remote images.

Copying this file creates data.json, which is excluded from version control. The app reads that file once at startup and saves the whole collection after each successful create, edit or delete operation. Its top-level structure is:

{
  "exercises": [],
  "workouts": [],
  "nextExerciseID": 1,
  "nextWorkoutID": 1
}

Each workout stores an array of exercise IDs. The two counters supply IDs for new records; editing and deletion do not change them. When preparing custom data, keep those counters above the largest existing IDs and ensure workout references point to existing exercises.

Saving first writes a temporary file beside the collection, then replaces the collection file. The in-memory data changes only after this succeeds. If a write or replacement fails, the previous collection and ID counters remain intact, and the API returns a save error. The data directory must be writable as well as the file itself.

To back up your collection, stop the server and copy data.json to a safe location outside the repository. To restore it, stop the server, replace data.json with your backup, and restart. Manual file changes are not picked up until the server restarts.

To reset to the sample collection, stop the server, back up anything you want to keep, and repeat the setup copy command. Replacing data.json replaces your current collection. A missing or malformed JSON file prevents the server from starting.

Tests

With dependencies installed, run:

npm test -- --runInBand

The API suite uses Supertest to exercise the Express app without running npm start separately. Each test gets a temporary data fixture and leaves your runtime collection untouched. Coverage includes reads, creation, editing, validation, malformed requests, missing records, deletion, cascading workout updates, and recovery from failed writes and file replacements.

Client regression tests check editing and cancellation, failed saves, workout selections across search results, image fallback, and confirmation behavior using lightweight DOM fixtures. They do not replace testing in a real browser. Mobile layout and visual rendering have not been verified in this environment.

The test workflow runs the suite on Linux and Windows with Node.js 22 and checks npm dependencies for high-severity advisories. To collect local coverage, run npm test -- --runInBand --coverage.

Local verification on 14 September 2026: 109 tests passed (87 API and 22 client tests), with 99.35% line coverage for server/serverApp.js. The VM-based client tests do not contribute browser coverage. npm audit reported zero vulnerabilities; audit results may change as new advisories are published.

Known limitations

  • Single shared collection: there are no accounts, authentication, or authorization. Anyone who can reach the server can read, create, edit, or delete data. The listener does not explicitly restrict itself to localhost.
  • Simple file storage: each change writes the whole JSON file synchronously. Failed saves preserve the previous collection, but there is no file locking or coordination between server processes. Run one server process against a data file and keep backups.
  • External browser resources: styling, enhanced confirmation dialogs, and optional images rely on external hosts. Deletion falls back to the browser's built-in confirmation dialog if SweetAlert2 cannot load.
  • Planning only: there is no exercise ordering control, sets/reps tracking, workout history, or progress reporting.

This is a small local application and a full-stack project example. Public deployment would need authentication and more robust storage.

Possible next steps

  • Add tests in a real browser for keyboard interaction and responsive layouts.
  • Replace file storage with a database if the app grows beyond one local process.
  • Add user accounts before supporting a shared public service.

Licensing

Licensed under the ISC License, matching the project's package metadata. Third-party dependencies retain their own licenses.

About

An exercise library and workout planner built with Express and vanilla JavaScript.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages