/** * @component LiveSearch * @purpose Search bar matching Screenshots 1 & 2 with All Categories selector, search input, and solid blue Search button. * @dependencies storefrontService, catalogService, lucide-react */ 'use client'; import { useState, useEffect, useRef } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import Image from 'next/image'; import { Loader2, X, ChevronDown, Check, LayoutGrid, Search } from 'lucide-react'; import { storefrontService, LiveSearchResult } from '@/services/api/storefrontService'; import { catalogService, CategoryResponse } from '@/services/api/catalogService'; import { getImageUrl } from '@/lib/utils'; import { getCategoryMenuItems, menuItemCategoryParam, } from '@/components/CategorySidebar'; export function LiveSearch() { const router = useRouter(); const [query, setQuery] = useState(''); const [selectedCategory, setSelectedCategory] = useState('all'); const [categories, setCategories] = useState([]); const [results, setResults] = useState({ products: [], categories: [], brands: [], services: [] }); const [loading, setLoading] = useState(false); const [isOpen, setIsOpen] = useState(false); const [catOpen, setCatOpen] = useState(false); const [catQuery, setCatQuery] = useState(''); const containerRef = useRef(null); const catSearchRef = useRef(null); useEffect(() => { catalogService.getCategories().then(setCategories); }, []); useEffect(() => { if (!query.trim()) { setResults({ products: [], categories: [], brands: [], services: [] }); setIsOpen(false); return; } setLoading(true); const timer = setTimeout(async () => { const data = await storefrontService.getLiveSearch(query, selectedCategory); setResults(data); setLoading(false); setCatOpen(false); setIsOpen(true); }, 300); return () => clearTimeout(timer); }, [query, selectedCategory]); const goToResults = () => { const trimmed = query.trim(); if (!trimmed) return; const params = new URLSearchParams({ search: trimmed }); if (selectedCategory !== 'all') params.set('category', selectedCategory); setIsOpen(false); router.push(`/products?${params.toString()}`); }; useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setIsOpen(false); setCatOpen(false); } }; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') { setIsOpen(false); setCatOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('keydown', handleKeyDown); }; }, []); const hasResults = results.products.length > 0 || results.categories.length > 0 || results.brands.length > 0 || results.services.length > 0; const categoryMenu = getCategoryMenuItems(categories); const selectedCategoryLabel = selectedCategory === 'all' ? 'All Categories' : categoryMenu.find((item) => menuItemCategoryParam(item.href) === selectedCategory) ?.label || categories.find((cat) => cat.slug === selectedCategory)?.name || 'All Categories'; const filteredCategories = catQuery.trim() ? categoryMenu.filter((item) => item.label.toLowerCase().includes(catQuery.trim().toLowerCase()) ) : categoryMenu; useEffect(() => { if (!catOpen) { setCatQuery(''); return; } const id = window.setTimeout(() => catSearchRef.current?.focus(), 0); return () => window.clearTimeout(id); }, [catOpen]); const pickCategory = (value: string) => { setSelectedCategory(value); setCatOpen(false); }; return (
{ e.preventDefault(); goToResults(); }} className="flex items-center border border-gray-200 rounded-lg bg-white shadow-2xs overflow-visible" >
{catOpen && (
setCatQuery(e.target.value)} placeholder="Search categories..." className="w-full h-9 pl-8 pr-3 rounded-lg bg-gray-50 border border-gray-200 text-[12px] text-[#1a1a1a] outline-none focus:border-primary focus:ring-2 focus:ring-primary/15" />
  • {filteredCategories.length === 0 ? (
  • No categories found
  • ) : ( filteredCategories.map((item) => { const slug = menuItemCategoryParam(item.href) || item.id; const isSelected = selectedCategory === slug; return (
  • ); }) )}
)}
{/* Search Input Field */}
setQuery(e.target.value)} onFocus={() => { setCatOpen(false); if (query.trim()) setIsOpen(true); }} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); goToResults(); } }} placeholder="Search products..." className="w-full text-xs text-gray-800 px-4 py-2.5 outline-none placeholder:text-gray-400" /> {query && ( )}
{/* Solid Blue Search Button */}
{/* Live Search Autocomplete Dropdown */} {isOpen && (
{loading && (
Searching catalog...
)} {!loading && !hasResults && (
No products found matching "{query}"
)} {!loading && hasResults && (
{results.products.slice(0, 5).map((product) => ( setIsOpen(false)} className="flex items-center gap-3 p-2 hover:bg-gray-50 rounded-md transition-colors" >
{product.images[0]?.image_url ? ( {product.name} ) : (
)}
{product.name}
₹{Number(product.variants[0]?.price || 0).toFixed(2)}
))}
)}
)}
); }