Implement active Location Sharing Stopped/Ended state with red dot pin

This commit is contained in:
Shanmuga Krishnan S M
2026-07-27 13:51:41 +05:30
parent def3a7c9ce
commit a3e1f1e826
4 changed files with 42 additions and 9 deletions

View File

@@ -37,6 +37,20 @@ public class BusLocationController {
return ResponseEntity.ok(Map.of("status", "success", "message", "Location updated successfully"));
}
@PostMapping("/{routeNumber}/stop")
public ResponseEntity<Map<String, String>> stopLocation(
@PathVariable String routeNumber,
@RequestBody Map<String, String> 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<List<BusLocation>> getActiveLocations() {
return ResponseEntity.ok(busLocationService.getActiveLocations());

View File

@@ -13,4 +13,5 @@ public class BusLocation {
private Double latitude;
private Double longitude;
private Instant lastUpdated;
private boolean stopped = false;
}

View File

@@ -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) {

View File

@@ -56,7 +56,7 @@ const liveBusIcon = L.divIcon({
});
const stoppedBusIcon = L.divIcon({
html: `<div class="relative flex items-center justify-center w-9 h-9 rounded-full bg-slate-500 border-2 border-white shadow-lg text-white text-sm font-bold">🚌</div>`,
html: `<div class="relative flex items-center justify-center w-9 h-9 rounded-full bg-red-500 border-2 border-white shadow-lg text-white text-sm font-bold">🚌</div>`,
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<Record<string, { latitude: number; longitude: number; lastUpdated: string }>>({});
const [allLiveLocations, setAllLiveLocations] = useState<Record<string, { latitude: number; longitude: number; lastUpdated: string; stopped: boolean }>>({});
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<string, { latitude: number; longitude: number; lastUpdated: string }> = {};
const locMap: Record<string, { latitude: number; longitude: number; lastUpdated: string; stopped: boolean }> = {};
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 (
<Marker
@@ -190,7 +195,11 @@ export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapPro
>
<Popup>
<div className="p-1 font-sans text-center">
{isStale ? (
{loc.stopped ? (
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-red-100 text-red-800 text-[10px] font-bold">
LOCATION SHARING ENDED
</span>
) : isStale ? (
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-100 text-slate-800 text-[10px] font-bold">
LOCATION SHARING STOPPED
</span>
@@ -201,7 +210,7 @@ export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapPro
)}
<h4 className="font-bold text-slate-800 text-xs mt-1">Bus {rNum}</h4>
<p className="text-[10px] text-slate-500 mt-0.5">
{isStale ? "Last known location" : "Broadcasting live coordinates"}
{loc.stopped ? "Sharing ended by driver" : isStale ? "Last known location" : "Broadcasting live coordinates"}
</p>
</div>
</Popup>