import { useState, useEffect, useRef } from 'react' import { Search, X, Flag } from 'lucide-react' import { searchTasks, getProjects } from '../utils/api' import { formatTime } from '../utils/format' import { useNavigate } from 'react-router-dom' const FLAG_COLORS = { red: 'text-red-500', orange: 'text-orange-500', yellow: 'text-yellow-500', green: 'text-green-500', blue: 'text-blue-500', purple: 'text-purple-500', pink: 'text-pink-500' } function SearchBar() { const [query, setQuery] = useState('') const [results, setResults] = useState([]) const [projects, setProjects] = useState([]) const [selectedProjects, setSelectedProjects] = useState([]) const [isSearching, setIsSearching] = useState(false) const [showResults, setShowResults] = useState(false) const [showProjectFilter, setShowProjectFilter] = useState(false) const searchRef = useRef(null) const navigate = useNavigate() useEffect(() => { loadProjects() }, []) useEffect(() => { function handleClickOutside(event) { if (searchRef.current && !searchRef.current.contains(event.target)) { setShowResults(false) setShowProjectFilter(false) } } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, []) const loadProjects = async () => { try { const data = await getProjects() setProjects(data) } catch (err) { console.error('Failed to load projects:', err) } } const handleSearch = async (searchQuery) => { if (!searchQuery.trim()) { setResults([]) setShowResults(false) return } setIsSearching(true) try { const projectIds = selectedProjects.length > 0 ? selectedProjects : null const data = await searchTasks(searchQuery, projectIds) setResults(data) setShowResults(true) } catch (err) { console.error('Search failed:', err) setResults([]) } finally { setIsSearching(false) } } const handleQueryChange = (e) => { const newQuery = e.target.value setQuery(newQuery) } // Debounced search effect useEffect(() => { if (!query.trim()) { setResults([]) setShowResults(false) return } const debounceMs = parseInt(import.meta.env.VITE_SEARCH_DEBOUNCE_MS || '300') const timeoutId = setTimeout(() => { handleSearch(query) }, debounceMs) return () => clearTimeout(timeoutId) }, [query, selectedProjects]) const toggleProjectFilter = (projectId) => { setSelectedProjects(prev => { if (prev.includes(projectId)) { return prev.filter(id => id !== projectId) } else { return [...prev, projectId] } }) } const handleTaskClick = (task) => { navigate(`/project/${task.project_id}`) setShowResults(false) setQuery('') } const clearSearch = () => { setQuery('') setResults([]) setShowResults(false) } return (
{/* Search Input */}
query && setShowResults(true)} placeholder="Search tasks..." className="w-64 pl-9 pr-8 py-2 bg-cyber-darker border border-cyber-orange/30 rounded text-gray-100 text-sm focus:outline-none focus:border-cyber-orange placeholder-gray-500" /> {query && ( )}
{/* Project Filter Button */} {projects.length > 1 && ( )}
{/* Project Filter Dropdown */} {showProjectFilter && (
Filter by projects:
{projects.map(project => ( ))} {selectedProjects.length > 0 && ( )}
)} {/* Search Results */} {showResults && (
{isSearching ? (
Searching...
) : results.length === 0 ? (
No results found
) : (
{results.length} result{results.length !== 1 ? 's' : ''}
{results.map(task => { const project = projects.find(p => p.id === task.project_id) return ( ) })}
)}
)}
) } export default SearchBar