See every court, book every session, and track payments from one lightweight desk — built for solo racket-sport operators.
Stop juggling paper sheets and phone calls. Court Flow puts your schedule, roster, and daily revenue in one place: a day grid by court (badminton, pickleball, tennis, padel, squash, table tennis), fast booking for walk-ins and regulars, payment type and reference on each session, and filters when you need to review who paid what.
Runs on a laptop or small server with plain Node.js and a single JSON file — no database, no monthly booking software, no build step. Configure hours, court types, and prices in Settings, lock the app with an optional operator PIN, and back up your data anytime.
Built for badminton halls, pickleball clubs, tennis facilities, and anyone running courts alone who wants clarity at the counter without enterprise overhead.
- Schedule — day view grid by court and 30-minute slots; click to book or view sessions
- Book — player typeahead from regular roster, walk-in names, optional save-to-roster
- Players — CRUD with search; import many at once by pasting CSV
- Courts — manage courts by type (badminton, pickleball, tennis, padel, squash, table tennis)
- Dashboard — today's sessions, revenue tally, upcoming week
- Payments — payment type and reference on book/edit; history with player filter and running totals
- Settings — operating hours, default prices by court type, backup/restore, reset sample data
- Plain Node.js
httpserver with static HTML + JSON API (no framework or build step) - Lightweight custom CSS UI with Lucide icons via CDN
cd court-flow
npm install
cp .env.example .env # optional — edit settings
npm run seed # optional — sample courts, players, bookings
npm startOpen http://localhost:3010.
Or use Make:
make help # list all commands
make setup # .env + npm install
make seed # sample data
make start # background server
make stop
make restart
make status
make dev # foreground with file watchCopy .env.example to .env and edit as needed.
| Variable | Description |
|---|---|
APP_NAME |
App title shown in the header |
APP_ENV |
development or production |
PORT |
Server port (default: 3010) |
SESSION_SECRET |
Session signing secret (change in production) |
OPERATOR_PIN |
Require PIN unlock before use (leave empty for open local access) |
DATA_PATH |
Path to JSON data file (default: storage/data.json) |
Most day-to-day settings — operating hours, currency, default prices by court type, payment types — are edited in Settings inside the app and saved to storage/data.json.
Default court types (badminton, pickleball, tennis, padel, squash, table tennis) and their default prices are editable in Settings.
Durations are 30-minute steps (e.g. 0 hr 30 min, 1 hr 0 min, 1 hr 30 min …) up to the longest slot that fits your operating hours, plus All day for a full-day hold. Options update when you change open/close times in Settings.
All records live in a single JSON file (storage/data.json by default):
| Key | Contents |
|---|---|
settings |
Operating hours, currency, court types, payment types |
courts |
Court inventory |
players |
Regular player roster |
bookings |
Sessions, prices, payment info, status |
Download a backup anytime from Settings → Download backup. Restore via Settings → Restore backup, or run npm run reset to wipe and reload sample seed data.
make setup && make seed && make start # background on http://localhost:3010
make status && make stopOr npm start after npm install. Verify with curl http://localhost:3010/api/health.
Host on Railway with a persistent volume mounted at /app/storage. Set SESSION_SECRET and OPERATOR_PIN in Railway variables.
See DEPLOY.md for the full local and Railway setup guide (volume, env vars, health check, troubleshooting).
On a VPS: npm install --omit=dev, optional npm run seed, then npm start or a process manager (PM2, systemd). Back up storage/data.json regularly.
Need custom workflows, features, or integrations?
Contact us at:
MIT — see LICENSE.md.