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:
135
app/composables/useWatchlist.ts
Normal file
135
app/composables/useWatchlist.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
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 }
|
||||
}
|
||||
Reference in New Issue
Block a user