feat: redesign NodeFormDecision to label-only options, remove NodePicker

Users now type answer labels only. Stub nodes are created automatically
by TreeCanvas when the decision node is saved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-18 01:31:56 -05:00
parent e29aadf8a5
commit 10238f85e2

View File

@@ -1,6 +1,5 @@
import { Play } from 'lucide-react' import { Play } from 'lucide-react'
import { DynamicArrayField } from './DynamicArrayField' import { DynamicArrayField } from './DynamicArrayField'
import { NodePicker } from './NodePicker'
import { useTreeEditorStore } from '@/store/treeEditorStore' import { useTreeEditorStore } from '@/store/treeEditorStore'
import type { TreeStructure, TreeOption } from '@/types' import type { TreeStructure, TreeOption } from '@/types'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
@@ -147,52 +146,34 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
const optionLabelError = validationErrors.find( const optionLabelError = validationErrors.find(
e => e.nodeId === node.id && e.field === `options[${index}].label` e => e.nodeId === node.id && e.field === `options[${index}].label`
) )
const optionNextError = validationErrors.find(
e => e.nodeId === node.id && e.field === `options[${index}].next_node_id`
)
const letter = indexToLetter(index) const letter = indexToLetter(index)
return ( return (
<div className="rounded-md border border-border bg-accent/50 p-3"> <div className="flex items-center gap-2">
<div className="mb-2 flex items-center gap-2"> <span className={cn(
{/* Letter badge */} 'flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-bold',
<span className={cn( isRootNode ? 'bg-blue-500/20 text-blue-400' : 'bg-accent text-muted-foreground'
'flex h-6 w-6 items-center justify-center rounded-full text-xs font-bold', )}>
isRootNode {letter}
? 'bg-blue-500/20 text-blue-400' </span>
: 'bg-accent text-muted-foreground' <div className="flex-1">
)}>
{letter}
</span>
<input <input
type="text" type="text"
value={option.label} value={option.label}
onChange={(e) => handleUpdateOption(index, { label: e.target.value })} onChange={(e) => handleUpdateOption(index, { label: e.target.value })}
placeholder={isRootNode placeholder={isRootNode
? `Branch ${letter}: e.g., "Network Issues", "Application Errors"...` ? `Branch ${letter}: e.g., "Network Issues"...`
: `Option ${letter} label`} : `Option ${letter} label`}
className={cn( className={cn(
'block flex-1 rounded-md border px-3 py-2 text-sm', 'block w-full rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground', 'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary', 'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
optionLabelError ? 'border-red-400' : 'border-border' optionLabelError ? 'border-red-400' : 'border-border'
)} )}
/> />
</div> {optionLabelError && (
{optionLabelError && ( <p className="mt-1 text-xs text-red-400">{optionLabelError.message}</p>
<p className="mb-2 text-xs text-red-400">{optionLabelError.message}</p> )}
)}
<div className="pl-8">
<NodePicker
value={option.next_node_id}
onChange={(nodeId) => handleUpdateOption(index, { next_node_id: nodeId })}
parentNodeId={node.id}
excludeNodeId={node.id}
placeholder={isRootNode
? `What happens when user selects "${option.label || `Branch ${letter}`}"?`
: "Select or create next node..."}
error={optionNextError?.message}
/>
</div> </div>
</div> </div>
) )