'use client'; import { useState, useEffect, useRef, useCallback } from 'react'; import { X, Search } from 'lucide-react'; import { Item } from '@/lib/db'; import { axiosInstance } from '@/lib/api'; interface SearchModalProps { isOpen: boolean; onClose: () => void; onSelectItem: (item: Item) => void; } export default function SearchModal({ isOpen, onClose, onSelectItem, }: SearchModalProps) { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const inputRef = useRef(null); const debounceTimerRef = useRef(null); // Auto-focus input when modal opens useEffect(() => { if (isOpen && inputRef.current) { inputRef.current.focus(); } }, [isOpen]); // Debounced search const performSearch = useCallback(async (searchQuery: string) => { if (searchQuery.length < 1) { setResults([]); setError(null); return; } setIsLoading(true); setError(null); try { const response = await axiosInstance.get('/items/search', { params: { q: searchQuery } }); setResults(response.data || []); } catch (err) { const errorMsg = err instanceof Error ? err.message : 'Search failed'; setError(errorMsg); setResults([]); } finally { setIsLoading(false); } }, []); // Handle input change with debouncing (300ms) const handleInputChange = (e: React.ChangeEvent) => { const newQuery = e.target.value; setQuery(newQuery); // Clear previous debounce timer if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); } // Set new debounce timer debounceTimerRef.current = setTimeout(() => { performSearch(newQuery); }, 300); }; // Handle Escape key useEffect(() => { if (!isOpen) return; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') { onClose(); } }; document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('keydown', handleKeyDown); }; }, [isOpen, onClose]); // Handle item selection const handleSelectItem = (item: Item) => { onSelectItem(item); setQuery(''); setResults([]); onClose(); }; // Cleanup debounce on unmount useEffect(() => { return () => { if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); } }; }, []); if (!isOpen) return null; return (
{/* Header */}

Search inventory

{/* Search Input */}
{/* Results */}
{error && (
{error}
)} {isLoading && (
)} {!isLoading && !error && results.length === 0 && query.length > 0 && (
No items found matching "{query}"
)} {!isLoading && !error && results.length === 0 && query.length === 0 && (
Start typing to search
)} {!isLoading && results.length > 0 && (
{results.map((item) => ( ))}
)}
); }