1- import React , { useEffect , useState } from 'react' ;
2- import { Card , Spin , Alert } from 'antd' ;
3- import { useGraphStore } from '../store/graphStore' ;
1+ import React , { useCallback , useMemo } from "react" ;
2+ import { Card } from "antd" ;
3+ import ReactFlow , {
4+ Background ,
5+ Controls ,
6+ MiniMap ,
7+ useNodesState ,
8+ useEdgesState ,
9+ addEdge ,
10+ } from "reactflow" ;
11+ import "reactflow/dist/style.css" ;
12+ import { useGraphStore } from "../store/graphStore" ;
413
5- export const GraphVisualizer = ( ) => {
6- const { nodes, edges, traversalState } = useGraphStore ( ) ;
7- const [ graphImage , setGraphImage ] = useState ( null ) ;
8- const [ loading , setLoading ] = useState ( false ) ;
9- const [ error , setError ] = useState ( null ) ;
14+ const nodeTypes = { } ;
1015
11- const generateGraph = async ( ) => {
12- if ( edges . length === 0 ) {
13- setGraphImage ( null ) ;
14- return ;
15- }
16+ export const GraphVisualizer = ( ) => {
17+ const {
18+ nodes : storeNodes ,
19+ edges : storeEdges ,
20+ traversalState,
21+ } = useGraphStore ( ) ;
1622
17- setLoading ( true ) ;
18- setError ( null ) ;
23+ const initialNodes = useMemo ( ( ) => {
24+ return storeNodes . map ( ( node ) => ( {
25+ id : node . id ,
26+ position : node . position ,
27+ data : { label : node . data . label } ,
28+ style : {
29+ background : getNodeColor ( node . id , traversalState ) ,
30+ color : "#fff" ,
31+ border : "2px solid #222" ,
32+ borderRadius : "50%" ,
33+ width : 50 ,
34+ height : 50 ,
35+ display : "flex" ,
36+ alignItems : "center" ,
37+ justifyContent : "center" ,
38+ fontSize : "14px" ,
39+ fontWeight : "bold" ,
40+ transition : "all 0.3s ease" ,
41+ } ,
42+ type : "default" ,
43+ } ) ) ;
44+ } , [ storeNodes , traversalState ] ) ;
1945
20- try {
21- // Prepare data for Python backend
22- const graphData = {
23- edges : edges . map ( edge => [ edge . source , edge . target ] ) ,
24- visited_nodes : Array . from ( traversalState . visited ) ,
25- current_node : traversalState . current ,
26- current_edge : traversalState . currentEdge
27- } ;
46+ const initialEdges = useMemo ( ( ) => {
47+ return storeEdges . map ( ( edge ) => ( {
48+ id : edge . id ,
49+ source : edge . source ,
50+ target : edge . target ,
51+ label : edge . label ,
52+ type : 'straight' ,
53+ style : {
54+ stroke : getEdgeColor ( edge , traversalState ) ,
55+ strokeWidth : 2 ,
56+ transition : "all 0.3s ease" ,
57+ } ,
58+ labelStyle : {
59+ fontSize : 12 ,
60+ fontWeight : "bold" ,
61+ background : "#fff" ,
62+ padding : "2px 4px" ,
63+ borderRadius : "4px" ,
64+ } ,
65+ animated : isEdgeAnimated ( edge , traversalState ) ,
66+ } ) ) ;
67+ } , [ storeEdges , traversalState ] ) ;
2868
29- const response = await fetch ( 'http://localhost:5000/generate_graph' , {
30- method : 'POST' ,
31- headers : {
32- 'Content-Type' : 'application/json' ,
33- } ,
34- body : JSON . stringify ( graphData )
35- } ) ;
69+ const [ nodes , setNodes , onNodesChange ] = useNodesState ( initialNodes ) ;
70+ const [ edges , setEdges , onEdgesChange ] = useEdgesState ( initialEdges ) ;
3671
37- const result = await response . json ( ) ;
72+ const onConnect = useCallback (
73+ ( params ) => setEdges ( ( eds ) => addEdge ( params , eds ) ) ,
74+ [ setEdges ]
75+ ) ;
3876
39- if ( result . success ) {
40- setGraphImage ( result . image ) ;
41- } else {
42- setError ( result . error || 'Failed to generate graph' ) ;
43- }
44- } catch ( err ) {
45- setError ( 'Failed to connect to graph service. Make sure Python backend is running.' ) ;
46- } finally {
47- setLoading ( false ) ;
48- }
49- } ;
77+ // Update nodes and edges when store changes
78+ React . useEffect ( ( ) => {
79+ setNodes ( initialNodes ) ;
80+ } , [ initialNodes , setNodes ] ) ;
5081
51- useEffect ( ( ) => {
52- generateGraph ( ) ;
53- } , [ edges , traversalState . visited , traversalState . current , traversalState . currentEdge ] ) ;
82+ React . useEffect ( ( ) => {
83+ setEdges ( initialEdges ) ;
84+ } , [ initialEdges , setEdges ] ) ;
5485
55- if ( nodes . length === 0 ) {
86+ if ( storeNodes . length === 0 ) {
5687 return (
57- < Card
58- title = "Graph Visualization"
59- style = { {
60- borderRadius : ' 12px' ,
61- boxShadow : ' 0 2px 8px rgba(0, 0, 0, 0.1)' ,
62- border : ' 1px solid #e8e8e8'
88+ < Card
89+ title = "Graph Visualization"
90+ style = { {
91+ borderRadius : " 12px" ,
92+ boxShadow : " 0 2px 8px rgba(0, 0, 0, 0.1)" ,
93+ border : " 1px solid #e8e8e8" ,
6394 } }
6495 >
65- < div style = { {
66- height : 400 ,
67- display : 'flex' ,
68- alignItems : 'center' ,
69- justifyContent : 'center' ,
70- background : '#f8fafc' ,
71- borderRadius : '8px' ,
72- color : '#64748b' ,
73- fontSize : '16px' ,
74- fontWeight : '500'
75- } } >
96+ < div
97+ style = { {
98+ height : 400 ,
99+ display : "flex" ,
100+ alignItems : "center" ,
101+ justifyContent : "center" ,
102+ background : "#f8fafc" ,
103+ borderRadius : "8px" ,
104+ color : "#64748b" ,
105+ fontSize : "16px" ,
106+ fontWeight : "500" ,
107+ } }
108+ >
76109 Please input a graph to visualize
77110 </ div >
78111 </ Card >
79112 ) ;
80113 }
81114
82115 return (
83- < Card
84- title = "Graph Visualization (Python Generated)"
85- style = { {
86- borderRadius : ' 12px' ,
87- boxShadow : ' 0 2px 8px rgba(0, 0, 0, 0.1)' ,
88- border : ' 1px solid #e8e8e8'
116+ < Card
117+ title = "Interactive Graph Visualization"
118+ style = { {
119+ borderRadius : " 12px" ,
120+ boxShadow : " 0 2px 8px rgba(0, 0, 0, 0.1)" ,
121+ border : " 1px solid #e8e8e8" ,
89122 } }
90123 >
91- < div style = { {
92- minHeight : 400 ,
93- display : 'flex' ,
94- alignItems : 'center' ,
95- justifyContent : 'center' ,
96- background : '#f8fafc' ,
97- borderRadius : '8px' ,
98- padding : '20px'
99- } } >
100- { loading && (
101- < div style = { { textAlign : 'center' } } >
102- < Spin size = "large" />
103- < div style = { { marginTop : '16px' , color : '#64748b' } } >
104- Generating beautiful graph...
105- </ div >
106- </ div >
107- ) }
108-
109- { error && (
110- < Alert
111- message = "Graph Generation Error"
112- description = {
113- < div >
114- < p > { error } </ p >
115- < p style = { { marginTop : '8px' , fontSize : '12px' } } >
116- To start the Python backend:
117- < br />
118- < code > pip install -r requirements.txt</ code >
119- < br />
120- < code > python graph_generator.py</ code >
121- </ p >
122- </ div >
123- }
124- type = "error"
125- showIcon
124+ < div style = { { height : 500 , background : "#f8fafc" , borderRadius : "8px" } } >
125+ < ReactFlow
126+ nodes = { nodes }
127+ edges = { edges }
128+ onNodesChange = { onNodesChange }
129+ onEdgesChange = { onEdgesChange }
130+ onConnect = { onConnect }
131+ nodeTypes = { nodeTypes }
132+ fitView
133+ attributionPosition = "bottom-left"
134+ >
135+ < Background color = "#aaa" gap = { 16 } />
136+ < Controls />
137+ < MiniMap
138+ nodeColor = { ( node ) => getNodeColor ( node . id , traversalState ) }
139+ nodeStrokeWidth = { 3 }
140+ zoomable
141+ pannable
126142 />
127- ) }
128-
129- { graphImage && ! loading && ! error && (
130- < img
131- src = { graphImage }
132- alt = "Graph Visualization"
133- style = { {
134- maxWidth : '100%' ,
135- maxHeight : '500px' ,
136- borderRadius : '8px' ,
137- boxShadow : '0 4px 12px rgba(0, 0, 0, 0.1)'
138- } }
139- />
140- ) }
143+ </ ReactFlow >
141144 </ div >
142145 </ Card >
143146 ) ;
144- } ;
147+ } ;
148+
149+ function getNodeColor ( nodeId , traversalState ) {
150+ if ( traversalState . current === nodeId ) {
151+ return "#ff6b6b" ; // Current node - red
152+ }
153+ if ( traversalState . visited . has ( nodeId ) ) {
154+ return "#51cf66" ; // Visited node - green
155+ }
156+ return "#339af0" ; // Unvisited node - blue
157+ }
158+
159+ function getEdgeColor ( edge , traversalState ) {
160+ // Current edge being traversed
161+ if ( traversalState . currentEdge && traversalState . currentEdge === edge . id ) {
162+ return "#ff6b6b" ; // Current edge - red
163+ }
164+
165+ // Edge between visited nodes (traversed edge)
166+ if ( traversalState . visited . has ( edge . source ) && traversalState . visited . has ( edge . target ) ) {
167+ return "#51cf66" ; // Traversed edge - green
168+ }
169+
170+ // Edge connected to current node
171+ if ( traversalState . current &&
172+ ( edge . source === traversalState . current || edge . target === traversalState . current ) ) {
173+ return "#ffd43b" ; // Connected to current - yellow
174+ }
175+
176+ return "#b1b1b7" ; // Default edge - gray
177+ }
178+
179+ function isEdgeAnimated ( edge , traversalState ) {
180+ return traversalState . currentEdge && traversalState . currentEdge === edge . id ;
181+ }
0 commit comments