Project switcher and user dropdown
[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.map((p,i) => {
67             return {
68                 label: p.name,
69                 value: p.name
70             }
71         });
72         return (
73             <div className="userSection app">
74                 Project:
75                 <SelectOption
76                     options={projects}
77                     value={currentValue}
78                     defaultValue={currentValue}
79                     onChange={props.onSelectProject}
80                     className="projectSelect" />
81             </div>
82         )
83     }
84 }
85
86 class UserNav extends React.Component {
87     constructor(props) {
88         super(props);
89     }
90     handleLogout() {
91         Utils.clearAuthentication();
92     }
93     selectProject(e) {
94         let value = JSON.parse(e.currentTarget.value)
95         console.log('selected project', value)
96     }
97     render() {
98         let props = this.props;
99         return (
100             <div className="app">
101                 <h2>
102                     <a>
103                         {props.currentUser}
104                     </a>
105                     <span className="oi" data-glyph="caret-bottom"></span>
106                 </h2>
107                 <ul className="menu">
108                     <li>
109                         <a onClick={this.handleLogout}>
110                             Logout
111                         </a>
112                     </li>
113                 </ul>
114             </div>
115         )
116     }
117 }
118
119 UserNav.defaultProps = {
120     projects: [
121
122     ]
123 }
124
125 //
126 // Exported classes and functions
127 //
128
129 //
130 /**
131  * Skyquake Nav Component. Provides navigation functionality between all plugins
132  */
133 export default class skyquakeNav extends React.Component {
134     constructor(props) {
135         super(props);
136         this.state = {};
137         this.state.validateErrorEvent = 0;
138         this.state.validateErrorMsg = '';
139     }
140     componentDidMount() {
141         this.props.store.openProjectSocket();
142         this.props.store.getUserProfile();
143     }
144     validateError = (msg) => {
145         this.setState({
146             validateErrorEvent: true,
147             validateErrorMsg: msg
148         });
149     }
150     validateReset = () => {
151         this.setState({
152             validateErrorEvent: false
153         });
154     }
155     returnCrouton = () => {
156         return <Crouton
157             id={Date.now()}
158             message={this.state.validateErrorMsg}
159             type={"error"}
160             hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
161             onDismiss={this.validateReset}
162         />;
163     }
164     render() {
165         let html;
166         html = (
167                 <div>
168                 {this.returnCrouton()}
169             <nav className="skyquakeNav">
170                 {buildNav.call(this, this.props.nav, this.props.currentPlugin, this.props)}
171             </nav>
172
173             </div>
174         )
175         return html;
176     }
177 }
178 skyquakeNav.defaultProps = {
179     nav: {}
180 }
181 /**
182  * Returns a React Component
183  * @param  {object} link  Information about the nav link
184  * @param  {string} link.route Hash route that the SPA should resolve
185  * @param  {string} link.name Link name to be displayed
186  * @param  {number} index index of current array item
187  * @return {object} component A React LI Component
188  */
189 //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.
190 export function buildNavListItem (k, link, index) {
191     let html = false;
192     if (link.type == 'external') {
193         this.hasSubNav[k] = true;
194         html = (
195             <li key={index}>
196                 {returnLinkItem(link)}
197             </li>
198         );
199     }
200     return html;
201 }
202
203 /**
204  * Builds a link to a React Router route or a new plugin route.
205  * @param  {object} link Routing information from nav object.
206  * @return {object}  component   returns a react component that links to a new route.
207  */
208 export function returnLinkItem(link) {
209     let ref;
210     let route = link.route;
211     if(link.isExternal) {
212         ref = (
213             <a href={route}>{link.label}</a>
214         )
215     } else {
216         if(link.path && link.path.replace(' ', '') != '') {
217             route = link.path;
218         }
219         if(link.query) {
220             let query = {};
221             query[link.query] = '';
222             route = {
223                 pathname: route,
224                 query: query
225             }
226         }
227         ref = (
228            <Link to={route}>
229                 {link.label}
230             </Link>
231         )
232     }
233     return ref;
234 }
235
236
237
238
239 /**
240  * Constructs nav for each plugin, along with available subnavs
241  * @param  {array} nav List returned from /nav endpoint.
242  * @return {array}     List of constructed nav element for each plugin
243  */
244 export function buildNav(nav, currentPlugin, props) {
245     let navList = [];
246     let navListHTML = [];
247     let secondaryNav = [];
248     let self = this;
249     self.hasSubNav = {};
250     let secondaryNavHTML = (
251         <div className="secondaryNav" key="secondaryNav">
252             {secondaryNav}
253             <SelectProject
254                 onSelectProject={props.store.selectActiveProject}
255                 projects={props.projects}
256                 currentProject={props.currentProject} />
257             <UserNav
258                 currentUser={props.currentUser}  />
259         </div>
260     )
261     for (let k in nav) {
262         if (nav.hasOwnProperty(k)) {
263             self.hasSubNav[k] = false;
264             let header = null;
265             let navClass = "app";
266             let routes = nav[k].routes;
267             let navItem = {};
268             //Primary plugin title and link to dashboard.
269             let route;
270             let NavList;
271             if (API_SERVER) {
272                 route = routes[0].isExternal ? '/' + k + '/index.html?api_server=' + API_SERVER + '' + '&upload_server=' + UPLOAD_SERVER + '' : '';
273             } else {
274                 route = routes[0].isExternal ? '/' + k + '/' : '';
275             }
276             let dashboardLink = returnLinkItem({
277                 isExternal: routes[0].isExternal,
278                 pluginName: nav[k].pluginName,
279                 label: nav[k].label || k,
280                 route: route
281             });
282             if (nav[k].pluginName == currentPlugin) {
283                 navClass += " active";
284             }
285             NavList = nav[k].routes.map(buildNavListItem.bind(self, k));
286             navItem.priority = nav[k].priority;
287             navItem.order = nav[k].order;
288             navItem.html = (
289                 <div key={k} className={navClass}>
290                     <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
291                     <ul className="menu">
292                         {
293                             NavList
294                         }
295                     </ul>
296                 </div>
297             );
298             navList.push(navItem)
299         }
300     }
301     //Sorts nav items by order and returns only the markup
302     navListHTML = navList.sort((a,b) => a.order - b.order).map(function(n) {
303         if((n.priority  < 2)){
304             return n.html;
305         } else {
306             secondaryNav.push(n.html);
307         }
308     });
309     navListHTML.push(secondaryNavHTML);
310     return navListHTML;
311 }