'use client'; import { useEffect, useState, useMemo, useCallback, Suspense } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { Search, Calendar, ListTodo, BookOpen, FolderKanban, Hash, ExternalLink, Clock, Filter, X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; interface SearchResult { id: string; type: 'task' | 'note' | 'project' | 'habit' | 'domain'; title: string; snippet: string; score: number; workspaceId: string; link: string; } const typeIcons: Record = { task: , note: , project: , habit: , domain: , }; const typeColors: Record = { task: 'bg-blue-500/10 text-blue-600', note: 'bg-green-500/10 text-green-600', project: 'bg-purple-500/10 text-purple-600', habit: 'bg-orange-500/10 text-orange-600', domain: 'bg-gray-500/10 text-gray-600', }; export default function SearchPage() { return (
Loading search...
}>
); } function SearchPageContent() { const router = useRouter(); const searchParams = useSearchParams(); const initialQuery = searchParams.get('q') || ''; const [query, setQuery] = useState(initialQuery); const [results, setResults] = useState([]); const [totalCount, setTotalCount] = useState(0); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [selectedTypes, setSelectedTypes] = useState(['task', 'note', 'project', 'habit', 'domain']); const [selectedDomain, setSelectedDomain] = useState('all'); const [recentSearches, setRecentSearches] = useState([]); // Load recent searches from localStorage useEffect(() => { try { const stored = localStorage.getItem('project-e-recent-searches'); if (stored) setRecentSearches(JSON.parse(stored)); } catch {} }, []); const saveRecentSearch = useCallback((q: string) => { const updated = [q, ...recentSearches.filter(s => s !== q)].slice(0, 10); setRecentSearches(updated); try { localStorage.setItem('project-e-recent-searches', JSON.stringify(updated)); } catch {} }, [recentSearches]); const doSearch = useCallback(async (q: string) => { if (!q.trim()) { setResults([]); setTotalCount(0); return; } setLoading(true); setError(null); try { const params = new URLSearchParams({ q }); if (selectedTypes.length < 5) params.set('types', selectedTypes.join(',')); if (selectedDomain !== 'all') params.set('domain', selectedDomain); const res = await fetch(`/api/search?${params}`); if (!res.ok) throw new Error('Search failed'); const data = await res.json(); setResults(data.results || []); setTotalCount(data.totalCount || 0); saveRecentSearch(q); } catch (err) { setError('Search failed. Please try again.'); console.error('Search error:', err); } finally { setLoading(false); } }, [selectedTypes, selectedDomain, saveRecentSearch]); // Initial search from URL param useEffect(() => { if (initialQuery) doSearch(initialQuery); }, []); // eslint-disable-line react-hooks/exhaustive-deps const handleSearch = (e: React.FormEvent) => { e.preventDefault(); doSearch(query); router.replace(`/search?q=${encodeURIComponent(query)}`); }; const groupedResults = useMemo(() => { const groups: Record = { task: [], note: [], project: [], habit: [], domain: [], }; for (const r of results) { if (groups[r.type]) groups[r.type].push(r); } return Object.entries(groups).filter(([, items]) => items.length > 0); }, [results]); const toggleType = (type: string) => { setSelectedTypes(prev => prev.includes(type) ? prev.filter(t => t !== type) : [...prev, type] ); }; return (

Search

Find anything across your workspace.

{/* Search bar */}
setQuery(e.target.value)} placeholder="Search tasks, notes, projects, habits..." className="pl-10 pr-20" autoFocus />
{/* Filters */}
Filter:
{['task', 'note', 'project', 'habit', 'domain'].map(type => ( toggleType(type)} > {typeIcons[type]} {type}s ))}
{/* Results */} {error && ( {error} )} {!loading && !error && query && results.length === 0 && (

No results found

Try different keywords or adjust your filters.

)} {!query && !loading && (

Search your workspace

Type a query above to search across tasks, notes, projects, habits, and domains.

{recentSearches.length > 0 && (

Recent searches

{recentSearches.map((s, i) => ( { setQuery(s); doSearch(s); }} > {s} ))}
)}
)} {loading && (
{Array.from({ length: 3 }).map((_, i) => (
))}
)} {!loading && results.length > 0 && (

Found {totalCount} result{totalCount !== 1 ? 's' : ''} for “{query}”

{groupedResults.map(([type, items]) => (

{typeIcons[type]} {type}s {items.length}

{items.map((result) => ( router.push(result.link)} >
{typeIcons[result.type]}
{result.title} {result.type}
{result.snippet && (

)}

))}
))}
)}
); }