feat(kontakte): Zustaendigkeit, LinkedIn-Foto, Netzwerkauswahl

Drei Verbesserungen an den Kontakten:

* Beim Anlegen ist der angemeldete Benutzer voreingestellt zustaendig.
  Bisher stand dort der erste Benutzer der Liste - fast immer falsch.
* Wird eine LinkedIn-Adresse hinterlegt, holt das CRM das oeffentliche
  Profilfoto und setzt es als Kontaktfoto: beim Verlassen des Feldes
  einmal je Adresse, solange kein Foto gesetzt ist, und auf Klick ueber
  den Knopf daneben. Der Abruf laeuft ueber das Backend
  (server/src/linkedinPhoto.js, POST /api/leads/linkedin-photo), weil
  ihn die Same-Origin-Policy im Browser verhindert. Gelesen wird die
  Open-Graph-Vorschau der Profilseite; Herkunft, Groesse und Antwortzeit
  des Bildes werden geprueft, Platzhalter-Silhouetten abgewiesen.
  Schlaegt der Abruf fehl, steht der Grund unter dem Feld.
* In der Netzwerkansicht hebt ein Klick auf freie Flaeche die Auswahl
  auf - das Diagramm sieht danach aus wie beim Wechsel in die Ansicht.
  Ein Ziehen des Hintergrunds (ab 4 px) laesst die Auswahl stehen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sascha Wolf
2026-09-09 23:25:37 +02:00
co-authored by Claude Opus 5
parent 3c9d83161e
commit 0a2cf52e8a
14 changed files with 540 additions and 26 deletions
+28
View File
@@ -519,6 +519,27 @@ Das Backend akzeptiert nur Bild-Data-URLs und http(s)-Adressen und begrenzt die
Länge; die Quelldatei darf bis 25 MB groß sein, weil sie ohnehin
komprimiert wird.
### Profilfoto von LinkedIn
Trägt man beim Anlegen eines Kontakts eine LinkedIn-Adresse ein, holt das CRM
das öffentliche Profilbild und setzt es als Kontaktfoto - beim Verlassen des
Feldes von selbst (einmal je Adresse, nur solange kein Foto gesetzt ist) oder
auf Klick über den Knopf *Foto* daneben.
Den Abruf macht das Backend (`server/src/linkedinPhoto.js`, Route
`POST /api/leads/linkedin-photo`): aus dem Browser heraus stünde die
Same-Origin-Policy davor. Gelesen wird die Open-Graph-Vorschau der öffentlichen
Profilseite (`og:image`), das Bild kommt anschließend von `media.licdn.com`.
Geprüft wird die Herkunft (nur `https` von `licdn.com` mit `/dms/image/` im
Pfad), die Größe (max. 5 MB) und die Antwortzeit (12 s); die graue
Platzhalter-Silhouette der Profile ohne eigenes Foto wird abgewiesen. Verkleinert
wird danach im Browser über denselben Weg wie ein Upload
(`compressImageSource()`).
Der Abruf ist bewusst als **Komfort** gebaut, nicht als Zusage: LinkedIn weist
Anfragen aus Rechenzentren häufiger ab als solche aus einem Büronetz. Schlägt er
fehl, steht der Grund unter dem Feld und das Foto wird wie bisher hochgeladen.
Benutzerfotos pflegt jeder für sich unter *Einstellungen → Mein
Konto*, Admins für alle in der Nutzerverwaltung. Angezeigt werden sie in der
Sidebar, bei Terminteilnehmern und Erstellern, im Benutzer-Detailfenster, in den
@@ -544,6 +565,13 @@ Knoten ziehen fixiert ihn (grüner Punkt), Doppelklick löst die Fixierung.
Über *Verbindung erstellen* zwei Kontakte anklicken, um eine Kante anzulegen;
Esc bricht ab. Die Ansicht respektiert Suche und Statusfilter.
Ein Klick auf einen Knoten hebt ihn samt Nachbarn hervor und blendet den Rest
auf 22 % Deckkraft ab. Ein Klick auf eine **freie Fläche** hebt die Auswahl
wieder auf - der Ausgangszustand der Ansicht. Damit das Verschieben nicht
versehentlich die Auswahl verwirft, gilt eine Geste erst ab 4 px Weg als Ziehen;
Knoten stoppen das `pointerdown` ohnehin, ihre Klicks erreichen den Hintergrund
nicht.
## Lokale Entwicklung
```bash