Skip to content

Latest commit

 

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ShiftSwap

A shift-scheduling and swap-management platform for hourly workforces (retail, cafes, hospitals) - built to replace the "post it in a WhatsApp group and hope nobody double-books" workflow with an automated, rule-enforced swap system.

Live app: https://shift-swap-eta.vercel.app

Backend API: https://shiftswap-kwyn.onrender.com

Note: The backend is hosted on Render's free tier, which spins down after periods of inactivity. The first request after a period of inactivity may take 30–60 seconds while the server wakes up.

Demo Login

Role Email Password
Manager aarav.shah@cafe.com password123
Employee priya.nair@cafe.com password123

All seeded accounts use the same password: password123.


What it does

Employees can post a shift they can't work as an open swap request. Other employees with the matching role can claim it - but before a claim is accepted, the system automatically checks it against real scheduling rules:

  • No double-booking - the claiming employee can't already have an overlapping shift.
  • Weekly hour limits - the claim can't push the employee over their personal weekly hour cap.
  • Minimum rest period - there must be at least 8 hours between shifts.

A claim that fails any of these is rejected immediately, with the specific reason shown to the user - no manager has to manually cross-check a schedule to catch it. A claim that passes goes to a manager for one-click approval, which reassigns the shift.


Why this project

Most "shift scheduling" side projects are CRUD wrappers around a calendar. The interesting engineering problem here isn't the calendar - it's the conflict-detection logic underneath it: interval-overlap SQL, aggregate hour calculations scoped to a rolling week, and rest-period checks that have to handle boundary cases correctly (exact 8-hour gaps, shifts spanning midnight, adjacent weeks).

That logic is validated by an automated test suite covering 29 constructed scenarios, including deliberately adversarial boundary cases (off-by-one-minute gaps, shifts landing exactly at a limit), run automatically in CI on every push.


Tech Stack

Backend

  • Node.js
  • Express
  • TypeScript
  • PostgreSQL
  • JWT Authentication
  • bcrypt

Frontend

  • React
  • TypeScript
  • Vite
  • FullCalendar

Infrastructure

  • GitHub Actions (CI)
  • Render (API + PostgreSQL)
  • Vercel (Frontend)

Architecture

Employee / Manager (Browser)
            │
            ▼
React + Vite Frontend
        (Vercel)
            │
            │ JWT (Authorization Header)
            ▼
      Express REST API
         (Render)
            │
            ├── requireAuth
            ├── requireManager
            ├── Conflict Validation Service
            │      ├── Interval Overlap
            │      ├── Weekly Hour Limits
            │      └── Rest Period Checks
            │
            ▼
 PostgreSQL (Render Managed Database)

The conflict-detection logic lives in a single service module (conflictValidator.ts), called from the claim endpoint and reused-with an in-memory equivalent of the same three rules-by the seed script, so demo data is guaranteed to satisfy the same constraints the app enforces live.


Database Schema

  • employees - name, role, weekly hour cap, manager flag, hashed password
  • shifts - employee, role required, start/end time (TIMESTAMPTZ), status
  • swap_requests - links a shift to the employee giving it up; a partial unique index ensures a shift can only have one open swap request at a time while still allowing a new request after a previous one was rejected or cancelled.
  • swap_claims - records every claim attempt (including failed ones) with the validation result and reason; a second partial unique index prevents two simultaneous pending claims on the same request.

Engineering Challenges Worth Knowing About

Cross-system timezone bug (found twice)

Early in development, conflict-check results showed shift times that were consistently 5.5 hours off from the actual shift.

Tracing it required ruling out:

  • the database (TIMESTAMP vs TIMESTAMPTZ)
  • the Node/pg driver layer
  • the seed script

The real culprit was that the seed script used Date.setHours() (local time) instead of setUTCHours() (explicit UTC), making the definition of "7 AM" ambiguous.

Fixing the column type alone wasn't enough. The final solution required ensuring every timestamp-producing code path in the project consistently used UTC.

The same class of bug resurfaced later in a frontend formatting function that relied on the browser's implicit timezone instead of UTC. It was caught manually by comparing displayed times against FullCalendar's correct rendering-a useful reminder that automated tests don't catch every category of bug.

Transactional integrity across multi-step writes

Claiming a swap involves:

  1. Running conflict-detection queries
  2. Inserting a claim record
  3. Updating the swap request

These operations must either all succeed or all fail.

Every multi-step write (claim, approve, reject) runs inside an explicit PostgreSQL transaction (BEGIN / COMMIT / ROLLBACK) using a single database connection.

Race-condition prevention via partial unique indexes

Rather than relying only on application logic, the database itself prevents duplicate pending claims.

A partial unique index:

UNIQUE (...) WHERE validated IS NULL

makes duplicate pending claims structurally impossible regardless of timing.


Test Suite

The conflict-detection engine is validated by 29 scenarios covering:

  • Overlap detection
    • containment
    • boundary touches
    • exact duplicates
    • multi-shift weeks
  • Weekly hour limits
    • accumulated hours
    • exact-boundary cases
    • correct week scoping
  • Rest period enforcement
    • exact 8-hour boundary
    • one-minute-under violations
    • overnight shifts

Each scenario runs against a real disposable PostgreSQL transaction (rolled back after each test) rather than a mocked database.

Run locally with:

npm run test:conflicts

Tests also run automatically on every push through GitHub Actions.


Security

  • Passwords hashed with bcrypt (cost factor 10)
  • JWT-based authentication
  • Every mutating endpoint requires a valid token
  • Manager-only endpoints enforced server-side via middleware
  • Generic login errors prevent user enumeration
  • Parameterized SQL queries throughout

Running Locally

Prerequisites

  • Node.js 20+
  • PostgreSQL

Backend

cd backend

npm install

npm run migrate
npm run migrate:002
npm run migrate:003
npm run migrate:004
npm run migrate:005
npm run migrate:006

npm run seed
npm run dev

Frontend

cd frontend

npm install
npm run dev

Backend runs on:

http://localhost:5000

Frontend runs on:

http://localhost:5173

Copy .env.example to .env inside backend/ and provide:

  • PostgreSQL credentials
  • JWT secret

before running migrations.


Future Improvements

  • Real-time updates (WebSockets/SSE) instead of 15-second polling.
  • Audit log for approvals and rejections.
  • Refresh tokens for improved authentication UX.
  • Email and in-app notifications for swap events.

Project Structure

shiftswap/
├── backend/
│   └── src/
│       ├── config/          # Database connection pool
│       ├── db/              # Migrations and seed script
│       ├── middleware/      # Authentication middleware
│       ├── routes/          # Express route handlers
│       ├── services/        # Conflict-detection engine
│       ├── tests/           # Automated test suite
│       └── utils/           # JWT signing & verification
│
├── frontend/
│   └── src/
│       ├── api/             # Configured Axios client
│       ├── components/      # React components
│       ├── context/         # Authentication context
│       └── types/           # Shared TypeScript types
│
└── .github/
    └── workflows/           # CI pipeline

Backend CI TypeScript Node.js Express PostgreSQL React Vite JWT Deployed on Render Deployed on Vercel

About

Full-stack shift-swap platform with an automated conflict-detection engine (overlap, weekly-hour, and rest-period rules) enforced via transactional SQL. Node.js/Express/PostgreSQL backend, React frontend, JWT auth, CI-tested.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages