diff --git a/frontend/src/components/FoodSearchInput.jsx b/frontend/src/components/FoodSearchInput.jsx index 716c542..223260d 100644 --- a/frontend/src/components/FoodSearchInput.jsx +++ b/frontend/src/components/FoodSearchInput.jsx @@ -10,7 +10,9 @@ export default function FoodSearchInput({ onFoodSelect, initialValue = '' }) { const [loading, setLoading] = useState(false); const [showDropdown, setShowDropdown] = useState(false); const [quantity, setQuantity] = useState(1); + const [error, setError] = useState(null); const dropdownRef = useRef(null); + const requestIdRef = useRef(0); useEffect(() => { const handleClickOutside = (event) => { @@ -35,33 +37,48 @@ export default function FoodSearchInput({ onFoodSelect, initialValue = '' }) { }, []); useEffect(() => { + setError(null); if (query.length < 2) { + requestIdRef.current += 1; setResults([]); setCustomFoods([]); + setLoading(false); return; } + const controller = new AbortController(); const timer = setTimeout(async () => { + const requestId = ++requestIdRef.current; + const isStale = () => controller.signal.aborted || requestId !== requestIdRef.current; setLoading(true); try { const [dbResponse, customResponse] = await Promise.all([ - api.get(`/search-food?query=${encodeURIComponent(query)}`), + api.get(`/search-food?query=${encodeURIComponent(query)}`, { signal: controller.signal }), searchCustomFoods(query) ]); + if (isStale()) return; setResults(dbResponse.data.foods || []); setCustomFoods(customResponse); + setError(null); setShowDropdown(true); - } catch (error) { - console.error('[FoodSearch] Search failed:', error); + } catch (err) { + const canceled = err?.name === 'CanceledError' || err?.code === 'ERR_CANCELED'; + if (canceled || isStale()) return; + console.error('[FoodSearch] Search failed:', err); setResults([]); setCustomFoods([]); + setShowDropdown(false); + setError('Search unavailable, try again'); } finally { - setLoading(false); + if (requestId === requestIdRef.current) setLoading(false); } }, 500); - return () => clearTimeout(timer); + return () => { + clearTimeout(timer); + controller.abort(); + }; }, [query]); const searchCustomFoods = async (searchQuery) => { @@ -111,6 +128,18 @@ export default function FoodSearchInput({ onFoodSelect, initialValue = '' }) { setQuantity(1); }; + const handleAddManually = () => { + onFoodSelect({ + name: query, + portion: `${quantity} serving`, + calories: 0, + protein_g: 0, + carbs_g: 0, + fat_g: 0, + fiber_g: 0, + }); + }; + return (
@@ -204,24 +233,28 @@ export default function FoodSearchInput({ onFoodSelect, initialValue = '' }) {
)} - {showDropdown && !loading && results.length === 0 && customFoods.length === 0 && query.length >= 2 && ( + {error && !loading && query.length >= 2 && ( +
+

+ {error} +

+ +
+ )} + + {showDropdown && !error && !loading && results.length === 0 && customFoods.length === 0 && query.length >= 2 && (

No foods found. Try a different search term or add manually: