From f8b1db4542a716d2873384c25335364cd6dcf34c Mon Sep 17 00:00:00 2001 From: Apar Agarwal Date: Sat, 11 Oct 2025 07:55:21 +0530 Subject: [PATCH 1/2] Fix: Implement product sorting and improve dropdown functionality --- src/api/productService.js | 25 ++++++++- src/components/Products/ProductCard.jsx | 25 +++++++-- src/components/Products/SortDropdown.jsx | 46 +++++++++------- src/pages/Products/Products.jsx | 69 +++++++----------------- 4 files changed, 89 insertions(+), 76 deletions(-) diff --git a/src/api/productService.js b/src/api/productService.js index c74d0330..434f8ad2 100644 --- a/src/api/productService.js +++ b/src/api/productService.js @@ -53,11 +53,32 @@ export const getProducts = async (params = {}) => { if (params.sortBy) apiParams.sortBy = params.sortBy; if (params.sortOrder) apiParams.sortOrder = params.sortOrder; - const response = await axiosInstance.get('/products', { params: apiParams }); + let response; + if (params.sort) { + const sortKey = encodeURIComponent(params.sort); + response = await axiosInstance.get(`/products/sort/${sortKey}`, { params: apiParams }); + } else { + response = await axiosInstance.get('/products', { params: apiParams }); + } + const data = response.data; + + if (Array.isArray(data)) { + return { + success: true, + data: { + products: data, + total: data.length, + page: 1, + pages: 1, + }, + status: response.status, + }; + } + return { success: true, - data: transformApiResponse(response.data), + data: transformApiResponse(data), status: response.status, }; } catch (error) { diff --git a/src/components/Products/ProductCard.jsx b/src/components/Products/ProductCard.jsx index f3c5162b..6f0e50b7 100644 --- a/src/components/Products/ProductCard.jsx +++ b/src/components/Products/ProductCard.jsx @@ -171,11 +171,26 @@ const ProductCard = forwardRef(({ product }, ref) => {
- {[...Array(5)].map((_, i) => ( - - - - ))} + {[0, 1, 2, 3, 4].map((i) => { + const ratingValue = Number(product.rating) || 0; + const fill = Math.max(0, Math.min(1, ratingValue - i)); // 0..1 + const pct = Math.round(fill * 100); + return ( +
+ {/* Gray background star */} + + + + + {/* Yellow foreground star clipped to pct width */} +
+ + + +
+
+ ); + })}
({product.reviewCount || 0})
diff --git a/src/components/Products/SortDropdown.jsx b/src/components/Products/SortDropdown.jsx index 39d69fcf..f757bc6b 100644 --- a/src/components/Products/SortDropdown.jsx +++ b/src/components/Products/SortDropdown.jsx @@ -18,7 +18,7 @@ export default function SortDropdown({ sortBy = 'featured', sortOrder = 'desc', option => option.field === sortBy && option.order === sortOrder ) || SORT_OPTIONS[0]; - // This hook now handles clicks outside AND page scrolling + // Handle clicks outside to close the dropdown. Do NOT close on scroll — UX requires it remain open until user closes. useEffect(() => { const handleClickOutside = (event) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { @@ -26,21 +26,12 @@ export default function SortDropdown({ sortBy = 'featured', sortOrder = 'desc', } }; - // A simple function to close the dropdown - const handleScroll = () => { - setIsOpen(false); - }; - - // Add event listeners only when the dropdown is open if (isOpen) { document.addEventListener('mousedown', handleClickOutside); - document.addEventListener('scroll', handleScroll, true); // Added scroll listener } - // Cleanup: remove both event listeners return () => { document.removeEventListener('mousedown', handleClickOutside); - document.removeEventListener('scroll', handleScroll, true); // Removed scroll listener }; }, [isOpen]); @@ -50,16 +41,16 @@ export default function SortDropdown({ sortBy = 'featured', sortOrder = 'desc', }; return ( -
-
+
+
Sort by: -
+
-
+
- {isOpen && ( -
+
+ {/* Header with title and X close button */} +
+
Sort by
+ +
+
{SORT_OPTIONS.map((option, index) => (
- )}
); diff --git a/src/pages/Products/Products.jsx b/src/pages/Products/Products.jsx index cc574e6e..5ee4e9e9 100644 --- a/src/pages/Products/Products.jsx +++ b/src/pages/Products/Products.jsx @@ -70,51 +70,7 @@ export default function Products() { }); }, [allProducts, filters, maxProductPrice]); - // Apply client-side sorting to filtered products - const sortedProducts = useMemo(() => { - if (!filteredProducts || filteredProducts.length === 0) return []; - - // shallow copy to avoid mutating original array - const copy = [...filteredProducts]; - - const getString = (p, key) => (p[key] || p.name || p.title || '').toString().toLowerCase(); - const getNumber = (p, key) => { - const val = p[key]; - if (val === undefined || val === null || val === '') return 0; - const n = Number(val); - return Number.isNaN(n) ? 0 : n; - }; - - switch (`${sortBy}:${sortOrder}`) { - case 'title:asc': - copy.sort((a, b) => getString(a, 'name').localeCompare(getString(b, 'name'))); - break; - case 'title:desc': - copy.sort((a, b) => getString(b, 'name').localeCompare(getString(a, 'name'))); - break; - case 'price:asc': - copy.sort((a, b) => getNumber(a, 'price') - getNumber(b, 'price')); - break; - case 'price:desc': - copy.sort((a, b) => getNumber(b, 'price') - getNumber(a, 'price')); - break; - case 'rating:asc': - copy.sort((a, b) => getNumber(a, 'rating') - getNumber(b, 'rating')); - break; - case 'rating:desc': - copy.sort((a, b) => getNumber(b, 'rating') - getNumber(a, 'rating')); - break; - case 'featured:desc': - default: - // Featured (placeholder) - keep backend/default ordering - break; - } - - return copy; - }, [filteredProducts, sortBy, sortOrder]); - - // Simple display of filtered products with pagination/infinite scroll - const displayedProducts = sortedProducts.slice(0, displayedCount); + const displayedProducts = filteredProducts.slice(0, displayedCount); const hasMoreProducts = displayedCount < filteredProducts.length; // Infinite scroll callback @@ -135,7 +91,7 @@ export default function Products() { ); useEffect(() => { - dispatch(fetchProducts({ page: 1, limit: 1000 })); + dispatch(fetchProducts({ page: 1, limit: 1000, sort: 'feature' })); }, [dispatch]); return ( @@ -174,7 +130,7 @@ export default function Products() {
{/* Left: All Filters button (full width on mobile) */} -
From 8c81c402b493425bc4fd4570adce3768660e4ce7 Mon Sep 17 00:00:00 2001 From: Apar Agarwal Date: Sat, 11 Oct 2025 08:33:37 +0530 Subject: [PATCH 2/2] Refactor SortDropdown component for clarity and functionality --- src/components/Products/SortDropdown.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Products/SortDropdown.jsx b/src/components/Products/SortDropdown.jsx index f757bc6b..d637014b 100644 --- a/src/components/Products/SortDropdown.jsx +++ b/src/components/Products/SortDropdown.jsx @@ -61,7 +61,7 @@ export default function SortDropdown({ sortBy = 'featured', sortOrder = 'desc',
); -} \ No newline at end of file + +}