import { useMemo } from 'react' import type { TreeStructure, ValidationError } from '@/types' interface ValidationResult { canPublish: boolean errors: ValidationError[] warnings: ValidationError[] } /** * Client-side tree validation hook * Validates tree structure before allowing publish */ export function useTreeValidation( name: string, description: string | null, treeStructure: TreeStructure | null ): ValidationResult { return useMemo(() => { const errors: ValidationError[] = [] const warnings: ValidationError[] = [] // Validate name if (!name || name.trim().length === 0) { errors.push({ field: 'name', message: 'Tree name is required', }) } else if (name.length > 255) { errors.push({ field: 'name', message: 'Tree name must be 255 characters or less', }) } // Validate tree structure exists if (!treeStructure) { errors.push({ field: 'tree_structure', message: 'Tree structure is required', }) // Can't validate further without a tree structure return { canPublish: errors.length === 0, errors, warnings, } } // Validate root node if (!treeStructure.type) { errors.push({ field: 'tree_structure.type', message: 'Root node must have a type', }) } // Validate root node content based on type if (treeStructure.type === 'decision') { if (!treeStructure.question || treeStructure.question.trim().length === 0) { errors.push({ field: 'tree_structure.question', message: 'Decision node must have a question', }) } if (!treeStructure.options || treeStructure.options.length === 0) { errors.push({ field: 'tree_structure.options', message: 'Decision node must have at least one option', }) } else { // Validate each option treeStructure.options.forEach((option, index) => { if (!option.label || option.label.trim().length === 0) { errors.push({ field: `tree_structure.options[${index}].label`, message: `Option ${index + 1} must have a label`, }) } }) } } else if (treeStructure.type === 'action') { if (!treeStructure.title || treeStructure.title.trim().length === 0) { errors.push({ field: 'tree_structure.title', message: 'Action node must have a title', }) } if (!treeStructure.description || treeStructure.description.trim().length === 0) { errors.push({ field: 'tree_structure.description', message: 'Action node must have a description', }) } } else if (treeStructure.type === 'solution') { if (!treeStructure.title || treeStructure.title.trim().length === 0) { errors.push({ field: 'tree_structure.title', message: 'Solution node must have a title', }) } if (!treeStructure.description || treeStructure.description.trim().length === 0) { errors.push({ field: 'tree_structure.description', message: 'Solution node must have a description', }) } } // Validate children recursively (basic check) const validateChildren = (node: TreeStructure, path: string = 'tree_structure') => { if (node.children && node.children.length > 0) { node.children.forEach((child, index) => { const childPath = `${path}.children[${index}]` if (!child.type) { errors.push({ field: `${childPath}.type`, message: 'Child node must have a type', }) } // Recursively validate if (child.type === 'decision' && (!child.question || child.question.trim().length === 0)) { errors.push({ field: `${childPath}.question`, message: 'Decision node must have a question', }) } if ((child.type === 'action' || child.type === 'solution') && (!child.title || child.title.trim().length === 0)) { errors.push({ field: `${childPath}.title`, message: `${child.type} node must have a title`, }) } if (child.children) { validateChildren(child, childPath) } }) } else if (node.type === 'decision' && (!node.options || node.options.length === 0)) { // Decision nodes without children should have had options validation above // This is just a warning for decision nodes that might be incomplete warnings.push({ field: path, message: 'Decision node has no children (paths)', }) } } validateChildren(treeStructure) // Warnings if (!description || description.trim().length === 0) { warnings.push({ field: 'description', message: 'Adding a description helps users understand the tree purpose', }) } if (treeStructure.type === 'decision' && treeStructure.children && treeStructure.children.length < 2) { warnings.push({ field: 'tree_structure', message: 'Tree has very few paths - consider adding more troubleshooting options', }) } return { canPublish: errors.length === 0, errors, warnings, } }, [name, description, treeStructure]) }