github.com/argoproj/argo-cd/v3@v3.2.1/ui/src/app/applications/components/applications-list/applications-summary.tsx (about) 1 import * as React from 'react'; 2 const PieChart = require('react-svg-piechart').default; 3 4 import {COLORS} from '../../../shared/components'; 5 import * as models from '../../../shared/models'; 6 import {HealthStatusCode, SyncStatusCode} from '../../../shared/models'; 7 import {ComparisonStatusIcon, HealthStatusIcon, HydrateOperationPhaseIcon} from '../utils'; 8 9 const healthColors = new Map<models.HealthStatusCode, string>(); 10 healthColors.set('Unknown', COLORS.health.unknown); 11 healthColors.set('Progressing', COLORS.health.progressing); 12 healthColors.set('Suspended', COLORS.health.suspended); 13 healthColors.set('Healthy', COLORS.health.healthy); 14 healthColors.set('Degraded', COLORS.health.degraded); 15 healthColors.set('Missing', COLORS.health.missing); 16 17 const syncColors = new Map<models.SyncStatusCode, string>(); 18 syncColors.set('Unknown', COLORS.sync.unknown); 19 syncColors.set('Synced', COLORS.sync.synced); 20 syncColors.set('OutOfSync', COLORS.sync.out_of_sync); 21 22 const hydratorColors = new Map<string, string>(); 23 hydratorColors.set('Hydrating', COLORS.operation.running); 24 hydratorColors.set('Hydrated', COLORS.operation.success); 25 hydratorColors.set('Failed', COLORS.operation.failed); 26 hydratorColors.set('None', COLORS.sync.unknown); 27 28 export const ApplicationsSummary = ({applications}: {applications: models.Application[]}) => { 29 const sync = new Map<string, number>(); 30 applications.forEach(app => sync.set(app.status.sync.status, (sync.get(app.status.sync.status) || 0) + 1)); 31 const health = new Map<string, number>(); 32 applications.forEach(app => health.set(app.status.health.status, (health.get(app.status.health.status) || 0) + 1)); 33 const hydrator = new Map<string, number>(); 34 applications.forEach(app => { 35 const phase = app.status.sourceHydrator?.currentOperation?.phase || 'None'; 36 hydrator.set(phase, (hydrator.get(phase) || 0) + 1); 37 }); 38 39 const attributes = [ 40 { 41 title: 'APPLICATIONS', 42 value: applications.length 43 }, 44 { 45 title: 'SYNCED', 46 value: applications.filter(app => app.status.sync.status === 'Synced').length 47 }, 48 { 49 title: 'HEALTHY', 50 value: applications.filter(app => app.status.health.status === 'Healthy').length 51 }, 52 { 53 title: 'HYDRATED', 54 value: applications.filter(app => app.status.sourceHydrator?.currentOperation?.phase === 'Hydrated').length 55 }, 56 { 57 title: 'CLUSTERS', 58 value: new Set(applications.map(app => app.spec.destination.server || app.spec.destination.name)).size 59 }, 60 { 61 title: 'NAMESPACES', 62 value: new Set(applications.map(app => app.spec.destination.namespace)).size 63 } 64 ]; 65 66 const charts = [ 67 { 68 title: 'Sync', 69 data: Array.from(sync.keys()).map(key => ({title: key, value: sync.get(key), color: syncColors.get(key as models.SyncStatusCode)})), 70 legend: syncColors as Map<string, string> 71 }, 72 { 73 title: 'Health', 74 data: Array.from(health.keys()).map(key => ({title: key, value: health.get(key), color: healthColors.get(key as models.HealthStatusCode)})), 75 legend: healthColors as Map<string, string> 76 }, 77 { 78 title: 'Hydrator', 79 data: Array.from(hydrator.keys()).map(key => ({title: key, value: hydrator.get(key), color: hydratorColors.get(key)})), 80 legend: hydratorColors as Map<string, string> 81 } 82 ]; 83 return ( 84 <div className='white-box applications-list__summary'> 85 <div className='row'> 86 <div className='columns large-3 small-12'> 87 <div className='white-box__details'> 88 <p className='row'>SUMMARY</p> 89 {attributes.map(attr => ( 90 <div className='row white-box__details-row' key={attr.title}> 91 <div className='columns small-8'>{attr.title}</div> 92 <div style={{textAlign: 'right'}} className='columns small-4'> 93 {attr.value} 94 </div> 95 </div> 96 ))} 97 </div> 98 </div> 99 <div className='columns large-9 small-12'> 100 <div className='row chart-group'> 101 {charts.map(chart => { 102 const getLegendValue = (key: string) => { 103 const index = chart.data.findIndex((data: {title: string}) => data.title === key); 104 return index > -1 ? chart.data[index].value : 0; 105 }; 106 return ( 107 <React.Fragment key={chart.title}> 108 <div className='columns large-6 small-12'> 109 <div className='row chart'> 110 <div className='large-8 small-6'> 111 <h4 style={{textAlign: 'center'}}>{chart.title}</h4> 112 <PieChart data={chart.data} /> 113 </div> 114 <div className='large-3 small-1'> 115 <ul> 116 {Array.from(chart.legend.keys()).map(key => ( 117 <li style={{listStyle: 'none', whiteSpace: 'nowrap'}} key={key}> 118 {chart.title === 'Health' && <HealthStatusIcon state={{status: key as HealthStatusCode, message: ''}} noSpin={true} />} 119 {chart.title === 'Sync' && <ComparisonStatusIcon status={key as SyncStatusCode} noSpin={true} />} 120 {chart.title === 'Hydrator' && key !== 'None' && ( 121 <HydrateOperationPhaseIcon 122 operationState={{ 123 phase: key as any, 124 startedAt: '', 125 message: '', 126 drySHA: '', 127 hydratedSHA: '', 128 sourceHydrator: {} as any 129 }} 130 /> 131 )} 132 {chart.title === 'Hydrator' && key === 'None' && ( 133 <i className='fa fa-minus-circle' style={{color: hydratorColors.get(key)}} /> 134 )} 135 {` ${key} (${getLegendValue(key)})`} 136 </li> 137 ))} 138 </ul> 139 </div> 140 </div> 141 </div> 142 </React.Fragment> 143 ); 144 })} 145 </div> 146 </div> 147 </div> 148 </div> 149 ); 150 };