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:
co-authored by
Claude Opus 5
parent
3c9d83161e
commit
0a2cf52e8a
@@ -340,7 +340,15 @@ export const LeadNetwork: React.FC<LeadNetworkProps> = ({
|
||||
return () => element.removeEventListener('wheel', onWheel);
|
||||
}, []);
|
||||
|
||||
const panStateRef = useRef<{ pointerId: number; startX: number; startY: number } | null>(null);
|
||||
const panStateRef = useRef<{
|
||||
pointerId: number;
|
||||
startX: number;
|
||||
startY: number;
|
||||
moved: boolean;
|
||||
} | null>(null);
|
||||
|
||||
/** Ab dieser Verschiebung gilt die Geste als Ziehen und nicht mehr als Klick. */
|
||||
const CLICK_TOLERANCE = 4;
|
||||
|
||||
const handleBackgroundPointerDown = (event: React.PointerEvent) => {
|
||||
if (event.button !== 0) return;
|
||||
@@ -348,6 +356,7 @@ export const LeadNetwork: React.FC<LeadNetworkProps> = ({
|
||||
pointerId: event.pointerId,
|
||||
startX: event.clientX - transform.x,
|
||||
startY: event.clientY - transform.y,
|
||||
moved: false,
|
||||
};
|
||||
(event.currentTarget as Element).setPointerCapture(event.pointerId);
|
||||
};
|
||||
@@ -355,10 +364,33 @@ export const LeadNetwork: React.FC<LeadNetworkProps> = ({
|
||||
const handleBackgroundPointerMove = (event: React.PointerEvent) => {
|
||||
const pan = panStateRef.current;
|
||||
if (!pan || pan.pointerId !== event.pointerId) return;
|
||||
setTransform((prev) => ({ ...prev, x: event.clientX - pan.startX, y: event.clientY - pan.startY }));
|
||||
const x = event.clientX - pan.startX;
|
||||
const y = event.clientY - pan.startY;
|
||||
if (Math.abs(x - transform.x) > CLICK_TOLERANCE || Math.abs(y - transform.y) > CLICK_TOLERANCE) {
|
||||
pan.moved = true;
|
||||
}
|
||||
setTransform((prev) => ({ ...prev, x, y }));
|
||||
};
|
||||
|
||||
/*
|
||||
* Ein Klick ins Leere hebt die Auswahl auf. Damit sieht das Netz wieder so
|
||||
* aus wie beim Wechsel auf die Ansicht: nichts ist ausgegraut. Ein Ziehen
|
||||
* (Verschieben der Ansicht) darf die Auswahl dagegen nicht verwerfen,
|
||||
* deshalb der Vergleich mit der Toleranz. Knoten stoppen das Ereignis
|
||||
* bereits beim pointerdown, ihre Klicks landen hier gar nicht erst.
|
||||
*/
|
||||
const handleBackgroundPointerUp = (event: React.PointerEvent) => {
|
||||
const pan = panStateRef.current;
|
||||
if (pan?.pointerId !== event.pointerId) return;
|
||||
panStateRef.current = null;
|
||||
if (pan.moved) return;
|
||||
setSelectedId(null);
|
||||
setHoveredId(null);
|
||||
if (linkMode) setLinkSourceId(null);
|
||||
};
|
||||
|
||||
/** Verlaesst der Zeiger die Flaeche, endet die Geste ohne Auswirkung auf die Auswahl. */
|
||||
const handleBackgroundPointerLeave = (event: React.PointerEvent) => {
|
||||
if (panStateRef.current?.pointerId === event.pointerId) panStateRef.current = null;
|
||||
};
|
||||
|
||||
@@ -654,7 +686,7 @@ export const LeadNetwork: React.FC<LeadNetworkProps> = ({
|
||||
handleNodePointerMove(event);
|
||||
}}
|
||||
onPointerUp={handleBackgroundPointerUp}
|
||||
onPointerLeave={handleBackgroundPointerUp}
|
||||
onPointerLeave={handleBackgroundPointerLeave}
|
||||
className="h-full w-full cursor-grab active:cursor-grabbing"
|
||||
style={{ touchAction: 'none' }}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user