The features JSON column required defensive fallback stubs in three
places (Plan::resolveForUser, PlanFeatures::__construct, PlanSeeder)
and silently swallowed misspelled keys. Typed columns give Eloquent
type-safe reads, simplify the Filament form (no more dotted JSON
paths), and let resolveForUser fail loud when the free row is
missing.
PlanFeatures public API is unchanged so consumers (jobs, middleware)
need no rewrites — one missed JSON read in SendScheduledWhatsAppJob
was caught and converted to a typed where() query.
tests/Pest.php seeds PlanSeeder in beforeEach so any feature test
that resolves a plan finds the free row, mirroring production where
plans always exist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Consolidate prediction functionality by merging /api/prediction endpoint into /api/stations response. Move prediction logic from PredictionController into StationController, returning prediction data alongside station results. Replace usePrediction composable with unified useStations that returns {stations, meta, prediction}. Remove PredictionRequest, related tests, and unused Vue components (FuelFinderTest, MapTest, RecommendationTest, StationListTest). Add PredictionFull component and UpsellBanner. Extend NationalFuelPredictionService to include weekly_summary (7-day series, yesterday/today averages, cheapest/priciest days) and oil signal from price_predictions table. Update Home.vue to consume prediction from stations response. Add Plan::resolveCadenceForUser helper and configure Cashier to use custom Subscription model.
Extends NearbyStationsRequest to accept `postcode` (full or outcode) as an alternative to lat/lng. PostcodeService resolves it via postcodes.io and falls through to coordinates. Also adds SearchResource to the Filament admin panel for viewing logged search activity with fuel type filter and price/distance stats columns. Includes SQLite GREATEST/LEAST function polyfills in AppServiceProvider for test compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>