diff --git a/src/pages/Listings.tsx b/src/pages/Listings.tsx index 32a296f..184989f 100644 --- a/src/pages/Listings.tsx +++ b/src/pages/Listings.tsx @@ -1,6 +1,7 @@ -import React, { useState, useEffect } from 'react'; -import { useLocation, useSearchParams } from 'react-router-dom'; -import { ListFilter, Search, ChevronDown, X } from 'lucide-react'; +import { useState, useEffect } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { ListFilter, Search, X } from 'lucide-react'; + import ListingCard from '../components/listings/ListingCard'; import Button from '../components/common/Button'; import { Listing } from '../types'; @@ -8,100 +9,119 @@ import { listings as mockListings, categories } from '../data/mockData'; const Listings: React.FC = () => { const [searchParams, setSearchParams] = useSearchParams(); - const location = useLocation(); - + const [listings, setListings] = useState([]); const [filteredListings, setFilteredListings] = useState([]); const [isFiltersOpen, setIsFiltersOpen] = useState(false); - // Parse filters from URL + /* ---------------- URL FILTERS ---------------- */ const categoryFilter = searchParams.get('category') || ''; const priceMinFilter = searchParams.get('price_min') || ''; const priceMaxFilter = searchParams.get('price_max') || ''; const searchFilter = searchParams.get('search') || ''; const sortBy = searchParams.get('sort') || 'newest'; - - // Form state + + /* ---------------- FORM STATE ---------------- */ const [filters, setFilters] = useState({ category: categoryFilter, priceMin: priceMinFilter, priceMax: priceMaxFilter, search: searchFilter, }); - - // Initialize with mock data (only approved listings) + + /* ---------------- LOAD DATA ---------------- */ useEffect(() => { - const approvedListings = mockListings.filter(listing => listing.status === 'approved'); + const approvedListings = mockListings.filter( + (listing) => listing.status === 'approved' + ); setListings(approvedListings); }, []); - - // Apply filters + + /* ---------------- APPLY FILTERS ---------------- */ useEffect(() => { let result = [...listings]; - - // Apply search filter + if (searchFilter) { - const searchLower = searchFilter.toLowerCase(); - result = result.filter(listing => - listing.title.toLowerCase().includes(searchLower) || - listing.description.toLowerCase().includes(searchLower) + const q = searchFilter.toLowerCase(); + result = result.filter( + (l) => + l.title.toLowerCase().includes(q) || + l.description.toLowerCase().includes(q) ); } - - // Apply category filter + if (categoryFilter) { - result = result.filter(listing => listing.categoryId === categoryFilter); + result = result.filter( + (l) => l.categoryId === categoryFilter + ); } - - // Apply price filters + if (priceMinFilter) { - const min = parseFloat(priceMinFilter); - result = result.filter(listing => listing.price >= min); + result = result.filter( + (l) => l.price >= Number(priceMinFilter) + ); } - + if (priceMaxFilter) { - const max = parseFloat(priceMaxFilter); - result = result.filter(listing => listing.price <= max); + result = result.filter( + (l) => l.price <= Number(priceMaxFilter) + ); } - - // Apply sorting - if (sortBy === 'price_low') { - result.sort((a, b) => a.price - b.price); - } else if (sortBy === 'price_high') { - result.sort((a, b) => b.price - a.price); - } else if (sortBy === 'oldest') { - result.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()); - } else { - // Default: newest first - result.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + + switch (sortBy) { + case 'price_low': + result.sort((a, b) => a.price - b.price); + break; + case 'price_high': + result.sort((a, b) => b.price - a.price); + break; + case 'oldest': + result.sort( + (a, b) => + new Date(a.createdAt).getTime() - + new Date(b.createdAt).getTime() + ); + break; + default: + result.sort( + (a, b) => + new Date(b.createdAt).getTime() - + new Date(a.createdAt).getTime() + ); } - + setFilteredListings(result); - }, [listings, categoryFilter, priceMinFilter, priceMaxFilter, searchFilter, sortBy]); - - const handleFilterChange = (e: React.ChangeEvent) => { + }, [ + listings, + categoryFilter, + priceMinFilter, + priceMaxFilter, + searchFilter, + sortBy, + ]); + + /* ---------------- HANDLERS ---------------- */ + const handleFilterChange = ( + e: React.ChangeEvent + ) => { const { name, value } = e.target; - setFilters(prev => ({ ...prev, [name]: value })); + setFilters((prev) => ({ ...prev, [name]: value })); }; - + const applyFilters = (e: React.FormEvent) => { e.preventDefault(); - - // Update URL with filters - const newSearchParams = new URLSearchParams(); - - if (filters.category) newSearchParams.set('category', filters.category); - if (filters.priceMin) newSearchParams.set('price_min', filters.priceMin); - if (filters.priceMax) newSearchParams.set('price_max', filters.priceMax); - if (filters.search) newSearchParams.set('search', filters.search); - if (sortBy) newSearchParams.set('sort', sortBy); - - setSearchParams(newSearchParams); - - // Close mobile filters + const params = new URLSearchParams(); + + if (filters.category) params.set('category', filters.category); + if (filters.priceMin) params.set('price_min', filters.priceMin); + if (filters.priceMax) params.set('price_max', filters.priceMax); + if (filters.search) params.set('search', filters.search); + params.set('sort', sortBy); + + setSearchParams(params); setIsFiltersOpen(false); }; - + const clearFilters = () => { setFilters({ category: '', @@ -111,260 +131,46 @@ const Listings: React.FC = () => { }); setSearchParams(new URLSearchParams({ sort: sortBy })); }; - - const hasActiveFilters = categoryFilter || priceMinFilter || priceMaxFilter || searchFilter; - - const handleSortChange = (e: React.ChangeEvent) => { - const newSortValue = e.target.value; - - // Update URL with new sort parameter but keep existing filters - const newSearchParams = new URLSearchParams(searchParams); - newSearchParams.set('sort', newSortValue); - setSearchParams(newSearchParams); - }; + const hasActiveFilters = + categoryFilter || priceMinFilter || priceMaxFilter || searchFilter; + + /* ---------------- UI ---------------- */ return ( -
-
-

Browse Listings

-

- Explore our collection of quality, verified listings from professional agencies +

+

Browse Listings

+ + {categoryFilter && ( +

+ Category:{' '} + + {categories.find(c => c.id === categoryFilter)?.name} +

-
- - {/* Search and filter bar */} -
-
-
-
- - - - -
- -
- - -
- -
- -
- - -
-
-
-
- - {/* Filter panel */} - {isFiltersOpen && ( -
-
-
-

Filters

- -
- -
-
- - -
- -
- - -
- -
- - -
- -
- -
-
-
-
)} - - {/* Active filters badges */} - {hasActiveFilters && ( -
- Active Filters: - - {categoryFilter && ( - - {categories.find(c => c.id === categoryFilter)?.name || 'Category'} - - - )} - - {priceMinFilter && ( - - Min: ${priceMinFilter} - - - )} - - {priceMaxFilter && ( - - Max: ${priceMaxFilter} - - - )} - - {searchFilter && ( - - Search: "{searchFilter}" - - - )} -
- )} - - {/* Results count */} -
-

- Showing {filteredListings.length} results - {hasActiveFilters ? ' with applied filters' : ''} -

-
- - {/* Listings grid */} + + {/* SEARCH */} +
+ + + + + {/* RESULTS */} {filteredListings.length > 0 ? ( -
- {filteredListings.map(listing => ( +
+ {filteredListings.map((listing) => ( ))}
) : ( -
-

No Listings Found

-

- We couldn't find any listings matching your criteria. -

+
+

No listings found

@@ -374,4 +180,4 @@ const Listings: React.FC = () => { ); }; -export default Listings; \ No newline at end of file +export default Listings;