diff --git a/frontend/src/components/molecules/TaskCard.tsx b/frontend/src/components/molecules/TaskCard.tsx index db1ab474..57be199b 100644 --- a/frontend/src/components/molecules/TaskCard.tsx +++ b/frontend/src/components/molecules/TaskCard.tsx @@ -2,8 +2,11 @@ import { CalendarClock, UserCircle } from "lucide-react"; import { cn } from "@/lib/utils"; +type TaskStatus = "inbox" | "in_progress" | "review" | "done"; + interface TaskCardProps { title: string; + status?: TaskStatus; priority?: string; assignee?: string; due?: string; @@ -19,6 +22,7 @@ interface TaskCardProps { export function TaskCard({ title, + status, priority, assignee, due, @@ -32,10 +36,13 @@ export function TaskCard({ onDragEnd, }: TaskCardProps) { const hasPendingApproval = approvalsPendingCount > 0; + const needsLeadReview = status === "review" && !isBlocked && !hasPendingApproval; const leftBarClassName = isBlocked ? "bg-rose-400" : hasPendingApproval ? "bg-amber-400" + : needsLeadReview + ? "bg-indigo-400" : null; const priorityBadge = (value?: string) => { if (!value) return null; @@ -61,6 +68,7 @@ export function TaskCard({ isDragging && "opacity-60 shadow-none", hasPendingApproval && "border-amber-200 bg-amber-50/40", isBlocked && "border-rose-200 bg-rose-50/50", + needsLeadReview && "border-indigo-200 bg-indigo-50/30", )} draggable={draggable} onDragStart={onDragStart} @@ -98,6 +106,12 @@ export function TaskCard({ Approval needed · {approvalsPendingCount} ) : null} + {needsLeadReview ? ( +
+ + Waiting for lead review +
+ ) : null}
void | Promise; }; +type ReviewBucket = "all" | "approval_needed" | "waiting_lead" | "blocked"; + const columns: Array<{ title: string; status: TaskStatus; @@ -99,6 +101,7 @@ export const TaskBoard = memo(function TaskBoard({ const [draggingId, setDraggingId] = useState(null); const [activeColumn, setActiveColumn] = useState(null); + const [reviewBucket, setReviewBucket] = useState("all"); const setCardRef = useCallback( (taskId: string) => (node: HTMLDivElement | null) => { @@ -310,6 +313,42 @@ export const TaskBoard = memo(function TaskBoard({ > {columns.map((column) => { const columnTasks = grouped[column.status] ?? []; + const reviewCounts = + column.status === "review" + ? columnTasks.reduce( + (acc, task) => { + if (task.is_blocked) { + acc.blocked += 1; + return acc; + } + if ((task.approvals_pending_count ?? 0) > 0) { + acc.approval_needed += 1; + return acc; + } + acc.waiting_lead += 1; + return acc; + }, + { + all: columnTasks.length, + approval_needed: 0, + waiting_lead: 0, + blocked: 0, + }, + ) + : null; + + const filteredTasks = + column.status === "review" && reviewBucket !== "all" + ? columnTasks.filter((task) => { + if (reviewBucket === "blocked") return Boolean(task.is_blocked); + if (reviewBucket === "approval_needed") + return (task.approvals_pending_count ?? 0) > 0 && !task.is_blocked; + if (reviewBucket === "waiting_lead") + return !task.is_blocked && (task.approvals_pending_count ?? 0) === 0; + return true; + }) + : columnTasks; + return (
- {columnTasks.length} + {filteredTasks.length}
+ {column.status === "review" && reviewCounts ? ( +
+ {( + [ + { key: "all", label: "All", count: reviewCounts.all }, + { + key: "approval_needed", + label: "Approval needed", + count: reviewCounts.approval_needed, + }, + { + key: "waiting_lead", + label: "Lead review", + count: reviewCounts.waiting_lead, + }, + { key: "blocked", label: "Blocked", count: reviewCounts.blocked }, + ] as const + ).map((option) => ( + + ))} +
+ ) : null}
- {columnTasks.map((task) => ( + {filteredTasks.map((task) => (