From 538f3507657f3e066f089e9982cdb9bd529c6cde Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:24:21 +0000 Subject: [PATCH 1/3] Initial plan From 4f6b6283782b1a102a8a5da4fb5d2daa9f82de52 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:54:29 +0000 Subject: [PATCH 2/3] Implement search and filter functionality for tasks page Co-authored-by: BitoviAI <226138784+BitoviAI@users.noreply.github.com> --- app/(dashboard)/tasks/page.tsx | 7 +- components/tasks-with-search.tsx | 143 +++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 components/tasks-with-search.tsx diff --git a/app/(dashboard)/tasks/page.tsx b/app/(dashboard)/tasks/page.tsx index a8a0ca2..9aa474e 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 { TasksWithSearch } from "@/components/tasks-with-search" 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.tsx b/components/tasks-with-search.tsx new file mode 100644 index 0000000..060ca74 --- /dev/null +++ b/components/tasks-with-search.tsx @@ -0,0 +1,143 @@ +"use client" + +import { useState, useMemo } from "react" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import { TaskList } from "@/components/task-list" +import { Search, X, Filter } from "lucide-react" +import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuLabel } from "@/components/ui/dropdown-menu" +import type { Task as PrismaTask, User } from "@/app/generated/prisma/client" + +type TaskWithProfile = PrismaTask & { + assignee?: Pick | null +} + +interface TasksWithSearchProps { + initialTasks: TaskWithProfile[] +} + +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 TasksWithSearch({ initialTasks }: TasksWithSearchProps) { + const [searchQuery, setSearchQuery] = useState("") + const [selectedStatuses, setSelectedStatuses] = useState( + STATUS_OPTIONS.map(opt => opt.value) + ) + const [selectedPriorities, setSelectedPriorities] = useState( + PRIORITY_OPTIONS.map(opt => opt.value) + ) + + const handleClearSearch = () => { + setSearchQuery("") + } + + 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] + ) + } + + const filteredTasks = useMemo(() => { + return initialTasks.filter(task => { + // Apply search filter + const matchesSearch = searchQuery.trim() === "" || + task.name.toLowerCase().includes(searchQuery.toLowerCase()) || + task.description.toLowerCase().includes(searchQuery.toLowerCase()) + + // Apply status filter + const matchesStatus = selectedStatuses.includes(task.status) + + // Apply priority filter + const matchesPriority = selectedPriorities.includes(task.priority) + + return matchesSearch && matchesStatus && matchesPriority + }) + }, [initialTasks, searchQuery, selectedStatuses, selectedPriorities]) + + return ( +
+
+
+ + setSearchQuery(e.target.value)} + className="pl-10 pr-10" + /> + {searchQuery && ( + + )} +
+ + + + + + Status + {STATUS_OPTIONS.map(option => ( + toggleStatus(option.value)} + > + {option.label} + + ))} + + Priority + {PRIORITY_OPTIONS.map(option => ( + togglePriority(option.value)} + > + {option.label} + + ))} + + +
+ + {filteredTasks.length === 0 ? ( +
+

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

+
+ ) : ( + + )} +
+ ) +} From 547eee9c759309c670e81527fdb3013ae32008d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:56:46 +0000 Subject: [PATCH 3/3] Add accessibility improvements to search and filter controls Co-authored-by: BitoviAI <226138784+BitoviAI@users.noreply.github.com> --- components/tasks-with-search.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/tasks-with-search.tsx b/components/tasks-with-search.tsx index 060ca74..df0f1fe 100644 --- a/components/tasks-with-search.tsx +++ b/components/tasks-with-search.tsx @@ -63,7 +63,7 @@ export function TasksWithSearch({ initialTasks }: TasksWithSearchProps) { // Apply search filter const matchesSearch = searchQuery.trim() === "" || task.name.toLowerCase().includes(searchQuery.toLowerCase()) || - task.description.toLowerCase().includes(searchQuery.toLowerCase()) + (task.description || "").toLowerCase().includes(searchQuery.toLowerCase()) // Apply status filter const matchesStatus = selectedStatuses.includes(task.status) @@ -92,6 +92,7 @@ export function TasksWithSearch({ initialTasks }: TasksWithSearchProps) { size="icon" className="absolute right-1 top-1/2 h-7 w-7 -translate-y-1/2" onClick={handleClearSearch} + aria-label="Clear search" > @@ -99,7 +100,7 @@ export function TasksWithSearch({ initialTasks }: TasksWithSearchProps) { -