From dcf0f921e838dcd9c0c1404c2d2e95a66ccc5cfe Mon Sep 17 00:00:00 2001 From: Yoel Kidane <76665883+yoelkidane@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:41:23 -0600 Subject: [PATCH] Updated Trip Planner Trip Planner test cases fully working. UI still must be changed to be on the right of grid box. Map is not added yet note: may need the new "HomePage.tsx" to work idk --- TripPlanner.tsx | 219 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 TripPlanner.tsx diff --git a/TripPlanner.tsx b/TripPlanner.tsx new file mode 100644 index 0000000..fabeeaf --- /dev/null +++ b/TripPlanner.tsx @@ -0,0 +1,219 @@ +import React, { useState } from "react"; +import { + Flex, + ComboBox, + Item, + View, + Button, + Heading, + Divider, + Text, + useAsyncList, + Tabs, + TabList, + TabPanels, + Content +} from '@adobe/react-spectrum'; + +interface Place { + type: string; + name: string; + id: string; + lat: number; + lon: number; +} + +interface Trip { + id: string; + duration: string; + segments: Array<{ + mode: string; + from: string; + to: string; + start_time: string; + end_time: string; + }>; +} + +const usePlaceSuggestions = () => { + return useAsyncList({ + async load({ signal, filterText }) { + if (!filterText) return { items: [] }; + const response: Response = await fetch(`http://motis.metroll.live/api/v1/geocode?text=${filterText}`, { signal }); + const data = await response.json(); + const places = data.map((pl: Place) => ({ + type: pl.type, + name: pl.name, + id: pl.id || null, + lat: pl.lat, + lon: pl.lon + })); + return { items: places.slice(0, 5) }; + } + }); +}; + +const getTripSuggestions = async (fromPlace: string, toPlace: string): Promise => { + const currentTime = new Date().toISOString(); + const response = await fetch(`http://motis.metroll.live/api/v1/plan?time=${currentTime}&fromPlace=${fromPlace}&toPlace=${toPlace}&arriveBy=false`); + + if (!response.ok) { + console.error("Error fetching trip data:", response.status); + return []; + } + + const data = await response.json(); + if (!data.itineraries || data.itineraries.length === 0) { + return []; + } + + return data.itineraries.slice(0, 5).map((itinerary: any) => ({ + id: itinerary.id || "N/A", + duration: itinerary.duration || "N/A", + segments: itinerary.legs.map((leg: any) => ({ + mode: leg.mode, + from: leg.from.name || "N/A", + to: leg.to.name || "N/A", + start_time: leg.startTime || "N/A", + end_time: leg.endTime || "N/A" + })) + })); +}; + +const TripPlanner: React.FC = () => { + const [fromPlace, setFromPlace] = useState(null); + const [toPlace, setToPlace] = useState(null); + const [trips, setTrips] = useState([]); + const [selectedTripIndex, setSelectedTripIndex] = useState(0); + const [error, setError] = useState(null); + + const fromPlaceSuggestions = usePlaceSuggestions(); + const toPlaceSuggestions = usePlaceSuggestions(); + + const handlePlanTripButton = async () => { + setError(null); + + if (!fromPlace && !toPlace) { + setError("Both origin and destination are required."); + } else if (!fromPlace) { + setError("Origin is required."); + } else if (!toPlace) { + setError("Destination is required."); + } else { + const tripData = await getTripSuggestions(fromPlace.id, toPlace.id); + if (tripData.length > 0) { + setTrips(tripData); + setSelectedTripIndex(0); + } else { + setTrips([]); + setError("No trip found. Please try different locations."); + } + } + + + + + }; + + const formatDuration = (seconds: number): string => { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + let formattedDuration = ""; + + if (hours > 0) { + formattedDuration += `${hours} hour${hours > 1 ? 's' : ''}`; + } + + if (minutes > 0) { + if (hours > 0) formattedDuration += " "; + formattedDuration += `${minutes} minute${minutes > 1 ? 's' : ''}`; + } + + return formattedDuration || "0 minutes"; + }; + + return ( + + + Trip Planner + + + + { + const selectedItem = fromPlaceSuggestions.items.find(item => item.id === key); + setFromPlace(selectedItem || null); + }} + loadingState={fromPlaceSuggestions.loadingState} + placeholder="Select origin" + width="100%" + > + {(item) => {item.name}} + + + { + const selectedItem = toPlaceSuggestions.items.find(item => item.id === key); + setToPlace(selectedItem || null); + }} + loadingState={toPlaceSuggestions.loadingState} + placeholder="Select destination" + width="100%" + > + {(item) => {item.name}} + + + + + + + + {error ? ( + Error: {error} + ) : trips.length > 0 ? ( + setSelectedTripIndex(Number(key))}> + + {trips.map((_, index) => ( + Trip {index + 1} + ))} + + + {trips.map((trip, index) => ( + + + Trip Details + Duration: {formatDuration(Number(trip.duration))} + + {trip.segments.map((segment, segIndex) => ( + + + {segIndex === 0 ? "START: " : ""}{segment.mode}{segIndex === trip.segments.length - 1 ? " END" : ""} - {new Date(segment.start_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + +
{segment.from}
to
{segment.to}
+ +
+ ))} +
+
+ ))} +
+
+ ) : ( + Enter an Origin and Destination to receive a planned trip. + )} +
+
+ ); +}; + +export default TripPlanner;