feat: Add search and filter UI components
- Created SearchBar component with debounced input - Built FilterPanel component with controls for priority, completion, tags, and due date - Added sort controls (field and direction) - Implemented filter clear functionality - Display search results with highlighted text - Added GlobalSearch component for home page - Integrated search and filter into list detail view - Added CSS styles for search highlighting Requirements: 6.1, 6.2, 6.3, 6.4, 8.1, 8.2, 8.3
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind utilities;
|
||||
|
||||
/* Search highlight styles */
|
||||
mark {
|
||||
background-color: #fef08a;
|
||||
color: inherit;
|
||||
padding: 0 2px;
|
||||
border-radius: 2px;
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { getListWithItems } from '@/lib/actions'
|
||||
import { notFound } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { ArrowLeft, Plus } from 'lucide-react'
|
||||
import ItemCard from '@/components/ItemCard'
|
||||
import { ArrowLeft } from 'lucide-react'
|
||||
import InlineItemCreate from '@/components/InlineItemCreate'
|
||||
import ListDetailView from '@/components/ListDetailView'
|
||||
|
||||
export default async function ListDetailPage({ params }: { params: { id: string } }) {
|
||||
const list = await getListWithItems(params.id)
|
||||
@@ -13,7 +13,6 @@ export default async function ListDetailPage({ params }: { params: { id: string
|
||||
}
|
||||
|
||||
const completedItems = list.items.filter(item => item.completed)
|
||||
const incompleteItems = list.items.filter(item => !item.completed)
|
||||
|
||||
return (
|
||||
<main className="container mx-auto px-4 py-8 max-w-4xl">
|
||||
@@ -45,44 +44,8 @@ export default async function ListDetailPage({ params }: { params: { id: string
|
||||
<InlineItemCreate listId={list.id} />
|
||||
</div>
|
||||
|
||||
{/* Items List */}
|
||||
<div className="space-y-6">
|
||||
{/* Incomplete Items */}
|
||||
{incompleteItems.length > 0 && (
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-3">
|
||||
To Do ({incompleteItems.length})
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
{incompleteItems.map((item) => (
|
||||
<ItemCard key={item.id} item={item} listId={list.id} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Completed Items */}
|
||||
{completedItems.length > 0 && (
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-500 mb-3">
|
||||
Completed ({completedItems.length})
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
{completedItems.map((item) => (
|
||||
<ItemCard key={item.id} item={item} listId={list.id} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Empty State */}
|
||||
{list.items.length === 0 && (
|
||||
<div className="text-center py-12 bg-gray-50 rounded-lg">
|
||||
<p className="text-gray-500 text-lg mb-2">No tasks yet</p>
|
||||
<p className="text-gray-400">Add your first task to get started</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Search, Filter, and Items List */}
|
||||
<ListDetailView listId={list.id} initialItems={list.items} />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getLists } from '@/lib/actions'
|
||||
import ListCard from '@/components/ListCard'
|
||||
import NewListButton from '@/components/NewListButton'
|
||||
import GlobalSearch from '@/components/GlobalSearch'
|
||||
|
||||
export default async function Home() {
|
||||
const lists = await getLists()
|
||||
@@ -12,6 +13,11 @@ export default async function Home() {
|
||||
<NewListButton />
|
||||
</div>
|
||||
|
||||
{/* Global Search */}
|
||||
<div className="mb-8">
|
||||
<GlobalSearch />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{lists.map((list) => (
|
||||
<ListCard key={list.id} list={list} />
|
||||
|
||||
Reference in New Issue
Block a user