feat: add autocomplete suggestions feature in search page

This commit is contained in:
Yash-1511
2025-01-05 17:30:00 +05:30
parent 756f4a2a66
commit f306159a5a
2 changed files with 98 additions and 22 deletions

View File

@@ -173,7 +173,10 @@ export default function Search() {
const [searchResultsLoading, setSearchResultsLoading] = useState(false);
const [focusSearchResult, setFocusSearchResult] = useState<SearchResult | null>(null);
const [exampleQuery, setExampleQuery] = useState("");
const [fileSuggestions, setFileSuggestions] = useState<string[]>([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const suggestionsTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const isMobileWidth = useIsMobileWidth();
@@ -205,29 +208,62 @@ export default function Search() {
});
}
useEffect(() => {
if (!searchQuery.trim()) {
function getFileSuggestions(query: string) {
// Get suggestions only if query starts with "file:"
if (!query.toLowerCase().startsWith("file:")) {
setFileSuggestions([]);
setShowSuggestions(false);
return;
}
setFocusSearchResult(null);
const filePrefix = query.substring(5).trim(); // Remove "file:" prefix
const apiUrl = `/api/file-suggestions?q=${encodeURIComponent(filePrefix)}`;
fetch(apiUrl, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => response.json())
.then((data) => {
setFileSuggestions(data);
setShowSuggestions(true);
})
.catch((error) => {
console.error("Error:", error);
});
}
function handleSearchInputChange(value: string) {
setSearchQuery(value);
// Clear previous timeouts
if (searchTimeoutRef.current) {
clearTimeout(searchTimeoutRef.current);
}
if (searchQuery.trim()) {
searchTimeoutRef.current = setTimeout(() => {
search();
}, 750); // 1000 milliseconds = 1 second
if (suggestionsTimeoutRef.current) {
clearTimeout(suggestionsTimeoutRef.current);
}
return () => {
if (searchTimeoutRef.current) {
clearTimeout(searchTimeoutRef.current);
}
};
}, [searchQuery]);
// Get file suggestions immediately
suggestionsTimeoutRef.current = setTimeout(() => {
getFileSuggestions(value);
}, 100);
// Debounce search
if (value.trim()) {
searchTimeoutRef.current = setTimeout(() => {
search();
}, 750);
}
}
function applySuggestion(suggestion: string) {
setSearchQuery(`file:${suggestion}`);
setShowSuggestions(false);
search();
}
return (
<SidebarProvider>
@@ -247,14 +283,38 @@ export default function Search() {
<div className="md:w-3/4 sm:w-full mx-auto pt-6 md:pt-8">
<div className="p-4 md:w-3/4 sm:w-full mx-auto">
<div className="flex justify-between items-center border-2 border-muted p-1 gap-1 rounded-lg">
<Input
autoFocus={true}
className="border-none pl-4"
onChange={(e) => setSearchQuery(e.currentTarget.value)}
onKeyDown={(e) => e.key === "Enter" && search()}
type="search"
placeholder="Search Documents"
/>
<div className="relative flex-1">
<Input
autoFocus={true}
className="border-none pl-4"
onChange={(e) => handleSearchInputChange(e.currentTarget.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
if (showSuggestions && fileSuggestions.length > 0) {
applySuggestion(fileSuggestions[0]);
} else {
search();
}
}
}}
type="search"
placeholder="Search Documents (type 'file:' for file suggestions)"
value={searchQuery}
/>
{showSuggestions && fileSuggestions.length > 0 && (
<div className="absolute z-10 w-full mt-1 bg-background border rounded-md shadow-lg">
{fileSuggestions.map((suggestion, index) => (
<div
key={index}
className="px-4 py-2 hover:bg-muted cursor-pointer"
onClick={() => applySuggestion(suggestion)}
>
{suggestion}
</div>
))}
</div>
)}
</div>
<button
className="px-2 gap-2 inline-flex items-center rounded border-l border-gray-300 hover:text-gray-500"
onClick={() => search()}