Vitron-Front/app/components/FilterDrawer.tsx
2026-04-29 01:44:16 +03:30

277 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;