Updated Sparky to add ECOMP functionality Browse, Specialized Search, BYOQ, and the...
[aai/sparky-fe.git] / src / app / customQuery / CustomQuery.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
23 import Select from 'react-select';
24 import Button from 'react-bootstrap/lib/Button';
25 import Row from 'react-bootstrap/lib/Row';
26 import Col from 'react-bootstrap/lib/Col';
27 import Grid from 'react-bootstrap/lib/Grid';
28 import PanelGroup from 'react-bootstrap/lib/PanelGroup';
29 import Panel from 'react-bootstrap/lib/Panel';
30 import Label from 'react-bootstrap/lib/Label';
31 import Collapse from 'react-bootstrap/lib/Collapse';
32 import moment from "moment";
33 import DatePicker from 'react-datepicker';
34 import Modal from 'react-bootstrap/lib/Modal';
35 import {ExportExcel} from 'utils/ExportExcel.js';
36 import commonApi from 'utils/CommonAPIService.js';
37 import {GlobalExtConstants} from 'utils/GlobalExtConstants.js';
38 import Spinner from 'utils/SpinnerContainer.jsx';
39 import ModelGallery from 'app/model/modelSearch/components/ModelGallery.jsx';
40 import ModelCard from 'app/model/modelSearch/components/ModelCard.jsx';
41 import OutputToggle from 'generic-components/OutputToggle.jsx';
42 import Pagination from 'react-js-pagination';
43 import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
44 import Tooltip from 'react-bootstrap/lib/Tooltip';
45 import {Visualization} from 'generic-components/OutputVisualization.jsx';
46 import DownloadRangeModel from 'generic-components/DownloadRangeModel.jsx';
47
48
49
50 let PAGINATION_CONSTANT = GlobalExtConstants.PAGINATION_CONSTANT;
51 let INVLIST = GlobalExtConstants.INVLIST;
52 let CUSTOMQUERYLIST = GlobalExtConstants.CUSTOMQUERYLIST;
53 let generateExcels = ExportExcel.generateExcels;
54 let DOWNLOAD_ALL = GlobalExtConstants.DOWNLOAD_ALL;
55 let DOWNLOAD_TOOLTIP = GlobalExtConstants.DOWNLOAD_TOOLTIP;
56
57
58 let dropdownList = null;
59 let header = null;
60
61 const inputClasses = ['form-control'];
62
63 class CustomQuery extends Component {
64   nodeResults = '';
65   typeOfCall = true;
66   downloadTooltip = DOWNLOAD_TOOLTIP;
67   downloadAllTooltip = 'Downloads First ' + DOWNLOAD_ALL + ' Results';
68   downloadRangeTooltip= 'Download Results By Custom Range Selection';
69   historyStackString = '';
70   state = {
71     queryPlaceHolder: 'Please Select a Query',
72     placeholder: 'Please Select a Query',
73     reqPropPlaceholder: 'Please Enter Required Query or Property',
74     optionalPropPlaceholder: 'Please Enter Optional Query or Property',
75     displayReqProps: 'hidden',
76     startNode: '',
77     reqProps: '',
78     optionalProps: '',
79     disableInputs: true,
80     query: '',
81     showText: false,
82     description: '',
83     additionalInfo: '',
84     displayDescription: 'hidden',
85     customQueryOptions: null,
86     formIsValid: false,
87     nodes: [],
88     displayNodes: 'hidden',
89     multipleNodes: '',
90     isLoading: false,
91     isInitialLoad: true,
92     activePage: 1,
93     totalResults: 0,
94     showPagination: false,
95     showResults: false,
96     errorResults:false,
97     errorMessage: '',
98     noResults: false,
99     displayOptionalProps: 'hidden',
100     showHistoryModal:false,
101     startDate: moment(),
102     currentPayload: null,
103     viewName: localStorage.getItem(GlobalExtConstants.ENVIRONMENT + '_' + sessionStorage.getItem(GlobalExtConstants.ENVIRONMENT + 'userId') + '_viewPreference')  || 'CardLayout',
104     res: null,
105     visualAddition: false,
106     nodeDisplay: '',
107     showNodeModal: false,
108     focusedNode: {},
109     historyType: 'cq',
110     focusedNodeUri: 0,
111     focusedNodeType: '',
112     historyParams: '',
113     showModelOptions: false,
114     enableCalendar: true,
115     resetColumnFilters: true,
116     isPageNumberChange: false,
117     totalPages: 0,
118     pageRange: 1,
119     showDownloadResultsModal: false,
120     errorDownloadResults:false,
121     downloadErrorMsg: '',      
122     enableModelBusyFeedback:false,
123     downloadCount:DOWNLOAD_ALL,
124     defaultViewName: localStorage.getItem(GlobalExtConstants.ENVIRONMENT + '_' + sessionStorage.getItem(GlobalExtConstants.ENVIRONMENT + 'userId') + '_viewPreference')  || 'CardLayout'
125   }
126
127   componentWillMount () {
128     console.log('componentWillMount');
129     sessionStorage.setItem(GlobalExtConstants.ENVIRONMENT + 'ENABLE_ANALYSIS', false);
130     if(!this.props.location.historyStackString){
131       this.props.location.historyStackString = this.props.location.pathname + ',,Origin||';
132     }else{
133       this.historyStackString = this.props.location.historyStackString;
134     }
135     let queryList = Object.values(CUSTOMQUERYLIST.CUSTOMQUERYLIST);
136     dropdownList = queryList.sort(function (filter1, filter2) {
137       if (filter1.value < filter2.value) {
138         return -1;
139       } else if (filter1.value > filter2.value) {
140         return 1;
141       } else {
142         return 0;
143       }
144     });
145   };
146   onInputChangeHandler (){
147     console.log('onInputChangeHandler of select-Clear');
148     this.setState({ 
149         placeholder: 'Please Select A Query',
150         queryPlaceHolder: 'Please Select A Query', query: '',
151         reqPropPlaceholder: 'Please Enter Required Query or Property',
152         optionalPropPlaceholder: 'Please Enter Optional Query or Property',
153         startNode: '',
154         reqProps: '', 
155         optionalProps: '',
156         disableInputs: true,
157         displayDescription: 'hidden',
158         nodes: [],
159         displayNodes: 'hidden',
160         multipleNodes: '',
161         isLoading: false,
162         displayOptionalProps: 'hidden',
163         formIsValid: false,
164         showPagination: false,
165         showResults: false,
166         errorResults: false,
167         errorMessage: '',
168         noResults: false,
169         isInitialLoad: true
170       });
171   }
172
173   updateSelectedHandler = (selectedOption) => {
174
175     const value = selectedOption === null ? 'Please Select A Query' : selectedOption.value
176     if(selectedOption){
177       console.log('updateSelectedHandler.reqPropPlaceholder: ' + selectedOption.reqPropPlaceholder);
178       console.log('updateSelectedHandler.value: ' + selectedOption.value);
179       this.setState({ placeholder: selectedOption.placeholder });
180       this.setState({ displayReqProps: selectedOption.reqPropPlaceholder ? 'show' : 'hidden' });
181       this.setState({ displayOptionalProps: selectedOption.optionalPropPlaceholder ? 'show' : 'hidden'});
182       this.setState({ reqPropPlaceholder: selectedOption.reqPropPlaceholder });
183       this.setState({ optionalPropPlaceholder: selectedOption.optionalPropPlaceholder});
184       this.setState({ disableInputs: false });
185       this.setState({ query: value });
186       this.setState({ description: selectedOption.description.summary });
187       this.setState({ additionalInfo: selectedOption.description.additionalInfo });
188       this.setState({ displayDescription: 'show' });
189       this.setState({ startNode: ''});
190       this.setState({ reqProps: ''});
191       this.setState({ optionalProps: ''});
192       this.setState({ queryPlaceHolder: value});
193     }else{
194        /*this.setState({ queryPlaceHolder: value, query: '',
195           reqPropPlaceholder: this.state.reqPropPlaceholder,optionalPropPlaceholder: this.state.optionalPropPlaceholder,
196           startNode: '',reqProps: '', optionalProps: ''});*/
197           this.onInputChangeHandler();
198     }    
199   }
200   checkValidity() {
201     let isValid = true;
202     isValid = this.state.startNode.trim().length > 1 && isValid;
203     if (this.state.reqPropPlaceholder) {
204       console.log('Checking if reqProps is on state...');
205       isValid = this.state.reqProps.length > 1 && isValid;
206     }
207     return isValid;
208   }
209
210   inputChangeHandler = (event) => {
211     const target = event.target;
212     const value = target.value;
213     const name = target.name;
214     console.log('inputChangeHandler.value: ' + value);
215     console.log('inputChangeHandler.name: ' + name);
216     let formIsValid = null;
217     this.setState(
218       { [name]: value },
219       function () {
220         console.log('startNode:' + this.state.startNode);
221         console.log('reqProps:' + this.state.reqProps);
222         formIsValid = this.checkValidity();
223         console.log('formIsValid:' + formIsValid);
224         this.setState({ formIsValid: formIsValid });
225       });
226   }
227
228   onAdditem = (event) => {
229     //window.alert('onAddItem clicked');
230     event.preventDefault();
231     this.nodeResults = '';
232     this.typeOfCall = true;
233     this.formQueryString();
234   };
235   formPayload = () => {
236     var startNode = this.state.startNode;
237     startNode.trim();
238     console.log('startNode.trim().length: ' + startNode.trim().length);
239     var queryName = this.state.query;
240     var reqProps = this.state.reqProps;
241     var optionalProps = this.state.optionalProps;
242     console.log('start Node: ' + startNode);
243     console.log('query name: ' + queryName);
244     console.log('req props: ' + reqProps);
245     console.log('Optional props: ' + optionalProps);
246     let payload = null;
247
248     if(queryName === 'node-fromURI'){
249         payload = {
250             start: startNode
251         };
252     } else if ( reqProps === '' && optionalProps === '' ){
253       payload = {
254         start: startNode,
255         query: 'query/' + queryName
256       };
257     } else if(reqProps !== ''){
258       reqProps = '?' + reqProps;
259       payload = {
260         start: startNode,
261         query: 'query/' + queryName + reqProps
262       };
263     }
264     if(optionalProps !== ''){
265       if(reqProps === ''){
266         optionalProps = '?' + optionalProps;
267       }else{
268         optionalProps = reqProps + optionalProps;
269       }
270       payload = {
271         start: startNode,
272         query: 'query/' + queryName + optionalProps
273       };
274     }
275     return payload
276   }
277   formQueryString = () =>{
278     let payload = this.formPayload();
279
280     console.log('payload>>>>>' + payload.toString());
281     const settings = {
282       'NODESERVER': INVLIST.NODESERVER,
283       'PROXY': INVLIST.PROXY,
284       'PREFIX': INVLIST.PREFIX,
285       'VERSION': INVLIST.VERSION,
286       'USESTUBS': INVLIST.useStubs
287
288     };
289     let queryStr = '';
290     if(this.typeOfCall){
291       queryStr = 'query?format=simple&resultIndex='
292                    + this.state.activePage + '&resultSize=' + PAGINATION_CONSTANT.RESULTS_PER_PAGE;
293       this.setState({isLoading: true,nodes: [], currentPayload:payload});
294     }else{
295       let pagerange=this.state.pageRange.toString();
296       pagerange=pagerange.split('-');
297       if(pagerange.length > 1){
298         queryStr = 'query?format=simple&resultIndex=' + parseInt(pagerange[0]) + '&resultSize=' + PAGINATION_CONSTANT.RESULTS_PER_PAGE + '&resultRangeEnd=' + parseInt(pagerange[1]);
299       }else{
300         queryStr = 'query?format=simple&resultIndex=1&resultSize=' + parseInt(pagerange);
301       }
302       this.setState({enableModelBusyFeedback: true, currentPayload:payload});
303     }
304         this.getNodes(settings, queryStr, 'PUT', payload);
305   }
306
307
308   getNodes(settings, path, httpMethodType, payload) {
309     commonApi(settings, path, httpMethodType, payload, 'CQDefault')
310       .then(res => {
311         console.log(Object.keys(res.data));
312         this.processData(res);
313         if(this.state.nodes.length > 0 && this.state.visualAddition){
314             Visualization.chart('currentState' , [], [], this.state.res.data, this);
315         }
316         console.log('res:' + res);
317       }, error => {
318           console.log(JSON.stringify(error));
319           if(this.typeOfCall){
320             this.triggerError(error);
321           }else{
322             let errMsg = this.renderErrorMsg(error);
323             this.setState({ isLoading: false,errorDownloadResults:true,downloadErrorMsg:errMsg,enableModelBusyFeedback:false});       
324              }          
325       }).catch(error =>
326       {
327         console.log(JSON.stringify(error));        
328         if(this.typeOfCall){
329                                         this.triggerError(error);
330                                 }else{
331                                         let errMsg = this.renderErrorMsg(error);
332                                         this.setState({ isLoading: false,errorDownloadResults:true,downloadErrorMsg:errMsg,enableModelBusyFeedback:false});       
333                 }
334       });
335
336     this.isEnabled = [];
337   }
338
339   triggerError = (error) =>{
340     // this.processData('');
341      this.setState({isLoading: false,
342        totalResults: 0,
343        showPagination: false,
344        showResults: false,
345        isInitialLoad: false,
346        errorResults:true,
347        noResults: true
348      });
349      this.downloadAllTooltip = 'Downloads First ' + DOWNLOAD_ALL + ' Results';
350      let errMsg = this.renderErrorMsg(error);
351      this.setState({errorMessage:errMsg});     
352   }
353   renderErrorMsg = (error) =>{
354      let errMsg='';
355      if (error.response) {
356        // The request was made and the server responded with a status code
357        // that falls out of the range of 2xx
358        console.log(error.response.data);
359        console.log(error.response.status);
360        console.log(error.response.headers);
361        if(error.response.status){
362            errMsg += " Code: " + error.response.status;
363        }
364            if(error.response.data){
365            errMsg += " - " + JSON.stringify(error.response.data);
366        }
367      } else if (error.request) {
368        // The request was made but no response was received
369        // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
370        // http.ClientRequest in node.js
371        console.log(error.request);
372        errMsg += " - Request was made but no response received";
373      } else {
374        // Something happened in setting up the request that triggered an Error
375        console.log('Error', error.message);
376        errMsg += " - Unknown error occurred " + error.message;
377      }     
378      console.log(error.config);
379      return errMsg;
380   }
381
382   processData(resp) {
383     //data = CustomQueryResponse;
384     console.log( 'in Custom Query component file DATA: ' + JSON.stringify(resp.data));
385     if(this.typeOfCall){
386       if(resp.data && resp.data.results) {
387         this.setState(
388           { res: resp,
389             nodes: resp.data.results},
390           function () {
391             console.log('how many nodes? ', this.state.nodes.length);
392             this.setState({ multipleNodes: this.state.nodes.length > 1 ? 'col-lg-3 col-xl-3' : '' });
393           });
394       }
395       let totalResults = 0;
396       let downloadCount = DOWNLOAD_ALL;  
397       if(resp.headers){
398           totalResults = parseInt(resp.headers['total-results']);
399           if(totalResults > DOWNLOAD_ALL){
400             this.downloadAllTooltip = DOWNLOAD_ALL + ' results out of '+ totalResults +' Results will be downloaded, please filter results further to obtain full report';
401           }else{          
402             this.downloadAllTooltip = (totalResults === 1) ?'Downloads ' + totalResults + ' Results' : 'Downloads all ' + totalResults + ' Results' ;
403             downloadCount= totalResults;
404           } 
405           this.setState({isLoading: false,
406             totalResults: totalResults,
407             totalPages: parseInt(resp.headers['total-pages']),
408             showResults: resp.headers['total-results'] > 0 ? true : false,
409             showPagination: resp.headers['total-results'] > 0 ? true : false,
410             isInitialLoad: false,
411             noResults: resp.headers['total-results'] && resp.headers['total-results'] > 0 ? false : true,
412             errorResults: !resp.headers['total-results'],
413             downloadCount: downloadCount
414           });        
415         }
416
417         this.setState({ displayNodes: 'show', viewName: this.state.defaultViewName });
418     }else{
419       if(resp.data && resp.data.results) {
420         this.nodeResults = resp.data.results;
421         let totalResults = 0;
422         let totalPages = 0;
423         if(resp.headers){
424             totalResults = parseInt(resp.headers['total-results']);
425             totalPages = parseInt(resp.headers['total-pages']);
426         }                
427         this.setState({isLoading: false,totalPages:totalPages,errorDownloadResults:false,downloadErrorMsg:''},() => {this.getAllExcels()});
428       }else{
429         this.nodeResults = '';
430         this.setState({isLoading: false,errorDownloadResults:true,downloadErrorMsg:error+'',enableModelBusyFeedback:false});
431       }
432     }
433   }
434
435   nodeOnClick(event, uri, id, nodeType) {
436     event.preventDefault();
437     let delimiter = '\/';
438     let start = 3;
439     let tokens = uri.split(delimiter).slice(start);
440     let result = tokens.join(delimiter);
441     sessionStorage.setItem(GlobalExtConstants.ENVIRONMENT + 'URI', result);
442     console.log(['/model/' + nodeType + '/' + id + '/' + '1']);
443   };
444   openDownloadRange = () =>{    
445     this.setState({
446       showDownloadResultsModal: true,
447       errorDownloadResults: false,
448       downloadErrorMsg:''});    
449   }
450   closeDownloadResults = () =>{
451     this.setState({
452       showDownloadResultsModal: false,
453       enableModelBusyFeedback: false
454     });   
455   }
456   getAllExcels = (pageRange,rangeState) =>{   
457     
458     console.log('getAllExcels>>>>>>>>>>>*',pageRange);
459     if(pageRange){
460       this.typeOfCall=false;
461       let rangeModelState=(rangeState)? rangeState: false;
462       this.setState(
463         { pageRange: pageRange,enableModelBusyFeedback:true,showDownloadResultsModal:rangeModelState},
464         function () { this.formQueryString(); }.bind(this)
465       );
466     }else{
467       this.setState(
468         {errorDownloadResults: false, showDownloadResultsModal: false, downloadErrorMsg:'', isLoading: false, enableModelBusyFeedback:false},
469         function () { generateExcels(this.nodeResults);this.nodeResults='';this.typeOfCall = true;}.bind(this)
470       );      
471     }   
472   }
473   getExcels = () =>{
474     this.typeOfCall = true;
475     generateExcels(this.state.nodes);
476   }
477
478     handlePageChange = (pageNumber) => {
479     console.log('[CustomQuery.jsx] HandelPageChange active page is', pageNumber);
480     this.typeOfCall = true;
481     this.setState(
482       { activePage: pageNumber, isLoading: true, nodes: [], resetColumnFilters: false, isPageNumberChange: true },
483       function () { this.formQueryString(); }.bind(this)
484     );
485   };
486   openHistory = (nodeDisplay, nodeUri, nodeType) => { // open modal from Card
487     console.log('history >> showModal',nodeDisplay);
488     
489     if(nodeType){
490       this.setState({
491         nodeDisplay: nodeDisplay,
492         showHistoryModal: true,
493         showModelOptions:true,
494         enableCalendar:true,
495         historyType:(this.state.historyType === 'cq') ? 'nodeState' : this.state.historyType,
496         focusedNodeUri: nodeUri,
497         focusedNodeType: nodeType,
498       });
499     }else{
500       let payload = this.formPayload();
501     this.setState({
502         showHistoryModal:true,
503         showModelOptions:false,
504         focusedNodeUri: JSON.stringify(payload),
505         focusedNodeType: nodeType,
506         enableCalendar:true,
507                 historyType :'cq'
508     });
509   }
510     }
511   closeHistory = () => {
512         this.setState({
513                 showHistoryModal: false,
514                 enableCalendar:true,
515                 historyType :'nodeState'
516         });
517   }
518
519   setViewName(event) {
520     console.log(event.currentTarget.value);
521     this.setState({
522                 viewName: event.currentTarget.value
523     });
524   }
525
526   setDefaultViewName(event) {
527     let ENVIRONMENT = GlobalExtConstants.ENVIRONMENT;
528     let layout =  event.target.value;
529
530     if(sessionStorage.getItem(ENVIRONMENT + 'userId')) {
531       if (event.target.checked) {
532         localStorage.setItem(ENVIRONMENT + '_' + sessionStorage.getItem(ENVIRONMENT + 'userId') + '_viewPreference', layout);
533       } else {
534         localStorage.removeItem(ENVIRONMENT + '_' + sessionStorage.getItem(ENVIRONMENT + 'userId') + '_viewPreference');
535       }
536     }
537
538     this.setState({
539       defaultViewName: event.target.value
540     });
541   }
542
543   submitHistory = () => {
544     console.log("submitting history");
545     let paramToPassThrough = '';
546     if(this.state.focusedNodeType){
547       paramToPassThrough = '/history/' + this.state.historyType +'/' + this.state.focusedNodeType + '/' + btoa(this.state.focusedNodeUri);
548     }else{
549       paramToPassThrough = '/historyQuery/' + this.state.historyType + '/' + btoa(this.state.focusedNodeUri);     
550     }
551     let epochStartTime = (this.state.startDate).unix();
552     this.props.history.push(paramToPassThrough + '/' + epochStartTime * 1000);
553   }
554
555   handleDateChange = (newDate) =>{
556     this.setState({ startDate: moment(+newDate) });
557     console.log('[CustomQuery.jsx] handleDateChange date is ', this.state.startDate);
558     console.log('[CustomQuery.jsx] handleDateChange date is in millis ', +this.state.startDate);
559   }
560   setHistoryType(event) {
561     console.log(event.target.value);
562     let enableCalendar = false;
563     if(event.target.value === 'nodeLifeCycle'){
564         enableCalendar = false;
565     }else{
566         enableCalendar = true;
567     }
568     this.setState({
569                 historyType: event.target.value,
570                 enableCalendar: enableCalendar
571     });
572     console.log(this.state.enableCalendar);
573   }
574   openNodeModal(nodeDisplay, nodeUri, nodeType){ // open modal
575                  console.log('customquery >> showModal');
576                  nodeDisplay = "Node Details of " + nodeUri;
577                  let node = null;
578                  let found = false;
579                  for(var j = 0; j < this.state.nodes.length && !found; j++){
580                     if(this.state.nodes[j].url === nodeUri){
581                         node = this.state.nodes[j];
582                         found = true;
583                     }
584                  }
585                  if(nodeDisplay && found){
586                     this.setState({
587                        nodeDisplay: nodeDisplay,
588                        focusedNode: node,
589                        showNodeModal:true
590                     });
591                  }else{
592                      this.setState({
593                      showNodeModal:true
594                      });
595                  }
596   }
597
598   closeNodeModal = () => {
599     this.setState({
600         showNodeModal: false
601     });
602   }
603   componentDidUpdate(prevProps, prevState, snapshot) {
604     if(this.state.nodes.length > 0 && !this.state.visualAddition){
605         Visualization.chart('currentState', [], [], this.state.res.data, this);
606         this.setState({
607                     showPagination: this.state.nodes.length > 0 ? true : false,
608                         visualAddition: true
609         });
610     }
611   }
612   prepareDownloadRangeModel = () =>{
613     let downloadRangeModel = (this.state.showDownloadResultsModal)?<DownloadRangeModel 
614               showDownloadResultsModal={this.state.showDownloadResultsModal}
615               totalPages={this.state.totalPages}
616               totalResults={this.state.totalResults}
617               triggerDownload={this.getAllExcels}
618               errorDownloadResults={this.state.errorDownloadResults}
619               downloadErrorMsg={this.state.downloadErrorMsg}
620               triggerClose={this.closeDownloadResults}
621               enableModelBusyFeedback={this.state.enableModelBusyFeedback}
622           /> : '';
623     return downloadRangeModel;
624   }
625   render() {
626     let simple = 'Enter Start Node Location => network/generic-vnfs/generic-vnf/a642ad94-9a84-4418-9358-c1ee860315e2';
627     let advanced = 'Enter Start Node Location => /cloud-infrastructure/cloud-regions/cloud-region/cloudowner1/cloudregion1';
628     let downloadRangeModel = this.prepareDownloadRangeModel();
629     if (this.state.displayValidationError) {
630       inputClasses.push('invalid');
631     }
632     let styling = inputClasses.join(' ');
633     console.log('styling:' + styling);
634     let nodes = '';
635     let classes = `${this.state.displayNodes} container-fluid`;
636     const modelGalleryElement = <ModelGallery
637                 nodes={this.state.nodes} 
638                 viewName={this.state.viewName}
639                 historyStackString={this.props.location.historyStackString} 
640                 openHistoryModal={this.openHistory}
641                 isPageNumberChange={this.state.isPageNumberChange}
642                 resetColumnInd={this.state.resetColumnFilters}
643                 enableRealTime={false}/>;
644     if (this.state.nodes.length > 0) {
645       console.log('nodes exist');
646       nodes =
647         <div>
648           <hr />          
649           {modelGalleryElement}
650         </div >;
651     }
652
653     return (
654       <div>
655         <header className='addPadding jumbotron my-4'>
656           <h1 className='display-2'>Welcome to Custom Queries!</h1>
657           <p className='lead'>
658             On this page you can choose from the many different
659             predefined custom queries has to offer. Simply
660             choose the query name and provide the location of the
661             start node.<br />
662             If prompted please also enter the values in required
663             properties field as some queries require these values
664             to execute.<br />
665             Check the Help Section on the right for examples.
666           </p>
667         </header>
668         <div className='static-modal'>
669            <Modal show={this.state.showHistoryModal} onHide={this.closeHistory}>
670                 <Modal.Header>
671                         <Modal.Title>Retrieve {(this.state.focusedNodeType) ? this.state.focusedNodeType: 'Custom Query '} History</Modal.Title>
672                 </Modal.Header>
673                 <Modal.Body>
674                         <form className={this.state.showModelOptions ? 'show' : 'hidden'}>
675                                         <div className="radio">
676                                                 <label>
677                                                 <input type="radio" value="nodeState"
678                                                                                 checked={this.state.historyType === 'nodeState'}
679                                                                                 onChange={(e) => this.setHistoryType(e)} />
680                                                 View state at
681                                                 </label>
682                                         </div>
683                                         <div className="radio">
684                                                 <label>
685                                                 <input type="radio"  value="nodeLifeCycleSince"
686                                                                                 checked={this.state.historyType === 'nodeLifeCycleSince'}
687                                                                                 onChange={(e) => this.setHistoryType(e)} />
688                                                 View updates since
689                                                 </label>
690                                         </div>
691                                         <div className="radio">
692                                                 <label>
693                                                 <input type="radio" value="nodeLifeCycle"
694                                                                                 checked={this.state.historyType === 'nodeLifeCycle'}
695                                                                                 onChange={(e) => this.setHistoryType(e)} />
696                                                 View all updates
697                                                 </label>
698                                         </div>
699                         </form>
700                         <div className={this.state.enableCalendar ? 'show' : 'hidden'}>
701                             <DatePicker
702                                 inline
703                            selected={this.state.startDate}
704                            onChange={(newDate) => this.handleDateChange(newDate)}
705                            showTimeSelect
706                            timeFormat="HH:mm"
707                            timeIntervals={15}
708                            dateFormat="MMMM D, YYYY h:mm a"
709                            timeCaption="time"
710                        />
711                    </div>
712                 </Modal.Body>
713                 <Modal.Footer>
714                         <Button onClick={this.closeHistory}>Close</Button>
715                         <Button onClick={this.submitHistory}>Submit</Button>
716                 </Modal.Footer>
717            </Modal>
718         </div>
719         <div className='static-modal'>
720                                         <Modal show={this.state.showNodeModal} onHide={this.closeNodeModal} dialogClassName="modal-override">
721                                                 <Modal.Header>
722                                                         <Modal.Title>Retrieve {this.state.nodeDisplay}</Modal.Title>
723                                                 </Modal.Header>
724                                                 <Modal.Body>
725                                        <Grid fluid={true}>
726                                              <Row className='show-grid'>
727                                                <Col lg={12} md={12} sm={12} xs={12}>
728                                                    <ModelCard
729                                                                key={this.state.focusedNode.id}
730                                                                nodeId={this.state.focusedNode.id}
731                                                                nodeType={this.state.focusedNode['node-type']}
732                                                                nodeProps={this.state.focusedNode.properties}
733                                                                nodeRelatives={this.state.focusedNode['related-to']}
734                                                                nodeUrl={this.state.focusedNode.url}
735                                                                historyStackString={this.props.location.historyStackString}
736                                                                openHistoryModal={this.openHistory}
737                                                                enableRealTime={false}/>
738                                                </Col>
739                                              </Row>
740                                        </Grid>
741                                                 </Modal.Body>
742                                                 <Modal.Footer>
743                                                         <Button onClick={this.closeNodeModal}>Close</Button>
744                                                 </Modal.Footer>
745                                         </Modal>
746                </div>
747         <div className='addPadding'>
748           <div className='row addPaddingTop'>
749             <Col md={8}>
750               <form style={{ margin: 0 }} onSubmit={this.onAdditem} id='customQueryForm' name='customQueryForm'>
751                 <div className='form-group'>
752                   <label>Query:</label><br />
753                   <div id='customQueryText'>
754                     <Select
755                       className='dropdown-item'
756                       autosize={true}
757                       placeholder={this.state.queryPlaceHolder}
758                       value={this.state.query}
759                       onChange={this.updateSelectedHandler}
760                       options={dropdownList} />
761                   </div>
762                 </div>
763                 <div className={this.state.displayDescription} id='customQueryDescription'>
764                   <Label bsStyle='info'>Description</Label>
765                   <p><br />{this.state.description}</p>
766                   <a onClick={() => this.setState({ showText: !this.state.showText })}
767                       className={this.state.additionalInfo==='' ? 'hidden' : 'show'}>See more</a><br />
768                   <Collapse in={this.state.showText}>
769                     <div>
770                       <span>
771                         {this.state.additionalInfo.split('\n').map(info => {
772                           return <div>{info}</div>;
773                         })}
774                       </span>
775                     </div>
776                   </Collapse>
777                 </div>
778                 <div className='form-group addPaddingTop' id='startNodeText'>
779                   <label htmlFor='StartNode'>Enter Start Node Location (full uri path): </label>
780                   <input type='text'
781                     id='startNode'
782                     className={styling}
783                     name='startNode'
784                     placeholder={this.state.placeholder}
785                     onChange={this.inputChangeHandler}
786                     disabled={this.state.disableInputs}
787                     value={this.state.startNode} />
788                 </div>
789                 <div className={this.state.displayReqProps} id='reqPropsText'>
790                   <label htmlFor={'reqProps'}>Required Properties</label>
791                   <input type='text'
792                     id='reqProps'
793                     className={styling}
794                     name='reqProps'
795                     placeholder={this.state.reqPropPlaceholder}
796                     onChange={this.inputChangeHandler}
797                     disabled={this.state.disableInputs} 
798                     value={this.state.reqProps}/>
799                 </div>
800                 <div className={this.state.displayOptionalProps}>
801                   <label htmlFor={'optionalProps'}>Optional Properties</label>
802                   <input type='text'
803                          id='optionalProps'
804                          className={styling}
805                          name='optionalProps'
806                          placeholder={this.state.optionalPropPlaceholder}
807                          onChange={this.inputChangeHandler}
808                          disabled={this.state.disableInputs}
809                          value={this.state.optionalProps} />
810                 </div>
811                 <div className='row addPaddingTop'>
812                   <Col sm={12}>
813                     <button className='btn btn-primary btn-md' disabled={!this.state.formIsValid}>Submit</button>
814                     { INVLIST.isHistoryEnabled && (<button className='btn btn-outline-secondary' type='button' onClick={this.openHistory} disabled={!this.state.formIsValid}>History</button>)}
815                   </Col>
816                 </div>
817               </form>
818             </Col>
819             <Col md={4}>
820               <PanelGroup accordion id='rb-accordion'>
821                 <Panel eventKey='1'>
822                   <Panel.Heading>
823                     <Panel.Title toggle>+ Simple Query</Panel.Title>
824                   </Panel.Heading>
825                   <Panel.Body collapsible className='cardwrap'>
826                     <p>
827                       Please specify a Query from the Dropdown Menu.<br />
828                       <br />
829                       Please enter exact location of the Starting Node Path
830                       <br /> <br />
831                       Submit
832                       <br /> <br />
833                       Examples: <br />
834                       Query => complex-fromVnf<br />
835                       Enter Starting Node => /network/pnfs/pnf/pnf-name1<br /> <br />
836                       {simple}<br /><br />
837                       *Simple Query requires no other values to be passed other than the start node path
838                     </p>
839                   </Panel.Body>
840                 </Panel>
841                 <Panel eventKey='2'>
842                   <Panel.Heading>
843                     <Panel.Title toggle>+ Advanced Queries with Properties</Panel.Title>
844                   </Panel.Heading>
845                   <Panel.Body collapsible>
846                     <p className='cardwrap'>
847                       Please specify a Query from the Dropdown Menu.<br />
848                       <br />
849                       Please enter exact location of the Starting Node Path<br /> <br />
850                       Please enter required query parameter - if applicable<br /> <br />
851                       Please enter optional query parameter - if applicable<br /> <br />
852                       Example: <br />
853                       Query => vlantag-fromVlanidouter<br /> <br />
854                       {advanced}<br /> <br />
855                       Required Properties: vlanIdOuter=203<br /> <br />
856                       Optional Properties: vlanIdInner=103
857                     </p>
858                   </Panel.Body>
859                 </Panel>
860               </PanelGroup>
861             </Col>
862           </div>
863         </div>
864
865         <div className='addPaddingTop'>
866           <div className='container-fluid model-container custom-query-result'>
867           <Spinner loading={this.state.isLoading}>
868             <Row className={this.state.isInitialLoad ? 'hidden' : 'show'}>
869               <Col md={12}>
870                 <h2>
871                   <span className='badge badge-dark'>{this.state.query}</span> Results
872                 </h2>
873                 <br/>
874                 <h5>Total Results: <strong>{this.state.totalResults}</strong></h5>
875               </Col>
876             </Row>
877             { this.state.showResults && <div className='addPaddingTop'>
878                 <OutputToggle scope={this} visualDisabled={this.state.totalResults > PAGINATION_CONSTANT}/>
879             </div> }
880             <Row className={this.state.showResults ? 'show' : 'hidden'}>
881                       <Col md={8}  className={this.state.showPagination ? 'show' : 'hidden'}>
882                 <Pagination
883                   activePage={this.state.activePage}
884                   itemsCountPerPage={PAGINATION_CONSTANT.RESULTS_PER_PAGE}
885                   totalItemsCount={this.state.totalResults}
886                   pageRangeDisplayed={PAGINATION_CONSTANT.PAGE_RANGE_DISPLAY}
887                   onChange={this.handlePageChange} />
888               </Col>
889               <Col md={2} className='text-right'>
890                 <OverlayTrigger  placement='top' overlay={<Tooltip id='tooltip-top'>{this.downloadAllTooltip}</Tooltip>}>
891                   <span className="d-inline-block" style={{display: 'inline-block'}}>
892                     <Button bsSize='small' onClick={()=>{this.getAllExcels(this.state.downloadCount)}}>
893                       Download XLSX <i className='icon-documents-downloadablefile'></i>
894                 </Button>
895                   </span>
896                 </OverlayTrigger>
897               </Col>
898               <Col md={2} className='text-right'>
899                 <OverlayTrigger  placement='top' overlay={<Tooltip id='tooltip-top'>{this.downloadRangeTooltip}</Tooltip>}>
900                   <span className="d-inline-block" style={{display: 'inline-block'}}>
901                     <Button bsSize='small' onClick={this.openDownloadRange}>
902                       Download XLSX (Range) <i className='icon-documents-downloadablefile'></i>
903                     </Button>
904                   </span>
905                </OverlayTrigger>
906               </Col>
907                   </Row>
908               <div className={'addPaddingTop alert alert-danger ' +(this.state.errorResults ? 'show' : 'hidden')} role="alert">
909                 An error occurred, please try again later. If this issue persists, please contact the system administrator. {this.state.errorMessage}
910               </div>
911                   <Row className={'addPaddingTop ' + (this.state.noResults ? 'show' : 'hidden')}>
912                         <Col md={12}>
913                                 <h2>No Results Found</h2>
914                         </Col>
915                   </Row>
916                   <Row className={this.state.isInitialLoad ? 'hidden' : 'show'}>              
917               <div className={this.state.isLoading ? 'hidden' : 'show'}>
918                 <div className='col align-self-center'>
919                   <div className={classes}>
920                     {nodes}
921                   </div>
922                 </div>
923               </div>
924                   </Row>
925                   <Row className={this.state.showPagination ? 'show' : 'hidden'}>
926                           <Col md={12}>
927                 <Pagination
928                   activePage={this.state.activePage}
929                   itemsCountPerPage={PAGINATION_CONSTANT.RESULTS_PER_PAGE}
930                   totalItemsCount={this.state.totalResults}
931                   pageRangeDisplayed={PAGINATION_CONSTANT.PAGE_RANGE_DISPLAY}
932                   onChange={this.handlePageChange} />
933               </Col>
934             </Row>
935             </Spinner>
936             <Spinner loading={this.state.enableModelBusyFeedback}>
937               {downloadRangeModel}
938             </Spinner>
939           </div>
940         </div>
941       </div>
942     );
943   }
944 }
945
946 export default CustomQuery;