Establishes core rules and conventions for the FuelAlert Laravel application: architecture patterns (fat services, thin controllers), database schema with partitioned station_prices table, multi-tier notification system with Vonage and OneSignal, 4-signal scoring engine for fuel price recommendations, Stripe subscription tiers, Livewire classic component structure, and Pest testing standards.
2.2 KiB
Livewire Components
Classic two-file components only
Do NOT use Volt single-file syntax for new components. Volt files created by the Livewire starter kit (auth screens) are left untouched.
Component locations
app/Livewire/
├── Dashboard/
│ ├── FuelRecommendation.php # Main fill-up/wait card
│ ├── NearbyStations.php # Map + station list
│ └── PriceHistory.php # 14-day trend chart
├── Account/
│ ├── NotificationSettings.php # Channel prefs + WhatsApp OTP
│ ├── SubscriptionManager.php # Upgrade/downgrade UI
│ └── FuelPreferences.php # Fuel type, postcode
└── Public/
└── PriceWatchdog.php # Public-facing local price watchdog
Component conventions
- Component properties that are shown in the view must be
public - Use
#[Computed]attribute for derived values — not re-computed on every render - Validate with
#[Validate]attribute on properties, not in separate rules array - Never put Service instantiation in the component — inject via method parameter or mount()
- Dispatch browser events with
$this->dispatch()not$this->emit()(Livewire 3 syntax) - Use
wire:loadingon all buttons that trigger server actions
Alpine.js usage
Alpine.js handles local UI state only: dropdowns, modals, tab switching, copy-to-clipboard.
Do not replicate server state in Alpine — use Livewire for anything that needs PHP.
Alpine components stay inline in Blade (x-data="{}"), not in separate JS files unless reused 3+ times.
Map (Leaflet.js)
Leaflet is a plain JS drop-in, not a Livewire component. Station data is fetched from a dedicated Livewire endpoint and passed to Leaflet via Alpine:
<div
x-data="stationMap(@entangle('stations'))"
id="map"
style="height: 400px"
></div>
Map initialisation lives in resources/js/maps/station-map.js.
Page routing
Livewire full-page components are mounted in routes/web.php using Route::get()->component().
No separate view files for pages — the Livewire component IS the page.
paths:
- "app/Livewire/**/*.php"
- "resources/views/livewire/**/*.blade.php"