- Add database indexes for frequently queried fields (listId, completed, priority, dueDate, createdAt) - Add compound indexes for common query patterns (listId+completed, listId+priority) - Implement pagination support in getItemsByFilter with configurable page size (default 50) - Optimize components with React.memo (ItemCard, ListCard, FilterPanel, SearchBar, SearchResults, EmptyState) - Configure Next.js for better bundle optimization: - Enable optimizePackageImports for lucide-react - Enable SWC minification - Remove console logs in production (except errors/warnings) - Optimize images with AVIF/WebP formats - Enable compression - Disable production source maps for smaller bundles - Loading states already implemented to prevent layout shift Task 18 complete - all performance optimizations implemented
59 lines
1.7 KiB
TypeScript
59 lines
1.7 KiB
TypeScript
'use client'
|
|
|
|
import { memo } from 'react'
|
|
import { Search, X } from 'lucide-react'
|
|
import { useSearch } from '@/hooks'
|
|
|
|
interface SearchBarProps {
|
|
onSearch: (query: string) => void
|
|
placeholder?: string
|
|
debounceMs?: number
|
|
initialValue?: string
|
|
}
|
|
|
|
function SearchBar({
|
|
onSearch,
|
|
placeholder = 'Search tasks...',
|
|
debounceMs = 300,
|
|
initialValue = ''
|
|
}: SearchBarProps) {
|
|
const { query, setQuery, clearSearch, isDebouncing } = useSearch({
|
|
debounceMs,
|
|
onSearch
|
|
})
|
|
|
|
return (
|
|
<div className="relative">
|
|
<div className="relative">
|
|
<Search
|
|
className="absolute left-3 sm:left-4 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none"
|
|
size={20}
|
|
/>
|
|
<input
|
|
type="text"
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
placeholder={placeholder}
|
|
className="w-full pl-10 sm:pl-12 pr-10 sm:pr-12 py-3 sm:py-3.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm sm:text-base transition-shadow"
|
|
/>
|
|
{query && (
|
|
<button
|
|
onClick={clearSearch}
|
|
className="absolute right-3 sm:right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 active:text-gray-700 transition-colors tap-target-sm"
|
|
aria-label="Clear search"
|
|
>
|
|
<X size={20} />
|
|
</button>
|
|
)}
|
|
{isDebouncing && (
|
|
<div className="absolute right-12 sm:right-14 top-1/2 -translate-y-1/2">
|
|
<div className="w-4 h-4 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default memo(SearchBar)
|