import React, { useEffect, useRef } from 'react'; import mapboxgl from 'mapbox-gl'; import 'mapbox-gl/dist/mapbox-gl.css'; import { Badge } from "@/components/ui/badge"; import { Star } from 'lucide-react'; import { Skeleton } from "@/components/ui/skeleton"; mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN || ''; interface Location { lat: number; lng: number; } interface Place { name: string; location: Location; vicinity?: string; rating?: number; user_ratings_total?: number; } interface MapProps { center: Location; places?: Place[]; zoom?: number; } const MapComponent = ({ center, places = [], zoom = 14 }: MapProps) => { const mapRef = useRef(null); const mapInstance = useRef(null); const markersRef = useRef([]); // Initialize the map only once useEffect(() => { if (!mapRef.current || mapInstance.current) return; if (!mapboxgl.accessToken) { console.error('Mapbox access token is not set'); return; } mapInstance.current = new mapboxgl.Map({ container: mapRef.current, style: 'mapbox://styles/mapbox/standard', center: [center.lng, center.lat], zoom, }); return () => { mapInstance.current?.remove(); mapInstance.current = null; }; }, [center.lat, center.lng, zoom]); // Update map center when 'center' prop changes useEffect(() => { if (mapInstance.current) { mapInstance.current.flyTo({ center: [center.lng, center.lat], zoom, essential: true, }); } }, [center, zoom]); // Update markers when 'places' prop changes useEffect(() => { if (!mapInstance.current) return; // Remove existing markers markersRef.current.forEach((marker) => marker.remove()); markersRef.current = []; // Add new markers places.forEach((place) => { const marker = new mapboxgl.Marker() .setLngLat([place.location.lng, place.location.lat]) .setPopup( new mapboxgl.Popup({ offset: 25 }).setText( `${place.name}${place.vicinity ? `\n${place.vicinity}` : ''}` ) ) .addTo(mapInstance.current!); markersRef.current.push(marker); }); }, [places]); return (
); }; export default React.memo(MapComponent, (prevProps, nextProps) => { return ( prevProps.center.lat === nextProps.center.lat && prevProps.center.lng === nextProps.center.lng && prevProps.zoom === nextProps.zoom && JSON.stringify(prevProps.places) === JSON.stringify(nextProps.places) ); }); const MapSkeleton = () => ( ); const PlaceDetails = ({ place }: { place: Place }) => (

{place.name}

{place.vicinity &&

{place.vicinity}

}
{place.rating && ( {place.rating} ({place.user_ratings_total}) )}
); interface MapContainerProps { title: string; center: Location; places?: Place[]; loading?: boolean; } const MapContainer: React.FC = ({ title, center, places = [], loading = false, }) => { if (loading) { return (

Loading map...

); } return (

{title}

{places.map((place, index) => ( ))}
); }; export { MapComponent, MapSkeleton, MapContainer, PlaceDetails };