From 8793793ddca0238d7e57f4fe9e968d9fefa6b9d2 Mon Sep 17 00:00:00 2001 From: Yoel Kidane <76665883+yoelkidane@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:00:40 -0600 Subject: [PATCH 1/2] All Changes All the changes I made including departures and trip-planner from other branches. Updated HomePage.tsx and TransitBar.tsx --- src/components/Departures.tsx | 164 ++++++++++++++++++++++ src/components/Routes.tsx | 12 ++ src/components/TopNavBar.tsx | 46 +++---- src/components/TransitBar.tsx | 48 ++++--- src/components/TripPlanner.css | 32 +++++ src/components/TripPlanner.tsx | 219 ++++++++++++++++++++++++++++++ src/components/TripPlannerOld.tsx | 186 +++++++++++++++++++++++++ src/components/test.tsx | 53 ++++++++ src/index.css | 26 ++-- src/index.tsx | 38 +++--- src/pages/HomePage.tsx | 99 +++++++++----- src/react-app-env.d.ts | 2 +- src/reportWebVitals.ts | 30 ++-- src/setupTests.ts | 10 +- 14 files changed, 834 insertions(+), 131 deletions(-) create mode 100644 src/components/Departures.tsx create mode 100644 src/components/Routes.tsx create mode 100644 src/components/TripPlanner.css create mode 100644 src/components/TripPlanner.tsx create mode 100644 src/components/TripPlannerOld.tsx create mode 100644 src/components/test.tsx diff --git a/src/components/Departures.tsx b/src/components/Departures.tsx new file mode 100644 index 0000000..d71fa8b --- /dev/null +++ b/src/components/Departures.tsx @@ -0,0 +1,164 @@ +import React, { useState } from 'react'; +import { + ComboBox, + Item, + Button, + Flex, + View, + Heading, + Text, + Divider, + useAsyncList, +} from '@adobe/react-spectrum'; +import moment from 'moment'; + +interface Stop { + id: string; + name: string; + latitude: number; + longitude: number; +} + +interface Departure { + routeShortName: string; + headsign: string; + departure: string; +} + +const useStopSuggestions = () => { + return useAsyncList({ + async load({ filterText, signal }) { + if (!filterText) return { items: [] }; + const res: Response = await fetch(`http://motis.metroll.live/api/v1/geocode?text=${filterText}`, { signal }); + const data = await res.json(); + return { items: data.slice(0, 5) }; + }, + }); +}; + +// Function to fetch stations near the user's location +const fetchNearbyStations = async (latitude: number, longitude: number) => { + try { + // Example API call - update with actual endpoint if necessary + const res = await fetch(`http://motis.metroll.live/api/v1/stations?lat=${latitude}&lng=${longitude}`); + const data = await res.json(); + + console.log('Nearby stations:', data); + return data; // Return station data + } catch (error) { + console.error('Error fetching nearby stations:', error); + return []; + } +}; + +// Function to fetch departures for a given stop +const fetchDepartures = async (stopId: string) => { + const res = await fetch( + `http://motis.metroll.live/api/v1/stoptimes?stopId=${stopId}&time=${new Date().toISOString()}&arriveBy=false&n=10` + ); + const data = await res.json(); + return data.stopTimes || []; +}; + +const Departures: React.FC = () => { + const [selectedStop, setSelectedStop] = useState(null); + const [departures, setDepartures] = useState([]); + const [error, setError] = useState(null); + const [locationError, setLocationError] = useState(null); + const stopSuggestions = useStopSuggestions(); + + const handleGetLocation = async () => { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + async (position) => { + const { latitude, longitude } = position.coords; + try { + // Fetch nearest stations based on the user's location + const nearbyStations = await fetchNearbyStations(latitude, longitude); + console.log('Nearby Stations:', nearbyStations); + setLocationError(null); // Reset location error + // You can select the nearest station or update your UI here + setSelectedStop(nearbyStations[0] || null); // Example: Select the first station + } catch { + setLocationError('Failed to fetch nearby stations.'); + } + }, + (err) => { + setLocationError('Geolocation error: ' + err.message); + } + ); + } else { + setLocationError('Geolocation is not supported by this browser.'); + } + }; + + const handleGetDepartures = async () => { + if (!selectedStop) { + setError('Please select a stop.'); + return; + } + + try { + setError(null); + const departures = await fetchDepartures(selectedStop.id); + console.log('Fetched Departures:', departures); + setDepartures(departures); + } catch { + setError('Failed to load departures.'); + } + }; + + const formatTime = (time: string) => moment.utc(time).local().format('DD/MM/YYYY HH:mm:ss'); + + return ( + + + Stop Departures + + + + + {locationError && Error: {locationError}} + + setSelectedStop(stopSuggestions.items.find(item => item.id === key) || null)} + loadingState={stopSuggestions.loadingState} + placeholder="Select a stop" + width="100%" + > + {(item) => {item.name}} + + + + + + + {error && Error: {error}} + {!error && departures.length === 0 && Enter a stop to receive information.} + {departures.map((departure, idx) => { + console.log(`Departure ${idx + 1}:`, departure); + return ( + + + Route: {departure.routeShortName}
+ Headsign: {departure.headsign}
+ Departure: {formatTime(departure.departure)} +
+ +
+ ); + })} +
+
+ ); +}; + +export default Departures; diff --git a/src/components/Routes.tsx b/src/components/Routes.tsx new file mode 100644 index 0000000..227d37e --- /dev/null +++ b/src/components/Routes.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +const Routes: React.FC = () => { + return ( +
+

Routes

+

This is the routes page content.

+
+ ); +}; + +export default Routes; diff --git a/src/components/TopNavBar.tsx b/src/components/TopNavBar.tsx index c041aaf..0e33fca 100644 --- a/src/components/TopNavBar.tsx +++ b/src/components/TopNavBar.tsx @@ -1,23 +1,23 @@ -import React from 'react'; -import {Flex, Heading, MenuTrigger, ActionButton, Menu, Item} from '@adobe/react-spectrum'; - -// Nishil -// Implements the top navigation bar -const TopNavBar: React.FC = () => { - return ( - - - Metroll - - - alert(key)}*/> - Login/Sign In - Home - About - - - - ); -} - -export default TopNavBar; +import React from 'react'; +import {Flex, Heading, MenuTrigger, ActionButton, Menu, Item} from '@adobe/react-spectrum'; + +// Nishil +// Implements the top navigation bar +const TopNavBar: React.FC = () => { + return ( + + + Metroll + + + alert(key)}*/> + Login/Sign In + Home + About + + + + ); +} + +export default TopNavBar; \ No newline at end of file diff --git a/src/components/TransitBar.tsx b/src/components/TransitBar.tsx index b222715..ba08325 100644 --- a/src/components/TransitBar.tsx +++ b/src/components/TransitBar.tsx @@ -1,18 +1,30 @@ -import React from 'react'; -import {Flex, Item, ActionGroup} from '@adobe/react-spectrum'; - -// Nishil -// Implements the three transit use case buttons -const TransitBar: React.FC = () => { - return ( - - - Departures - Trip Planner - Routes - - - ); -} - -export default TransitBar; +import React from 'react'; +import { Flex, ActionGroup, Item } from '@adobe/react-spectrum'; + +interface TransitBarProps { + onSelect: (selectedView: string) => void; // Define the expected prop type +} + +const TransitBar: React.FC = ({ onSelect }) => { + return ( + + { + const key = Array.from(selected)[0] as string; // Correctly extract and cast the selected key + if (key) { + onSelect(key); // Pass the selected key to onSelect + } + }} + > + Departures + Trip Planner + Routes + + + ); +} + +export default TransitBar; diff --git a/src/components/TripPlanner.css b/src/components/TripPlanner.css new file mode 100644 index 0000000..9c0ee8e --- /dev/null +++ b/src/components/TripPlanner.css @@ -0,0 +1,32 @@ +.autocomplete-container { + position: relative; +} + +.suggestions { + border: 1px solid #ccc; /* Optional: Add border for better visibility */ + background-color: white; /* Background color for the dropdown */ + z-index: 1000; /* Make sure it's on top of other elements */ + max-height: 200px; /* Optional: Limit the height */ + overflow-y: auto; /* Optional: Add scroll if there are many items */ +} + +.suggestion-item { + padding: 8px; /* Padding for each suggestion item */ + cursor: pointer; /* Change cursor to pointer on hover */ + color: black; /* Set text color to black */ +} + +.suggestion-item:hover { + background-color: #f0f0f0; /* Change background on hover */ +} + +.map-container { + height: 100%; + display: flex; + flex-direction: column; +} + +.full-map { + height: 400px; /* or another height as needed */ + width: 100%; +} diff --git a/src/components/TripPlanner.tsx b/src/components/TripPlanner.tsx new file mode 100644 index 0000000..fabeeaf --- /dev/null +++ b/src/components/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; diff --git a/src/components/TripPlannerOld.tsx b/src/components/TripPlannerOld.tsx new file mode 100644 index 0000000..75b290c --- /dev/null +++ b/src/components/TripPlannerOld.tsx @@ -0,0 +1,186 @@ +// Worked on by Yoel and Nishil + +import React, { useState, useEffect, useRef } from 'react'; +import { Flex, TextField, Button, View, Heading } from '@adobe/react-spectrum'; +import './TripPlanner.css'; // Import custom CSS for styling + +interface SuggestionProps { + name: string; + lat: number; + lon: number; + id: string; +} +const TripPlannerOld: React.FC = () => { + + + const [startLocation, setStartLocation] = useState(''); + const [endLocation, setEndLocation] = useState(''); + const [tripDetails, setTripDetails] = useState(null); + const [startSuggestions, setStartSuggestions] = useState([]); + const [endSuggestions, setEndSuggestions] = useState([]); + const [startDetails, setStartDetails] = useState<{ lat: number; lon: number; id: string } | null>(null); + const [endDetails, setEndDetails] = useState<{ lat: number; lon: number; id: string } | null>(null); + + // Refs for suggestion containers + const startSuggestionsRef = useRef(null); + const endSuggestionsRef = useRef(null); + + // Fetch location suggestions (Station Guesser) + const fetchLocationSuggestions = async (input: string) => { + const response = await fetch(`http://motis.metroll.live/api/v1/geocode?text=${input}`); + const data = await response.json(); + return data.map((loc: { name: string; lat: number; lon: number; id: string }) => ({ + name: loc.name, + lat: loc.lat, + lon: loc.lon, + id: loc.id, + })); + }; + + // Effect to fetch suggestions for start location + useEffect(() => { + if (startLocation) { + const getSuggestions = async () => { + const suggestions = await fetchLocationSuggestions(startLocation); + setStartSuggestions(suggestions); + }; + getSuggestions(); + } else { + setStartSuggestions([]); // Clear suggestions if input is empty + } + }, [startLocation]); + + // Effect to fetch suggestions for end location + useEffect(() => { + if (endLocation) { + const getSuggestions = async () => { + const suggestions = await fetchLocationSuggestions(endLocation); + setEndSuggestions(suggestions); + }; + getSuggestions(); + } else { + setEndSuggestions([]); // Clear suggestions if input is empty + } + }, [endLocation]); + + // Fetch trip itinerary using MOTIS + const fetchTripItinerary = async () => { + + if (!startDetails?.id || !endDetails?.id) { + alert('Please select a valid start and end location'); + return; + } + + // Fetch trip details if ids are valid + const response = await fetch( + `http://motis.metroll.live/api/v1/plan?time=2024-10-28T01%3A22%3A00.000Z&fromPlace=${startDetails.id}&toPlace=${endDetails.id}&arriveBy=false&timetableView=true&wheelchair=false&mode=TRANSIT,WALK`, + { method: 'GET' } + ); + + const data = await response.json(); + if (data) { + setTripDetails(data); // Set trip details if data is valid + } else { + setTripDetails(null); // Clear trip details if no valid trip data is found + } + + }; + + // Handle clicks outside of suggestion boxes to close them + const handleClickOutside = (event: MouseEvent) => { + if (startSuggestionsRef.current && !startSuggestionsRef.current.contains(event.target as Node)) { + setStartSuggestions([]); + } + if (endSuggestionsRef.current && !endSuggestionsRef.current.contains(event.target as Node)) { + setEndSuggestions([]); + } + }; + + useEffect(() => { + // Add event listener for click events + document.addEventListener('mousedown', handleClickOutside); + return () => { + // Clean up the event listener on component unmount + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( + + {/* Left Side: Trip Planner Form */} + + Trip Planner + +
+ + {startSuggestions.length > 0 && ( +
+ {startSuggestions.map((suggestion, index) => ( +
{ + setStartLocation(suggestion.name); + setStartDetails({ lat: suggestion.lat, lon: suggestion.lon, id: suggestion.id }); + setStartSuggestions([]); // Clear suggestions on selection + }} + > + {suggestion.name} +
+ ))} +
+ )} +
+ +
+ + {endSuggestions.length > 0 && ( +
+ {endSuggestions.map((suggestion, index) => ( +
{ + setEndLocation(suggestion.name); + setEndDetails({ lat: suggestion.lat, lon: suggestion.lon, id: suggestion.id }); + setEndSuggestions([]); // Clear suggestions on selection + }} + > + {suggestion.name} +
+ ))} +
+ )} +
+ + +
+
+ + {/* Right Side: Trip Details */} + + Trip Details + {tripDetails && ( + + {/* testing if working */} + {
{JSON.stringify(tripDetails, null, 2)}
} +
+ )} +
+
+ ); +}; + +export default TripPlannerOld; diff --git a/src/components/test.tsx b/src/components/test.tsx new file mode 100644 index 0000000..0fec083 --- /dev/null +++ b/src/components/test.tsx @@ -0,0 +1,53 @@ +// Nicholas +// trip planner ui + +import React, { useState } from 'react'; +import { ComboBox, Item, Flex } from '@adobe/react-spectrum'; +import Search from '@spectrum-icons/workflow/Search'; + +const StopSearchDropdown = () => { +// // Initial empty list of stops +// const [allStops, setAllStops] = useState([]); +// const [filteredStops, setFilteredStops] = useState([]); +// +// const handleInputChange = (inputValue: string) => { +// // Generate results dynamically based on input +// if (inputValue) { +// const generatedResults = Array.from({ length: 5 }, (_, index) => ({ +// id: `${index + 1}`, +// name: `${inputValue} Result ${index + 1}` +// })); +// setFilteredStops(generatedResults); +// } else { +// setFilteredStops([]); // Clear results if input is empty +// } +// }; +// +// return ( +// +// {/* Start Point ComboBox */} +// +// {item => {item.name}} +// +// +// {/* End Point ComboBox */} +// +// {item => {item.name}} +// +// +// ); +}; +export default StopSearchDropdown; + diff --git a/src/index.css b/src/index.css index ec2585e..bd5bd6d 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,13 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/src/index.tsx b/src/index.tsx index f449765..eefaec2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,19 +1,19 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -); -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; + +const root = ReactDOM.createRoot( + document.getElementById('root') as HTMLElement +); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); \ No newline at end of file diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 3aea0b3..f240ec0 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,37 +1,62 @@ -import React from 'react'; -import TopNavBar from '../components/TopNavBar'; -import {Provider, defaultTheme, Grid, View} from "@adobe/react-spectrum"; -import TransitBar from "../components/TransitBar"; - -// Nishil -// Arranges home page layout using react-spectrum and grid -const HomePage: React.FC = () => { - return ( - - - - - - - - - - - - - - ); -} - -export default HomePage; +import React, { useState } from 'react'; +import TopNavBar from '../components/TopNavBar'; +import { Provider, defaultTheme, Grid, View } from '@adobe/react-spectrum'; +import TransitBar from "../components/TransitBar"; +import TripPlanner from '../components/TripPlanner'; +import Departures from '../components/Departures'; +import Routes from '../components/Routes'; + + +// Nishil and Yoel +// Arranges home page layout using react-spectrum and grid +const HomePage: React.FC = () => { + const [selectedView, setSelectedView] = useState('planner'); + + const renderContent = () => { + switch (selectedView) { + case 'departures': + return ; // Load Departures on Click + case 'routes': + return ; // Load Routes on Click + case 'planner': + default: + return null; // Do not render anything here, as TripPlanner is already in the sidebar + } + }; + + + return ( + + + + + + + + + + {/* Sidebar can contain additional content if needed */} + {selectedView === 'planner' && } + + + {/* Load content to be clicked */renderContent()} + + + + + ); +} + +export default HomePage; + diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 6431bc5..ece12df 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/src/reportWebVitals.ts b/src/reportWebVitals.ts index 49a2a16..f5ae799 100644 --- a/src/reportWebVitals.ts +++ b/src/reportWebVitals.ts @@ -1,15 +1,15 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; +import { ReportHandler } from 'web-vitals'; + +const reportWebVitals = (onPerfEntry?: ReportHandler) => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/src/setupTests.ts b/src/setupTests.ts index 8f2609b..141e479 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -1,5 +1,5 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; From 2f3a6fa53e03be34451de5d983cb780aac2240dc Mon Sep 17 00:00:00 2001 From: NishilJ <78994772+NishilJ@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:13:55 -0600 Subject: [PATCH 2/2] runnable for TA test --- src/components/Departures.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/Departures.tsx b/src/components/Departures.tsx index d71fa8b..4d6c56f 100644 --- a/src/components/Departures.tsx +++ b/src/components/Departures.tsx @@ -10,7 +10,6 @@ import { Divider, useAsyncList, } from '@adobe/react-spectrum'; -import moment from 'moment'; interface Stop { id: string; @@ -54,7 +53,7 @@ const fetchNearbyStations = async (latitude: number, longitude: number) => { // Function to fetch departures for a given stop const fetchDepartures = async (stopId: string) => { const res = await fetch( - `http://motis.metroll.live/api/v1/stoptimes?stopId=${stopId}&time=${new Date().toISOString()}&arriveBy=false&n=10` + `http://motis.metroll.live/api/v1/stoptimes?stopId=${stopId}&arriveBy=false&n=10` ); const data = await res.json(); return data.stopTimes || []; @@ -108,7 +107,7 @@ const Departures: React.FC = () => { } }; - const formatTime = (time: string) => moment.utc(time).local().format('DD/MM/YYYY HH:mm:ss'); + //const formatTime = (time: string) => utc(time).local().format('DD/MM/YYYY HH:mm:ss'); return ( @@ -150,7 +149,7 @@ const Departures: React.FC = () => { Route: {departure.routeShortName}
Headsign: {departure.headsign}
- Departure: {formatTime(departure.departure)} + Departure: {departure.departure}