feat: Add tree editor validation UI (Workstream A complete)
Implements comprehensive validation feedback system for tree editor: Task A.1 - Circular Reference Detection: - Added detectCircularRefs() function in treeEditorStore - Detects loops in both decision options and action next_node_id chains - Prevents infinite navigation paths Task A.2 - ValidationSummary Component: - Created collapsible panel showing error/warning count - Click error to select problematic node - Color-coded: red for errors, yellow for warnings - Icon indicators (AlertCircle, AlertTriangle) Task A.3 - TreeEditorPage Integration: - Added ValidationSummary component display - Save button disabled when errors exist - Warnings are informational only (don't block save) - Added manual "Validate" button in toolbar - Imported CheckCircle2 icon for validate button Task A.4 - Visual Node Error Indicators: - Added error/warning badges on problem nodes - Tooltip on hover showing specific error messages - Red ring for errors, yellow ring for warnings - Shows count of errors/warnings per node All tasks from implementation plan completed. Build tested successfully. Related: Issue #1 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -623,6 +623,44 @@ export const useTreeEditorStore = create<TreeEditorState>()(
|
||||
|
||||
validateNode(state.treeStructure)
|
||||
|
||||
// Check for circular references in next_node_id chains
|
||||
const detectCircularRefs = (startId: string, visited: Set<string> = new Set()): boolean => {
|
||||
if (visited.has(startId)) return true
|
||||
visited.add(startId)
|
||||
|
||||
const node = findNodeInTree(startId, state.treeStructure)
|
||||
if (!node) return false
|
||||
|
||||
// Check options
|
||||
if (node.options) {
|
||||
for (const opt of node.options) {
|
||||
if (opt.next_node_id && detectCircularRefs(opt.next_node_id, new Set(visited))) {
|
||||
errors.push({
|
||||
nodeId: node.id,
|
||||
message: `Circular reference detected: "${opt.label}" creates a loop`,
|
||||
severity: 'error'
|
||||
})
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check next_node_id
|
||||
if (node.next_node_id && detectCircularRefs(node.next_node_id, new Set(visited))) {
|
||||
errors.push({
|
||||
nodeId: node.id,
|
||||
message: `Circular reference detected in node "${node.title || node.id}"`,
|
||||
severity: 'error'
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Run from root
|
||||
detectCircularRefs('root')
|
||||
|
||||
// Check for at least one solution
|
||||
if (!hasSolution) {
|
||||
errors.push({
|
||||
|
||||
Reference in New Issue
Block a user