'use client' // IdeaRowActions — Grill Me / Make Plan / Materialiseer / Archive / Open. // Disabled-rules per M12 T-508: // // Grill Me: niet in GRILLING|PLANNING; vereist product-met-repo + // connectedWorkers > 0 // Make Plan: alleen in GRILLED|PLAN_FAILED|PLAN_READY (re-plan); idem // voorwaarden // Materialiseer: alleen in PLAN_READY (geen worker nodig — synchrone parser) // PLANNED: alle drie disabled, "Bekijk PBI" link // *_FAILED: "Probeer opnieuw" knop (= start-job opnieuw) // // Demo-tooltip om elke muteer-knop. connectedWorkers wordt gelezen uit // useSoloStore (M12 grill-keuze 16 — geen lift voor v1). import { useTransition } from 'react' import { useRouter } from 'next/navigation' import { Archive, ArrowRight, ExternalLink, Flame, Layers, RotateCw, Sparkles, } from 'lucide-react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' import { DemoTooltip } from '@/components/shared/demo-tooltip' import { useSoloStore } from '@/stores/solo-store' import { startGrillJobAction, startMakePlanJobAction, materializeIdeaPlanAction, } from '@/actions/ideas' import type { IdeaDto } from '@/lib/idea-dto' interface IdeaRowActionsProps { idea: IdeaDto isDemo: boolean onArchive: () => void } export function IdeaRowActions({ idea, isDemo, onArchive }: IdeaRowActionsProps) { const router = useRouter() const connectedWorkers = useSoloStore((s) => s.connectedWorkers) const [pending, startTransition] = useTransition() const hasProductWithRepo = idea.product != null && idea.product.repo_url !== null const workerOk = connectedWorkers > 0 const status = idea.status // ---- Grill Me ---- const grillBlockedReason = (() => { if (status === 'grilling' || status === 'planning') return 'Job loopt al' if (status === 'planned') return 'Idee is gepland — open de PBI' if (!hasProductWithRepo) return 'Idee heeft een product met repo nodig' if (!workerOk) return 'Geen Claude-worker actief' return null })() const grillEnabled = !grillBlockedReason && !isDemo && !pending // ---- Make Plan ---- const makePlanAllowedStates = ['grilled', 'plan_failed', 'plan_ready'] const makePlanBlockedReason = (() => { if (!makePlanAllowedStates.includes(status)) { if (status === 'draft' || status === 'grill_failed') return 'Eerst grillen' if (status === 'grilling' || status === 'planning') return 'Job loopt al' if (status === 'planned') return 'Idee is gepland — open de PBI' return null } if (!hasProductWithRepo) return 'Idee heeft een product met repo nodig' if (!workerOk) return 'Geen Claude-worker actief' return null })() const makePlanEnabled = !makePlanBlockedReason && !isDemo && !pending // ---- Materialiseer ---- const materializeBlockedReason = (() => { if (status !== 'plan_ready') return 'Plan is niet klaar' return null })() const materializeEnabled = !materializeBlockedReason && !isDemo && !pending // ---- Failed-states tonen "Probeer opnieuw" ---- const isFailedState = status === 'grill_failed' || status === 'plan_failed' function runStart(action: typeof startGrillJobAction | typeof startMakePlanJobAction) { startTransition(async () => { const r = await action(idea.id) if ('error' in r) { toast.error(r.error) return } toast.success('Job in de wachtrij — een worker pakt hem op.') router.refresh() }) } function handleMaterialize() { if (!confirm('Plan materialiseren? Dit maakt PBI + stories + taken aan.')) return startTransition(async () => { const r = await materializeIdeaPlanAction(idea.id) if ('error' in r) { toast.error(r.error) return } toast.success(`Gematerialiseerd als ${r.data?.pbi_code}`) // Navigeer naar de product-backlog. Anchor-scrolling per-PBI bestaat // (nog) niet in pbi-list, dus gewoon naar de overview-pagina; de nieuwe // PBI is de meest recente. if (idea.product_id) { router.push(`/products/${idea.product_id}`) } else { router.refresh() } }) } // PLANNED-state: kortere variant met "Bekijk PBI"-link if (status === 'planned' && idea.pbi && idea.product_id) { return (