feat(postfach): crm@perinco.de lesend anbinden, Kontakte aus Mails
Spiegelt das Vertriebspostfach ueber Microsoft Graph ins CRM. Aus jeder Mail entstehen Kontakt-Vorschlaege, und jede Mail laesst sich an Opportunities und Kontakte haengen. Uebernommen ist die Idee aus dem Altsystem KeepTrack, nicht die Umsetzung: - Delta Query statt "$filter=isRead eq false" mit anschliessendem PATCH. Damit genuegt die Anwendungsberechtigung Mail.Read, es braucht keinen Schreibzugriff auf das Postfach, und eine vorher in Outlook geoeffnete Mail geht nicht verloren. Der Fortschritt liegt als delta_link in der Datenbank und ueberlebt Neustarts; ein Lauf ist gedeckelt und fortsetzbar. - Tenant- und Client-ID kommen aus der .env, nicht aus dem Browser. Im Altsystem konnte jeder Angemeldete das Server-Secret gegen einen fremden Tenant verwenden. - Kontaktdaten werden aus den Graph-Feldern und der Signatur gelesen, ohne Sprachmodell. Mailinhalte verlassen den Server nicht. - Ein Vorschlag ohne E-Mail-Adresse wird nicht mit einer erfundenen Adresse angelegt, sondern bleibt zur Durchsicht liegen. Ist die Adresse schon als Kontakt vorhanden, werden nur leere Felder ergaenzt. - Mails haengen als eigene Verknuepfung an Opportunity und Kontakt, nicht als Text im Notizfeld - loesbar und mehrfach moeglich. - Fremdes Mail-HTML wird serverseitig entschaerft und in einem iframe mit sandbox="" gezeigt. Externe Bilder sind per CSP gesperrt, damit das Oeffnen dem Absender nichts meldet. - Der Verbindungstest prueft Anmeldung und tatsaechlichen Postfachzugriff und benennt den wahrscheinlichen Grund. Der alte Test holte nur ein Token. Fehlen die MS_*-Werte, startet das CRM normal; die Einstellungen nennen die fehlende Variable. Neue Tabellen: crm_mail_messages, crm_mail_links, crm_mail_contact_candidates, crm_mail_sync_state, crm_settings. Uebernommene Kontakte tragen source = 'Mail'; die Kontaktliste hat dafuer einen Herkunftsfilter. Nicht enthalten: Senden, Antworten, Weiterleiten, Anhaenge und die automatische Zuordnung zu Opportunities. npm test im Verzeichnis server prueft Kontaktextraktion und Graph-Client (47 Pruefungen, ohne Framework und ohne Datenbank). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3647201111
commit
6fd713497d
+32
-7
@@ -12,6 +12,9 @@ flowchart TD
|
||||
K --> O["Opportunities<br/>(Startansicht)"]
|
||||
K --> C["Kalender"]
|
||||
K --> L["Kontakte"]
|
||||
K --> M["Mails"]
|
||||
M --> M1["Postfach"]
|
||||
M --> M2["Kontakt-Vorschlaege"]
|
||||
K --> SC["Scanner"]
|
||||
SC --> SC1["Dashboard"]
|
||||
SC --> SC2["Unpassend"]
|
||||
@@ -20,10 +23,14 @@ flowchart TD
|
||||
SB --> E["Einstellungen"]
|
||||
E --> E1["Nutzerverwaltung (admin)"]
|
||||
E --> E2["Mein Konto"]
|
||||
E --> E3["Scanner (admin)"]
|
||||
E --> E4["Whisper"]
|
||||
E --> E3["Postfach (admin)"]
|
||||
E --> E4["Scanner (admin)"]
|
||||
E --> E5["Whisper"]
|
||||
```
|
||||
|
||||
*Postfach* und *Kontakt-Vorschlaege* sind Reiter **innerhalb** der Ansicht
|
||||
*Mails*, keine Menuepunkte - sie gehoeren zusammen und wechseln oft hin und her.
|
||||
|
||||
Der Menuepunkt *Scanner* klappt seine Unterpunkte auf; *Scanner* selbst ist das
|
||||
Dashboard. Bei eingeklappter Leiste (Symbolbreite) erscheinen nur die Symbole,
|
||||
die Unterpunkte kommen mit den Beschriftungen zurueck.
|
||||
@@ -39,8 +46,8 @@ flowchart TB
|
||||
WH["WhisperProvider<br/>Modell, Worker"]
|
||||
end
|
||||
|
||||
APP["App.tsx<br/>currentView, settingsTab"]
|
||||
NAV["NavigationProvider<br/>goTo(view, {tab})"]
|
||||
APP["App.tsx<br/>currentView, settingsTab, focusLeadId"]
|
||||
NAV["NavigationProvider<br/>goTo(view, {tab, leadId})"]
|
||||
DATA["loadData()<br/>leads, connections, events, users, opportunities"]
|
||||
|
||||
Kontexte --> APP
|
||||
@@ -49,14 +56,25 @@ flowchart TB
|
||||
DATA --> V1["Opportunities"]
|
||||
DATA --> V2["Kalender"]
|
||||
DATA --> V3["Kontakte"]
|
||||
DATA --> V4["Mails<br/>leads + opportunities fuer den Zuordnungsdialog"]
|
||||
NAV --> DF["DictationField<br/>springt nach Whisper"]
|
||||
NAV --> ML["Mails<br/>springt zum Kontakt einer Zuordnung"]
|
||||
|
||||
SCV["Scanner-Ansichten"] -->|"laden selbst"| API["/api/scanner"]
|
||||
MV["Mailansicht"] -->|"laedt selbst"| MAPI["/api/mail"]
|
||||
```
|
||||
|
||||
Die CRM-Daten werden **einmal zentral** geladen und als Props durchgereicht; die
|
||||
Scanner-Ansichten laden dagegen selbst, weil sie eine andere Datenbank und eine
|
||||
eigene Auffrischung im 30-Sekunden-Takt haben.
|
||||
Scanner- und Mailansichten laden ihre eigenen Daten selbst - der Scanner, weil er
|
||||
eine andere Datenbank und eine Auffrischung im 30-Sekunden-Takt hat; die Mails,
|
||||
weil sie blaettern, suchen und filtern und dabei nicht die ganze Liste im Speicher
|
||||
halten sollen. Kontakte und Opportunities bekommt die Mailansicht dagegen als
|
||||
Props: der Zuordnungsdialog braucht sie beide zum Vorschlagen.
|
||||
|
||||
`goTo('Kontakte', { leadId })` ist der Sprung aus einer Mailzuordnung zum
|
||||
Kontakt. `App.tsx` haelt die Id in `focusLeadId`, die Kontaktansicht oeffnet den
|
||||
Kontakt und meldet mit `onFocusHandled()` zurueck - der Effekt haengt an `leads`
|
||||
und wartet damit auf einen gerade erst angelegten Kontakt.
|
||||
|
||||
`reload={() => loadData(true)}` laedt still nach - ohne Ladeanzeige, damit ein
|
||||
Speichern im Dialog nicht die ganze Liste flackern laesst.
|
||||
@@ -70,6 +88,7 @@ src/
|
||||
lib/
|
||||
api.ts Fetch-Wrapper (JWT, 401-Behandlung) + CRM-Endpunkte
|
||||
scannerApi.ts Typisierte Endpunkte unter /api/scanner
|
||||
mailApi.ts Endpunkte unter /api/mail, UTC-Datumshelfer
|
||||
auth.tsx Anmeldung, Benutzer, Rolle
|
||||
theme.tsx Hell/Dunkel/System
|
||||
navigation.tsx Ansichtswechsel fuer tiefe Komponenten
|
||||
@@ -83,8 +102,9 @@ src/
|
||||
opportunities/ Kachel, Liste, Pipeline (Drag & Drop), Formular
|
||||
calendar/ Monat/Woche/Tag, Termine (Drag & Drop), @-Mentions
|
||||
leads/ Kachel, Liste, Netzwerkdiagramm (d3-force)
|
||||
mail/ Postfachliste, Detail, Zuordnung, Kontakt-Vorschlaege
|
||||
scanner/ Dashboard, Unpassend, Suchagenten, Boersen-Konten
|
||||
settings/ Reiter: Nutzerverwaltung, Konto, Scanner, Whisper
|
||||
settings/ Reiter: Nutzerverwaltung, Konto, Postfach, Scanner, Whisper
|
||||
whisper/ Einrichtung der Spracherkennung
|
||||
ui/ Modal, Avatare, Diktatfeld
|
||||
```
|
||||
@@ -109,3 +129,8 @@ Ein Inline-Skript in `index.html` setzt die Klasse vor dem ersten Frame, sonst
|
||||
blitzt beim Laden die helle Variante auf. Drei Werte bleiben in beiden Modi
|
||||
gleich: `ink-950` (liegt nur unter Dialog-Overlays), `text-white` (steht immer auf
|
||||
farbigen Flaechen) und `brand-500` (das CI-Gruen der Buttons).
|
||||
|
||||
Eine bewusste Ausnahme: der **Mailinhalt** bleibt auch im Dunkelmodus auf weissem
|
||||
Grund. Fremdes Mail-HTML bringt seine eigenen Farben mit; ein dunkler Hintergrund
|
||||
darunter macht schwarze Schrift auf schwarzer Flaeche. Der Rahmen liest sich damit
|
||||
wie ein Blatt Papier im dunklen Fenster - gewollt.
|
||||
|
||||
Reference in New Issue
Block a user