0377a54b180926c59efa8a6517b4ffee398c4687
[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({
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             });
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="Update" type="submit" onClick={this.updateUser} />
228                                         <Button label="Delete" onClick={this.deleteUser} />
229                                         <Button label="Cancel" onClick={this.cancelEditUser} />
230                                     </ButtonGroup>
231                                 )
232                                 : (
233                                     <ButtonGroup className="buttonGroup">
234                                         <Button label="Create" type="submit" onClick={this.createUser}  />
235                                     </ButtonGroup>
236                                 )
237                             )
238         }
239         html = (
240             <PanelWrapper column>
241                 <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} >
242                     <AppHeader nav={[{name: 'PLATFORM ROLE MANAGEMENT', onClick: this.context.router.push.bind(this, {pathname: '/platform'})}]}/>
243                 </SkyquakeRBAC>
244                 <PanelWrapper className={`row userManagement ${!this.state.userOpen ? 'userList-open' : ''}`} style={{'flexDirection': 'row'}} >
245                     <PanelWrapper ref={(div) => { this.UserList = div}} className={`column userList expanded ${this.state.userOpen ? 'collapsed ' : ' '} ${this.state.hideColumns ? 'hideColumns ' : ' '}`}>
246                         <Panel title="User List" style={{marginBottom: 0}} no-corners>
247                             <div className="tableRow tableRow--header">
248                                 <div className="userName">
249                                     Username
250                                 </div>
251                                 <div>
252                                     Domain
253                                 </div>
254                             </div>
255                             {state.users && state.users.map((u, k) => {
256                                 let platformRoles = [];
257                                 for(let role in u.platformRoles) {
258                                     platformRoles.push(<div>{`${role}: ${u.platformRoles[role]}`}</div>)
259                                 }
260                                 return (
261                                     <div ref={(el) => this[`user-ref-${k}`] = el} className={`tableRow tableRow--data ${((self.state.activeIndex == k) && self.state.userOpen) ? 'tableRow--data-active' : ''}`}
262                                         key={k}
263                                         onClick={self.viewUser.bind(null, u, k)}>
264                                         <div
265                                             className={`userName userName-header ${((self.state.activeIndex == k) && self.state.userOpen) ? 'activeUser' : ''}`}
266                                             >
267                                             {u['user-name']}
268                                         </div>
269                                         <div>
270                                             {u['user-domain']}
271                                         </div>
272
273
274                                     </div>
275                                 )
276                             })}
277                         </Panel>
278                         <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} className="rbacButtonGroup">
279                             <ButtonGroup  className="buttonGroup">
280                                 <Button label="Add User" onClick={this.addUser} />
281                         </ButtonGroup>
282                         </SkyquakeRBAC>
283                     </PanelWrapper>
284                     <PanelWrapper onKeyUp={this.evaluateSubmit}
285                         className={`userAdmin column`}>
286                         <Panel
287                             title={state.isEdit ? state['user-name'] : 'Create User'}
288                             style={{marginBottom: 0}}
289                             hasCloseButton={this.closePanel}
290                             no-corners>
291                             <FormSection title="USER INFO">
292                                 {
293                                     this.state.isEdit ?
294                                         null
295                                         : <Input  readonly={state.isReadOnly}  label="Username" value={state['user-name']} onChange={this.updateInput.bind(null, 'user-name')} />
296                                 }
297                                 <Input readonly={true} label="Domain" value={state['user-domain']}  onChange={this.updateInput.bind(null, 'user-domain')}></Input>
298                                 <Input
299                                     type="radiogroup"
300                                     onChange={this.disableChange}
301                                     label="Disabled"
302                                     value={this.state.disabled}
303                                     options={["TRUE","FALSE"]}
304                                     readonly={state.isReadOnly} />
305                             </FormSection>
306                             <FormSection title="PLATFORM ROLES" style={{display:'none'}}>
307                                 <Input label="Super Admin" onChange={this.platformChange.bind(null, 'super_admin')} checked={state.platformRoles.super_admin} type="checkbox" />
308                                 <Input label="Platform Admin" onChange={this.platformChange.bind(null, 'platform_admin')}  checked={state.platformRoles.platform_admin} type="checkbox" />
309                                 <Input label="Platform Oper" onChange={this.platformChange.bind(null, 'platform_oper')}  checked={state.platformRoles.platform_oper} type="checkbox" />
310                             </FormSection>
311                             <FormSection title="PROJECT ROLES" style={{display:'none'}}>
312                                 <InputCollection
313                                     inital={true}
314                                     type='select'
315                                     readonly={state.isReadOnly}
316                                     options={state.projectRolesOptions}
317                                     collection={state.projectRoles}
318                                     onChange={this.updateProjectRole}
319                                     AddItemFn={this.addProjectRole}
320                                     RemoveItemFn={this.removeProjectRole}
321                                     />
322                             </FormSection>
323                             {passwordSectionHTML}
324
325                         </Panel>
326                         <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} className="rbacButtonGroup">
327                             {formButtonsHTML}
328                         </SkyquakeRBAC>
329                     </PanelWrapper>
330                 </PanelWrapper>
331             </PanelWrapper>
332         );
333         return html;
334     }
335 }
336 // onClick={this.Store.update.bind(null, Account)}
337 UserManagementDashboard.contextTypes = {
338     router: React.PropTypes.object,
339     userProfile: React.PropTypes.object
340 };
341
342 UserManagementDashboard.defaultProps = {
343     userList: [],
344     selectedUser: {}
345 }
346
347 export default SkyquakeComponent(UserManagementDashboard);
348
349
350 function isElementInView(el) {
351     var rect = el && el.getBoundingClientRect() || {};
352
353     return (
354         rect.top >= 0 &&
355         rect.left >= 0 &&
356         rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
357         rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
358     );
359 }
360
361
362 // isReadOnly={state.isReadOnly} disabled={state.disabled} onChange={this.disableChange}
363
364 class isDisabled extends React.Component {
365     constructor(props) {
366         super(props);
367     }
368     render() {
369         let props = this.props;
370         return (<div/>)
371     }
372 }
373
374 /**
375  * AddItemFn:
376  */
377 class InputCollection extends React.Component {
378     constructor(props) {
379         super(props);
380         this.collection = props.collection;
381     }
382     buildTextInput(onChange, v, i) {
383         return (
384             <Input
385                 readonly={this.props.readonly}
386                 style={{flex: '1 1'}}
387                 key={i}
388                 value={v}
389                 onChange= {onChange.bind(null, i)}
390             />
391         )
392     }
393     buildSelectOption(initial, options, onChange, v, i) {
394         return (
395             <SelectOption
396                 readonly={this.props.readonly}
397                 key={`${i}-${v.replace(' ', '_')}`}
398                 intial={initial}
399                 defaultValue={v}
400                 options={options}
401                 onChange={onChange.bind(null, i)}
402             />
403         );
404     }
405     showInput() {
406
407     }
408     render() {
409         const props = this.props;
410         let inputType;
411         let className = "InputCollection";
412         if (props.className) {
413             className = `${className} ${props.className}`;
414         }
415         if (props.type == 'select') {
416             inputType = this.buildSelectOption.bind(this, props.initial, props.options, props.onChange);
417         } else {
418             inputType = this.buildTextInput.bind(this, props.onChange)
419         }
420         let html = (
421             <div className="InputCollection-wrapper">
422                 {props.collection.map((v,i) => {
423                     return (
424                         <div key={i} className={className} >
425                             {inputType(v, i)}
426                             {
427                                 props.readonly ? null : <span onClick={props.RemoveItemFn.bind(null, i)} className="removeInput"><img src={imgRemove} />Remove</span>}
428                         </div>
429                     )
430                 })}
431                 { props.readonly ? null : <span onClick={props.AddItemFn} className="addInput"><img src={imgAdd} />Add</span>}
432             </div>
433         );
434         return html;
435     }
436 }
437
438 InputCollection.defaultProps = {
439     input: Input,
440     collection: [],
441     onChange: function(i, e) {
442         console.log(`
443                         Updating with: ${e.target.value}
444                         At index of: ${i}
445                     `)
446     },
447     AddItemFn: function(e) {
448         console.log(`Adding a new item to collection`)
449     },
450     RemoveItemFn: function(i, e) {
451         console.log(`Removing item from collection at index of: ${i}`)
452     }
453 }
454
455 class FormSection extends React.Component {
456     render() {
457         let className = 'FormSection ' + this.props.className;
458         let html = (
459             <div
460                 style={this.props.style}
461                 className={className}
462             >
463                 <div className="FormSection-title">
464                     {this.props.title}
465                 </div>
466                 <div className="FormSection-body">
467                     {this.props.children}
468                 </div>
469             </div>
470         );
471         return html;
472     }
473 }
474
475 FormSection.defaultProps = {
476     className: ''
477 }