This commit is contained in:
Jun-te Kim 2026-02-21 08:39:00 +00:00
parent 862fcb0f19
commit d2636c668e

View file

@ -6,6 +6,7 @@ import { Badge } from "@/app/shadcn_components/ui/badge";
import { ScrollArea } from "@/app/shadcn_components/ui/scroll-area";
import { Card } from "@/app/shadcn_components/ui/card";
import { Button } from "@/app/shadcn_components/ui/button";
import { ChevronDown } from "lucide-react";
interface SubtaskDetailsProps {
selectedTaskId: string;
@ -76,11 +77,131 @@ function CopyableCodeBlock({
);
}
interface ExpandedSubtask {
[key: string]: boolean;
}
function ExpandableSubtaskTile({
subtask,
index,
isExpanded,
onToggle,
}: {
subtask: SubTask;
index: number;
isExpanded: boolean;
onToggle: () => void;
}) {
return (
<Card className="overflow-hidden">
{/* Tile Header */}
<button
onClick={onToggle}
className="w-full p-4 flex items-center justify-between hover:bg-gray-50 transition-colors"
>
<div className="flex-1 text-left">
<p className="text-sm font-semibold text-gray-900">
Subtask {index + 1}
</p>
<code className="text-xs font-mono text-gray-600">
{subtask.id}
</code>
</div>
<div className="flex items-center gap-3">
<Badge variant={getStatusColor(subtask.status)}>
{subtask.status}
</Badge>
<ChevronDown
size={20}
className={`text-gray-500 transition-transform ${
isExpanded ? "rotate-180" : ""
}`}
/>
</div>
</button>
{/* Expanded Content */}
{isExpanded && (
<div className="border-t border-gray-200 p-4 space-y-4 bg-gray-50">
{/* Timeline */}
{(subtask.jobStarted || subtask.jobCompleted) && (
<div className="grid grid-cols-2 gap-3 text-sm">
{subtask.jobStarted && (
<div>
<p className="text-gray-600 text-xs font-medium">Started</p>
<p className="text-gray-900 text-xs">
{new Date(subtask.jobStarted).toLocaleString()}
</p>
</div>
)}
{subtask.jobCompleted && (
<div>
<p className="text-gray-600 text-xs font-medium">Completed</p>
<p className="text-gray-900 text-xs">
{new Date(subtask.jobCompleted).toLocaleString()}
</p>
</div>
)}
</div>
)}
{/* Inputs */}
{subtask.inputs && (
<CopyableCodeBlock
content={formatJson(subtask.inputs)}
label="Inputs"
/>
)}
{/* Outputs */}
{subtask.outputs && (
<CopyableCodeBlock
content={formatJson(subtask.outputs)}
label="Outputs"
/>
)}
{/* Cloud Logs */}
{subtask.cloudLogsURL && (
<div>
<p className="text-sm font-medium text-gray-900 mb-2">
Cloud Logs
</p>
<a
href={subtask.cloudLogsURL}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:text-blue-800 text-xs break-all"
>
{subtask.cloudLogsURL}
</a>
</div>
)}
{/* Updated */}
<p className="text-xs text-gray-500">
Updated: {new Date(subtask.updatedAt).toLocaleString()}
</p>
</div>
)}
</Card>
);
}
export default function SubtaskDetails({
selectedTaskId,
subtasks,
task,
}: SubtaskDetailsProps) {
const [expandedSubtasks, setExpandedSubtasks] = useState<ExpandedSubtask>({});
const toggleSubtask = (subtaskId: string) => {
setExpandedSubtasks((prev) => ({
...prev,
[subtaskId]: !prev[subtaskId],
}));
};
return (
<div className="flex flex-col h-full">
{/* Task Header */}
@ -149,84 +270,15 @@ export default function SubtaskDetails({
</div>
)}
<div className="space-y-4">
<div className="space-y-3">
{subtasks.map((subtask, index) => (
<Card key={subtask.id} className="p-4">
<div className="space-y-3">
{/* Header */}
<div className="flex items-start justify-between">
<div>
<p className="text-sm font-semibold text-gray-900">
Subtask {index + 1}
</p>
<code className="text-xs font-mono text-gray-600">
{subtask.id}
</code>
</div>
<Badge variant={getStatusColor(subtask.status)}>
{subtask.status}
</Badge>
</div>
{/* Timeline */}
<div className="grid grid-cols-2 gap-3 text-sm">
{subtask.jobStarted && (
<div>
<p className="text-gray-600 text-xs">Started</p>
<p className="text-gray-900 text-xs">
{new Date(subtask.jobStarted).toLocaleString()}
</p>
</div>
)}
{subtask.jobCompleted && (
<div>
<p className="text-gray-600 text-xs">Completed</p>
<p className="text-gray-900 text-xs">
{new Date(subtask.jobCompleted).toLocaleString()}
</p>
</div>
)}
</div>
{/* Inputs */}
{subtask.inputs && (
<CopyableCodeBlock
content={formatJson(subtask.inputs)}
label="Inputs"
/>
)}
{/* Outputs */}
{subtask.outputs && (
<CopyableCodeBlock
content={formatJson(subtask.outputs)}
label="Outputs"
/>
)}
{/* Cloud Logs */}
{subtask.cloudLogsURL && (
<div>
<p className="text-sm font-medium text-gray-900 mb-2">
Cloud Logs
</p>
<a
href={subtask.cloudLogsURL}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:text-blue-800 text-xs break-all"
>
{subtask.cloudLogsURL}
</a>
</div>
)}
{/* Updated */}
<p className="text-xs text-gray-500">
Updated: {new Date(subtask.updatedAt).toLocaleString()}
</p>
</div>
</Card>
<ExpandableSubtaskTile
key={subtask.id}
subtask={subtask}
index={index}
isExpanded={expandedSubtasks[subtask.id] || false}
onToggle={() => toggleSubtask(subtask.id)}
/>
))}
</div>
</div>