From 138f26e29307a9f3808e3006451d1e5519c8eaa9 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Tue, 2 Sep 2025 14:53:58 +0330 Subject: [PATCH] responsive base v2 --- src/app/products/[id]/page.tsx | 30 ++++++- src/app/products/components/Filters.tsx | 34 +++++++- src/app/products/components/Sorts.tsx | 102 ++++++++++++++++++------ src/components/Input.tsx | 4 +- 4 files changed, 138 insertions(+), 32 deletions(-) diff --git a/src/app/products/[id]/page.tsx b/src/app/products/[id]/page.tsx index 8348519..adddb5b 100644 --- a/src/app/products/[id]/page.tsx +++ b/src/app/products/[id]/page.tsx @@ -1,3 +1,5 @@ +'use client' + import Layout from '@/hoc/Layout' import { NextPage } from 'next' import { @@ -12,10 +14,14 @@ import Filters from '../components/Filters' import Sorts from '../components/Sorts' import GridWrapper from '@/components/GridWrapper' import ProductCard from '@/components/ProductCard' +import { Setting5 } from 'iconsax-react' +import { useState } from 'react' const Products: NextPage = () => { + const [showMobileFilters, setShowMobileFilters] = useState(false) + return ( -
+
@@ -32,8 +38,28 @@ const Products: NextPage = () => { -
+ {/* Mobile Filter Button */} +
+ +
+ +
+ {/* Desktop Filters */} + + {/* Mobile Filters Modal */} + setShowMobileFilters(false)} + /> +
diff --git a/src/app/products/components/Filters.tsx b/src/app/products/components/Filters.tsx index b5c47b8..64dfd0e 100644 --- a/src/app/products/components/Filters.tsx +++ b/src/app/products/components/Filters.tsx @@ -11,8 +11,15 @@ import { getCategories } from '@/share/service/ShareService' import { usePathname, useRouter, useSearchParams } from 'next/navigation' import CategoryTree from './CategoryTree' import { Category } from '@/share/types/SharedTypes' +import Modal from '@/components/Modal' -const Filters: FC = () => { +type FiltersProps = { + isMobile?: boolean + isOpen?: boolean + onClose?: () => void +} + +const Filters: FC = ({ isMobile = false, isOpen = false, onClose }) => { const router = useRouter() const pathname = usePathname() const searchParams = useSearchParams() @@ -64,6 +71,11 @@ const Filters: FC = () => { if (rating) params.set('rating', String(rating)) else params.delete('rating') router.push(`${pathname}?${params.toString()}`) + + // Close mobile modal after applying filters + if (isMobile && onClose) { + onClose() + } } const clearFilters = () => { @@ -77,8 +89,8 @@ const Filters: FC = () => { router.push(`${pathname}?${params.toString()}`) } - return ( -
+ const FilterContent = () => ( + <>
فیلترها
@@ -169,6 +181,22 @@ const Filters: FC = () => {
+ + ) + + if (isMobile) { + return ( + { })} title="فیلترها" isHeader={true}> +
+ +
+
+ ) + } + + return ( +
+
) } diff --git a/src/app/products/components/Sorts.tsx b/src/app/products/components/Sorts.tsx index 3195ba1..d9ff742 100644 --- a/src/app/products/components/Sorts.tsx +++ b/src/app/products/components/Sorts.tsx @@ -1,39 +1,91 @@ -import { Sort } from 'iconsax-react' -import { FC } from 'react' +import { Sort, ArrowDown2 } from 'iconsax-react' +import { FC, useState, useEffect, useRef } from 'react' + +const sortOptions = [ + { key: 'relevant', label: 'مرتبط ترین' }, + { key: 'most_visited', label: 'پربازدیدترین' }, + { key: 'newest', label: 'جدیدترین' }, + { key: 'best_selling', label: 'پرفروش ترین' }, + { key: 'cheapest', label: 'ارزانترین' }, + { key: 'most_expensive', label: 'گرانترین' }, + { key: 'customer_suggestion', label: 'پیشنهاد خریداران' }, +] const Sorts: FC = () => { + const [selectedSort, setSelectedSort] = useState('newest') + const [showDropdown, setShowDropdown] = useState(false) + const dropdownRef = useRef(null) + + const selectedOption = sortOptions.find(option => option.key === selectedSort) + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setShowDropdown(false) + } + } + + if (showDropdown) { + document.addEventListener('mousedown', handleClickOutside) + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [showDropdown]) + return (
-
+ {/* Desktop Version */} +
مرتب سازی:
-
- مرتبط ترین -
-
- پربازدیدترین -
-
- جدیدترین -
-
- پرفروش ترین -
-
- ارزانترین -
-
- گرانترین -
-
- پیشنهاد خریداران -
+ {sortOptions.map((option) => ( +
setSelectedSort(option.key)} + > + {option.label} +
+ ))}
-
+ {/* Mobile Version */} +
+ +
مرتب سازی:
+
setShowDropdown(!showDropdown)} + > + {selectedOption?.label} + +
+ + {showDropdown && ( +
+ {sortOptions.map((option) => ( +
{ + setSelectedSort(option.key) + setShowDropdown(false) + }} + > + {option.label} +
+ ))} +
+ )} +
+ +
100 کالا
diff --git a/src/components/Input.tsx b/src/components/Input.tsx index c4f48b9..294321d 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -33,7 +33,7 @@ const Input: FC = (props: Props) => { const [search, setSearch] = useState('') const inputClass = clx( - 'w-full h-10 sm:h-12 text-black block px-3 sm:px-4 text-xs sm:text-sm rounded-xl border border-[#D0D0D0]', + 'w-full h-10 text-black block px-4 text-xs rounded-xl border border-[#D0D0D0]', props.readOnly && 'bg-gray-100 border-0 text-description', props.variant === 'search' && 'bg-[#EEF0F7] border-0 pr-10', props.className @@ -80,7 +80,7 @@ const Input: FC = (props: Props) => { return (
-