/ frontend / app / components / routing-input.js
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  }