Files
vuelato/app/composables/useWatchlist.ts
Alejandro Martinez b8906efc80
Some checks failed
ci / ci (22, ubuntu-latest) (push) Has been cancelled
Initial commit: Vuelato - buscador de vuelos
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>
2026-04-10 23:37:06 +02:00

136 lines
3.9 KiB
TypeScript

interface WatchlistItem {
id: string
booking_token: string
route_summary: string
departure_code: string
arrival_code: string
departure_date: string
original_price: number
current_price: number | null
price_status: string
passengers_adult: number
passengers_child: number
passengers_infant: number
last_checked_at: string | null
created_at: string
}
export function useWatchlist() {
const supabase = useSupabaseClient()
const user = useSupabaseUser()
const items = ref<WatchlistItem[]>([])
const loading = ref(false)
async function fetchAll() {
if (!user.value) return
loading.value = true
const { data } = await supabase
.from('watchlist')
.select('*')
.order('created_at', { ascending: false })
items.value = (data as WatchlistItem[]) || []
loading.value = false
}
async function add(params: {
bookingToken: string
routeSummary: string
departureCode: string
arrivalCode: string
departureDate: string
price: number
passengers: { adult: number; child: number; infant: number }
}) {
if (!user.value) return false
const { error } = await supabase.from('watchlist').insert({
user_id: user.value.id,
booking_token: params.bookingToken,
route_summary: params.routeSummary,
departure_code: params.departureCode,
arrival_code: params.arrivalCode,
departure_date: params.departureDate,
original_price: params.price,
current_price: params.price,
passengers_adult: params.passengers.adult,
passengers_child: params.passengers.child,
passengers_infant: params.passengers.infant
})
if (!error) await fetchAll()
return !error
}
async function remove(id: string) {
const { error } = await supabase.from('watchlist').delete().eq('id', id)
if (!error) items.value = items.value.filter(i => i.id !== id)
return !error
}
async function checkPrice(item: WatchlistItem) {
try {
const data = await $fetch<any>('/api/check', {
method: 'POST',
body: {
bookingToken: item.booking_token,
local: 'en',
passengersCount: {
adult: item.passengers_adult,
child: item.passengers_child,
infant: item.passengers_infant
}
}
})
const newPrice = data.trip.totalCost
let status = 'available'
if (newPrice < item.original_price) status = 'price_down'
else if (newPrice > item.original_price) status = 'price_up'
await supabase.from('watchlist').update({
current_price: newPrice,
price_status: status,
last_checked_at: new Date().toISOString()
}).eq('id', item.id)
// Update local state
const idx = items.value.findIndex(i => i.id === item.id)
if (idx >= 0) {
items.value[idx] = { ...items.value[idx], current_price: newPrice, price_status: status, last_checked_at: new Date().toISOString() }
}
return { price: newPrice, status }
} catch {
await supabase.from('watchlist').update({
price_status: 'unavailable',
last_checked_at: new Date().toISOString()
}).eq('id', item.id)
const idx = items.value.findIndex(i => i.id === item.id)
if (idx >= 0) {
items.value[idx] = { ...items.value[idx], price_status: 'unavailable', last_checked_at: new Date().toISOString() }
}
return { price: null, status: 'unavailable' }
}
}
async function checkAll() {
for (const item of items.value) {
await checkPrice(item)
}
}
function isWatched(bookingToken: string) {
return items.value.some(i => i.booking_token === bookingToken)
}
function getWatchedItem(bookingToken: string) {
return items.value.find(i => i.booking_token === bookingToken)
}
watch(user, (u) => {
if (u) fetchAll()
else items.value = []
}, { immediate: true })
return { items, loading, fetchAll, add, remove, checkPrice, checkAll, isWatched, getWatchedItem }
}