Initial commit: Vuelato - buscador de vuelos
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>
This commit is contained in:
Alejandro Martinez
2026-04-10 23:37:06 +02:00
commit b8906efc80
122 changed files with 37809 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
<script setup lang="ts">
import { Line } from 'vue-chartjs'
import type { ChartOptions } from 'chart.js'
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler
} from 'chart.js'
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler)
const props = defineProps<{
snapshots: Array<{
cheapest_price: number
avg_price: number | null
median_price: number | null
total_results: number
recorded_at: string
}>
}>()
const chartData = computed(() => {
const labels = props.snapshots.map(s =>
new Date(s.recorded_at).toLocaleDateString('es-ES', { day: 'numeric', month: 'short' })
)
return {
labels,
datasets: [
{
label: 'Precio mas barato',
data: props.snapshots.map(s => s.cheapest_price),
borderColor: '#16a34a',
backgroundColor: 'rgba(22, 163, 74, 0.1)',
fill: true,
tension: 0.3,
pointRadius: 4,
pointHoverRadius: 6
},
{
label: 'Precio medio',
data: props.snapshots.map(s => s.avg_price),
borderColor: '#9ca3af',
backgroundColor: 'transparent',
borderDash: [],
tension: 0.3,
pointRadius: 2,
pointHoverRadius: 4
},
{
label: 'Mediana',
data: props.snapshots.map(s => s.median_price),
borderColor: '#d1d5db',
backgroundColor: 'transparent',
borderDash: [5, 5],
tension: 0.3,
pointRadius: 2,
pointHoverRadius: 4
}
]
}
})
const chartOptions: ChartOptions<'line'> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom' as const,
labels: {
usePointStyle: true,
padding: 16
}
},
tooltip: {
callbacks: {
label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y?.toFixed(0)}\u20AC`
}
}
},
scales: {
y: {
beginAtZero: false,
ticks: {
callback: (value) => `${value}\u20AC`
}
}
},
interaction: {
intersect: false,
mode: 'index' as const
}
}
</script>
<template>
<div>
<div v-if="snapshots.length < 2" class="text-center py-8">
<UIcon name="i-lucide-chart-line" class="text-3xl text-neutral-300 mb-2" />
<p class="text-neutral-500 text-sm">Se necesitan al menos 2 puntos de datos para mostrar el grafico</p>
</div>
<div v-else class="h-72">
<Line :data="chartData" :options="chartOptions" />
</div>
</div>
</template>