Account page RBAC
[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         if (confirm('Are you sure you want to delete this user?')) {
97             this.Store.deleteUser({
98                 'user-name': this.state['user-name'],
99                 'user-domain': this.state['user-domain']
100             });
101         }
102
103     }
104     createUser = (e) => {
105         e.preventDefault();
106         e.stopPropagation();
107         if(this.state['new-password'] != this.state['confirm-password']) {
108             this.props.actions.showNotification('Passwords do not match')
109         } else {
110             this.Store.createUser({
111                 'user-name': this.state['user-name'],
112                 'user-domain': this.state['user-domain'],
113                 'password': this.state['new-password']
114                 // 'confirm-password': this.state['confirm-password']
115             });
116         }
117     }
118     updateUser = (e) => {
119         let self = this;
120         e.preventDefault();
121         e.stopPropagation();
122         let validatedPasswords = validatePasswordFields(this.state);
123         if(validatedPasswords) {
124             this.Store.updateUser(_.merge({
125                             'user-name': this.state['user-name'],
126                             'user-domain': this.state['user-domain'],
127                             'password': this.state['new-password']
128                         }));
129         }
130         function validatePasswordFields(state) {
131             let oldOne = state['old-password'];
132             let newOne = state['new-password'];
133             let confirmOne = state['confirm-password'];
134             if(true) {
135                 if(oldOne == newOne) {
136                     self.props.actions.showNotification('Your new password must not match your old one');
137                     return false;
138                 }
139                 if(newOne != confirmOne) {
140                     self.props.actions.showNotification('Passwords do not match');
141                     return false;
142                 }
143                 return {
144                     // 'old-password': oldOne,
145                     'new-password': newOne,
146                     'confirm-password': confirmOne
147                 }
148             } else {
149                 return {};
150             }
151         }
152     }
153      evaluateSubmit = (e) => {
154         if (e.keyCode == 13) {
155             if (this.props.isEdit) {
156                 this.updateUser(e);
157             } else {
158                 this.createUser(e);
159             }
160             e.preventDefault();
161             e.stopPropagation();
162         }
163     }
164     onTransitionEnd = (e) => {
165         this.actions.handleHideColumns(e);
166         console.log('transition end')
167     }
168     disableChange = (e) => {
169         let value = e.target.value;
170         value = value.toUpperCase();
171         if (value=="TRUE") {
172             value = true;
173         } else {
174             value = false;
175         }
176         console.log(value)
177     }
178     render() {
179         let self = this;
180         let html;
181         let props = this.props;
182         let state = this.state;
183         let passwordSectionHTML = null;
184         let formButtonsHTML = (
185             <ButtonGroup className="buttonGroup">
186                 <Button label="EDIT" type="submit" onClick={this.editUser} />
187             </ButtonGroup>
188         );
189         if(!this.state.isReadOnly) {
190             passwordSectionHTML = ( this.state.isEdit ?
191                                         (
192                                             <FormSection title="PASSWORD CHANGE">
193                                                 <Input label="NEW PASSWORD" type="password" value={state['new-password']}  onChange={this.updateInput.bind(null, 'new-password')}/>
194                                                 <Input label="REPEAT NEW PASSWORD" type="password"  value={state['confirm-password']}  onChange={this.updateInput.bind(null, 'confirm-password')}/>
195                                             </FormSection>
196                                         ) :
197                                         (
198                                             <FormSection title="CREATE PASSWORD">
199                                                 <Input label="CREATE PASSWORD" type="password" value={state.newPassword}  onChange={this.updateInput.bind(null, 'new-password')}/>
200                                                 <Input label="REPEAT PASSWORD" type="password"  value={state.repeatNewPassword}  onChange={this.updateInput.bind(null, 'confirm-password')}/>
201                                             </FormSection>
202                                         )
203                                     );
204             formButtonsHTML = (
205                                 state.isEdit ?
206                                 (
207                                     <ButtonGroup className="buttonGroup">
208                                         <Button label="Update" type="submit" onClick={this.updateUser} />
209                                         <Button label="Delete" onClick={this.deleteUser} />
210                                         <Button label="Cancel" onClick={this.cancelEditUser} />
211                                     </ButtonGroup>
212                                 )
213                                 : (
214                                     <ButtonGroup className="buttonGroup">
215                                         <Button label="Create" type="submit" onClick={this.createUser}  />
216                                     </ButtonGroup>
217                                 )
218                             )
219         }
220         html = (
221             <PanelWrapper column>
222                 <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} >
223                     <AppHeader nav={[{name: 'PLATFORM ROLE MANAGEMENT', onClick: this.context.router.push.bind(this, {pathname: '/platform'})}]}/>
224                 </SkyquakeRBAC>
225                 <PanelWrapper className={`row userManagement ${!this.state.userOpen ? 'userList-open' : ''}`} style={{'flexDirection': 'row'}} >
226                     <PanelWrapper ref={(div) => { this.UserList = div}} className={`column userList expanded ${this.state.userOpen ? 'collapsed ' : ' '} ${this.state.hideColumns ? 'hideColumns ' : ' '}`}>
227                         <Panel title="User List" style={{marginBottom: 0}} no-corners>
228                             <div className="tableRow tableRow--header">
229                                 <div className="userName">
230                                     Username
231                                 </div>
232                                 <div>
233                                     Domain
234                                 </div>
235                             </div>
236                             {state.users && state.users.map((u, k) => {
237                                 let platformRoles = [];
238                                 for(let role in u.platformRoles) {
239                                     platformRoles.push(<div>{`${role}: ${u.platformRoles[role]}`}</div>)
240                                 }
241                                 return (
242                                     <div ref={(el) => this[`user-ref-${k}`] = el} className={`tableRow tableRow--data ${((self.state.activeIndex == k) && self.state.userOpen) ? 'tableRow--data-active' : ''}`}
243                                         key={k}
244                                         onClick={self.viewUser.bind(null, u, k)}>
245                                         <div
246                                             className={`userName userName-header ${((self.state.activeIndex == k) && self.state.userOpen) ? 'activeUser' : ''}`}
247                                             >
248                                             {u['user-name']}
249                                         </div>
250                                         <div>
251                                             {u['user-domain']}
252                                         </div>
253
254
255                                     </div>
256                                 )
257                             })}
258                         </Panel>
259                         <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} className="rbacButtonGroup">
260                             <ButtonGroup  className="buttonGroup">
261                                 <Button label="Add User" onClick={this.addUser} />
262                         </ButtonGroup>
263                         </SkyquakeRBAC>
264                     </PanelWrapper>
265                     <PanelWrapper onKeyUp={this.evaluateSubmit}
266                         className={`userAdmin column`}>
267                         <Panel
268                             title={state.isEdit ? state['user-name'] : 'Create User'}
269                             style={{marginBottom: 0}}
270                             hasCloseButton={this.closePanel}
271                             no-corners>
272                             <FormSection title="USER INFO">
273                                 {
274                                     this.state.isEdit ?
275                                         null
276                                         : <Input  readonly={state.isReadOnly}  label="Username" value={state['user-name']} onChange={this.updateInput.bind(null, 'user-name')} />
277                                 }
278                                 <Input readonly={true} label="Domain" value={state['user-domain']}  onChange={this.updateInput.bind(null, 'user-domain')}></Input>
279                             </FormSection>
280                             <FormSection title="PLATFORM ROLES" style={{display:'none'}}>
281                                 <Input label="Super Admin" onChange={this.platformChange.bind(null, 'super_admin')} checked={state.platformRoles.super_admin} type="checkbox" />
282                                 <Input label="Platform Admin" onChange={this.platformChange.bind(null, 'platform_admin')}  checked={state.platformRoles.platform_admin} type="checkbox" />
283                                 <Input label="Platform Oper" onChange={this.platformChange.bind(null, 'platform_oper')}  checked={state.platformRoles.platform_oper} type="checkbox" />
284                             </FormSection>
285                             <FormSection title="PROJECT ROLES" style={{display:'none'}}>
286                                 <InputCollection
287                                     inital={true}
288                                     type='select'
289                                     readonly={state.isReadOnly}
290                                     options={state.projectRolesOptions}
291                                     collection={state.projectRoles}
292                                     onChange={this.updateProjectRole}
293                                     AddItemFn={this.addProjectRole}
294                                     RemoveItemFn={this.removeProjectRole}
295                                     />
296                             </FormSection>
297                             {passwordSectionHTML}
298
299                         </Panel>
300                         <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} className="rbacButtonGroup">
301                             {formButtonsHTML}
302                         </SkyquakeRBAC>
303                     </PanelWrapper>
304                 </PanelWrapper>
305             </PanelWrapper>
306         );
307         return html;
308     }
309 }
310 // onClick={this.Store.update.bind(null, Account)}
311 UserManagementDashboard.contextTypes = {
312     router: React.PropTypes.object,
313     userProfile: React.PropTypes.object
314 };
315
316 UserManagementDashboard.defaultProps = {
317     userList: [],
318     selectedUser: {}
319 }
320
321 export default SkyquakeComponent(UserManagementDashboard);
322
323
324 function isElementInView(el) {
325     var rect = el && el.getBoundingClientRect() || {};
326
327     return (
328         rect.top >= 0 &&
329         rect.left >= 0 &&
330         rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
331         rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
332     );
333 }
334
335
336 // isReadOnly={state.isReadOnly} disabled={state.disabled} onChange={this.disableChange}
337
338 class isDisabled extends React.Component {
339     constructor(props) {
340         super(props);
341     }
342     render() {
343         let props = this.props;
344         return (<div/>)
345     }
346 }
347
348 /**
349  * AddItemFn:
350  */
351 class InputCollection extends React.Component {
352     constructor(props) {
353         super(props);
354         this.collection = props.collection;
355     }
356     buildTextInput(onChange, v, i) {
357         return (
358             <Input
359                 readonly={this.props.readonly}
360                 style={{flex: '1 1'}}
361                 key={i}
362                 value={v}
363                 onChange= {onChange.bind(null, i)}
364             />
365         )
366     }
367     buildSelectOption(initial, options, onChange, v, i) {
368         return (
369             <SelectOption
370                 readonly={this.props.readonly}
371                 key={`${i}-${v.replace(' ', '_')}`}
372                 intial={initial}
373                 defaultValue={v}
374                 options={options}
375                 onChange={onChange.bind(null, i)}
376             />
377         );
378     }
379     showInput() {
380
381     }
382     render() {
383         const props = this.props;
384         let inputType;
385         let className = "InputCollection";
386         if (props.className) {
387             className = `${className} ${props.className}`;
388         }
389         if (props.type == 'select') {
390             inputType = this.buildSelectOption.bind(this, props.initial, props.options, props.onChange);
391         } else {
392             inputType = this.buildTextInput.bind(this, props.onChange)
393         }
394         let html = (
395             <div className="InputCollection-wrapper">
396                 {props.collection.map((v,i) => {
397                     return (
398                         <div key={i} className={className} >
399                             {inputType(v, i)}
400                             {
401                                 props.readonly ? null : <span onClick={props.RemoveItemFn.bind(null, i)} className="removeInput"><img src={imgRemove} />Remove</span>}
402                         </div>
403                     )
404                 })}
405                 { props.readonly ? null : <span onClick={props.AddItemFn} className="addInput"><img src={imgAdd} />Add</span>}
406             </div>
407         );
408         return html;
409     }
410 }
411
412 InputCollection.defaultProps = {
413     input: Input,
414     collection: [],
415     onChange: function(i, e) {
416         console.log(`
417                         Updating with: ${e.target.value}
418                         At index of: ${i}
419                     `)
420     },
421     AddItemFn: function(e) {
422         console.log(`Adding a new item to collection`)
423     },
424     RemoveItemFn: function(i, e) {
425         console.log(`Removing item from collection at index of: ${i}`)
426     }
427 }
428
429 class FormSection extends React.Component {
430     render() {
431         let className = 'FormSection ' + this.props.className;
432         let html = (
433             <div
434                 style={this.props.style}
435                 className={className}
436             >
437                 <div className="FormSection-title">
438                     {this.props.title}
439                 </div>
440                 <div className="FormSection-body">
441                     {this.props.children}
442                 </div>
443             </div>
444         );
445         return html;
446     }
447 }
448
449 FormSection.defaultProps = {
450     className: ''
451 }