I’m building a React application with Next.js where I need to fetch cards from an API and display them based on user-selected filters. Specifically, I want to fetch cards initially and then filter them based on the user’s selected location. Here’s the structure of my components and API routes:
page.js
import { useState } from "react";
import Navbar from "./Navbar";
import Title from "./Title";
import SearchBar from "./SearchBar";
import CardsArea from "./CardsArea";
import Footer from "./Footer";
export default function Home() {
const [filterOption, setFilterOption] = useState({});
const [sortOption, setSortOption] = useState("");
const handleFilterChange = (filter) => {
setFilterOption(filter);
};
const handleSortChange = (sort) => {
setSortOption(sort);
};
return (
<div>
<Navbar />
<Title />
<SearchBar onFilter={handleFilterChange} onSort={handleSortChange} />
<CardsArea filterOption={filterOption} sortOption={sortOption} />
<Footer />
</div>
);
}
CardsArea.js
javascript
Copy code
import { useEffect, useState } from "react";
import Card from "./Card";
export default function CardsArea({ filterOption, sortOption }) {
const [cards, setCards] = useState([]);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
useEffect(() => {
loadInitialCards();
}, [filterOption, sortOption]); // Reload cards when filter or sort options change
const loadInitialCards = async () => {
setLoading(true);
try {
// Fetch cards with filter and sort options
const response = await fetchCards(page, filterOption, sortOption);
const data = await response.json();
if (data.length === 0) {
setHasMore(false);
} else {
setCards(data);
setPage(page + 1);
}
} catch (error) {
console.error("Error loading initial cards:", error);
} finally {
setLoading(false);
}
};
const fetchCards = async (pageNum, filterOption, sortOption) => {
// Construct API URL with filter and sort parameters
const url = `/api/cards?page=${pageNum}&location=${filterOption.location}&month=${filterOption.month}&sort=${sortOption}`;
const response = await fetch(url);
return response;
};
return (
<div className="h-auto w-full flex bg-white flex-col justify-center pt-5">
<div className="flex justify-center flex-col mx-auto">
{cards.map((card, index) => (
<Card key={index} {...card} />
))}
{loading && <p>Loading...</p>}
{!loading && !hasMore && <p>End</p>}
</div>
</div>
);
}
route.js
import { NextResponse } from "next/server";
import { query } from "../../../lib/db";
export const GET = async (req) => {
const { searchParams } = new URL(req.url);
const location = searchParams.get('location');
const month = searchParams.get('month');
const sort = searchParams.get('sort');
try {
let sqlQuery = "SELECT * FROM card";
let conditions = [];
if (location) {
conditions.push(`location = '${location}'`);
}
if (month) {
conditions.push(`MONTH(departDate) = '${month}'`);
}
if (conditions.length > 0) {
sqlQuery += " WHERE " + conditions.join(" AND ");
}
if (sort) {
sqlQuery += ` ORDER BY ${sort}`;
}
const results = await query({ query: sqlQuery });
return NextResponse.json(results);
} catch (e) {
return NextResponse.json({ message: e.message }, { status: 500 });
}
};
SearchBar.js
import { useState } from "react";
export default function SearchBar({ onFilter, onSort }) {
const [locationFilter, setLocationFilter] = useState("");
const [monthFilter, setMonthFilter] = useState("");
const [sortOption, setSortOption] = useState("");
const handleFilterChange = () => {
onFilter({ location: locationFilter, month: monthFilter });
};
const handleSortChange = () => {
onSort(sortOption);
};
return (
<div className="max-w-4xl mx-auto border rounded-md border-black dark:bg-white bg-white shadow-lg p-2 flex flex-col sm:flex-row">
<div className="flex sm:border-none border-b border-black">
<div className="flex flex-1 justify-center sm:justify-start">
<select
id="location"
value={locationFilter}
onChange={(e) => setLocationFilter(e.target.value)}
className="text-black w-full sm:w-64"
>
<option value="">Departure Location</option>
<option value="Mumbai">Mumbai</option>
<option value="Delhi">Delhi</option>
<option value="Aurangabad">Aurangabad</option>
</select>
</div>
<div className="flex flex-1 justify-center mt-0 sm:mt-0">
<input
id="month"
type="month"
value={monthFilter}
onChange={(e) => setMonthFilter(e.target.value)}
className="text-black"
/>
</div>
</div>
<div className="flex">
<div className="flex flex-1 justify-center sm:justify-end mt-0 sm:mt-0">
<select
id="sort"
value={sortOption}
onChange={(e) => setSortOption(e.target.value)}
className="text-black dark:text-black w-[174px] lg:pt-3 pt-6 sm:w-48"
>
<option value="">Sort</option>
<option value="price ASC">Low to High</option>
<option value="price DESC">High to Low</option>
<option value="ratings DESC">Ratings</option>
</select>
</div>
<div className="flex flex-1 justify-center sm:justify-end mt-2 sm:mt-0">
<input className="w-full sm:w-auto" type="text" placeholder="Search"/>
</div>
<div className="flex justify-center mt-2 sm:mt-0">
<button
className="sm:w-9 w-full sm:block sm:pt-0 pt-2 sm:pr-0 pr-2 mt-1 flex sm:justify-normal justify-center h-8 rounded bg-gray-900"
onClick={handleFilterChange}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 ml-3" viewBox="0 0 512 512">
<path fill="#ffffff" d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/>
</svg>
</button>
</div>
</div>
</div>
);
}
How can i achieve it ?
i tried few ways but it didnt worked