Skip to content

Repository files navigation

🚚 TransitOps

Smart Transport Operations Platform

Fleet · Drivers · Dispatch · Maintenance · Fuel · Analytics — one operational cockpit, with business rules enforced at every layer.


Spring Boot Java React Vite PostgreSQL JWT Docker


📋 The Problem

Logistics companies still run transport operations on spreadsheets and paper logbooks. The result:

  • 🔴 Double-booked vehicles and drivers
  • 🔴 Underused assets and no fleet-utilization visibility
  • 🔴 Missed maintenance and expired driver licenses slipping through
  • 🔴 Untracked fuel and operational spend
  • 🔴 Zero real-time operational insight for decision-makers

💡 The Solution

TransitOps replaces the spreadsheet with a single, role-aware platform that takes a vehicle from registry → dispatch → maintenance → cost tracking → analytics — and, critically, enforces the business rules in the service layer and database, not just as UI hints. A dispatcher physically cannot send out a van that's over capacity, in the shop, or driven by someone with an expired license.


✨ Key Features

Module What it does
🔐 Auth & RBAC Stateless JWT auth with 4 seeded roles, each scoped to what they're allowed to touch
🚐 Vehicle Registry Full CRUD with unique registration numbers, capacity, odometer, lifecycle status
🧑‍✈️ Driver Management License tracking, expiry checks, safety scores, compliance status
📦 Trip Dispatch A guarded state machine — DRAFT → DISPATCHED → COMPLETED / CANCELLED
🔧 Maintenance Workflow Opening a log pulls the vehicle off the road (IN_SHOP); closing it restores availability
Fuel & Expense Logging Per-vehicle, per-trip fuel and operating-cost capture
📊 Live Dashboard KPIs Fleet utilization %, active/available vehicles, drivers on duty, trips in flight
📈 Reports & Analytics Fuel efficiency, operational cost, per-vehicle ROI, and one-click CSV export
🗺️ Map View Leaflet-powered geographic view of operations

🛡️ Business Rules — enforced, not suggested

Every one of these is validated in the service layer inside a transaction, so the data can never end up inconsistent:

  • ✅ A vehicle must be AVAILABLE to be dispatched — never IN_SHOP, ON_TRIP, or RETIRED
  • ✅ A driver must be AVAILABLE and hold a non-expired license (checked at dispatch time)
  • Cargo weight can never exceed the vehicle's max load capacity
  • ✅ Dispatch flips both vehicle and driver to ON_TRIP atomically — a partial failure can't strand an asset
  • ✅ Complete / cancel restore both to AVAILABLE in the same transaction
  • ✅ Vehicles or drivers currently ON_TRIP cannot be deleted — trip history is protected
  • Optimistic locking (@Version) stops two dispatchers from grabbing the same vehicle at once

🏗️ Architecture

Clean, layered, and boundary-safe — entities never leak out of the API.

        ┌──────────────────────── React 19 + Vite SPA ────────────────────────┐
        │  Login · Dashboard · Directory · Dispatch · Maintenance · Reports    │
        └───────────────────────────────┬─────────────────────────────────────┘
                                         │  fetch  ( /api  →  JWT Bearer )
                                         ▼
   Controller  ──►  Service  ──►  Repository  ──►  Entity (JPA)  ──►  PostgreSQL
       ▲              ▲
     DTOs      Business rules + @Transactional guards
       ▲
  @RestControllerAdvice  ── consistent JSON errors, never a raw stack trace

Two layers of validation:

  1. Bean Validation (@Valid on DTOs) — structural correctness (required fields, positive numbers, valid email).
  2. Service-layer checks — stateful business rules that need the database (license expiry, availability, capacity).

A global @RestControllerAdvice turns validation failures into 400s and business-rule violations into 422s with a clean, predictable JSON shape.


🧰 Tech Stack

Backend

  • Spring Boot 4.1.0 · Java 17
  • Spring Security 7 — stateless JWT (jjwt 0.12.6), BCrypt password hashing
  • Spring Data JPA + Hibernate · Bean Validation · Lombok
  • PostgreSQL (schema auto-built from JPA entities via ddl-auto)

Frontend

  • React 19 · Vite 8 · React Router 7
  • Leaflet + react-leaflet (map view) · lucide-react (icons)
  • Zero-dependency fetch API layer with JWT token management (no Axios)

Database & Ops

  • PostgreSQL 17 — fully self-hosted, no cloud/BaaS
  • Multi-stage Docker build (single image ships the DB, backend, and built SPA on one port)
  • Postman collection + full API docs included

🚀 Quick Start

Pick the path that fits you. Docker is the fastest way to see the whole thing running.

Option A — 🐳 All-in-One Docker (recommended for the demo)

One image builds the React SPA, compiles the Spring Boot backend, embeds the SPA into it, and bundles PostgreSQL. Everything on port 8080.

