diff --git a/frontend/src/components/DriverLocationModal.tsx b/frontend/src/components/DriverLocationModal.tsx
new file mode 100644
index 0000000..60ddad2
--- /dev/null
+++ b/frontend/src/components/DriverLocationModal.tsx
@@ -0,0 +1,147 @@
+import { X, Navigation, Battery, Compass, Clock } from 'lucide-react';
+import { MapContainer, TileLayer, Marker } from 'react-leaflet';
+import L from 'leaflet';
+import 'leaflet/dist/leaflet.css';
+import { formatDistanceToNow } from 'date-fns';
+import type { DriverLocation } from '@/types/gps';
+
+interface DriverLocationModalProps {
+ driverLocation: DriverLocation | null;
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+// Custom driver marker icon (same as GpsTracking page)
+const createDriverIcon = () => {
+ return L.divIcon({
+ className: 'custom-driver-marker',
+ html: `
+
+ `,
+ iconSize: [32, 32],
+ iconAnchor: [16, 32],
+ popupAnchor: [0, -32],
+ });
+};
+
+function getCourseDirection(course: number | null): string {
+ if (course === null || course === undefined) return 'N/A';
+ const directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
+ const index = Math.round(course / 45) % 8;
+ return `${directions[index]} (${Math.round(course)}°)`;
+}
+
+export function DriverLocationModal({ driverLocation, isOpen, onClose }: DriverLocationModalProps) {
+ if (!isOpen || !driverLocation) return null;
+
+ const hasLocation = driverLocation.location !== null;
+
+ return (
+
+
+ {/* Header */}
+
+
+
{driverLocation.driverName}
+ {driverLocation.driverPhone && (
+
{driverLocation.driverPhone}
+ )}
+
+
+
+
+ {/* Map */}
+ {hasLocation ? (
+ <>
+
+
+
+
+
+
+
+ {/* Info Grid */}
+
+
+
+
+
Speed
+
+ {driverLocation.location!.speed?.toFixed(1) || '0'} mph
+
+
+
+
+
+
+
Heading
+
+ {getCourseDirection(driverLocation.location!.course)}
+
+
+
+
+
+
+
Battery
+
+ {driverLocation.location!.battery !== null
+ ? `${Math.round(driverLocation.location!.battery)}%`
+ : 'N/A'}
+
+
+
+
+
+
+
Last Seen
+
+ {formatDistanceToNow(new Date(driverLocation.location!.timestamp), { addSuffix: true })}
+
+
+
+
+ >
+ ) : (
+
+
+
No location data available for this driver.
+
The driver may not have reported their position yet.
+
+ )}
+
+
+ );
+}
diff --git a/frontend/src/components/GpsIndicatorDot.tsx b/frontend/src/components/GpsIndicatorDot.tsx
new file mode 100644
index 0000000..6d3c6e7
--- /dev/null
+++ b/frontend/src/components/GpsIndicatorDot.tsx
@@ -0,0 +1,37 @@
+import type { DriverLocation } from '@/types/gps';
+
+interface GpsIndicatorDotProps {
+ driverLocation: DriverLocation | undefined;
+ onClick: (loc: DriverLocation) => void;
+ className?: string;
+}
+
+const TEN_MINUTES = 10 * 60 * 1000;
+
+export function GpsIndicatorDot({ driverLocation, onClick, className = '' }: GpsIndicatorDotProps) {
+ // Don't render if driver has no GPS enrollment
+ if (!driverLocation) return null;
+
+ const isRecentlyActive = driverLocation.lastActive &&
+ (Date.now() - new Date(driverLocation.lastActive).getTime()) < TEN_MINUTES;
+
+ return (
+
+ );
+}
diff --git a/frontend/src/pages/CommandCenter.tsx b/frontend/src/pages/CommandCenter.tsx
index fd543be..7e5944c 100644
--- a/frontend/src/pages/CommandCenter.tsx
+++ b/frontend/src/pages/CommandCenter.tsx
@@ -17,11 +17,16 @@ import {
Activity,
Calendar,
} from 'lucide-react';
-import { useEffect, useState, useRef } from 'react';
+import { useEffect, useState, useRef, useMemo } from 'react';
import { Link } from 'react-router-dom';
+import { formatDistanceToNow } from 'date-fns';
import { DriverChatBubble } from '@/components/DriverChatBubble';
import { DriverChatModal } from '@/components/DriverChatModal';
+import { GpsIndicatorDot } from '@/components/GpsIndicatorDot';
+import { DriverLocationModal } from '@/components/DriverLocationModal';
import { useUnreadCounts, useDriverResponseCheck } from '@/hooks/useSignalMessages';
+import { useDriverLocations } from '@/hooks/useGps';
+import type { DriverLocation } from '@/types/gps';
interface Event {
id: string;
@@ -89,6 +94,8 @@ export function CommandCenter() {
const [lastRefresh, setLastRefresh] = useState(new Date());
const [chatDriver, setChatDriver] = useState(null);
const [isChatOpen, setIsChatOpen] = useState(false);
+ const [locationDriver, setLocationDriver] = useState(null);
+ const [isLocationOpen, setIsLocationOpen] = useState(false);
// Refs for smooth auto-scrolling
const activeScrollRef = useRef(null);
@@ -121,6 +128,24 @@ export function CommandCenter() {
setChatDriver(null);
};
+ const openLocationModal = (loc: DriverLocation) => {
+ setLocationDriver(loc);
+ setIsLocationOpen(true);
+ };
+
+ const closeLocationModal = () => {
+ setIsLocationOpen(false);
+ setLocationDriver(null);
+ };
+
+ // GPS driver locations
+ const { data: driverLocations } = useDriverLocations();
+ const driverLocationMap = useMemo(() => {
+ const map = new Map();
+ driverLocations?.forEach(loc => map.set(loc.driverId, loc));
+ return map;
+ }, [driverLocations]);
+
const { data: events, refetch: refetchEvents } = useQuery({
queryKey: ['events'],
queryFn: async () => {
@@ -643,6 +668,11 @@ export function CommandCenter() {
{trip.driver.name}
+
{trip.driver.name}
+
openChat(trip.driver!)}
@@ -877,8 +912,14 @@ export function CommandCenter() {
{vehicle.name}
-
+
{activeEvent?.driver?.name || 'Driver'}
+ {activeEvent?.driver && (
+
+ )}
);
@@ -888,42 +929,66 @@ export function CommandCenter() {
- {/* Quick Links */}
-
-
-
Quick Actions
-
-
-
-
- Events
-
-
-
- Drivers
-
-
-
- VIPs
-
-
-
- Vehicles
-
-
-
+ {/* Active Drivers (GPS) */}
+ {(() => {
+ const TEN_MINUTES = 10 * 60 * 1000;
+ const activeDrivers = (driverLocations || []).filter(loc =>
+ loc.lastActive && (Date.now() - new Date(loc.lastActive).getTime()) < TEN_MINUTES && loc.location
+ );
+ return (
+
+
+
+
+
Active Drivers
+
+
{activeDrivers.length}
+
+
+ {activeDrivers.length === 0 ? (
+
+
No active GPS drivers
+
+ ) : (
+
+ {activeDrivers.map((loc) => (
+
+ ))}
+
+ )}
+
+ {/* Quick Action Links - compact icon row */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ })()}
{/* Driver Chat Modal */}
@@ -932,6 +997,13 @@ export function CommandCenter() {
isOpen={isChatOpen}
onClose={closeChat}
/>
+
+ {/* Driver Location Modal */}
+
);
}