1- import React , { useEffect , useMemo , useState } from 'react' ;
2- import { ReactFlow , Background , Controls , useNodesState , useEdgesState , MarkerType , Position } from '@xyflow/react' ;
3- import type { Edge , Node , NodeProps } from '@xyflow/react' ;
1+ import React , { useMemo , useState } from 'react' ;
2+ import { ReactFlow , Background , Controls , MarkerType } from '@xyflow/react' ;
3+ import type { NodeProps } from '@xyflow/react' ;
44import styles from './Graph.module.css' ;
5- import dagre from 'dagre' ;
65import '@xyflow/react/dist/style.css' ;
7- import { useApiResource , useProvidersConfigResource } from '../../lib/api/useApiResource' ;
8- import { ManagedResourcesRequest } from '../../lib/api/types/crossplane/listManagedResources' ;
9- import { resourcesInterval } from '../../lib/shared/constants' ;
10- import { NodeData , ManagedResourceGroup , ManagedResourceItem } from './types' ;
6+ import { ManagedResourceItem } from './types' ;
117import CustomNode from './CustomNode' ;
128import { Legend , LegendItem } from './Legend' ;
13- import { extractRefs , generateColorMap , getStatusFromConditions , resolveProviderType } from './graphUtils' ;
149import { YamlViewDialog } from '../Yaml/YamlViewDialog' ;
1510import YamlViewer from '../Yaml/YamlViewer' ;
1611import { stringify } from 'yaml' ;
1712import { removeManagedFieldsProperty } from '../../utils/removeManagedFieldsProperty' ;
1813import { useTranslation } from 'react-i18next' ;
19-
20- const nodeWidth = 250 ;
21- const nodeHeight = 60 ;
22-
23- function buildGraph (
24- treeData : NodeData [ ] ,
25- colorBy : 'provider' | 'source' ,
26- colorMap : Record < string , string > ,
27- ) : { nodes : Node < NodeData > [ ] ; edges : Edge [ ] } {
28- const dagreGraph = new dagre . graphlib . Graph ( ) ;
29- dagreGraph . setDefaultEdgeLabel ( ( ) => ( { } ) ) ;
30- dagreGraph . setGraph ( { rankdir : 'TB' } ) ;
31-
32- const nodeMap : Record < string , Node < NodeData > > = { } ;
33- treeData . forEach ( ( n ) => {
34- const colorKey = colorBy === 'source' ? n . providerType : n . providerConfigName ;
35- const node : Node < NodeData > = {
36- id : n . id ,
37- type : 'custom' ,
38- data : { ...n } ,
39- style : {
40- border : `2px solid ${ colorMap [ colorKey ] || '#ccc' } ` ,
41- borderRadius : 8 ,
42- backgroundColor : '#fff' ,
43- width : nodeWidth ,
44- height : nodeHeight ,
45- } ,
46- width : nodeWidth ,
47- height : nodeHeight ,
48- position : { x : 0 , y : 0 } ,
49- sourcePosition : Position . Bottom ,
50- targetPosition : Position . Top ,
51- } ;
52- nodeMap [ n . id ] = node ;
53- dagreGraph . setNode ( n . id , { width : nodeWidth , height : nodeHeight } ) ;
54- } ) ;
55-
56- const edgeList : Edge [ ] = [ ] ;
57- treeData . forEach ( ( n ) => {
58- if ( n . parentId ) {
59- dagreGraph . setEdge ( n . parentId , n . id ) ;
60- edgeList . push ( {
61- id : `e-${ n . parentId } -${ n . id } ` ,
62- source : n . parentId ,
63- target : n . id ,
64- markerEnd : { type : MarkerType . ArrowClosed } ,
65- } ) ;
66- }
67- n . extraRefs ?. forEach ( ( refId ) => {
68- if ( nodeMap [ refId ] ) {
69- dagreGraph . setEdge ( refId , n . id ) ;
70- edgeList . push ( {
71- id : `e-${ refId } -${ n . id } ` ,
72- source : refId ,
73- target : n . id ,
74- markerEnd : { type : MarkerType . ArrowClosed } ,
75- } ) ;
76- }
77- } ) ;
78- } ) ;
79-
80- dagre . layout ( dagreGraph ) ;
81- Object . values ( nodeMap ) . forEach ( ( node ) => {
82- const pos = dagreGraph . node ( node . id ) ;
83- node . position = { x : pos . x - nodeWidth / 2 , y : pos . y - nodeHeight / 2 } ;
84- } ) ;
85-
86- return { nodes : Object . values ( nodeMap ) , edges : edgeList } ;
87- }
14+ import { useGraph } from './useGraph' ;
8815
8916const Graph : React . FC = ( ) => {
9017 const { t } = useTranslation ( ) ;
91-
92- const { data : managedResources , error : managedResourcesError } = useApiResource ( ManagedResourcesRequest , {
93- refreshInterval : resourcesInterval ,
94- } ) ;
95- const { data : providerConfigsList , error : providerConfigsError } = useProvidersConfigResource ( {
96- refreshInterval : resourcesInterval ,
97- } ) ;
98- const [ nodes , setNodes ] = useNodesState < Node < NodeData > > ( [ ] ) ;
99- const [ edges , setEdges ] = useEdgesState < Edge > ( [ ] ) ;
10018 const [ colorBy , setColorBy ] = useState < 'provider' | 'source' > ( 'provider' ) ;
19+ const { nodes, edges, colorMap, treeData, loading } = useGraph ( colorBy ) ;
10120
10221 const [ yamlDialogOpen , setYamlDialogOpen ] = useState ( false ) ;
10322 const [ yamlResource , setYamlResource ] = useState < ManagedResourceItem | null > ( null ) ;
@@ -128,74 +47,6 @@ const Graph: React.FC = () => {
12847 return `${ kind ?? '' } ${ metadata ?. name ? '_' : '' } ${ metadata ?. name ?? '' } ` ;
12948 } , [ yamlResource ] ) ;
13049
131- const treeData = useMemo ( ( ) => {
132- const allNodesMap = new Map < string , NodeData > ( ) ;
133- if ( managedResources ) {
134- managedResources . forEach ( ( group : ManagedResourceGroup ) => {
135- group . items ?. forEach ( ( item : ManagedResourceItem ) => {
136- const id = item ?. metadata ?. name ;
137- const kind = item ?. kind ;
138- const providerConfigName = item ?. spec ?. providerConfigRef ?. name ?? 'unknown' ;
139- const providerType = resolveProviderType ( providerConfigName , providerConfigsList ) ;
140- const status = getStatusFromConditions ( item ?. status ?. conditions ) ;
141-
142- const {
143- subaccountRef,
144- serviceManagerRef,
145- spaceRef,
146- orgRef,
147- cloudManagementRef,
148- directoryRef,
149- entitlementRef,
150- globalAccountRef,
151- orgRoleRef,
152- spaceMembersRef,
153- cloudFoundryEnvironmentRef,
154- kymaEnvironmentRef,
155- roleCollectionRef,
156- roleCollectionAssignmentRef,
157- subaccountTrustConfigurationRef,
158- globalaccountTrustConfigurationRef,
159- } = extractRefs ( item ) ;
160-
161- const parentId = serviceManagerRef || subaccountRef ;
162- const extraRefs = [
163- spaceRef ,
164- orgRef ,
165- cloudManagementRef ,
166- directoryRef ,
167- entitlementRef ,
168- globalAccountRef ,
169- orgRoleRef ,
170- spaceMembersRef ,
171- cloudFoundryEnvironmentRef ,
172- kymaEnvironmentRef ,
173- roleCollectionRef ,
174- roleCollectionAssignmentRef ,
175- subaccountTrustConfigurationRef ,
176- globalaccountTrustConfigurationRef ,
177- ] . filter ( Boolean ) as string [ ] ;
178-
179- if ( id ) {
180- allNodesMap . set ( id , {
181- id,
182- label : id ,
183- type : kind ,
184- providerConfigName,
185- providerType,
186- status,
187- parentId,
188- extraRefs,
189- item,
190- } ) ;
191- }
192- } ) ;
193- } ) ;
194- }
195- return Array . from ( allNodesMap . values ( ) ) ;
196- } , [ managedResources , providerConfigsList ] ) ;
197-
198- const colorMap = useMemo ( ( ) => generateColorMap ( treeData , colorBy ) , [ treeData , colorBy ] ) ;
19950 const legendItems : LegendItem [ ] = useMemo (
20051 ( ) =>
20152 Object . entries ( colorMap ) . map ( ( [ name , color ] ) => ( {
@@ -205,22 +56,11 @@ const Graph: React.FC = () => {
20556 [ colorMap ] ,
20657 ) ;
20758
208- useEffect ( ( ) => {
209- if ( ! treeData . length ) return ;
210- const { nodes, edges } = buildGraph ( treeData , colorBy , colorMap ) ;
211- setNodes ( nodes ) ;
212- setEdges ( edges ) ;
213- } , [ treeData , colorBy , colorMap , setNodes , setEdges ] ) ;
214-
215- if ( managedResourcesError || providerConfigsError ) {
216- return < div className = { `${ styles . message } ${ styles . errorMessage } ` } > { t ( 'Graphs.loadingError' ) } </ div > ;
217- }
218-
219- if ( ! managedResources || ! providerConfigsList ) {
59+ if ( loading ) {
22060 return < div className = { styles . message } > { t ( 'Graphs.loadingGraph' ) } </ div > ;
22161 }
22262
223- if ( treeData . length === 0 ) {
63+ if ( ! treeData . length ) {
22464 return < div className = { styles . message } > { t ( 'Graphs.noResources' ) } </ div > ;
22565 }
22666
0 commit comments