diff --git a/components/jobs/sprint-sub-tasks-pane.tsx b/components/jobs/sprint-sub-tasks-pane.tsx new file mode 100644 index 0000000..7c91193 --- /dev/null +++ b/components/jobs/sprint-sub-tasks-pane.tsx @@ -0,0 +1,67 @@ +'use client' + +import { useEffect, useState } from 'react' +import { cn } from '@/lib/utils' +import { JOB_STATUS_LABELS, JOB_STATUS_COLORS } from '@/components/shared/job-status' +import type { ClaudeJobStatusApi } from '@/lib/job-status' + +type SubTask = { + id: string + taskCode: string | null + taskTitle: string + status: string +} + +interface SprintSubTasksPaneProps { + jobId: string | null + isSprintJob: boolean +} + +function SubTaskList({ jobId }: { jobId: string }) { + const [subTasks, setSubTasks] = useState([]) + const [loading, setLoading] = useState(true) + + useEffect(() => { + const controller = new AbortController() + + fetch(`/api/jobs/${jobId}/sub-tasks`, { signal: controller.signal }) + .then(res => res.json()) + .then((data: SubTask[]) => { + setSubTasks(data) + setLoading(false) + }) + .catch(() => { + setLoading(false) + }) + + return () => controller.abort() + }, [jobId]) + + if (loading) { + return
Laden…
+ } + + if (subTasks.length === 0) return null + + return ( +
+ {subTasks.map(t => { + const apiStatus = t.status.toLowerCase() as ClaudeJobStatusApi + return ( +
+ {t.taskCode} + {t.taskTitle} + + {JOB_STATUS_LABELS[apiStatus] ?? t.status} + +
+ ) + })} +
+ ) +} + +export default function SprintSubTasksPane({ jobId, isSprintJob }: SprintSubTasksPaneProps) { + if (!isSprintJob || !jobId) return null + return +}