import { useState, useRef, useEffect, useCallback } from 'react' import { Search } from 'lucide-react' import { useTasks } from '../api/tasks.ts' import { CATEGORY_COLORS, COLUMN_CONFIG } from '../lib/constants.ts' import type { WorkTask } from '../types/index.ts' interface SearchBarProps { onSelect: (taskId: number) => void } export default function SearchBar({ onSelect }: SearchBarProps) { const { data: tasks } = useTasks() const [query, setQuery] = useState('') const [debouncedQuery, setDebouncedQuery] = useState('') const [isOpen, setIsOpen] = useState(false) const [selectedIndex, setSelectedIndex] = useState(0) const containerRef = useRef(null) const inputRef = useRef(null) const timerRef = useRef | null>(null) // Debounce the query by 200ms useEffect(() => { if (timerRef.current) clearTimeout(timerRef.current) timerRef.current = setTimeout(() => { setDebouncedQuery(query) }, 200) return () => { if (timerRef.current) clearTimeout(timerRef.current) } }, [query]) // Filter tasks based on debounced query const results: WorkTask[] = (() => { if (!debouncedQuery.trim() || !tasks) return [] const q = debouncedQuery.toLowerCase() return tasks .filter( (t) => t.title.toLowerCase().includes(q) || (t.description && t.description.toLowerCase().includes(q)) ) .slice(0, 8) })() // Open/close dropdown based on results useEffect(() => { setIsOpen(results.length > 0) setSelectedIndex(0) }, [results.length]) // Close dropdown when clicking outside useEffect(() => { function handleClickOutside(e: MouseEvent) { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setIsOpen(false) } } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, []) const handleSelect = useCallback( (taskId: number) => { onSelect(taskId) setQuery('') setDebouncedQuery('') setIsOpen(false) inputRef.current?.blur() }, [onSelect] ) const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (!isOpen) return switch (e.key) { case 'ArrowDown': e.preventDefault() setSelectedIndex((prev) => Math.min(prev + 1, results.length - 1)) break case 'ArrowUp': e.preventDefault() setSelectedIndex((prev) => Math.max(prev - 1, 0)) break case 'Enter': e.preventDefault() if (results[selectedIndex]) { handleSelect(results[selectedIndex].id) } break case 'Escape': e.preventDefault() setIsOpen(false) inputRef.current?.blur() break } }, [isOpen, results, selectedIndex, handleSelect] ) const getStatusLabel = (status: number) => { const col = COLUMN_CONFIG.find((c) => c.status === status) return col ? col.label : 'Unknown' } const getStatusColor = (status: number) => { const col = COLUMN_CONFIG.find((c) => c.status === status) return col ? col.color : '#64748b' } return (
setQuery(e.target.value)} onKeyDown={handleKeyDown} onFocus={() => { if (results.length > 0) setIsOpen(true) }} placeholder="Search tasks..." className="w-full h-8 pl-9 pr-3 rounded-full bg-[#1a1d27] text-white text-sm placeholder-[#94a3b8] border border-white/5 focus:border-indigo-500/50 focus:outline-none transition-colors" />
{isOpen && results.length > 0 && (
{results.map((task, index) => { const categoryColor = CATEGORY_COLORS[task.category ?? 'Unknown'] ?? CATEGORY_COLORS['Unknown'] const statusColor = getStatusColor(task.status) const statusLabel = getStatusLabel(task.status) return ( ) })}
)}
) }