Files
thrillwiki_laravel/app/Livewire/Location/LocationSelectorComponent.php

167 lines
4.8 KiB
PHP

<?php
namespace App\Livewire\Location;
use Livewire\Component;
use App\Services\GeocodeService;
use App\Exceptions\GeocodingException;
use App\Exceptions\ValidationException;
class LocationSelectorComponent extends Component
{
// Search State
public string $searchQuery = '';
public array $searchResults = [];
public bool $isSearching = false;
public ?string $validationError = null;
// Location State
public ?float $latitude = null;
public ?float $longitude = null;
public ?string $formattedAddress = null;
public bool $isValidLocation = false;
// UI State
public bool $showSearchResults = false;
public bool $isLoadingLocation = false;
public string $mode = 'search'; // search|coordinates|current
// Component Configuration
protected $geocodeService;
protected $listeners = [
'mapLocationSelected' => 'handleMapLocation',
'clearLocation' => 'clearSelection',
];
public function mount(GeocodeService $geocodeService)
{
$this->geocodeService = $geocodeService;
}
public function render()
{
return view('livewire.location.location-selector');
}
public function updatedSearchQuery()
{
if (strlen($this->searchQuery) < 3) {
$this->searchResults = [];
$this->showSearchResults = false;
return;
}
try {
$this->isSearching = true;
$this->searchResults = $this->geocodeService->geocode($this->searchQuery);
$this->showSearchResults = true;
$this->validationError = null;
} catch (GeocodingException $e) {
$this->validationError = $e->getMessage();
} finally {
$this->isSearching = false;
}
}
public function selectLocation(array $location)
{
try {
if (!isset($location['lat'], $location['lon'])) {
throw new ValidationException('Invalid location data');
}
$this->setCoordinates($location['lat'], $location['lon']);
$this->formattedAddress = $location['display_name'] ?? null;
$this->showSearchResults = false;
$this->searchQuery = '';
$this->isValidLocation = true;
$this->validationError = null;
$this->dispatch('locationSelected', [
'latitude' => $this->latitude,
'longitude' => $this->longitude,
'address' => $this->formattedAddress,
]);
} catch (ValidationException $e) {
$this->validationError = $e->getMessage();
$this->isValidLocation = false;
}
}
public function setCoordinates(float $lat, float $lon)
{
try {
if (!$this->geocodeService->validateCoordinates($lat, $lon)) {
throw new ValidationException('Invalid coordinates');
}
$this->latitude = $lat;
$this->longitude = $lon;
$this->isValidLocation = true;
$this->validationError = null;
$this->dispatch('coordinatesChanged', [
'latitude' => $this->latitude,
'longitude' => $this->longitude,
]);
} catch (ValidationException $e) {
$this->validationError = $e->getMessage();
$this->isValidLocation = false;
}
}
public function handleMapLocation($lat, $lon)
{
$this->setCoordinates($lat, $lon);
try {
$address = $this->geocodeService->reverseGeocode($lat, $lon);
$this->formattedAddress = $address['display_name'] ?? null;
} catch (GeocodingException $e) {
$this->validationError = $e->getMessage();
}
}
public function detectCurrentLocation()
{
$this->mode = 'current';
$this->isLoadingLocation = true;
$this->dispatch('requestCurrentLocation');
}
public function clearSelection()
{
$this->reset([
'latitude',
'longitude',
'formattedAddress',
'isValidLocation',
'searchQuery',
'searchResults',
'showSearchResults',
'validationError',
]);
$this->mode = 'search';
}
public function switchMode(string $mode)
{
if (!in_array($mode, ['search', 'coordinates', 'current'])) {
return;
}
$this->mode = $mode;
$this->clearSelection();
$this->dispatch('modeChanged', ['mode' => $mode]);
}
protected function rules()
{
return [
'latitude' => 'required|numeric|between:-90,90',
'longitude' => 'required|numeric|between:-180,180',
'searchQuery' => 'nullable|string|min:3|max:200',
];
}
}