From 50456e6d80d0044380e87d5fb2d44d5c1d3e1ce5 Mon Sep 17 00:00:00 2001 From: tarunsinghofficial Date: Wed, 24 Jul 2024 20:52:58 +0530 Subject: [PATCH 1/7] added the api for trip details, created the tripdetailpane with all information --- package-lock.json | 22 ++++ package.json | 5 +- src/components/ArrivalDeparture.svelte | 26 ++++- src/components/oba/StopPane.svelte | 71 +++++++++++- src/components/oba/TripDetailsPane.svelte | 109 ++++++++++++++++++ src/lib/RestAPI/tripDetails.js | 24 ++++ .../api/oba/trip-details/[tripId]/+server.js | 44 +++++++ 7 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 src/components/oba/TripDetailsPane.svelte create mode 100644 src/lib/RestAPI/tripDetails.js create mode 100644 src/routes/api/oba/trip-details/[tripId]/+server.js diff --git a/package-lock.json b/package-lock.json index 192aae3..790dc0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "onebusaway-webapp", "version": "0.0.1", + "dependencies": { + "@fortawesome/free-regular-svg-icons": "^6.6.0" + }, "devDependencies": { "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", @@ -549,6 +552,25 @@ "node": ">=6" } }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz", + "integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", + "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", + "engines": { + "node": ">=6" + } + }, "node_modules/@fortawesome/free-solid-svg-icons": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz", diff --git a/package.json b/package.json index b389acf..2a6542a 100644 --- a/package.json +++ b/package.json @@ -34,5 +34,8 @@ "vite": "^5.0.3", "vitest": "^1.2.0" }, - "type": "module" + "type": "module", + "dependencies": { + "@fortawesome/free-regular-svg-icons": "^6.6.0" + } } diff --git a/src/components/ArrivalDeparture.svelte b/src/components/ArrivalDeparture.svelte index af135f5..e5b0cc4 100644 --- a/src/components/ArrivalDeparture.svelte +++ b/src/components/ArrivalDeparture.svelte @@ -3,6 +3,12 @@ export let tripHeadsign; export let scheduledArrivalTime; export let predictedArrivalTime; + export let tripId; + export let vehicleId; + export let serviceDate; + import { createEventDispatcher } from 'svelte'; + + const dispatch = createEventDispatcher(); function formatTime(time) { const date = new Date(time); @@ -45,10 +51,24 @@ return `${Math.floor((chosenTime - now) / 60000)}m`; } + + function handleTripDetail() { + dispatch('showTripDetails', { + tripId, + vehicleId, + serviceDate, + routeShortName, + tripHeadsign, + scheduledArrivalTime, + timeToReach: calculateTimeToReach(predictedArrivalTime, scheduledArrivalTime), + arrivalStatus: getArrivalStatus(predictedArrivalTime, scheduledArrivalTime) + }); + } -

@@ -66,4 +86,4 @@ {calculateTimeToReach(predictedArrivalTime, scheduledArrivalTime)}

