Merge branch 'projects' of https://osm.etsi.org/gerrit/osm/UI into projects
[osm/UI.git] / skyquake / framework / widgets / skyquake_nav / skyquakeNav.jsx
1 /*
2  *
3  *   Copyright 2016 RIFT.IO Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  *
17  */
18
19 import React from 'react';
20 import { Link } from 'react-router';
21 import Utils from 'utils/utils.js';
22 import Crouton from 'react-crouton';
23 import 'style/common.scss';
24
25 import './skyquakeNav.scss';
26 import SelectOption from '../form_controls/selectOption.jsx';
27 import {FormSection} from '../form_controls/formControls.jsx';
28
29 //Temporary, until api server is on same port as webserver
30 var rw = require('utils/rw.js');
31 var API_SERVER = rw.getSearchParams(window.location).api_server;
32 var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
33
34 //
35 // Internal classes/functions
36 //
37
38 class LogoutAppMenuItem extends React.Component {
39     handleLogout() {
40         Utils.clearAuthentication();
41     }
42     render() {
43         return (
44             <div className="app">
45                 <h2>
46                     <a onClick={this.handleLogout}>
47                         Logout
48                     </a>
49                 </h2>
50             </div>
51         );
52     }
53 }
54
55 class SelectProject extends React.Component {
56     constructor(props) {
57         super(props);
58     }
59     selectProject(e) {
60         let value = JSON.parse(e.currentTarget.value);
61         console.log('selected project', value)
62     }
63     render() {
64         let props = this.props;
65         let currentValue = JSON.stringify(props.currentProject);
66         let projects = this.props.projects && this.props.projects.map((p,i) => {
67             return {
68                 label: p.name,
69                 value: p.name
70             }
71         });
72         let hasProjects = (this.props.projects && (this.props.projects.length > 0))
73         return (
74             <div className="userSection app">
75                 {
76                     hasProjects ?  'Project:' : 'No Projects Assigned'
77                 }
78                 {
79                     hasProjects ?
80                     <SelectOption
81                         options={projects}
82                         value={currentValue}
83                         defaultValue={currentValue}
84                         onChange={props.onSelectProject}
85                         className="projectSelect" />
86                     : null
87                 }
88             </div>
89         )
90     }
91 }
92
93 class UserNav extends React.Component {
94     constructor(props) {
95         super(props);
96     }
97     handleLogout() {
98         Utils.clearAuthentication();
99     }
100     selectProject(e) {
101         let value = JSON.parse(e.currentTarget.value)
102         console.log('selected project', value)
103     }
104     render() {
105         let props = this.props;
106         let userProfileLink = '';
107         this.props.nav['user_management'] && this.props.nav['user_management'].routes.map((r) => {
108             if(r.unique) {
109                 userProfileLink = returnLinkItem(r, props.currentUser)
110             }
111         })
112         return (
113             <div className="app">
114                 <h2>
115                     {userProfileLink}
116                     <span className="oi" data-glyph="caret-bottom"></span>
117                 </h2>
118                 <ul className="menu">
119                     <li>
120                         <a onClick={this.handleLogout}>
121                             Logout
122                         </a>
123                     </li>
124                 </ul>
125             </div>
126         )
127     }
128 }
129
130 UserNav.defaultProps = {
131     projects: [
132
133     ]
134 }
135
136 //
137 // Exported classes and functions
138 //
139
140 //
141 /**
142  * Skyquake Nav Component. Provides navigation functionality between all plugins
143  */
144 export default class skyquakeNav extends React.Component {
145     constructor(props) {
146         super(props);
147         this.state = {};
148         this.state.validateErrorEvent = 0;
149         this.state.validateErrorMsg = '';
150     }
151     componentDidMount() {
152         this.props.store.openProjectSocket();
153         this.props.store.getUserProfile();
154     }
155     validateError = (msg) => {
156         this.setState({
157             validateErrorEvent: true,
158             validateErrorMsg: msg
159         });
160     }
161     validateReset = () => {
162         this.setState({
163             validateErrorEvent: false
164         });
165     }
166     returnCrouton = () => {
167         return <Crouton
168             id={Date.now()}
169             message={this.state.validateErrorMsg}
170             type={"error"}
171             hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
172             onDismiss={this.validateReset}
173         />;
174     }
175     render() {
176         let html;
177         html = (
178                 <div>
179                 {this.returnCrouton()}
180             <nav className="skyquakeNav">
181                 {buildNav.call(this, this.props.nav, this.props.currentPlugin, this.props)}
182             </nav>
183
184             </div>
185         )
186         return html;
187     }
188 }
189 skyquakeNav.defaultProps = {
190     nav: {}
191 }
192 /**
193  * Returns a React Component
194  * @param  {object} link  Information about the nav link
195  * @param  {string} link.route Hash route that the SPA should resolve
196  * @param  {string} link.name Link name to be displayed
197  * @param  {number} index index of current array item
198  * @return {object} component A React LI Component
199  */
200 //This should be extended to also make use of internal/external links and determine if the link should refer to an outside plugin or itself.
201 export function buildNavListItem (k, link, index) {
202     let html = false;
203     if (link.type == 'external') {
204         this.hasSubNav[k] = true;
205         html = (
206             <li key={index}>
207                 {returnLinkItem(link)}
208             </li>
209         );
210     }
211     return html;
212 }
213
214 /**
215  * Builds a link to a React Router route or a new plugin route.
216  * @param  {object} link Routing information from nav object.
217  * @return {object}  component   returns a react component that links to a new route.
218  */
219 export function returnLinkItem(link, label) {
220     let ref;
221     let route = link.route;
222     if(link.isExternal) {
223         ref = (
224             <a href={route}>{label || link.label}</a>
225         )
226     } else {
227         if(link.path && link.path.replace(' ', '') != '') {
228             route = link.path;
229         }
230         if(link.query) {
231             let query = {};
232             query[link.query] = '';
233             route = {
234                 pathname: route,
235                 query: query
236             }
237         }
238         ref = (
239             <Link to={route}>
240                 {label || link.label}
241             </Link>
242         )
243     }
244     return ref;
245 }
246
247
248
249
250 /**
251  * Constructs nav for each plugin, along with available subnavs
252  * @param  {array} nav List returned from /nav endpoint.
253  * @return {array}     List of constructed nav element for each plugin
254  */
255 export function buildNav(nav, currentPlugin, props) {
256     let navList = [];
257     let navListHTML = [];
258     let secondaryNav = [];
259     let self = this;
260     self.hasSubNav = {};
261     let secondaryNavHTML = (
262         <div className="secondaryNav" key="secondaryNav">
263             {secondaryNav}
264             <SelectProject
265                 onSelectProject={props.store.selectActiveProject}
266                 projects={props.projects}
267                 currentProject={props.currentProject} />
268             <UserNav
269                 currentUser={props.currentUser}
270                 nav={nav}  />
271         </div>
272     )
273     for (let k in nav) {
274         if (nav.hasOwnProperty(k)) {
275             self.hasSubNav[k] = false;
276             let header = null;
277             let navClass = "app";
278             let routes = nav[k].routes;
279             let navItem = {};
280             //Primary plugin title and link to dashboard.
281             let route;
282             let NavList;
283             if (API_SERVER) {
284                 route = routes[0].isExternal ? '/' + k + '/index.html?api_server=' + API_SERVER + '' + '&upload_server=' + UPLOAD_SERVER + '' : '';
285             } else {
286                 route = routes[0].isExternal ? '/' + k + '/' : '';
287             }
288             let dashboardLink = returnLinkItem({
289                 isExternal: routes[0].isExternal,
290                 pluginName: nav[k].pluginName,
291                 label: nav[k].label || k,
292                 route: route
293             });
294             if (nav[k].pluginName == currentPlugin) {
295                 navClass += " active";
296             }
297             NavList = nav[k].routes.map(buildNavListItem.bind(self, k));
298             navItem.priority = nav[k].priority;
299             navItem.order = nav[k].order;
300             navItem.html = (
301                 <div key={k} className={navClass}>
302                     <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
303                     <ul className="menu">
304                         {
305                             NavList
306                         }
307                     </ul>
308                 </div>
309             );
310             navList.push(navItem)
311         }
312     }
313     //Sorts nav items by order and returns only the markup
314     navListHTML = navList.sort((a,b) => a.order - b.order).map(function(n) {
315         if((n.priority  < 2)){
316             return n.html;
317         } else {
318             secondaryNav.push(n.html);
319         }
320     });
321     navListHTML.push(secondaryNavHTML);
322     return navListHTML;
323 }