diff --git a/.claude/rules/api-data.md b/.claude/rules/api-data.md index ed7f44d..48b2022 100644 --- a/.claude/rules/api-data.md +++ b/.claude/rules/api-data.md @@ -80,7 +80,7 @@ for that `(station_id, fuel_type)` combination. Avoids row explosion on unchange ``` FUEL_FINDER_CLIENT_ID= FUEL_FINDER_CLIENT_SECRET= -FUEL_FINDER_BASE_URL=https://api.fuel-finder.service.gov.uk +FUEL_FINDER_BASE_URL=https://www.fuel-finder.service.gov.uk/api/v1 ``` ## Postcodes.io — location resolution diff --git a/app/Services/FuelPriceService.php b/app/Services/FuelPriceService.php index c1baeaa..ad66f47 100644 --- a/app/Services/FuelPriceService.php +++ b/app/Services/FuelPriceService.php @@ -45,7 +45,8 @@ class FuelPriceService { return Cache::remember(self::TOKEN_CACHE_KEY, 3540, function (): string { $url = config('services.fuel_finder.base_url').'/oauth/generate_access_token'; - $response = $this->apiLogger->send('fuel_finder', 'POST', $url, fn () => Http::timeout(10) + $response = $this->apiLogger->send('fuel_finder', 'POST', $url, fn () => Http::retry(3, 500) + ->timeout(60) ->post($url, [ 'client_id' => config('services.fuel_finder.client_id'), 'client_secret' => config('services.fuel_finder.client_secret'), diff --git a/resources/js/components/LeafletMap.vue b/resources/js/components/LeafletMap.vue index ae08835..1486dd5 100644 --- a/resources/js/components/LeafletMap.vue +++ b/resources/js/components/LeafletMap.vue @@ -91,8 +91,8 @@ function getZoomForRadius(radiusMiles) { if (radiusMiles <= 1) return 16 if (radiusMiles <= 2) return 15 if (radiusMiles <= 5) return 14 - if (radiusMiles <= 10) return 12 - if (radiusMiles <= 15) return 12 + if (radiusMiles <= 10) return 11 + if (radiusMiles <= 15) return 11 if (radiusMiles <= 20) return 10 if (radiusMiles <= 25) return 10 if (radiusMiles <= 50) return 9 diff --git a/resources/js/components/SearchBar.vue b/resources/js/components/PostSearchFilters.vue similarity index 59% rename from resources/js/components/SearchBar.vue rename to resources/js/components/PostSearchFilters.vue index 3671e52..47b7a86 100644 --- a/resources/js/components/SearchBar.vue +++ b/resources/js/components/PostSearchFilters.vue @@ -1,11 +1,10 @@ @@ -80,13 +117,22 @@ const DEFAULTS = Object.freeze({ sort: 'reliable', }) +const sortOptions = [ + { label: 'Reliable', value: 'reliable', icon: 'lucide:shield-check' }, + { label: 'Price', value: 'price', icon: 'lucide:pound-sterling' }, + { label: 'Distance', value: 'distance', icon: 'lucide:map-pin' }, + { label: 'Updated', value: 'updated', icon: 'lucide:clock' }, +] + const props = defineProps({ initial: { type: Object, default: () => ({}) }, - resultCount: { type: Number, default: null }, + brands: { type: Array, default: () => [] }, + brandFilter: { type: String, default: '' }, mapOpen: { type: Boolean, default: true }, + stationCount: { type: Number, default: 0 }, }) -const emit = defineEmits(['search', 'toggle-map']) +const emit = defineEmits(['search', 'toggle-map', 'update:brandFilter']) const postcode = ref('') const coords = ref(null) @@ -120,12 +166,14 @@ const hasActive = computed(() => ( fuelType.value !== DEFAULTS.fuelType || radius.value !== DEFAULTS.radius || sort.value !== DEFAULTS.sort + || Boolean(props.brandFilter) )) function resetFilters() { fuelType.value = DEFAULTS.fuelType radius.value = DEFAULTS.radius sort.value = DEFAULTS.sort + if (props.brandFilter) emit('update:brandFilter', '') } function emitSearch() { diff --git a/resources/js/components/StationList.vue b/resources/js/components/StationList.vue index 902069c..e14ee30 100644 --- a/resources/js/components/StationList.vue +++ b/resources/js/components/StationList.vue @@ -1,47 +1,5 @@ @@ -393,7 +392,7 @@ import { RouterLink, useRoute, useRouter } from 'vue-router' import { useAuth } from '../composables/useAuth.js' import { useStations } from '../composables/useStations.js' import api from '../axios.js' -import SearchBar from '../components/SearchBar.vue' +import PostSearchFilters from '../components/PostSearchFilters.vue' import StationList from '../components/StationList.vue' const LeafletMap = defineAsyncComponent(() => import('../components/LeafletMap.vue')) @@ -453,7 +452,7 @@ const PRICES = { annual: { basic: '£9.90', plus: '£24.90', pro: '£39.90' }, } const PRICE_SUFFIX = { monthly: '/mo', annual: '/yr' } -const { stations, meta, loading, error, search } = useStations() +const { stations, meta, loading, error, search, reset } = useStations() watch(loading, (isLoading) => { if (!isLoading) return @@ -571,16 +570,15 @@ async function onSearch(params) { await runSearch(params) } -async function onSort(newSort) { - if (!lastParams.value) return - const next = { ...lastParams.value, sort: newSort } - await router.push({ query: queryFromParams(next) }) - await runSearch(next) -} - watch(() => route.query, (query) => { const params = paramsFromQuery(query) - if (!params) return + if (!params) { + searchAttempted.value = false + lastParams.value = null + brandFilter.value = '' + reset() + return + } const sameAsLast = lastParams.value && JSON.stringify(queryFromParams(lastParams.value)) === JSON.stringify(queryFromParams(params)) if (sameAsLast) return