import type { TaskSummary, TaskStatus } from "../../lib/api";
interface TaskListProps {
tasks: TaskSummary[];
loading: boolean;
onSelect: (id: string) => void;
onDelete: (id: string) => void;
onCreate: () => void;
}
function formatDate(dateStr: string): string {
const date = new Date(dateStr);
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
});
}
function getStatusColor(status: TaskStatus): string {
switch (status) {
case "pending":
return "text-[#9bc3ff]";
case "running":
return "text-green-400";
case "paused":
return "text-yellow-400";
case "blocked":
return "text-orange-400";
case "done":
return "text-emerald-400";
case "failed":
return "text-red-400";
case "merged":
return "text-purple-400";
default:
return "text-[#9bc3ff]";
}
}
function getStatusBgColor(status: TaskStatus): string {
switch (status) {
case "pending":
return "bg-[rgba(117,170,252,0.1)]";
case "running":
return "bg-green-400/10";
case "paused":
return "bg-yellow-400/10";
case "blocked":
return "bg-orange-400/10";
case "done":
return "bg-emerald-400/10";
case "failed":
return "bg-red-400/10";
case "merged":
return "bg-purple-400/10";
default:
return "bg-[rgba(117,170,252,0.1)]";
}
}
export function TaskList({
tasks,
loading,
onSelect,
onDelete,
onCreate,
}: TaskListProps) {
if (loading) {
return (
<div className="panel h-full flex items-center justify-center">
<div className="font-mono text-[#9bc3ff] text-sm">Loading tasks...</div>
</div>
);
}
// Separate root tasks (no parent) from subtasks
const rootTasks = tasks.filter((t) => !t.parentTaskId);
return (
<div className="panel h-full flex flex-col">
<div className="flex items-center justify-between p-4 pb-2 border-b border-dashed border-[rgba(117,170,252,0.35)]">
<div className="font-mono text-xs text-[#9bc3ff] tracking-wide uppercase">
MESH//TASKS
</div>
<button
onClick={onCreate}
className="px-3 py-1 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] hover:bg-[rgba(117,170,252,0.05)] transition-colors uppercase"
>
+ New Task
</button>
</div>
<div className="flex-1 overflow-y-auto">
{rootTasks.length === 0 ? (
<div className="text-center text-[#9bc3ff] text-sm font-mono opacity-60 py-8">
No tasks yet. Create one to start orchestrating Claude Code instances.
</div>
) : (
<div className="divide-y divide-[rgba(117,170,252,0.15)]">
{rootTasks.map((task) => (
<div
key={task.id}
className="p-4 hover:bg-[rgba(117,170,252,0.05)] transition-colors"
>
<div className="flex items-start justify-between gap-4">
<button
onClick={() => onSelect(task.id)}
className="flex-1 text-left"
>
<div className="flex items-center gap-2 mb-1">
<h3 className="font-mono text-sm text-[#dbe7ff]">
{task.name}
</h3>
<span
className={`px-2 py-0.5 font-mono text-[10px] uppercase ${getStatusColor(
task.status
)} ${getStatusBgColor(task.status)} border border-current/20`}
>
{task.status}
</span>
{task.depth === 0 && task.subtaskCount > 0 && (
<span className="px-2 py-0.5 font-mono text-[10px] text-purple-400 bg-purple-400/10 border border-purple-400/20">
Orchestrator
</span>
)}
{task.priority > 0 && (
<span className="px-2 py-0.5 font-mono text-[10px] text-orange-400 bg-orange-400/10 border border-orange-400/20">
P{task.priority}
</span>
)}
</div>
{task.progressSummary && (
<p className="font-mono text-xs text-[#9bc3ff] mb-2 line-clamp-2">
{task.progressSummary}
</p>
)}
<div className="flex gap-4 font-mono text-[10px] text-[#75aafc]">
{task.subtaskCount > 0 && (
<span>{task.subtaskCount} subtasks</span>
)}
<span>{formatDate(task.createdAt)}</span>
</div>
</button>
<button
onClick={(e) => {
e.stopPropagation();
onDelete(task.id);
}}
className="px-2 py-1 font-mono text-[10px] text-red-400 hover:bg-red-400/10 border border-red-400/30 hover:border-red-400/50 transition-colors uppercase"
>
Delete
</button>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}