/ src / hooks / useKnowledgeGraph.ts
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  };