docker build -t transitops .
docker run -p 8080:8080 transitops

Then open http://localhost:8080 — frontend and API served together, no CORS, no port juggling.

Option B — 🧩 Docker Compose (Postgres + pgAdmin + Backend)

Great when you want a real database GUI alongside the app.

docker compose up --build
Service URL Credentials
Backend API http://localhost:8080
PostgreSQL localhost:5433 postgres / postgres
pgAdmin http://localhost:5050 admin@transitops.com / admin

Option C — 💻 Run locally (backend + frontend separately)

Prerequisites: JDK 17, Node 20+, and a local PostgreSQL with a database named transitops.

1. Database — create the DB (Hibernate builds the tables for you on startup):

CREATE DATABASE transitops;

2. Backend — from TransitOps_backend/:

./mvnw spring-boot:run          # Windows: mvnw.cmd spring-boot:run

Defaults expect Postgres on localhost:5433. Override without touching code:

SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/transitops \
SPRING_DATASOURCE_USERNAME=postgres SPRING_DATASOURCE_PASSWORD=postgres \
./mvnw spring-boot:run

3. Frontend — from TransitOps_frontend/vite-project/:

npm install
npm run dev

Vite serves on http://localhost:5173 and proxies /apihttp://localhost:8080, so login works out of the box.


🔑 Roles & Access

The backend auto-seeds four roles on first startup. Register a user against any of them:

Role Can do
FLEET_MANAGER Full CRUD over vehicles, maintenance, fuel & expenses
DRIVER Create, dispatch, complete, and cancel trips
SAFETY_OFFICER Manage driver profiles, compliance, and safety scores
FINANCIAL_ANALYST Read-only access to expenses, fuel logs, and financial reports

Get a token in 10 seconds:

curl -X POST http://localhost:8080/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice Manager","email":"manager@transitops.com","password":"password123","roleName":"FLEET_MANAGER"}'

The response includes a JWT — send it as Authorization: Bearer <token> on every other call.


📡 API Overview

Base URL http://localhost:8080 · all routes except /api/auth/** require a JWT.

POST   /api/auth/register            POST /api/auth/login

GET    /api/vehicles                 POST /api/vehicles          (FLEET_MANAGER)
GET    /api/drivers                  POST /api/drivers           (FLEET_MANAGER · SAFETY_OFFICER)

POST   /api/trips                    ← create a DRAFT trip
POST   /api/trips/{id}/dispatch      ← runs the full validation chain
POST   /api/trips/{id}/complete      POST /api/trips/{id}/cancel

POST   /api/maintenance              POST /api/maintenance/{id}/close
POST   /api/fuel-logs                POST /api/expenses

GET    /api/dashboard/kpis           ← live fleet KPIs
GET    /api/reports/fuel-efficiency  GET /api/reports/operational-cost
GET    /api/reports/roi              GET /api/reports/export/csv

📖 Full request/response schemas, validation rules, and error formats: api_documentation.md 📮 Ready-to-run Postman collection (auto-captures the JWT): transitops_postman_collection.json


🎬 Demo Flow (the 90-second pitch)

  1. Register a Fleet Manager → instantly logged in with a JWT.
  2. Add VAN-005 (1500 kg capacity) and a driver with a valid license.
  3. Create a trip with 850 kg cargo → dispatch it → watch both van and driver flip to ON_TRIP atomically.
  4. Try to dispatch a second trip on the same van → blocked with a clean 422. Rules hold. ✅
  5. Try 1800 kg cargo → rejected: "Cargo weight exceeds vehicle max capacity."
  6. Complete the trip → both assets return to AVAILABLE.
  7. Open the Dashboard → utilization %, active trips, and cost/ROI reports update live. Export CSV.

📁 Project Structure

TransitOps/
├── TransitOps_backend/         # Spring Boot API (com.transitops_backend)
│   └── src/main/java/.../
│       ├── controller/         # Thin REST controllers
│       ├── service/            # Business rules + @Transactional guards
│       ├── repository/         # Spring Data JPA
│       ├── entity/ · enums/    # JPA model + status enums
│       ├── dto/ · exception/   # Boundary DTOs + global error handler
│       └── security/ · config/ # JWT filter, RBAC, CORS, seeders
├── TransitOps_frontend/
│   └── vite-project/           # React 19 + Vite SPA
│       └── src/{pages,components,context,services,layouts}
├── Dockerfile                  # Multi-stage all-in-one image
├── docker-compose.yml          # Postgres + pgAdmin + backend
├── api_documentation.md        # Full API reference
└── transitops_postman_collection.json

Built in an 8-hour hackathon. 🏆

Self-hosted database · rules enforced in the service layer · atomic state transitions · one-command Docker demo.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages