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

257
README.md Normal file
View File

@@ -0,0 +1,257 @@
# Vuelato
Buscador de vuelos con fechas flexibles, seguimiento de precios y exploracion de destinos en mapa. Construido con Nuxt 4, Supabase y la API de Flightics.
## Stack
| Capa | Tecnologia |
|------|-----------|
| Framework | [Nuxt 4](https://nuxt.com) (Vue 3, file-based routing) |
| UI | [@nuxt/ui v4](https://ui.nuxt.com) + Tailwind CSS v4 |
| Iconos | [Lucide](https://lucide.dev) via `@iconify-json/lucide` (`i-lucide-*`) |
| Auth + DB | [Supabase](https://supabase.com) (PostgreSQL, GoTrue, RLS) en Docker |
| Mapas | [Leaflet](https://leafletjs.com) via `@vue-leaflet/vue-leaflet` |
| Graficos | [Chart.js](https://www.chartjs.org) via `vue-chartjs` |
| API de vuelos | [Flightics](https://flightics.com) (proxy via server routes) |
| Vuelos en vivo | [Flightradar24](https://flightradar24.com) (endpoint no oficial, cache 5 min) |
| Runtime | [Nitro](https://nitro.build) (server routes, cached handlers) |
## Arrancar
```bash
# 1. Instalar dependencias
pnpm install
# 2. Copiar variables de entorno
cp .env.example .env
# 3. Levantar Supabase local
pnpm supabase:up
# 4. Arrancar Nuxt
pnpm dev
# 5. Sincronizar aeropuertos (3300+) y paises (223) desde Flightics
curl -X POST http://localhost:3000/api/sync/locations
# 6. (Opcional) Sincronizar aerolineas desde Wikidata
curl -X POST http://localhost:3000/api/sync/airlines
```
### URLs
| Servicio | URL |
|---|---|
| App | http://localhost:3000 |
| Supabase Studio | http://localhost:3100 |
| Supabase API (Kong) | http://localhost:8000 |
| PostgreSQL | localhost:54322 (user: postgres, pass: postgres) |
### Comandos
| Comando | Descripcion |
|---------|-------------|
| `pnpm dev` | Servidor de desarrollo (localhost:3000) |
| `pnpm build` | Build de produccion |
| `pnpm preview` | Preview del build |
| `pnpm lint` | ESLint |
| `pnpm typecheck` | Comprobacion de tipos Vue |
| `pnpm supabase:up` | Levantar Supabase Docker |
| `pnpm supabase:down` | Parar Supabase |
| `pnpm supabase:reset` | Reset completo (borra datos y volumenes) |
## Funcionalidades
### Busqueda de vuelos
- **5 modos de busqueda**: ida y vuelta, solo ida, multi-ciudad, fin de semana, explorar destinos
- **Origenes y destinos multiples**: selecciona varios aeropuertos con pills (badges)
- **Validacion de fechas**: "hasta" no puede ser anterior a "desde", no se permiten fechas pasadas; correccion automatica si se cambia "desde" a una fecha posterior a "hasta"
- **Filtros avanzados**: precio maximo (slider + input numerico editable), numero de escalas (botones rapidos + input numerico), aerolineas (filtrado exclusivo: solo vuelos operados exclusivamente por las seleccionadas), hora de salida
- **Nombres de aerolineas**: diccionario integrado de 50+ aerolineas comunes (AF = Air France, IB = Iberia, etc.) ya que la API no devuelve nombres
- **Ordenacion**: por precio, hora de salida, duracion de vuelo o numero de escalas
- **Info por tarjeta de vuelo**:
- Duracion de cada tramo (centrada en la linea de vuelo con icono de avion)
- Tiempo total de vuelo (suma de todos los tramos)
- Tiempo efectivo en destino (descontando vuelos)
- Noches en destino
- Dias totales del viaje (de salida a llegada, inclusive — util para vacaciones)
- **Hora en origen**: opcion (guardada en cuenta) para ver entre parentesis la hora equivalente en tu aeropuerto de salida, solo cuando hay diferencia de huso horario
- **Vista completa o compacta** de resultados
- **Creacion de seguimiento** directamente desde resultados
### Exploracion de destinos
- Mapa interactivo con Leaflet mostrando destinos baratos desde tu aeropuerto
- Tarjetas de destino con imagenes (Unsplash) y atribucion de fotografo
- Filtro por presupuesto y vuelos directos
- Click en destino navega a la ruta detallada
### Seguimiento de precios
- Crea seguimientos automaticos de busquedas de vuelos
- Configura frecuencia: cada 6h, 12h, diario, cada 2 dias, semanal
- **Edicion completa**: nombre, origenes, destinos (con pills), fechas (con validacion), estancia, frecuencia, fecha de expiracion, activo/pausado
- Grafico de evolucion de precios historico (7, 14, 30, 60 dias)
- Estadisticas: precio actual, minimo, maximo, media, tendencia
- Historial de ejecuciones con estado y duracion
### Multi-ciudad
- Inspiracion para itinerarios con varias paradas
- Seleccion de aeropuertos de origen multiples (con pills)
### Watchlist
- Guarda vuelos individuales con seguimiento de precio
- Verificacion de precios (badges: bajo/subio/no disponible)
- Replay de busquedas recientes
- Conversion de busquedas guardadas a seguimiento automatico
### Preferencias de usuario (`/settings`)
- **Busquedas**: aeropuertos de origen habituales (se aplican como defecto), pasajeros por defecto (adultos/menores/bebes)
- **Visualizacion**: mostrar hora en origen (sincronizado con la cuenta, fallback a cookie para anonimos)
- **Cuenta**: email del usuario
- Accesible desde el dropdown del menu de usuario > "Preferencias"
### Autenticacion
- Login con email/contrasena via Supabase GoTrue
- Login con Google (OAuth)
- Perfil creado automaticamente al registrarse (trigger PostgreSQL)
## Paginas (13)
| Ruta | Funcion |
|---|---|
| `/` | Home: buscador compacto, inspiraciones, budget explorer, multi-city, busquedas recientes |
| `/search` | Buscador dedicado con 5 modos |
| `/results` | Resultados con sort, filtros, seguimiento, vista dual, hora en origen |
| `/detail/[token]` | Itinerario completo, verificacion de precio, watchlist, compartir, tracking FR24 |
| `/explore` | Mapa Leaflet con destinos baratos, imagenes Unsplash, filtros |
| `/route/[from]-[to]` | Vuelos de una ruta agrupados por aerolinea |
| `/multi-city` | Inspiraciones multi-ciudad con origenes multiples |
| `/tracking` | Lista de seguimientos de precios |
| `/tracking/[id]` | Detalle y edicion de seguimiento, grafico de precios, historial |
| `/watchlist` | Vuelos guardados, verificacion de precios, busquedas recientes |
| `/settings` | Preferencias de usuario (busquedas, visualizacion, cuenta) |
| `/auth` | Login / registro |
| `/auth/confirm` | Callback OAuth |
## Estructura del proyecto
```
app/
pages/ # 13 paginas (file-based routing)
components/ # 33 componentes por dominio
auth/ # LoginForm, UserMenu
search/ # AirportInput, DateRangePicker, ModeTabs, etc.
results/ # ResultsToolbar, ResultsFilters, TripCardCompact
detail/ # SegmentCard, ItineraryTimeline, WatchlistToggle, etc.
map/ # FlightMap, MapControls
inspiration/ # BudgetExplorer, MultiCityCard
tracking/ # TrackingConfig, CreateTrackingForm, TrackedSearchCard, etc.
SearchForm.vue # Formulario principal (5 modos)
TripCard.vue # Tarjeta de vuelo con toda la info de tiempos
FlightLeg.vue # Tramo con duracion por segmento
PassengerPicker.vue # Selector adultos/menores/bebes
InspirationGrid.vue # Grid de inspiraciones
composables/ # 12 composables
useFlightSearch.ts # Busqueda con polling (hasta 3 rondas)
useResultFilters.ts # Filtrado exclusivo y ordenacion
useAirlineNames.ts # Diccionario de 50+ aerolineas + cache API
useOriginTime.ts # Preferencia hora en origen (cuenta + cookie)
useUserPreferences.ts # Perfil via /api/profile (useState singleton)
useTrackedSearches.ts # CRUD seguimientos de precios
useDestinationImages.ts # Imagenes Unsplash de destinos
useWatchlist.ts # Vuelos guardados
useRecentSearches.ts # Busquedas recientes
useLocations.ts # Aeropuertos y paises
useRouteFlights.ts # Vuelos entre dos aeropuertos
useAuth.ts # Login/logout/registro
server/
api/ # 22 endpoints Nitro
search.post.ts # Proxy busqueda Flightics
detail.post.ts # Detalle de vuelo
check.post.ts # Verificar precio
inspirations.get.ts # Inspiraciones por aeropuerto
locations.get.ts # Aeropuertos (cache 24h)
countries.get.ts # Paises (cache 24h)
airlines.get.ts # Aerolineas (cache 24h)
flight-info.get.ts # Info vuelo FR24 (cache 5min)
destination-image.get.ts # Imagenes Unsplash
profile.get.ts # Obtener perfil de usuario
profile.patch.ts # Actualizar perfil de usuario
route-flights.post.ts # Vuelos entre dos aeropuertos
weekend-search.post.ts # Busqueda de fin de semana
multi-city-inspirations.post.ts
sync/locations.post.ts # Sincronizar aeropuertos
sync/airlines.post.ts # Sincronizar aerolineas (Wikidata)
tracking/ # CRUD seguimientos + historial + ejecuciones
utils/
flightics.ts # Cliente API Flightics (tipos + funciones)
wikidata.ts # Cliente SPARQL Wikidata
supabase/
migrations/
00001_init.sql # profiles, watchlist, recent_searches, airports, countries
00002_search_queue.sql # tracked_searches, search_runs, price_snapshots
00003_profile_preferences.sql # show_origin_time en profiles
docker-compose.yml # Supabase stack (6 servicios)
```
## Base de datos
### Tablas publicas (SELECT para anon y authenticated)
- `airports` — 3300+ aeropuertos con IATA, nombre, lat/lon, ciudad, pais
- `countries` — 223 paises con codigo, nombre, idiomas
- `airlines` — aerolineas con IATA, ICAO y nombre
### Tablas de usuario (RLS: `auth.uid() = user_id`)
- `profiles` — aeropuertos habituales, pasajeros por defecto, locale, show_origin_time
- `watchlist` — vuelos guardados con precio original/actual y estado
- `recent_searches` — busquedas recientes con params y route_summary
- `tracked_searches` — seguimientos con frecuencia, estado, parametros de busqueda
- `search_runs` — ejecuciones de seguimientos
- `price_snapshots` — historico de precios por seguimiento
### Resetear y re-sincronizar
```bash
pnpm supabase:reset
pnpm dev
curl -X POST http://localhost:3000/api/sync/locations
curl -X POST http://localhost:3000/api/sync/airlines
```
## Notas de desarrollo
### Auto-import de componentes (Nuxt 4)
Nuxt deduplica el prefijo del directorio cuando el nombre del archivo ya empieza con el nombre del directorio:
- `results/ResultsFilters.vue``<ResultsFilters>` (no `<ResultsResultsFilters>`)
- `tracking/TrackingConfig.vue``<TrackingConfig>` (no `<TrackingTrackingConfig>`)
- `tracking/CreateTrackingForm.vue``<TrackingCreateTrackingForm>` (no empieza con "Tracking", se prefija)
### Nuxt UI v4
- `UToggle` no existe — usar `<USwitch>`
- `URange` no existe — no usar slider de Nuxt UI
### Timestamps de vuelos
Los campos `departureTimestamp`/`arrivalTimestamp` de Flightics estan en **hora local** de cada aeropuerto. Para calcular duraciones entre aeropuertos de distintas zonas horarias, usar siempre `departureUtcTimestamp`/`arrivalUtcTimestamp`.
### Preferencias de usuario
`useUserPreferences` usa `useState` de Nuxt para estado compartido y `$fetch('/api/profile')` para persistencia. La preferencia "hora en origen" (`useOriginTime`) lee de la cuenta del usuario si esta logueado, con fallback a `useCookie` para visitantes anonimos.
### API Flightics
- La busqueda requiere polling: primera respuesta tiene `notComplete: true`. El composable hace hasta 3 rondas automaticamente.
- `company.name` viene vacio (`""`) en la mayoria de respuestas. Se resuelve con diccionario integrado en `useAirlineNames` (50+ aerolineas).
- Las fechas vacias causan 400. Se generan fechas por defecto (hoy + 30 dias).
- `getInspirations` devuelve vacio para aeropuertos pequenos — es normal.
### Supabase Docker
- La imagen trae todos los roles internos pre-creados. Los SQL de init solo ajustan passwords.
- Si GoTrue falla: `docker compose down -v && docker compose up -d`.
- Studio: puerto 3100. Meta: puerto 8085 (remapeado de 8080).
## Variables de entorno
```bash
SUPABASE_URL=http://localhost:8000 # Kong gateway
SUPABASE_KEY=eyJ... # anon key (JWT demo)
SUPABASE_SERVICE_ROLE_KEY=eyJ... # service_role key (server-side)
```
Las keys demo estan en `.env.example`. Para produccion, generar nuevas con un `JWT_SECRET` propio.