blob: 7e2dda8d475b8f2aad44c3bcfaedebb3d4d018c4 [file] [log] [blame]
Michael Landoefa037d2017-02-19 12:57:33 +02001import React from 'react';
2import FontAwesome from 'react-fontawesome';
3import classNames from 'classnames';
4import Collapse from 'react-bootstrap/lib/Collapse.js';
5
6import i18n from 'nfvo-utils/i18n/i18n.js';
7import {nodeTypes, mouseActions} from './AttachmentsConstants';
8
9const typeToIcon = Object.freeze({
10 heat: 'building-o',
11 volume: 'database',
12 network: 'cloud',
13 artifact: 'gear',
14 env: 'server',
15 other: 'cube'
16});
17
18const leftPanelWidth = 250;
19
20class SoftwareProductAttachmentsView extends React.Component {
21
22 static propTypes = {
23 attachmentsTree: React.PropTypes.object.isRequired
24 };
25 state = {
26 treeWidth: '400',
27 };
28
29 render() {
30 let {attachmentsTree, errorList = []} = this.props;
31
32 let {treeWidth} = this.state;
33 return (
34 <div className='software-product-attachments'>
35 <div className='software-product-view'>
36 <div className='software-product-landing-view-right-side'>
37 <div className='software-product-attachments-main'>
38 <div className='software-product-attachments-tree' style={{'width' : treeWidth + 'px'}}>
39 <div className='tree-wrapper'>
40 {
41 attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind]))
42 }
43 </div>
44 </div>
45 <div className='software-product-attachments-separator' onMouseDown={e => this.onChangeTreeWidth(e)} />
46 <div className='software-product-attachments-error-list'>
47 {errorList.length ? this.renderErrorList(errorList) : <div className='no-errors'>{attachmentsTree.children ?
48 i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }</div>}
49 </div>
50 </div>
51 </div>
52 </div>
53 </div>
54 );
55 }
56
57
58
59 renderNode(node, path) {
60 let isFolder = node.children && node.children.length > 0;
61 let {onSelectNode} = this.props;
62 return (
63 <div key={node.name} className='tree-block-inside'>
64 {
65 <div onDoubleClick={() => this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}>
66 {
67 isFolder &&
68 <div onClick={() => this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}>
69 <FontAwesome name='caret-down'/>
70 </div>
71 }
72 {
73
74 <span className='tree-node-icon'>
75 <FontAwesome name={typeToIcon[node.type]}/>
76 </span>
77 }
78 {
79
80 <span onClick={() => onSelectNode(node.name)} className={this.getTreeTextClassName(node)}>
81 {node.name}
82 </span>
83 }
84 </div>
85 }
86 {
87 isFolder &&
88 <Collapse in={node.expanded}>
89 <div className='tree-node-children'>
90 {
91 node.children.map((child, ind) => this.renderNode(child, [...path, ind]))
92 }
93 </div>
94 </Collapse>
95 }
96 </div>
97 );
98 }
99
100 createErrorList(errorList, node, parent) {
101 if (node.errors) {
102 node.errors.forEach(error => errorList.push({
103 error,
104 name: node.name,
105 parentName: parent.name,
106 type: node.type
107 }));
108 }
109 if (node.children && node.children.length) {
110 node.children.map((child) => this.createErrorList(errorList, child, node));
111 }
112 }
113
114 renderErrorList(errors) {
115 let prevError = {};
116 let {selectedNode} = this.props;
117 return errors.map(error => {
118 let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName;
119 prevError = error;
120
121 return (
122 <div
123 key={error.name + error.errorMessage + error.parentName}
124
125 onClick={() => this.selectNode(error.name)}
126 className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}>
127 <span className={classNames('error-item-file-type', {'strong': !isSameNodeError})}>
128 {
129 error.hasParent ?
130 i18n('{type} {name} in {parentName}: ', {
131 type: nodeTypes[error.type],
132 name: error.name,
133 parentName: error.parentName
134 }) :
135 i18n('{type} {name}: ', {
136 type: nodeTypes[error.type],
137 name: error.name
138 })
139 }
140 </span>
141 <span className={`error-item-file-type ${error.errorLevel}`}> {error.errorMessage} </span>
142 </div>
143 );
144 });
145 }
146
147 selectNode(currentSelectedNode) {
148 let {onUnselectNode, onSelectNode, selectedNode} = this.props;
149 if (currentSelectedNode !== selectedNode) {
150 onSelectNode(currentSelectedNode);
151 }else{
152 onUnselectNode();
153 }
154
155 }
156
157 getTreeRowClassName(name) {
158 let {hoveredNode, selectedNode} = this.props;
159 return classNames({
160 'tree-node-row': true,
161 'tree-node-selected': name === hoveredNode,
162 'tree-node-clicked': name === selectedNode
163 });
164 }
165
166 getTreeTextClassName(node) {
167 let {selectedNode} = this.props;
168 return classNames({
169 'tree-element-text': true,
170 'error-status': node.errors,
171 'error-status-selected': node.name === selectedNode
172 });
173 }
174
175 onChangeTreeWidth(e) {
176 if (e.button === mouseActions.MOUSE_BUTTON_CLICK) {
177 let onMouseMove = (e) => {
178 this.setState({treeWidth: e.clientX - leftPanelWidth});
179 };
180 let onMouseUp = () => {
181 document.removeEventListener('mousemove', onMouseMove);
182 document.removeEventListener('mouseup', onMouseUp);
183 };
184 document.addEventListener('mousemove', onMouseMove);
185 document.addEventListener('mouseup', onMouseUp);
186 }
187 }
188}
189
190export default SoftwareProductAttachmentsView;