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'; 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); // --- HARDCODED FALLBACK KEY --- const AUTO_PASTE_KEY = 'AIzaSyAq9IgZswt5j7GGfH2s-ESenHmfvWFCFCg'; // Directly access the environment variable OR fallback to manual input OR auto-paste const getEffectiveKey = () => { // 1. Check manual override if (tripData.googleMapsKey) return tripData.googleMapsKey; // 2. Check Vite env // @ts-ignore if (import.meta.env && import.meta.env.VITE_GOOGLE_MAPS_KEY) { // @ts-ignore return import.meta.env.VITE_GOOGLE_MAPS_KEY; } // 3. Check Standard process.env if (process.env.GOOGLE_MAPS_KEY) return process.env.GOOGLE_MAPS_KEY; // 4. Fallback return AUTO_PASTE_KEY; }; const apiKey = getEffectiveKey(); // Load script if not present (e.g. refreshed on Result page) useEffect(() => { if ((window as any).google?.maps) { setScriptLoaded(true); return; } if (!apiKey) return; // Check if script exists in DOM 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 using Directions Service 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(); // Prepare valid waypoints (exclude empty stops) const waypoints = tripData.stops .filter(s => s.place && s.place.trim().length > 2) .map(s => ({ location: s.place, stopover: true })); // Determine Travel Mode const gMaps = (window as any).google.maps; const mode = tripData.travelMode === 'WALKING' ? gMaps.TravelMode.WALKING : gMaps.TravelMode.DRIVING; console.log("TripMap: Requesting route...", { origin: tripData.startPoint.place, dest: tripData.endPoint.place, mode: tripData.travelMode, waypointsCount: waypoints.length }); 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 { console.warn("TripMap: Directions API Error", status); reject(status); } }); }); // Extract overview polyline const polyline = result.routes[0].overview_polyline; setEncodedPolyline(polyline); setImgError(false); } catch (error: any) { console.error("Directions Service Failed:", error); // Specific Error Handling if (error === 'REQUEST_DENIED') { setRoutingError("API 'Directions API' nie jest włączone w Google Cloud. Mapa pokazuje linię prostą."); } else if (error === 'ZERO_RESULTS') { const modeName = tripData.travelMode === 'WALKING' ? 'pieszej' : 'samochodowej'; setRoutingError(`Nie znaleziono drogi ${modeName} pomiędzy tymi punktami.`); } else { setRoutingError(`Błąd wyznaczania trasy: ${error}`); } setEncodedPolyline(null); // Fallback to straight lines } finally { setIsRouting(false); } }; fetchRoute(); }, [scriptLoaded, tripData.startPoint.place, tripData.endPoint.place, tripData.stops, tripData.travelMode, retryCount]); // Construct Google Static Maps URL const getMapUrl = () => { if (!apiKey) return null; const baseUrl = 'https://maps.googleapis.com/maps/api/staticmap'; const size = '600x400'; const scale = '2'; // Retina const format = 'png'; const maptype = 'roadmap'; const startPlace = tripData.startPoint.place; const endPlace = tripData.endPoint.place; if (!startPlace || !endPlace) return null; // Markers const startMarker = `markers=color:green|label:S|${encodeURIComponent(startPlace)}`; const endMarker = `markers=color:red|label:F|${encodeURIComponent(endPlace)}`; // Stop Markers 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) { console.error("Download failed", e); alert("Nie udało się pobrać mapy."); } }; // ERROR STATE 1: MISSING KEY if (!apiKey) { return (

Brak Klucza API

Wprowadź klucz w kroku "Szczegóły" lub dodaj go do pliku .env.

); } return (

Mapa Trasy

{routingError && ( )}
{/* Warning Banner for Directions API issues */} {routingError && (

Widzisz prostą linię zamiast drogi?

{routingError}

{routingError.includes("Directions API") ? <>Rozwiązanie: Wejdź w Google Cloud Console → APIs & Services → Włącz "Directions API". : <>Jeśli idziesz szlakiem, upewnij się, że wybrałeś tryb "Pieszo".}

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

Rysowanie dokładnej trasy ({tripData.travelMode})...

) : imgError ? (

Błąd ładowania obrazka mapy (Static Maps API)

Możliwe przyczyny błędu "g.co/staticmaperror":

  • Maps Static API nie jest włączone w konsoli Google Cloud (To inne API niż Places/JavaScript!).
  • Brak podpiętej karty płatniczej w projekcie Google Cloud.
  • Klucz API: {apiKey.slice(0,6)}... jest niepoprawny lub ma restrykcje HTTP, które blokują serwer zdjęć.
) : (

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

)}
)} {/* Branding Overlay */} {!imgError && mapUrl && !isRouting && (
Generated by PromptStory
)}
{/* Helper Text below map */}

{encodedPolyline && encodedPolyline.length < 8000 ? `*Trasa wyznaczona automatycznie (${tripData.travelMode === 'WALKING' ? 'szlaki/chodniki' : 'drogi'}).` : "*Trasa uproszczona (linia prosta) - włącz Directions API lub zmień tryb."}

); }; export default TripMap;