feat(ST-1111.6): add 'Voer uit' + cancel buttons to task detail dialog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-29 19:04:45 +02:00
parent ece0aa963d
commit b9c65eb145

View file

@ -5,9 +5,11 @@ import Link from 'next/link'
import { toast } from 'sonner'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { useSoloStore } from '@/stores/solo-store'
import { enqueueClaudeJobAction, cancelClaudeJobAction } from '@/actions/claude-jobs'
import { cn } from '@/lib/utils'
import type { SoloTask } from './solo-board'
@ -43,12 +45,33 @@ type SaveState = 'idle' | 'saving' | 'saved'
function TaskDetailContent({ task, productId, isDemo, onClose }: TaskDetailContentProps) {
const { updatePlan } = useSoloStore()
const job = useSoloStore(s => s.claudeJobsByTaskId[task.id])
const [localPlan, setLocalPlan] = useState(task.implementation_plan ?? '')
const [saveState, setSaveState] = useState<SaveState>('idle')
const [, startTransition] = useTransition()
const [jobPending, startJobTransition] = useTransition()
const fadeTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
const savedPlanRef = useRef(task.implementation_plan ?? '')
function handleEnqueue() {
startJobTransition(async () => {
const result = await enqueueClaudeJobAction(task.id)
if ('error' in result) {
toast.error(result.error)
} else {
toast.success('Agent ingeschakeld')
}
})
}
function handleCancel() {
if (!job) return
startJobTransition(async () => {
const result = await cancelClaudeJobAction(job.job_id)
if ('error' in result) toast.error(result.error)
})
}
function handleBlur() {
if (isDemo || localPlan === savedPlanRef.current) return
@ -133,14 +156,43 @@ function TaskDetailContent({ task, productId, isDemo, onClose }: TaskDetailConte
</div>
</div>
<div className="-mx-4 -mb-4 flex items-center border-t bg-muted/50 px-4 py-3 rounded-b-xl">
<div className="-mx-4 -mb-4 flex flex-wrap items-center gap-2 border-t bg-muted/50 px-4 py-3 rounded-b-xl">
<Link
href={`/products/${productId}/sprint/planning`}
className="text-xs text-primary hover:underline"
className="text-xs text-primary hover:underline mr-auto"
onClick={onClose}
>
Open in Sprint Board
</Link>
{!isDemo && !job && (
<Button size="sm" className="h-7 text-xs" onClick={handleEnqueue} disabled={jobPending}>
Voer uit
</Button>
)}
{job?.status === 'queued' && (
<span className="text-xs text-muted-foreground">Wacht op agent</span>
)}
{(job?.status === 'claimed' || job?.status === 'running') && (
<>
<span className="text-xs text-muted-foreground">Bezig: {job.summary ?? '…'}</span>
<Button size="sm" variant="outline" className="h-7 text-xs" onClick={handleCancel} disabled={jobPending}>
Annuleer
</Button>
</>
)}
{job?.status === 'done' && (
<span className="text-xs text-status-done">
Klaar{job.branch ? ` — branch ${job.branch}` : ''}
</span>
)}
{job?.status === 'failed' && (
<span className="text-xs text-error">Mislukt: {job.error}</span>
)}
</div>
</>
)