Remove obsolete Livewire fuel search components and consolidate pricing tiers
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

- 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'
This commit is contained in:
Ovidiu U
2026-04-20 14:12:15 +01:00
parent aec547cd86
commit 5acb99c9e3
33 changed files with 739 additions and 391 deletions

View File

@@ -2,7 +2,7 @@
namespace App\Http\Controllers\Api;
use App\Enums\PriceClassification;
use App\Enums\PriceReliability;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\NearbyStationsRequest;
use App\Http\Resources\Api\StationResource;
@@ -57,12 +57,20 @@ class StationController extends Controller
$filtered = $all->filter(fn ($s) => (float) $s->distance_km <= $radius);
$stations = $sort === 'reliable'
? $filtered->sortBy([
fn ($s) => PriceClassification::fromUpdatedAt(
$s->price_effective_at ? Carbon::parse($s->price_effective_at) : null
)->weight(),
fn ($s) => (int) $s->price_pence,
])->values()
? $filtered
->sort(function ($a, $b) {
$weightA = PriceReliability::fromUpdatedAt(
$a->price_effective_at ? Carbon::parse($a->price_effective_at) : null
)->weight();
$weightB = PriceReliability::fromUpdatedAt(
$b->price_effective_at ? Carbon::parse($b->price_effective_at) : null
)->weight();
return $weightA <=> $weightB
?: ((int) $a->price_pence <=> (int) $b->price_pence)
?: ((float) $a->distance_km <=> (float) $b->distance_km);
})
->values()
: $filtered->sortBy(match ($sort) {
'price' => fn ($s) => (int) $s->price_pence,
'updated' => fn ($s) => $s->price_effective_at ? -strtotime($s->price_effective_at) : PHP_INT_MAX,
@@ -72,6 +80,12 @@ class StationController extends Controller
$prices = $stations->pluck('price_pence');
$reliabilityCounts = $stations
->groupBy(fn ($s) => PriceReliability::fromUpdatedAt(
$s->price_effective_at ? Carbon::parse($s->price_effective_at) : null
)->value)
->map->count();
Search::create([
'lat_bucket' => round($lat, 2),
'lng_bucket' => round($lng, 2),
@@ -96,6 +110,11 @@ class StationController extends Controller
'highest_pence' => $prices->max(),
'cheapest_price_pence' => $prices->min(),
'avg_pence' => $prices->isNotEmpty() ? round($prices->avg(), 2) : null,
'reliability_counts' => [
'reliable' => (int) $reliabilityCounts->get(PriceReliability::Reliable->value, 0),
'stale' => (int) $reliabilityCounts->get(PriceReliability::Stale->value, 0),
'outdated' => (int) $reliabilityCounts->get(PriceReliability::Outdated->value, 0),
],
],
]);
}