# 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:loading` on 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: ```blade
``` 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" ---