github.com/argoproj/argo-cd/v2@v2.10.9/ui/src/app/applications/components/application-details/application-resource-list.tsx (about) 1 import {DropDown} from 'argo-ui'; 2 import * as React from 'react'; 3 import * as classNames from 'classnames'; 4 import * as models from '../../../shared/models'; 5 import {ResourceIcon} from '../resource-icon'; 6 import {ResourceLabel} from '../resource-label'; 7 import {ComparisonStatusIcon, HealthStatusIcon, nodeKey, createdOrNodeKey} from '../utils'; 8 import {Consumer} from '../../../shared/context'; 9 import * as _ from 'lodash'; 10 import Moment from 'react-moment'; 11 import {format} from 'date-fns'; 12 import {ResourceNode, ResourceRef} from '../../../shared/models'; 13 14 export const ApplicationResourceList = ({ 15 resources, 16 onNodeClick, 17 nodeMenu, 18 tree 19 }: { 20 resources: models.ResourceStatus[]; 21 onNodeClick?: (fullName: string) => any; 22 nodeMenu?: (node: models.ResourceNode) => React.ReactNode; 23 tree?: models.ApplicationTree; 24 }) => { 25 function getResNode(nodes: ResourceNode[], nodeId: string): models.ResourceNode { 26 for (const node of nodes) { 27 if (nodeKey(node) === nodeId) { 28 return node; 29 } 30 } 31 return null; 32 } 33 const parentNode = ((resources || []).length > 0 && (getResNode(tree.nodes, nodeKey(resources[0])) as ResourceNode)?.parentRefs?.[0]) || ({} as ResourceRef); 34 const searchParams = new URLSearchParams(window.location.search); 35 const view = searchParams.get('view'); 36 37 const ParentRefDetails = () => { 38 return Object.keys(parentNode).length > 0 ? ( 39 <div className='resource-parent-node-info-title'> 40 <div>Parent Node Info</div> 41 <div className='resource-parent-node-info-title__label'> 42 <div>Name:</div> 43 <div>{parentNode?.name}</div> 44 </div> 45 <div className='resource-parent-node-info-title__label'> 46 <div>Kind:</div> 47 <div>{parentNode?.kind}</div> 48 </div> 49 </div> 50 ) : ( 51 <div /> 52 ); 53 }; 54 return ( 55 <div> 56 {/* Display only when the view is set to or network */} 57 {(view === 'tree' || view === 'network') && ( 58 <div className='resource-details__header' style={{paddingTop: '20px'}}> 59 <ParentRefDetails /> 60 </div> 61 )} 62 <div className='argo-table-list argo-table-list--clickable'> 63 <div className='argo-table-list__head'> 64 <div className='row'> 65 <div className='columns small-1 xxxlarge-1' /> 66 <div className='columns small-2 xxxlarge-1'>NAME</div> 67 <div className='columns small-1 xxxlarge-1'>GROUP/KIND</div> 68 <div className='columns small-1 xxxlarge-1'>SYNC ORDER</div> 69 <div className='columns small-2 xxxlarge-1'>NAMESPACE</div> 70 {(parentNode.kind === 'Rollout' || parentNode.kind === 'Deployment') && <div className='columns small-1 xxxlarge-1'>REVISION</div>} 71 <div className='columns small-2 xxxlarge-1'>CREATED AT</div> 72 <div className='columns small-2 xxxlarge-1'>STATUS</div> 73 </div> 74 </div> 75 {resources 76 .sort((first, second) => -createdOrNodeKey(first).localeCompare(createdOrNodeKey(second))) 77 .map(res => ( 78 <div 79 key={nodeKey(res)} 80 className={classNames('argo-table-list__row', { 81 'application-resource-tree__node--orphaned': res.orphaned 82 })} 83 onClick={() => onNodeClick(nodeKey(res))}> 84 <div className='row'> 85 <div className='columns small-1 xxxlarge-1'> 86 <div className='application-details__resource-icon'> 87 <ResourceIcon kind={res.kind} /> 88 <br /> 89 <div>{ResourceLabel({kind: res.kind})}</div> 90 </div> 91 </div> 92 <div className='columns small-2 xxxlarge-1'> 93 {res.name} 94 {res.kind === 'Application' && ( 95 <Consumer> 96 {ctx => ( 97 <span className='application-details__external_link'> 98 <a 99 href={ctx.baseHref + 'applications/' + res.namespace + '/' + res.name} 100 onClick={e => e.stopPropagation()} 101 title='Open application'> 102 <i className='fa fa-external-link-alt' /> 103 </a> 104 </span> 105 )} 106 </Consumer> 107 )} 108 </div> 109 <div className='columns small-1 xxxlarge-1'>{[res.group, res.kind].filter(item => !!item).join('/')}</div> 110 <div className='columns small-1 xxxlarge-1'>{res.syncWave || '-'}</div> 111 <div className='columns small-2 xxxlarge-1'>{res.namespace}</div> 112 {res.kind === 'ReplicaSet' && 113 ((getResNode(tree.nodes, nodeKey(res)) as ResourceNode).info || []) 114 .filter(tag => !tag.name.includes('Node')) 115 .slice(0, 4) 116 .map((tag, i) => { 117 return ( 118 <div key={i} className='columns small-1 xxxlarge-1'> 119 {tag?.value?.split(':')[1] || '-'} 120 </div> 121 ); 122 })} 123 124 <div className='columns small-2 xxxlarge-1'> 125 {res.createdAt && ( 126 <span> 127 <Moment fromNow={true} ago={true}> 128 {res.createdAt} 129 </Moment> 130 ago {format(new Date(res.createdAt), 'MM/dd/yy')} 131 </span> 132 )} 133 </div> 134 <div className='columns small-2 xxxlarge-1'> 135 {res.health && ( 136 <React.Fragment> 137 <HealthStatusIcon state={res.health} /> {res.health.status} 138 </React.Fragment> 139 )} 140 {res.status && <ComparisonStatusIcon status={res.status} resource={res} label={true} />} 141 {res.hook && <i title='Resource lifecycle hook' className='fa fa-anchor' />} 142 <div className='application-details__node-menu'> 143 <DropDown 144 isMenu={true} 145 anchor={() => ( 146 <button className='argo-button argo-button--light argo-button--lg argo-button--short'> 147 <i className='fa fa-ellipsis-v' /> 148 </button> 149 )}> 150 {nodeMenu({ 151 name: res.name, 152 version: res.version, 153 kind: res.kind, 154 namespace: res.namespace, 155 group: res.group, 156 info: null, 157 uid: '', 158 resourceVersion: null, 159 parentRefs: [] 160 })} 161 </DropDown> 162 </div> 163 </div> 164 </div> 165 </div> 166 ))} 167 </div> 168 </div> 169 ); 170 };