# CLAUDE.md — Guia para trabajar en Vuelato ## Comandos rapidos ```bash pnpm dev # Dev server (localhost:3000) pnpm supabase:up # Levantar Supabase Docker pnpm supabase:down # Parar Supabase pnpm supabase:reset # Reset completo (borra datos) pnpm lint # ESLint pnpm typecheck # Vue type checking ``` ## Estructura del proyecto ``` app/ pages/ # 10 paginas (Nuxt file-based routing) components/ # 27 componentes organizados por dominio (auth/, search/, results/, detail/, map/, inspiration/) composables/ # 9 composables (useFlightSearch, useAuth, useWatchlist, etc.) assets/css/ # Tailwind CSS server/ api/ # 13 endpoints Nitro (proxy Flightics + sync + flight-info + airlines) utils/flightics.ts # Cliente API Flightics completo (tipos + 9 funciones) utils/wikidata.ts # Cliente SPARQL Wikidata (aerolineas) supabase/ migrations/ # SQL init (tablas cache + usuario + RLS + trigger) volumes/db/ # roles.sql, jwt.sql (config Supabase internal) volumes/api/ # kong.yml (API gateway config) docker-compose.yml # Supabase stack completo (6 servicios) ``` ## Convenciones ### Nuxt 4 - El directorio raiz de la app es `app/` (no src/). - Los server utils (`server/utils/`) se auto-importan en server routes. **No usar** `import ... from '~/server/utils/...'` — causa error porque `~` apunta a `app/`. - Componentes se auto-importan con prefijo de directorio: `search/AirportInput.vue` -> ``. ### Componentes - Usar `@nuxt/ui` v4 components (UCard, UButton, UInput, UBadge, UFormField, UPopover, etc.). - Iconos: `@iconify-json/lucide` con prefijo `i-lucide-*`. - No usar emojis en la UI. - Idioma de la UI: espanol (es). ### Composables - Cada composable maneja su propio estado reactivo. - Los composables de usuario (useWatchlist, useRecentSearches, useUserPreferences) hacen watch del user y cargan/limpian datos automaticamente al login/logout. - `useAirlineNames` es un cache reactivo global — aprende nombres de aerolineas de cualquier respuesta API y los resuelve en la vista. ### Server routes - Los endpoints de Flightics son proxy directos (sin cache, excepto locations y countries que cachean 24h). - `defineCachedEventHandler` para cachear respuestas (flight-info: 5 min, locations/countries/airlines: 24h). - Usar `serverSupabaseServiceRole` para operaciones server-side que necesitan bypass de RLS (ej: sync/locations, sync/airlines). ### Base de datos - Tablas publicas (airports, countries, airlines, etc.) accesibles por `anon` y `authenticated` (SELECT). - Tablas de usuario protegidas con RLS: `auth.uid() = user_id`. - Trigger `on_auth_user_created` crea profile automaticamente. - Para resetear: `pnpm supabase:reset` (borra volumen Docker y reinicia). - Para re-sincronizar aeropuertos: `curl -X POST http://localhost:3000/api/sync/locations`. - Para re-sincronizar aerolineas: `curl -X POST http://localhost:3000/api/sync/airlines` (datos de Wikidata: IATA, ICAO, nombre, logo). ## Gotchas ### Flightics API - La busqueda requiere polling: primera respuesta tiene `notComplete: true`. El composable hace hasta 3 rondas automaticamente. - `company.name` viene vacio en rondas de polling posteriores. Se resuelve via `useAirlineNames` (cache reactivo). - Fechas vacias en el payload causan 400. `useFlightSearch.buildPayload` genera fechas por defecto (hoy + 30 dias). - `getRouteFlights` devuelve trips ida+vuelta, no vuelos individuales. - `getInspirations` puede devolver vacio para aeropuertos pequenos — es normal. ### Supabase Docker - La imagen `supabase/postgres` crea roles internos automaticamente. Los SQL de init solo ajustan passwords. - Si GoTrue falla al arrancar, verificar que el volumen este limpio: `docker compose down -v && docker compose up -d`. - Studio corre en puerto 3100 (no 3000, que es Nuxt). - Meta corre en 8085 (remapeado de 8080 que suele estar ocupado). ### Frontend - `AirportInput` busca sin acentos (normaliza NFD). "malaga" encuentra "Malaga". - Modo `multiple` en AirportInput: badges + input. Backspace borra ultimo. Enter/click selecciona. - Leaflet se renderiza solo client-side (``). Requiere `import 'leaflet/dist/leaflet.css'`. - El `v-if="seg.company.name"` falla con string vacio — usar `useAirlineNames().resolve()` en su lugar. ## Variables de entorno ```bash SUPABASE_URL=http://localhost:8000 # Kong gateway SUPABASE_KEY=eyJ... # anon key (demo JWT) SUPABASE_SERVICE_ROLE_KEY=eyJ... # service_role key (para server-side) ``` Las keys demo estan en `.env.example`. Para produccion, generar nuevas con un JWT_SECRET propio.