diff --git a/app/(dashboard)/tasks/page.tsx b/app/(dashboard)/tasks/page.tsx index a8a0ca2..2d846da 100644 --- a/app/(dashboard)/tasks/page.tsx +++ b/app/(dashboard)/tasks/page.tsx @@ -1,9 +1,8 @@ import { Suspense } from "react" import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Plus, Search } from "lucide-react" +import { Plus } from "lucide-react" import Link from "next/link" -import { TaskList } from "@/components/task-list" +import { TasksWithSearchFilter } from "@/components/tasks-with-search-filter" import { poppins } from "@/lib/fonts" import { getAllTasks } from "@/app/(dashboard)/tasks/actions" @@ -31,7 +30,7 @@ export default async function TasksPage() { Loading tasks...}> - + ) diff --git a/components/tasks-with-search-filter.tsx b/components/tasks-with-search-filter.tsx new file mode 100644 index 0000000..5643514 --- /dev/null +++ b/components/tasks-with-search-filter.tsx @@ -0,0 +1,151 @@ +"use client" + +import { useState, useMemo } from "react" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuLabel } from "@/components/ui/dropdown-menu" +import { Search, X, Filter } from "lucide-react" +import { TaskList } from "@/components/task-list" +import type { Task as PrismaTask, User } from "@/app/generated/prisma/client" + +type TaskWithProfile = PrismaTask & { + assignee?: Pick | null; +}; + +type TasksWithSearchFilterProps = { + initialTasks: TaskWithProfile[] +} + +// Status mapping for filtering +const STATUS_OPTIONS = [ + { value: "todo", label: "Todo" }, + { value: "in_progress", label: "In progress" }, + { value: "review", label: "Review" }, + { value: "done", label: "Done" }, +] + +const PRIORITY_OPTIONS = [ + { value: "high", label: "High" }, + { value: "medium", label: "Medium" }, + { value: "low", label: "Low" }, +] + +export function TasksWithSearchFilter({ initialTasks }: TasksWithSearchFilterProps) { + const [searchText, setSearchText] = useState("") + const [selectedStatuses, setSelectedStatuses] = useState( + STATUS_OPTIONS.map(s => s.value) + ) + const [selectedPriorities, setSelectedPriorities] = useState( + PRIORITY_OPTIONS.map(p => p.value) + ) + + const handleClearSearch = () => { + setSearchText("") + } + + const toggleStatus = (status: string) => { + setSelectedStatuses(prev => + prev.includes(status) + ? prev.filter(s => s !== status) + : [...prev, status] + ) + } + + const togglePriority = (priority: string) => { + setSelectedPriorities(prev => + prev.includes(priority) + ? prev.filter(p => p !== priority) + : [...prev, priority] + ) + } + + // Filter tasks based on search text and selected filters + const filteredTasks = useMemo(() => { + return initialTasks.filter(task => { + // Filter by search text (search in name and description) + const searchLower = searchText.toLowerCase() + const matchesSearch = !searchText || + task.name.toLowerCase().includes(searchLower) || + task.description.toLowerCase().includes(searchLower) + + // Filter by status + const matchesStatus = selectedStatuses.includes(task.status) + + // Filter by priority + const matchesPriority = selectedPriorities.includes(task.priority) + + return matchesSearch && matchesStatus && matchesPriority + }) + }, [initialTasks, searchText, selectedStatuses, selectedPriorities]) + + return ( +
+ {/* Search and Filter Bar */} +
+ {/* Search Input */} +
+ + setSearchText(e.target.value)} + className="pl-9 pr-9" + /> + {searchText && ( + + )} +
+ + {/* Filter Dropdown */} + + + + + + Status + {STATUS_OPTIONS.map(status => ( + toggleStatus(status.value)} + > + {status.label} + + ))} + + Priority + {PRIORITY_OPTIONS.map(priority => ( + togglePriority(priority.value)} + > + {priority.label} + + ))} + + +
+ + {/* Task List or No Results Message */} + {filteredTasks.length > 0 ? ( + + ) : ( +
+

+ No tasks match the current search and filter criteria. +

+
+ )} +
+ ) +}