# 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` → `` (no ``) - `tracking/TrackingConfig.vue` → `` (no ``) - `tracking/CreateTrackingForm.vue` → `` (no empieza con "Tracking", se prefija) ### Nuxt UI v4 - `UToggle` no existe — usar `` - `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.