A
Anonymous

Service Filter Sidebar - Copy this React, Tailwind Component to your project

import React, { useState, useEffect } from "react"; import { FaSpinner, FaStar, FaTimes, FaUser, FaShoppingCart, FaHome, FaGraduationCap, FaPaw, FaCalendarAlt, FaHeartbeat, FaLaptop, FaUtensils, FaCut, FaPaintBrush, FaShippingFast, FaChevronLeft, FaChevronRight, } from "react-icons/fa"; import { IoMenuOutline } from "react-icons/io5"; import dashBoardApi from "../apis/dashboard.api"; import { toast } from "react-toastify"; const ProductFilterSidebar = () => { const [selectedCategory, setSelectedCategory] = useState(""); const [topRatedOnly, setTopRatedOnly] = useState(false); const [loading, setLoading] = useState(false); const [services, setServices] = useState([]); const [searchTerm, setSearchTerm] = useState(""); const [suggestions, setSuggestions] = useState([]); const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage] = useState(6); const [data, setData] = useState([]); const categories = [ "Home Improvement", "Tutoring", "Pet Care", "Event Planning", "Health & Wellness", "Technology", "Home Cooking", "Beauty & Personal Care", "Arts & Crafts", "Errands & Delivery", ]; const categoryIcons = { "Home Improvement": <FaHome />, Tutoring: <FaGraduationCap />, "Pet Care": <FaPaw />, "Event Planning": <FaCalendarAlt />, "Health & Wellness": <FaHeartbeat />, Technology: <FaLaptop />, "Home Cooking": <FaUtensils />, "Beauty & Personal Care": <FaCut />, "Arts & Crafts": <FaPaintBrush />, "Errands & Delivery": <FaShippingFast />, }; const [isLoading, setIsLoading] = useState(true); // Add a loading state // ... (your other functions) useEffect(() => { const fetchData = async () => { setIsLoading(true); // Set loading to true before fetching try { dashBoardApi.AllServices( { success:(res)=>{ setData(res.data.data); const flattenedServices =res.data.data?.reduce((acc, category) => { return [...acc, ...category.services]; }, []); setServices(flattenedServices); setIsLoading(false); } } ); } catch (err) { toast.error("Error in Server", { position: "top-center", autoClose: "2000", }); } finally { setIsLoading(false); } }; fetchData(); // Call the async function to fetch data }, []); const dummyData = []; const flattenedServices = data?.reduce((acc, category) => { return [...acc, ...category.services]; }, []); useEffect(() => { console.log("is") setServices(flattenedServices); }, [setIsLoading,data]); const handleSearch = (value) => { setSearchTerm(value); const filtered = flattenedServices.filter((service) => service.name.toLowerCase().includes(value.toLowerCase()) ); setSuggestions(filtered); setCurrentPage(1); }; const handleFilter = () => { setLoading(true); setTimeout(() => { let filtered = [...flattenedServices]; if (selectedCategory) { filtered = filtered.filter( (service) => service.category === selectedCategory ); } if (searchTerm) { filtered = filtered.filter((service) => service.name.toLowerCase().includes(searchTerm.toLowerCase()) ); } if (topRatedOnly) { filtered = filtered.filter((service) => service.rating >= 4.8); } setServices(filtered); setCurrentPage(1); setLoading(false); }, 500); }; useEffect(() => { handleFilter(); }, [selectedCategory, topRatedOnly, searchTerm]); const indexOfLastItem = currentPage * itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage; const currentItems = services.slice(indexOfFirstItem, indexOfLastItem); const totalPages = Math.ceil(services.length / itemsPerPage); const paginate = (pageNumber) => { setCurrentPage(pageNumber); window.scrollTo({ top: 0, behavior: "smooth" }); }; return ( <div className="min-h-screen bg-gray-100"> <nav className="fixed top-0 left-0 right-0 bg-white shadow-lg z-50"> <div className="container mx-auto px-4"> <div className="flex items-center justify-between h-16"> <div className="flex items-center"> <FaHome className="text-2xl text-blue-600" /> <span className="ml-2 text-xl font-bold">ServiceHub Pro</span> </div> <div className="hidden md:flex items-center space-x-4"> <button className="p-2 hover:bg-gray-100 rounded-full"> <FaUser className="text-xl" /> </button> <button className="p-2 hover:bg-gray-100 rounded-full"> <FaShoppingCart className="text-xl" /> </button> </div> </div> </div> </nav> <button onClick={() => setIsSidebarOpen(!isSidebarOpen)} className="fixed top-20 left-4 z-50 p-2 bg-white rounded-md shadow-lg md:hidden" > {isSidebarOpen ? <FaTimes /> : <IoMenuOutline />} </button> <div className={`fixed top-16 left-0 w-64 h-full bg-white shadow-lg p-6 overflow-y-auto z-40 transition-transform duration-300 ease-in-out transform ${ isSidebarOpen ? "translate-x-0" : "-translate-x-full" } md:translate-x-0`} > <div className="space-y-6 mt-12 md:mt-0"> <div className="relative"> <input type="text" placeholder="Search services..." value={searchTerm} onChange={(e) => handleSearch(e.target.value)} className="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" /> {suggestions.length > 0 && searchTerm && ( <div className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg"> {suggestions.map((service) => ( <div key={service.id} className="p-2 hover:bg-gray-100 cursor-pointer" onClick={() => { setSearchTerm(service.name); setSuggestions([]); setIsSidebarOpen(false); }} > {service.name} </div> ))} </div> )} </div> <div className="space-y-2"> <h3 className="font-semibold text-gray-700">Categories</h3> <div className="space-y-1"> {categories.map((category) => ( <button key={category} onClick={() => { setSelectedCategory( category === selectedCategory ? "" : category ); setIsSidebarOpen(false); }} className={`w-full flex items-center space-x-2 px-3 py-2 rounded-md transition-colors ${ category === selectedCategory ? "bg-blue-500 text-white" : "hover:bg-gray-100" }`} > <span className="text-lg">{categoryIcons[category]}</span> <span>{category}</span> </button> ))} </div> </div> <button onClick={() => { setTopRatedOnly(!topRatedOnly); setIsSidebarOpen(false); }} className={`w-full px-4 py-2 rounded-md transition-colors ${ topRatedOnly ? "bg-blue-500 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200" }`} > <div className="flex items-center justify-center space-x-2"> <FaStar className={topRatedOnly ? "text-yellow-300" : "text-gray-400"} /> <span>Top Rated</span> </div> </button> </div> </div> <div className="ml-0 md:ml-64 p-6 mt-16"> {isLoading ? ( // Conditionally render loading indicator <div className="flex justify-center items-center h-64"> <FaSpinner className="animate-spin text-4xl text-blue-500" /> </div> ): <> {loading ? ( <div className="flex justify-center items-center h-64"> <FaSpinner className="animate-spin text-4xl text-blue-500" /> </div> ) : currentItems.length > 0 ? ( <> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> {currentItems.map((service) => ( <div key={service.id} className="bg-white rounded-lg shadow-md overflow-hidden transition-transform hover:scale-105" > <div className="relative w-full h-48"> <img src={`${service.image}`} alt={service.name} className="w-full h-full object-cover" loading="lazy" onError={(e) => { e.target.src = "https://images.unsplash.com/photo-1560520653-9e0e4c89eb11"; }} /> </div> <div className="p-4 space-y-3"> <div className="flex items-center space-x-2"> <span className="text-xl text-blue-500"> {categoryIcons[service.category]} </span> <h3 className="text-lg font-semibold text-gray-800"> {service.name} </h3> </div> <p className="text-gray-600">{service.category}</p> <div className="flex items-center justify-between"> <div className="flex items-center"> <FaStar className="text-yellow-400" /> <span className="ml-1 text-gray-700"> {service.rating} </span> </div> <button className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 transition-colors" onClick={() => alert(`Booking ${service.name}`)} > Book Now </button> </div> </div> </div> ))} </div> {/* Pagination Controls */} <div className="flex justify-center items-center mt-8 space-x-4"> <button onClick={() => paginate(currentPage - 1)} disabled={currentPage === 1} className={`p-2 rounded-full ${ currentPage === 1 ? "text-gray-400 cursor-not-allowed" : "text-blue-500 hover:bg-blue-100" }`} > <FaChevronLeft /> </button> {Array.from({ length: totalPages }).map((_, index) => ( <button key={index} onClick={() => paginate(index + 1)} className={`w-8 h-8 rounded-full ${ currentPage === index + 1 ? "bg-blue-500 text-white" : "text-blue-500 hover:bg-blue-100" }`} > {index + 1} </button> ))} <button onClick={() => paginate(currentPage + 1)} disabled={currentPage === totalPages} className={`p-2 rounded-full ${ currentPage === totalPages ? "text-gray-400 cursor-not-allowed" : "text-blue-500 hover:bg-blue-100" }`} > <FaChevronRight /> </button> </div> </> ) : ( <div className="flex flex-col items-center justify-center h-64 text-gray-500"> <p className="text-xl">No services found</p> <p className="mt-2">Try adjusting your filters</p> </div> )} </> } </div> </div> ); }; export default ProductFilterSidebar;check this code it is not showing items after click on catgories it is showing data

Prompt

About

ServiceFilterSidebar - A responsive sidebar for filtering services by category and top ratings, professionally built with React and Tailwind. Download code free!

Share

Last updated 1 month ago