174 lines
5.5 KiB
Vue
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>
|