feat(18-02): create TaskRow component

Renders a single task row with Unicode tree connectors (├── / └──),
StatusBadge, inline agent name, and DependencyIndicator for blocked
tasks. Entire row is clickable with hover feedback.
This commit is contained in:
Lukas May
2026-02-04 21:32:56 +01:00
parent 3baba49edd
commit 4becfe8452

View File

@@ -0,0 +1,73 @@
import { StatusBadge } from "@/components/StatusBadge";
import { DependencyIndicator } from "@/components/DependencyIndicator";
import { cn } from "@/lib/utils";
/** Task shape as returned by tRPC (Date fields serialized to string over JSON) */
export interface SerializedTask {
id: string;
planId: string;
name: string;
description: string | null;
type: string;
priority: string;
status: string;
order: number;
createdAt: string;
updatedAt: string;
}
interface TaskRowProps {
task: SerializedTask;
agentName: string | null;
blockedBy: Array<{ name: string; status: string }>;
isLast: boolean;
onClick: () => void;
}
export function TaskRow({
task,
agentName,
blockedBy,
isLast,
onClick,
}: TaskRowProps) {
const connector = isLast ? "└──" : "├──";
return (
<div>
{/* Task row */}
<div
className={cn(
"flex cursor-pointer items-center gap-2 rounded px-2 py-1 hover:bg-accent",
!isLast && "border-l-2 border-muted-foreground/20",
)}
onClick={onClick}
>
{/* Tree connector */}
<span className="shrink-0 font-mono text-sm text-muted-foreground">
{connector}
</span>
{/* Task name */}
<span className="min-w-0 flex-1 truncate text-sm">{task.name}</span>
{/* Agent assignment */}
{agentName && (
<span className="shrink-0 text-xs text-blue-500">[{agentName}]</span>
)}
{/* Status badge */}
<StatusBadge status={task.status} className="shrink-0" />
</div>
{/* Dependency indicator below the row */}
{blockedBy.length > 0 && (
<DependencyIndicator
blockedBy={blockedBy}
type="task"
className="ml-6"
/>
)}
</div>
);
}