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) => (