initial cleanup

This commit is contained in:
terraputix
2026-01-07 23:45:10 +01:00
parent 2d31df88f0
commit f781ef4fd6
325 changed files with 1903 additions and 1000 deletions
+140 -131
View File
@@ -1,14 +1,13 @@
<script lang="ts">
import { createEventDispatcher, onDestroy } from 'svelte';
import { type GeoLocation } from '$lib/stores/settings';
import * as Alert from '$lib/components/ui/alert';
import { Button } from '$lib/components/ui/button';
import * as Dialog from '$lib/components/ui/dialog';
import { Input } from '$lib/components/ui/input';
export let label: string = 'Search Locations...';
export let placeholder: string = 'Enter city name...';
interface ResultSet {
results: GeoLocation[] | undefined;
@@ -80,7 +79,6 @@
};
}
// Always set format=json to fetch data
const url = 'https://geocoding-api.open-meteo.com/v1/search';
const fetchUrl = `${url}?${new URLSearchParams({ name: searchQuery })}`;
const result = await fetch(fetchUrl);
@@ -97,169 +95,180 @@
<Dialog.Root bind:open={dialogOpen}>
<Dialog.Trigger
id="location_search"
class="group flex h-14 w-full cursor-pointer items-center justify-start rounded-xl border-2 border-gray-200 bg-white px-6 transition-all duration-200 hover:border-blue-400 hover:shadow-md focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 dark:border-gray-600 dark:bg-gray-700"
onclick={(e) => {
e.preventDefault();
dialogOpen = !dialogOpen;
}}
class="flex h-12 w-full cursor-pointer items-center justify-center rounded-md border border-border px-5 pr-6 duration-200 hover:bg-accent"
><svg
class="lucide lucide-search mr-[5px]"
>
<svg
class="mr-3 h-5 w-5 text-gray-400 transition-colors group-hover:text-blue-500"
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.3-4.3" />
</svg>
{label}</Dialog.Trigger
>
<span
class="text-gray-600 transition-colors group-hover:text-gray-900 dark:text-gray-300 dark:group-hover:text-white"
>
{label}
</span>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay class="bg-black/5" />
<Dialog.Overlay class="bg-black/20 backdrop-blur-sm" />
<Dialog.Content
class="top-[10%] flex max-h-[calc(100vh-10%)] min-h-[400px] translate-y-0 flex-col overflow-y-auto border-border sm:max-w-[600px]"
class="top-[10%] flex max-h-[calc(100vh-10%)] min-h-[500px] translate-y-0 flex-col overflow-hidden rounded-2xl border-border bg-white shadow-2xl sm:max-w-[700px] dark:bg-gray-800"
>
<Dialog.Header>
<Dialog.Title>Search Locations</Dialog.Title>
<Dialog.Header class="pb-6">
<Dialog.Title class="text-center text-2xl font-bold">Find Your Location</Dialog.Title>
<p class="text-center text-gray-600 dark:text-gray-300">
Search for a city or use GPS to detect your location
</p>
</Dialog.Header>
<div>
<div class="flex gap-3">
<Input
type="search"
id="location_search_input"
autocomplete="off"
spellcheck="false"
aria-label="Search Location"
bind:value={searchQuery}
/>
<Button
id="location_search_gps"
variant="outline"
title="Detect Location via GPS"
onclick={() => (searchQuery = 'GPS')}
><svg
class="lucide lucide-mouse-pointer-2"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z"
<div class="flex-1 overflow-hidden">
<div class="px-6">
<div class="mb-6 flex gap-3">
<div class="flex-1">
<Input
type="search"
{placeholder}
class="h-12 text-lg"
autocomplete="off"
spellcheck="false"
aria-label="Search Location"
bind:value={searchQuery}
/>
</svg></Button
>
</div>
{#await results}
<div class="mt-4 flex h-full items-center justify-center">
<Alert.Root class="my-auto w-[unset] border-border">
<Alert.Description>Loading...</Alert.Description>
</Alert.Root>
</div>
<Button
variant="outline"
size="lg"
class="px-4"
title="Use GPS Location"
onclick={() => (searchQuery = 'GPS')}
>
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
</Button>
</div>
{:then results}
{#if results.results && results.results.length === 0}
{#if searchQuery.length < 2}
<Alert.Root class="my-auto mt-4 w-[unset] border-border">
<Alert.Description>Start typing to search for locations</Alert.Description>
</Alert.Root>
{:else}
<Alert.Root class="my-auto !mt-4 w-[unset] border-border">
</div>
<div class="flex-1 overflow-y-auto px-6 pb-6">
{#await results}
<div class="flex h-32 items-center justify-center">
<div class="flex items-center space-x-3">
<div class="h-6 w-6 animate-spin rounded-full border-b-2 border-blue-600"></div>
<span class="text-gray-600 dark:text-gray-300">Searching...</span>
</div>
</div>
{:then results}
{#if results.results && results.results.length === 0}
{#if searchQuery.length < 2}
<Alert.Root class="border-blue-200 bg-blue-50 dark:bg-blue-900/20">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<Alert.Description class="text-blue-700 dark:text-blue-300">
Start typing to search for locations or use GPS to detect your current position
</Alert.Description>
</Alert.Root>
{:else}
<Alert.Root class="border-orange-200 bg-orange-50 dark:bg-orange-900/20">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.728-.833-2.498 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
/>
</svg>
<Alert.Description class="text-orange-700 dark:text-orange-300">
No locations found for "{searchQuery}". Try a different search term.
</Alert.Description>
</Alert.Root>
{/if}
{:else if !results.results}
<Alert.Root variant="destructive">
<Alert.Description>No locations found</Alert.Description>
</Alert.Root>
{/if}
{:else if !results.results}
<Alert.Root class="my-auto !mt-4 w-[unset] border-border">
<Alert.Description>No locations found</Alert.Description>
</Alert.Root>
{:else}
<div class="list-group mt-4">
<div id="location_search_results" class="rounded-lg border border-border">
{:else}
<div class="space-y-2">
{#each results.results || [] as location, i (i)}
<Button
variant="outline"
class="location-search-result flex h-[unset] w-full justify-between gap-0 rounded-none py-2 pr-1 pl-3 not-last:border-b md:pr-2 {i ===
0
? 'rounded-t-md'
: ''} {results.results && i === results.results.length - 1
? 'rounded-b-md'
: ''}"
<button
class="group w-full rounded-xl border border-gray-200 p-4 text-left transition-all duration-200 hover:border-blue-400 hover:bg-blue-50 dark:border-gray-600 dark:hover:bg-blue-900/20"
onclick={() => selectLocation(location)}
>
<div class="pointer-events-none flex flex-col gap-1 truncate">
<div class="flex items-center gap-2 truncate text-lg">
<div class="flex items-center justify-between">
<div class="flex flex-1 items-center space-x-4">
<img
height="24"
width="24"
class="h-10 w-10 rounded-full shadow-md"
src="/images/country-flags/{(
location.country_code || 'united_nations'
).toLowerCase()}.svg"
title={location.country}
alt={location.country_code}
alt={location.country}
/>
{location.name}
<div class="flex-1">
<h3
class="font-semibold text-gray-900 group-hover:text-blue-600 dark:text-white dark:group-hover:text-blue-400"
>
{location.name}
</h3>
<p class="text-sm text-gray-600 dark:text-gray-300">
{location.admin1 || ''}
{location.country || ''}
</p>
<p class="text-xs text-gray-500 dark:text-gray-400">
{location.latitude.toFixed(2)}°N {location.longitude.toFixed(2)}°E
{#if location.elevation}{location.elevation.toFixed(0)}m{/if}
</p>
</div>
</div>
<div class="truncate text-left text-sm text-muted-foreground">
{location.admin1 || ''} ({location.latitude.toFixed(2)}°N {location.longitude.toFixed(
2
)}°E{#if location.elevation}{' ' +
location.elevation.toFixed(0) +
'm asl'}{/if})
</div>
</div>
<div class="-mr-1 flex justify-self-end">
<Button
variant="ghost"
class="px-2 duration-200 hover:brightness-[140%] md:px-3"
href="https://www.openstreetmap.org/#map=13/{location.latitude}/{location.longitude}"
target="_blank"
title="Show on map"
onclick={(e) => {
e.stopPropagation();
}}
<svg
class="h-5 w-5 text-gray-400 group-hover:text-blue-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<svg
class="lucide lucide-map"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.2"
<path
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z"
/>
<path d="M15 5.764v15" />
<path d="M9 3.236v15" />
</svg>
</Button>
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</div>
</Button>
</button>
{/each}
</div>
</div>
{/if}
{:catch error}
<Alert.Root variant="destructive" class="my-auto mt-4 w-[unset]">
<Alert.Description>{error.message}</Alert.Description>
</Alert.Root>
{/await}
{/if}
{:catch error}
<Alert.Root variant="destructive">
<Alert.Description>Error: {error.message}</Alert.Description>
</Alert.Root>
{/await}
</div>
</div>
</Dialog.Content>
</Dialog.Portal>