131 lines
6.1 KiB
PHP
131 lines
6.1 KiB
PHP
<div class="space-y-3 px-5 pb-4 pt-5">
|
|
<form wire:submit="findStations">
|
|
|
|
<div
|
|
x-data="{
|
|
query: @js($search),
|
|
locatingUser: false,
|
|
_usedIpFallback: false,
|
|
async _postcodeFromLatLng(lat, lng) {
|
|
const res = await fetch(`https://api.postcodes.io/postcodes?lon=${lng}&lat=${lat}&limit=1&radius=1000`);
|
|
const data = await res.json();
|
|
return data?.result?.[0]?.postcode ?? null;
|
|
},
|
|
async locateUser() {
|
|
this.locatingUser = true;
|
|
this._usedIpFallback = false;
|
|
try {
|
|
let lat, lng;
|
|
try {
|
|
const pos = await new Promise((resolve, reject) =>
|
|
navigator.geolocation.getCurrentPosition(resolve, reject, { enableHighAccuracy: false, timeout: 5000, maximumAge: 60000 })
|
|
);
|
|
lat = pos.coords.latitude;
|
|
lng = pos.coords.longitude;
|
|
} catch (e) {
|
|
const d = await fetch('https://ipapi.co/json/').then(r => r.json());
|
|
lat = d.latitude;
|
|
lng = d.longitude;
|
|
this._usedIpFallback = true;
|
|
}
|
|
const postcode = await this._postcodeFromLatLng(lat, lng);
|
|
if (postcode) {
|
|
this.query = postcode;
|
|
this.$wire.set('search', postcode);
|
|
this.$wire.findStations();
|
|
}
|
|
} catch (e) {
|
|
// silent
|
|
} finally {
|
|
this.locatingUser = false;
|
|
}
|
|
}
|
|
}"
|
|
class="relative mb-3"
|
|
>
|
|
<iconify-icon
|
|
icon="lucide:map-pin"
|
|
class="pointer-events-none absolute left-4 top-1/2 -translate-y-1/2 text-xl text-text-muted"
|
|
></iconify-icon>
|
|
<input
|
|
wire:model="search"
|
|
x-model="query"
|
|
x-ref="searchInput"
|
|
type="text"
|
|
name="search"
|
|
@focus="_usedIpFallback = false"
|
|
placeholder="Postcode, town or city"
|
|
class="h-14 w-full rounded-xl border border-border bg-surface pl-12 pr-36 text-base font-semibold text-text-base focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary"
|
|
/>
|
|
<div class="absolute inset-y-0 right-0 flex items-center gap-2 pr-3">
|
|
<button
|
|
x-show="query.length > 0"
|
|
x-cloak
|
|
type="button"
|
|
@click="query = ''; $wire.set('search', ''); _usedIpFallback = false"
|
|
class="text-text-muted hover:text-text-base"
|
|
>
|
|
<iconify-icon icon="lucide:x" class="text-base"></iconify-icon>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
@click="locateUser()"
|
|
:disabled="locatingUser"
|
|
class="flex items-center gap-1.5 rounded-full bg-surface-subtle px-3 py-1.5 text-sm font-semibold text-text-base disabled:opacity-40"
|
|
>
|
|
<iconify-icon x-show="!locatingUser" icon="lucide:locate-fixed" class="text-sm"></iconify-icon>
|
|
<iconify-icon x-show="locatingUser" icon="lucide:loader-circle" class="animate-spin text-sm"></iconify-icon>
|
|
<span x-text="locatingUser ? 'Finding...' : 'Near me'">Near me</span>
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
wire:loading.attr="disabled"
|
|
class="text-text-muted disabled:opacity-60"
|
|
>
|
|
<iconify-icon wire:loading.remove wire:target="findStations" icon="lucide:search" class="text-xl"></iconify-icon>
|
|
<iconify-icon wire:loading wire:target="findStations" icon="lucide:loader-circle" class="animate-spin text-xl"></iconify-icon>
|
|
</button>
|
|
</div>
|
|
|
|
{{-- IP fallback nudge --}}
|
|
<div
|
|
x-show="_usedIpFallback"
|
|
x-cloak
|
|
x-transition:enter="transition ease-out duration-300 delay-500"
|
|
x-transition:enter-start="opacity-0 -translate-y-2"
|
|
x-transition:enter-end="opacity-100 translate-y-0"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-end="opacity-0"
|
|
class="absolute left-0 right-0 top-full z-40 mt-2"
|
|
>
|
|
<div
|
|
@click="$refs.searchInput.focus(); _usedIpFallback = false"
|
|
class="cursor-pointer rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 transition-colors hover:bg-amber-100"
|
|
>
|
|
<p class="text-center text-xs text-amber-800">
|
|
<span class="font-medium">Showing approximate location.</span>
|
|
<span class="underline">Enter your postcode above</span> for exact results.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@error('search')
|
|
<p class="mb-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
|
|
<div class="flex gap-2 overflow-x-auto pb-1" style="-ms-overflow-style:none;scrollbar-width:none;">
|
|
<div class="shrink-0"><x-fuel.type-select wire:model.live="fuelType" /></div>
|
|
<div class="shrink-0"><x-fuel.sort-select wire:model.live="sort" /></div>
|
|
<div class="shrink-0"><x-fuel.radius-select wire:model.live="radius" /></div>
|
|
</div>
|
|
|
|
</form>
|
|
|
|
@if ($apiError)
|
|
<div class="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
|
|
{{ $apiError }}
|
|
</div>
|
|
@endif
|
|
</div>
|