useKnowledgeGraph.ts
1 2 import { useCallback, useState } from 'react'; 3 import { Node, Edge, useNodesState, useEdgesState } from '@xyflow/react'; 4 import { getKnowledgeNodeBySlug, getRelatedKnowledgeNodes } from '@/data/knowledgeNodes'; 5 import { KnowledgeNodeDisplay } from '@/types/knowledge'; 6 7 export const useKnowledgeGraph = (slug: string | undefined, blockId: string | null) => { 8 const [nodes, setNodes, onNodesChange] = useNodesState([]); 9 const [edges, setEdges, onEdgesChange] = useEdgesState([]); 10 const [centerNode, setCenterNode] = useState<KnowledgeNodeDisplay | null>(null); 11 12 const generateGraph = useCallback(() => { 13 if (!slug) return; 14 15 const currentNode = getKnowledgeNodeBySlug(slug); 16 if (!currentNode) { 17 console.error(`Node with slug "${slug}" not found`); 18 return; 19 } 20 21 const relatedNodesData = getRelatedKnowledgeNodes(currentNode.relatedNodes); 22 23 const displayNode: KnowledgeNodeDisplay = { 24 ...currentNode, 25 relatedNodes: relatedNodesData.map(node => ({ 26 id: node.id, 27 title: node.title, 28 slug: node.slug, 29 description: node.description, 30 verificationPercentage: node.verificationPercentage 31 })) 32 }; 33 34 setCenterNode(displayNode); 35 36 const flowNodes: Node[] = [ 37 { 38 id: currentNode.id, 39 position: { x: 0, y: 0 }, 40 data: { 41 label: currentNode.title, 42 verificationPercentage: currentNode.verificationPercentage 43 }, 44 type: 'centerNode', 45 className: 'center-node' 46 } 47 ]; 48 49 const radius = 250; 50 const relatedNodesCount = relatedNodesData.length; 51 52 relatedNodesData.forEach((relatedNode, index) => { 53 const angle = (index / relatedNodesCount) * 2 * Math.PI; 54 const x = radius * Math.cos(angle); 55 const y = radius * Math.sin(angle); 56 57 flowNodes.push({ 58 id: relatedNode.id, 59 position: { x, y }, 60 data: { 61 label: relatedNode.title, 62 slug: relatedNode.slug, 63 verificationPercentage: relatedNode.verificationPercentage 64 }, 65 className: 'related-node' 66 }); 67 }); 68 69 const flowEdges: Edge[] = relatedNodesData.map(relatedNode => ({ 70 id: `e-${currentNode.id}-${relatedNode.id}`, 71 source: currentNode.id, 72 target: relatedNode.id, 73 animated: true, 74 className: 'knowledge-edge' 75 })); 76 77 if (blockId) { 78 const block = currentNode.blocks.find(b => b.id === blockId); 79 if (block) { 80 block.links.forEach(link => { 81 const targetNode = relatedNodesData.find(rn => rn.slug === link.slug); 82 if (targetNode) { 83 flowEdges.push({ 84 id: `e-block-${blockId}-${targetNode.id}`, 85 source: currentNode.id, 86 target: targetNode.id, 87 animated: true, 88 style: { strokeWidth: 3 }, 89 className: 'block-specific-edge' 90 }); 91 } 92 }); 93 } 94 } 95 96 setNodes(flowNodes); 97 setEdges(flowEdges); 98 }, [slug, blockId, setNodes, setEdges]); 99 100 return { 101 nodes, 102 edges, 103 centerNode, 104 onNodesChange, 105 onEdgesChange, 106 generateGraph 107 }; 108 };