feat(PBI-59): SprintSubTasksPane component voor jobs-pagina

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scrum4Me Agent 2026-05-07 18:42:59 +02:00
parent fa4192a28a
commit 6b64869cf3

View file

@ -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<SubTask[]>([])
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 <div className="text-xs text-muted-foreground p-3">Laden</div>
}
if (subTasks.length === 0) return null
return (
<div className="border-b p-2 space-y-1 max-h-44 overflow-y-auto shrink-0">
{subTasks.map(t => {
const apiStatus = t.status.toLowerCase() as ClaudeJobStatusApi
return (
<div key={t.id} className="flex items-center gap-2 py-1 px-2 rounded hover:bg-surface-container text-sm">
<span className="text-xs font-mono text-muted-foreground w-16 shrink-0 truncate">{t.taskCode}</span>
<span className="flex-1 truncate">{t.taskTitle}</span>
<span className={cn('text-xs px-1.5 py-0.5 rounded-full border', JOB_STATUS_COLORS[apiStatus])}>
{JOB_STATUS_LABELS[apiStatus] ?? t.status}
</span>
</div>
)
})}
</div>
)
}
export default function SprintSubTasksPane({ jobId, isSprintJob }: SprintSubTasksPaneProps) {
if (!isSprintJob || !jobId) return null
return <SubTaskList key={jobId} jobId={jobId} />
}