277 lines
9.8 KiB
TypeScript
277 lines
9.8 KiB
TypeScript
import React, { useState } from "react";
|
||
import { useSearchParams } from "@remix-run/react";
|
||
import { X } from "lucide-react";
|
||
import { DrawerHeader, DrawerTitle, DrawerContent, Drawer } from "./ui/drawer";
|
||
import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
|
||
import { Slider } from "./ui/slider";
|
||
import { Button } from "./ui/button";
|
||
import { useRootData } from "~/hooks/use-root-data";
|
||
import filterIcon from "~/assets/icons/filter.png";
|
||
import filterIconDark from "~/assets/icons/filter-dark.png";
|
||
|
||
interface FilterDrawerProps {
|
||
minimumPrice: number;
|
||
maximumPrice: number;
|
||
}
|
||
|
||
const FilterDrawer: React.FC<FilterDrawerProps> = ({
|
||
minimumPrice,
|
||
maximumPrice,
|
||
}) => {
|
||
const { theme } = useRootData();
|
||
const [isOpen, setIsOpen] = useState(false);
|
||
const [searchParams, setSearchParams] = useSearchParams();
|
||
const [order, setOrder] = useState(searchParams.get("order") || "all");
|
||
|
||
// Get initial values from URL params or use defaults
|
||
const initialMinPrice = searchParams.get("priceMin")
|
||
? parseInt(searchParams.get("priceMin") || "", 10)
|
||
: minimumPrice;
|
||
|
||
const initialMaxPrice = searchParams.get("priceMax")
|
||
? parseInt(searchParams.get("priceMax") || "", 10)
|
||
: maximumPrice;
|
||
|
||
// Check if any filters are different from defaults
|
||
const isFiltersChanged =
|
||
order !== "all" ||
|
||
initialMinPrice !== minimumPrice ||
|
||
initialMaxPrice !== maximumPrice;
|
||
|
||
const [priceRange, setPriceRange] = useState<[number, number]>([
|
||
initialMinPrice,
|
||
initialMaxPrice,
|
||
]);
|
||
|
||
// Check if current values are different from URL values
|
||
const hasChanges =
|
||
order !== (searchParams.get("order") || "all") ||
|
||
priceRange[0] !== initialMinPrice ||
|
||
priceRange[1] !== initialMaxPrice;
|
||
|
||
return (
|
||
<>
|
||
<div
|
||
className="flex items-center justify-center rounded-[10px] h-10 w-10"
|
||
onClick={() => setIsOpen(true)}
|
||
onKeyDown={(e) => {
|
||
if (e.key === "Enter" || e.key === " ") {
|
||
setIsOpen(true);
|
||
}
|
||
}}
|
||
role="button"
|
||
tabIndex={0}
|
||
aria-label="Open filters"
|
||
>
|
||
<img
|
||
src={theme === "dark" ? filterIconDark : filterIcon}
|
||
alt="filter"
|
||
className="w-6 h-6"
|
||
/>
|
||
</div>
|
||
<Drawer open={isOpen} onOpenChange={setIsOpen}>
|
||
<DrawerContent className="w-full pb-6">
|
||
<DrawerHeader className="flex flex-col py-3">
|
||
<div className="flex relative pt-2 text-B12 font-bold items-center justify-center w-full">
|
||
<X
|
||
className="absolute right-0 cursor-pointer"
|
||
size={24}
|
||
onClick={() => setIsOpen(false)}
|
||
/>
|
||
<DrawerTitle className="text-B12 font-bold">
|
||
فیلتر نتایج
|
||
</DrawerTitle>
|
||
</div>
|
||
</DrawerHeader>
|
||
<div className="w-full p-4">
|
||
<div className="flex flex-col gap-4 w-full border-b pb-4">
|
||
<RadioGroup
|
||
className="flex w-full justify-between"
|
||
onValueChange={(value) => setOrder(value)}
|
||
value={order}
|
||
>
|
||
<RadioGroupItem
|
||
circleClass="h-4 w-4"
|
||
className="h-6 w-6"
|
||
value={"all"}
|
||
id="all"
|
||
/>
|
||
<label htmlFor="all" className="text-R16 w-full text-right">
|
||
همه نتایج
|
||
</label>
|
||
</RadioGroup>
|
||
<RadioGroup
|
||
className="flex w-full justify-between"
|
||
onValueChange={(value) => setOrder(value)}
|
||
value={order}
|
||
>
|
||
<RadioGroupItem
|
||
circleClass="h-4 w-4"
|
||
className="h-6 w-6"
|
||
value={"cheap"}
|
||
id="cheap"
|
||
/>
|
||
<label htmlFor="cheap" className="text-R16 w-full text-right">
|
||
ارزان ترین
|
||
</label>
|
||
</RadioGroup>
|
||
<RadioGroup
|
||
className="flex w-full justify-between"
|
||
onValueChange={(value) => setOrder(value)}
|
||
value={order}
|
||
>
|
||
<RadioGroupItem
|
||
circleClass="h-4 w-4"
|
||
className="h-6 w-6"
|
||
value={"expensive"}
|
||
id="expensive"
|
||
/>
|
||
<label
|
||
htmlFor="expensive"
|
||
className="text-R16 w-full text-right"
|
||
>
|
||
گران ترین
|
||
</label>
|
||
</RadioGroup>
|
||
<RadioGroup
|
||
className="flex w-full justify-between"
|
||
onValueChange={(value) => setOrder(value)}
|
||
value={order}
|
||
>
|
||
<RadioGroupItem
|
||
circleClass="h-4 w-4"
|
||
className="h-6 w-6"
|
||
value={"newest"}
|
||
id="newest"
|
||
/>
|
||
<label htmlFor="newest" className="text-R16 w-full text-right">
|
||
جدید ترین
|
||
</label>
|
||
</RadioGroup>
|
||
</div>
|
||
</div>
|
||
<div className="w-full p-4 flex flex-col gap-4">
|
||
<p className="R14">محدوده قیمت :</p>
|
||
<Slider
|
||
dir="rtl"
|
||
defaultValue={[initialMinPrice, initialMaxPrice]}
|
||
value={priceRange}
|
||
min={minimumPrice}
|
||
max={maximumPrice}
|
||
step={10000}
|
||
onValueChange={(value: number[]) =>
|
||
setPriceRange(value as [number, number])
|
||
}
|
||
className="w-full"
|
||
/>
|
||
<div className="flex gap-6 justify-between w-full">
|
||
<div className="flex flex-col gap-1 w-full">
|
||
<p className="R12">کمترین قیمت</p>
|
||
<div className="flex w-full items-center gap-1 h-12 border border-BLACK rounded-[8px]">
|
||
<input
|
||
type="text"
|
||
value={priceRange[0].toLocaleString()}
|
||
onChange={(e) => {
|
||
const value = parseInt(
|
||
e.target.value.replace(/,/g, ""),
|
||
10
|
||
);
|
||
if (!isNaN(value)) {
|
||
setPriceRange([value, priceRange[1]]);
|
||
}
|
||
}}
|
||
className="w-full text-GRAY focus:text-BLACK px-4 h-full flex items-center justify-start bg-transparent outline-none"
|
||
/>
|
||
<div className="text-R12 text-GRAY p-2 border-r border-BLACK h-full flex items-center justify-start">
|
||
تومان
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-col gap-1 w-full">
|
||
<p className="R12">بیشترین قیمت</p>
|
||
<div className="flex w-full items-center gap-1 h-12 border border-BLACK rounded-[8px]">
|
||
<input
|
||
type="text"
|
||
value={priceRange[1].toLocaleString()}
|
||
onChange={(e) => {
|
||
const value = parseInt(
|
||
e.target.value.replace(/,/g, ""),
|
||
10
|
||
);
|
||
if (!isNaN(value)) {
|
||
setPriceRange([priceRange[0], value]);
|
||
}
|
||
}}
|
||
className="w-full text-GRAY focus:text-BLACK px-4 h-full flex items-center justify-start bg-transparent outline-none"
|
||
/>
|
||
<div className="text-R12 text-GRAY p-2 border-r border-BLACK h-full flex items-center justify-start">
|
||
تومان
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-row gap-6 w-full px-4 pt-6 pb-2">
|
||
<Button
|
||
disabled={!hasChanges}
|
||
className="w-[100%]"
|
||
size={"xl"}
|
||
variant="dark"
|
||
onClick={() => {
|
||
const params = new URLSearchParams(searchParams);
|
||
|
||
if (order !== "all") {
|
||
params.set("order", order);
|
||
} else {
|
||
params.delete("order");
|
||
}
|
||
|
||
if (priceRange[0] !== minimumPrice) {
|
||
params.set("priceMin", priceRange[0].toString());
|
||
} else {
|
||
params.delete("priceMin");
|
||
}
|
||
|
||
if (priceRange[1] !== maximumPrice) {
|
||
params.set("priceMax", priceRange[1].toString());
|
||
} else {
|
||
params.delete("priceMax");
|
||
}
|
||
|
||
setSearchParams(params, { replace: true });
|
||
setIsOpen(false);
|
||
}}
|
||
>
|
||
مشاهده نتایج
|
||
</Button>
|
||
<Button
|
||
disabled={!isFiltersChanged}
|
||
className="w-[100%]"
|
||
size={"xl"}
|
||
variant="primary"
|
||
onClick={() => {
|
||
// Reset all filter values
|
||
setOrder("all");
|
||
setPriceRange([minimumPrice, maximumPrice]);
|
||
|
||
// Keep search query but clear filter params
|
||
const params = new URLSearchParams();
|
||
const searchQuery = searchParams.get("q");
|
||
if (searchQuery) {
|
||
params.set("q", searchQuery);
|
||
}
|
||
setSearchParams(params, { replace: true });
|
||
setIsOpen(false);
|
||
}}
|
||
>
|
||
حذف فیلترها
|
||
</Button>
|
||
</div>
|
||
</DrawerContent>
|
||
</Drawer>
|
||
</>
|
||
);
|
||
};
|
||
|
||
export default FilterDrawer;
|