"use client"; import { Search } from "lucide-react"; import Link from "next/link"; import { useEffect, useRef, useState } from "react"; import { getPosterUrl, type TmdbSearchResult } from "@/lib/tmdb"; import { cn } from "@/lib/utils"; export function SearchBar({ autoFocus = false, onSelect, compact = false, }: { autoFocus?: boolean; onSelect?: (movie: TmdbSearchResult) => void; compact?: boolean; }) { const containerRef = useRef(null); const [query, setQuery] = useState(""); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); const [open, setOpen] = useState(false); useEffect(() => { if (!query.trim()) { return; } const controller = new AbortController(); const timeout = setTimeout(async () => { setLoading(true); try { const response = await fetch( `/api/search?q=${encodeURIComponent(query)}`, { signal: controller.signal }, ); const data = (await response.json()) as { results: TmdbSearchResult[] }; setResults(data.results ?? []); setOpen(true); } catch (error) { if ((error as Error).name !== "AbortError") { setResults([]); } } finally { setLoading(false); } }, 300); return () => { clearTimeout(timeout); controller.abort(); }; }, [query]); useEffect(() => { function handleClickOutside(event: MouseEvent) { if (!containerRef.current?.contains(event.target as Node)) { setOpen(false); } } if (open) { document.addEventListener("click", handleClickOutside); } return () => document.removeEventListener("click", handleClickOutside); }, [open]); return (
{ const value = event.target.value; setQuery(value); if (!value.trim()) { setResults([]); setOpen(false); } }} onFocus={() => query.trim() && setOpen(true)} placeholder="Search for a movie..." className={cn( "w-full border border-stone-300 bg-white text-stone-950 outline-none transition focus:border-amber-500 focus:bg-amber-50 focus:ring-2 focus:ring-amber-200", compact ? "rounded-xl py-2 pl-9 pr-3 text-sm" : "rounded-2xl py-4 pl-12 pr-4 text-base", )} />
{open && (
    {loading && (
  • Searching...
  • )} {!loading && results.length === 0 && (
  • No results found
  • )} {!loading && results.map((result) => { const poster = getPosterUrl(result.poster_path, "w185"); const year = result.release_date?.slice(0, 4); const inner = ( <>
    {poster ? ( ) : (
    N/A
    )}

    {result.title} {year ? ( ({year}) ) : null}

    {result.overview || "No overview available"}

    ); return (
  • {onSelect ? ( ) : ( setOpen(false)} className="flex items-center gap-3 border-b border-stone-100 px-4 py-3 transition hover:bg-stone-50 last:border-b-0" > {inner} )}
  • ); })}
)}
); }