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'
This commit is contained in:
@@ -220,7 +220,25 @@
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-4xl md:text-5xl font-black font-display text-zinc-800 mb-4">Pricing for every driver</h2>
|
||||
<p class="text-zinc-500 text-lg">Save hundreds for less than the cost of a coffee.</p>
|
||||
<p class="text-zinc-500 text-lg mb-8">Save hundreds for less than the cost of a coffee.</p>
|
||||
<div class="inline-flex items-center gap-1 p-1 bg-white border border-zinc-300 rounded-full">
|
||||
<button
|
||||
:class="cadence === 'monthly' ? 'bg-accent text-white' : 'text-zinc-500'"
|
||||
class="px-5 py-2 rounded-full text-sm font-bold transition-colors"
|
||||
type="button"
|
||||
@click="cadence = 'monthly'"
|
||||
>
|
||||
Monthly
|
||||
</button>
|
||||
<button
|
||||
:class="cadence === 'annual' ? 'bg-accent text-white' : 'text-zinc-500'"
|
||||
class="px-5 py-2 rounded-full text-sm font-bold transition-colors"
|
||||
type="button"
|
||||
@click="cadence = 'annual'"
|
||||
>
|
||||
Annual <span class="text-[10px] opacity-80">(save 17%)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
@@ -238,7 +256,7 @@
|
||||
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Daily Updates</li>
|
||||
<li class="text-sm flex gap-2 text-zinc-500"><iconify-icon class="text-zinc-300" icon="lucide:x"></iconify-icon> No Alerts</li>
|
||||
</ul>
|
||||
<a class="w-full py-3 px-4 border border-zinc-300 rounded-xl text-center font-bold hover:bg-zinc-100 transition-colors" href="/register">Get Started</a>
|
||||
<a :href="ctaHref('free')" class="w-full py-3 px-4 border border-zinc-300 rounded-xl text-center font-bold hover:bg-zinc-100 transition-colors">{{ ctaLabel('free') }}</a>
|
||||
</div>
|
||||
|
||||
<!-- Basic -->
|
||||
@@ -246,8 +264,8 @@
|
||||
<div class="mb-8">
|
||||
<h4 class="text-xl font-bold font-display mb-2">Basic</h4>
|
||||
<div class="flex items-baseline gap-1">
|
||||
<span class="text-4xl font-black">£0.99</span>
|
||||
<span class="text-zinc-500 text-sm">/mo</span>
|
||||
<span class="text-4xl font-black">{{ PRICES[cadence].basic }}</span>
|
||||
<span class="text-zinc-500 text-sm">{{ PRICE_SUFFIX[cadence] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="space-y-4 mb-8 flex-1">
|
||||
@@ -255,7 +273,7 @@
|
||||
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> 14-day Trend Data</li>
|
||||
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> 3 Daily Price Alerts</li>
|
||||
</ul>
|
||||
<a class="w-full py-3 px-4 border border-zinc-300 rounded-xl text-center font-bold hover:bg-zinc-100 transition-colors" href="/register">Select Basic</a>
|
||||
<a :href="ctaHref('basic')" class="w-full py-3 px-4 border border-zinc-300 rounded-xl text-center font-bold hover:bg-zinc-100 transition-colors">{{ ctaLabel('basic') }}</a>
|
||||
</div>
|
||||
|
||||
<!-- Plus -->
|
||||
@@ -264,8 +282,8 @@
|
||||
<div class="mb-8">
|
||||
<h4 class="text-xl font-bold font-display mb-2">Plus</h4>
|
||||
<div class="flex items-baseline gap-1">
|
||||
<span class="text-4xl font-black text-accent">£2.49</span>
|
||||
<span class="text-zinc-500 text-sm">/mo</span>
|
||||
<span class="text-4xl font-black text-accent">{{ PRICES[cadence].plus }}</span>
|
||||
<span class="text-zinc-500 text-sm">{{ PRICE_SUFFIX[cadence] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="space-y-4 mb-8 flex-1">
|
||||
@@ -273,7 +291,7 @@
|
||||
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Priority Price Alerts</li>
|
||||
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Multi-location tracking</li>
|
||||
</ul>
|
||||
<a class="w-full py-3 px-4 bg-accent text-white rounded-xl text-center font-bold shadow-lg hover:bg-primary-dark transition-all" href="/register">Join Plus</a>
|
||||
<a :href="ctaHref('plus')" class="w-full py-3 px-4 bg-accent text-white rounded-xl text-center font-bold shadow-lg hover:bg-primary-dark transition-all">{{ ctaLabel('plus') }}</a>
|
||||
</div>
|
||||
|
||||
<!-- Pro -->
|
||||
@@ -281,8 +299,8 @@
|
||||
<div class="mb-8">
|
||||
<h4 class="text-xl font-bold font-display mb-2">Pro</h4>
|
||||
<div class="flex items-baseline gap-1">
|
||||
<span class="text-4xl font-black">£3.99</span>
|
||||
<span class="text-zinc-400 text-sm">/mo</span>
|
||||
<span class="text-4xl font-black">{{ PRICES[cadence].pro }}</span>
|
||||
<span class="text-zinc-400 text-sm">{{ PRICE_SUFFIX[cadence] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="space-y-4 mb-8 flex-1">
|
||||
@@ -290,7 +308,7 @@
|
||||
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Multi-Vehicle Fleet</li>
|
||||
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Exportable Price History</li>
|
||||
</ul>
|
||||
<a class="w-full py-3 px-4 bg-white text-zinc-800 rounded-xl text-center font-bold hover:bg-zinc-100 transition-colors" href="/register">Go Pro</a>
|
||||
<a :href="ctaHref('pro')" class="w-full py-3 px-4 bg-white text-zinc-800 rounded-xl text-center font-bold hover:bg-zinc-100 transition-colors">{{ ctaLabel('pro') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -417,10 +435,45 @@ import SearchBar from '../components/SearchBar.vue'
|
||||
import LeafletMap from '../components/LeafletMap.vue'
|
||||
import StationList from '../components/StationList.vue'
|
||||
|
||||
const { isAuthenticated } = useAuth()
|
||||
const { isAuthenticated, userTier } = useAuth()
|
||||
|
||||
const cadence = ref('monthly')
|
||||
|
||||
function ctaHref(tier) {
|
||||
if (tier === 'free') {
|
||||
return isAuthenticated.value ? '/dashboard' : '/register'
|
||||
}
|
||||
if (!isAuthenticated.value) {
|
||||
return '/register?tier=' + tier + '&cadence=' + cadence.value
|
||||
}
|
||||
if (userTier.value === tier) {
|
||||
return '/billing/portal'
|
||||
}
|
||||
return '/billing/checkout/' + tier + '/' + cadence.value
|
||||
}
|
||||
|
||||
function ctaLabel(tier) {
|
||||
if (tier === 'free') {
|
||||
return isAuthenticated.value ? 'Go to dashboard' : 'Get started'
|
||||
}
|
||||
if (isAuthenticated.value && userTier.value === tier) {
|
||||
return 'Manage subscription'
|
||||
}
|
||||
return {
|
||||
basic: 'Select Basic',
|
||||
plus: 'Join Plus',
|
||||
pro: 'Go Pro',
|
||||
}[tier]
|
||||
}
|
||||
|
||||
const PRICES = {
|
||||
monthly: { basic: '£0.99', plus: '£2.49', pro: '£3.99' },
|
||||
annual: { basic: '£9.90', plus: '£24.90', pro: '£39.90' },
|
||||
}
|
||||
const PRICE_SUFFIX = { monthly: '/mo', annual: '/yr' }
|
||||
const { stations, loading, error, search } = useStations()
|
||||
|
||||
const sort = ref('price')
|
||||
const sort = ref('reliable')
|
||||
const lastParams = ref(null)
|
||||
const searchAttempted = ref(false)
|
||||
const radiusMiles = ref(10)
|
||||
|
||||
@@ -9,12 +9,9 @@
|
||||
v-model="form.preferred_fuel_type"
|
||||
class="w-full h-12 px-4 bg-zinc-50 border border-zinc-300 rounded-xl font-medium text-zinc-800 focus:outline-none focus:ring-2 focus:ring-accent"
|
||||
>
|
||||
<option value="petrol">Petrol (E10)</option>
|
||||
<option value="diesel">Diesel (B7)</option>
|
||||
<option value="e5">Premium Unleaded (E5)</option>
|
||||
<option value="b7_premium">Premium Diesel</option>
|
||||
<option value="b10">B10 Biodiesel</option>
|
||||
<option value="hvo">HVO</option>
|
||||
<option v-for="fuel in FUEL_TYPES" :key="fuel.value" :value="fuel.value">
|
||||
{{ fuel.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -46,14 +43,15 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import api from '../../axios.js'
|
||||
import { FUEL_TYPES } from '../../constants/fuelTypes.js'
|
||||
|
||||
const form = ref({ preferred_fuel_type: 'petrol', postcode: '' })
|
||||
const form = ref({ preferred_fuel_type: 'e10', postcode: '' })
|
||||
const saving = ref(false)
|
||||
const saved = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
const response = await api.get('/user/preferences')
|
||||
form.value.preferred_fuel_type = response.data.preferred_fuel_type ?? 'petrol'
|
||||
form.value.preferred_fuel_type = response.data.preferred_fuel_type ?? 'e10'
|
||||
form.value.postcode = response.data.postcode ?? ''
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user