Skip to content

Commit

Permalink
✨ UI Improvement, sort menu implemented. + User can now research with…
Browse files Browse the repository at this point in the history
…out return date in explorer mode.
  • Loading branch information
quentin-aslan committed Nov 10, 2024
1 parent 5d73891 commit 75ad552
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 50 deletions.
6 changes: 4 additions & 2 deletions components/SearchDetailsMobile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@
<i class="pi pi-map-marker font-bold text-xl" /> <span class="">{{ departureStation }}</span>
</div>
<div class="flex flex-row items-center gap-2 text-lg">
<span>{{ departureDate?.toISOString().slice(0, 10) }}</span>
<span>{{ toISOStringWithOffset(departureDate).slice(0, 10) }}</span>
<i class="pi pi-arrow-circle-right font-extrabold" />
<span>{{ returnDate?.toISOString().slice(0, 10) }}</span>
<span>{{ toISOStringWithOffset(returnDate).slice(0, 10) }}</span>
</div>
</div>
</div>
</template>

<script lang="ts" setup>
import { toISOStringWithOffset } from '~/utils'
const { departureStation, destinationStation, departureDate, returnDate } = useSearchForm()
const emit = defineEmits(['search-details-click'])
</script>
29 changes: 5 additions & 24 deletions components/TrainCard.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<div class="flex flex-col gap-4 bg-max-bg border border-max-sec rounded-lg p-4 shadow-sm">
<!-- Header: Travel Duration -->
<div class="flex flex-row items-center text-max-pri font-sans">
<div class="flex flex-row items-center">
<i class="pi pi-clock mr-2" /> <!-- Clock Icon -->
<span>{{ msToTime(totalDuration) }}</span>
<span>Durée total du trajet : <span class="font-sans-semibold border-b-4 border-b-max-action">{{ msToTime(totalDuration) }}</span></span>
</div>
<div
class="space-y-4"
Expand All @@ -29,7 +29,7 @@
<!-- Connection Info -->
<div class="p-2 ml-12 mt-2 w-fit bg-max-action rounded-md relative">
<p class="text-max-pri font-sans-medium text-xs">
Changement à {{ journey[index+1].origin }}<br>
Changement à <span class="font-sans-semibold">{{ journey[index+1].origin }}</span><br>
{{ formattedTime(train.arrivalDateTime) }} - {{ formattedTime(journey[index+1].departureDateTime) }} ({{ msToTime(calculateConnectionTime(journey, index+1)) }})
</p>
</div>
Expand All @@ -51,28 +51,9 @@ type Props = {
const props = defineProps<Props>()
const isDirectJourney = props.journey?.length === 1
const { calculateJourneyTotalDuration, calculateConnectionTime } = useDestinations()
const calculateConnectionTime = (journey: Journey, index: number) => {
if (index === 0) return 0
return new Date(journey[index].departureDateTime) - new Date(journey[index - 1].departureDateTime)
}
// Calculate the total duration of the journey
const calculateJourneyTotalDuration = (journey: Journey) => {
let totalDuration = 0 // ms
for (let i = 0; i < journey.length; i++) {
const connectionTime = calculateConnectionTime(journey, i)
const journeyDuration = new Date(journey[i].arrivalDateTime) - new Date(journey[i].departureDateTime)
totalDuration += journeyDuration + connectionTime
}
return totalDuration
}
const totalDuration = calculateJourneyTotalDuration(props.journey)
const totalDuration = computed(() => calculateJourneyTotalDuration(props.journey))
// const props = defineProps({
// isDirectTrip: { type: Boolean, required: true },
Expand Down
87 changes: 84 additions & 3 deletions components/TrainList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,18 @@
<TabPanels class="z-10 w-full">
<TabPanel value="departure">
<div class="flex flex-col gap-4">
<div class="flex flex-row gap-4">
<Select
v-model="sortChoice"
:options="sortChoicesOptions"
option-label="label"
option-value="choice"
placeholder="Select a City"
class="w-full md:w-56"
/>
</div>
<TrainCard
v-for="(journey, index) in departureJourneys"
v-for="(journey, index) in departureJourneysSorted"
:key="index"
:journey="journey"
/>
Expand All @@ -27,7 +37,7 @@
>
<div class="flex flex-col gap-4">
<TrainCard
v-for="(journey, index) in returnJourneys"
v-for="(journey, index) in returnJourneysSorted"
:key="index"
:journey="journey"
/>
Expand All @@ -51,8 +61,79 @@ type Props = {
}
const props = defineProps<Props>()
const isReturnTabDisplayed = computed(() => props.returnJourneys.length > 0)
// SORT
const { calculateJourneyTotalDuration } = useDestinations()
enum SortChoices {
DURATION_ASC = 'duration_asc',
DURATION_DESC = 'duration_desc',
DEPARTURE_TIME_ASC = 'departure_time_asc',
DEPARTURE_TIME_DESC = 'departure_time_desc',
}
const sortChoicesOptions = [
{ label: 'Durée (croissant)', choice: SortChoices.DURATION_ASC },
{ label: 'Durée (décroissant)', choice: SortChoices.DURATION_DESC },
{ label: 'Heure de départ (croissant)', choice: SortChoices.DEPARTURE_TIME_ASC },
{ label: 'Heure de départ (décroissant)', choice: SortChoices.DEPARTURE_TIME_DESC },
]
const sortChoice = ref<SortChoices>(SortChoices.DURATION_ASC)
const departureJourneysSorted = computed(() => {
if (sortChoice.value === SortChoices.DURATION_ASC) {
return sortJourneysByDuration(props.departureJourneys, 'asc')
}
else if (sortChoice.value === SortChoices.DURATION_DESC) {
return sortJourneysByDuration(props.departureJourneys, 'desc')
}
else if (sortChoice.value === SortChoices.DEPARTURE_TIME_ASC) {
return sortJourneysByDepartureTime(props.departureJourneys, 'asc')
}
else if (sortChoice.value === SortChoices.DEPARTURE_TIME_DESC) {
return sortJourneysByDepartureTime(props.departureJourneys, 'desc')
}
else {
return sortJourneysByDuration(props.departureJourneys, 'asc')
}
})
const returnJourneysSorted = computed(() => {
if (sortChoice.value === SortChoices.DURATION_ASC) {
return sortJourneysByDuration(props.returnJourneys, 'asc')
}
else if (sortChoice.value === SortChoices.DURATION_DESC) {
return sortJourneysByDuration(props.returnJourneys, 'desc')
}
else if (sortChoice.value === SortChoices.DEPARTURE_TIME_ASC) {
return sortJourneysByDepartureTime(props.returnJourneys, 'asc')
}
else if (sortChoice.value === SortChoices.DEPARTURE_TIME_DESC) {
return sortJourneysByDepartureTime(props.returnJourneys, 'desc')
}
else {
return sortJourneysByDuration(props.returnJourneys, 'asc')
}
})
const sortJourneysByDuration = (journeys: Journey[], order: 'asc' | 'desc' = 'asc') => {
return [...journeys].sort((a, b) => {
const durationA = calculateJourneyTotalDuration(a)
const durationB = calculateJourneyTotalDuration(b)
return order === 'asc' ? durationA - durationB : durationB - durationA
})
}
const sortJourneysByDepartureTime = (journeys: Journey[], order: 'asc' | 'desc' = 'asc') => {
return [...journeys].sort((a, b) => {
const departureTimeA = new Date(a.departureTime).getTime()
const departureTimeB = new Date(b.departureTime).getTime()
return order === 'asc' ? departureTimeA - departureTimeB : departureTimeB - departureTimeA
})
}
</script>

<style scoped>
Expand Down
18 changes: 8 additions & 10 deletions components/TrainSegment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@
<!-- Train Details -->
<div>
<div class="text-xl text-max-pri font-sans-semibold">
{{ train.trainNo }}
<span class="border-b-2 border-b-max-action">{{ formattedTime(train.departureDateTime) }}</span> <span class="text-base font-sans"> - {{ formattedDateWithoutTime(train.departureDateTime) }}</span>
</div>
<div class="text-sm text-max-pri">
<div class="text-sm font-sans-semibold text-max-pri">
{{ train.origin }}
</div>
<div class="text-sm text-max-pri font-sans-medium">
{{ formattedDate(train.departureDateTime) }}
</div>

<div class="text-sm text-max-pri font-sans-medium" />
{{ train.trainNo }}
<!-- End Station -->
<div
v-if="isLast"
Expand All @@ -39,11 +37,11 @@
style="position: absolute; top: 1rem; left: -2.24rem;"
/> <!-- Dot for end station -->
<div>
<div class="text-sm text-max-pri">
<div class="text-sm font-sans-semibold text-max-pri">
{{ train.destination }}
</div>
<div class="text-sm text-max-pri font-sans-medium">
{{ formattedDate(train.arrivalDateTime) }}
<div class="text-xl text-max-pri font-sans-medium">
{{ formattedTime(train.arrivalDateTime) }} <span class="text-base font-sans">- {{ formattedDateWithoutTime(train.departureDateTime) }}</span>
</div>
</div>
</div>
Expand All @@ -56,7 +54,7 @@
import { defineProps } from 'vue'
import trainIcon from 'assets/icons/train.svg?raw'
import type { AdaptedTrainData } from '~/types/common'
import { formattedDate } from '~/utils'
import { formattedDate, formattedDateWithoutTime, formattedTime } from '~/utils'
type Props = {
train: AdaptedTrainData
Expand Down
23 changes: 22 additions & 1 deletion composables/use-destinations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ref } from 'vue'
import type { RoundTripDestination } from '~/types/common'
import type { Journey, RoundTripDestination } from '~/types/common'
import { toISOStringWithOffset } from '~/utils'

const destinations = ref<RoundTripDestination[]>([])
Expand Down Expand Up @@ -44,9 +44,30 @@ export const useDestinations = () => {
}
}

