Updated Sparky to add ECOMP functionality Browse, Specialized Search, BYOQ, and the...
[aai/sparky-fe.git] / src / app / model / modelSearch / components / ModelGallery.jsx
1 /*
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2021 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *       http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 import React, { Component } from 'react';
22 import Grid from 'react-bootstrap/lib/Grid';
23 import Row from 'react-bootstrap/lib/Row';
24 import Col from 'react-bootstrap/lib/Col';
25 import ModelCard from './ModelCard.jsx';
26 import Modal from 'react-bootstrap/lib/Modal';
27 import Button from 'react-bootstrap/lib/Button';
28 import BootstrapTable from 'react-bootstrap-table-next';
29 import {ExportExcel} from 'utils/ExportExcel.js';
30 import filterFactory, { textFilter, customFilter } from 'react-bootstrap-table2-filter';
31 //import overlayFactory from 'react-bootstrap-table2-overlay';
32 import OutputVisualization from 'generic-components/OutputVisualization.jsx';
33 import RelationshipList from './ModelTabularView.jsx';
34 import PropTypes from 'prop-types';
35 import Tabs from 'react-bootstrap/lib/Tabs';
36 import Tab from 'react-bootstrap/lib/Tab';
37 import commonApi from 'utils/CommonAPIService.js';
38 import {GlobalExtConstants} from 'utils/GlobalExtConstants.js';
39 import {GeneralCommonFunctions} from 'utils/GeneralCommonFunctions.js';
40 import Spinner from 'utils/SpinnerContainer.jsx';
41
42 let INVLIST = GlobalExtConstants.INVLIST;
43 let ENVIRONMENT = GlobalExtConstants.ENVIRONMENT;
44
45 /**
46  * This function will take all of the node objects and turn them into
47  * a ui grid of ModelCard components. This function is essentially a container
48  * for the ModelCards
49  * @param props
50  * @returns {*}
51  */
52 class AttributeFilter extends Component {
53   
54   constructor(props) {
55     super(props);
56     this.filter = this.filter.bind(this);
57     this.getValue = this.getValue.bind(this);
58     this.props = props;
59     this.state = {
60       filterText: '',
61       isPageChange: this.props.isPageChange,
62       columnValue : ''
63     };
64   }
65   getValue = () => {
66     return this.input.value;
67   }
68   setPlaceHolder = () => {
69     let filterText = '';
70     if(filterText === ''){
71       filterText = 'Enter ' + this.props.column.text;
72     }
73     return filterText;
74   }
75   setFilterValue = () =>{
76     let filterText = '';
77     var columnFilter = this.props.columnFilterList[this.props.nodeType][0];
78     for(var i=0;i<columnFilter.length;i++){
79       if(columnFilter[i][this.props.column.text] != undefined){
80         filterText = columnFilter[i][this.props.column.text];        
81       }
82     }    
83     return filterText;
84   }
85   filter = () => {
86     let txt=this.props.column.text;
87     let obj = {};
88     obj[txt] = this.getValue();
89     var columnFilterList = this.props.columnFilterList;
90     var columnFilter = columnFilterList[this.props.nodeType][0];    
91    for(var i=0;i<columnFilter.length;i++){
92       if(columnFilter[i][txt] != undefined){
93         columnFilter[i][txt] = this.getValue();
94         columnFilterList[this.props.nodeType] = [];
95         columnFilterList[this.props.nodeType].push(columnFilter);
96         this.props.handleOnFilter(columnFilterList,this.props.nodeType,this.props.columns,this.getValue(),this.props.aliasColumnList);        
97         this.props.onFilter(this.getValue());      
98       }
99     }
100   }
101   render() {
102     return (
103       <div>
104         
105           <input
106           key="input"
107           ref={ node => this.input = node }
108           type="text"
109             placeholder={this.setPlaceHolder()}
110            // value={this.setFilterValue}
111             onChange={this.filter}
112           />
113         
114       </div>
115     )
116   }
117 }
118
119 class ModelGallery extends Component {
120   constructor(props){
121     super(props);
122     this.props = props;
123     this.state = {
124       rerender: false,
125       expanded: [],
126       columnFilterList : {},
127       columnsList: {},
128       aliasColumnList: {},
129       nodeType: '',
130       disableFilter: true,
131       showEditNodeModal: false,
132       focusedNode: null,
133       isEditSuccess: false,
134       isWriteAllowed: sessionStorage.getItem(ENVIRONMENT + 'roles') && sessionStorage.getItem(ENVIRONMENT + 'roles').indexOf('ui_write') > -1,
135       editInputFields: []
136     }
137   }
138   componentWillMount() {
139     console.log('Model gallery component will mount****');
140   }
141   componentWillUnmount() {
142     console.log('Model Gallery component will unmount****');
143   }
144   handleOnExpand = (row, isExpand, rowIndex, e) => {
145     console.log('handleOnExpand single Row...',row.id);
146     if (isExpand) {
147       this.setState(() => ({
148         expanded: [...this.state.expanded,row.id]
149       }));
150     } else {
151       this.setState(() => ({
152         expanded: this.state.expanded.filter(x => x !== row.id)
153       }),function () { this.forceUpdate(); }.bind(this));
154       
155     }
156   }
157   handleOnExpandAll = (isExpand, rows, e) => {
158     console.log('handleOnExpandAll to expand all rows');
159     var expandArr = [];    
160     if (isExpand) {
161       for(var r=0; r < rows.length; r++){
162         expandArr.push(rows[r].id);
163       }     
164     }
165     this.setState(() => ({
166       expanded: expandArr
167     }),function () { this.forceUpdate(); }.bind(this));
168     
169   } 
170   handleOnFilter = (colFilterList,nodeType,columns,value,aliasColumnList) =>{
171     console.log('handleOnFilter to Re-render',colFilterList);
172     var applyState = true;
173     if(value === ''){
174       Object.keys(colFilterList).forEach(function(pkey){
175         var filterList = colFilterList[pkey][0];
176         for(var j in filterList){
177           Object.keys(filterList[j]).forEach(function(key){         
178             if(filterList[j][key] !== ''){
179               applyState = false;
180             }
181           });
182         }        
183       });
184     }else{
185       applyState = false;
186     }  
187     this.setState({columnFilterList : colFilterList,rerender:true,columnsList : columns,nodeType: nodeType, disableFilter: applyState,aliasColumnList: aliasColumnList});    
188   } 
189   generateRegexForDsl= (nodeType) =>{
190     var nodePatternwithProp = nodeType+"\\*\\{.*?\\}\\(.*?\\)[\\,|\\>|\\]|\\)]|"+nodeType+"\\*\\(.*?\\)\\{.*?\\}[\\,|\\>|\\]|\\)]|"+nodeType+"\\{.*?\\}\\(.*?\\)[\\,|\\>|\\]|\\)]|"+nodeType+"\\(.*?\\)\\{.*?\\}[\\,|\\>|\\]|\\)]|"+nodeType+"\\{.*?\\}[\\,|\\>|\\]|\\)]|"+nodeType+"\\*\\{.*?\\}[\\,|\\>|\\]|\\)]";
191                 return nodePatternwithProp;
192   }
193   /* Start Edit Node Modal Functions */
194   closeEditNodeModal = () =>{
195     this.setState({editErrMsg: null, editInfoMsg: null, showEditNodeModal:false});
196   }
197   submitEditNodeModal = () =>{
198     var payload = {"operations": []};
199     const settings = {
200       'NODESERVER': INVLIST.NODESERVER,
201       'PROXY': INVLIST.PROXY,
202       'PREFIX': INVLIST.PREFIX,
203       'VERSION': INVLIST.VERSION,
204       'USESTUBS': INVLIST.useStubs,
205       'APERTURE': INVLIST.APERTURE,
206       'APERTURE_SERVICENAME':INVLIST.APERTURE_SERVICENAME
207     };
208     let delimiter = '\/';
209     let start = 3;
210     if((this.state.focusedNode.url).indexOf("/aperture/v") > -1){
211         start = 4;
212     }
213     let tokens = (this.state.focusedNode.url).split(delimiter).slice(start);
214     let patchURL = tokens.join(delimiter);
215     var entry = {
216                     "action": "patch",
217                     "uri": patchURL,
218                     "body": {}
219                 };
220     let path = "bulk/single-transaction";
221     this.setState({editErrMsg: null, isPatchLoading: true});
222     for(var key in this.state.editInputFields){
223         if(this.state.editInputFields[key].isEdited){
224             if(this.state.editInputFields[key].newValue !== ""){
225                 entry.body[key] =  encodeURI(this.state.editInputFields[key].newValue);
226             }else{
227                 entry.body[key] = null;
228             }
229         }
230     }
231     payload.operations.push(entry);
232     console.log('ModelGallery: settings:' + JSON.stringify(settings));
233     console.log('ModelGallery: path:' + path);
234     console.log('ModelGallery: payload:' + JSON.stringify(payload));
235     commonApi(settings, path, 'POST', payload, 'SingleTransactionEdit', null, null, null, true)
236     .then(res => {
237             console.log('ModelGallery: Response', Object.keys(res.data));
238             if(res.status === 201 || res.status === 200){
239                 if(res.data["operation-responses"] && res.data["operation-responses"][0] && res.data["operation-responses"][0]["response-status-code"] === 200 ){
240                     this.setState({isEditSuccess: true, isPatchLoading: false, showEditNodeModal:false});
241                     GeneralCommonFunctions.scrollTo("editSuccessMessage");
242                 }else{
243                     this.triggerError(res.data);
244                 }
245             }else{
246                this.triggerError(res.data);
247             }
248         }, error=>{
249                 this.triggerError(error);
250         }).catch(error => {
251                 this.triggerError(error);
252     });
253   }
254   triggerError = (error) => {
255      console.error('[ModelGallery.jsx] error : ', JSON.stringify(error));
256      let errMsg = this.renderErrorMsg(error);
257      this.setState({
258            isPatchLoading: false,
259            isEditSuccess: false,
260            editErrMsg: errMsg
261        });
262   };
263   renderErrorMsg = (error) =>{
264       let errMsg='';
265         if (error.response) {
266             // The request was made and the server responded with a status code
267             // that falls out of the range of 2xx
268             console.log('[ModeGallery.jsx] error :', error.response);
269             if(error.response.status){
270                 errMsg += " Code: " + error.response.status;
271             }
272             if(error.response.data){
273                 errMsg += " - " + JSON.stringify(error.response.data);
274             }
275         } else if (error["requestError"]){
276             errMsg += JSON.stringify(error["requestError"]);
277         } else if (error.request) {
278             // The request was made but no response was received
279             // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
280             // http.ClientRequest in node.js
281             console.log(error.request);
282             errMsg += " - Request was made but no response received";
283         } else {
284             // Something happened in setting up the request that triggered an Error
285             console.log('Error', error.message);
286             errMsg += " - Unknown error occurred " + error.message;
287         }
288        return errMsg;
289   }
290   openEditNodeModal = (nodeKey) => {
291     console.log("ModelGallery :: openEditNodeModal called with " + nodeKey);
292     var focusedNode = null;
293     for (var i = 0; i < this.props.nodes.length && !focusedNode; i++){
294         if(nodeKey === this.props.nodes[i].url){
295             focusedNode = this.props.nodes[i];
296             break;
297         }
298     }
299     var editInputFields = [];
300     if(focusedNode){
301         var nodeType = focusedNode['node-type'];
302         focusedNode.allowedEditProps = [];
303         //call to check what props can be modified in oxm here;
304         focusedNode.allowedEditProps = GeneralCommonFunctions.getEditableAttributes(nodeType);
305         if(focusedNode.allowedEditProps.length > 0){
306             for (var key in focusedNode.allowedEditProps){
307                 var attr = focusedNode.allowedEditProps[key];
308                 editInputFields[attr] = {};
309                 editInputFields[attr].isEdited = false;
310                 if(focusedNode.properties[attr]){
311                     editInputFields[attr].oldValue = focusedNode.properties[attr];
312                     editInputFields[attr].newValue = focusedNode.properties[attr];
313                 }else{
314                     editInputFields[attr].oldValue = "";
315                     editInputFields[attr].newValue = "";
316                 }
317             }
318         }else{
319             this.setState({editInfoMsg: "This element cannot be edited, please contact an administrator if you need the ability to edit the attributes on this element."});
320         }
321     }else{
322         //could not find the node, this shouldn't happen
323         console.log("ModelGallery :: openEditNodeModal could not find " + nodeKey + " in this.props.nodes. This shouldn't happen.");
324     }
325
326     this.setState({showEditNodeModal:true, isEditSuccess: false, focusedNode: focusedNode, editInputFields: editInputFields});
327   }
328   handleInputChange(event) {
329     const target = event.target;
330     const value = target.type === 'checkbox' ? target.checked : target.value;
331     const name = target.name;
332     var editInputFields = this.state.editInputFields;
333     if(target.type === 'text'){
334         editInputFields[name].newValue = value;
335     }else if(target.type === 'checkbox'){
336         if(!value){
337             editInputFields[name].newValue =  editInputFields[name].oldValue,
338             editInputFields[name].isEdited = value
339         }else{
340             editInputFields[name].isEdited = value
341         }
342     }
343     this.setState({
344               editInputFields: editInputFields
345             });
346   }
347   /* End Edit Node Modal Functions */
348   render(){
349     let cards = null;
350     let tableColumns = [];
351     let tableValues = [];
352     let rowIndexValue = 0;
353     let svgWidth = window.outerWidth * 0.8;
354     let nodesList=[];
355     let tableColumnsList={};
356     let tableDataList={};
357     let columnFilter = [];
358     let columnFilterList = (this.state.rerender) ? this.state.columnFilterList : {};
359     let aliasColumnList=(this.state.rerender) ? this.state.aliasColumnList: {};
360     let aliasRegex=/\'(\s)as(\s)\'|\'as\'/ig;
361     console.log('Model gallery this.props>>>',this.props);
362     console.log('columnFilterList while rendering:',columnFilterList);
363     this.onTableFilterClick = (nodeType) => {
364       this.setState({rerender:false},function(){this.props.isTableFilterApply(this.state.columnFilterList,nodeType,this.state.columnsList,this.state.aliasColumnList);}.bind(this))
365     }
366     const expandRows = {
367       parentClassName: 'parent-expand-bar',
368       onlyOneExpanding: true,
369       renderer: (row, rowIndex) => (                    
370         <div>
371             <RelationshipList node={this.props.nodes[parseInt(row.id.split('_')[0])]}
372                 key={this.props.nodes[parseInt(row.id.split('_')[0])].id}
373                 nodeId={this.props.nodes[parseInt(row.id.split('_')[0])].id}
374                 nodeType={this.props.nodes[parseInt(row.id.split('_')[0])]['node-type']}
375                 nodeProps={this.props.nodes[parseInt(row.id.split('_')[0])].properties}
376                 nodeRelatives={this.props.nodes[parseInt(row.id.split('_')[0])]['related-to']}
377                 nodeUrl={this.props.nodes[parseInt(row.id.split('_')[0])].url}
378                 historyStackString={this.props.historyStackString}
379                 openHistoryModal={this.props.openHistoryModal}
380                 openEditNodeModal={this.openEditNodeModal}
381                 isWriteAllowed={this.state.isWriteAllowed}
382                 rowIndex={parseInt(row.id.split('_')[0])}
383                 enableRealTime={this.props.enableRealTime}
384                 aliasColumnList={this.props.tableFilterAliasColumns}
385           />
386         </div>
387       ),
388       showExpandColumn: true,
389       expandByColumnOnly: true,     
390       onExpandAll: this.handleOnExpandAll,
391       expanded: this.state.expanded,
392       onExpand: this.handleOnExpand
393
394     };
395     const rowEvents = {
396       onClick: (e, row, rowIndex) => {
397         //row index is usefull when single node type exist, for multiple node type use row id
398         rowIndexValue = parseInt(row.id.split('_')[0]);        
399       },
400       onMouseEnter: (e, row, rowIndex) => {
401         //row index is usefull when single node type exist, for multiple node type use row id
402         rowIndexValue = parseInt(row.id.split('_')[0]);
403       }
404     };
405     let aliasColumns=[]
406     
407     if(this.props.nodes && this.props.nodes[0] && this.props.nodes[0]['node-type'] && this.props.viewName === "CellLayout" ){
408         for(var n=0; n<this.props.nodes.length; n++){
409           let nodeType = this.props.nodes[n]['node-type'];
410           let nodeTypeProperties =[];
411           let aliasProperties=[];
412           let plainNodes ='';
413           let dslQuery = this.props.dslQuery + ',';
414           if(this.props.dslQuery){
415             var nodePatternwithProp = this.generateRegexForDsl(nodeType);
416             var nodeRegularExp = new RegExp(nodePatternwithProp, 'g');
417             plainNodes = dslQuery.match(nodeRegularExp);
418             console.log('plainNodes model Gallery>>>>>*',plainNodes);
419             if(plainNodes){
420               let propertiesPattern ="\\{.*?\\}"; 
421               var propRegularExp = new RegExp(propertiesPattern, 'g');
422               let nodeTypeProp = plainNodes[0].match(propRegularExp);
423               nodeTypeProp = nodeTypeProp[0].slice(1,-1).split(',');//.replace(/\'/g,'').toLowerCase().split(',');
424               for(var s=0;s<nodeTypeProp.length;s++){
425                 let nodeTypePropes=nodeTypeProp[s].match(aliasRegex);
426                 let alias='';
427                 let nprop='';
428                 if(nodeTypePropes){
429                   let nodeTypeSplit=nodeTypeProp[s].split(aliasRegex);
430                   nprop=nodeTypeSplit[0].replace(/\'/g,'');                  
431                   alias=nodeTypeSplit[nodeTypeSplit.length-1].replace(/\'/g,'');                 
432                 }else{
433                   nprop=nodeTypeProp[s].replace(/\'/g,'').toLowerCase();
434                 } 
435                 aliasProperties.push(alias);
436                 nodeTypeProperties.push(nprop);
437               }             
438             }  
439           }
440           if(nodesList.indexOf(nodeType) === -1){
441             tableColumns=[];
442             tableValues=[];
443             nodesList.push(nodeType);            
444             let tableColumnsBuilt = ExportExcel.buildAttrList(nodeType,[],'required');
445             if(this.props.dslQuery && plainNodes){
446               for(var z=0;z<tableColumnsBuilt.length;z++){ 
447                 let index= nodeTypeProperties.indexOf(tableColumnsBuilt[z].value.toLowerCase());         
448                 if(index !== -1){
449                   if(aliasProperties[index] !==''){
450                     let objAlias = {};
451                     objAlias[aliasProperties[index]]=nodeTypeProperties[index];
452                     aliasColumns.push(objAlias);
453                     tableColumnsBuilt[z].value=aliasProperties[index];                   
454                   }
455                   tableColumns.push(tableColumnsBuilt[z]);                                    
456                 }
457               }
458             }else{
459               tableColumns=tableColumnsBuilt;
460             }
461             console.log('after condition table columns>>>>',tableColumns);
462             tableColumns.push({value:'id'});
463             if(!columnFilterList[nodeType]){             
464               columnFilterList[nodeType] = [];
465               columnFilter = [];
466               for(var j = 0; j < tableColumns.length; j++){
467                 let txt = tableColumns[j].value;
468                 //if(!this.state.reRender && (!columnFilter[j] || (columnFilter[j] && columnFilter[j][txt] === undefined))){                
469                   let obj = {};
470                   obj[txt] = '';
471                   obj['description'] = tableColumns[j].description;
472                   columnFilter.push(obj);
473                 //}              
474               }
475               columnFilterList[nodeType].push(columnFilter); 
476             }
477             if(!aliasColumnList[nodeType]){   
478               aliasColumnList[nodeType]=[]; 
479               aliasColumnList[nodeType].push(aliasColumns);      
480             }                 
481             for(var j = 0; j < tableColumns.length; j++){                                         
482               if(j === tableColumns.length-1){
483                 tableColumns[j].dataField = 'id';
484                 tableColumns[j].hidden = true;
485                 tableColumns[j].text = tableColumns[j].value;
486               }else{
487                 tableColumns[j].dataField = tableColumns[j].value;
488                 tableColumns[j].text = tableColumns[j].value;
489                 tableColumns[j].headerAttrs= { title:tableColumns[j].description};
490                 tableColumns[j].ref=tableColumns[j].value;
491                 tableColumns[j].filter = customFilter();
492                 tableColumns[j].filterRenderer = (onFilter, column) => <AttributeFilter handleOnFilter= {this.handleOnFilter} onFilter={ onFilter } column={ column } isPageChange={this.props.isPageNumberChange} nodeType={nodeType} columnFilterList={columnFilterList} columns={tableColumnsList} aliasColumnList={aliasColumnList}/>;
493               }
494             }
495             tableColumnsList[nodeType] = tableColumns;
496             tableDataList[nodeType] = [];            
497             for(var m=0; m<this.props.nodes.length; m++){
498               let nodeTypeForData = this.props.nodes[m]['node-type'];
499               if(nodeTypeForData === nodeType){
500                 let propertiesOfNode = this.props.nodes[m].properties; 
501                 propertiesOfNode.id = m + '_' + nodeType + '_id';         
502                 tableValues.push(propertiesOfNode);     
503                 tableDataList[nodeType].push(tableValues);  
504               }
505             }
506           }                 
507         }
508     }else{
509       cards = this.props.nodes.map(node => {
510         return (
511            <Col key={node.id} lg={3} md={3} sm={6} xs={12}>
512                 <ModelCard
513                   key={node.id}
514                   nodeId={node.id}
515                   nodeType={node['node-type']}
516                   nodeProps={node.properties}
517                   nodeRelatives={node['related-to']}
518                   nodeUrl={node.url}
519                   historyStackString={this.props.historyStackString}
520                   openHistoryModal={this.props.openHistoryModal}
521                   openEditNodeModal={this.openEditNodeModal}
522                   isWriteAllowed={this.state.isWriteAllowed}
523                   enableRealTime={this.props.enableRealTime}
524                   aliasColumnList={this.props.tableFilterAliasColumns}/>
525             </Col>
526         );
527       });
528     }
529     let tabs=nodesList.map((nodeType,index) => {
530       return(
531         <Tab eventKey={nodeType} title={nodeType} key={nodeType}>
532           <BootstrapTable
533               id={nodeType}
534               keyField='id'
535               data={tableDataList[nodeType][0]}
536               columns={tableColumnsList[nodeType]}
537               filter={filterFactory()}
538               bordered={true}
539               columnFilter={true}
540               headerClasses='table-header-view'
541               expandRow={expandRows}
542               rowEvents={rowEvents}
543               bootstrap4 striped hover condensed
544           />
545         </Tab>
546       )
547     });
548     return (
549       <div>
550        <div className={'addPaddingTop alert alert-success ' +(this.state.isEditSuccess ? 'show' : 'hidden')} id="editSuccessMessage" role="alert">
551          Update made successfully to {this.state.focusedNode ? this.state.focusedNode.url : ""}. If you wish, you may check your update using a real-time mode query, it may take some time to reflect in analysis mode.
552        </div>
553            <div className='static-modal'>
554                         <Modal show={this.state.showEditNodeModal} onHide={this.closeEditNodeModal}>
555                                 <Modal.Header>
556                                         <Modal.Title>Edit Element</Modal.Title>
557                                 </Modal.Header>
558                                 <Modal.Body>
559                                     <Spinner loading={this.state.isPatchLoading}>
560                                         <div className={'addPaddingTop alert alert-danger ' +(this.state.editErrMsg && this.state.editErrMsg !== '' ? 'show' : 'hidden')} id="editErrorMessage" role="alert">
561                           An error occurred in editing the element. Please see details {this.state.editErrMsg}
562                         </div>
563                         <div className={'addPaddingTop alert alert-info ' +(this.state.editInfoMsg && this.state.editInfoMsg !== '' ? 'show' : 'hidden')} id="editNotAllowedMessage" role="alert">
564                           {this.state.editInfoMsg}
565                         </div>
566                                         <form>
567                                         {this.state.focusedNode && Object.keys(this.state.editInputFields).length > 0 && (this.state.focusedNode.allowedEditProps).sort().map((attr) => {
568                             return <div class="form-group row">
569                                        <div className="col-sm-3">
570                                             <label for={attr} class="col-form-label">{attr}</label>
571                                        </div>
572                                        <div class="col-sm-1">
573                                             <div className="checkbox">
574                                                <input type="checkbox" name={attr} checked={this.state.editInputFields[attr].isEdited} onChange={this.handleInputChange.bind(this)} />
575                                             </div>
576                                        </div>
577                                        <div class="col-sm-8">
578                                          <input type="text" class="form-control" id={attr} name={attr} disabled={!this.state.editInputFields[attr].isEdited} onChange={this.handleInputChange.bind(this)} value={this.state.editInputFields[attr].newValue}/>
579                                        </div>
580                                      </div>;
581
582                          })
583                         }
584                         </form>
585                     </Spinner>
586                                 </Modal.Body>
587                                 <Modal.Footer>
588                                         <Button onClick={this.closeEditNodeModal}>Close</Button>
589                                         <Button className={this.state.editInfoMsg && this.state.editInfoMsg !== '' ? 'hidden' : ''} onClick={this.submitEditNodeModal}>Submit</Button>
590                                 </Modal.Footer>
591                         </Modal>
592        </div>
593       {(() => {
594               if (this.props.viewName === "CellLayout" && tableValues.length > 0) {
595                 if(nodesList.length > 1){
596                   if(this.props.isTableFilterApply){
597                     return (
598                       <div className="addPaddingSide">
599                         <button type='button' className={(this.state.disableFilter)? 'btn btn-outline-secondary' : 'btn btn-primary'} disabled={this.state.disableFilter} onClick={() => {this.onTableFilterClick(nodesList)}} style={{float: 'right', margin: '2px'}}>Apply Filters (All)</button>
600                         <Tabs defaultActiveKey={nodesList[0]} id="multipleTabularView">
601                           {tabs}
602                         </Tabs>
603                       </div>
604                     )
605                   }else{
606                     return (
607                       <div className="addPaddingSide">
608                         <Tabs defaultActiveKey={nodesList[0]} id="multipleTabularView">
609                           {tabs}
610                         </Tabs>
611                       </div>
612                     )
613                   }                
614                 }else{
615                   if(this.props.isTableFilterApply){
616                     return(
617                       <div className="addPaddingSide">
618                         <button type='button' className={(this.state.disableFilter)? 'btn btn-outline-secondary' : 'btn btn-primary'} disabled={this.state.disableFilter} onClick={() => {this.onTableFilterClick(this.state.nodeType)}} style={{float: 'right', margin: '10px'}}>Apply Filters (All)</button>
619                         <BootstrapTable
620                           id='modelGallery'
621                           keyField='id'
622                           data={ tableValues }
623                           columns={ tableColumns }
624                           filter={ filterFactory() }
625                           bordered={ true }
626                           columnFilter={ true }
627                           headerClasses='table-header-view'
628                           expandRow={ expandRows }
629                           rowEvents={ rowEvents }
630                           bootstrap4 striped hover condensed
631                         />
632                       </div>
633                     )
634                   }else{
635                     return (
636                       <div className="addPaddingSide">
637                         <BootstrapTable
638                           id='modelGallery'
639                           keyField='id'
640                           data={ tableValues }
641                           columns={ tableColumns }
642                           filter={ filterFactory() }
643                           bordered={ true }
644                           columnFilter={ true }
645                           headerClasses='table-header-view'
646                           expandRow={ expandRows }
647                           rowEvents={ rowEvents }
648                           bootstrap4 striped hover condensed
649                         />
650                       </div>
651                     )
652                   }                  
653                 }
654               } else if (this.props.viewName === "CardLayout") {
655                 return (
656                   <Grid fluid={true}>
657                           <Row className='show-grid'>
658                             {cards}
659                           </Row>
660                   </Grid>
661                 )
662             }
663            })()}
664             <div className={this.props.viewName === "VisualLayout" ? 'show' : 'hidden'}>
665                 <OutputVisualization identifier="currentState" width={svgWidth} height="1200" overflow="scroll"/>
666             </div>
667       </div>
668     );
669   } 
670 };
671
672 export default ModelGallery;