Some checks failed
ci / ci (22, ubuntu-latest) (push) Has been cancelled
Nuxt 4 + Supabase + Flightics API. Incluye búsqueda de vuelos, inspiraciones, watchlist, tracking de precios y mapa interactivo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
146 lines
4.9 KiB
Vue
146 lines
4.9 KiB
Vue
<script setup lang="ts">
|
|
const props = defineProps<{
|
|
flightCode: string
|
|
}>()
|
|
|
|
const info = ref<any>(null)
|
|
const fr24Url = ref<string | null>(null)
|
|
const loading = ref(false)
|
|
const loaded = ref(false)
|
|
|
|
async function loadInfo() {
|
|
if (loaded.value) return
|
|
loading.value = true
|
|
try {
|
|
const data = await $fetch<any>('/api/flight-info', {
|
|
query: { flightno: props.flightCode }
|
|
})
|
|
fr24Url.value = data.fr24Url || `https://www.flightradar24.com/data/flights/${props.flightCode.toLowerCase()}`
|
|
if (data.found) info.value = data.flight
|
|
} catch {
|
|
fr24Url.value = `https://www.flightradar24.com/data/flights/${props.flightCode.toLowerCase()}`
|
|
} finally {
|
|
loading.value = false
|
|
loaded.value = true
|
|
}
|
|
}
|
|
|
|
function formatAltitude(ft: number) {
|
|
return `${Math.round(ft * 0.3048)}m (FL${Math.round(ft / 100)})`
|
|
}
|
|
|
|
function formatSpeed(knots: number) {
|
|
return `${Math.round(knots * 1.852)} km/h`
|
|
}
|
|
|
|
function formatDelay(min: number | null) {
|
|
if (min == null || min === 0) return null
|
|
if (min > 0) return `+${min} min`
|
|
return `${min} min`
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<!-- Toggle button -->
|
|
<UButton
|
|
:label="loading ? 'Cargando...' : (info ? 'Info del vuelo' : 'Ver info en vivo')"
|
|
:icon="info ? 'i-lucide-radar' : 'i-lucide-radio'"
|
|
color="neutral"
|
|
variant="ghost"
|
|
size="xs"
|
|
:loading="loading"
|
|
@click="loadInfo"
|
|
/>
|
|
|
|
<!-- Flight info panel -->
|
|
<Transition
|
|
enter-active-class="transition-all duration-200 ease-out"
|
|
enter-from-class="opacity-0 max-h-0"
|
|
enter-to-class="opacity-100 max-h-96"
|
|
leave-active-class="transition-all duration-150 ease-in"
|
|
leave-from-class="opacity-100 max-h-96"
|
|
leave-to-class="opacity-0 max-h-0"
|
|
>
|
|
<div v-if="info" class="mt-2 overflow-hidden">
|
|
<div class="rounded-lg bg-neutral-50 dark:bg-neutral-800/50 p-3 space-y-2 text-sm">
|
|
<!-- Status -->
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-2">
|
|
<span
|
|
class="w-2 h-2 rounded-full"
|
|
:class="info.onGround ? 'bg-amber-500' : info.altitude > 0 ? 'bg-green-500 animate-pulse' : 'bg-neutral-400'"
|
|
/>
|
|
<span class="font-medium">{{ info.status }}</span>
|
|
</div>
|
|
<a
|
|
:href="info.fr24Url"
|
|
target="_blank"
|
|
class="text-xs text-primary-500 hover:underline"
|
|
>
|
|
Flightradar24
|
|
<UIcon name="i-lucide-external-link" class="inline text-[10px]" />
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Aircraft & airline -->
|
|
<div class="grid grid-cols-2 gap-2 text-xs">
|
|
<div v-if="info.aircraft">
|
|
<span class="text-muted">Avion</span>
|
|
<p class="font-medium">{{ info.aircraftAge || info.aircraft }}</p>
|
|
</div>
|
|
<div v-if="info.registration">
|
|
<span class="text-muted">Matricula</span>
|
|
<p class="font-medium">{{ info.registration }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Live data (if in flight) -->
|
|
<div v-if="info.altitude > 0 && !info.onGround" class="grid grid-cols-3 gap-2 text-xs">
|
|
<div>
|
|
<span class="text-muted">Altitud</span>
|
|
<p class="font-medium">{{ formatAltitude(info.altitude) }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-muted">Velocidad</span>
|
|
<p class="font-medium">{{ formatSpeed(info.speed) }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="text-muted">Rumbo</span>
|
|
<p class="font-medium">{{ info.heading }}°</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delays -->
|
|
<div v-if="formatDelay(info.departureDelay) || formatDelay(info.arrivalDelay)" class="flex gap-4 text-xs">
|
|
<div v-if="formatDelay(info.departureDelay)">
|
|
<span class="text-muted">Retraso salida</span>
|
|
<p class="font-medium" :class="info.departureDelay > 0 ? 'text-red-500' : 'text-green-500'">
|
|
{{ formatDelay(info.departureDelay) }}
|
|
</p>
|
|
</div>
|
|
<div v-if="formatDelay(info.arrivalDelay)">
|
|
<span class="text-muted">Retraso llegada</span>
|
|
<p class="font-medium" :class="info.arrivalDelay > 0 ? 'text-red-500' : 'text-green-500'">
|
|
{{ formatDelay(info.arrivalDelay) }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
|
|
<!-- Not found — link to FR24 anyway -->
|
|
<div v-if="loaded && !info && !loading" class="mt-1">
|
|
<a
|
|
:href="fr24Url"
|
|
target="_blank"
|
|
class="text-xs text-muted hover:text-primary-500 transition-colors"
|
|
>
|
|
No hay datos en vivo · Ver historial en Flightradar24
|
|
<UIcon name="i-lucide-external-link" class="inline text-[10px]" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</template>
|