Files
fuel-alert/resources/js/components/PredictionCard.vue
Ovidiu U 97e27fc057
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
feat(ui): mobile-first redesign — compact hero, inline submit button, map-first with collapsible list
- 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
2026-05-14 13:23:52 +01:00

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>