Files
FlightsAPI/resources/js/Components/FlightsGoneBy/FlightStatsBar.vue
T
2026-06-20 22:21:17 +10:00

174 lines
5.5 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue'
import type { Flight, SharedProps } from '@/Types/types'
import StatItem from "@/Components/StatItem.vue"
import { usePage } from "@inertiajs/vue3"
const props = defineProps<{
flights: Flight[]
upcomingFlights: Flight[]
}>()
const page = usePage<SharedProps>().props
// ── Past ──────────────────────────────────────────────────────────────────────
const totalDistanceKm = computed(() =>
Math.round(props.flights.reduce((sum, f) => sum + (f.distance ?? 0), 0))
)
const uniqueRoutes = computed(() => {
const keys = new Set(props.flights.map(f =>
[f.departure_airport.id, f.arrival_airport.id].sort().join('-')
))
return keys.size
})
const uniqueCountries = computed(() => {
const codes = new Set<string>()
props.flights.forEach(f => {
const depCode = f.departure_airport.region?.country?.code
const arrCode = f.arrival_airport.region?.country?.code
if (depCode) codes.add(depCode)
if (arrCode) codes.add(arrCode)
})
return codes.size
})
const uniqueAirports = computed(() => {
const ids = new Set<number>()
props.flights.forEach(f => {
ids.add(f.departure_airport.id)
ids.add(f.arrival_airport.id)
})
return ids.size
})
const durationDisplay = computed(() => {
const totalMinutes = props.flights.reduce((sum, f) => sum + (f.duration ?? 0), 0)
const totalHours = Math.floor(totalMinutes / 60)
return {
hours: totalHours,
days: Math.floor(totalHours / 24),
weeks: Math.floor(totalHours / 24 / 7),
}
})
// ── Upcoming ──────────────────────────────────────────────────────────────────
const upcomingDistanceKm = computed(() =>
Math.round(props.upcomingFlights.reduce((sum, f) => sum + (f.distance ?? 0), 0))
)
const uniqueUpcomingRoutes = computed(() => {
const keys = new Set(props.upcomingFlights.map(f =>
[f.departure_airport.id, f.arrival_airport.id].sort().join('-')
))
return keys.size
})
const uniqueUpcomingCountries = computed(() => {
const codes = new Set<string>()
props.upcomingFlights.forEach(f => {
const depCode = f.departure_airport.region?.country?.code
const arrCode = f.arrival_airport.region?.country?.code
if (depCode) codes.add(depCode)
if (arrCode) codes.add(arrCode)
})
return codes.size
})
const uniqueUpcomingAirports = computed(() => {
const ids = new Set<number>()
props.upcomingFlights.forEach(f => {
ids.add(f.departure_airport.id)
ids.add(f.arrival_airport.id)
})
return ids.size
})
const upcomingDurationDisplay = computed(() => {
const totalMinutes = props.upcomingFlights.reduce((sum, f) => sum + (f.duration ?? 0), 0)
const totalHours = Math.floor(totalMinutes / 60)
return {
hours: totalHours,
days: Math.floor(totalHours / 24),
weeks: Math.floor(totalHours / 24 / 7),
}
})
const distanceUnit = computed(() => page.auth?.user?.resolved_settings?.distance_unit)
</script>
<template>
<div class="stats-bar glass">
<StatItem
:primary="flights.length || null"
unit="flights"
:upcoming="upcomingFlights.length || null"
:upcoming-label="flights.length ? 'upcoming' : 'flights'"
:show-upcoming-badge="!flights.length"
/>
<StatItem
:primary="uniqueRoutes || null"
unit="routes"
:upcoming="uniqueUpcomingRoutes || null"
:upcoming-label="uniqueRoutes ? 'upcoming' : 'routes'"
:show-upcoming-badge="!uniqueRoutes"
/>
<StatItem
:primary="totalDistanceKm || null"
:upcoming="upcomingDistanceKm || null"
:upcoming-label="totalDistanceKm ? 'upcoming' : undefined"
is-distance
:distance-unit="distanceUnit"
/>
<StatItem
:primary="uniqueCountries || null"
unit="countries"
:upcoming="uniqueUpcomingCountries || null"
:upcoming-label="uniqueCountries ? 'upcoming' : 'countries'"
:show-upcoming-badge="!uniqueCountries"
/>
<StatItem
:primary="uniqueAirports || null"
unit="airports"
:upcoming="uniqueUpcomingAirports || null"
:upcoming-label="uniqueAirports ? 'upcoming' : 'airports'"
:show-upcoming-badge="!uniqueAirports"
/>
<StatItem
:primary="durationDisplay.hours || null"
unit="hours in the air"
:upcoming="upcomingDurationDisplay.hours || null"
:upcoming-label="durationDisplay.hours ? 'hrs upcoming' : 'hours in the air'"
:show-upcoming-badge="!durationDisplay.hours"
:sub="!durationDisplay.hours ? `${upcomingDurationDisplay.days} days · ${upcomingDurationDisplay.weeks} weeks` : undefined"
/>
</div>
</template>
<style scoped>
.stats-bar {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 1px;
border-radius: 10px;
overflow: hidden;
margin-top: 12px;
}
@media (max-width: 1024px) {
.stats-bar { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
.stats-bar { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
</style>