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