/** * Global search results page with tabs for companies/contacts/mails/files/events. */ import React, { useState, useMemo } from 'react'; import { useSearchParams, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useGlobalSearch, SearchResult } from '@/api/hooks'; import { Card } from '@/components/ui/Card'; import { Input } from '@/components/ui/Input'; import { Button } from '@/components/ui/Button'; import { EmptyState } from '@/components/ui/EmptyState'; import { Badge } from '@/components/ui/Badge'; import { Skeleton } from '@/components/ui/Skeleton'; import { Tabs } from '@/components/shared/Tabs'; function highlightMatch(text: string, query: string): React.ReactNode { if (!query.trim()) return text; const lowerText = text.toLowerCase(); const lowerQuery = query.toLowerCase(); const idx = lowerText.indexOf(lowerQuery); if (idx === -1) return text; return ( <> {text.slice(0, idx)} {text.slice(idx, idx + query.length)} {text.slice(idx + query.length)} ); } const TYPE_LABELS: Record = { company: 'search.companies', contact: 'search.contacts', mail: 'search.mails', file: 'search.files', event: 'search.events', }; function renderResultIcon(type: string): React.ReactNode { const iconClass = 'w-10 h-10 rounded-lg flex items-center justify-center font-semibold flex-shrink-0'; switch (type) { case 'company': return ; case 'contact': return ; case 'mail': return ; case 'file': return ; case 'event': return ; default: return ; } } function renderResultBadge(type: string, t: (s: string) => string): React.ReactNode { const labelKey = TYPE_LABELS[type] || 'search.allTypes'; const variantMap: Record = { company: 'primary', contact: 'info', mail: 'success', file: 'warning', event: 'default', }; return {t(labelKey)}; } export function GlobalSearchResultsPage() { const { t } = useTranslation(); const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); const query = searchParams.get('q') || ''; const [searchInput, setSearchInput] = useState(query); const [activeTab, setActiveTab] = useState('all'); const { data: results, isLoading } = useGlobalSearch(query); const handleSearch = (e: React.FormEvent) => { e.preventDefault(); const params: Record = {}; if (searchInput) params.q = searchInput; setSearchParams(params); }; const groupedResults = useMemo(() => { const groups: Record = { company: [], contact: [], mail: [], file: [], event: [] }; if (results) { for (const r of results) { if (groups[r.type]) { groups[r.type].push(r); } } } return groups; }, [results]); const totalResults = useMemo(() => { if (!results) return 0; return results.length; }, [results]); const renderResults = (items: SearchResult[]) => { if (items.length === 0) { return ; } return (
{items.map((result) => ( ))}
); }; const tabs = useMemo(() => [ { key: 'all', label: t('search.allTypes'), badge: totalResults, content: (
{totalResults === 0 && !isLoading ? ( ) : ( renderResults(results || []) )}
), }, { key: 'company', label: t('search.companies'), badge: groupedResults.company.length, content:
{renderResults(groupedResults.company)}
, }, { key: 'contact', label: t('search.contacts'), badge: groupedResults.contact.length, content:
{renderResults(groupedResults.contact)}
, }, { key: 'mail', label: t('search.mails'), badge: groupedResults.mail.length, content:
{renderResults(groupedResults.mail)}
, }, { key: 'file', label: t('search.files'), badge: groupedResults.file.length, content:
{renderResults(groupedResults.file)}
, }, { key: 'event', label: t('search.events'), badge: groupedResults.event.length, content:
{renderResults(groupedResults.event)}
, }, ], [t, totalResults, groupedResults, results, isLoading, query, navigate]); return (

{t('search.title')}

setSearchInput(e.target.value)} placeholder={t('common.search')} data-testid="search-input" />
{query && (

{t('search.resultsFor', { query })}:

)} {!query ? ( ) : isLoading ? (
{[1, 2, 3].map((i) => ( ))}
) : (
)}
); }