routing-input.js
1 import React, {useRef, useState} from "react"; 2 import {Autocomplete, DirectionsRenderer, GoogleMap, useJsApiLoader,} from "@react-google-maps/api"; 3 import flatPale from "../map-styles/flat-pale.json"; 4 import Sliders from "@/app/components/sliders"; 5 import Options from "@/app/components/options"; 6 import {CopyToClipboard} from "react-copy-to-clipboard/src"; 7 import Message from "@/app/components/message"; 8 9 const TOKEN = process.env.GOOGLE_MAPS_ACCESS_TOKEN; 10 11 const containerStyle = { 12 width: "100%", 13 height: "100%", 14 }; 15 16 const center = { 17 lat: 52.377956, 18 lng: 4.89707, 19 }; 20 21 export default function RoutingInput({ 22 tripInformation, 23 setTripInformation, 24 currentUrl, 25 }) { 26 const { isLoaded } = useJsApiLoader({ 27 id: "e3b5ba4bb308558e", 28 googleMapsApiKey: TOKEN, 29 libraries: ["places"], 30 }); 31 32 const [copied, setCopied] = useState(false); 33 34 const [map, setMap] = useState(null); 35 const [directionsResponse, setDirectionsResponse] = useState(null); 36 37 const originRef = useRef(null); 38 const destinationRef = useRef(null); 39 40 async function calculateRoute() { 41 if (originRef.current.value === "" || destinationRef.current.value === "") { 42 return; 43 } 44 // eslint-disable-next-line no-undef 45 const directionsService = new google.maps.DirectionsService(); 46 const results = await directionsService.route({ 47 origin: originRef.current.value, 48 destination: destinationRef.current.value, 49 // eslint-disable-next-line no-undef 50 travelMode: google.maps.TravelMode.DRIVING, 51 }); 52 setDirectionsResponse(results); 53 54 setTripInformation((update) => { 55 update.distance_kilometer = Math.floor( 56 results.routes[0].legs[0].distance.value / 1000 57 ); 58 }); 59 setTripInformation((update) => { 60 update.time_minutes = Math.floor( 61 results.routes[0].legs[0].duration.value / 60 62 ); 63 }); 64 } 65 66 function onCopied() { 67 setCopied(true); 68 const timeout = setTimeout(() => { 69 setCopied(false); 70 }, 3000); 71 72 return () => { 73 clearTimeout(timeout); 74 }; 75 } 76 77 const onLoad = React.useCallback(function callback(map) { 78 setMap(map); 79 }, []); 80 81 const onUnmount = React.useCallback(function callback(map) { 82 setMap(null); 83 }, []); 84 85 return isLoaded ? ( 86 <section className="body-font relative text-gray-600"> 87 <div className="absolute inset-0 bg-gray-300"> 88 <GoogleMap 89 mapContainerStyle={containerStyle} 90 center={center} 91 zoom={12} 92 onLoad={onLoad} 93 onUnmount={onUnmount} 94 options={{ 95 zoomControl: false, 96 streetViewControl: false, 97 mapTypeControl: false, 98 fullscreenControl: false, 99 styles: flatPale, 100 }} 101 > 102 {directionsResponse && ( 103 <DirectionsRenderer directions={directionsResponse} /> 104 )} 105 <></> 106 </GoogleMap> 107 </div> 108 109 <div className="container mx-auto flex px-5 py-20"> 110 <div className="relative z-10 mt-10 flex w-full flex-col rounded-lg bg-white p-8 shadow-md md:ml-auto md:mt-0 md:w-1/2 lg:w-1/3"> 111 {copied && <Message message={"Deelbare link gekopieerd!"}></Message>} 112 <div className="flex justify-between"> 113 <h2 className="title-font mb-1 text-lg font-medium text-gray-900"> 114 Waar wil je naar toe? 115 </h2> 116 <div className="absolute right-6 top-5"> 117 <CopyToClipboard text={currentUrl} onCopy={onCopied}> 118 <button className="ml-4 inline-flex h-10 w-14 items-center justify-center rounded-full border-0 bg-gray-200 p-0 text-gray-500"> 119 <svg 120 xmlns="http://www.w3.org/2000/svg" 121 fill="none" 122 viewBox="0 0 24 24" 123 strokeWidth="1.5" 124 stroke="currentColor" 125 className="h-5 w-5" 126 > 127 <path 128 strokeLinecap="round" 129 strokeLinejoin="round" 130 d="M11.35 3.836c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m8.9-4.414c.376.023.75.05 1.124.08 1.131.094 1.976 1.057 1.976 2.192V16.5A2.25 2.25 0 0118 18.75h-2.25m-7.5-10.5H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V18.75m-7.5-10.5h6.375c.621 0 1.125.504 1.125 1.125v9.375m-8.25-3l1.5 1.5 3-3.75" 131 /> 132 </svg> 133 </button> 134 </CopyToClipboard> 135 </div> 136 </div> 137 <p className="mb-5 mt-2 leading-relaxed text-gray-600"> 138 Vul hier je start en eind adres in om de voordeligste 139 deel-vervoerder te kiezen! 140 </p> 141 <ol className="border-gray-250 relative border-l dark:border-gray-700"> 142 <li className="mb-5 ml-4"> 143 <div className="absolute -left-1.5 mt-1.5 h-3 w-3 rounded-full border border-white bg-gray-200 dark:border-gray-900 dark:bg-gray-700"></div> 144 <div className="relative mb-4"> 145 <label 146 htmlFor="start-address" 147 className="text-base leading-7 text-gray-600" 148 > 149 Start adres 150 </label> 151 <div className="relative"> 152 <div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> 153 <svg 154 aria-hidden="true" 155 className="h-5 w-5 text-gray-500 dark:text-gray-400" 156 fill="none" 157 stroke="currentColor" 158 viewBox="0 0 24 24" 159 xmlns="http://www.w3.org/2000/svg" 160 > 161 <path 162 strokeLinecap="round" 163 strokeLinejoin="round" 164 strokeWidth="2" 165 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" 166 ></path> 167 </svg> 168 </div> 169 <Autocomplete 170 onPlaceChanged={calculateRoute} 171 restrictions={{ country: ["nl", "de", "be"] }} 172 > 173 <input 174 type="search" 175 id="start-address" 176 className="block w-full rounded-lg border border-gray-300 bg-gray-50 p-4 pl-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" 177 placeholder="Amsterdam" 178 ref={originRef} 179 required 180 /> 181 </Autocomplete> 182 </div> 183 </div> 184 </li> 185 <li className="ml-4"> 186 <div className="absolute -left-1.5 mt-1.5 h-3 w-3 rounded-full border border-white bg-gray-200 dark:border-gray-900 dark:bg-gray-700"></div> 187 <div className="relative mb-2"> 188 <label 189 htmlFor="end-address" 190 className="text-base leading-7 text-gray-600" 191 > 192 Eind adres 193 </label> 194 <div className="relative"> 195 <div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> 196 <svg 197 aria-hidden="true" 198 className="h-5 w-5 text-gray-500 dark:text-gray-400" 199 fill="none" 200 stroke="currentColor" 201 viewBox="0 0 24 24" 202 xmlns="http://www.w3.org/2000/svg" 203 > 204 <path 205 strokeLinecap="round" 206 strokeLinejoin="round" 207 strokeWidth="2" 208 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" 209 ></path> 210 </svg> 211 </div> 212 <Autocomplete 213 onPlaceChanged={calculateRoute} 214 restrictions={{ country: ["nl", "de", "be"] }} 215 > 216 <input 217 type="search" 218 id="end-address" 219 className="block w-full rounded-lg border border-gray-300 bg-gray-50 p-4 pl-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" 220 placeholder="Den Haag" 221 ref={destinationRef} 222 required 223 /> 224 </Autocomplete> 225 </div> 226 </div> 227 </li> 228 </ol> 229 <Options 230 tripInformation={tripInformation} 231 setTripInformation={setTripInformation} 232 /> 233 <Sliders 234 tripInformation={tripInformation} 235 setTripInformation={setTripInformation} 236 /> 237 </div> 238 </div> 239 </section> 240 ) : ( 241 <div>Loading map</div> 242 ); 243 }