← Back

FleetPulse

One dashboard. Every truck. No more surprises on the road.

Fleet dispatchers lose time cross-referencing a maintenance spreadsheet, a driver roster, and a route board that don't talk to each other. FleetPulse puts vehicles, drivers, maintenance records, and alerts in one place — and automatically surfaces anything overdue before someone has to go looking.

Java 21Spring Boot 3.2Spring Data JPAFlywayH2 / PostgreSQLHibernate 6ThymeleafChart.jsSwagger / OpenAPIJUnit 5MockitoDocker
GitHub →

// dispatch.log — 07:42:11

The problem

[ALERT] Vehicle FP-TRK-003 — engine service OVERDUE

last service: 89,200 km  |  current: 102,450 km

dispatcher: when did this happen?

dispatcher: checking spreadsheet...

dispatcher: cross-referencing driver logs...

dispatcher: calling maintenance yard...

truck already out on a 400-mile run.

The typical fleet runs on three disconnected systems: a maintenance spreadsheet in the shop, a driver roster in HR, and a route board with the dispatcher. None of them update the others. An overdue oil change on truck FP-TRK-003 stays invisible until the truck calls in from the highway.

FleetPulse solves the coordination problem by making everything a first-class data entity. Vehicles, drivers, maintenance records, telematics readings, and alerts all live in one database with proper foreign keys and validation — not separate files with no connection between them.

A scheduler runs every hour, finds any maintenance task that's past its due date, marks it OVERDUE, and raises an alert on the dashboard — exactly once, no matter how many times it runs. The operations dashboard refreshes its KPIs automatically; alerts can be resolved with one click.

What changed

  • Overdue maintenance caught before the truck leaves the yard
  • One source of truth instead of three disconnected files
  • Alerts fire once — no repeated noise for known issues
  • Full service history with cost tracking and audit trail
  • Dashboard KPIs refresh live; resolve alerts without reloading

Java design highlights

  • Java 21 records as immutable DTOs
  • Text blocks for multi-line JPQL queries
  • Service-interface / Impl separation (DIP)
  • @Transactional(readOnly) default strategy
  • Idempotent alert generation with repo guard
01

Domain model & data layer

Five JPA entities, five Spring Data repositories, Flyway versioned schema with full seed data

// entity relationships

Vehicleid · regNumber · make · modelstatus · fuelType · mileageDriverid · employeeId · namelicenseExpiry · statusassignedMaintenanceRecordtype · status · scheduledDatecost · technicianNameTelematicsReadingmileage · fuelLevel · speedAlerttype · severity · resolved
02

Service layer & scheduler

Interface/Impl separation enforces DIP. Scheduler runs hourly — idempotent by design.

// MaintenanceService.markOverdueRecords()

@Transactional
public int markOverdueRecords() {
  LocalDate today = LocalDate.now();
  List<MaintenanceRecord> overdue =
    repository.findOverdueRecords(today);
  overdue.forEach(r ->
    r.setStatus(OVERDUE));
  return overdue.size(); // batch updated
}

// AlertService.generateMaintenanceAlerts()

public void generateMaintenanceAlerts() {
  repository.findOverdue().forEach(r -> {
    // idempotency guard — no duplicate alerts
    var existing = alertRepo
      .findActiveAlertsForVehicleAndType(
        r.getVehicle().getId(), MAINTENANCE_DUE);
    if (existing.isEmpty()) {
      alertRepo.save(buildAlert(r)); // fire once
    }
  });
}
03

Operations dashboard

Thymeleaf + Chart.js — KPI cards, maintenance cost trend (6 months), fleet status doughnut, live alerts table

⬡ FleetPulseOperations Dashboardlocalhost:8080

Total Vehicles

8

Active

5

In Maintenance

2

Overdue Tasks

1

Upcoming 30d

4

Open Alerts

3

// maintenance cost trend — last 6 months

$4k$3k$2k$1kOctNovDecJanFebMar

// fleet status

8
Active5
In Maint2
Retired1

// unresolved alerts

VehicleTypeSeverityMessage
FP-TRK-003MAINTENANCE_DUECRITICALEngine service overdue by 13,250 km
FP-VAN-007LICENSE_EXPIRYHIGHDriver license expires in 12 days
FP-SUV-002FUEL_LOWMEDIUMFuel level at 12% — schedule refuel
04

REST API & Swagger

25+ endpoints across 4 controllers — all annotated with OpenAPI 3 descriptions, auto-documented at /swagger-ui.html

Vehicles

GET/api/vehicles
POST/api/vehicles
PATCH/api/vehicles/{id}/assign-driver/{dId}
DELETE/api/vehicles/{id}

Maintenance

POST/api/maintenance
PATCH/api/maintenance/{id}/complete
GET/api/maintenance/overdue
GET/api/maintenance/upcoming

Alerts

GET/api/alerts/unresolved
GET/api/alerts/count
PATCH/api/alerts/{id}/resolve

Drivers

GET/api/drivers/available
POST/api/drivers
PUT/api/drivers/{id}
05

Testing & build

16 tests across 4 suites — 0 failures

$ mvn clean verify

FleetPulseApplicationTest1 passed
VehicleServiceTest6 passed
MaintenanceServiceTest5 passed
VehicleApiControllerTest4 passed
BUILD SUCCESSTests run: 16, Failures: 0, Errors: 0, Skipped: 0

Run locally

git clone github.com/Sankartk/fleetpulse
cd fleetpulse
mvn spring-boot:run

H2 in-memory DB, seeded automatically. No setup required.

Explore the app

localhost:8080/Operations Dashboardlocalhost:8080/swagger-ui.htmlInteractive API docslocalhost:8080/h2-consoleDatabase console

Docker

docker compose up -d

Starts the app + PostgreSQL. Switch profiles via SPRING_PROFILES_ACTIVE=postgres.