import { useState, useMemo, useRef, useEffect } from 'react'; import { useLocation } from 'wouter'; import { useQuery } from '@tanstack/react-query'; import { Search, FileText, CheckSquare } from 'lucide-react'; import { Input } from '@/components/ui/input'; import { apiRequest } from '@/lib/queryClient'; import { useDebounce } from '@/hooks/useDebounce'; import { cn } from '@/lib/utils'; interface FormItem { id: number; name: string; } interface TaskItem { id: number; title: string; formId: number; isCompleted?: boolean; statusName?: string | null; statusColor?: string | null; statusIsFinal?: boolean | null; } interface HomeGlobalSearchProps { className?: string; } export function HomeGlobalSearch({ className }: HomeGlobalSearchProps) { const [rawQuery, setRawQuery] = useState(''); const [open, setOpen] = useState(false); const query = useDebounce(rawQuery, 250); const [_, setLocation] = useLocation(); const containerRef = useRef(null); const trimmed = query.trim().toLowerCase(); const { data: formsData } = useQuery<{ success: boolean; forms: FormItem[] }>({ queryKey: ['/api/forms'], staleTime: 5 * 60 * 1000, }); const { data: tasksData, isFetching: isTasksLoading } = useQuery<{ success: boolean; tasks: TaskItem[] }>({ queryKey: ['/api/tasks', 'search', trimmed], queryFn: async () => { const res = await apiRequest('GET', `/api/tasks?search=${encodeURIComponent(trimmed)}&limit=50`); return res.json(); }, enabled: trimmed.length > 0, }); const forms = formsData?.forms || []; const tasks = tasksData?.tasks || []; const filteredForms = useMemo(() => { if (!trimmed) return []; return forms.filter((f) => f.name.toLowerCase().includes(trimmed)).slice(0, 10); }, [forms, trimmed]); const handleSelectForm = (formId: number) => { setOpen(false); setRawQuery(''); setLocation(`/forms/${formId}/tasks`); }; const handleSelectTask = (task: TaskItem) => { setOpen(false); setRawQuery(''); setLocation(`/forms/${task.formId}/tasks/${task.id}`); }; useEffect(() => { if (!open) return; const handler = (e: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setOpen(false); } }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, [open]); const hasResults = filteredForms.length > 0 || tasks.length > 0; const showDropdown = open && trimmed; return (
{ setRawQuery(e.target.value); setOpen(true); }} onFocus={() => setOpen(true)} />
{showDropdown && (
{isTasksLoading && !hasResults ? (
Поиск...
) : !hasResults ? (
Ничего не найдено
) : (
{filteredForms.length > 0 && (
Формы
{filteredForms.map((f) => ( ))}
)} {tasks.length > 0 && (
Задачи
{tasks.map((t) => { const isClosed = !!t.isCompleted || !!t.statusIsFinal; return ( ); })}
)}
)}
)}
); }