2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
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';
42 let INVLIST = GlobalExtConstants.INVLIST;
43 let ENVIRONMENT = GlobalExtConstants.ENVIRONMENT;
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
52 class AttributeFilter extends Component {
56 this.filter = this.filter.bind(this);
57 this.getValue = this.getValue.bind(this);
61 isPageChange: this.props.isPageChange,
66 return this.input.value;
68 setPlaceHolder = () => {
70 if(filterText === ''){
71 filterText = 'Enter ' + this.props.column.text;
75 setFilterValue = () =>{
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];
86 let txt=this.props.column.text;
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());
107 ref={ node => this.input = node }
109 placeholder={this.setPlaceHolder()}
110 // value={this.setFilterValue}
111 onChange={this.filter}
119 class ModelGallery extends Component {
126 columnFilterList : {},
131 showEditNodeModal: false,
133 isEditSuccess: false,
134 isWriteAllowed: sessionStorage.getItem(ENVIRONMENT + 'roles') && sessionStorage.getItem(ENVIRONMENT + 'roles').indexOf('ui_write') > -1,
138 componentWillMount() {
139 console.log('Model gallery component will mount****');
141 componentWillUnmount() {
142 console.log('Model Gallery component will unmount****');
144 handleOnExpand = (row, isExpand, rowIndex, e) => {
145 console.log('handleOnExpand single Row...',row.id);
147 this.setState(() => ({
148 expanded: [...this.state.expanded,row.id]
151 this.setState(() => ({
152 expanded: this.state.expanded.filter(x => x !== row.id)
153 }),function () { this.forceUpdate(); }.bind(this));
157 handleOnExpandAll = (isExpand, rows, e) => {
158 console.log('handleOnExpandAll to expand all rows');
161 for(var r=0; r < rows.length; r++){
162 expandArr.push(rows[r].id);
165 this.setState(() => ({
167 }),function () { this.forceUpdate(); }.bind(this));
170 handleOnFilter = (colFilterList,nodeType,columns,value,aliasColumnList) =>{
171 console.log('handleOnFilter to Re-render',colFilterList);
172 var applyState = true;
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] !== ''){
187 this.setState({columnFilterList : colFilterList,rerender:true,columnsList : columns,nodeType: nodeType, disableFilter: applyState,aliasColumnList: aliasColumnList});
189 generateRegexForDsl= (nodeType) =>{
190 var nodePatternwithProp = nodeType+"\\*\\{.*?\\}\\(.*?\\)[\\,|\\>|\\]|\\)]|"+nodeType+"\\*\\(.*?\\)\\{.*?\\}[\\,|\\>|\\]|\\)]|"+nodeType+"\\{.*?\\}\\(.*?\\)[\\,|\\>|\\]|\\)]|"+nodeType+"\\(.*?\\)\\{.*?\\}[\\,|\\>|\\]|\\)]|"+nodeType+"\\{.*?\\}[\\,|\\>|\\]|\\)]|"+nodeType+"\\*\\{.*?\\}[\\,|\\>|\\]|\\)]";
191 return nodePatternwithProp;
193 /* Start Edit Node Modal Functions */
194 closeEditNodeModal = () =>{
195 this.setState({editErrMsg: null, editInfoMsg: null, showEditNodeModal:false});
197 submitEditNodeModal = () =>{
198 var payload = {"operations": []};
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
208 let delimiter = '\/';
210 if((this.state.focusedNode.url).indexOf("/aperture/v") > -1){
213 let tokens = (this.state.focusedNode.url).split(delimiter).slice(start);
214 let patchURL = tokens.join(delimiter);
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);
227 entry.body[key] = null;
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)
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");
243 this.triggerError(res.data);
246 this.triggerError(res.data);
249 this.triggerError(error);
251 this.triggerError(error);
254 triggerError = (error) => {
255 console.error('[ModelGallery.jsx] error : ', JSON.stringify(error));
256 let errMsg = this.renderErrorMsg(error);
258 isPatchLoading: false,
259 isEditSuccess: false,
263 renderErrorMsg = (error) =>{
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;
272 if(error.response.data){
273 errMsg += " - " + JSON.stringify(error.response.data);
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";
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;
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];
299 var editInputFields = [];
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];
314 editInputFields[attr].oldValue = "";
315 editInputFields[attr].newValue = "";
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."});
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.");
326 this.setState({showEditNodeModal:true, isEditSuccess: false, focusedNode: focusedNode, editInputFields: editInputFields});
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'){
337 editInputFields[name].newValue = editInputFields[name].oldValue,
338 editInputFields[name].isEdited = value
340 editInputFields[name].isEdited = value
344 editInputFields: editInputFields
347 /* End Edit Node Modal Functions */
350 let tableColumns = [];
351 let tableValues = [];
352 let rowIndexValue = 0;
353 let svgWidth = window.outerWidth * 0.8;
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))
367 parentClassName: 'parent-expand-bar',
368 onlyOneExpanding: true,
369 renderer: (row, rowIndex) => (
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}
388 showExpandColumn: true,
389 expandByColumnOnly: true,
390 onExpandAll: this.handleOnExpandAll,
391 expanded: this.state.expanded,
392 onExpand: this.handleOnExpand
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]);
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]);
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=[];
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);
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);
429 let nodeTypeSplit=nodeTypeProp[s].split(aliasRegex);
430 nprop=nodeTypeSplit[0].replace(/\'/g,'');
431 alias=nodeTypeSplit[nodeTypeSplit.length-1].replace(/\'/g,'');
433 nprop=nodeTypeProp[s].replace(/\'/g,'').toLowerCase();
435 aliasProperties.push(alias);
436 nodeTypeProperties.push(nprop);
440 if(nodesList.indexOf(nodeType) === -1){
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());
449 if(aliasProperties[index] !==''){
451 objAlias[aliasProperties[index]]=nodeTypeProperties[index];
452 aliasColumns.push(objAlias);
453 tableColumnsBuilt[z].value=aliasProperties[index];
455 tableColumns.push(tableColumnsBuilt[z]);
459 tableColumns=tableColumnsBuilt;
461 console.log('after condition table columns>>>>',tableColumns);
462 tableColumns.push({value:'id'});
463 if(!columnFilterList[nodeType]){
464 columnFilterList[nodeType] = [];
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))){
471 obj['description'] = tableColumns[j].description;
472 columnFilter.push(obj);
475 columnFilterList[nodeType].push(columnFilter);
477 if(!aliasColumnList[nodeType]){
478 aliasColumnList[nodeType]=[];
479 aliasColumnList[nodeType].push(aliasColumns);
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;
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}/>;
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);
509 cards = this.props.nodes.map(node => {
511 <Col key={node.id} lg={3} md={3} sm={6} xs={12}>
515 nodeType={node['node-type']}
516 nodeProps={node.properties}
517 nodeRelatives={node['related-to']}
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}/>
529 let tabs=nodesList.map((nodeType,index) => {
531 <Tab eventKey={nodeType} title={nodeType} key={nodeType}>
535 data={tableDataList[nodeType][0]}
536 columns={tableColumnsList[nodeType]}
537 filter={filterFactory()}
540 headerClasses='table-header-view'
541 expandRow={expandRows}
542 rowEvents={rowEvents}
543 bootstrap4 striped hover condensed
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.
553 <div className='static-modal'>
554 <Modal show={this.state.showEditNodeModal} onHide={this.closeEditNodeModal}>
556 <Modal.Title>Edit Element</Modal.Title>
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}
563 <div className={'addPaddingTop alert alert-info ' +(this.state.editInfoMsg && this.state.editInfoMsg !== '' ? 'show' : 'hidden')} id="editNotAllowedMessage" role="alert">
564 {this.state.editInfoMsg}
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>
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)} />
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}/>
588 <Button onClick={this.closeEditNodeModal}>Close</Button>
589 <Button className={this.state.editInfoMsg && this.state.editInfoMsg !== '' ? 'hidden' : ''} onClick={this.submitEditNodeModal}>Submit</Button>
594 if (this.props.viewName === "CellLayout" && tableValues.length > 0) {
595 if(nodesList.length > 1){
596 if(this.props.isTableFilterApply){
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">
607 <div className="addPaddingSide">
608 <Tabs defaultActiveKey={nodesList[0]} id="multipleTabularView">
615 if(this.props.isTableFilterApply){
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>
623 columns={ tableColumns }
624 filter={ filterFactory() }
626 columnFilter={ true }
627 headerClasses='table-header-view'
628 expandRow={ expandRows }
629 rowEvents={ rowEvents }
630 bootstrap4 striped hover condensed
636 <div className="addPaddingSide">
641 columns={ tableColumns }
642 filter={ filterFactory() }
644 columnFilter={ true }
645 headerClasses='table-header-view'
646 expandRow={ expandRows }
647 rowEvents={ rowEvents }
648 bootstrap4 striped hover condensed
654 } else if (this.props.viewName === "CardLayout") {
657 <Row className='show-grid'>
664 <div className={this.props.viewName === "VisualLayout" ? 'show' : 'hidden'}>
665 <OutputVisualization identifier="currentState" width={svgWidth} height="1200" overflow="scroll"/>
672 export default ModelGallery;