diff --git a/ui/web/src/pages/teams/board/board-container.tsx b/ui/web/src/pages/teams/board/board-container.tsx index 90c44492..a3102204 100644 --- a/ui/web/src/pages/teams/board/board-container.tsx +++ b/ui/web/src/pages/teams/board/board-container.tsx @@ -272,6 +272,7 @@ export const BoardContainer = memo(function BoardContainer({ isTeamV2={isTeamV2} groupBy={groupBy} emojiLookup={emojiLookup} + memberLookup={memberLookup} taskLookup={taskLookup} onTaskClick={handleTaskClick} onDeleteTask={deleteTask ? handleDeleteTask : undefined} diff --git a/ui/web/src/pages/teams/board/kanban-board.tsx b/ui/web/src/pages/teams/board/kanban-board.tsx index 99576d02..8be0f0f8 100644 --- a/ui/web/src/pages/teams/board/kanban-board.tsx +++ b/ui/web/src/pages/teams/board/kanban-board.tsx @@ -9,12 +9,13 @@ interface KanbanBoardProps { isTeamV2?: boolean; groupBy: GroupBy; emojiLookup?: Map; + memberLookup?: Map; taskLookup?: Map; onTaskClick: (task: TeamTaskData) => void; onDeleteTask?: (taskId: string) => void; } -export const KanbanBoard = memo(function KanbanBoard({ tasks, isTeamV2, groupBy, emojiLookup, taskLookup, onTaskClick, onDeleteTask }: KanbanBoardProps) { +export const KanbanBoard = memo(function KanbanBoard({ tasks, isTeamV2, groupBy, emojiLookup, memberLookup, taskLookup, onTaskClick, onDeleteTask }: KanbanBoardProps) { const grouped = useMemo(() => groupTasksBy(tasks, groupBy), [tasks, groupBy]); const columns = useMemo(() => { @@ -40,6 +41,7 @@ export const KanbanBoard = memo(function KanbanBoard({ tasks, isTeamV2, groupBy, tasks={grouped.get(col) ?? []} isTeamV2={isTeamV2} emojiLookup={emojiLookup} + memberLookup={memberLookup} taskLookup={taskLookup} onTaskClick={onTaskClick} onDeleteTask={onDeleteTask} diff --git a/ui/web/src/pages/teams/board/kanban-card.tsx b/ui/web/src/pages/teams/board/kanban-card.tsx index d0c1505a..ea5fe0ad 100644 --- a/ui/web/src/pages/teams/board/kanban-card.tsx +++ b/ui/web/src/pages/teams/board/kanban-card.tsx @@ -1,6 +1,6 @@ import { memo } from "react"; import { motion } from "framer-motion"; -import { Trash2, Ban } from "lucide-react"; +import { Trash2, Ban, MessageSquare, Paperclip } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { useTranslation } from "react-i18next"; import { isTaskLocked } from "./board-utils"; @@ -25,16 +25,18 @@ interface KanbanCardProps { task: TeamTaskData; isTeamV2?: boolean; emojiLookup?: Map; + memberLookup?: Map; taskLookup?: Map; onClick: () => void; onDelete?: (taskId: string) => void; } -export const KanbanCard = memo(function KanbanCard({ task, isTeamV2, emojiLookup, taskLookup, onClick, onDelete }: KanbanCardProps) { +export const KanbanCard = memo(function KanbanCard({ task, isTeamV2, emojiLookup, memberLookup, taskLookup, onClick, onDelete }: KanbanCardProps) { const { t } = useTranslation("teams"); const locked = isTaskLocked(task); const blocked = task.status === "blocked"; const ownerEmoji = task.owner_agent_id && emojiLookup?.get(task.owner_agent_id); + const ownerName = (task.owner_agent_id && memberLookup?.get(task.owner_agent_id)) || task.owner_agent_key; const canDelete = onDelete && isTerminalStatus(task.status); const prio = PRIORITY_LABELS[task.priority] ?? PRIORITY_LABELS[0]!; const hasBlockers = task.blocked_by && task.blocked_by.length > 0; @@ -51,11 +53,19 @@ export const KanbanCard = memo(function KanbanCard({ task, isTeamV2, emojiLookup } onClick={onClick} > - {/* Top row: identifier + running badge + delete button */} + {/* Top row: identifier + priority + running badge + delete button */}
- - {task.identifier || `#${task.task_number ?? ""}`} - +
+ + {task.identifier || `#${task.task_number ?? ""}`} + + + {prio.label} + +
{locked && ( @@ -87,21 +97,25 @@ export const KanbanCard = memo(function KanbanCard({ task, isTeamV2, emojiLookup

)} - {/* Bottom row: owner + type badge + priority */} + {/* Bottom row: owner + type badge + counts */}
{ownerEmoji && {ownerEmoji}} - {task.owner_agent_key || t("board.unassigned")} + {ownerName || t("board.unassigned")} {task.task_type && task.task_type !== "general" && ( {task.task_type} )} - - {prio.label} - + {(task.comment_count ?? 0) > 0 && ( + + {task.comment_count} + + )} + {(task.attachment_count ?? 0) > 0 && ( + + {task.attachment_count} + + )}
{isTeamV2 && task.progress_percent != null && task.progress_percent > 0 && !isTerminalStatus(task.status) && ( diff --git a/ui/web/src/pages/teams/board/kanban-column.tsx b/ui/web/src/pages/teams/board/kanban-column.tsx index eb96020f..10f3c541 100644 --- a/ui/web/src/pages/teams/board/kanban-column.tsx +++ b/ui/web/src/pages/teams/board/kanban-column.tsx @@ -12,12 +12,13 @@ interface KanbanColumnProps { tasks: TeamTaskData[]; isTeamV2?: boolean; emojiLookup?: Map; + memberLookup?: Map; taskLookup?: Map; onTaskClick: (task: TeamTaskData) => void; onDeleteTask?: (taskId: string) => void; } -export const KanbanColumn = memo(function KanbanColumn({ columnId, title, tasks, isTeamV2, emojiLookup, taskLookup, onTaskClick, onDeleteTask }: KanbanColumnProps) { +export const KanbanColumn = memo(function KanbanColumn({ columnId, title, tasks, isTeamV2, emojiLookup, memberLookup, taskLookup, onTaskClick, onDeleteTask }: KanbanColumnProps) { const { t } = useTranslation("teams"); return ( @@ -40,6 +41,7 @@ export const KanbanColumn = memo(function KanbanColumn({ columnId, title, tasks, task={task} isTeamV2={isTeamV2} emojiLookup={emojiLookup} + memberLookup={memberLookup} taskLookup={taskLookup} onClick={() => onTaskClick(task)} onDelete={onDeleteTask}