From add9df2006eb1f6006217d6326ed73b00f244611 Mon Sep 17 00:00:00 2001 From: iliya Date: Sat, 28 Mar 2026 15:08:32 +0200 Subject: [PATCH] fix(graph): Profile opens member dialog (not message) + Add Task button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Profile button dispatches 'graph:open-profile' → MemberDetailDialog (was incorrectly mapped to 'graph:send-message') - Double-click on member → opens profile (was: send message) - New "Add Task" button on member popover → CreateTaskDialog with owner pre-filled - New events: graph:open-profile, graph:create-task - TeamDetailView listens for both new events --- .../components/team/TeamDetailView.tsx | 15 +++++++++++++ .../agent-graph/ui/GraphNodePopover.tsx | 20 ++++++++++++++++-- .../features/agent-graph/ui/TeamGraphTab.tsx | 21 +++++++++++++++---- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/renderer/components/team/TeamDetailView.tsx b/src/renderer/components/team/TeamDetailView.tsx index 878c9565..021305fb 100644 --- a/src/renderer/components/team/TeamDetailView.tsx +++ b/src/renderer/components/team/TeamDetailView.tsx @@ -246,11 +246,26 @@ export const TeamDetailView = ({ teamName }: TeamDetailViewProps): React.JSX.Ele setSendDialogDefaultChip(undefined); setSendDialogOpen(true); }; + const onOpenProfile = (e: Event) => { + const { teamName: tn, memberName } = (e as CustomEvent).detail ?? {}; + if (tn !== teamName || !data) return; + const member = data.members.find((m: { name: string }) => m.name === memberName); + if (member) setSelectedMember(member); + }; + const onCreateTask = (e: Event) => { + const { teamName: tn, owner } = (e as CustomEvent).detail ?? {}; + if (tn !== teamName) return; + openCreateTaskDialog('', '', owner ?? ''); + }; window.addEventListener('graph:open-task', onOpenTask); window.addEventListener('graph:send-message', onSendMsg); + window.addEventListener('graph:open-profile', onOpenProfile); + window.addEventListener('graph:create-task', onCreateTask); return () => { window.removeEventListener('graph:open-task', onOpenTask); window.removeEventListener('graph:send-message', onSendMsg); + window.removeEventListener('graph:open-profile', onOpenProfile); + window.removeEventListener('graph:create-task', onCreateTask); }; }); diff --git a/src/renderer/features/agent-graph/ui/GraphNodePopover.tsx b/src/renderer/features/agent-graph/ui/GraphNodePopover.tsx index 67996ce9..0b124a95 100644 --- a/src/renderer/features/agent-graph/ui/GraphNodePopover.tsx +++ b/src/renderer/features/agent-graph/ui/GraphNodePopover.tsx @@ -7,7 +7,7 @@ import { Badge } from '@renderer/components/ui/badge'; import { Button } from '@renderer/components/ui/button'; import { agentAvatarUrl } from '@renderer/utils/memberHelpers'; -import { MessageSquare, ExternalLink, User } from 'lucide-react'; +import { MessageSquare, ExternalLink, User, Plus } from 'lucide-react'; import type { GraphNode } from '@claude-teams/agent-graph'; @@ -17,6 +17,7 @@ interface GraphNodePopoverProps { onSendMessage?: (memberName: string) => void; onOpenTaskDetail?: (taskId: string) => void; onOpenMemberProfile?: (memberName: string) => void; + onCreateTask?: (owner: string) => void; } export function GraphNodePopover({ @@ -25,6 +26,7 @@ export function GraphNodePopover({ onSendMessage, onOpenTaskDetail, onOpenMemberProfile, + onCreateTask, }: GraphNodePopoverProps): React.JSX.Element { if (node.kind === 'member' || node.kind === 'lead') { return ( @@ -33,6 +35,7 @@ export function GraphNodePopover({ onClose={onClose} onSendMessage={onSendMessage} onOpenProfile={onOpenMemberProfile} + onCreateTask={onCreateTask} /> ); } @@ -66,11 +69,13 @@ function MemberPopoverContent({ onClose, onSendMessage, onOpenProfile, + onCreateTask, }: { node: GraphNode; onClose: () => void; onSendMessage?: (name: string) => void; onOpenProfile?: (name: string) => void; + onCreateTask?: (owner: string) => void; }): React.JSX.Element { const memberName = node.domainRef.kind === 'member' ? node.domainRef.memberName : 'team-lead'; const avatarSrc = node.avatarUrl ?? agentAvatarUrl(memberName, 64); @@ -167,7 +172,7 @@ function MemberPopoverContent({ )} {/* Actions */} -
+
+
); diff --git a/src/renderer/features/agent-graph/ui/TeamGraphTab.tsx b/src/renderer/features/agent-graph/ui/TeamGraphTab.tsx index 8f634922..951d3079 100644 --- a/src/renderer/features/agent-graph/ui/TeamGraphTab.tsx +++ b/src/renderer/features/agent-graph/ui/TeamGraphTab.tsx @@ -37,18 +37,30 @@ export const TeamGraphTab = ({ teamName }: TeamGraphTabProps): React.JSX.Element ), [teamName] ); + const dispatchOpenProfile = useCallback( + (memberName: string) => + window.dispatchEvent( + new CustomEvent('graph:open-profile', { detail: { teamName, memberName } }) + ), + [teamName] + ); + const dispatchCreateTask = useCallback( + (owner: string) => + window.dispatchEvent(new CustomEvent('graph:create-task', { detail: { teamName, owner } })), + [teamName] + ); const events: GraphEventPort = { onNodeDoubleClick: useCallback( (ref: GraphDomainRef) => { if (ref.kind === 'task') dispatchOpenTask(ref.taskId); - else if (ref.kind === 'member') dispatchSendMessage(ref.memberName); + else if (ref.kind === 'member') dispatchOpenProfile(ref.memberName); }, - [dispatchOpenTask, dispatchSendMessage] + [dispatchOpenTask, dispatchOpenProfile] ), onSendMessage: dispatchSendMessage, onOpenTaskDetail: dispatchOpenTask, - onOpenMemberProfile: dispatchSendMessage, + onOpenMemberProfile: dispatchOpenProfile, }; return ( @@ -64,7 +76,8 @@ export const TeamGraphTab = ({ teamName }: TeamGraphTabProps): React.JSX.Element onClose={onClose} onSendMessage={dispatchSendMessage} onOpenTaskDetail={dispatchOpenTask} - onOpenMemberProfile={dispatchSendMessage} + onOpenMemberProfile={dispatchOpenProfile} + onCreateTask={dispatchCreateTask} /> )} />