github.com/argoproj/argo-cd@v1.8.7/ui/src/app/settings/components/cluster-details/cluster-details.tsx (about) 1 import * as classNames from 'classnames'; 2 import * as moment from 'moment'; 3 import * as React from 'react'; 4 import {FieldApi, FormField as ReactFormField, Text} from 'react-form'; 5 import {RouteComponentProps} from 'react-router-dom'; 6 import {Observable} from 'rxjs'; 7 8 import {FormField, Ticker} from 'argo-ui'; 9 import {ConnectionStateIcon, DataLoader, EditablePanel, Page, Timestamp} from '../../../shared/components'; 10 import {Cluster} from '../../../shared/models'; 11 import {services} from '../../../shared/services'; 12 13 function isRefreshRequested(cluster: Cluster): boolean { 14 return cluster.info.connectionState.attemptedAt && cluster.refreshRequestedAt && moment(cluster.info.connectionState.attemptedAt).isBefore(moment(cluster.refreshRequestedAt)); 15 } 16 17 export const NamespacesEditor = ReactFormField((props: {fieldApi: FieldApi; className: string}) => { 18 const val = (props.fieldApi.getValue() || []).join(','); 19 return <input className={props.className} value={val} onChange={event => props.fieldApi.setValue(event.target.value.split(','))} />; 20 }); 21 22 export const ClusterDetails = (props: RouteComponentProps<{server: string}>) => { 23 const server = decodeURIComponent(props.match.params.server); 24 const loaderRef = React.useRef<DataLoader>(); 25 const [updating, setUpdating] = React.useState(false); 26 return ( 27 <DataLoader ref={loaderRef} input={server} load={(url: string) => Observable.timer(0, 1000).flatMap(() => Observable.fromPromise(services.clusters.get(url, '')))}> 28 {(cluster: Cluster) => ( 29 <Page 30 title={server} 31 toolbar={{ 32 breadcrumbs: [{title: 'Settings', path: '/settings'}, {title: 'Cluster', path: '/settings/clusters'}, {title: server}], 33 actionMenu: { 34 items: [ 35 { 36 iconClassName: classNames('fa fa-redo', {'status-icon--spin': isRefreshRequested(cluster)}), 37 title: 'Invalidate Cache', 38 disabled: isRefreshRequested(cluster) || updating, 39 action: async () => { 40 setUpdating(true); 41 try { 42 const updated = await services.clusters.invalidateCache(props.match.params.server); 43 loaderRef.current.setData(updated); 44 } finally { 45 setUpdating(false); 46 } 47 } 48 } 49 ] 50 } 51 }}> 52 <p /> 53 54 <div className='argo-container'> 55 <EditablePanel 56 values={cluster} 57 save={async updated => { 58 const item = await services.clusters.get(updated.server, ''); 59 item.name = updated.name; 60 item.namespaces = updated.namespaces; 61 loaderRef.current.setData(await services.clusters.update(item, 'name', 'namespaces')); 62 }} 63 title='GENERAL' 64 items={[ 65 { 66 title: 'SERVER', 67 view: cluster.server 68 }, 69 { 70 title: 'CREDENTIALS TYPE', 71 view: 72 (cluster.config.awsAuthConfig && `IAM AUTH (cluster name: ${cluster.config.awsAuthConfig.clusterName})`) || 73 (cluster.config.execProviderConfig && `External provider (command: ${cluster.config.execProviderConfig.command})`) || 74 'Token/Basic Auth' 75 }, 76 { 77 title: 'NAME', 78 view: cluster.name, 79 edit: formApi => <FormField formApi={formApi} field='name' component={Text} /> 80 }, 81 { 82 title: 'NAMESPACES', 83 view: ((cluster.namespaces || []).length === 0 && 'All namespaces') || cluster.namespaces.join(', '), 84 edit: formApi => <FormField formApi={formApi} field='namespaces' component={NamespacesEditor} /> 85 } 86 ]} 87 /> 88 <div className='white-box'> 89 <p>CONNECTION STATE</p> 90 <div className='white-box__details'> 91 <div className='row white-box__details-row'> 92 <div className='columns small-3'>STATUS:</div> 93 <div className='columns small-9'> 94 <ConnectionStateIcon state={cluster.info.connectionState} /> {cluster.info.connectionState.status} 95 </div> 96 </div> 97 <div className='row white-box__details-row'> 98 <div className='columns small-3'>VERSION:</div> 99 <div className='columns small-9'> {cluster.info.serverVersion}</div> 100 </div> 101 <div className='row white-box__details-row'> 102 <div className='columns small-3'>DETAILS:</div> 103 <div className='columns small-9'> {cluster.info.connectionState.message} </div> 104 </div> 105 <div className='row white-box__details-row'> 106 <div className='columns small-3'>MODIFIED AT:</div> 107 <div className='columns small-9'> 108 <Ticker> 109 {now => { 110 if (!cluster.info.connectionState.attemptedAt) { 111 return <span>Never (next refresh in few seconds)</span>; 112 } 113 const secondsBeforeRefresh = Math.round(Math.max(10 - now.diff(moment(cluster.info.connectionState.attemptedAt)) / 1000, 1)); 114 return ( 115 <React.Fragment> 116 <Timestamp date={cluster.info.connectionState.attemptedAt} /> (next refresh in {secondsBeforeRefresh} seconds) 117 </React.Fragment> 118 ); 119 }} 120 </Ticker> 121 </div> 122 </div> 123 </div> 124 </div> 125 126 <div className='white-box'> 127 <p>CACHE INFO</p> 128 <div className='white-box__details'> 129 <Ticker> 130 {() => ( 131 <div className='row white-box__details-row'> 132 <div className='columns small-3'>RE-SYNCHRONIZED:</div> 133 <div className='columns small-9'> 134 <Timestamp date={cluster.info.cacheInfo.lastCacheSyncTime} /> 135 </div> 136 </div> 137 )} 138 </Ticker> 139 <div className='row white-box__details-row'> 140 <div className='columns small-3'>APIs COUNT:</div> 141 <div className='columns small-9'> {cluster.info.cacheInfo.apisCount} </div> 142 </div> 143 <div className='row white-box__details-row'> 144 <div className='columns small-3'>RESOURCES COUNT:</div> 145 <div className='columns small-9'> {cluster.info.cacheInfo.resourcesCount} </div> 146 </div> 147 <div className='row white-box__details-row'> 148 <div className='columns small-3'>APPLICATIONS COUNT:</div> 149 <div className='columns small-9'> {cluster.info.applicationsCount} </div> 150 </div> 151 </div> 152 </div> 153 </div> 154 </Page> 155 )} 156 </DataLoader> 157 ); 158 };