Fleet · Drivers · Dispatch · Maintenance · Fuel · Analytics — one operational cockpit, with business rules enforced at every layer.
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
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.
| 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 |
| 🧑 |
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 |
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
AVAILABLEto be dispatched — neverIN_SHOP,ON_TRIP, orRETIRED - ✅ A driver must be
AVAILABLEand 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_TRIPatomically — a partial failure can't strand an asset - ✅ Complete / cancel restore both to
AVAILABLEin the same transaction - ✅ Vehicles or drivers currently
ON_TRIPcannot be deleted — trip history is protected - ✅ Optimistic locking (
@Version) stops two dispatchers from grabbing the same vehicle at once
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:
- Bean Validation (
@Validon DTOs) — structural correctness (required fields, positive numbers, valid email). - 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.
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
fetchAPI 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
Pick the path that fits you. Docker is the fastest way to see the whole thing running.
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 transitopsThen open http://localhost:8080 — frontend and API served together, no CORS, no port juggling.
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 |
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:runDefaults 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 devVite serves on http://localhost:5173 and proxies /api → http://localhost:8080, so login works out of the box.
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.
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
- Register a Fleet Manager → instantly logged in with a JWT.
- Add
VAN-005(1500 kg capacity) and a driver with a valid license. - Create a trip with 850 kg cargo → dispatch it → watch both van and driver flip to
ON_TRIPatomically. - Try to dispatch a second trip on the same van → blocked with a clean
422. Rules hold. ✅ - Try 1800 kg cargo → rejected: "Cargo weight exceeds vehicle max capacity."
- Complete the trip → both assets return to
AVAILABLE. - Open the Dashboard → utilization %, active trips, and cost/ROI reports update live. Export CSV.
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.