import { apiClient } from './client'; // TODO: P2-F3 — Replace hardcoded ENTITY_URL_MAP with dynamic backend config export interface SearchResult { type: string; id: string; name: string; description?: string; url: string; score?: number; data?: Record; } export interface SearchFacet { value: string; count: number; } export interface SearchFilters { entityTypes?: string[]; tags?: string[]; dateFrom?: string; dateTo?: string; sort?: 'relevance' | 'date' | 'name'; } export interface SearchResponse { results: SearchResult[]; facets?: Record; summary?: string; suggestions?: string[]; } export interface FacetsResponse { entity_types: string[]; tags: string[]; date_ranges: Record; } // Map backend entity_type to frontend route URL const ENTITY_URL_MAP: Record) => string> = { contact: (id) => `/contacts/${id}`, company: (id) => `/contacts/${id}`, mail: (id) => `/mail?message_id=${id}`, file: (id) => `/dms?file_id=${id}`, event: (id) => `/calendar?event_id=${id}`, message: (_id, data) => data?.conversation_id ? `/communication?conversation_id=${data.conversation_id}` : '/communication', }; function mapBackendResult(r: Record): SearchResult { const entityType = (r.entity_type as string) || 'unknown'; const entityId = (r.entity_id as string) || (r.id as string) || ''; const title = (r.title as string) || (r.name as string) || ''; const snippet = (r.snippet as string) || (r.description as string) || ''; const data = (r.data as Record) || {}; const urlFn = ENTITY_URL_MAP[entityType]; const url = urlFn ? urlFn(entityId, data) : '#'; return { type: entityType, id: entityId, name: title, description: snippet, url, score: (r.score as number) || 0, data, }; } export async function search(query: string, filters: SearchFilters = {}, limit = 20): Promise { const r = await apiClient.post('/search', { query, entity_types: filters.entityTypes, tags: filters.tags, date_from: filters.dateFrom, date_to: filters.dateTo, sort: filters.sort || 'relevance', limit, }); const data = r.data; // Backend returns { results: [{entity_type, entity_id, title, snippet, score, data}], ... } const rawResults = data.results || []; return { results: rawResults.map(mapBackendResult), facets: data.facets, summary: data.summary, suggestions: data.suggestions, }; } export async function fetchFacets(): Promise { const r = await apiClient.get('/search/facets'); return r.data as FacetsResponse; } export async function searchSuggest(q: string): Promise { const r = await apiClient.get('/search/suggest', { params: { q } }); return r.data.suggestions || []; } export async function searchSimilar(entityType: string, entityId: string): Promise { const r = await apiClient.post('/search/similar', { entity_type: entityType, entity_id: entityId, }); const similar = r.data.similar || {}; const all: SearchResult[] = []; for (const items of Object.values(similar) as Record[][]) { for (const item of items) { all.push(mapBackendResult(item)); } } return all; }