e485161085c928d18d30358caa26c9c5380d79cb
[aai/sparky-fe.git] / src / app / MainScreenHeader.jsx
1 /*
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21 import React, {Component} from 'react';
22 import { PropTypes } from 'prop-types';
23 import {connect} from 'react-redux';
24 import FontAwesome from 'react-fontawesome';
25 import {clearFilters} from 'filter-bar-utils';
26 import Button from 'react-bootstrap/lib/Button.js';
27 import Modal from 'react-bootstrap/lib/Modal.js';
28 import GlobalAutoCompleteSearchBar from 'app/globalAutoCompleteSearchBar/GlobalAutoCompleteSearchBar.jsx';
29 import {postAnalyticsData} from 'app/analytics/AnalyticsActions.js';
30 import GlobalInlineMessageBar from 'app/globalInlineMessageBar/GlobalInlineMessageBar.jsx';
31 import {getClearGlobalMessageEvent} from 'app/globalInlineMessageBar/GlobalInlineMessageBarActions.js';
32 import {externalUrlRequest, externalMessageRequest, getSubscriptionPayload} from 'app/contextHandler/ContextHandlerActions.js';
33 import {
34   filterBarActionTypes
35 } from 'utils/GlobalConstants.js';
36
37 import {
38   Route,
39   NavLink
40 } from 'react-router-dom';
41
42 import {
43   AAI_TITLE,
44   MENU_ITEM_TIER_SUPPORT,
45   MENU_ITEM_VNF_SEARCH
46 } from './MainScreenWrapperConstants.js';
47
48 import {
49   showMainMenu,
50   clearExtensibleViewData,
51   setSecondaryTitle
52 } from './MainScreenWrapperActionHelper.js';
53
54 import {clearSuggestionsTextField} from 'app/globalAutoCompleteSearchBar/GlobalAutoCompleteSearchBarActions.js';
55 import {changeUrlAddress} from 'utils/Routes.js';
56 import extensibleViews from 'resources/views/extensibleViews.json';
57
58
59 const mapStateToProps = ({mainWrapper}) => {
60   let {
61     showMenu = false,
62     toggleButtonActive = false,
63     externalRequestFound = {},
64     secondaryTitle = '',
65     subscriptionPayload = {},
66     subscriptionEnabled = false
67   } = mainWrapper;
68
69   return {
70     showMenu,
71     toggleButtonActive,
72     externalRequestFound,
73     secondaryTitle,
74     subscriptionPayload,
75     subscriptionEnabled
76   };
77 };
78
79
80 const mapActionsToProps = (dispatch) => {
81   return {
82     onShowMenu: () => dispatch(showMainMenu(true)),
83     onHideMenu: () => {
84       dispatch(showMainMenu(false));
85     },
86     dispatchAnalyticsData: () => dispatch(
87       postAnalyticsData(document.documentElement.outerHTML.replace('\s+', ''))),
88     onRouteChange: () => {
89       dispatch(getClearGlobalMessageEvent());
90       dispatch(clearSuggestionsTextField());
91       dispatch(clearExtensibleViewData());
92       dispatch(clearFilters(filterBarActionTypes.CLEAR_FILTERS));
93       dispatch(setSecondaryTitle(undefined));
94     },
95     onExternalUrlRequest: (urlParamString) => {
96       dispatch(externalUrlRequest(urlParamString));
97     },
98     onExternalMessageRecieved: (messageJson) => {
99       dispatch(externalMessageRequest(messageJson));
100     },
101     onGetSubscriptionPayload: () => {
102       dispatch(getSubscriptionPayload());
103     }
104   };
105 };
106
107 class MainScreenHeader extends Component {
108   static propTypes = {
109     showMenu: PropTypes.bool,
110     toggleButtonActive: PropTypes.bool,
111     externalRequestFound: PropTypes.object,
112     secondaryTitle: PropTypes.string,
113     subscriptionPayload: PropTypes.object
114   };
115
116   navigationLinkAndCurrentPathMatch(location, to) {
117     let linkPathElements = to.split('/');
118     let locationElements = location.pathname.split('/');
119
120     // the element arrays above will have the route at index 1 ... need to
121     // verify if the routes match
122     return locationElements[1] === linkPathElements[1];
123   }
124
125   hasRouteChanged(currentPath, nextPath) {
126     let currentPathParts = currentPath.split('/');
127     let nextPathParts = nextPath.split('/');
128
129     if (currentPathParts[1] !== nextPathParts[1]) {
130       return true;
131     } else {
132       return false;
133     }
134   }
135   
136   isValidExternalURL(url) {
137     if(decodeURIComponent(url).indexOf('&') > 0 ) {
138       return true;
139     } else {
140       return false;
141     }
142   }
143
144   componentWillMount() {
145     this.props.onGetSubscriptionPayload();
146     if(this.props.match.params.externalUrl !== undefined &&
147       this.isValidExternalURL(this.props.match.params.externalUrl)) {
148       this.props.onExternalUrlRequest(this.props.match.params.externalUrl);
149     }
150   }
151
152   componentWillReceiveProps(nextProps) {
153     if (this.props.location &&
154       this.props.location.pathname !==
155       nextProps.location.pathname) {
156       // update analytics
157       this.props.dispatchAnalyticsData();
158
159       if (this.hasRouteChanged(this.props.location.pathname,
160           nextProps.location.pathname)) {
161         this.props.onRouteChange();
162       }
163     }
164
165     if(nextProps.match.params.externalUrl !== undefined &&
166       nextProps.match.params.externalUrl !== this.props.match.params.externalUrl &&
167       this.isValidExternalURL(nextProps.match.params.externalUrl)) {
168       this.props.onExternalUrlRequest(nextProps.match.params.externalUrl);
169     }
170     /* if the externalURL is not valid, we do not add any message as other proper
171     views will get that messages since the route will be this parameter.*/
172
173     if(this.props.externalRequestFound !== nextProps.externalRequestFound &&
174       nextProps.externalRequestFound !== undefined && nextProps.externalRequestFound.suggestion !== undefined) {
175       changeUrlAddress(nextProps.externalRequestFound.suggestion, nextProps.history);
176     }
177
178     if (nextProps.subscriptionEnabled) {
179       if (nextProps.subscriptionPayload !== this.props.subscriptionPayload &&
180         Object.keys(nextProps.subscriptionPayload).length > 0) {
181         var getWindowUrl = function (url) {
182           var split = url.split('/');
183           return split[0] + '//' + split[2];
184         };
185         window.parent.postMessage(
186           JSON.stringify(nextProps.subscriptionPayload),
187           getWindowUrl(document.referrer));
188       }
189     }
190   }
191
192   receiveMessage(event, $this) {
193     function isJson(str) {
194       try {
195         JSON.parse(str);
196       } catch (e) {
197         return false;
198       }
199       return true;
200     }
201     if(isJson(event.data)) {
202       let messageData = JSON.parse(event.data);
203       if(isJson(messageData.message)) {
204         $this.props.onExternalMessageRecieved(messageData.message);
205       }
206     }
207
208   }
209   componentDidMount() {
210     //TODO Move this logic to the component will receive props.
211     //Check if the event lister is available and if the subscription is
212     // enabled before registering for it
213     if(document.referrer) {
214       var $this = this;
215       window.addEventListener('message', function (e) {
216         $this.receiveMessage(e, $this);
217       }, false);
218     }
219   }
220   componentWillUnmount() {
221     if(this.props.subscriptionEnabled) {
222       var $this = this;
223       window.removeEventListener('message', function (e) {
224         $this.receiveMessage(e, $this);
225       }
226       );
227     }
228   }
229
230   render() {
231     let {
232       showMenu,
233       onShowMenu,
234       onHideMenu,
235       toggleButtonActive,
236       secondaryTitle
237     } = this.props;
238
239     let menuOptions = [];
240
241     const MenuItem = ({label, iconClass, to}) => (
242       <Route path={to} children={({location}) => (
243         <NavLink to={to} onClick={onHideMenu}>
244           <div className={this.navigationLinkAndCurrentPathMatch(location, to) ? 'main-menu-button-active' : 'main-menu-button'}>
245             <div className={iconClass}/>
246             <div className='button-icon'>{label}</div>
247           </div>
248         </NavLink>
249       )}/>
250     );
251
252     // add Tier Support view
253     menuOptions.push(
254       <MenuItem key='schemaMenu' to='/schema' label={MENU_ITEM_TIER_SUPPORT}
255                 iconClass='button-icon view-inspect-button-icon'/>
256     );
257
258     // add VNF view
259     menuOptions.push(
260       <MenuItem key='vnfSearchMenu'
261         to='/vnfSearch'
262         label={MENU_ITEM_VNF_SEARCH}
263         iconClass='button-icon vnf-search-button-icon'/>
264     );
265
266     // add all custom view menu options
267     for (let view in extensibleViews) {
268       let shouldDisplayIcon = false;
269       if(extensibleViews[view]['onlyRoute'] === undefined){
270         shouldDisplayIcon = true;
271       } else if(extensibleViews[view]['onlyRoute'] === false){
272         shouldDisplayIcon = true;
273       }
274       if(shouldDisplayIcon === true){
275         menuOptions.push(
276           <MenuItem key={extensibleViews[view]['viewName'] + 'Menu'} to={'/' + extensibleViews[view]['viewName']}
277                     label={extensibleViews[view]['displayName']}
278                     iconClass={'button-icon ' + extensibleViews[view]['iconClass']}/>
279         );
280       } 
281     }
282
283     let secondaryTitleClass = 'secondary-header';
284     if (secondaryTitle === undefined || secondaryTitle === '') {
285       secondaryTitleClass = secondaryTitleClass + ' hidden';
286     }
287
288     return (
289       <div className='header'>
290         <div>
291           <Button
292             bsClass={(toggleButtonActive)
293             ? 'toggle-view-button-active'
294             : 'toggle-view-button'}
295             onClick={onShowMenu}>
296             <FontAwesome name='bars'/>
297           </Button>
298           <Modal show={showMenu} onHide={onHideMenu}
299                  dialogClassName='modal-main-menu'>
300             <Modal.Body>
301               {menuOptions}
302             </Modal.Body>
303           </Modal>
304           <span className='application-title'>{AAI_TITLE}</span>
305           <GlobalAutoCompleteSearchBar history={this.props.history}/>
306         </div>
307         <GlobalInlineMessageBar />
308         <div className={secondaryTitleClass}>
309           <span className='secondary-title'>{secondaryTitle}</span>
310         </div>
311       </div>
312     );
313   }
314 }
315
316 export default connect(mapStateToProps, mapActionsToProps)(MainScreenHeader);