Files
FlightsAPI/resources/js/Composables/useContinentPairs.ts
T
2026-05-18 14:31:53 +10:00

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 }
}