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);
|
setSendDialogDefaultChip(undefined);
|
||||||
setSendDialogOpen(true);
|
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:open-task', onOpenTask);
|
||||||
window.addEventListener('graph:send-message', onSendMsg);
|
window.addEventListener('graph:send-message', onSendMsg);
|
||||||
|
window.addEventListener('graph:open-profile', onOpenProfile);
|
||||||
|
window.addEventListener('graph:create-task', onCreateTask);
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('graph:open-task', onOpenTask);
|
window.removeEventListener('graph:open-task', onOpenTask);
|
||||||
window.removeEventListener('graph:send-message', onSendMsg);
|
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 { Badge } from '@renderer/components/ui/badge';
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
|
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';
|
import type { GraphNode } from '@claude-teams/agent-graph';
|
||||||
|
|
||||||
|
|
@ -17,6 +17,7 @@ interface GraphNodePopoverProps {
|
||||||
onSendMessage?: (memberName: string) => void;
|
onSendMessage?: (memberName: string) => void;
|
||||||
onOpenTaskDetail?: (taskId: string) => void;
|
onOpenTaskDetail?: (taskId: string) => void;
|
||||||
onOpenMemberProfile?: (memberName: string) => void;
|
onOpenMemberProfile?: (memberName: string) => void;
|
||||||
|
onCreateTask?: (owner: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GraphNodePopover({
|
export function GraphNodePopover({
|
||||||
|
|
@ -25,6 +26,7 @@ export function GraphNodePopover({
|
||||||
onSendMessage,
|
onSendMessage,
|
||||||
onOpenTaskDetail,
|
onOpenTaskDetail,
|
||||||
onOpenMemberProfile,
|
onOpenMemberProfile,
|
||||||
|
onCreateTask,
|
||||||
}: GraphNodePopoverProps): React.JSX.Element {
|
}: GraphNodePopoverProps): React.JSX.Element {
|
||||||
if (node.kind === 'member' || node.kind === 'lead') {
|
if (node.kind === 'member' || node.kind === 'lead') {
|
||||||
return (
|
return (
|
||||||
|
|
@ -33,6 +35,7 @@ export function GraphNodePopover({
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onSendMessage={onSendMessage}
|
onSendMessage={onSendMessage}
|
||||||
onOpenProfile={onOpenMemberProfile}
|
onOpenProfile={onOpenMemberProfile}
|
||||||
|
onCreateTask={onCreateTask}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -66,11 +69,13 @@ function MemberPopoverContent({
|
||||||
onClose,
|
onClose,
|
||||||
onSendMessage,
|
onSendMessage,
|
||||||
onOpenProfile,
|
onOpenProfile,
|
||||||
|
onCreateTask,
|
||||||
}: {
|
}: {
|
||||||
node: GraphNode;
|
node: GraphNode;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSendMessage?: (name: string) => void;
|
onSendMessage?: (name: string) => void;
|
||||||
onOpenProfile?: (name: string) => void;
|
onOpenProfile?: (name: string) => void;
|
||||||
|
onCreateTask?: (owner: string) => void;
|
||||||
}): React.JSX.Element {
|
}): React.JSX.Element {
|
||||||
const memberName = node.domainRef.kind === 'member' ? node.domainRef.memberName : 'team-lead';
|
const memberName = node.domainRef.kind === 'member' ? node.domainRef.memberName : 'team-lead';
|
||||||
const avatarSrc = node.avatarUrl ?? agentAvatarUrl(memberName, 64);
|
const avatarSrc = node.avatarUrl ?? agentAvatarUrl(memberName, 64);
|
||||||
|
|
@ -167,7 +172,7 @@ function MemberPopoverContent({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className="mt-3 flex gap-1.5">
|
<div className="mt-3 flex flex-wrap gap-1.5">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -190,6 +195,17 @@ function MemberPopoverContent({
|
||||||
>
|
>
|
||||||
<User size={12} /> Profile
|
<User size={12} /> Profile
|
||||||
</Button>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -37,18 +37,30 @@ export const TeamGraphTab = ({ teamName }: TeamGraphTabProps): React.JSX.Element
|
||||||
),
|
),
|
||||||
[teamName]
|
[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 = {
|
const events: GraphEventPort = {
|
||||||
onNodeDoubleClick: useCallback(
|
onNodeDoubleClick: useCallback(
|
||||||
(ref: GraphDomainRef) => {
|
(ref: GraphDomainRef) => {
|
||||||
if (ref.kind === 'task') dispatchOpenTask(ref.taskId);
|
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,
|
onSendMessage: dispatchSendMessage,
|
||||||
onOpenTaskDetail: dispatchOpenTask,
|
onOpenTaskDetail: dispatchOpenTask,
|
||||||
onOpenMemberProfile: dispatchSendMessage,
|
onOpenMemberProfile: dispatchOpenProfile,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -64,7 +76,8 @@ export const TeamGraphTab = ({ teamName }: TeamGraphTabProps): React.JSX.Element
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onSendMessage={dispatchSendMessage}
|
onSendMessage={dispatchSendMessage}
|
||||||
onOpenTaskDetail={dispatchOpenTask}
|
onOpenTaskDetail={dispatchOpenTask}
|
||||||
onOpenMemberProfile={dispatchSendMessage}
|
onOpenMemberProfile={dispatchOpenProfile}
|
||||||
|
onCreateTask={dispatchCreateTask}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue