'use client' import { useRef, useState, useTransition } from 'react' 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 { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/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' const STATUS_COLORS: Record = { TO_DO: 'bg-status-todo/15 text-status-todo border-status-todo/30', IN_PROGRESS: 'bg-status-in-progress/15 text-status-in-progress border-status-in-progress/30', REVIEW: 'bg-status-in-progress/15 text-status-in-progress border-status-in-progress/30', DONE: 'bg-status-done/15 text-status-done border-status-done/30', } const STATUS_LABELS: Record = { TO_DO: 'To Do', IN_PROGRESS: 'Bezig', REVIEW: 'Review', DONE: 'Klaar', } interface TaskDetailDialogProps { task: SoloTask | null productId: string isDemo: boolean onClose: () => void } interface TaskDetailContentProps { task: SoloTask productId: string isDemo: boolean onClose: () => void } type SaveState = 'idle' | 'saving' | 'saved' function TaskDetailContent({ task, productId, isDemo, onClose }: TaskDetailContentProps) { const { updatePlan } = useSoloStore() const job = useSoloStore(s => s.claudeJobsByTaskId[task.id]) const connectedWorkers = useSoloStore(s => s.connectedWorkers) const [localPlan, setLocalPlan] = useState(task.implementation_plan ?? '') const [saveState, setSaveState] = useState('idle') const [, startTransition] = useTransition() const [jobPending, startJobTransition] = useTransition() const fadeTimer = useRef | 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 setSaveState('saving') if (fadeTimer.current) clearTimeout(fadeTimer.current) // fetch naar Route Handler i.p.v. Server Action — Server Actions // kappen anders de open SSE-stream van het Solo Paneel af. Zie // notitie in solo-board.tsx handleDragEnd. startTransition(async () => { try { const res = await fetch(`/api/tasks/${task.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ implementation_plan: localPlan }), }) if (!res.ok) { setSaveState('idle') toast.error('Implementatieplan opslaan mislukt') return } savedPlanRef.current = localPlan updatePlan(task.id, localPlan || null) setSaveState('saved') fadeTimer.current = setTimeout(() => setSaveState('idle'), 2000) } catch { setSaveState('idle') toast.error('Implementatieplan opslaan mislukt') } }) } return ( <>
{task.title} {task.task_code && ( {task.task_code} )} {STATUS_LABELS[task.status]}

{task.story_code && {task.story_code}} {task.story_title}

{task.description && (

Beschrijving

{task.description}

)}

Implementatieplan