/** * Resource Detail Modal * * Shows resource details including a map of the staff member's * current location when they are en route or in progress. */ import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { GoogleMap, useJsApiLoader, Marker } from '@react-google-maps/api'; import { Resource } from '../types'; import { useResourceLocation, useLiveResourceLocation } from '../hooks/useResourceLocation'; import Portal from './Portal'; import { X, MapPin, Navigation, Clock, User as UserIcon, Activity, Loader2, AlertCircle, } from 'lucide-react'; interface ResourceDetailModalProps { resource: Resource; onClose: () => void; } const mapContainerStyle = { width: '100%', height: '300px', borderRadius: '0.5rem', }; const defaultCenter = { lat: 39.8283, // Center of US lng: -98.5795, }; const ResourceDetailModal: React.FC = ({ resource, onClose }) => { const { t } = useTranslation(); const googleMapsApiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY || ''; const hasApiKey = googleMapsApiKey.length > 0; // Fetch location data const { data: location, isLoading, error } = useResourceLocation(resource.id); // Connect to live updates when tracking is active useLiveResourceLocation(resource.id, { enabled: location?.isTracking === true, }); // Load Google Maps API only if we have a key // When no API key, we skip the hook entirely to avoid warnings const shouldLoadMaps = hasApiKey; const { isLoaded: mapsLoaded, loadError: mapsLoadError } = useJsApiLoader({ googleMapsApiKey: shouldLoadMaps ? googleMapsApiKey : 'SKIP_LOADING', }); // Treat missing API key as if maps failed to load const effectiveMapsLoaded = shouldLoadMaps && mapsLoaded; const effectiveMapsError = !shouldLoadMaps || mapsLoadError; // Map center based on location const mapCenter = useMemo(() => { if (location?.hasLocation && location.latitude && location.longitude) { return { lat: location.latitude, lng: location.longitude }; } return defaultCenter; }, [location]); // Format timestamp const formattedTimestamp = useMemo(() => { if (!location?.timestamp) return null; const date = new Date(location.timestamp); return date.toLocaleString(); }, [location?.timestamp]); // Status color based on job status const statusColor = useMemo(() => { if (!location?.activeJob) return 'gray'; switch (location.activeJob.status) { case 'EN_ROUTE': return 'yellow'; case 'IN_PROGRESS': return 'blue'; default: return 'gray'; } }, [location?.activeJob]); return (
{/* Header */}

{resource.name}

{t('resources.staffMember', 'Staff Member')}

{/* Content */}
{/* Active Job Status */} {location?.activeJob && (
{location.activeJob.statusDisplay}
{location.activeJob.title}
{location.isTracking && ( {t('resources.liveTracking', 'Live')} )}
)} {/* Map Section */}

{t('resources.currentLocation', 'Current Location')}

{isLoading ? (
) : error ? (

{t('resources.locationError', 'Failed to load location')}

) : !location?.hasLocation ? (

{location?.message || t('resources.noLocationData', 'No location data available')}

{t('resources.locationHint', 'Location will appear when staff is en route')}

) : effectiveMapsError ? ( // Fallback when Google Maps isn't available - show coordinates

{t('resources.gpsCoordinates', 'GPS Coordinates')}

{location.latitude?.toFixed(6)}, {location.longitude?.toFixed(6)}

{location.speed !== undefined && location.speed !== null && (

{t('resources.speed', 'Speed')}: {(location.speed * 2.237).toFixed(1)} mph

)} {location.heading !== undefined && location.heading !== null && (

{t('resources.heading', 'Heading')}: {location.heading.toFixed(0)}°

)}
{t('resources.openInMaps', 'Open in Google Maps')}
) : effectiveMapsLoaded ? ( {location.latitude && location.longitude && ( )} ) : (
)}
{/* Location Details */} {location?.hasLocation && (
{t('resources.lastUpdate', 'Last Update')}
{formattedTimestamp || '-'}
{location.accuracy && (
{t('resources.accuracy', 'Accuracy')}
{location.accuracy < 1000 ? `${location.accuracy.toFixed(0)}m` : `${(location.accuracy / 1000).toFixed(1)}km`}
)} {location.speed !== undefined && location.speed !== null && (
{t('resources.speed', 'Speed')}
{(location.speed * 2.237).toFixed(1)} mph
)} {location.heading !== undefined && location.heading !== null && (
{t('resources.heading', 'Heading')}
{location.heading.toFixed(0)}°
)}
)}
{/* Footer */}
); }; export default ResourceDetailModal;