fix(graph): Profile opens member dialog (not message) + Add Task button
- 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
This commit is contained in:
parent
26fe42739b
commit
add9df2006
3 changed files with 50 additions and 6 deletions
|
|
@ -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);
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */}
|
||||
<div className="mt-3 flex gap-1.5">
|
||||
<div className="mt-3 flex flex-wrap gap-1.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
|
@ -190,6 +195,17 @@ function MemberPopoverContent({
|
|||
>
|
||||
<User size={12} /> Profile
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 gap-1 px-2 text-xs"
|
||||
onClick={() => {
|
||||
onCreateTask?.(memberName);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<Plus size={12} /> Task
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue