feat: globale Suche und Anbindung an das ICON CRM

Beides lag bisher nur als nicht eingecheckte Arbeit im Arbeitsverzeichnis,
waehrend auf Gitea nur die Doku dazu stand (docs/12-suche.md). Damit ist
der Code jetzt versioniert.

Globale Suche:
- server/src/routes/search.js: Volltext ueber Kontakte, Opportunities und
  Akquise, dazu die erweiterte Suche mit dem Modell des Scanners
- src/components/GlobalSearch.tsx im Kopf der Anwendung; ein Treffer
  springt in die Ansicht und oeffnet den Datensatz (navigation.tsx)

ICON CRM:
- server/src/routes/integrations.js liefert die Kontakte mit gesetztem
  Haekchen hinaus, server/src/routes/iconCrm.js holt die Kontakte von
  drueben herein - beide nur lesend, mit festem Token statt JWT
- server/src/contactBridge.js und src/lib/contactMatch.ts erkennen
  Dubletten, src/components/leads/IconMergeDialog.tsx laesst sie
  zusammenfuehren
- server/src/ai.js buendelt die OpenAI-Aufrufe

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-17 23:07:59 +02:00
co-authored by Claude Opus 5
parent ad7f806258
commit 49c03f5e1b
22 changed files with 2339 additions and 23 deletions
+33
View File
@@ -204,6 +204,12 @@ export interface Lead {
* "Aus Mails" gibt es das Kennzeichen nicht.
*/
mail_consent: boolean;
/**
* Frueher das Haekchen "an das ICON CRM weitergeben". Ohne Funktion: drueben
* werden alle Kontakte angezeigt, sobald dort der Schalter gesetzt ist. Das
* Feld kommt noch aus der Datenbank mit und wird nirgends mehr ausgewertet.
*/
icon: boolean;
/** Wann der Haken gesetzt wurde - der Nachweis, seit wann es gilt. */
mail_consent_at?: string | null;
tags: string[];
@@ -572,3 +578,30 @@ export function leadInitials(lead: Pick<Lead, 'first_name' | 'last_name'>): stri
const b = lead.last_name?.charAt(0) ?? '';
return (a + b).toUpperCase() || '?';
}
/*
* Ein Kontakt aus dem ICON CRM.
*
* Er wird nur angezeigt, nie gespeichert. Die Kennung ist eine Zeichenkette
* ("icon-31"), weil sie in einer anderen Datenbank zaehlt und nie mit einer
* hiesigen verwechselt werden darf.
*/
export interface IconContact {
id: string;
first_name: string;
last_name: string;
company: string | null;
position: string | null;
email: string | null;
phone: string | null;
linkedin: string | null;
/** Drueben: Experte, Ansprechpartner oder Lead - unveraendert uebernommen. */
kind: string | null;
source: string | null;
tags: string[];
notes: string | null;
avatar: string | null;
created_at: string | null;
updated_at: string | null;
source_system: 'icon';
}