Scrum4Me/components/jobs/sprint-sub-tasks-pane.tsx
Janpeter Visser f166186374
feat(PBI-59): Jobs-pagina UI (vervolg na #149) (#150)
* feat(PBI-58): Vitest-tests voor SoloTaskCard veldmapping en 4-regels layout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): server action fetchJobsPageData voor jobs-pagina

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): SSE-route /api/realtime/jobs voor user-scoped job-events

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): JobCard component voor jobs-pagina

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): JobDetailPane component voor jobs-pagina

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): API route GET /api/jobs/[id]/sub-tasks voor sprint task executions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): Zustand store useJobsStore voor jobs-pagina

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): useJobsRealtime hook met SSE-verbinding en store-updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): JobsBoard 3-kolom SplitPane client component

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): /jobs server page met JobsBoard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-59): Jobs nav-link toevoegen aan NavBar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 19:16:20 +02:00

67 lines
2 KiB
TypeScript

'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} />
}