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