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>
192 lines
6.0 KiB
Vue
192 lines
6.0 KiB
Vue
<script setup lang="ts">
|
|
const props = defineProps<{
|
|
search: {
|
|
id: string
|
|
name: string
|
|
interval_hours: number
|
|
is_active: boolean
|
|
expires_at: string | null
|
|
search_params: Record<string, unknown>
|
|
route_summary: string
|
|
}
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
updated: []
|
|
}>()
|
|
|
|
const { update } = useTrackedSearches()
|
|
|
|
const name = ref(props.search.name)
|
|
const intervalHours = ref(props.search.interval_hours)
|
|
const isActive = ref(props.search.is_active)
|
|
const expiresAt = ref(props.search.expires_at?.slice(0, 10) || '')
|
|
const saving = ref(false)
|
|
const today = new Date().toISOString().slice(0, 10)
|
|
|
|
// Parametros de busqueda editables
|
|
const params = props.search.search_params
|
|
const departures = ref((params.departures as string[])?.join(',') || '')
|
|
const destination = ref('')
|
|
const dateFrom = ref('')
|
|
const dateTo = ref('')
|
|
|
|
watch(dateFrom, (from) => {
|
|
if (from && dateTo.value && dateTo.value < from) {
|
|
dateTo.value = from
|
|
}
|
|
})
|
|
const stayMinDays = ref(2)
|
|
const stayMaxDays = ref(7)
|
|
|
|
// Extraer destino y fechas de los search_params guardados
|
|
const interval = params.departureDateInterval as { begin?: string; end?: string } | undefined
|
|
if (interval?.begin) dateFrom.value = interval.begin.slice(0, 10)
|
|
if (interval?.end) dateTo.value = interval.end.slice(0, 10)
|
|
|
|
const stops = params.stops as Array<{ locations: string[]; stayRange?: { min: number; max: number } }> | undefined
|
|
if (stops?.[0]?.locations?.length) destination.value = stops[0].locations.join(',')
|
|
if (stops?.[0]?.stayRange) {
|
|
stayMinDays.value = Math.round((stops[0].stayRange.min || 0) / 24) || 2
|
|
stayMaxDays.value = Math.round((stops[0].stayRange.max || 0) / 24) || 7
|
|
}
|
|
|
|
const intervalOptions = [
|
|
{ label: 'Cada 6 horas', value: 6 },
|
|
{ label: 'Cada 12 horas', value: 12 },
|
|
{ label: 'Diario (24h)', value: 24 },
|
|
{ label: 'Cada 2 dias', value: 48 },
|
|
{ label: 'Semanal', value: 168 }
|
|
]
|
|
|
|
function buildSearchParams() {
|
|
const depList = departures.value.split(',').map(s => s.trim().toUpperCase()).filter(Boolean)
|
|
const destList = destination.value.split(',').map(s => s.trim().toUpperCase()).filter(Boolean)
|
|
|
|
const from = dateFrom.value || new Date().toISOString().slice(0, 10)
|
|
const to = dateTo.value || new Date(Date.now() + 30 * 86400000).toISOString().slice(0, 10)
|
|
const isOneWay = from === to
|
|
|
|
const newStops = isOneWay
|
|
? [{
|
|
locations: destList,
|
|
stayRange: { min: 0, max: 0 },
|
|
stayDateRange: { begin: '0001-01-01T00:00:00', end: '0001-01-01T00:00:00' },
|
|
continueFromAny: false
|
|
}]
|
|
: [
|
|
{
|
|
locations: destList,
|
|
stayRange: { min: stayMinDays.value * 24, max: stayMaxDays.value * 24 },
|
|
stayDateRange: { begin: '0001-01-01T00:00:00', end: '0001-01-01T00:00:00' },
|
|
continueFromAny: true
|
|
},
|
|
{
|
|
locations: depList,
|
|
stayRange: { min: 0, max: 0 },
|
|
stayDateRange: { begin: '0001-01-01T00:00:00', end: '0001-01-01T00:00:00' },
|
|
continueFromAny: false
|
|
}
|
|
]
|
|
|
|
return {
|
|
...params,
|
|
departures: depList,
|
|
departureDateInterval: {
|
|
begin: `${from}T00:00:00+00:00`,
|
|
end: `${to}T00:00:00+00:00`
|
|
},
|
|
stops: newStops,
|
|
endInSameLocation: !isOneWay
|
|
}
|
|
}
|
|
|
|
async function save() {
|
|
saving.value = true
|
|
try {
|
|
const depList = departures.value.split(',').filter(Boolean)
|
|
const destList = destination.value.split(',').filter(Boolean)
|
|
const routeSummary = `${depList.join(',')} > ${destList.join(',')}`
|
|
|
|
await update(props.search.id, {
|
|
name: name.value,
|
|
interval_hours: intervalHours.value,
|
|
is_active: isActive.value,
|
|
expires_at: expiresAt.value || null,
|
|
search_params: buildSearchParams(),
|
|
route_summary: routeSummary
|
|
})
|
|
emit('updated')
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="font-semibold text-sm">Configuracion</h3>
|
|
</template>
|
|
|
|
<div class="space-y-3">
|
|
<UFormField label="Nombre">
|
|
<UInput v-model="name" class="w-full" />
|
|
</UFormField>
|
|
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<UFormField label="Origen">
|
|
<SearchAirportInput v-model="departures" placeholder="MAD" icon="i-lucide-plane-takeoff" multiple />
|
|
</UFormField>
|
|
<UFormField label="Destino">
|
|
<SearchAirportInput v-model="destination" placeholder="BCN" icon="i-lucide-plane-landing" multiple />
|
|
</UFormField>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<UFormField label="Fecha desde">
|
|
<UInput v-model="dateFrom" type="date" :min="today" :max="dateTo || undefined" class="w-full" />
|
|
</UFormField>
|
|
<UFormField label="Fecha hasta">
|
|
<UInput v-model="dateTo" type="date" :min="dateFrom || today" class="w-full" />
|
|
</UFormField>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<UFormField label="Estancia min (dias)">
|
|
<UInput v-model.number="stayMinDays" type="number" :min="1" class="w-full" />
|
|
</UFormField>
|
|
<UFormField label="Estancia max (dias)">
|
|
<UInput v-model.number="stayMaxDays" type="number" :min="1" class="w-full" />
|
|
</UFormField>
|
|
</div>
|
|
|
|
<USeparator />
|
|
|
|
<UFormField label="Frecuencia">
|
|
<select
|
|
v-model.number="intervalHours"
|
|
class="w-full rounded-md border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-2 text-sm"
|
|
>
|
|
<option v-for="opt in intervalOptions" :key="opt.value" :value="opt.value">
|
|
{{ opt.label }}
|
|
</option>
|
|
</select>
|
|
</UFormField>
|
|
|
|
<UFormField label="Expira el">
|
|
<UInput v-model="expiresAt" type="date" :min="today" class="w-full" />
|
|
</UFormField>
|
|
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm">Activa</span>
|
|
<USwitch v-model="isActive" />
|
|
</div>
|
|
</div>
|
|
|
|
<template #footer>
|
|
<UButton label="Guardar cambios" size="sm" :loading="saving" @click="save" />
|
|
</template>
|
|
</UCard>
|
|
</template>
|