From a3e1f1e826e5de24f867475364bea90b918fc87c Mon Sep 17 00:00:00 2001 From: Shanmuga Krishnan S M Date: Mon, 27 Jul 2026 13:51:41 +0530 Subject: [PATCH] Implement active Location Sharing Stopped/Ended state with red dot pin --- .../controller/BusLocationController.java | 14 +++++++++++ .../com/rit/portal/model/BusLocation.java | 1 + .../portal/service/BusLocationService.java | 11 +++++++- src/components/BusRouteMap/BusRouteMap.tsx | 25 +++++++++++++------ 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/com/rit/portal/controller/BusLocationController.java b/backend/src/main/java/com/rit/portal/controller/BusLocationController.java index 878a10e..8619ee3 100644 --- a/backend/src/main/java/com/rit/portal/controller/BusLocationController.java +++ b/backend/src/main/java/com/rit/portal/controller/BusLocationController.java @@ -37,6 +37,20 @@ public class BusLocationController { return ResponseEntity.ok(Map.of("status", "success", "message", "Location updated successfully")); } + @PostMapping("/{routeNumber}/stop") + public ResponseEntity> stopLocation( + @PathVariable String routeNumber, + @RequestBody Map body) { + + String pin = body != null ? body.get("pin") : null; + if (!REQUIRED_PIN.equals(pin)) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("error", "Invalid driver PIN")); + } + + busLocationService.stopLocation(routeNumber); + return ResponseEntity.ok(Map.of("status", "success", "message", "Location sharing stopped")); + } + @GetMapping public ResponseEntity> getActiveLocations() { return ResponseEntity.ok(busLocationService.getActiveLocations()); diff --git a/backend/src/main/java/com/rit/portal/model/BusLocation.java b/backend/src/main/java/com/rit/portal/model/BusLocation.java index 10d1702..aff41bf 100644 --- a/backend/src/main/java/com/rit/portal/model/BusLocation.java +++ b/backend/src/main/java/com/rit/portal/model/BusLocation.java @@ -13,4 +13,5 @@ public class BusLocation { private Double latitude; private Double longitude; private Instant lastUpdated; + private boolean stopped = false; } diff --git a/backend/src/main/java/com/rit/portal/service/BusLocationService.java b/backend/src/main/java/com/rit/portal/service/BusLocationService.java index 40b803b..ebda89f 100644 --- a/backend/src/main/java/com/rit/portal/service/BusLocationService.java +++ b/backend/src/main/java/com/rit/portal/service/BusLocationService.java @@ -19,10 +19,19 @@ public class BusLocationService { routeNumber, latitude, longitude, - Instant.now() + Instant.now(), + false )); } + public void stopLocation(String routeNumber) { + BusLocation loc = locationCache.get(routeNumber); + if (loc != null) { + loc.setStopped(true); + loc.setLastUpdated(Instant.now()); + } + } + public BusLocation getLocation(String routeNumber) { BusLocation loc = locationCache.get(routeNumber); if (loc != null) { diff --git a/src/components/BusRouteMap/BusRouteMap.tsx b/src/components/BusRouteMap/BusRouteMap.tsx index 55b46f3..31566d7 100644 --- a/src/components/BusRouteMap/BusRouteMap.tsx +++ b/src/components/BusRouteMap/BusRouteMap.tsx @@ -56,7 +56,7 @@ const liveBusIcon = L.divIcon({ }); const stoppedBusIcon = L.divIcon({ - html: `
🚌
`, + html: `
🚌
`, className: '', iconSize: [36, 36], iconAnchor: [18, 18], @@ -81,7 +81,7 @@ interface BusRouteMapProps { const DEFAULT_CENTER = [13.0118, 80.0214]; // RIT Campus default export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapProps) { - const [allLiveLocations, setAllLiveLocations] = useState>({}); + const [allLiveLocations, setAllLiveLocations] = useState>({}); useEffect(() => { const fetchLiveLocations = () => { @@ -90,12 +90,17 @@ export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapPro if (res.ok) return res.json(); return []; }) - .then((data: Array<{ routeNumber: string; latitude: number; longitude: number; lastUpdated: string }>) => { + .then((data: Array<{ routeNumber: string; latitude: number; longitude: number; lastUpdated: string; stopped: boolean }>) => { if (Array.isArray(data)) { - const locMap: Record = {}; + const locMap: Record = {}; data.forEach((item) => { if (item.routeNumber && item.latitude && item.longitude) { - locMap[item.routeNumber] = { latitude: item.latitude, longitude: item.longitude, lastUpdated: item.lastUpdated }; + locMap[item.routeNumber] = { + latitude: item.latitude, + longitude: item.longitude, + lastUpdated: item.lastUpdated, + stopped: item.stopped + }; } }); setAllLiveLocations(locMap); @@ -180,7 +185,7 @@ export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapPro {Object.entries(allLiveLocations).map(([rNum, loc]) => { if (selectedRoute && selectedRoute.number !== rNum) return null; - const isStale = loc.lastUpdated ? (new Date().getTime() - new Date(loc.lastUpdated).getTime() > 120000) : false; + const isStale = loc.stopped || (loc.lastUpdated ? (new Date().getTime() - new Date(loc.lastUpdated).getTime() > 120000) : false); return (
- {isStale ? ( + {loc.stopped ? ( + + LOCATION SHARING ENDED + + ) : isStale ? ( LOCATION SHARING STOPPED @@ -201,7 +210,7 @@ export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapPro )}

Bus {rNum}

- {isStale ? "Last known location" : "Broadcasting live coordinates"} + {loc.stopped ? "Sharing ended by driver" : isStale ? "Last known location" : "Broadcasting live coordinates"}