Merge "RIFT-16181: Project switch must affect non-polling/non-socket based pages...
[osm/UI.git] / skyquake / plugins / user_management / src / dashboard / dashboard.jsx
1 /*
2  * STANDARD_RIFT_IO_COPYRIGHT
3  */
4
5 import React from 'react';
6 import ReactDOM from 'react-dom';
7 import AppHeader from 'widgets/header/header.jsx';
8 import UserManagementStore from './userMgmtStore.js';
9 import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
10 import 'style/layout.scss';
11 import './userMgmt.scss';
12 import {Panel, PanelWrapper} from 'widgets/panel/panel';
13 import SkyquakeRBAC from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
14
15 import TextInput from 'widgets/form_controls/textInput.jsx';
16 import Input from 'widgets/form_controls/input.jsx';
17 import Button, {ButtonGroup} from 'widgets/button/sq-button.jsx';
18 import SelectOption from 'widgets/form_controls/selectOption.jsx';
19 import 'widgets/form_controls/formControls.scss';
20 import imgAdd from '../../node_modules/open-iconic/svg/plus.svg'
21 import imgRemove from '../../node_modules/open-iconic/svg/trash.svg';
22
23 import ROLES from 'utils/roleConstants.js';
24 const PLATFORM = ROLES.PLATFORM;
25
26 class UserManagementDashboard extends React.Component {
27     constructor(props) {
28         super(props);
29         this.Store = this.props.flux.stores.hasOwnProperty('UserManagementStore') ? this.props.flux.stores.UserManagementStore : this.props.flux.createStore(UserManagementStore);
30        this.state = this.Store.getState();
31        this.actions = this.state.actions;
32     }
33     componentDidUpdate() {
34         let self = this;
35         ReactDOM.findDOMNode(this.UserList).addEventListener('transitionend', this.onTransitionEnd, false);
36         setTimeout(function() {
37             let element = self[`user-ref-${self.state.activeIndex}`]
38             element && !isElementInView(element) && element.scrollIntoView({block: 'end', behavior: 'smooth'});
39         })
40     }
41     componentWillMount() {
42         this.Store.listen(this.updateState);
43         this.Store.getUsers();
44     }
45     componentWillUnmount() {
46         this.Store.unlisten(this.updateState);
47     }
48     updateState = (state) => {
49         this.setState(state);
50     }
51     updateInput = (key, e) => {
52         let property = key;
53         this.actions.handleUpdateInput({
54             [property]:e.target.value
55         })
56     }
57     disabledChange = (e) => {
58         this.actions.handleDisabledChange(e.target.checked);
59     }
60     platformChange = (platformRole, e) => {
61         this.actions.handlePlatformRoleUpdate(platformRole, e.currentTarget.checked);
62     }
63     addProjectRole = (e) => {
64         this.actions.handleAddProjectItem();
65     }
66     removeProjectRole = (i, e) => {
67         this.actions.handleRemoveProjectItem(i);
68     }
69     updateProjectRole = (i, e) => {
70         this.actions.handleUpdateProjectRole(i, e)
71     }
72     addUser = () => {
73         this.actions.handleAddUser();
74     }
75     viewUser = (un, index) => {
76         this.actions.viewUser(un, index);
77     }
78     editUser = () => {
79         this.actions.editUser(false);
80     }
81     cancelEditUser = () => {
82         this.actions.editUser(true)
83     }
84     osePanel = () => {
85         this.actions.handleCloseUserPanel();
86     }
87     // updateUser = (e) => {
88     //     e.preventDefault();
89     //     e.stopPropagation();
90
91     //     this.Store.updateUser();
92     // }
93     deleteUser = (e) => {
94         e.preventDefault();
95         e.stopPropagation();
96         this.Store.deleteUser({
97                 'user-name': this.state['user-name'],
98                 'user-domain': this.state['user-domain']
99             });
100     }
101     createUser = (e) => {
102         e.preventDefault();
103         e.stopPropagation();
104         if(this.state['new-password'] != this.state['confirm-password']) {
105             this.props.actions.showNotification('Passwords do not match')
106         } else {
107             this.Store.createUser({
108                 'user-name': this.state['user-name'],
109                 'user-domain': this.state['user-domain'],
110                 'password': this.state['new-password']
111                 // 'confirm-password': this.state['confirm-password']
112             });
113         }
114     }
115     updateUser = (e) => {
116         let self = this;
117         e.preventDefault();
118         e.stopPropagation();
119         let validatedPasswords = validatePasswordFields(this.state);
120         if(validatedPasswords) {
121             this.Store.updateUser(_.merge({
122                             'user-name': this.state['user-name'],
123                             'user-domain': this.state['user-domain'],
124                             'password': this.state['new-password']
125                         }));
126         }
127         function validatePasswordFields(state) {
128             let oldOne = state['old-password'];
129             let newOne = state['new-password'];
130             let confirmOne = state['confirm-password'];
131             if(true) {
132                 if(oldOne == newOne) {
133                     self.props.actions.showNotification('Your new password must not match your old one');
134                     return false;
135                 }
136                 if(newOne != confirmOne) {
137                     self.props.actions.showNotification('Passwords do not match');
138                     return false;
139                 }
140                 return {
141                     // 'old-password': oldOne,
142                     'new-password': newOne,
143                     'confirm-password': confirmOne
144                 }
145             } else {
146                 return {};
147             }
148         }
149     }
150      evaluateSubmit = (e) => {
151         if (e.keyCode == 13) {
152             if (this.props.isEdit) {
153                 this.updateUser(e);
154             } else {
155                 this.createUser(e);
156             }
157             e.preventDefault();
158             e.stopPropagation();
159         }
160     }
161     onTransitionEnd = (e) => {
162         this.actions.handleHideColumns(e);
163         console.log('transition end')
164     }
165     disableChange = (e) => {
166         let value = e.target.value;
167         value = value.toUpperCase();
168         if (value=="TRUE") {
169             value = true;
170         } else {
171             value = false;
172         }
173         console.log(value)
174     }
175     render() {
176         let self = this;
177         let html;
178         let props = this.props;
179         let state = this.state;
180         let passwordSectionHTML = null;
181         let formButtonsHTML = (
182             <ButtonGroup className="buttonGroup">
183                 <Button label="EDIT" type="submit" onClick={this.editUser} />
184             </ButtonGroup>
185         );
186         if(!this.state.isReadOnly) {
187             passwordSectionHTML = ( this.state.isEdit ?
188                                         (
189                                             <FormSection title="PASSWORD CHANGE">
190                                                 <Input label="NEW PASSWORD" type="password" value={state['new-password']}  onChange={this.updateInput.bind(null, 'new-password')}/>
191                                                 <Input label="REPEAT NEW PASSWORD" type="password"  value={state['confirm-password']}  onChange={this.updateInput.bind(null, 'confirm-password')}/>
192                                             </FormSection>
193                                         ) :
194                                         (
195                                             <FormSection title="CREATE PASSWORD">
196                                                 <Input label="CREATE PASSWORD" type="password" value={state.newPassword}  onChange={this.updateInput.bind(null, 'new-password')}/>
197                                                 <Input label="REPEAT PASSWORD" type="password"  value={state.repeatNewPassword}  onChange={this.updateInput.bind(null, 'confirm-password')}/>
198                                             </FormSection>
199                                         )
200                                     );
201             formButtonsHTML = (
202                                 state.isEdit ?
203                                 (
204                                     <ButtonGroup className="buttonGroup">
205                                         <Button label="Update" type="submit" onClick={this.updateUser} />
206                                         <Button label="Delete" onClick={this.deleteUser} />
207                                         <Button label="Cancel" onClick={this.cancelEditUser} />
208                                     </ButtonGroup>
209                                 )
210                                 : (
211                                     <ButtonGroup className="buttonGroup">
212                                         <Button label="Create" type="submit" onClick={this.createUser}  />
213                                     </ButtonGroup>
214                                 )
215                             )
216         }
217         html = (
218             <PanelWrapper column>
219                 <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} >
220                     <AppHeader nav={[{name: 'PLATFORM ROLE MANAGEMENT', onClick: this.context.router.push.bind(this, {pathname: '/platform'})}]}/>
221                 </SkyquakeRBAC>
222                 <PanelWrapper className={`row userManagement ${!this.state.userOpen ? 'userList-open' : ''}`} style={{'flexDirection': 'row'}} >
223                     <PanelWrapper ref={(div) => { this.UserList = div}} className={`column userList expanded ${this.state.userOpen ? 'collapsed ' : ' '} ${this.state.hideColumns ? 'hideColumns ' : ' '}`}>
224                         <Panel title="User List" style={{marginBottom: 0}} no-corners>
225                             <div className="tableRow tableRow--header">
226                                 <div className="userName">
227                                     Username
228                                 </div>
229                                 <div>
230                                     Domain
231                                 </div>
232                             </div>
233                             {state.users && state.users.map((u, k) => {
234                                 let platformRoles = [];
235                                 for(let role in u.platformRoles) {
236                                     platformRoles.push(<div>{`${role}: ${u.platformRoles[role]}`}</div>)
237                                 }
238                                 return (
239                                     <div ref={(el) => this[`user-ref-${k}`] = el} className={`tableRow tableRow--data ${((self.state.activeIndex == k) && self.state.userOpen) ? 'tableRow--data-active' : ''}`}
240                                         key={k}
241                                         onClick={self.viewUser.bind(null, u, k)}>
242                                         <div
243                                             className={`userName userName-header ${((self.state.activeIndex == k) && self.state.userOpen) ? 'activeUser' : ''}`}
244                                             >
245                                             {u['user-name']}
246                                         </div>
247                                         <div>
248                                             {u['user-domain']}
249                                         </div>
250
251
252                                     </div>
253                                 )
254                             })}
255                         </Panel>
256                         <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} className="rbacButtonGroup">
257                             <ButtonGroup  className="buttonGroup">
258                                 <Button label="Add User" onClick={this.addUser} />
259                         </ButtonGroup>
260                         </SkyquakeRBAC>
261                     </PanelWrapper>
262                     <PanelWrapper onKeyUp={this.evaluateSubmit}
263                         className={`userAdmin column`}>
264                         <Panel
265                             title={state.isEdit ? state['user-name'] : 'Create User'}
266                             style={{marginBottom: 0}}
267                             hasCloseButton={this.closePanel}
268                             no-corners>
269                             <FormSection title="USER INFO">
270                                 {
271                                     this.state.isEdit ?
272                                         null
273                                         : <Input  readonly={state.isReadOnly}  label="Username" value={state['user-name']} onChange={this.updateInput.bind(null, 'user-name')} />
274                                 }
275                                 <Input readonly={true} label="Domain" value={state['user-domain']}  onChange={this.updateInput.bind(null, 'user-domain')}></Input>
276                             </FormSection>
277                             <FormSection title="PLATFORM ROLES" style={{display:'none'}}>
278                                 <Input label="Super Admin" onChange={this.platformChange.bind(null, 'super_admin')} checked={state.platformRoles.super_admin} type="checkbox" />
279                                 <Input label="Platform Admin" onChange={this.platformChange.bind(null, 'platform_admin')}  checked={state.platformRoles.platform_admin} type="checkbox" />
280                                 <Input label="Platform Oper" onChange={this.platformChange.bind(null, 'platform_oper')}  checked={state.platformRoles.platform_oper} type="checkbox" />
281                             </FormSection>
282                             <FormSection title="PROJECT ROLES" style={{display:'none'}}>
283                                 <InputCollection
284                                     inital={true}
285                                     type='select'
286                                     readonly={state.isReadOnly}
287                                     options={state.projectRolesOptions}
288                                     collection={state.projectRoles}
289                                     onChange={this.updateProjectRole}
290                                     AddItemFn={this.addProjectRole}
291                                     RemoveItemFn={this.removeProjectRole}
292                                     />
293                             </FormSection>
294                             {passwordSectionHTML}
295
296                         </Panel>
297                         <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} className="rbacButtonGroup">
298                             {formButtonsHTML}
299                         </SkyquakeRBAC>
300                     </PanelWrapper>
301                 </PanelWrapper>
302             </PanelWrapper>
303         );
304         return html;
305     }
306 }
307 // onClick={this.Store.update.bind(null, Account)}
308 UserManagementDashboard.contextTypes = {
309     router: React.PropTypes.object,
310     userProfile: React.PropTypes.object
311 };
312
313 UserManagementDashboard.defaultProps = {
314     userList: [],
315     selectedUser: {}
316 }
317
318 export default SkyquakeComponent(UserManagementDashboard);
319
320
321 function isElementInView(el) {
322     var rect = el && el.getBoundingClientRect() || {};
323
324     return (
325         rect.top >= 0 &&
326         rect.left >= 0 &&
327         rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
328         rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
329     );
330 }
331
332
333 // isReadOnly={state.isReadOnly} disabled={state.disabled} onChange={this.disableChange}
334
335 class isDisabled extends React.Component {
336     constructor(props) {
337         super(props);
338     }
339     render() {
340         let props = this.props;
341         return (<div/>)
342     }
343 }
344
345 /**
346  * AddItemFn:
347  */
348 class InputCollection extends React.Component {
349     constructor(props) {
350         super(props);
351         this.collection = props.collection;
352     }
353     buildTextInput(onChange, v, i) {
354         return (
355             <Input
356                 readonly={this.props.readonly}
357                 style={{flex: '1 1'}}
358                 key={i}
359                 value={v}
360                 onChange= {onChange.bind(null, i)}
361             />
362         )
363     }
364     buildSelectOption(initial, options, onChange, v, i) {
365         return (
366             <SelectOption
367                 readonly={this.props.readonly}
368                 key={`${i}-${v.replace(' ', '_')}`}
369                 intial={initial}
370                 defaultValue={v}
371                 options={options}
372                 onChange={onChange.bind(null, i)}
373             />
374         );
375     }
376     showInput() {
377
378     }
379     render() {
380         const props = this.props;
381         let inputType;
382         let className = "InputCollection";
383         if (props.className) {
384             className = `${className} ${props.className}`;
385         }
386         if (props.type == 'select') {
387             inputType = this.buildSelectOption.bind(this, props.initial, props.options, props.onChange);
388         } else {
389             inputType = this.buildTextInput.bind(this, props.onChange)
390         }
391         let html = (
392             <div className="InputCollection-wrapper">
393                 {props.collection.map((v,i) => {
394                     return (
395                         <div key={i} className={className} >
396                             {inputType(v, i)}
397                             {
398                                 props.readonly ? null : <span onClick={props.RemoveItemFn.bind(null, i)} className="removeInput"><img src={imgRemove} />Remove</span>}
399                         </div>
400                     )
401                 })}
402                 { props.readonly ? null : <span onClick={props.AddItemFn} className="addInput"><img src={imgAdd} />Add</span>}
403             </div>
404         );
405         return html;
406     }
407 }
408
409 InputCollection.defaultProps = {
410     input: Input,
411     collection: [],
412     onChange: function(i, e) {
413         console.log(`
414                         Updating with: ${e.target.value}
415                         At index of: ${i}
416                     `)
417     },
418     AddItemFn: function(e) {
419         console.log(`Adding a new item to collection`)
420     },
421     RemoveItemFn: function(i, e) {
422         console.log(`Removing item from collection at index of: ${i}`)
423     }
424 }
425
426 class FormSection extends React.Component {
427     render() {
428         let className = 'FormSection ' + this.props.className;
429         let html = (
430             <div
431                 style={this.props.style}
432                 className={className}
433             >
434                 <div className="FormSection-title">
435                     {this.props.title}
436                 </div>
437                 <div className="FormSection-body">
438                     {this.props.children}
439                 </div>
440             </div>
441         );
442         return html;
443     }
444 }
445
446 FormSection.defaultProps = {
447     className: ''
448 }