856a08d8ddec4750443f1b0e18152cb006b6fdea
[aai/sparky-fe.git] / src / app / MainScreenHeader.jsx
1 /*
2  * ============LICENSE_START===================================================
3  * SPARKY (AAI UI service)
4  * ============================================================================
5  * Copyright © 2017 AT&T Intellectual Property.
6  * Copyright © 2017 Amdocs
7  * All rights reserved.
8  * ============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=====================================================
21  *
22  * ECOMP and OpenECOMP are trademarks
23  * and service marks of AT&T Intellectual Property.
24  */
25
26 import React, {Component} from 'react';
27 import {connect} from 'react-redux';
28 import FontAwesome from 'react-fontawesome';
29 import Button from 'react-bootstrap/lib/Button.js';
30 import Modal from 'react-bootstrap/lib/Modal.js';
31 import GlobalAutoCompleteSearchBar from 'app/globalAutoCompleteSearchBar/GlobalAutoCompleteSearchBar.jsx';
32 import {postAnalyticsData} from 'app/analytics/AnalyticsActions.js';
33 import GlobalInlineMessageBar from 'app/GlobalInlineMessageBar/GlobalInlineMessageBar.jsx';
34 import {getClearGlobalMessageEvent} from 'app/globalInlineMessageBar/GlobalInlineMessageBarActions.js';
35 import {externalUrlRequest, externalMessageRequest} from 'app/contextHandler/ContextHandlerActions.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 import {clearFilters} from 'generic-components/filterBar/FilterBarUtils.js';
58 const mapStateToProps = ({mainWrapper}) => {
59   let {
60     showMenu = false,
61     toggleButtonActive = false,
62     externalRequestFound = {},
63     secondaryTitle = ''
64   } = mainWrapper;
65
66   return {
67     showMenu,
68     toggleButtonActive,
69     externalRequestFound,
70     secondaryTitle
71   };
72 };
73
74
75 const mapActionsToProps = (dispatch) => {
76   return {
77     onShowMenu: () => dispatch(showMainMenu(true)),
78     onHideMenu: () => {
79       dispatch(showMainMenu(false));
80     },
81     dispatchAnalyticsData: () => dispatch(
82       postAnalyticsData(document.documentElement.outerHTML.replace('\s+', ''))),
83     onRouteChange: () => {
84       dispatch(getClearGlobalMessageEvent());
85       dispatch(clearSuggestionsTextField());
86       dispatch(clearExtensibleViewData());
87       dispatch(clearFilters());
88       dispatch(setSecondaryTitle(undefined));
89     },
90     onExternalUrlRequest: (urlParamString) => {
91       dispatch(externalUrlRequest(urlParamString));
92     },
93     onExternalMessageRecieved: (messageJson) => {
94       dispatch(externalMessageRequest(messageJson));
95     }
96   };
97 };
98
99 class MainScreenHeader extends Component {
100   static propTypes = {
101     showMenu: React.PropTypes.bool,
102     toggleButtonActive: React.PropTypes.bool,
103     externalRequestFound: React.PropTypes.object,
104     secondaryTitle: React.PropTypes.string
105   };
106
107   navigationLinkAndCurrentPathMatch(location, to) {
108     let linkPathElements = to.split('/');
109     let locationElements = location.pathname.split('/');
110
111     // the element arrays above will have the route at index 1 ... need to
112     // verify if the routes match
113     return locationElements[1] === linkPathElements[1];
114   }
115
116   hasRouteChanged(currentPath, nextPath) {
117     let currentPathParts = currentPath.split('/');
118     let nextPathParts = nextPath.split('/');
119
120     if (currentPathParts[1] !== nextPathParts[1]) {
121       return true;
122     } else {
123       return false;
124     }
125   }
126   isValidExternalURL(url) {
127     if(decodeURIComponent(url).indexOf('&') > 0 ) {
128       return true;
129     } else {
130       return false;
131     }
132   }
133   componentWillMount() {
134     if(this.props.match.params.externalUrl !== undefined &&
135       this.isValidExternalURL(this.props.match.params.externalUrl)) {
136       this.props.onExternalUrlRequest(this.props.match.params.externalUrl);
137     }
138   }
139   componentWillReceiveProps(nextProps) {
140     if (this.props.location &&
141       this.props.location.pathname !==
142       nextProps.location.pathname) {
143       // update analytics
144       this.props.dispatchAnalyticsData();
145
146       if (this.hasRouteChanged(this.props.location.pathname,
147           nextProps.location.pathname)) {
148         this.props.onRouteChange();
149       }
150     }
151
152     if(nextProps.match.params.externalUrl !== undefined &&
153       nextProps.match.params.externalUrl !== this.props.match.params.externalUrl &&
154       this.isValidExternalURL(nextProps.match.params.externalUrl)) {
155       this.props.onExternalUrlRequest(nextProps.match.params.externalUrl);
156     }
157     /* if the externalURL is not valid, we do not add any message as other proper
158     views will get that messages since the route will be this parameter.*/
159
160     if(this.props.externalRequestFound !== nextProps.externalRequestFound &&
161       nextProps.externalRequestFound !== undefined && nextProps.externalRequestFound.suggestion !== undefined) {
162       changeUrlAddress(nextProps.externalRequestFound.suggestion, nextProps.history);
163     }
164   }
165
166   receiveMessage(event) {
167     function isJson(str) {
168       try {
169         JSON.parse(str);
170       } catch (e) {
171         return false;
172       }
173       return true;
174     }
175     let messageData = event.data.message;
176     if(isJson(messageData)) {
177       this.props.onExternalMessageRecieved(JSON.parse(messageData));
178     }
179   }
180   componentDidMount() {
181     window.addEventListener('message', this.receiveMessage, false);
182   }
183   componentWillUnmount() {
184     window.removeEventListener('message', this.receiveMessage);
185   }
186
187   render() {
188     let {
189       showMenu,
190       onShowMenu,
191       onHideMenu,
192       toggleButtonActive,
193       secondaryTitle
194     } = this.props;
195
196     let menuOptions = [];
197
198     const MenuItem = ({label, iconClass, to}) => (
199       <Route path={to} children={({location}) => (
200         <NavLink to={to} onClick={onHideMenu}>
201           <div className={this.navigationLinkAndCurrentPathMatch(location, to) ? 'main-menu-button-active' : 'main-menu-button'}>
202             <div className={iconClass}/>
203             <div className='button-icon'>{label}</div>
204           </div>
205         </NavLink>
206       )}/>
207     );
208
209     // add Tier Support view
210     menuOptions.push(
211       <MenuItem key='schemaMenu' to='/schema' label={MENU_ITEM_TIER_SUPPORT}
212                 iconClass='button-icon view-inspect-button-icon'/>
213     );
214
215     // add VNF view
216     menuOptions.push(
217       <MenuItem key='vnfSearchMenu'
218         to='/vnfSearch'
219         label={MENU_ITEM_VNF_SEARCH}
220         iconClass='button-icon vnf-search-button-icon'/>
221     );
222
223     // add all custom view menu options
224     for (let view in extensibleViews) {
225       menuOptions.push(
226         <MenuItem key={extensibleViews[view]['viewName'] + 'Menu'} to={'/' + extensibleViews[view]['viewName']}
227                   label={extensibleViews[view]['displayName']}
228                   iconClass={'button-icon ' + extensibleViews[view]['iconClass']}/>
229       );
230     }
231
232     let secondaryTitleClass = 'secondary-header';
233     if (secondaryTitle === undefined || secondaryTitle === '') {
234       secondaryTitleClass = secondaryTitleClass + ' hidden';
235     }
236
237     return (
238       <div className='header'>
239         <div>
240           <Button
241             bsClass={(toggleButtonActive)
242             ? 'toggle-view-button-active'
243             : 'toggle-view-button'}
244             onClick={onShowMenu}>
245             <FontAwesome name='bars'/>
246           </Button>
247           <Modal show={showMenu} onHide={onHideMenu}
248                  dialogClassName='modal-main-menu'>
249             <Modal.Body>
250               {menuOptions}
251             </Modal.Body>
252           </Modal>
253           <span className='application-title'>{AAI_TITLE}</span>
254           <GlobalAutoCompleteSearchBar history={this.props.history}/>
255         </div>
256         <GlobalInlineMessageBar />
257         <div className={secondaryTitleClass}>
258           <span className='secondary-title'>{secondaryTitle}</span>
259         </div>
260       </div>
261     );
262   }
263 }
264
265 export default connect(mapStateToProps, mapActionsToProps)(MainScreenHeader);