# TASK-FE-008: Search & Global Filters UI **ID:** TASK-FE-008 **Title:** Global Search, Advanced Filters & Results UI **Category:** Supporting Features **Priority:** P2 (Medium) **Effort:** 3-4 days **Dependencies:** TASK-FE-003, TASK-BE-010 **Assigned To:** Frontend Developer --- ## 📋 Overview Implement global search functionality with advanced filters, faceted search, and unified results display across all document types. --- ## 🎯 Objectives 1. Create global search bar in header 2. Build advanced search page with filters 3. Implement faceted search (by type, status, date) 4. Create unified results display 5. Add search suggestions/autocomplete 6. Implement search history --- ## ✅ Acceptance Criteria - [ ] Global search accessible from header - [ ] Advanced filters work (type, status, date range, organization) - [ ] Results show across all document types - [ ] Search suggestions appear as user types - [ ] Search history saved locally - [ ] Results paginated with highlighting --- ## 🔧 Implementation Steps ### Step 1: Global Search Component in Header ```typescript // File: src/components/layout/global-search.tsx 'use client'; import { useState, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { Search, X } from 'lucide-react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; import { useDebounce } from '@/hooks/use-debounce'; import { searchApi } from '@/lib/api/search'; export function GlobalSearch() { const router = useRouter(); const [open, setOpen] = useState(false); const [query, setQuery] = useState(''); const [suggestions, setSuggestions] = useState([]); const debouncedQuery = useDebounce(query, 300); // Fetch suggestions useEffect(() => { if (debouncedQuery.length > 2) { searchApi.suggest(debouncedQuery).then(setSuggestions); } }, [debouncedQuery]); const handleSearch = () => { if (query.trim()) { router.push(`/search?q=${encodeURIComponent(query)}`); setOpen(false); } }; return (
setQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} className="pl-9" />
{suggestions.length === 0 ? ( No results found ) : ( {suggestions.map((item: any) => ( { setQuery(item.title); router.push(`/${item.type}s/${item.id}`); setOpen(false); }} >
{item.type} {item.title}
))}
)}
); } ``` ### Step 2: Advanced Search Page ```typescript // File: src/app/(dashboard)/search/page.tsx 'use client'; import { useState, useEffect } from 'react'; import { useSearchParams } from 'next/navigation'; import { SearchFilters } from '@/components/search/filters'; import { SearchResults } from '@/components/search/results'; import { searchApi } from '@/lib/api/search'; export default function SearchPage() { const searchParams = useSearchParams(); const query = searchParams.get('q') || ''; const [results, setResults] = useState([]); const [filters, setFilters] = useState({}); const [loading, setLoading] = useState(false); useEffect(() => { if (query) { setLoading(true); searchApi .search({ query, ...filters }) .then(setResults) .finally(() => setLoading(false)); } }, [query, filters]); return (

Search Results

Found {results.length} results for "{query}"

); } ``` ### Step 3: Search Filters Component ```typescript // File: src/components/search/filters.tsx 'use client'; import { Card } from '@/components/ui/card'; import { Label } from '@/components/ui/label'; import { Checkbox } from '@/components/ui/checkbox'; import { Button } from '@/components/ui/button'; import { Calendar } from '@/components/ui/calendar'; export function SearchFilters({ onFilterChange, }: { onFilterChange: (filters: any) => void; }) { const [filters, setFilters] = useState({ types: [], statuses: [], dateFrom: null, dateTo: null, }); const handleFilterChange = (key: string, value: any) => { const newFilters = { ...filters, [key]: value }; setFilters(newFilters); onFilterChange(newFilters); }; return (

Document Type

{['Correspondence', 'RFA', 'Drawing', 'Transmittal'].map((type) => ( ))}

Status

{['Draft', 'Pending', 'Approved', 'Rejected'].map((status) => ( ))}

Date Range

); } ``` ### Step 4: Search Results Component ```typescript // File: src/components/search/results.tsx import { Card } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import Link from 'next/link'; import { FileText, Clipboard, Image } from 'lucide-react'; export function SearchResults({ results, query, loading }: any) { if (loading) { return
Loading...
; } if (results.length === 0) { return ( No results found for "{query}" ); } const getIcon = (type: string) => { switch (type) { case 'correspondence': return FileText; case 'rfa': return Clipboard; case 'drawing': return Image; default: return FileText; } }; return (
{results.map((result: any) => { const Icon = getIcon(result.type); return (

{result.title}

{result.type} {result.status}

{result.highlight || result.description}

{result.documentNumber} • {new Date(result.createdAt).toLocaleDateString()}
); })}
); } ``` --- ## 📦 Deliverables - [ ] Global search component in header - [ ] Advanced search page - [ ] Filters panel (type, status, date) - [ ] Results display with highlighting - [ ] Search suggestions/autocomplete - [ ] Mobile responsive design --- ## 🔗 Related Documents - [TASK-BE-010: Search & Elasticsearch](./TASK-BE-010-search-elasticsearch.md) --- **Created:** 2025-12-01 **Status:** Ready