From 6598ebb3bfdffc3b721cdfa457c4910fc91be0b9 Mon Sep 17 00:00:00 2001 From: cediackermann Date: Mon, 20 Apr 2026 18:27:24 +0200 Subject: [PATCH] feat: expand SearchBox to support both SL and SBB station search --- src/components/organisms/SearchBox.tsx | 77 ++++++++++++-------------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/src/components/organisms/SearchBox.tsx b/src/components/organisms/SearchBox.tsx index 54b31f7..f8a47e5 100644 --- a/src/components/organisms/SearchBox.tsx +++ b/src/components/organisms/SearchBox.tsx @@ -1,62 +1,55 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; import { StopLocation } from '../../types'; interface SearchBoxProps { onSelect: (stop: StopLocation) => void; + endpoint: string; + placeholder?: string; } -export const SearchBox = ({ onSelect }: SearchBoxProps) => { +export const SearchBox = ({ onSelect, endpoint, placeholder = 'Search for a station...' }: SearchBoxProps) => { + const [input, setInput] = useState(''); const [query, setQuery] = useState(''); - const [results, setResults] = useState([]); - const [loading, setLoading] = useState(false); - const search = async () => { - if (!query) return; - setLoading(true); - try { - const response = await fetch(`/api/sl/search?q=${encodeURIComponent(query)}`); - const data = await response.json(); - setResults(data.StopLocation || []); - } catch (e) { - console.error('Search failed', e); - } finally { - setLoading(false); - } - }; + useEffect(() => { + const timer = setTimeout(() => setQuery(input.trim()), 350); + return () => clearTimeout(timer); + }, [input]); + + const { data, isFetching } = useQuery<{ StopLocation: StopLocation[] }>({ + queryKey: ['search', endpoint, query], + queryFn: () => fetch(`${endpoint}?q=${encodeURIComponent(query)}`).then((r) => r.json()), + enabled: query.length > 0, + staleTime: 60_000, + }); + + const results = data?.StopLocation ?? []; return ( -
-
- setQuery(e.target.value)} - onKeyPress={(e) => e.key === 'Enter' && search()} - placeholder="Search for a station..." - className="flex-grow bg-surface text-white px-6 py-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-sl-blue transition-all text-xl" - /> - -
+
+ setInput(e.target.value)} + placeholder={placeholder} + className="w-full bg-black text-white px-4 py-2 border border-white outline-none text-lg" + /> -
- {loading &&
Searching...
} - {!loading && results.map((stop) => ( +
+ {isFetching &&

Searching...

} + {!isFetching && results.map((stop) => (
onSelect(stop)} - className="flex justify-between items-center p-6 bg-surface rounded-xl cursor-pointer hover:bg-zinc-800 transition-colors text-xl" + className="flex justify-between items-center py-3 border-b border-zinc-700 cursor-pointer text-lg" > - {stop.name} - ID: {stop.id} + {stop.name} + {stop.id}
))} - {!loading && query && results.length === 0 && ( -
No stations found.
+ {!isFetching && query && results.length === 0 && ( +

No stations found.

)}