Added Crew and General Aviation Filters
This commit is contained in:
@@ -8,19 +8,12 @@ defineProps<{
|
||||
flight: Flight
|
||||
}>()
|
||||
|
||||
function classKey(flight: Flight): string {
|
||||
const n = flight.flight_class?.name?.toLowerCase() ?? ''
|
||||
if (n.includes('private')) return 'private'
|
||||
if (n.includes('first')) return 'first'
|
||||
if (n.includes('business')) return 'business'
|
||||
if (n.includes('premium')) return 'premium'
|
||||
return 'economy'
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="boarding-pass">
|
||||
<div class="pass-header" :class="`class-${classKey(flight)}-global`">
|
||||
<div class="pass-header" :class="`class-${flight.flight_class?.internal_name}-global`">
|
||||
<span v-if="flight.flight_class?.name !== 'Unspecified'" class="pass-header-class">
|
||||
{{ flight.flight_class?.name }}
|
||||
</span>
|
||||
|
||||
@@ -22,7 +22,7 @@ const chartOptions = computed(() => ({
|
||||
},
|
||||
theme: { mode: 'dark' },
|
||||
labels: props.labels,
|
||||
colors: ['#4da6ff', '#ffc107', '#a150d5', '#22c55e', '#f97316', '#e11d48', '#06b6d4'],
|
||||
colors: ['#4da6ff', '#ffc107', '#a150d5', '#22c55e', '#f97316', '#e11d48', '#06b6d4', 'pink'],
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
formatter: (val: number) => `${Math.round(val)}%`,
|
||||
|
||||
+45
-17
@@ -1,12 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import PlaneLoader from "@/Components/FlightsGoneBy/PlaneLoader.vue"
|
||||
import VueApexCharts from "vue3-apexcharts"
|
||||
|
||||
interface TooltipItem {
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
series: { name: string; data: number[] }[]
|
||||
@@ -17,18 +12,36 @@ const props = defineProps<{
|
||||
barHeight?: number
|
||||
colors?: string[]
|
||||
options?: object
|
||||
events?: object
|
||||
}>()
|
||||
|
||||
const BAR_HEIGHT = computed(() => props.barHeight ?? 32)
|
||||
const BAR_HEIGHT = computed(() => props.barHeight ?? 32)
|
||||
const MAX_VISIBLE = computed(() => props.maxVisible ?? 12)
|
||||
|
||||
const chartHeight = computed(() => props.categories.length * BAR_HEIGHT.value + 40)
|
||||
const chartHeight = computed(() => props.categories.length * BAR_HEIGHT.value + 40)
|
||||
const scrollHeight = computed(() => {
|
||||
const visible = Math.min(props.categories.length, MAX_VISIBLE.value)
|
||||
return `${visible * BAR_HEIGHT.value + 40}px`
|
||||
})
|
||||
|
||||
// ── Tooltip state (exposed to parent via scoped slot) ─────────────────────────
|
||||
|
||||
const tooltipVisible = ref(false)
|
||||
const tooltipX = ref(0)
|
||||
const tooltipY = ref(0)
|
||||
const hoveredIndex = ref<number | null>(null)
|
||||
|
||||
function onMouseMove(e: MouseEvent) {
|
||||
tooltipX.value = e.clientX + 14
|
||||
tooltipY.value = e.clientY + 14
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
tooltipVisible.value = false
|
||||
hoveredIndex.value = null
|
||||
}
|
||||
|
||||
// ── Chart options ─────────────────────────────────────────────────────────────
|
||||
|
||||
const chartOptions = computed(() => ({
|
||||
chart: {
|
||||
type: 'bar',
|
||||
@@ -38,6 +51,16 @@ const chartOptions = computed(() => ({
|
||||
animations: { enabled: false },
|
||||
stacked: true,
|
||||
animation: { enabled: false },
|
||||
events: {
|
||||
dataPointMouseEnter: (_e: unknown, _ctx: unknown, config: { dataPointIndex: number }) => {
|
||||
tooltipVisible.value = true
|
||||
hoveredIndex.value = config.dataPointIndex
|
||||
},
|
||||
dataPointMouseLeave: () => {
|
||||
tooltipVisible.value = false
|
||||
hoveredIndex.value = null
|
||||
},
|
||||
},
|
||||
},
|
||||
theme: { mode: 'dark' },
|
||||
plotOptions: {
|
||||
@@ -68,12 +91,11 @@ const chartOptions = computed(() => ({
|
||||
markers: { width: 8, height: 8, radius: 2 },
|
||||
itemMargin: { horizontal: 8 },
|
||||
},
|
||||
tooltip: { theme: 'dark', shared: true, intersect: false },
|
||||
tooltip: { enabled: false },
|
||||
states: {
|
||||
hover: { filter: { type: 'lighten', value: 0.1 } },
|
||||
},
|
||||
}))
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -81,7 +103,12 @@ const chartOptions = computed(() => ({
|
||||
<div class="chart-title">{{ title }}</div>
|
||||
|
||||
<div v-if="categories.length" class="chart-outer">
|
||||
<div class="chart-scroll" :style="{ height: scrollHeight }">
|
||||
<div
|
||||
class="chart-scroll"
|
||||
:style="{ height: scrollHeight }"
|
||||
@mousemove="onMouseMove"
|
||||
@mouseleave="onMouseLeave"
|
||||
>
|
||||
<VueApexCharts
|
||||
type="bar"
|
||||
:height="chartHeight"
|
||||
@@ -90,8 +117,13 @@ const chartOptions = computed(() => ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Optional slot for custom tooltip -->
|
||||
<slot name="tooltip" />
|
||||
<slot
|
||||
name="tooltip"
|
||||
:visible="tooltipVisible"
|
||||
:x="tooltipX"
|
||||
:y="tooltipY"
|
||||
:index="hoveredIndex"
|
||||
/>
|
||||
|
||||
<div v-if="footerValue !== undefined" class="chart-footer">
|
||||
<span class="total-count">{{ footerValue }}</span>
|
||||
@@ -166,8 +198,4 @@ const chartOptions = computed(() => ({
|
||||
:deep(svg) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.chart-hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,19 +42,19 @@ const chartEvents = computed(() => ({
|
||||
@mousemove="onMouseMove"
|
||||
@mouseleave="onMouseLeave"
|
||||
>
|
||||
<template #tooltip>
|
||||
<ChartTooltip :visible="!!tooltipItem" :x="tooltipX" :y="tooltipY">
|
||||
<template #tooltip="{ visible, x, y, index }">
|
||||
<ChartTooltip :visible="visible" :x="x" :y="y">
|
||||
<div class="ct-name">
|
||||
<span :class="`fi fi-${tooltipItem?.code.toLowerCase()}`" />
|
||||
{{ tooltipItem?.name }}
|
||||
<span v-if="index !== null" :class="`fi fi-${countries[index].code.toLowerCase()}`" />
|
||||
{{ index !== null ? countries[index].name : '' }}
|
||||
</div>
|
||||
<div class="ct-row">
|
||||
<span class="ct-label">Flights</span>
|
||||
<span class="ct-val">{{ tooltipItem?.past }}</span>
|
||||
<span class="ct-val">{{ index !== null ? countries[index].past : '' }}</span>
|
||||
</div>
|
||||
<div class="ct-row" v-if="tooltipItem?.upcoming">
|
||||
<div v-if="index !== null && countries[index].upcoming" class="ct-row">
|
||||
<span class="ct-label">Upcoming</span>
|
||||
<span class="ct-val">{{ tooltipItem?.upcoming }}</span>
|
||||
<span class="ct-val">{{ countries[index].upcoming }}</span>
|
||||
</div>
|
||||
</ChartTooltip>
|
||||
</template>
|
||||
|
||||
@@ -43,19 +43,19 @@ const chartEvents = computed(() => ({
|
||||
@mousemove="onMouseMove"
|
||||
@mouseleave="onMouseLeave"
|
||||
>
|
||||
<template #tooltip>
|
||||
<ChartTooltip :visible="!!tooltipItem" :x="tooltipX" :y="tooltipY">
|
||||
<template #tooltip="{ visible, x, y, index }">
|
||||
<ChartTooltip :visible="visible" :x="x" :y="y">
|
||||
<div class="ct-name">
|
||||
<img :src="`${page.logo_api_url}/airlines/logos/tail/id/${tooltipItem?.id}`" width="24" height="24"/>
|
||||
{{ tooltipItem?.name }}
|
||||
<img :src="`${page.logo_api_url}/airlines/logos/tail/id/${index !== null ? airlines[index].id : ''}`" width="24" height="24"/>
|
||||
{{ index !== null ? airlines[index].name : '' }}
|
||||
</div>
|
||||
<div class="ct-row">
|
||||
<span class="ct-label">Flights</span>
|
||||
<span class="ct-val">{{ tooltipItem?.past }}</span>
|
||||
<span class="ct-val">{{ index !== null ? airlines[index].past : '' }}</span>
|
||||
</div>
|
||||
<div class="ct-row" v-if="tooltipItem?.upcoming">
|
||||
<div v-if="index !== null && airlines[index].upcoming" class="ct-row">
|
||||
<span class="ct-label">Upcoming</span>
|
||||
<span class="ct-val">{{ tooltipItem?.upcoming }}</span>
|
||||
<span class="ct-val">{{ airlines[index].upcoming }}</span>
|
||||
</div>
|
||||
</ChartTooltip>
|
||||
</template>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { FlightStats } from "@/Composables/useFlightStats"
|
||||
import { useChartTooltip } from '@/Composables/useChartTooltip'
|
||||
import ScrollingHorizontalBarChart from "@/Components/FlightsGoneBy/Charts/ChartTypes/ScrollingHorizontalBarChart.vue"
|
||||
import ChartTooltip from "@/Components/FlightsGoneBy/Charts/ChartTooltip.vue"
|
||||
import InlineBadge from "@/Components/FlightsGoneBy/InlineBadge.vue";
|
||||
|
||||
interface AirportItem {
|
||||
label: string
|
||||
@@ -42,21 +43,21 @@ const chartEvents = computed(() => ({
|
||||
@mousemove="onMouseMove"
|
||||
@mouseleave="onMouseLeave"
|
||||
>
|
||||
<template #tooltip>
|
||||
<ChartTooltip :visible="!!tooltipItem" :x="tooltipX" :y="tooltipY">
|
||||
<div class="ct-name">{{ tooltipItem?.fullName }}</div>
|
||||
<div class="ct-sub">{{ tooltipItem?.label }}</div>
|
||||
<template #tooltip="{ visible, x, y, index }">
|
||||
<ChartTooltip :visible="visible" :x="x" :y="y">
|
||||
<div class="ct-name">{{ index !== null ? airports[index].fullName : '' }}</div>
|
||||
<div class="ct-sub"><InlineBadge variant="generic">{{ index !== null ? airports[index].label : '' }}</InlineBadge></div>
|
||||
<div class="ct-row">
|
||||
<span class="ct-label">Departures</span>
|
||||
<span class="ct-val">{{ tooltipItem?.departures }}</span>
|
||||
<span class="ct-val">{{ index !== null ? airports[index].departures : '' }}</span>
|
||||
</div>
|
||||
<div class="ct-row">
|
||||
<span class="ct-label">Arrivals</span>
|
||||
<span class="ct-val">{{ tooltipItem?.arrivals }}</span>
|
||||
<span class="ct-val">{{ index !== null ? airports[index].arrivals : '' }}</span>
|
||||
</div>
|
||||
<div v-if="tooltipItem?.upcoming" class="ct-row">
|
||||
<div v-if="index !== null && airports[index].upcoming" class="ct-row">
|
||||
<span class="ct-label">Upcoming</span>
|
||||
<span class="ct-val">{{ tooltipItem?.upcoming }}</span>
|
||||
<span class="ct-val">{{ airports[index].upcoming }}</span>
|
||||
</div>
|
||||
</ChartTooltip>
|
||||
</template>
|
||||
|
||||
@@ -1,11 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { FlightStats } from "@/Composables/useFlightStats"
|
||||
import ScrollingHorizontalBarChart from "@/Components/FlightsGoneBy/Charts/ChartTypes/ScrollingHorizontalBarChart.vue"
|
||||
import ChartTooltip from "@/Components/FlightsGoneBy/Charts/ChartTooltip.vue"
|
||||
import { useChartTooltip } from "@/Composables/useChartTooltip"
|
||||
|
||||
interface RouteItem {
|
||||
label: string
|
||||
depLabel: string
|
||||
arrLabel: string
|
||||
past: number
|
||||
upcoming: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
flightStats: FlightStats
|
||||
}>()
|
||||
|
||||
const { tooltipItem, tooltipX, tooltipY, onMouseMove, onMouseLeave } = useChartTooltip<RouteItem>()
|
||||
|
||||
const routes = computed(() => props.flightStats.topRoutes.value.routes)
|
||||
const series = computed(() => props.flightStats.topRoutes.value.series)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<ScrollingHorizontalBarChart
|
||||
title="Top Routes"
|
||||
:series="series"
|
||||
:categories="routes.map(r => r.label)"
|
||||
:footer-value="routes.length"
|
||||
footer-label="total routes"
|
||||
:max-visible="18"
|
||||
>
|
||||
<template #tooltip="{ visible, x, y, index }">
|
||||
<ChartTooltip :visible="visible" :x="x" :y="y">
|
||||
<div class="ct-name">{{ index !== null ? routes[index].label : '' }}</div>
|
||||
<div class="ct-row">
|
||||
<span class="ct-label">Flights</span>
|
||||
<span class="ct-val">{{ index !== null ? routes[index].past : '' }}</span>
|
||||
</div>
|
||||
<div v-if="index !== null && routes[index].upcoming" class="ct-row">
|
||||
<span class="ct-label">Upcoming</span>
|
||||
<span class="ct-val">{{ routes[index].upcoming }}</span>
|
||||
</div>
|
||||
</ChartTooltip>
|
||||
</template>
|
||||
</ScrollingHorizontalBarChart>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<script setup lang="ts">
|
||||
import { CrewType } from "@/Types/types";
|
||||
import GlassTooltip from "@/Components/FlightsGoneBy/GlassTooltip.vue";
|
||||
|
||||
defineProps<{
|
||||
crewType: CrewType
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<GlassTooltip>
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<div v-bind="tooltipProps" style="cursor:pointer">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="tooltip-rows">
|
||||
<div class="tooltip-row">
|
||||
<span class="tooltip-label">Position</span>
|
||||
<span class="tooltip-value">{{ crewType.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</GlassTooltip>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tooltip-rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.tooltip-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.tooltip-label {
|
||||
font-family: 'Share Tech Mono', monospace;
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.15em;
|
||||
color: var(--muted, #445566);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tooltip-value {
|
||||
font-size: 0.78rem;
|
||||
color: var(--text, #c8cdd8);
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
@@ -9,6 +9,8 @@ import InlineBadge from "@/Components/FlightsGoneBy/InlineBadge.vue";
|
||||
import AirportToolTip from "@/Components/FlightsGoneBy/AirportToolTip.vue";
|
||||
import AircraftToolTip from "@/Components/FlightsGoneBy/AircraftToolTip.vue";
|
||||
import {FlightStats} from "@/Composables/useFlightStats";
|
||||
import GlassTooltip from "@/Components/FlightsGoneBy/GlassTooltip.vue";
|
||||
import CrewTooltip from "@/Components/FlightsGoneBy/CrewTooltip.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
flightStats: FlightStats
|
||||
@@ -225,9 +227,12 @@ watch(
|
||||
|
||||
<td class="v-data-table__td ">
|
||||
<span class="class-cell">
|
||||
<FlightClassBadge :flight="(item as Flight)" />
|
||||
<InlineBadge v-if="(item as Flight).seat_number" variant="economy">{{(item as Flight).seat_number}}</InlineBadge>
|
||||
<InlineBadge v-if="(item as Flight).seat_type?.name && (item as Flight).seat_type?.name !== 'Unassigned'" variant="economy">{{(item as Flight).seat_type?.name}}</InlineBadge>
|
||||
<CrewTooltip v-if="(item as Flight).flight_reason?.name == 'Crew'" :crew-type="(item as Flight).crew_type!">
|
||||
<FlightClassBadge :flight="(item as Flight)" />
|
||||
</CrewTooltip>
|
||||
<FlightClassBadge v-else :flight="(item as Flight)" />
|
||||
<InlineBadge v-if="(item as Flight).seat_number" variant="economy">{{(item as Flight).seat_number}}</InlineBadge>
|
||||
<InlineBadge v-if="(item as Flight).seat_type?.name && (item as Flight).seat_type?.name !== 'Unassigned'" variant="economy">{{(item as Flight).seat_type?.name}}</InlineBadge>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
|
||||
@@ -60,9 +60,7 @@ defineProps<{
|
||||
|
||||
<!-- Routes + International vs Domestic -->
|
||||
<div class="flight-charts glass charts-row">
|
||||
<!--
|
||||
<TopRoutesChart :flights="[...flights, ...upcomingFlights]" :upcoming-flights="upcomingFlights" />
|
||||
-->
|
||||
<TopRoutesChart :flight-stats="flightStats" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import {Flight} from "@/Types/types";
|
||||
import {BadgeVariant, Flight} from "@/Types/types";
|
||||
import InlineBadge from "@/Components/FlightsGoneBy/InlineBadge.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
flight: Flight
|
||||
}>();
|
||||
|
||||
type BadgeVariant = 'first' | 'business' | 'premium' | 'economy' | 'private' | 'unspecified';
|
||||
|
||||
function classVariant(name?: string | null): BadgeVariant {
|
||||
const n = name?.toLowerCase() ?? '';
|
||||
if (n.includes('private')) return 'private';
|
||||
if (n.includes('first')) return 'first';
|
||||
if (n.includes('business')) return 'business';
|
||||
if (n.includes('premium')) return 'premium';
|
||||
if (n.includes('economy')) return 'economy';
|
||||
return 'unspecified';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InlineBadge :variant="classVariant(flight.flight_class?.name)">
|
||||
<InlineBadge :variant="flight.flight_class?.internal_name ?? 'generic'">
|
||||
{{ flight.flight_class?.name }}
|
||||
</InlineBadge>
|
||||
</template>
|
||||
|
||||
@@ -14,12 +14,11 @@ const emit = defineEmits<{
|
||||
countries: string[]
|
||||
continents: string[]
|
||||
flightClasses: number[]
|
||||
crewTypes: number[]
|
||||
}]
|
||||
}>()
|
||||
|
||||
// ── Available options ─────────────────────────────────────────────────────────
|
||||
// flights is server-rendered and never mutated client-side, so we build options
|
||||
// once as a plain value rather than a reactive computed.
|
||||
|
||||
function buildOptions(flights: Flight[]) {
|
||||
const years = new Set<number>()
|
||||
@@ -27,6 +26,7 @@ function buildOptions(flights: Flight[]) {
|
||||
const countries = new Map<string, { code: string; name: string }>()
|
||||
const continents = new Map<string, { code: string; name: string }>()
|
||||
const classes = new Map<number, { id: number; name: string }>()
|
||||
const crewTypes = new Map<number, { id: number; name: string }>()
|
||||
|
||||
flights.forEach(f => {
|
||||
years.add(new Date(f.departure_date).getFullYear())
|
||||
@@ -44,6 +44,9 @@ function buildOptions(flights: Flight[]) {
|
||||
|
||||
if (f.flight_class?.id && f.flight_class?.name)
|
||||
classes.set(f.flight_class.id, { id: f.flight_class.id, name: f.flight_class.name })
|
||||
|
||||
if (f.crew_type?.id && f.crew_type?.name)
|
||||
crewTypes.set(f.crew_type.id, { id: f.crew_type.id, name: f.crew_type.name })
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -52,6 +55,7 @@ function buildOptions(flights: Flight[]) {
|
||||
countries: [...countries.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
||||
continents: [...continents.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
||||
classes: [...classes.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
||||
crewTypes: [...crewTypes.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +67,7 @@ const selectedAirlines = ref<number[]>([])
|
||||
const selectedCountries = ref<string[]>([])
|
||||
const selectedContinents = ref<string[]>([])
|
||||
const selectedFlightClasses = ref<number[]>([])
|
||||
const selectedCrewTypes = ref<number[]>([])
|
||||
|
||||
function emitFilters() {
|
||||
emit('change', {
|
||||
@@ -71,6 +76,7 @@ function emitFilters() {
|
||||
countries: selectedCountries.value,
|
||||
continents: selectedContinents.value,
|
||||
flightClasses: selectedFlightClasses.value,
|
||||
crewTypes: selectedCrewTypes.value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -196,6 +202,24 @@ const countryFlagClass = (code: string) =>
|
||||
<span v-if="index === 2" class="text-caption text-medium-emphasis">+{{ selectedFlightClasses.length - 2 }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
|
||||
<v-select
|
||||
v-if="availableOptions.crewTypes.length > 0"
|
||||
v-model="selectedCrewTypes"
|
||||
:items="availableOptions.crewTypes"
|
||||
item-title="name" item-value="id"
|
||||
label="Crew Type"
|
||||
multiple clearable hide-details
|
||||
density="compact" variant="outlined"
|
||||
@update:model-value="emitFilters"
|
||||
>
|
||||
<template #selection="{ item, index }">
|
||||
<span v-if="index < 2" class="v-select__selection-text">
|
||||
{{ (item as any).name }}<span v-if="index < Math.min(selectedCrewTypes.length, 2) - 1">, </span>
|
||||
</span>
|
||||
<span v-if="index === 2" class="text-caption text-medium-emphasis">+{{ selectedCrewTypes.length - 2 }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -114,10 +114,10 @@ function routePopupHTML(historical: Flight[], future: Flight[]): string {
|
||||
const key = `${f.departure_airport.id}-${f.arrival_airport.id}`
|
||||
const label = `${f.departure_airport.municipality} to ${f.arrival_airport.municipality}`
|
||||
if (!dirs.has(key)) dirs.set(key, { label, airlines: [] })
|
||||
const airline = `<span style="display:inline-flex;align-items:center;gap:6px;">
|
||||
const airline = f.airline ? `<span style="display:inline-flex;align-items:center;gap:6px;">
|
||||
<img src="${logoApiUrl}/airlines/logos/tail/id/${f.airline?.id}" width="24" height="24" alt="${f.airline?.IATA_code}" style="flex-shrink:0;" />
|
||||
${f.airline?.name}
|
||||
</span>`
|
||||
</span>` : ''
|
||||
if (airline && !dirs.get(key)!.airlines.includes(airline)) {
|
||||
dirs.get(key)!.airlines.push(airline)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ const emit = defineEmits<{
|
||||
countries: string[]
|
||||
continents: string[]
|
||||
flightClasses: number[]
|
||||
crewTypes: number[]
|
||||
}]
|
||||
}>()
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import {BadgeVariant} from "@/Types/types";
|
||||
|
||||
defineProps<{
|
||||
variant?: 'first' | 'business' | 'premium' | 'economy' | 'private' | 'unspecified' | 'generic';
|
||||
variant?: BadgeVariant
|
||||
}>();
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user