Skip to content

Commit

Permalink
added the api for trip details, created the tripdetailpane with all i…
Browse files Browse the repository at this point in the history
…nformation
  • Loading branch information
tarunsinghofficial committed Jul 24, 2024
1 parent a86de9c commit 50456e6
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 7 deletions.
22 changes: 22 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
26 changes: 23 additions & 3 deletions src/components/ArrivalDeparture.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
});
}
</script>

<div
class="flex h-auto items-center justify-between border-b-[1px] border-[#C6C6C8] bg-[#ffffff] p-4 hover:cursor-pointer hover:bg-[#e3e3e3] dark:border-[#313135] dark:bg-[#1c1c1c] hover:dark:bg-[#363636]"
<button
on:click={handleTripDetail}
class="flex h-auto w-full items-center justify-between border-b-[1px] border-[#C6C6C8] bg-[#ffffff] p-4 hover:cursor-pointer hover:bg-[#e3e3e3] dark:border-[#313135] dark:bg-[#1c1c1c] hover:dark:bg-[#363636]"
>
<div class="flex flex-col gap-1">
<p class="text-xl font-semibold text-black dark:text-white">
Expand All @@ -66,4 +86,4 @@
{calculateTimeToReach(predictedArrivalTime, scheduledArrivalTime)}
</p>
</div>
</div>
</button>
71 changes: 68 additions & 3 deletions src/components/oba/StopPane.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
<script>
import ArrivalDeparture from '../ArrivalDeparture.svelte';
import TripDetailsPane from './TripDetailsPane.svelte';
import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome';
import { faX, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
export let stop;
export let arrivalsAndDeparturesResponse = null;
let arrivalsAndDepartures;
let loading = false;
let error;
let showTripDetails = false;
let selectedTripDetails = null;
async function loadData(stopID) {
loading = true;
const response = await fetch(`/api/oba/arrivals-and-departures-for-stop/${stopID}`);
Expand All @@ -25,8 +31,7 @@
// instead of loading fresh data.
if (arrDep) {
arrivalsAndDepartures = arrDep.data.entry;
}
else {
} else {
await loadData(s.id);
}
})(stop, arrivalsAndDeparturesResponse);
Expand All @@ -41,6 +46,15 @@
}
return _routeShortNames;
}
function handleShowTripDetails(event) {
selectedTripDetails = {
...event.detail,
routeShortName: event.detail.routeShortName,
tripHeadsign: event.detail.tripHeadsign,
scheduledArrivalTime: event.detail.scheduledArrivalTime
};
showTripDetails = true;
}
</script>

<div>
Expand Down Expand Up @@ -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}
</div>
</div>
</div>
{/if}

{#if showTripDetails}
<div class="trip-details-modal scrollbar-hidden">
<div class="py-1 text-right">
<button type="button" on:click={() => (showTripDetails = false)} class="close-button">
<FontAwesomeIcon icon={faArrowLeft} class="font-black text-black dark:text-white" />
<span class="sr-only">Back</span>
</button>
</div>
<div
class="flex items-center justify-between border-b-[1px] border-[#C6C6C8] bg-white px-4 py-2 dark:border-[#313135] dark:bg-gray-800"
>
<div>
<h2 class="text-lg font-semibold">
{selectedTripDetails.routeShortName} - {selectedTripDetails.tripHeadsign}
</h2>
<p class="text-sm font-semibold text-gray-600 dark:text-gray-400">
{new Date(selectedTripDetails.scheduledArrivalTime).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
})} -
<span class={selectedTripDetails.arrivalStatus.color}>
{selectedTripDetails.arrivalStatus.text}
</span>
</p>
</div>
<p class={`mt-1 text-sm ${selectedTripDetails.arrivalStatus.color}`}>
{selectedTripDetails.timeToReach}
</p>
</div>
<div class="px-4 py-2">
<TripDetailsPane
tripId={selectedTripDetails.tripId}
vehicleId={selectedTripDetails.vehicleId}
serviceDate={selectedTripDetails.serviceDate}
/>
</div>
</div>
{/if}
</div>

<style>
<style lang="postcss">
.scrollbar-hidden {
scrollbar-width: none;
-ms-overflow-style: none;
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] bg-white p-4 shadow-lg hover:cursor-pointer dark:bg-black;
}
.close-button {
@apply rounded px-4 py-2;
@apply transition duration-300 ease-in-out hover:bg-neutral-200 dark:hover:bg-neutral-200/50;
}
</style>
109 changes: 109 additions & 0 deletions src/components/oba/TripDetailsPane.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<script>
import { onMount, onDestroy } from 'svelte';
import { faBus } from '@fortawesome/free-solid-svg-icons';
import { faSquare } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome';
export let tripId;
export let serviceDate = null;
let tripDetails = null;
let routeInfo = null;
let stopInfo = {};
let error = null;
let interval;
let currentStopIndex = -1;
function formatTime(seconds) {
const date = new Date(seconds);
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}
async function loadTripDetails() {
try {
let url = `/api/oba/trip-details/${tripId}?includeTrip=true&includeSchedule=true&includeStatus=true`;
if (serviceDate) {
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;
}, {});
}
// Update current stop index
if (tripDetails.status && tripDetails.status.closestStop) {
currentStopIndex = tripDetails.schedule.stopTimes.findIndex(
(stop) => stop.stopId === tripDetails.status.closestStop.stopId
);
}
console.log('Trip details:', tripDetails);
console.log('Route info:', routeInfo);
console.log('Stop info:', stopInfo);
console.log('Current stop index:', currentStopIndex);
} else {
error = 'Unable to fetch trip details';
}
} catch (err) {
console.error('Error fetching trip details:', err);
error = 'Error fetching trip details';
}
}
onMount(() => {
loadTripDetails();
interval = setInterval(loadTripDetails, 30000);
});
onDestroy(() => {
clearInterval(interval);
});
</script>

<div class="trip-details-pane">
{#if error}
<p>{error}</p>
{:else if tripDetails}
<h2>
{#if routeInfo}
Route {routeInfo.shortName} -
{/if}
</h2>
{#if tripDetails.schedule && tripDetails.schedule.stopTimes && tripDetails.schedule.stopTimes.length > 0}
<div class="relative">
<div class="absolute bottom-0 left-5 top-0 w-0.5 bg-[#129900]"></div>
{#each tripDetails.schedule.stopTimes as stop, index}
<div class="relative mb-4 flex items-center">
<div class="relative z-10 flex h-12 w-12 items-center justify-center">
<FontAwesomeIcon icon={faSquare} class="text-2xl text-[#129900]" />
{#if index === currentStopIndex}
<FontAwesomeIcon icon={faBus} class="absolute text-lg text-[#129900]" />
{/if}
</div>
<div class="ml-4 flex w-full items-center justify-between">
<div class="text-md font-semibold text-[#000000] dark:text-white">
{stopInfo[stop.stopId] ? stopInfo[stop.stopId].name : stop.stopId}
</div>
<div class="text-sm text-[#86858B]">{formatTime(stop.arrivalTime)}</div>
</div>
</div>
{/each}
</div>
{:else}
<p>No stop times available for this trip.</p>
{/if}
{:else}
<p>Loading trip details...</p>
{/if}
</div>
24 changes: 24 additions & 0 deletions src/lib/RestAPI/tripDetails.js
Original file line number Diff line number Diff line change
@@ -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);
}
44 changes: 44 additions & 0 deletions src/routes/api/oba/trip-details/[tripId]/+server.js
Original file line number Diff line number Diff line change
@@ -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.');
}
}

0 comments on commit 50456e6

Please sign in to comment.