"use client";
import { useState } from "react";
import {
Filter,
ArrowUpDown,
ArrowUp,
ArrowDown,
X,
Check,
RotateCcw,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { Separator } from "@/components/ui/separator";
type SortDirection = "asc" | "desc";
type Filters = {
search: string;
status: string;
category: string;
dateFrom: string;
dateTo: string;
minAmount: string;
maxAmount: string;
};
type SortConfig = {
field: string;
direction: SortDirection;
};
const initialFilters: Filters = {
search: "",
status: "all",
category: "all",
dateFrom: "",
dateTo: "",
minAmount: "",
maxAmount: "",
};
const initialSort: SortConfig = {
field: "created",
direction: "desc",
};
const statusOptions = [
{ value: "all", label: "Any status" },
{ value: "active", label: "Active" },
{ value: "pending", label: "Pending" },
{ value: "completed", label: "Completed" },
{ value: "cancelled", label: "Cancelled" },
];
const categoryOptions = [
{ value: "all", label: "Any category" },
{ value: "sales", label: "Sales" },
{ value: "marketing", label: "Marketing" },
{ value: "support", label: "Support" },
{ value: "engineering", label: "Engineering" },
];
const sortFields = [
{ value: "created", label: "Date Created" },
{ value: "updated", label: "Date Updated" },
{ value: "name", label: "Name" },
{ value: "amount", label: "Amount" },
{ value: "status", label: "Status" },
];
export const title = "React Dialog Block Filter Sort";
export default function DialogFilterSort() {
const [open, setOpen] = useState(false);
const [filters, setFilters] = useState<Filters>(initialFilters);
const [sort, setSort] = useState<SortConfig>(initialSort);
const [appliedFilters, setAppliedFilters] = useState<Filters>(initialFilters);
const [appliedSort, setAppliedSort] = useState<SortConfig>(initialSort);
const activeFilterCount = Object.entries(appliedFilters).filter(
([key, value]) => value && value !== "all"
).length;
const handleFilterChange = (key: keyof Filters, value: string) => {
setFilters((prev) => ({ ...prev, [key]: value }));
};
const handleSortFieldChange = (field: string) => {
setSort((prev) => ({ ...prev, field }));
};
const handleSortDirectionToggle = () => {
setSort((prev) => ({
...prev,
direction: prev.direction === "asc" ? "desc" : "asc",
}));
};
const handleApply = () => {
setAppliedFilters(filters);
setAppliedSort(sort);
setOpen(false);
};
const handleClearAll = () => {
setFilters(initialFilters);
setSort(initialSort);
};
const handleReset = () => {
setFilters(appliedFilters);
setSort(appliedSort);
};
const hasChanges =
JSON.stringify(filters) !== JSON.stringify(appliedFilters) ||
JSON.stringify(sort) !== JSON.stringify(appliedSort);
return (
<Dialog open={open} onOpenChange={setOpen}>
<div className="flex min-h-[350px] items-center justify-center">
<DialogTrigger asChild>
<Button variant="outline">
<Filter className="mr-2 h-4 w-4" />
Filter & Sort
{activeFilterCount > 0 && (
<Badge variant="secondary" className="ml-2">
{activeFilterCount}
</Badge>
)}
</Button>
</DialogTrigger>
</div>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Filter className="h-5 w-5" />
Filter & Sort
</div>
{(Object.values(filters).some(Boolean) || sort.field !== "created") && (
<Button
variant="ghost"
size="sm"
onClick={handleClearAll}
className="h-8 text-xs"
>
<RotateCcw className="mr-1 h-3 w-3" />
Clear All
</Button>
)}
</DialogTitle>
<DialogDescription>
Refine your results with filters and sorting.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-3">
<Label className="text-sm font-medium">Search</Label>
<Input
placeholder="Search by name or keyword..."
value={filters.search}
onChange={(e) => handleFilterChange("search", e.target.value)}
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-2">
<Label className="text-sm">Status</Label>
<Select
value={filters.status}
onValueChange={(v) => handleFilterChange("status", v)}
>
<SelectTrigger>
<SelectValue placeholder="Any status" />
</SelectTrigger>
<SelectContent>
{statusOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label className="text-sm">Category</Label>
<Select
value={filters.category}
onValueChange={(v) => handleFilterChange("category", v)}
>
<SelectTrigger>
<SelectValue placeholder="Any category" />
</SelectTrigger>
<SelectContent>
{categoryOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<Label className="text-sm">Date Range</Label>
<div className="grid grid-cols-2 gap-3">
<Input
type="date"
value={filters.dateFrom}
onChange={(e) => handleFilterChange("dateFrom", e.target.value)}
placeholder="From"
/>
<Input
type="date"
value={filters.dateTo}
onChange={(e) => handleFilterChange("dateTo", e.target.value)}
placeholder="To"
/>
</div>
</div>
<div className="space-y-2">
<Label className="text-sm">Amount Range</Label>
<div className="grid grid-cols-2 gap-3">
<Input
type="number"
placeholder="Min"
value={filters.minAmount}
onChange={(e) => handleFilterChange("minAmount", e.target.value)}
/>
<Input
type="number"
placeholder="Max"
value={filters.maxAmount}
onChange={(e) => handleFilterChange("maxAmount", e.target.value)}
/>
</div>
</div>
<Separator />
<div className="space-y-2">
<Label className="text-sm font-medium">Sort By</Label>
<div className="flex gap-2">
<Select value={sort.field} onValueChange={handleSortFieldChange}>
<SelectTrigger className="flex-1">
<SelectValue />
</SelectTrigger>
<SelectContent>
{sortFields.map((field) => (
<SelectItem key={field.value} value={field.value}>
{field.label}
</SelectItem>
))}
</SelectContent>
</Select>
<Button
variant="outline"
size="icon"
onClick={handleSortDirectionToggle}
title={sort.direction === "asc" ? "Ascending" : "Descending"}
>
{sort.direction === "asc" ? (
<ArrowUp className="h-4 w-4" />
) : (
<ArrowDown className="h-4 w-4" />
)}
</Button>
</div>
<p className="text-xs text-muted-foreground">
{sort.direction === "asc" ? "Oldest first" : "Newest first"}
{sort.field === "name" && (sort.direction === "asc" ? " (A-Z)" : " (Z-A)")}
{sort.field === "amount" && (sort.direction === "asc" ? " (Low to High)" : " (High to Low)")}
</p>
</div>
</div>
<DialogFooter className="gap-2 sm:gap-0">
<Button variant="outline" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button onClick={handleApply}>
<Check className="mr-2 h-4 w-4" />
Apply Filters
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}