11import React , { useMemo } from 'react' ;
2+ import { useTranslation } from 'react-i18next' ;
23import { RadarChart } from '@ui5/webcomponents-react-charts' ;
3- import { ManagedResourceItem , Condition } from '../../lib/shared/types' ;
4+ import { ManagedResourceItem } from '../../lib/shared/types' ;
5+ import { calculateCrossplaneHoverData , HINT_COLORS } from './calculations' ;
6+ import { LegendSection } from './LegendSection' ;
47
58interface CrossplaneHoverContentProps {
69 allItems : ManagedResourceItem [ ] ;
@@ -11,82 +14,81 @@ export const CrossplaneHoverContent: React.FC<CrossplaneHoverContentProps> = ({
1114 allItems,
1215 enabled,
1316} ) => {
14- // Memoize resource type health calculations
15- const { resourceTypeHealth, resourceTypeTotal } = useMemo ( ( ) => {
16- const typeHealth : Record < string , number > = { } ;
17- const typeTotal : Record < string , number > = { } ;
18-
19- allItems . forEach ( ( item : ManagedResourceItem ) => {
20- const type = item . kind || 'Unknown' ;
21- typeTotal [ type ] = ( typeTotal [ type ] || 0 ) + 1 ;
22- const conditions = item . status ?. conditions || [ ] ;
23- const ready = conditions . find ( ( c : Condition ) => c . type === 'Ready' && c . status === 'True' ) ;
24- const synced = conditions . find ( ( c : Condition ) => c . type === 'Synced' && c . status === 'True' ) ;
25- if ( ready && synced ) {
26- typeHealth [ type ] = ( typeHealth [ type ] || 0 ) + 1 ;
27- }
28- } ) ;
17+ const { t } = useTranslation ( ) ;
2918
30- return { resourceTypeHealth : typeHealth , resourceTypeTotal : typeTotal } ;
31- } , [ allItems ] ) ;
19+ // Calculate all statistics using the dedicated calculation function
20+ const { resourceTypeStats, overallStats } = useMemo ( ( ) =>
21+ calculateCrossplaneHoverData ( allItems ) ,
22+ [ allItems ]
23+ ) ;
3224
33- // Memoize radar chart dataset
25+ // Prepare radar chart dataset
3426 const radarDataset = useMemo ( ( ) => {
35- return Object . keys ( resourceTypeTotal ) . map ( type => {
36- const total = resourceTypeTotal [ type ] ;
37- const healthy = resourceTypeHealth [ type ] || 0 ;
38-
39- // Count creating resources (no conditions yet or unknown status)
40- const creating = allItems . filter ( ( item : ManagedResourceItem ) => {
41- if ( item . kind !== type ) return false ;
42- const conditions = item . status ?. conditions || [ ] ;
43- const hasReadyCondition = conditions . some ( ( c : Condition ) => c . type === 'Ready' ) ;
44- const hasSyncedCondition = conditions . some ( ( c : Condition ) => c . type === 'Synced' ) ;
45- return ! hasReadyCondition || ! hasSyncedCondition ;
46- } ) . length ;
47-
48- return {
49- type,
50- healthy : Math . round ( ( healthy / total ) * 100 ) ,
51- creating : Math . round ( ( creating / total ) * 100 )
52- } ;
53- } ) ;
54- } , [ allItems , resourceTypeHealth , resourceTypeTotal ] ) ;
27+ return resourceTypeStats . map ( stats => ( {
28+ type : stats . type ,
29+ healthy : stats . healthyPercentage ,
30+ creating : stats . creatingPercentage ,
31+ unhealthy : stats . unhealthyPercentage
32+ } ) ) ;
33+ } , [ resourceTypeStats ] ) ;
5534
56- if ( ! enabled || radarDataset . length === 0 ) {
35+ if ( ! enabled || resourceTypeStats . length === 0 ) {
5736 return null ;
5837 }
5938
39+ // Prepare legend items with translations
40+ const legendItems = [
41+ {
42+ label : t ( 'Hints.CrossplaneHint.hoverContent.healthy' ) ,
43+ count : overallStats . healthy ,
44+ color : HINT_COLORS . healthy
45+ } ,
46+ {
47+ label : t ( 'Hints.CrossplaneHint.hoverContent.creating' ) ,
48+ count : overallStats . creating ,
49+ color : HINT_COLORS . creating
50+ } ,
51+ {
52+ label : t ( 'Hints.CrossplaneHint.hoverContent.failing' ) ,
53+ count : overallStats . unhealthy ,
54+ color : HINT_COLORS . unhealthy
55+ }
56+ ] ;
6057 return (
6158 < div style = { {
6259 width : '100%' ,
63- height : 300 ,
6460 display : 'flex' ,
65- justifyContent : 'center' ,
66- alignItems : 'center' ,
61+ flexDirection : 'column' ,
62+ alignItems : 'center' ,
6763 margin : '1rem 0' ,
6864 overflow : 'visible'
6965 } } >
70- < RadarChart
71- dataset = { radarDataset }
72- dimensions = { [ { accessor : 'type' } ] }
73- measures = { [
74- {
75- accessor : 'healthy' ,
76- color : '#28a745' ,
77- hideDataLabel : true ,
78- label : 'Healthy (%)'
79- } ,
80- {
81- accessor : 'creating' ,
82- color : '#fd7e14' ,
83- hideDataLabel : true ,
84- label : 'Creating (%)'
85- }
86- ] }
87- style = { { width : '100%' , height : '100%' , minWidth : 280 , minHeight : 280 } }
88- noLegend = { false }
66+ < LegendSection
67+ title = { `${ overallStats . total } ${ t ( 'Hints.CrossplaneHint.hoverContent.totalResources' ) } ` }
68+ items = { legendItems }
8969 />
70+ < div style = { {
71+ width : '100%' ,
72+ height : 300 ,
73+ display : 'flex' ,
74+ justifyContent : 'center' ,
75+ alignItems : 'center'
76+ } } >
77+ < RadarChart
78+ dataset = { radarDataset }
79+ dimensions = { [ { accessor : 'type' } ] }
80+ measures = { [
81+ {
82+ accessor : 'healthy' ,
83+ color : HINT_COLORS . healthy ,
84+ hideDataLabel : true ,
85+ label : t ( 'Hints.CrossplaneHint.hoverContent.healthy' ) + ' (%)'
86+ }
87+ ] }
88+ style = { { width : '100%' , height : '100%' , minWidth : 280 , minHeight : 280 } }
89+ noLegend = { true }
90+ />
91+ </ div >
9092 </ div >
9193 ) ;
9294} ;
0 commit comments