import { useState, useCallback, useEffect } from "react";
import type { TaskWithSubtasks, TaskStatus } from "../../lib/api";
import { getTask, updateTask } from "../../lib/api";
interface InlineSubtaskEditorProps {
subtaskId: string;
onClose: () => void;
onUpdated: () => void;
onNavigate?: (taskId: string) => void;
}
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 InlineSubtaskEditor({
subtaskId,
onClose,
onUpdated,
onNavigate,
}: InlineSubtaskEditorProps) {
const [subtask, setSubtask] = useState<TaskWithSubtasks | null>(null);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [editName, setEditName] = useState("");
const [editDescription, setEditDescription] = useState("");
const [editPlan, setEditPlan] = useState("");
// Load subtask details
useEffect(() => {
setLoading(true);
getTask(subtaskId)
.then((task) => {
setSubtask(task);
setEditName(task.name);
setEditDescription(task.description || "");
setEditPlan(task.plan);
})
.catch((err) => {
console.error("Failed to load subtask:", err);
})
.finally(() => {
setLoading(false);
});
}, [subtaskId]);
const handleSave = useCallback(async () => {
if (!subtask || saving) return;
setSaving(true);
try {
await updateTask(subtaskId, {
name: editName,
description: editDescription || undefined,
plan: editPlan,
version: subtask.version,
});
// Refresh subtask
const updated = await getTask(subtaskId);
setSubtask(updated);
setIsEditing(false);
onUpdated();
} catch (err) {
console.error("Failed to save subtask:", err);
} finally {
setSaving(false);
}
}, [subtask, subtaskId, editName, editDescription, editPlan, saving, onUpdated]);
const handleCancel = useCallback(() => {
if (subtask) {
setEditName(subtask.name);
setEditDescription(subtask.description || "");
setEditPlan(subtask.plan);
}
setIsEditing(false);
}, [subtask]);
if (loading) {
return (
<div className="p-4 bg-[rgba(0,0,0,0.2)] border-l-2 border-[#3f6fb3]">
<div className="font-mono text-xs text-[#75aafc]">Loading subtask...</div>
</div>
);
}
if (!subtask) {
return (
<div className="p-4 bg-[rgba(0,0,0,0.2)] border-l-2 border-red-400">
<div className="font-mono text-xs text-red-400">Failed to load subtask</div>
</div>
);
}
return (
<div className="bg-[rgba(0,0,0,0.2)] border-l-2 border-[#3f6fb3]">
{/* Header */}
<div className="flex items-center justify-between p-3 border-b border-[rgba(117,170,252,0.15)]">
<div className="flex items-center gap-2">
<button
onClick={onClose}
className="font-mono text-[10px] text-[#555] hover:text-[#75aafc]"
>
[close]
</button>
<span
className={`px-1.5 py-0.5 font-mono text-[9px] uppercase ${getStatusColor(
subtask.status as TaskStatus
)} ${getStatusBgColor(subtask.status as TaskStatus)} border border-current/20`}
>
{subtask.status}
</span>
{onNavigate && (
<button
onClick={() => onNavigate(subtaskId)}
className="font-mono text-[10px] text-[#75aafc] hover:text-[#9bc3ff]"
>
[open full view]
</button>
)}
</div>
<div className="flex items-center gap-2">
{isEditing ? (
<>
<button
onClick={handleCancel}
disabled={saving}
className="px-2 py-0.5 font-mono text-[10px] text-[#555] hover:text-[#9bc3ff] disabled:opacity-50"
>
Cancel
</button>
<button
onClick={handleSave}
disabled={saving}
className="px-2 py-0.5 font-mono text-[10px] text-green-400 border border-green-400/30 hover:border-green-400/50 disabled:opacity-50"
>
{saving ? "..." : "Save"}
</button>
</>
) : (
<button
onClick={() => setIsEditing(true)}
className="px-2 py-0.5 font-mono text-[10px] text-[#75aafc] hover:text-[#9bc3ff]"
>
Edit
</button>
)}
</div>
</div>
{/* Content */}
<div className="p-3 space-y-3">
{/* Name */}
{isEditing ? (
<input
type="text"
value={editName}
onChange={(e) => setEditName(e.target.value)}
className="w-full bg-transparent border border-[rgba(117,170,252,0.25)] text-[#dbe7ff] font-mono text-sm px-2 py-1 outline-none focus:border-[#3f6fb3]"
placeholder="Subtask name"
/>
) : (
<div className="font-mono text-sm text-[#dbe7ff]">{subtask.name}</div>
)}
{/* Description */}
{isEditing ? (
<textarea
value={editDescription}
onChange={(e) => setEditDescription(e.target.value)}
className="w-full bg-transparent border border-[rgba(117,170,252,0.25)] text-[#9bc3ff] font-mono text-xs px-2 py-1 outline-none focus:border-[#3f6fb3] min-h-[40px] resize-y"
placeholder="Description (optional)"
/>
) : subtask.description ? (
<div className="font-mono text-xs text-[#75aafc]">{subtask.description}</div>
) : null}
{/* Plan */}
<div className="space-y-1">
<div className="font-mono text-[10px] text-[#555] uppercase">Plan</div>
{isEditing ? (
<textarea
value={editPlan}
onChange={(e) => setEditPlan(e.target.value)}
className="w-full bg-[rgba(0,0,0,0.2)] border border-[rgba(117,170,252,0.25)] text-[#dbe7ff] font-mono text-xs px-2 py-1 outline-none focus:border-[#3f6fb3] min-h-[100px] resize-y"
placeholder="Plan/instructions..."
/>
) : (
<pre className="bg-[rgba(0,0,0,0.2)] border border-[rgba(117,170,252,0.15)] p-2 font-mono text-xs text-[#9bc3ff] whitespace-pre-wrap max-h-[150px] overflow-y-auto">
{subtask.plan}
</pre>
)}
</div>
{/* Progress/Error */}
{subtask.progressSummary && (
<div className="font-mono text-[10px] text-[#75aafc]">
<span className="text-[#555]">Progress:</span> {subtask.progressSummary}
</div>
)}
{subtask.errorMessage && (
<div className="font-mono text-[10px] text-red-400">
<span className="text-red-400/50">Error:</span> {subtask.errorMessage}
</div>
)}
{/* Nested subtasks indicator */}
{subtask.subtasks.length > 0 && (
<div className="font-mono text-[10px] text-[#555]">
Has {subtask.subtasks.length} subtask{subtask.subtasks.length > 1 ? "s" : ""}
{onNavigate && (
<button
onClick={() => onNavigate(subtaskId)}
className="ml-2 text-[#75aafc] hover:text-[#9bc3ff]"
>
[view all]
</button>
)}
</div>
)}
</div>
</div>
);
}