({ renderAs = 'button', label = 'Add stock', initialStocks }: SearchCommandProps)
| 9 | import {useDebounce} from "@/hooks/useDebounce"; |
| 10 | |
| 11 | export default function SearchCommand({ renderAs = 'button', label = 'Add stock', initialStocks }: SearchCommandProps) { |
| 12 | const [open, setOpen] = useState(false) |
| 13 | const [searchTerm, setSearchTerm] = useState("") |
| 14 | const [loading, setLoading] = useState(false) |
| 15 | const [stocks, setStocks] = useState<StockWithWatchlistStatus[]>(initialStocks); |
| 16 | |
| 17 | const isSearchMode = !!searchTerm.trim(); |
| 18 | const displayStocks = isSearchMode ? stocks : stocks?.slice(0, 10); |
| 19 | |
| 20 | useEffect(() => { |
| 21 | const onKeyDown = (e: KeyboardEvent) => { |
| 22 | if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") { |
| 23 | e.preventDefault() |
| 24 | setOpen(v => !v) |
| 25 | } |
| 26 | } |
| 27 | window.addEventListener("keydown", onKeyDown) |
| 28 | return () => window.removeEventListener("keydown", onKeyDown) |
| 29 | }, []) |
| 30 | |
| 31 | const handleSearch = async () => { |
| 32 | if(!isSearchMode) return setStocks(initialStocks); |
| 33 | |
| 34 | setLoading(true) |
| 35 | try { |
| 36 | const results = await searchStocks(searchTerm.trim()); |
| 37 | setStocks(results); |
| 38 | } catch { |
| 39 | setStocks([]) |
| 40 | } finally { |
| 41 | setLoading(false) |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | const debouncedSearch = useDebounce(handleSearch, 300); |
| 46 | |
| 47 | useEffect(() => { |
| 48 | debouncedSearch(); |
| 49 | }, [debouncedSearch, searchTerm]); |
| 50 | |
| 51 | const handleSelectStock = () => { |
| 52 | setOpen(false); |
| 53 | setSearchTerm(""); |
| 54 | setStocks(initialStocks); |
| 55 | } |
| 56 | |
| 57 | return ( |
| 58 | <> |
| 59 | {renderAs === 'text' ? ( |
| 60 | <button |
| 61 | type="button" |
| 62 | onClick={() => setOpen(true)} |
| 63 | className="search-text" |
| 64 | > |
| 65 | {label} |
| 66 | </button> |
| 67 | ): ( |
| 68 | <Button onClick={() => setOpen(true)} className="search-btn"> |
nothing calls this directly
no test coverage detected