import React, { useEffect, useRef, useState } from 'react'; import mapboxgl from 'mapbox-gl'; import 'mapbox-gl/dist/mapbox-gl.css'; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"; import { Badge } from "@/components/ui/badge"; import { MapPin, 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 = React.memo(({ center, places = [], zoom = 14 }: MapProps) => { const mapContainer = useRef(null); const map = useRef(null); const markers = useRef([]); const [mapError, setMapError] = useState(null); useEffect(() => { if (!mapContainer.current) return; try { map.current = new mapboxgl.Map({ container: mapContainer.current, style: 'mapbox://styles/mapbox/streets-v12', center: [center.lng, center.lat], zoom: zoom }); // Add navigation control map.current.addControl(new mapboxgl.NavigationControl(), 'top-right'); // Clean up markers when component unmounts return () => { markers.current.forEach(marker => marker.remove()); map.current?.remove(); }; } catch (error) { console.error('Error initializing map:', error); setMapError('Failed to initialize map'); } }, [center.lat, center.lng, zoom]); useEffect(() => { if (!map.current) return; // Update center when it changes map.current.flyTo({ center: [center.lng, center.lat], essential: true }); // Clear existing markers markers.current.forEach(marker => marker.remove()); markers.current = []; // Add new markers places.forEach(place => { const el = document.createElement('div'); el.className = 'marker'; el.innerHTML = ''; el.style.color = 'hsl(var(--primary))'; el.style.width = '24px'; el.style.height = '24px'; el.style.cursor = 'pointer'; const marker = new mapboxgl.Marker(el) .setLngLat([place.location.lng, place.location.lat]) .setPopup( new mapboxgl.Popup({ offset: 25 }) .setHTML( `${place.name}${place.rating ? `
Rating: ${place.rating} ⭐ (${place.user_ratings_total} reviews)` : ''}` ) ) .addTo(map.current!); markers.current.push(marker); }); }, [center, places]); if (mapError) { return (
{mapError}
); } return
; }); MapComponent.displayName = 'MapComponent'; 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 ( ); } return ( {title} {places.length > 0 && ( Place Details
{places.map((place, index) => ( ))}
)}
); }; export { MapComponent, MapSkeleton, MapContainer, PlaceDetails };