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.
// 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
Domain model & data layer
Five JPA entities, five Spring Data repositories, Flyway versioned schema with full seed data
// entity relationships
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
}
});
}Operations dashboard
Thymeleaf + Chart.js — KPI cards, maintenance cost trend (6 months), fleet status doughnut, live alerts table
Total Vehicles
8
Active
5
In Maintenance
2
Overdue Tasks
1
Upcoming 30d
4
Open Alerts
3
// maintenance cost trend — last 6 months
// fleet status
// unresolved alerts
REST API & Swagger
25+ endpoints across 4 controllers — all annotated with OpenAPI 3 descriptions, auto-documented at /swagger-ui.html
Vehicles
Maintenance
Alerts
Drivers
Testing & build
16 tests across 4 suites — 0 failures
$ mvn clean verify
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
Docker
docker compose up -d
Starts the app + PostgreSQL. Switch profiles via SPRING_PROFILES_ACTIVE=postgres.