'use client'; import { useState, useCallback, useRef, useEffect } from 'react'; interface SearchItem { id: number; name: string; part_number?: string; barcode?: string; quantity: number; } interface UseItemSearchResult { results: SearchItem[]; isLoading: boolean; error: string | null; } export function useItemSearch( query: string, enabled: boolean = true ): UseItemSearchResult { const [results, setResults] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const debounceTimerRef = useRef(null); const cacheRef = useRef>(new Map()); const performSearch = useCallback(async (searchQuery: string) => { // Client-side validation: require min 2 chars if (!searchQuery || searchQuery.length < 2) { setResults([]); setError(null); return; } // Check cache first if (cacheRef.current.has(searchQuery)) { setResults(cacheRef.current.get(searchQuery) || []); setError(null); return; } setIsLoading(true); setError(null); try { const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8906'; const response = await fetch(`${backendUrl}/items/search?q=${encodeURIComponent(searchQuery)}`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error('Search failed'); } const data = await response.json(); const items = data || []; // Cache the results cacheRef.current.set(searchQuery, items); setResults(items); setError(null); } catch (err) { const errorMsg = err instanceof Error ? err.message : 'Search failed'; setError(errorMsg); setResults([]); } finally { setIsLoading(false); } }, []); // Debounced search on query change useEffect(() => { if (!enabled) { setResults([]); setError(null); return; } // Clear previous debounce timer if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); } // Set new debounce timer (300ms) debounceTimerRef.current = setTimeout(() => { performSearch(query); }, 300); return () => { if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); } }; }, [query, enabled, performSearch]); // Cleanup cache on unmount useEffect(() => { return () => { cacheRef.current.clear(); if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); } }; }, []); return { results, isLoading, error }; }