fix: Communication page, search field mapping, type compatibility
- Create Communication.tsx page with tree structure (System, KI, Kollegen) - Chat window with messages, reactions, attachments, mini-apps - WebSocket real-time updates - AI streaming integration for AI conversations - Fix search.ts: map backend fields (entity_type/entity_id/title/snippet) to frontend format (type/id/name/description/url) - Fix hooks.ts: import SearchResult from search.ts instead of redefining - Fix JSX syntax error in Communication.tsx title attribute
This commit is contained in:
@@ -24,13 +24,8 @@ export * from './automation';
|
||||
|
||||
// ── Search hook (uses dynamic import, kept inline) ──
|
||||
|
||||
export interface SearchResult {
|
||||
type: 'company' | 'contact' | 'mail' | 'file' | 'event';
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
url: string;
|
||||
}
|
||||
import type { SearchResult } from './search';
|
||||
export type { SearchResult };
|
||||
|
||||
export function useGlobalSearch(query: string, entityTypes?: string[]) {
|
||||
return useQuery({
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { apiClient } from './client';
|
||||
|
||||
export interface SearchResult {
|
||||
type: 'contact' | 'mail' | 'file' | 'event';
|
||||
type: string;
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
url: string;
|
||||
score?: number;
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface SearchResponse {
|
||||
@@ -15,18 +17,55 @@ export interface SearchResponse {
|
||||
suggestions?: string[];
|
||||
}
|
||||
|
||||
// Map backend entity_type to frontend route URL
|
||||
const ENTITY_URL_MAP: Record<string, (id: string, data?: Record<string, unknown>) => 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<string, unknown>): 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<string, unknown>) || {};
|
||||
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, entityTypes?: string[], limit = 20): Promise<SearchResponse> {
|
||||
const r = await apiClient.post('/search', {
|
||||
query,
|
||||
entity_types: entityTypes,
|
||||
limit,
|
||||
});
|
||||
return r.data;
|
||||
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 searchSuggest(q: string): Promise<string[]> {
|
||||
const r = await apiClient.get('/search/suggest', { params: { q } });
|
||||
return r.data.suggestions;
|
||||
return r.data.suggestions || [];
|
||||
}
|
||||
|
||||
export async function searchSimilar(entityType: string, entityId: string): Promise<SearchResult[]> {
|
||||
@@ -34,5 +73,12 @@ export async function searchSimilar(entityType: string, entityId: string): Promi
|
||||
entity_type: entityType,
|
||||
entity_id: entityId,
|
||||
});
|
||||
return r.data.similar;
|
||||
const similar = r.data.similar || {};
|
||||
const all: SearchResult[] = [];
|
||||
for (const items of Object.values(similar) as Record<string, unknown>[][]) {
|
||||
for (const item of items) {
|
||||
all.push(mapBackendResult(item));
|
||||
}
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user