165 lines
5.8 KiB
TypeScript
165 lines
5.8 KiB
TypeScript
import { computed, ComputedRef } from 'vue'
|
|
import { Flight } from '@/Types/types'
|
|
|
|
export interface Continent {
|
|
id: number
|
|
name: string
|
|
code: string
|
|
internal_name: string
|
|
}
|
|
|
|
export interface ContinentPairEntry {
|
|
key: string
|
|
label: string
|
|
flights: Flight[]
|
|
}
|
|
|
|
/** Stable undirected key — always alphabetically sorted so A|B === B|A */
|
|
export function undirectedKey(a: string, b: string): string {
|
|
return [a, b].sort().join('|')
|
|
}
|
|
|
|
/** Directed key — order preserved */
|
|
export function directedKey(dep: string, arr: string): string {
|
|
return `${dep}|${arr}`
|
|
}
|
|
|
|
export function labelFor(a: string, b: string): string {
|
|
return `${a}↔${b}`
|
|
}
|
|
|
|
export function continentNameOf(flight: Flight, side: 'departure' | 'arrival'): string | null {
|
|
const airport = side === 'departure' ? flight.departure_airport : flight.arrival_airport
|
|
return airport.region?.continent?.name ?? null
|
|
}
|
|
|
|
export function isInternational(flight: Flight): boolean {
|
|
const depCountry = flight.departure_airport.region?.country_id
|
|
const arrCountry = flight.arrival_airport.region?.country_id
|
|
if (depCountry == null || arrCountry == null) return true
|
|
return depCountry !== arrCountry
|
|
}
|
|
|
|
/** Filter flights to only those that qualify for continent-pair achievements */
|
|
export function qualifyingFlights(flights: Flight[]): Flight[] {
|
|
return flights.filter(flight => {
|
|
const dep = continentNameOf(flight, 'departure')
|
|
const arr = continentNameOf(flight, 'arrival')
|
|
if (!dep || !arr) return false
|
|
if (dep === arr && !isInternational(flight)) return false
|
|
return true
|
|
})
|
|
}
|
|
|
|
// ── One-way (undirected) pairs ─────────────────────────────────────────────
|
|
|
|
export function useUndirectedContinentPairs(
|
|
flights: ComputedRef<Flight[]>,
|
|
continents: ComputedRef<Continent[]>,
|
|
) {
|
|
const allKeys = computed<string[]>(() => {
|
|
const names = continents.value.map(c => c.name).sort()
|
|
const keys: string[] = []
|
|
for (let i = 0; i < names.length; i++) {
|
|
for (let j = i; j < names.length; j++) {
|
|
keys.push(undirectedKey(names[i], names[j]))
|
|
}
|
|
}
|
|
return keys.sort((a, b) => {
|
|
const [a1, a2] = a.split('|')
|
|
const [b1, b2] = b.split('|')
|
|
return labelFor(a1, a2).localeCompare(labelFor(b1, b2))
|
|
})
|
|
})
|
|
|
|
const flightsByKey = computed<Map<string, Flight[]>>(() => {
|
|
const map = new Map(allKeys.value.map(k => [k, [] as Flight[]]))
|
|
for (const flight of qualifyingFlights(flights.value)) {
|
|
const dep = continentNameOf(flight, 'departure')!
|
|
const arr = continentNameOf(flight, 'arrival')!
|
|
const key = undirectedKey(dep, arr)
|
|
map.get(key)?.push(flight)
|
|
}
|
|
return map
|
|
})
|
|
|
|
const entries = computed<ContinentPairEntry[]>(() =>
|
|
allKeys.value.map(key => {
|
|
const [a, b] = key.split('|')
|
|
return { key, label: labelFor(a, b), flights: flightsByKey.value.get(key) ?? [] }
|
|
})
|
|
)
|
|
|
|
const completedCount = computed(() => entries.value.filter(e => e.flights.length > 0).length)
|
|
const totalCount = computed(() => entries.value.length)
|
|
|
|
return { entries, completedCount, totalCount }
|
|
}
|
|
|
|
// ── Both-ways (directed) pairs ─────────────────────────────────────────────
|
|
|
|
export function useDirectedContinentPairs(
|
|
flights: ComputedRef<Flight[]>,
|
|
continents: ComputedRef<Continent[]>,
|
|
) {
|
|
/** All directed keys grouped by the departure continent name */
|
|
const keysByDeparture = computed<Map<string, string[]>>(() => {
|
|
const names = continents.value.map(c => c.name).sort()
|
|
const map = new Map<string, string[]>()
|
|
for (const dep of names) {
|
|
map.set(dep, names.map(arr => directedKey(dep, arr)).sort((a, b) => {
|
|
const [, a2] = a.split('|')
|
|
const [, b2] = b.split('|')
|
|
return a2.localeCompare(b2)
|
|
}))
|
|
}
|
|
return map
|
|
})
|
|
|
|
const flightsByKey = computed<Map<string, Flight[]>>(() => {
|
|
const map = new Map<string, Flight[]>()
|
|
for (const keys of keysByDeparture.value.values()) {
|
|
for (const key of keys) map.set(key, [])
|
|
}
|
|
for (const flight of qualifyingFlights(flights.value)) {
|
|
const dep = continentNameOf(flight, 'departure')!
|
|
const arr = continentNameOf(flight, 'arrival')!
|
|
const key = directedKey(dep, arr)
|
|
map.get(key)?.push(flight)
|
|
}
|
|
return map
|
|
})
|
|
|
|
/** Entries grouped by departure continent, each sorted by arrival continent name */
|
|
const entriesByDeparture = computed<Map<string, ContinentPairEntry[]>>(() => {
|
|
const map = new Map<string, ContinentPairEntry[]>()
|
|
for (const [dep, keys] of keysByDeparture.value) {
|
|
map.set(dep, keys.map(key => {
|
|
const [a, b] = key.split('|')
|
|
return { key, label: `${a} → ${b}`, flights: flightsByKey.value.get(key) ?? [] }
|
|
}))
|
|
}
|
|
return map
|
|
})
|
|
|
|
const departureNames = computed(() => [...keysByDeparture.value.keys()])
|
|
|
|
const completedCount = computed(() => {
|
|
let count = 0
|
|
for (const entries of entriesByDeparture.value.values()) {
|
|
count += entries.filter(e => e.flights.length > 0).length
|
|
}
|
|
return count
|
|
})
|
|
|
|
const totalCount = computed(() => {
|
|
let count = 0
|
|
for (const entries of entriesByDeparture.value.values()) {
|
|
count += entries.length
|
|
}
|
|
return count
|
|
})
|
|
|
|
return { entriesByDeparture, departureNames, completedCount, totalCount }
|
|
}
|