Files
fuel-price/resources/js/components/StationList.vue
Ovidiu U 5acb99c9e3
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
Remove obsolete Livewire fuel search components and consolidate pricing tiers
- Delete unused Livewire Search test and fuel type select Blade component
- Move subscription webhook listener from EventServiceProvider to AppServiceProvider
- Add FUEL_TYPES global config to app layout for client-side use
- Add Billable trait to User model and include email_verified_at in fillable
- Implement monthly/annual cadence toggle with pricing display and smart CTA routing on homepage
- Update VerifyApiKeyMiddlewareTest to use e10 instead of petrol
- Refactor PollFuelPrices to auto-refresh stale stations based on last_seen_at
- Add incremental polling with cached timestamp and effective-start-timestamp param to FuelPriceService
- Normalize amenities/fuel_types from API objects to flat arrays, skip stations missing required fields
- Log response body on API failures in ApiLogger
- Default homepage sort to 'reliable' instead of 'price'
2026-04-20 14:12:15 +01:00

117 lines
4.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="space-y-3">
<!-- Sort tabs -->
<div class="flex gap-2 flex-wrap">
<button
v-for="option in sortOptions"
:key="option.value"
@click="emit('sort', option.value)"
:class="[
'px-4 py-1.5 rounded-full text-sm font-bold transition-colors',
currentSort === option.value
? 'bg-accent text-white'
: 'bg-white border border-zinc-300 text-zinc-500 hover:border-accent'
]"
>
{{ option.label }}
</button>
</div>
<!-- Count -->
<p class="text-sm text-zinc-500 font-medium">
{{ stations.length }} station{{ stations.length !== 1 ? 's' : '' }} found
</p>
<!-- Grouped results when sorting by reliability -->
<template v-if="currentSort === 'reliable'">
<section v-if="reliable.length" class="space-y-2">
<header class="flex items-center gap-2 pt-2">
<iconify-icon class="text-status-good text-lg" icon="lucide:shield-check"></iconify-icon>
<h3 class="font-black text-zinc-800">Reliable</h3>
<span class="text-xs text-zinc-500 font-medium">Updated in the last 3 days</span>
</header>
<StationCard
v-for="station in reliable"
:key="station.station_id"
:lowest-price="lowestPrice"
:station="station"
/>
</section>
<section v-if="stale.length" class="space-y-2 pt-4">
<header class="flex items-center gap-2">
<iconify-icon class="text-status-warn text-lg" icon="lucide:clock"></iconify-icon>
<h3 class="font-black text-zinc-800">Older prices</h3>
<span class="text-xs text-zinc-500 font-medium">37 days old verify before driving</span>
</header>
<div class="opacity-80">
<StationCard
v-for="station in stale"
:key="station.station_id"
:lowest-price="lowestPrice"
:station="station"
class="mb-2"
/>
</div>
</section>
<section v-if="outdated.length" class="space-y-2 pt-4">
<header class="flex items-center gap-2">
<iconify-icon class="text-status-bad text-lg" icon="lucide:triangle-alert"></iconify-icon>
<h3 class="font-black text-zinc-800">Outdated</h3>
<span class="text-xs text-zinc-500 font-medium">Over 7 days old likely inaccurate</span>
</header>
<div class="opacity-60">
<StationCard
v-for="station in outdated"
:key="station.station_id"
:lowest-price="lowestPrice"
:station="station"
class="mb-2"
/>
</div>
</section>
</template>
<!-- Flat list for other sort modes -->
<div v-else class="space-y-2">
<StationCard
v-for="station in stations"
:key="station.station_id"
:station="station"
:lowest-price="lowestPrice"
/>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import StationCard from './StationCard.vue'
const props = defineProps({
stations: { type: Array, required: true },
currentSort: { type: String, default: 'reliable' },
})
const emit = defineEmits(['sort'])
const sortOptions = [
{ label: 'Reliable', value: 'reliable' },
{ label: 'Price', value: 'price' },
{ label: 'Distance', value: 'distance' },
{ label: 'Updated', value: 'updated' },
{ label: 'Brand', value: 'brand' },
]
const reliable = computed(() => props.stations.filter(s => s.reliability === 'reliable'))
const stale = computed(() => props.stations.filter(s => s.reliability === 'stale'))
const outdated = computed(() => props.stations.filter(s => s.reliability === 'outdated'))
const lowestPrice = computed(() => {
if (!reliable.value.length && !props.stations.length) return null
const pool = reliable.value.length ? reliable.value : props.stations
return Math.min(...pool.map(s => s.price_pence))
})
</script>