const calculateConnectionTime = (journey: Journey, index: number) => {
if (index === 0) return 0

return new Date(journey[index].departureDateTime) - new Date(journey[index - 1].arrivalDateTime)
}

// Calculate the total duration of the journey
const calculateJourneyTotalDuration = (journey: Journey) => {
let totalDuration = 0 // ms

for (let i = 0; i < journey.length; i++) {
const connectionTime = calculateConnectionTime(journey, i)
const journeyDuration = new Date(journey[i].arrivalDateTime) - new Date(journey[i].departureDateTime)
totalDuration += journeyDuration + connectionTime
}

return totalDuration
}

return {
fetchDestinations,
isFetchDestinationLoading,
destinations,
calculateJourneyTotalDuration,
calculateConnectionTime,
}
}
12 changes: 7 additions & 5 deletions pages/results.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@
}"
>
<!-- Section pour les résultats -->
<h2
<div
v-if="noResults"
class="text-3xl text-max-action text-center"
class="flex flex-row text-3xl text-max-action text-center justify-center"
>
Aucun Résultat :/
</h2>
<Message severity="warn">
<span class="text-3xl">Aucun Résultat :/</span>
</Message>
</div>
<section
v-else
class="bg-max-bg"
Expand Down Expand Up @@ -139,7 +141,7 @@ const noResults = computed(() => !destinations.value || destinations.value.lengt
const destinationSelected = ref<RoundTripDestination | null>(null)
const { mobileHeader, desktopHeader, contentMainMarginTop, contentMainMinHeight } = useHeaderHeights(isMobile)
watch(destinations, () => destinationSelected.value = (isTripMode.value) ? destinations.value[0] : null)
watch(destinations, () => destinationSelected.value = (isTripMode.value) ? destinations.value?.[0] : null)
watch(destinationSelected, (destination) => {
if (destination) {
Expand Down
18 changes: 13 additions & 5 deletions server/api/train-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,21 @@ const findRoundTrips = async (
}

// If the destination is not specified, look for a round trip to all destinations
let i = 0
for (const departureDestination of departureDestinations) {
i++
const returnDestination = await findRoute(departureDestination.destinationName, origin, returnDate, minConnectionTimeSameStationMinutes, minConnectionTimeSameCityMinutes, maxConnectionTimeMinutes)
if (returnDestination) {
roundTripsDestinations.push({ destinationName: departureDestination.destinationName, departureJourneys: departureDestination.journeys, returnJourneys: (returnDestination as DestinationJourneys).journeys })
const destinationObj: RoundTripDestination = {
destinationName: departureDestination.destinationName,
departureJourneys: departureDestination.journeys,
returnJourneys: [],
}

if (returnDate) {
const returnDestination = await findRoute(departureDestination.destinationName, origin, returnDate, minConnectionTimeSameStationMinutes, minConnectionTimeSameCityMinutes, maxConnectionTimeMinutes)
if (returnDestination) {
destinationObj.returnJourneys = (returnDestination as DestinationJourneys).journeys
}
}

roundTripsDestinations.push(destinationObj)
}

// Get the coordinates and the traffic of the destinations
Expand Down
11 changes: 11 additions & 0 deletions utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ export const formattedDate = (date: Date | string): string => {
})
}

export const formattedDateWithoutTime = (date: Date | string): string => {
if (typeof date === 'string') {
date = new Date(date)
}
return date.toLocaleDateString('fr-FR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
}

export const formattedTime = (date: Date | string): string => {
if (typeof date === 'string') {
date = new Date(date)
Expand Down

0 comments on commit 75ad552

Please sign in to comment.