import React, { useMemo, useEffect, useRef } from "react"; import { Loader } from "@googlemaps/js-api-loader"; interface GoogleMapWithRadiusProps { latitude?: number | string; longitude?: number | string; mapOptions?: { zoom?: number; }; } // Utility function to safely convert coordinates to numbers const safeParseNumber = (value: number | string | undefined): number | null => { if (value === undefined || value === null || value === "") return null; const num = typeof value === "string" ? parseFloat(value) : value; return !isNaN(num) && isFinite(num) ? num : null; }; // 2 miles in meters for radius circle const RADIUS_METERS = 2 * 1609.34; const GoogleMapWithRadius: React.FC = ({ latitude: rawLatitude, longitude: rawLongitude, mapOptions = {}, }) => { // Convert coordinates to numbers safely const latitude = safeParseNumber(rawLatitude); const longitude = safeParseNumber(rawLongitude); // Destructure mapOptions to create stable references const { zoom = 12 } = mapOptions; // Get API key and Map ID from environment const apiKey = process.env.REACT_APP_GOOGLE_MAPS_PUBLIC_API_KEY; const mapId = process.env.REACT_APP_GOOGLE_MAPS_MAP_ID; // Refs for map container and instances const mapRef = useRef(null); const mapInstanceRef = useRef(null); const circleRef = useRef(null); // Memoize map center const mapCenter = useMemo(() => { if (latitude === null || longitude === null) return null; return { lat: latitude, lng: longitude }; }, [latitude, longitude]); // Initialize map useEffect(() => { if (!apiKey || !mapRef.current || !mapCenter) return; const initializeMap = async () => { const loader = new Loader({ apiKey, version: "weekly", }); try { await loader.importLibrary("maps"); if (!mapRef.current) return; // Create map configuration const mapConfig: google.maps.MapOptions = { mapId: mapId, center: mapCenter, zoom: zoom, maxZoom: 15, // Prevent users from zooming too close zoomControl: true, mapTypeControl: false, scaleControl: true, streetViewControl: false, rotateControl: false, fullscreenControl: false, }; const map = new google.maps.Map(mapRef.current, mapConfig); mapInstanceRef.current = map; // Create circle overlay const circle = new google.maps.Circle({ center: mapCenter, radius: RADIUS_METERS, fillColor: "#6c757d", fillOpacity: 0.2, strokeColor: "#6c757d", strokeOpacity: 0.8, strokeWeight: 2, map: map, }); circleRef.current = circle; } catch (error) { console.error("Failed to load Google Maps:", error); } }; initializeMap(); // Cleanup function return () => { if (circleRef.current) { circleRef.current.setMap(null); } mapInstanceRef.current = null; }; }, [apiKey, mapId, mapCenter, zoom]); // Update map center and circle when coordinates change useEffect(() => { if (!mapInstanceRef.current || !circleRef.current || !mapCenter) return; mapInstanceRef.current.setCenter(mapCenter); circleRef.current.setCenter(mapCenter); }, [mapCenter]); // Handle case where no coordinates are available if (latitude === null || longitude === null || mapCenter === null) { return (
Location

Map unavailable

); } return (
Location
); }; export default GoogleMapWithRadius;