diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 3f881a11..764da802 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,10 +1,11 @@ import React, { useEffect } from 'react'; +import { TooltipProvider } from '@renderer/components/ui/tooltip'; + import { ConfirmDialog } from './components/common/ConfirmDialog'; import { ContextSwitchOverlay } from './components/common/ContextSwitchOverlay'; import { ErrorBoundary } from './components/common/ErrorBoundary'; import { TabbedLayout } from './components/layout/TabbedLayout'; -import { TooltipProvider } from './components/ui/tooltip'; import { useTheme } from './hooks/useTheme'; import { api } from './api'; import { initializeNotificationListeners, useStore } from './store'; diff --git a/src/renderer/components/team/TeamDetailView.tsx b/src/renderer/components/team/TeamDetailView.tsx index 7a496cf1..acb2da32 100644 --- a/src/renderer/components/team/TeamDetailView.tsx +++ b/src/renderer/components/team/TeamDetailView.tsx @@ -5,6 +5,7 @@ import { Button } from '@renderer/components/ui/button'; import { getTeamColorSet } from '@renderer/constants/teamColors'; import { cn } from '@renderer/lib/utils'; import { useStore } from '@renderer/store'; +import { buildTaskCountsByOwner } from '@renderer/utils/pathNormalize'; import { MessageSquare, Pencil, Play, Plus, Search, Trash2, X } from 'lucide-react'; import { useShallow } from 'zustand/react/shallow'; @@ -293,6 +294,8 @@ export const TeamDetailView = ({ teamName }: TeamDetailViewProps): React.JSX.Ele const taskMap = useMemo(() => new Map((data?.tasks ?? []).map((t) => [t.id, t])), [data?.tasks]); + const memberTaskCounts = useMemo(() => buildTaskCountsByOwner(data?.tasks ?? []), [data?.tasks]); + const openCreateTaskDialog = (subject = '', description = '', owner = ''): void => { setCreateTaskDialog({ open: true, @@ -487,6 +490,7 @@ export const TeamDetailView = ({ teamName }: TeamDetailViewProps): React.JSX.Ele { @@ -625,7 +629,7 @@ export const TeamDetailView = ({ teamName }: TeamDetailViewProps): React.JSX.Ele setMessagesSearchQuery(e.target.value)} onPointerDown={(e) => e.stopPropagation()} diff --git a/src/renderer/components/team/members/MemberCard.tsx b/src/renderer/components/team/members/MemberCard.tsx index 963461bd..2ec8e7cf 100644 --- a/src/renderer/components/team/members/MemberCard.tsx +++ b/src/renderer/components/team/members/MemberCard.tsx @@ -4,11 +4,13 @@ import { formatAgentRole } from '@renderer/utils/formatAgentRole'; import { agentAvatarUrl, getMemberDotClass, getPresenceLabel } from '@renderer/utils/memberHelpers'; import { ListPlus, MessageSquare } from 'lucide-react'; +import type { TaskStatusCounts } from '@renderer/utils/pathNormalize'; import type { ResolvedTeamMember } from '@shared/types'; interface MemberCardProps { member: ResolvedTeamMember; memberColor: string; + taskCounts?: TaskStatusCounts | null; isTeamAlive?: boolean; onClick?: () => void; onSendMessage?: () => void; @@ -18,6 +20,7 @@ interface MemberCardProps { export const MemberCard = ({ member, memberColor, + taskCounts, isTeamAlive, onClick, onSendMessage, @@ -26,85 +29,112 @@ export const MemberCard = ({ const dotClass = getMemberDotClass(member, isTeamAlive); const presenceLabel = getPresenceLabel(member, isTeamAlive); const colors = getTeamColorSet(memberColor); + const pending = taskCounts?.pending ?? 0; + const inProgress = taskCounts?.inProgress ?? 0; + const completed = taskCounts?.completed ?? 0; + const totalTasks = pending + inProgress + completed; + const completedRatio = totalTasks > 0 ? completed / totalTasks : 0; return ( -
{ - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - onClick?.(); - } - }} - > -
-
- {member.name} - -
- - {member.name} - - {(() => { - const roleLabel = formatAgentRole(member.role) ?? formatAgentRole(member.agentType); - return roleLabel ? ( - - {roleLabel} - - ) : null; - })()} - +
{ + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick?.(); + } + }} > - {presenceLabel} - - - {member.taskCount} {member.taskCount === 1 ? 'task' : 'tasks'} - -
- - + {member.taskCount} {member.taskCount === 1 ? 'task' : 'tasks'} + +
+ + +
+
+
+
+
+
+
+ + {completed}/{totalTasks} + +
); diff --git a/src/renderer/components/team/members/MemberList.tsx b/src/renderer/components/team/members/MemberList.tsx index baa23290..75737a6a 100644 --- a/src/renderer/components/team/members/MemberList.tsx +++ b/src/renderer/components/team/members/MemberList.tsx @@ -2,10 +2,12 @@ import { getMemberColor } from '@shared/constants/memberColors'; import { MemberCard } from './MemberCard'; +import type { TaskStatusCounts } from '@renderer/utils/pathNormalize'; import type { ResolvedTeamMember } from '@shared/types'; interface MemberListProps { members: ResolvedTeamMember[]; + memberTaskCounts?: Map; isTeamAlive?: boolean; onMemberClick?: (member: ResolvedTeamMember) => void; onSendMessage?: (member: ResolvedTeamMember) => void; @@ -14,6 +16,7 @@ interface MemberListProps { export const MemberList = ({ members, + memberTaskCounts, isTeamAlive, onMemberClick, onSendMessage, @@ -34,6 +37,7 @@ export const MemberList = ({ key={member.name} member={member} memberColor={member.color ?? getMemberColor(index)} + taskCounts={memberTaskCounts?.get(member.name.toLowerCase())} isTeamAlive={isTeamAlive} onClick={() => onMemberClick?.(member)} onSendMessage={() => onSendMessage?.(member)} diff --git a/src/renderer/components/team/messages/MessagesFilterPopover.tsx b/src/renderer/components/team/messages/MessagesFilterPopover.tsx index 7037aafc..4e14430e 100644 --- a/src/renderer/components/team/messages/MessagesFilterPopover.tsx +++ b/src/renderer/components/team/messages/MessagesFilterPopover.tsx @@ -110,11 +110,11 @@ export const MessagesFilterPopover = ({

- Кто писал + From

{fromOptions.length === 0 ? ( -

Нет данных

+

No data

) : ( fromOptions.map((name) => (