import React from "react";
import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import InputAdornment from "@mui/material/InputAdornment";
import Paper from "@mui/material/Paper";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemText from "@mui/material/ListItemText";
import Typography from "@mui/material/Typography";
import IconButton from "@mui/material/IconButton";
import SearchIcon from "@mui/icons-material/Search";
import KeyboardReturnIcon from "@mui/icons-material/KeyboardReturn";
import { useNavigate, useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { LanguageContext } from "../../i18n/withTranslation.js";
const SearchBar = () => {
const navigate = useNavigate();
const location = useLocation();
const searchParams = new URLSearchParams(location.search);
const { t, i18n } = useTranslation();
const languageContext = React.useContext(LanguageContext);
// State management
const [searchQuery, setSearchQuery] = React.useState(
searchParams.get("q") || ""
);
const [suggestions, setSuggestions] = React.useState([]);
const [showSuggestions, setShowSuggestions] = React.useState(false);
const [selectedIndex, setSelectedIndex] = React.useState(-1);
// Refs for debouncing and timers
const debounceTimerRef = React.useRef(null);
const autocompleteTimerRef = React.useRef(null);
const isFirstKeystrokeRef = React.useRef(true);
const inputRef = React.useRef(null);
const suggestionBoxRef = React.useRef(null);
const handleSearch = (e) => {
e.preventDefault();
delete window.currentSearchQuery;
setShowSuggestions(false);
if (searchQuery.trim()) {
navigate(`/search?q=${encodeURIComponent(searchQuery)}`);
}
};
const updateSearchState = (value) => {
setSearchQuery(value);
// Dispatch global custom event with search query value
const searchEvent = new CustomEvent("search-query-change", {
detail: { query: value },
});
// Store the current search query in the window object
window.currentSearchQuery = value;
window.dispatchEvent(searchEvent);
};
// @note Autocomplete function using getSearchProducts Socket.io API - returns objects with name and seoName
const fetchAutocomplete = React.useCallback(
(query) => {
if (!query || query.length < 2) {
setSuggestions([]);
setShowSuggestions(false);
return;
}
const currentLanguage = languageContext?.currentLanguage || i18n?.language || 'de';
window.socketManager.emit(
"getSearchProducts",
{
query: query.trim(),
limit: 8,
language: currentLanguage,
requestTranslation: currentLanguage === 'de' ? false : true,
},
(response) => {
if (response && response.products) {
// getSearchProducts returns response.products array
const suggestions = response.products.slice(0, 8); // Limit to 8 suggestions
setSuggestions(suggestions);
setShowSuggestions(suggestions.length > 0);
setSelectedIndex(-1); // Reset selection
} else {
setSuggestions([]);
setShowSuggestions(false);
console.log("getSearchProducts failed or no products:", response);
}
}
);
},
[languageContext, i18n]
);
const handleSearchChange = (e) => {
const value = e.target.value;
// Always update the input field immediately for responsiveness
setSearchQuery(value);
// Clear any existing timers
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}
if (autocompleteTimerRef.current) {
clearTimeout(autocompleteTimerRef.current);
}
// Set the debounce timer for search state update
const delay = isFirstKeystrokeRef.current ? 100 : 200;
debounceTimerRef.current = setTimeout(() => {
updateSearchState(value);
isFirstKeystrokeRef.current = false;
// Reset first keystroke flag after 1 second of inactivity
debounceTimerRef.current = setTimeout(() => {
isFirstKeystrokeRef.current = true;
}, 1000);
}, delay);
// Set autocomplete timer with longer delay to reduce API calls
autocompleteTimerRef.current = setTimeout(() => {
fetchAutocomplete(value);
}, 300);
};
// Handle keyboard navigation in suggestions
const handleKeyDown = (e) => {
if (!showSuggestions || suggestions.length === 0) return;
switch (e.key) {
case "ArrowDown":
e.preventDefault();
setSelectedIndex((prev) =>
prev < suggestions.length - 1 ? prev + 1 : prev
);
break;
case "ArrowUp":
e.preventDefault();
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : -1));
break;
case "Enter":
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < suggestions.length) {
const selectedSuggestion = suggestions[selectedIndex];
setSearchQuery(selectedSuggestion.name);
updateSearchState(selectedSuggestion.name);
setShowSuggestions(false);
navigate(`/Artikel/${selectedSuggestion.seoName}`);
} else {
handleSearch(e);
}
break;
case "Escape":
setShowSuggestions(false);
setSelectedIndex(-1);
inputRef.current?.blur();
break;
}
};
// Handle suggestion click - navigate to product page directly
const handleSuggestionClick = (suggestion) => {
setSearchQuery(suggestion.name);
updateSearchState(suggestion.name);
setShowSuggestions(false);
navigate(`/Artikel/${suggestion.seoName}`);
};
// Handle input focus
const handleFocus = () => {
if (suggestions.length > 0 && searchQuery.length >= 2) {
setShowSuggestions(true);
}
};
// Handle input blur with delay to allow suggestion clicks
const handleBlur = () => {
setTimeout(() => {
setShowSuggestions(false);
setSelectedIndex(-1);
}, 200);
};
// Get delivery days based on availability
const getDeliveryDays = (product) => {
if (product.available === 1) {
return t('delivery.times.standard2to3Days');
} else if (product.incoming === 1 || product.availableSupplier === 1) {
return t('delivery.times.supplier7to9Days');
}
};
// Handle enter icon click
const handleEnterClick = () => {
delete window.currentSearchQuery;
setShowSuggestions(false);
if (searchQuery.trim()) {
navigate(`/search?q=${encodeURIComponent(searchQuery)}`);
}
};
// Clean up timers on unmount
React.useEffect(() => {
return () => {
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}
if (autocompleteTimerRef.current) {
clearTimeout(autocompleteTimerRef.current);
}
};
}, []);
// Close suggestions when clicking outside
React.useEffect(() => {
const handleClickOutside = (event) => {
if (
suggestionBoxRef.current &&
!suggestionBoxRef.current.contains(event.target) &&
!inputRef.current?.contains(event.target)
) {
setShowSuggestions(false);
setSelectedIndex(-1);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
return (
),
endAdornment: (
),
sx: { borderRadius: 2, bgcolor: "background.paper" },
}}
/>
{/* Autocomplete Suggestions Dropdown */}
{showSuggestions && suggestions.length > 0 && (
{suggestions.map((suggestion, index) => (
handleSuggestionClick(suggestion)}
sx={{
cursor: "pointer",
border: "none",
background: "none",
padding: 0,
margin: 0,
width: "100%",
textAlign: "left",
px: 2, // Add horizontal padding back
"&:hover": {
backgroundColor: "action.hover",
},
"&.Mui-selected": {
backgroundColor: "action.selected",
"&:hover": {
backgroundColor: "action.selected",
},
},
py: 1,
}}
>
{suggestion.name}
{getDeliveryDays(suggestion)}
{new Intl.NumberFormat('de-DE', {style: 'currency', currency: 'EUR'}).format(suggestion.price)}
{t('product.inclVat', { vat: suggestion.vat })}
}
/>
))}
{t('common.more')}
)}
);
};
export default SearchBar;