mirror of
http://88.130.71.182:3000/BlitTech/Projet1-RealEstate.git
synced 2026-06-12 23:33:21 +00:00
uPGRADED lISTING
✔ Home → Category click ✔ Categories page → Category click ✔ URL uses clean slug ✔ Listings filter correctly ✔ No redirect loops ✔ No empty results bug
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { ListFilter, Search, X } from 'lucide-react';
|
import { Search } from 'lucide-react';
|
||||||
|
|
||||||
import ListingCard from '../components/listings/ListingCard';
|
import ListingCard from '../components/listings/ListingCard';
|
||||||
import Button from '../components/common/Button';
|
import Button from '../components/common/Button';
|
||||||
@@ -12,10 +12,9 @@ const Listings: React.FC = () => {
|
|||||||
|
|
||||||
const [listings, setListings] = useState<Listing[]>([]);
|
const [listings, setListings] = useState<Listing[]>([]);
|
||||||
const [filteredListings, setFilteredListings] = useState<Listing[]>([]);
|
const [filteredListings, setFilteredListings] = useState<Listing[]>([]);
|
||||||
const [isFiltersOpen, setIsFiltersOpen] = useState(false);
|
|
||||||
|
|
||||||
/* ---------------- URL FILTERS ---------------- */
|
/* ---------------- URL FILTERS ---------------- */
|
||||||
const categoryFilter = searchParams.get('category') || '';
|
const categorySlug = searchParams.get('category') || '';
|
||||||
const priceMinFilter = searchParams.get('price_min') || '';
|
const priceMinFilter = searchParams.get('price_min') || '';
|
||||||
const priceMaxFilter = searchParams.get('price_max') || '';
|
const priceMaxFilter = searchParams.get('price_max') || '';
|
||||||
const searchFilter = searchParams.get('search') || '';
|
const searchFilter = searchParams.get('search') || '';
|
||||||
@@ -23,7 +22,7 @@ const Listings: React.FC = () => {
|
|||||||
|
|
||||||
/* ---------------- FORM STATE ---------------- */
|
/* ---------------- FORM STATE ---------------- */
|
||||||
const [filters, setFilters] = useState({
|
const [filters, setFilters] = useState({
|
||||||
category: categoryFilter,
|
category: categorySlug,
|
||||||
priceMin: priceMinFilter,
|
priceMin: priceMinFilter,
|
||||||
priceMax: priceMaxFilter,
|
priceMax: priceMaxFilter,
|
||||||
search: searchFilter,
|
search: searchFilter,
|
||||||
@@ -41,6 +40,7 @@ const Listings: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let result = [...listings];
|
let result = [...listings];
|
||||||
|
|
||||||
|
// 🔎 Search
|
||||||
if (searchFilter) {
|
if (searchFilter) {
|
||||||
const q = searchFilter.toLowerCase();
|
const q = searchFilter.toLowerCase();
|
||||||
result = result.filter(
|
result = result.filter(
|
||||||
@@ -50,24 +50,24 @@ const Listings: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (categoryFilter) {
|
// 🏷 Category (slug → id)
|
||||||
result = result.filter(
|
if (categorySlug) {
|
||||||
(l) => l.categoryId === categoryFilter
|
const category = categories.find(c => c.slug === categorySlug);
|
||||||
);
|
if (category) {
|
||||||
|
result = result.filter(l => l.categoryId === category.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 💰 Price
|
||||||
if (priceMinFilter) {
|
if (priceMinFilter) {
|
||||||
result = result.filter(
|
result = result.filter(l => l.price >= Number(priceMinFilter));
|
||||||
(l) => l.price >= Number(priceMinFilter)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priceMaxFilter) {
|
if (priceMaxFilter) {
|
||||||
result = result.filter(
|
result = result.filter(l => l.price <= Number(priceMaxFilter));
|
||||||
(l) => l.price <= Number(priceMaxFilter)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ↕ Sort
|
||||||
switch (sortBy) {
|
switch (sortBy) {
|
||||||
case 'price_low':
|
case 'price_low':
|
||||||
result.sort((a, b) => a.price - b.price);
|
result.sort((a, b) => a.price - b.price);
|
||||||
@@ -93,7 +93,7 @@ const Listings: React.FC = () => {
|
|||||||
setFilteredListings(result);
|
setFilteredListings(result);
|
||||||
}, [
|
}, [
|
||||||
listings,
|
listings,
|
||||||
categoryFilter,
|
categorySlug,
|
||||||
priceMinFilter,
|
priceMinFilter,
|
||||||
priceMaxFilter,
|
priceMaxFilter,
|
||||||
searchFilter,
|
searchFilter,
|
||||||
@@ -105,7 +105,7 @@ const Listings: React.FC = () => {
|
|||||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
||||||
) => {
|
) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
setFilters((prev) => ({ ...prev, [name]: value }));
|
setFilters(prev => ({ ...prev, [name]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyFilters = (e: React.FormEvent) => {
|
const applyFilters = (e: React.FormEvent) => {
|
||||||
@@ -119,7 +119,6 @@ const Listings: React.FC = () => {
|
|||||||
params.set('sort', sortBy);
|
params.set('sort', sortBy);
|
||||||
|
|
||||||
setSearchParams(params);
|
setSearchParams(params);
|
||||||
setIsFiltersOpen(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearFilters = () => {
|
const clearFilters = () => {
|
||||||
@@ -132,20 +131,16 @@ const Listings: React.FC = () => {
|
|||||||
setSearchParams(new URLSearchParams({ sort: sortBy }));
|
setSearchParams(new URLSearchParams({ sort: sortBy }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasActiveFilters =
|
const activeCategory = categories.find(c => c.slug === categorySlug);
|
||||||
categoryFilter || priceMinFilter || priceMaxFilter || searchFilter;
|
|
||||||
|
|
||||||
/* ---------------- UI ---------------- */
|
/* ---------------- UI ---------------- */
|
||||||
return (
|
return (
|
||||||
<div className="max-w-7xl mx-auto px-4 py-8">
|
<div className="max-w-7xl mx-auto px-4 py-8">
|
||||||
<h1 className="text-3xl font-bold mb-2">Browse Listings</h1>
|
<h1 className="text-3xl font-bold mb-2">Browse Listings</h1>
|
||||||
|
|
||||||
{categoryFilter && (
|
{activeCategory && (
|
||||||
<p className="mb-4 text-gray-600">
|
<p className="mb-4 text-gray-600">
|
||||||
Category:{' '}
|
Category: <span className="font-semibold">{activeCategory.name}</span>
|
||||||
<span className="font-semibold">
|
|
||||||
{categories.find(c => c.id === categoryFilter)?.name}
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -164,7 +159,7 @@ const Listings: React.FC = () => {
|
|||||||
{/* RESULTS */}
|
{/* RESULTS */}
|
||||||
{filteredListings.length > 0 ? (
|
{filteredListings.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{filteredListings.map((listing) => (
|
{filteredListings.map(listing => (
|
||||||
<ListingCard key={listing.id} listing={listing} />
|
<ListingCard key={listing.id} listing={listing} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user