Initial commit: Vuelato - buscador de vuelos
Some checks failed
ci / ci (22, ubuntu-latest) (push) Has been cancelled
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:
257
README.md
Normal file
257
README.md
Normal 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.
|
||||
Reference in New Issue
Block a user