Sprint: UI taken/ (#149)
* 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> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bd7478861b
commit
4a63b4b01f
6 changed files with 558 additions and 0 deletions
75
components/jobs/job-card.tsx
Normal file
75
components/jobs/job-card.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
'use client'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { JOB_STATUS_LABELS, JOB_STATUS_COLORS } from '@/components/shared/job-status'
|
||||
import { jobStatusToApi } from '@/lib/job-status'
|
||||
import type { ClaudeJobKind, ClaudeJobStatus } from '@prisma/client'
|
||||
|
||||
interface JobCardProps {
|
||||
id: string
|
||||
kind: ClaudeJobKind
|
||||
status: ClaudeJobStatus
|
||||
taskCode?: string | null
|
||||
taskTitle?: string | null
|
||||
ideaCode?: string | null
|
||||
ideaTitle?: string | null
|
||||
sprintGoal?: string | null
|
||||
sprintCode?: string | null
|
||||
productName: string
|
||||
branch?: string | null
|
||||
error?: string | null
|
||||
summary?: string | null
|
||||
isSelected?: boolean
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const KIND_LABELS: Record<ClaudeJobKind, string> = {
|
||||
TASK_IMPLEMENTATION: 'TAAK',
|
||||
SPRINT_IMPLEMENTATION: 'SPRINT',
|
||||
IDEA_GRILL: 'GRILL',
|
||||
IDEA_MAKE_PLAN: 'PLAN',
|
||||
PLAN_CHAT: 'CHAT',
|
||||
}
|
||||
|
||||
export default function JobCard({
|
||||
kind, status, taskCode, taskTitle, ideaCode, ideaTitle,
|
||||
sprintGoal, sprintCode, productName, branch, error, isSelected, onClick,
|
||||
}: JobCardProps) {
|
||||
let titleText: string
|
||||
if (kind === 'TASK_IMPLEMENTATION') {
|
||||
titleText = taskCode && taskTitle ? `${taskCode} ${taskTitle}` : taskTitle || 'Taak'
|
||||
} else if (kind === 'SPRINT_IMPLEMENTATION') {
|
||||
titleText = sprintGoal || (sprintCode ? `Sprint ${sprintCode}` : 'Sprint')
|
||||
} else if (kind === 'IDEA_GRILL' || kind === 'IDEA_MAKE_PLAN') {
|
||||
titleText = ideaCode && ideaTitle ? `${ideaCode} ${ideaTitle}` : ideaTitle || 'Idee'
|
||||
} else if (kind === 'PLAN_CHAT') {
|
||||
titleText = ideaCode ? `Chat ${ideaCode}` : 'Chat'
|
||||
} else {
|
||||
titleText = 'Job'
|
||||
}
|
||||
|
||||
const detailText = branch || (error ? error.slice(0, 80) : null) || productName
|
||||
|
||||
const apiStatus = jobStatusToApi(status)
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'border rounded-lg p-3 cursor-pointer hover:bg-surface-container transition-colors text-sm',
|
||||
isSelected && 'ring-2 ring-primary',
|
||||
)}
|
||||
>
|
||||
<div className="flex justify-between items-center gap-2">
|
||||
<span className="text-[10px] px-1.5 py-0.5 rounded border bg-muted text-muted-foreground font-mono">
|
||||
{KIND_LABELS[kind]}
|
||||
</span>
|
||||
<span className={cn('text-xs px-2 py-0.5 rounded-full border font-medium', JOB_STATUS_COLORS[apiStatus])}>
|
||||
{JOB_STATUS_LABELS[apiStatus]}
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-medium truncate mt-1">{titleText}</p>
|
||||
<p className="text-xs text-muted-foreground truncate mt-0.5">{detailText}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue