import React, { useRef, useState, useEffect } from 'react'; import { TripData } from '../types'; import { Download, Map as MapIcon, AlertTriangle, ImageOff, Loader2, Navigation, RefreshCw } from 'lucide-react'; import html2canvas from 'html2canvas'; import { getEnvVar } from '../utils/envUtils'; interface TripMapProps { tripData: TripData; } const TripMap: React.FC = ({ tripData }) => { const mapContainerRef = useRef(null); const [imgError, setImgError] = useState(false); const [encodedPolyline, setEncodedPolyline] = useState(null); const [isRouting, setIsRouting] = useState(false); const [scriptLoaded, setScriptLoaded] = useState(false); const [routingError, setRoutingError] = useState(null); const [retryCount, setRetryCount] = useState(0); // STRICT MODE: Use VITE_GOOGLE_MAPS_KEY const getEffectiveKey = () => { // 1. Check manual override from user input if (tripData.googleMapsKey) return tripData.googleMapsKey; // 2. Check Vite env return getEnvVar('VITE_GOOGLE_MAPS_KEY'); }; const apiKey = getEffectiveKey(); // Load script if not present useEffect(() => { if ((window as any).google?.maps) { setScriptLoaded(true); return; } if (!apiKey) return; if (document.querySelector(`script[src*="maps.googleapis.com/maps/api/js"]`)) { const check = setInterval(() => { if ((window as any).google?.maps) { setScriptLoaded(true); clearInterval(check); } }, 500); return; } const script = document.createElement('script'); script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,geometry&loading=async&v=weekly`; script.async = true; script.onload = () => setScriptLoaded(true); document.head.appendChild(script); }, [apiKey]); // Calculate Route useEffect(() => { if (!scriptLoaded || !tripData.startPoint.place || !tripData.endPoint.place) return; if (!(window as any).google) return; const fetchRoute = async () => { setIsRouting(true); setRoutingError(null); try { const directionsService = new (window as any).google.maps.DirectionsService(); const waypoints = tripData.stops .filter(s => s.place && s.place.trim().length > 2) .map(s => ({ location: s.place, stopover: true })); const gMaps = (window as any).google.maps; const mode = tripData.travelMode === 'WALKING' ? gMaps.TravelMode.WALKING : gMaps.TravelMode.DRIVING; const result = await new Promise((resolve, reject) => { directionsService.route({ origin: tripData.startPoint.place, destination: tripData.endPoint.place, waypoints: waypoints, travelMode: mode, }, (response: any, status: any) => { if (status === 'OK') { resolve(response); } else { reject(status); } }); }); setEncodedPolyline(result.routes[0].overview_polyline); setImgError(false); } catch (error: any) { console.error("Directions Service Failed:", error); if (error === 'REQUEST_DENIED') { setRoutingError("API 'Directions API' nie jest włączone."); } else if (error === 'ZERO_RESULTS') { setRoutingError(`Nie znaleziono drogi pomiędzy punktami.`); } else { setRoutingError(`Błąd wyznaczania trasy: ${error}`); } setEncodedPolyline(null); } finally { setIsRouting(false); } }; fetchRoute(); }, [scriptLoaded, tripData.startPoint.place, tripData.endPoint.place, tripData.stops, tripData.travelMode, retryCount]); const getMapUrl = () => { if (!apiKey) return null; const baseUrl = 'https://maps.googleapis.com/maps/api/staticmap'; const size = '600x400'; const scale = '2'; const format = 'png'; const maptype = 'roadmap'; const startPlace = tripData.startPoint.place; const endPlace = tripData.endPoint.place; if (!startPlace || !endPlace) return null; const startMarker = `markers=color:green|label:S|${encodeURIComponent(startPlace)}`; const endMarker = `markers=color:red|label:F|${encodeURIComponent(endPlace)}`; const stopMarkers = tripData.stops .filter(s => s.place.trim() !== '') .map((s, i) => `markers=color:blue|label:${i+1}|${encodeURIComponent(s.place)}`) .join('&'); let path = ''; if (encodedPolyline && encodedPolyline.length < 8000) { path = `path=color:0xEA4420ff|weight:5|enc:${encodedPolyline}`; } else { const pathPoints = [ startPlace, ...tripData.stops.filter(s => s.place.trim() !== '').map(s => s.place), endPlace ].map(p => encodeURIComponent(p)).join('|'); path = `path=color:0xEA4420ff|weight:5|${pathPoints}`; } let url = `${baseUrl}?size=${size}&scale=${scale}&format=${format}&maptype=${maptype}&${startMarker}&${endMarker}&${path}&key=${apiKey}`; if (stopMarkers) url += `&${stopMarkers}`; return url; }; const mapUrl = getMapUrl(); const handleDownload = async () => { if (!mapContainerRef.current) return; try { const canvas = await html2canvas(mapContainerRef.current, { useCORS: true, allowTaint: true, backgroundColor: '#ffffff' }); const link = document.createElement('a'); link.download = 'trasa-wycieczki.png'; link.href = canvas.toDataURL('image/png'); link.click(); } catch (e) { alert("Nie udało się pobrać mapy."); } }; if (!apiKey) { return (

Brak Klucza API

Skonfiguruj zmienną VITE_GOOGLE_MAPS_KEY w panelu.

); } return (

Mapa Trasy

{routingError && ( )}
{routingError && (

Info Trasy:

{routingError}

)}
{mapUrl && !imgError && !isRouting ? ( Mapa trasy setImgError(true)} /> ) : (
{isRouting ? ( <>

Rysowanie trasy...

) : imgError ? (

Błąd ładowania mapy

) : (

{tripData.startPoint.place ? 'Czekam na dane...' : 'Uzupełnij punkty trasy...'}

)}
)} {!imgError && mapUrl && !isRouting && (
Generated by PromptStory
)}
); }; export default TripMap;