Advertisement
thewitchking

Untitled

Jun 10th, 2025
1,051
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 3.73 KB | None | 0 0
  1. import React, { useState, useEffect, useRef, useCallback } from "react";
  2. import { FaSearch } from "react-icons/fa";
  3. import { useNavigate, useLocation } from "react-router-dom";
  4. import useDebounce from "../hooks/useDebounce";
  5. import { generateBaseURL } from "../utils";
  6. import "./SearchBar.css";
  7.  
  8. function SearchBar() {
  9.   const [searchText, setSearchText] = useState("");
  10.   const [searchResults, setSearchResults] = useState([]);
  11.   const [isDropdownOpen, setIsDropdownOpen] = useState(false);
  12.   const searchRef = useRef(null);
  13.   const navigate = useNavigate();
  14.   const location = useLocation();
  15.  
  16.   const debouncedSearchText = useDebounce(searchText, 300);
  17.  
  18.   // Fetch search results based on location text
  19.   const fetchSearchResults = useCallback(async (value) => {
  20.     if (value.length > 0) {
  21.       try {
  22.         const baseURL = generateBaseURL();
  23.         const response = await fetch(
  24.           `${baseURL}/api/apartments/search?location=${value}`
  25.         );
  26.  
  27.         if (!response.ok) {
  28.           throw new Error("Network response was not ok");
  29.         }
  30.  
  31.         const data = await response.json();
  32.         setSearchResults(data);
  33.         setIsDropdownOpen(true);
  34.       } catch (error) {
  35.         console.error("Error retrieving search results:", error);
  36.         setSearchResults([]);
  37.         setIsDropdownOpen(false);
  38.       }
  39.     } else {
  40.       setSearchResults([]);
  41.       setIsDropdownOpen(false);
  42.     }
  43.   }, []);
  44.  
  45.   // Debounced search execution
  46.   useEffect(() => {
  47.     fetchSearchResults(debouncedSearchText);
  48.   }, [debouncedSearchText, fetchSearchResults]);
  49.  
  50.   // Close dropdown on route change
  51.   useEffect(() => {
  52.     setIsDropdownOpen(false);
  53.   }, [location]);
  54.  
  55.   // Click outside handler
  56.   useEffect(() => {
  57.     const handleClickOutside = (event) => {
  58.       if (
  59.         searchRef.current &&
  60.         !searchRef.current.contains(event.target)
  61.       ) {
  62.         setIsDropdownOpen(false);
  63.       }
  64.     };
  65.  
  66.     document.addEventListener("mousedown", handleClickOutside);
  67.     return () => {
  68.       document.removeEventListener("mousedown", handleClickOutside);
  69.     };
  70.   }, []);
  71.  
  72.   // Navigate on result click
  73.   const handleResultClick = (result) => {
  74.     navigate(`/details/${result.id}`);
  75.     setIsDropdownOpen(false);
  76.   };
  77.  
  78.   return (
  79.     <div ref={searchRef} className="search-container relative w-full max-w-xl mx-auto">
  80.       <div
  81.         className="search-input-wrapper flex items-center border border-gray-300 rounded px-3 py-2 bg-white"
  82.         data-testid="search-div"
  83.       >
  84.         <FaSearch className="text-gray-500 mr-2" />
  85.         <input
  86.           type="text"
  87.           className="w-full bg-transparent focus:outline-none text-sm"
  88.           placeholder="Search apartments..."
  89.           value={searchText}
  90.           onChange={(e) => setSearchText(e.target.value)}
  91.           onFocus={() => {
  92.             if (searchResults.length > 0) setIsDropdownOpen(true);
  93.           }}
  94.           data-testid="search-input"
  95.         />
  96.       </div>
  97.  
  98.       {isDropdownOpen && (
  99.         <div
  100.           className="dropdown absolute top-full mt-2 w-full bg-white border border-gray-200 rounded shadow z-10"
  101.           data-testid="auth-dropdown-btn"
  102.         >
  103.           {searchResults.length > 0 ? (
  104.             searchResults.map((result, index) => (
  105.               <div
  106.                 key={index}
  107.                 className="px-4 py-2 hover:bg-gray-100 cursor-pointer text-sm"
  108.                 onClick={() => handleResultClick(result)}
  109.               >
  110.                 {result.name}
  111.               </div>
  112.             ))
  113.           ) : (
  114.             <div className="px-4 py-2 text-sm text-gray-500">No results found</div>
  115.           )}
  116.         </div>
  117.       )}
  118.     </div>
  119.   );
  120. }
  121.  
  122. export default SearchBar;
  123.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement