'use client'; import React, { useEffect, useRef, useState } from 'react'; import { useRouter } from 'next/navigation'; import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, } from '@/components/ui/command'; import { Button } from '@/components/ui/button'; import { Loader } from '@/components/ui/loader'; import { RiSearchLine, RiImageLine, RiCloseLine } from '@remixicon/react'; import { searchSuggestions, type SearchSuggestion, } from '@/hooks/use-shopify-search'; const DEBOUNCE_MS = 250; const SUGGESTION_COUNT = 3; const formatPrice = (amount: string) => `$${parseFloat(amount).toFixed(2)}`; const SearchDialog: React.FC = () => { const router = useRouter(); const [open, setOpen] = useState(false); const [term, setTerm] = useState(''); const [results, setResults] = useState([]); const [searching, setSearching] = useState(false); // Guards against a slow early request overwriting a newer one's results. const requestId = useRef(0); useEffect(() => { const query = term.trim(); if (!query) { setResults([]); setSearching(false); return; } setSearching(true); const id = ++requestId.current; const timer = setTimeout(async () => { try { const { products } = await searchSuggestions(query, SUGGESTION_COUNT); if (id !== requestId.current) return; setResults(products); } catch (err) { console.error('Search failed:', err); if (id === requestId.current) setResults([]); } finally { if (id === requestId.current) setSearching(false); } }, DEBOUNCE_MS); return () => clearTimeout(timer); }, [term]); const close = () => { setOpen(false); setTerm(''); setResults([]); }; const goToSearchPage = () => { const query = term.trim(); close(); router.push(query ? `/search?q=${encodeURIComponent(query)}` : '/search'); }; const goToProduct = (handle: string) => { close(); router.push(`/products/${handle}`); }; const hasQuery = Boolean(term.trim()); return ( <> !next && close()} title="Search" description="Search products" showCloseButton={false} className="top-24 max-w-xl translate-y-0" // Results come from the Storefront API, so cmdk must not re-filter them. shouldFilter={false} >
{ if (event.key === 'Enter') goToSearchPage(); }} />
{hasQuery && ( )}
{hasQuery && ( <>
{searching && results.length === 0 ? (
) : results.length === 0 ? ( No products found. ) : ( {results.map((product) => ( goToProduct(product.handle)} className="gap-3 py-2" >
{product.featuredImage ? ( {product.featuredImage.altText ) : (
)}
{product.title}
{formatPrice( product.priceRange.minVariantPrice.amount )}
))}
)}
{results.length > 0 && (
)} )}
); }; export default SearchDialog;