import React, { useEffect, useRef, useState } from 'react'; import mapboxgl from 'mapbox-gl'; import 'mapbox-gl/dist/mapbox-gl.css'; import { MapPin, Target } from 'lucide-react'; interface MapboxMapProps { initialLat?: number; initialLng?: number; onLocationChange?: (lat: number, lng: number) => void; height?: string; } export const MapboxMap: React.FC = ({ initialLat = -23.5505, // São Paulo como padrão initialLng = -46.6333, onLocationChange, height = '400px' }) => { const mapContainer = useRef(null); const map = useRef(null); const marker = useRef(null); const [currentLat, setCurrentLat] = useState(initialLat); const [currentLng, setCurrentLng] = useState(initialLng); const [mapLoaded, setMapLoaded] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (!mapContainer.current || map.current) return; try { console.log('🗺️ Inicializando mapa Mapbox...'); // Configurar token const token = import.meta.env.VITE_MAPBOX_TOKEN; if (!token) { setError('❌ Token do Mapbox não encontrado. Configure VITE_MAPBOX_TOKEN no arquivo .env.local'); return; } mapboxgl.accessToken = token; // Inicializar mapa map.current = new mapboxgl.Map({ container: mapContainer.current, style: 'mapbox://styles/mapbox/streets-v12', center: [initialLng, initialLat], zoom: 15 }); console.log('✅ Mapa criado com sucesso'); // Adicionar controles de navegação map.current.addControl(new mapboxgl.NavigationControl(), 'top-right'); // Adicionar controle de localização map.current.addControl( new mapboxgl.GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true, showUserHeading: true }), 'top-right' ); // Criar marcador arrastável marker.current = new mapboxgl.Marker({ color: '#c5a059', draggable: true }) .setLngLat([initialLng, initialLat]) .addTo(map.current); // Evento quando o marcador é arrastado marker.current.on('dragend', () => { if (marker.current) { const lngLat = marker.current.getLngLat(); setCurrentLat(lngLat.lat); setCurrentLng(lngLat.lng); if (onLocationChange) { onLocationChange(lngLat.lat, lngLat.lng); } } }); // Evento de clique no mapa para mover o marcador map.current.on('click', (e) => { if (marker.current) { marker.current.setLngLat([e.lngLat.lng, e.lngLat.lat]); setCurrentLat(e.lngLat.lat); setCurrentLng(e.lngLat.lng); if (onLocationChange) { onLocationChange(e.lngLat.lat, e.lngLat.lng); } } }); map.current.on('load', () => { setMapLoaded(true); }); } catch (error: any) { console.error('Erro ao inicializar mapa:', error); const errorMsg = error?.message || String(error); if (errorMsg.includes('token') || errorMsg.includes('Unauthorized') || errorMsg.includes('401')) { setError('❌ Token do Mapbox inválido. Obtenha um token gratuito em https://account.mapbox.com/ e configure em services/mapboxService.ts'); } else { setError(`Erro ao carregar o mapa: ${errorMsg}`); } } // Cleanup return () => { if (marker.current) { marker.current.remove(); } if (map.current) { map.current.remove(); map.current = null; } }; }, []); // Executar apenas uma vez // Atualizar posição do marcador quando as coordenadas mudarem externamente useEffect(() => { if (marker.current && map.current && mapLoaded) { marker.current.setLngLat([initialLng, initialLat]); map.current.flyTo({ center: [initialLng, initialLat], zoom: 15, duration: 1500 }); setCurrentLat(initialLat); setCurrentLng(initialLng); } }, [initialLat, initialLng, mapLoaded]); const centerOnMarker = () => { if (map.current && marker.current) { const lngLat = marker.current.getLngLat(); map.current.flyTo({ center: [lngLat.lng, lngLat.lat], zoom: 17, duration: 1000 }); } }; return (
{error && (
{error}
)}
{/* Info overlay - Responsivo */}

Coordenadas

{currentLat.toFixed(4)}, {currentLng.toFixed(4)}

{/* Botão de centralizar - Responsivo */} {/* Instruções - Responsivo */}

💡 Como usar:

  • Arraste o marcador para a posição exata
  • Clique no mapa para mover o marcador
  • Use os controles para zoom e navegação
); };