- 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
66 lines
2.2 KiB
Vue
66 lines
2.2 KiB
Vue
<template>
|
|
<div>
|
|
<!-- Loading state -->
|
|
<div
|
|
v-if="loading"
|
|
class="p-6 bg-white rounded-2xl border border-zinc-300 animate-pulse space-y-2"
|
|
>
|
|
<div class="h-4 bg-zinc-200 rounded w-1/3"></div>
|
|
<div class="h-6 bg-zinc-200 rounded w-2/3"></div>
|
|
</div>
|
|
|
|
<!-- Free / guest: compact one-liner -->
|
|
<div
|
|
v-else-if="!isPaidTier"
|
|
class="flex items-center gap-3 px-3 py-3 bg-white rounded-2xl border border-zinc-300"
|
|
>
|
|
<div :class="['shrink-0 w-10 h-10 rounded-full flex items-center justify-center', accentBg]">
|
|
<iconify-icon :icon="genericIcon" class="text-xl text-white"></iconify-icon>
|
|
</div>
|
|
<p class="flex-1 text-sm text-zinc-800 font-medium leading-snug">
|
|
{{ genericSentence }}
|
|
</p>
|
|
<a
|
|
class="hidden sm:inline-flex shrink-0 text-sm font-bold text-accent hover:text-accent-content whitespace-nowrap"
|
|
href="/pricing"
|
|
>
|
|
See full prediction →
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Paid: full prediction -->
|
|
<PredictionFull v-else :prediction="prediction" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
import PredictionFull from './PredictionFull.vue'
|
|
|
|
const props = defineProps({
|
|
prediction: { type: Object, default: null },
|
|
loading: { type: Boolean, default: false },
|
|
isPaidTier: { type: Boolean, default: false },
|
|
})
|
|
|
|
const direction = computed(() => props.prediction?.predicted_direction ?? 'stable')
|
|
|
|
const genericSentence = computed(() => ({
|
|
up: 'UK fuel prices are trending upward this week.',
|
|
down: 'UK fuel prices have been falling this week.',
|
|
stable: 'UK fuel prices have been steady this week.',
|
|
})[direction.value] ?? 'UK fuel prices have been steady this week.')
|
|
|
|
const genericIcon = computed(() => ({
|
|
up: 'lucide:trending-up',
|
|
down: 'lucide:trending-down',
|
|
stable: 'lucide:minus',
|
|
})[direction.value] ?? 'lucide:minus')
|
|
|
|
const accentBg = computed(() => ({
|
|
up: 'bg-mauve',
|
|
down: 'bg-teal',
|
|
stable: 'bg-tan',
|
|
})[direction.value] ?? 'bg-tan')
|
|
</script>
|