Remove prediction API endpoint and integrate into stations search
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.
This commit is contained in:
@@ -80,6 +80,41 @@ class Plan extends Model
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the active subscription cadence for a user.
|
||||
* Returns 'monthly' | 'annual', or null if the user has no paid subscription.
|
||||
*/
|
||||
public static function resolveCadenceForUser(User $user): ?string
|
||||
{
|
||||
if (! method_exists($user, 'subscriptions')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$priceId = $user->subscriptions()->active()->value('stripe_price');
|
||||
|
||||
if ($priceId === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$plan = static::where('stripe_price_id_monthly', $priceId)
|
||||
->orWhere('stripe_price_id_annual', $priceId)
|
||||
->first();
|
||||
|
||||
if ($plan === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($plan->stripe_price_id_monthly === $priceId) {
|
||||
return 'monthly';
|
||||
}
|
||||
|
||||
if ($plan->stripe_price_id_annual === $priceId) {
|
||||
return 'annual';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saved(function (): void {
|
||||
|
||||
@@ -9,7 +9,6 @@ use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
#[Fillable(['predicted_for', 'source', 'direction', 'confidence', 'reasoning', 'generated_at'])]
|
||||
class PricePrediction extends Model
|
||||
@@ -39,11 +38,17 @@ class PricePrediction extends Model
|
||||
*/
|
||||
public function scopeBestFirst(Builder $query): Builder
|
||||
{
|
||||
$priority = implode(', ', array_map(
|
||||
fn (string $v) => "'$v'",
|
||||
[PredictionSource::LlmWithContext->value, PredictionSource::Llm->value, PredictionSource::Ewma->value],
|
||||
));
|
||||
$priority = [
|
||||
PredictionSource::LlmWithContext->value,
|
||||
PredictionSource::Llm->value,
|
||||
PredictionSource::Ewma->value,
|
||||
];
|
||||
|
||||
return $query->orderByRaw("FIELD(source, $priority)");
|
||||
$cases = '';
|
||||
foreach ($priority as $rank => $source) {
|
||||
$cases .= " WHEN '$source' THEN $rank";
|
||||
}
|
||||
|
||||
return $query->orderByRaw("CASE source$cases ELSE ".count($priority).' END');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user