-
+ diff --git a/src/components/oba/StopPane.svelte b/src/components/oba/StopPane.svelte index ca1b373..33f32e4 100644 --- a/src/components/oba/StopPane.svelte +++ b/src/components/oba/StopPane.svelte @@ -1,5 +1,8 @@
@@ -96,19 +110,70 @@ tripHeadsign={arrival.tripHeadsign} scheduledArrivalTime={arrival.scheduledArrivalTime} predictedArrivalTime={arrival.predictedArrivalTime} + tripId={arrival.tripId} + vehicleId={arrival.vehicleId} + serviceDate={arrival.serviceDate} + on:showTripDetails={handleShowTripDetails} /> {/each}
{/if} + + {#if showTripDetails} +
+
+ +
+
+
+

+ {selectedTripDetails.routeShortName} - {selectedTripDetails.tripHeadsign} +

+

+ {new Date(selectedTripDetails.scheduledArrivalTime).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit' + })} - + + {selectedTripDetails.arrivalStatus.text} + +

+
+

+ {selectedTripDetails.timeToReach} +

+
+
+ +
+
+ {/if} - diff --git a/src/components/oba/TripDetailsPane.svelte b/src/components/oba/TripDetailsPane.svelte new file mode 100644 index 0000000..42ff019 --- /dev/null +++ b/src/components/oba/TripDetailsPane.svelte @@ -0,0 +1,109 @@ + + +
+ {#if error} +

{error}

+ {:else if tripDetails} +

+ {#if routeInfo} + Route {routeInfo.shortName} - + {/if} +

+ {#if tripDetails.schedule && tripDetails.schedule.stopTimes && tripDetails.schedule.stopTimes.length > 0} +
+
+ {#each tripDetails.schedule.stopTimes as stop, index} +
+
+ + {#if index === currentStopIndex} + + {/if} +
+
+
+ {stopInfo[stop.stopId] ? stopInfo[stop.stopId].name : stop.stopId} +
+
{formatTime(stop.arrivalTime)}
+
+
+ {/each} +
+ {:else} +

No stop times available for this trip.

+ {/if} + {:else} +

Loading trip details...

+ {/if} +
diff --git a/src/lib/RestAPI/tripDetails.js b/src/lib/RestAPI/tripDetails.js new file mode 100644 index 0000000..722c93a --- /dev/null +++ b/src/lib/RestAPI/tripDetails.js @@ -0,0 +1,24 @@ +import { error, json } from '@sveltejs/kit'; +import { PUBLIC_OBA_SERVER_URL as baseURL } from '$env/static/public'; +import { PRIVATE_OBA_API_KEY as apiKey } from '$env/static/private'; + +export async function GET({ params, url }) { + const { tripId } = params; + const vehicleId = url.searchParams.get('vehicleId'); + const serviceDate = url.searchParams.get('serviceDate'); + + let apiURL = `${baseURL}/api/where/trip-details/${tripId}.json?key=${apiKey}`; + + if (vehicleId) apiURL += `&vehicleId=${vehicleId}`; + if (serviceDate) apiURL += `&serviceDate=${serviceDate}`; + + const response = await fetch(apiURL); + + if (!response.ok) { + error(500, 'Unable to fetch trip-details.'); + return; + } + + const data = await response.json(); + return json(data); +} diff --git a/src/routes/api/oba/trip-details/[tripId]/+server.js b/src/routes/api/oba/trip-details/[tripId]/+server.js new file mode 100644 index 0000000..d499846 --- /dev/null +++ b/src/routes/api/oba/trip-details/[tripId]/+server.js @@ -0,0 +1,44 @@ +import { error, json } from '@sveltejs/kit'; +import { PUBLIC_OBA_SERVER_URL as baseURL } from '$env/static/public'; +import { PRIVATE_OBA_API_KEY as apiKey } from '$env/static/private'; + +export async function GET({ params, url }) { + const { tripId } = params; + + const serviceDate = url.searchParams.get('serviceDate'); + const includeTrip = url.searchParams.get('includeTrip') || 'true'; + const includeSchedule = url.searchParams.get('includeSchedule') || 'true'; + const includeStatus = url.searchParams.get('includeStatus') || 'true'; + const time = url.searchParams.get('time'); + + let apiURL = `${baseURL}/api/where/trip-details/${tripId}.json?key=${apiKey}`; + + if (serviceDate) apiURL += `&serviceDate=${serviceDate}`; + apiURL += `&includeTrip=${includeTrip}`; + apiURL += `&includeSchedule=${includeSchedule}`; + apiURL += `&includeStatus=${includeStatus}`; + if (time) apiURL += `&time=${time}`; + + try { + const response = await fetch(apiURL); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + // Ensure stops are included in the references + if (!data.data.references || !data.data.references.stops) { + // If stops are not included, we might need to fetch them separately + // This is a placeholder for that logic + data.data.references = data.data.references || {}; + data.data.references.stops = []; // Fetch stops here if needed + } + + return json(data); + } catch (err) { + console.error('Error fetching trip details:', err); + throw error(500, 'Unable to fetch trip details.'); + } +} From f8ddfcc798a8fbb0075b4e86085f220c4aab7fb2 Mon Sep 17 00:00:00 2001 From: tarunsinghofficial Date: Mon, 29 Jul 2024 20:50:53 +0530 Subject: [PATCH 2/7] aligned back btn, used real data on right side of Trip Pane, added the current location of bus (30 sec interval update), enhanced the modal pane UI, and user icon --- src/components/navigation/ModalPane.svelte | 2 +- src/components/oba/StopPane.svelte | 24 +++++++---- src/components/oba/TripDetailsPane.svelte | 46 +++++++++++++++++----- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/components/navigation/ModalPane.svelte b/src/components/navigation/ModalPane.svelte index e81a823..b9d80b5 100644 --- a/src/components/navigation/ModalPane.svelte +++ b/src/components/navigation/ModalPane.svelte @@ -33,7 +33,7 @@ diff --git a/src/components/oba/TripDetailsPane.svelte b/src/components/oba/TripDetailsPane.svelte index 42ff019..0f9080c 100644 --- a/src/components/oba/TripDetailsPane.svelte +++ b/src/components/oba/TripDetailsPane.svelte @@ -1,6 +1,6 @@ + +
+
+ +
+
+
+

+ {selectedTripDetails.routeShortName} - {selectedTripDetails.tripHeadsign} +

+

+ {new Date(selectedTripDetails.scheduledArrivalTime).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit' + })} - + + {selectedTripDetails.arrivalStatus.text} + +

+
+

+ {selectedTripDetails.timeToReach} +

+
+
+ +
+
+ + diff --git a/src/components/oba/StopPane.svelte b/src/components/oba/StopPane.svelte index 2e113ef..0f0d607 100644 --- a/src/components/oba/StopPane.svelte +++ b/src/components/oba/StopPane.svelte @@ -4,6 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome'; import { faX, faArrowLeft } from '@fortawesome/free-solid-svg-icons'; import { onMount } from 'svelte'; + import TripDetailsModal from '../navigation/TripDetailsModal.svelte'; export let stop; export let arrivalsAndDeparturesResponse = null; @@ -65,6 +66,9 @@ }; showTripDetails = true; } + function handleCloseTripDetailModal() { + showTripDetails = false; + }
@@ -132,42 +136,7 @@ {/if} {#if showTripDetails} -
-
- -
-
-
-

- {selectedTripDetails.routeShortName} - {selectedTripDetails.tripHeadsign} -

-

- {new Date(selectedTripDetails.scheduledArrivalTime).toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit' - })} - - - {selectedTripDetails.arrivalStatus.text} - -

-
-

- {selectedTripDetails.timeToReach} -

-
-
- -
-
+ {/if}
@@ -178,12 +147,4 @@ overflow: -moz-scrollbars-none; -webkit-scrollbar: none; } - .trip-details-modal { - @apply absolute bottom-0 left-0 z-40 h-full w-full overflow-y-scroll rounded-lg bg-white px-2 shadow-lg md:max-w-prose; - @apply rounded-lg border-b-[1px] border-[#C6C6C8] dark:border-[1px] dark:border-[#C6C6C8] dark:border-opacity-15 bg-white p-4 shadow-lg dark:bg-black; - } - .close-button { - @apply flex items-center gap-2 rounded px-4 py-2; - @apply transition duration-300 ease-in-out hover:bg-neutral-200 dark:hover:bg-neutral-200/50; - } diff --git a/src/components/oba/TripDetailsPane.svelte b/src/components/oba/TripDetailsPane.svelte index 0f9080c..1c1b838 100644 --- a/src/components/oba/TripDetailsPane.svelte +++ b/src/components/oba/TripDetailsPane.svelte @@ -44,37 +44,35 @@ url += `&serviceDate=${serviceDate}`; } const response = await fetch(url); - if (response.ok) { - const data = await response.json(); - tripDetails = data.data.entry; - - if (data.data.references && data.data.references.routes) { - routeInfo = data.data.references.routes.find((route) => route.id === tripDetails.routeId); - } - - if (data.data.references && data.data.references.stops) { - stopInfo = data.data.references.stops.reduce((acc, stop) => { - acc[stop.id] = stop; - return acc; - }, {}); - } - - if (tripDetails.status && tripDetails.status.closestStop) { - currentStopIndex = tripDetails.schedule.stopTimes.findIndex( - (stop) => stop.stopId === tripDetails.status.closestStop.stopId - ); - } - - calculateBusPosition(); - - console.log('Trip details:', tripDetails); - console.log('Route info:', routeInfo); - console.log('Stop info:', stopInfo); - console.log('Current stop index:', currentStopIndex); - console.log('Bus position:', busPosition); - } else { + + if (!response) { error = 'Unable to fetch trip details'; + return; + } + + const jsonBody = await response.json(); + const data = jsonBody.data; + + tripDetails = data.entry; + + if (data?.references?.routes) { + routeInfo = data.references.routes.find((route) => route.id === tripDetails.routeId); } + + if (data?.references?.stops) { + stopInfo = data.references.stops.reduce((acc, stop) => { + acc[stop.id] = stop; + return acc; + }, {}); + } + + if (tripDetails.status?.closestStop) { + currentStopIndex = tripDetails.schedule.stopTimes.findIndex( + (stop) => stop.stopId === tripDetails.status.closestStop.stopId + ); + } + + calculateBusPosition(); } catch (err) { console.error('Error fetching trip details:', err); error = 'Error fetching trip details'; @@ -100,7 +98,7 @@ Route {routeInfo.shortName} - {/if} - {#if tripDetails.schedule && tripDetails.schedule.stopTimes && tripDetails.schedule.stopTimes.length > 0} + {#if tripDetails.schedule?.stopTimes.length > 0}
{#each tripDetails.schedule.stopTimes as stop, index} @@ -111,11 +109,17 @@ class="bg-white text-3xl text-green-500 dark:bg-black" /> {#if index === busPosition} - - {/if} - {#if index === currentStopIndex} - - {/if} + + {/if} + {#if index === currentStopIndex} + + {/if}
From 49a385c5883bb5dfac2426d5b43101eeea84bf30 Mon Sep 17 00:00:00 2001 From: Aaron Brethorst Date: Mon, 29 Jul 2024 17:28:36 -0700 Subject: [PATCH 4/7] Run linter/formatter to appease the CI system --- src/components/navigation/ModalPane.svelte | 2 +- .../RestAPI/arrivalsAndDeparturesForStop.js | 2 +- src/lib/RestAPI/stop.js | 2 +- src/routes/stops/[stopID]/+page.server.js | 20 +++++++++---------- src/routes/stops/[stopID]/+page.svelte | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/navigation/ModalPane.svelte b/src/components/navigation/ModalPane.svelte index b9d80b5..fd6e5a0 100644 --- a/src/components/navigation/ModalPane.svelte +++ b/src/components/navigation/ModalPane.svelte @@ -33,7 +33,7 @@