- Hero: remove full-width mobile submit, add inline "Go" button next to locate
- Prediction cards: tighter mobile padding (px-3 py-3)
- Search filters: right-aligned toolbar, remove "X stations found" count and map toggle
- Map: initialize view immediately to avoid tile wiggle, skip recenter on fresh init
- Station list: hidden by default, toggled via "Stations {count}" pill above map
- Typography: hide desktop h1 on mobile, scale down section headings and spacing
- Footer: remove uppercase styling from headings and copyright line
- Filter popover: auto-close on fuel/radius/sort/brand selection
fix(llm): retry submit_overlay when events_cited is missing, extend Fuel Finder timeout with retries
- LlmOverlayService: add `minItems: 1` to events_cited schema, detect missing citations
in submit response, inject tool_result error and retry once with explicit prompt
- Log full raw_result context when no verified citations, capturing direction/confidence/reasoning
- FuelPriceService: add 3×1s retry with 60s timeout to batch price requests (was 30s no retry)
- Tests: cover successful retry recovery and rejection when retry also omits citations
103 lines
3.8 KiB
Vue
103 lines
3.8 KiB
Vue
<template>
|
|
<form class="w-full max-w-xl" @submit.prevent="submitPostcode">
|
|
<label class="flex items-center gap-2 h-14 md:h-15 pl-3.5 md:pl-4 pr-1.5 md:pr-2 bg-white md:bg-surface border border-zinc-200 rounded-2xl focus-within:border-primary transition-colors md:shadow-[0_20px_40px_-20px_rgba(0,0,0,0.12)]">
|
|
<iconify-icon class="text-zinc-400 text-lg shrink-0" icon="lucide:map-pin"></iconify-icon>
|
|
<input
|
|
v-model="postcode"
|
|
autocomplete="postal-code"
|
|
class="flex-1 min-w-0 bg-transparent outline-none text-[15px] md:text-base placeholder:text-zinc-400"
|
|
placeholder="Postcode"
|
|
type="text"
|
|
/>
|
|
|
|
<!-- Geolocation icon-button — visible on mobile AND desktop -->
|
|
<button
|
|
:disabled="locating"
|
|
aria-label="Use my location"
|
|
class="w-11 h-11 rounded-[10px] bg-zinc-100 text-primary inline-flex items-center justify-center shrink-0 hover:bg-zinc-200 transition-colors disabled:opacity-70"
|
|
type="button"
|
|
@click="useMyLocation"
|
|
>
|
|
<iconify-icon :class="{ 'animate-spin': locating }" :icon="locating ? 'lucide:loader-circle' : 'lucide:locate-fixed'" class="text-lg"></iconify-icon>
|
|
</button>
|
|
|
|
<!-- Mobile-only inline "Go" submit — sits next to the locate button -->
|
|
<button
|
|
class="md:hidden h-11 px-4 -ml-1 rounded-[10px] bg-primary text-white font-medium text-sm inline-flex items-center justify-center shrink-0 hover:opacity-90 transition"
|
|
type="submit"
|
|
>
|
|
Go
|
|
</button>
|
|
|
|
<!-- Desktop-only inline submit -->
|
|
<button
|
|
class="hidden md:inline-flex h-12 px-5 ml-1 rounded-xl bg-primary text-white font-medium text-[15px] items-center gap-2 hover:opacity-90 transition"
|
|
type="submit"
|
|
>
|
|
Check prices
|
|
<iconify-icon icon="lucide:arrow-right"></iconify-icon>
|
|
</button>
|
|
</label>
|
|
|
|
<div class="mt-3 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-zinc-400 justify-center md:justify-start">
|
|
<span class="hidden md:inline text-zinc-300">·</span>
|
|
<span class="hidden md:inline font-mono">Try SW1A 1AA · M1 1AD · EH1 1YZ</span>
|
|
</div>
|
|
</form>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, watch } from 'vue'
|
|
|
|
const props = defineProps({
|
|
initial: { type: Object, default: () => ({}) },
|
|
fuelType: { type: String, default: 'e10' },
|
|
radius: { type: Number, default: 10 },
|
|
sort: { type: String, default: 'reliable' },
|
|
})
|
|
|
|
const emit = defineEmits(['search'])
|
|
|
|
const postcode = ref('')
|
|
const locating = ref(false)
|
|
|
|
watch(() => props.initial, (v) => {
|
|
if (!v) return
|
|
if (typeof v.postcode === 'string') postcode.value = v.postcode
|
|
}, { immediate: true, deep: true })
|
|
|
|
function submitPostcode() {
|
|
const trimmed = postcode.value.trim()
|
|
if (!trimmed) return
|
|
emit('search', {
|
|
postcode: trimmed,
|
|
lat: null,
|
|
lng: null,
|
|
fuelType: props.fuelType,
|
|
radius: props.radius,
|
|
sort: props.sort,
|
|
})
|
|
}
|
|
|
|
function useMyLocation() {
|
|
if (!navigator.geolocation) return
|
|
locating.value = true
|
|
navigator.geolocation.getCurrentPosition(
|
|
({ coords }) => {
|
|
locating.value = false
|
|
postcode.value = ''
|
|
emit('search', {
|
|
postcode: null,
|
|
lat: coords.latitude,
|
|
lng: coords.longitude,
|
|
fuelType: props.fuelType,
|
|
radius: props.radius,
|
|
sort: props.sort,
|
|
})
|
|
},
|
|
() => { locating.value = false },
|
|
{ timeout: 8000, enableHighAccuracy: false, maximumAge: 30000 },
|
|
)
|
|
}
|
|
</script>
|