1 Commits
Author SHA1 Message Date
dredgy fe642300b6 Bug fix for individual alliance challenges 2026-07-20 15:59:15 +10:00
@@ -1,115 +1,125 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed, ref } from 'vue'
import { Achievement, Alliance, Flight, User } from '@/Types/types' import { Achievement, Airline, Alliance, Flight, User } from '@/Types/types'
import Panel from '@/Components/FlightsGoneBy/Panels/Panel.vue' import Panel from '@/Components/FlightsGoneBy/Panels/Panel.vue'
import PanelHeader from '@/Components/FlightsGoneBy/Panels/PanelHeader.vue' import PanelHeader from '@/Components/FlightsGoneBy/Panels/PanelHeader.vue'
import PanelSubHeader from '@/Components/FlightsGoneBy/Panels/PanelSubHeader.vue' import PanelSubHeader from '@/Components/FlightsGoneBy/Panels/PanelSubHeader.vue'
import BadgeTable from '@/Components/FlightsGoneBy/GenericBadgeTable.vue' import BadgeTable from '@/Components/FlightsGoneBy/GenericBadgeTable.vue'
import InlineBadge from '@/Components/FlightsGoneBy/InlineBadge.vue'
import AirlineLogo from '@/Components/FlightsGoneBy/AirlineLogo.vue'
import AllianceLogo from '@/Components/FlightsGoneBy/AllianceLogo.vue' import AllianceLogo from '@/Components/FlightsGoneBy/AllianceLogo.vue'
import FlightBadge from '@/Components/FlightsGoneBy/FlightBadge.vue' import FlightBadge from "@/Components/FlightsGoneBy/FlightBadge.vue";
defineOptions({ inheritAttrs: false })
interface AirlineEntry {
airline: Airline
flights: Flight[]
}
const props = defineProps<{ const props = defineProps<{
achievement: Achievement achievement: Achievement
user: User user: User
followStatus: string followStatus: string
majorAlliances: Alliance[] alliance: Alliance
airlines: Airline[]
flights: Flight[] flights: Flight[]
}>() }>()
interface AllianceEntry { const flightsByAirline = computed(() => {
alliance: Alliance const map = new Map<string, AirlineEntry>()
flights: Flight[] const idToKey = new Map<number, string>()
for (const airline of props.airlines) {
const key = rowKey(airline)
map.set(key, { airline, flights: [] })
idToKey.set(airline.id, key)
for (const alias of airline.merged_from ?? []) {
idToKey.set(alias.id, key)
}
} }
const alliances = computed<AllianceEntry[]>(() => {
const entries = props.majorAlliances.map(alliance => ({ alliance, flights: [] as Flight[] }))
const byName = new Map(entries.map(e => [e.alliance.internal_name, e]))
for (const flight of props.flights) { for (const flight of props.flights) {
const key = flight.airline?.alliance?.internal_name const airline = flight.airline
if (!airline) continue
const key = idToKey.get(airline.id)
if (!key) continue if (!key) continue
byName.get(key)?.flights.push(flight) map.get(key)?.flights.push(flight)
} }
return entries return map
}) })
const completedCount = computed( function rowKey(airline: Airline): string {
() => alliances.value.filter(a => a.flights.length > 0).length return airline.iata_code ?? airline.internal_name
)
function flightTitle(flight: Flight): string {
const name = flight.airline?.name ?? 'Unknown airline'
return `${name}: ${flight.departure_airport?.iata_code}${flight.arrival_airport?.iata_code}`
} }
const rows = computed(() => [...flightsByAirline.value.keys()])
function entryFor(key: string): AirlineEntry {
return flightsByAirline.value.get(key)!
}
</script> </script>
<template> <template>
<!-- Header --> <!-- Header -->
<Panel> <Panel>
<div class="alliance-header"> <div class="alliance-header">
<div class="logo-row"> <AllianceLogo :alliance="alliance" size="56" />
<AllianceLogo
v-for="entry in alliances"
:key="entry.alliance.internal_name"
:alliance="entry.alliance"
size="56"
/>
</div>
<div> <div>
<PanelHeader centered>Major Alliances</PanelHeader> <PanelHeader centered>{{ alliance.name }}</PanelHeader>
<PanelSubHeader centered> <PanelSubHeader centered>
{{ completedCount }} / {{ alliances.length }} alliances flown <slot />
</PanelSubHeader> </PanelSubHeader>
</div> </div>
</div> </div>
</Panel> </Panel>
<!-- Per-alliance breakdown -->
<Panel v-for="entry in alliances" :key="entry.alliance.internal_name"> <!-- Airlines table -->
<Panel>
<div class="table-toolbar"> <div class="table-toolbar">
<PanelHeader>{{ entry.alliance.name }}</PanelHeader> <PanelHeader>Airlines</PanelHeader>
</div> </div>
<BadgeTable <BadgeTable
:rows="[entry.alliance.internal_name]" :rows="rows"
:rowKey="key => key" :rowKey="key => key"
:hasItems="() => entry.flights.length > 0" :hasItems="key => entryFor(key).flights.length > 0"
labelWidth="14em" labelWidth="14em"
> >
<template #label> <template #label="{ row: key }">
<div class="airline-label" > <div class="airline-label" >
<AllianceLogo :alliance="entry.alliance" size="24" /> <AirlineLogo :airline="entryFor(key).airline" size="24" />
<span>{{ entry.alliance.name }}</span> <span>{{ entryFor(key).airline.name }}</span>
</div> </div>
</template> </template>
<template #items> <template #items="{ row: key }">
<FlightBadge <FlightBadge
v-for="flight in entry.flights" v-for="flight in entryFor(key).flights"
:key="flight.id" :key="flight.id"
:title="flightTitle(flight)" :title="`${flight.departure_airport?.iata_code} → ${flight.arrival_airport?.iata_code}`"
:flight="flight" :flight="flight" />
/>
</template> </template>
</BadgeTable> </BadgeTable>
</Panel> </Panel>
<!-- Slot for alliance-specific panels -->
<slot name="extra" />
<!-- Requirements --> <!-- Requirements -->
<Panel> <Panel>
<PanelHeader centered>Requirements</PanelHeader> <PanelHeader centered>Requirements</PanelHeader>
<p> <p>
To complete this challenge you must fly with at least one member airline from each of To complete this challenge you must fly with every current member airline of
the three major alliances. Unlike the individual alliance challenges, you don't need <strong>{{ alliance.name }}</strong>. Alliance membership changes over time, so the
to fly every airline in an alliance just one flight per alliance is enough. required airlines reflect the current roster.
</p> </p>
<p> <p>
Codeshare flights do not count, the operating carrier must be a member of the Codeshare flights do not count, the operating carrier must be a member of {{alliance.name}}.
alliance.
</p> </p>
</Panel> </Panel>
</template> </template>
@@ -123,10 +133,6 @@ function flightTitle(flight: Flight): string {
flex-wrap: wrap; flex-wrap: wrap;
} }
.logo-row {
display: flex;
gap: 0.75rem;
}
.table-toolbar { .table-toolbar {
display: flex; display: flex;
@@ -136,6 +142,7 @@ function flightTitle(flight: Flight): string {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.airline-label { .airline-label {
width: 100%; width: 100%;
display: flex; display: flex;
@@ -143,4 +150,5 @@ function flightTitle(flight: Flight): string {
gap: 0.5rem; gap: 0.5rem;
padding: 0 0.25rem; padding: 0 0.25rem;
} }
</style> </style>