'use client' import { useCallback, useMemo, useState, ChangeEvent } from 'react' import { Handle, Position, NodeProps, useReactFlow } from 'reactflow' import { nanoid } from 'nanoid' import { useEditorContext } from '@/components/editor/EditorContext' import OptionConditionEditor from '@/components/editor/OptionConditionEditor' import NodeLockIndicator from '@/components/editor/NodeLockIndicator' import type { Condition } from '@/types/flowchart' type ChoiceOption = { id: string label: string condition?: Condition } type ChoiceNodeData = { prompt: string options: ChoiceOption[] } const MIN_OPTIONS = 2 const MAX_OPTIONS = 6 export default function ChoiceNode({ id, data }: NodeProps) { const { setNodes } = useReactFlow() const { variables, nodeLocks, onNodeFocus, onNodeBlur } = useEditorContext() const [editingConditionOptionId, setEditingConditionOptionId] = useState(null) const lockInfo = nodeLocks.get(id) const isLockedByOther = !!lockInfo // --- Handlers de Atualização --- const updatePrompt = useCallback( (e: ChangeEvent) => { setNodes((nodes) => nodes.map((node) => node.id === id ? { ...node, data: { ...node.data, prompt: e.target.value } } : node ) ) }, [id, setNodes] ) const updateOptionLabel = useCallback( (optionId: string, label: string) => { setNodes((nodes) => nodes.map((node) => node.id === id ? { ...node, data: { ...node.data, options: node.data.options.map((opt: ChoiceOption) => opt.id === optionId ? { ...opt, label } : opt ), }, } : node ) ) }, [id, setNodes] ) const handleSaveCondition = useCallback((optionId: string, condition: Condition) => { setNodes((nodes) => nodes.map((node) => node.id === id ? { ...node, data: { ...node.data, options: node.data.options.map((opt: ChoiceOption) => opt.id === optionId ? { ...opt, condition } : opt ), }, } : node ) ) setEditingConditionOptionId(null) }, [id, setNodes]) const handleRemoveCondition = useCallback((optionId: string) => { setNodes((nodes) => nodes.map((node) => node.id === id ? { ...node, data: { ...node.data, options: node.data.options.map((opt: ChoiceOption) => opt.id === optionId ? { ...opt, condition: undefined } : opt ), }, } : node ) ) setEditingConditionOptionId(null) }, [id, setNodes]) const addOption = useCallback(() => { if (data.options.length >= MAX_OPTIONS) return setNodes((nodes) => nodes.map((node) => node.id === id ? { ...node, data: { ...node.data, options: [...node.data.options, { id: nanoid(), label: '' }], }, } : node ) ) }, [id, data.options.length, setNodes]) const removeOption = useCallback( (optionId: string) => { if (data.options.length <= MIN_OPTIONS) return setNodes((nodes) => nodes.map((node) => node.id === id ? { ...node, data: { ...node.data, options: node.data.options.filter((opt: ChoiceOption) => opt.id !== optionId), }, } : node ) ) }, [id, data.options.length, setNodes] ) // --- Auxiliares --- const editingOption = useMemo(() => { if (!editingConditionOptionId) return null return data.options.find((opt) => opt.id === editingConditionOptionId) || null }, [editingConditionOptionId, data.options]) const hasInvalidConditionReference = useCallback( (option: ChoiceOption) => { if (!option.condition?.variableId) return false return !variables.some((v) => v.id === option.condition!.variableId) }, [variables] ) const handleFocus = useCallback(() => { onNodeFocus(id) }, [id, onNodeFocus]) return (
{isLockedByOther && ( )} {isLockedByOther && (
Being edited by {lockInfo.displayName}
)}
Choice Node
{data.options.map((option, index) => (
updateOptionLabel(option.id, e.target.value)} placeholder={`Option ${index + 1}`} className="flex-1 rounded border border-zinc-200 px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-green-500 dark:border-zinc-700 dark:bg-zinc-800" /> {/* Botão de Condição */}
{/* Visualização da Condição */} {option.condition && (
IF: {option.condition.variableName} {option.condition.operator} {option.condition.value} {hasInvalidConditionReference(option) && " (Variable Missing!)"}
)}
))}
{/* Modal de Edição de Condição */} {editingOption && ( { if (cond) { handleSaveCondition(editingOption.id, cond) } else { handleRemoveCondition(editingOption.id) } }} onClose={() => setEditingConditionOptionId(null)} /> )}
) }