From 7302dfb98687690908844f331861aec374e95ed9 Mon Sep 17 00:00:00 2001 From: Laurence Maultsby Date: Mon, 3 Apr 2017 09:10:43 -0400 Subject: [PATCH] User profile first pass Signed-off-by: Laurence Maultsby --- .../widgets/form_controls/formControls.scss | 57 +++ .../skyquake_container/skyquakeComponent.jsx | 2 +- .../skyquake_container/skyquakeContainer.jsx | 13 +- .../skyquakeContainerStore.js | 5 +- .../widgets/skyquake_nav/skyquakeNav.jsx | 44 +- .../widgets/skyquake_nav/skyquakeNav.scss | 2 + .../src/dashboard/projectMgmt.scss | 2 +- .../src/dashboard/projectMgmtStore.js | 1 - skyquake/plugins/user_management/config.json | 10 +- .../src/dashboard/dashboard.jsx | 3 +- .../src/dashboard/userMgmt.scss | 71 +--- .../src/userProfile/userProfile.jsx | 402 ++++++++++++++++++ .../src/userProfile/userProfileActions.js | 25 ++ .../src/userProfile/userProfileSource.js | 151 +++++++ .../src/userProfile/userProfileStore.js | 216 ++++++++++ 15 files changed, 927 insertions(+), 77 deletions(-) create mode 100644 skyquake/plugins/user_management/src/userProfile/userProfile.jsx create mode 100644 skyquake/plugins/user_management/src/userProfile/userProfileActions.js create mode 100644 skyquake/plugins/user_management/src/userProfile/userProfileSource.js create mode 100644 skyquake/plugins/user_management/src/userProfile/userProfileStore.js diff --git a/skyquake/framework/widgets/form_controls/formControls.scss b/skyquake/framework/widgets/form_controls/formControls.scss index 54d69a8bf..afa8508d7 100644 --- a/skyquake/framework/widgets/form_controls/formControls.scss +++ b/skyquake/framework/widgets/form_controls/formControls.scss @@ -95,3 +95,60 @@ } } +.FormSection { + &-title { + color: #000; + background: lightgray; + padding: 0.5rem; + border-top: 1px solid #f1f1f1; + border-bottom: 1px solid #f1f1f1; + } + &-body { + padding: 0.5rem 0.75rem; + } + label { + -ms-flex: 1 0; + flex: 1 0; + } + /* label {*/ + /* display: -ms-flexbox;*/ + /* display: flex;*/ + /* -ms-flex-direction: column;*/ + /* flex-direction: column;*/ + /* width: 100%;*/ + /* margin: 0.5rem 0;*/ + /* -ms-flex-align: start;*/ + /* align-items: flex-start;*/ + /* -ms-flex-pack: start;*/ + /* justify-content: flex-start;*/ + /* }*/ + select { + font-size: 1rem; + min-width: 75%; + height: 35px; + } +} + + + + +.InputCollection { + display:-ms-flexbox; + display:flex; + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + -ms-flex-align: center; + align-items: center; + button { + padding: 0.25rem; + height: 1.5rem; + font-size: 0.75rem; + } + select { + min-width: 100%; + } + margin-bottom:0.5rem; + &-wrapper { + + } +} diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx index 893a4c135..cd489ac3e 100644 --- a/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx +++ b/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx @@ -7,7 +7,7 @@ export default function(Component) { this.actions = context.flux.actions.global; } render(props) { - return + return } } SkyquakeComponent.contextTypes = { diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx index 9d601831a..6347c8f6b 100644 --- a/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx +++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx @@ -40,13 +40,21 @@ export default class skyquakeContainer extends React.Component { this.state.eventCenterIsOpen = false; this.state.currentPlugin = SkyquakeContainerStore.currentPlugin; } - + getChildContext() { + return { + userProfile: this.state.user + }; + } + getUserProfile() { + return this.state.user; + } componentWillMount() { let self = this; Utils.bootstrapApplication().then(function() { SkyquakeContainerStore.listen(self.listener); SkyquakeContainerStore.getNav(); + SkyquakeContainerStore.getUserProfile(); SkyquakeContainerStore.getEventStreams(); }); @@ -133,6 +141,9 @@ export default class skyquakeContainer extends React.Component { return html; } } +skyquakeContainer.childContextTypes = { + userProfile: React.PropTypes.object +}; skyquakeContainer.contextTypes = { router: React.PropTypes.object }; diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js b/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js index 1403d0a2f..cb731c73c 100644 --- a/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js +++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js @@ -179,7 +179,10 @@ class SkyquakeContainerStore { self.closeSocket(); }); if (!_.isEqual(data.project, self.projects)) { + let user = self.user; + user.projects = data.project; self.setState({ + user: user, projects: data.project }); } @@ -209,7 +212,7 @@ class SkyquakeContainerStore { } else { if(!data) data = {}; - state.notificationMessage = data.msg || 'Something very bad happened wrong'; + state.notificationMessage = data.msg || 'Something wrong occurred. Check the network tab and console logs for more information.'; if(data.type) { state.notificationType = data.type; } diff --git a/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx index a61ee541b..51a21a021 100644 --- a/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx +++ b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx @@ -63,21 +63,28 @@ class SelectProject extends React.Component { render() { let props = this.props; let currentValue = JSON.stringify(props.currentProject); - let projects = this.props.projects.map((p,i) => { + let projects = this.props.projects && this.props.projects.map((p,i) => { return { label: p.name, value: p.name } }); + let hasProjects = (this.props.projects && (this.props.projects.length > 0)) return (
- Project: - + { + hasProjects ? 'Project:' : 'No Projects Assigned' + } + { + hasProjects ? + + : null + }
) } @@ -96,12 +103,16 @@ class UserNav extends React.Component { } render() { let props = this.props; + let userProfileLink = ''; + this.props.nav['user_management'] && this.props.nav['user_management'].routes.map((r) => { + if(r.unique) { + userProfileLink = returnLinkItem(r, props.currentUser) + } + }) return (

- - {props.currentUser} - + {userProfileLink}

    @@ -205,12 +216,12 @@ export function buildNavListItem (k, link, index) { * @param {object} link Routing information from nav object. * @return {object} component returns a react component that links to a new route. */ -export function returnLinkItem(link) { +export function returnLinkItem(link, label) { let ref; let route = link.route; if(link.isExternal) { ref = ( - {link.label} + {label || link.label} ) } else { if(link.path && link.path.replace(' ', '') != '') { @@ -225,8 +236,8 @@ export function returnLinkItem(link) { } } ref = ( - - {link.label} + + {label || link.label} ) } @@ -255,7 +266,8 @@ export function buildNav(nav, currentPlugin, props) { projects={props.projects} currentProject={props.currentProject} /> + currentUser={props.currentUser} + nav={nav} />
) for (let k in nav) { diff --git a/skyquake/framework/widgets/skyquake_nav/skyquakeNav.scss b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.scss index f15af7b65..5269be9e0 100644 --- a/skyquake/framework/widgets/skyquake_nav/skyquakeNav.scss +++ b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.scss @@ -77,6 +77,7 @@ padding:0.5rem 1rem; text-decoration:none; text-transform:uppercase; + text-align:center; color:white; } &:before { @@ -93,6 +94,7 @@ align-items:center; padding-left: 1rem; text-transform:uppercase; + text-align: center; border-left:1px solid white; .projectSelect { padding: 0 0.5rem; diff --git a/skyquake/plugins/project_management/src/dashboard/projectMgmt.scss b/skyquake/plugins/project_management/src/dashboard/projectMgmt.scss index 0d76a291e..82b62a86d 100644 --- a/skyquake/plugins/project_management/src/dashboard/projectMgmt.scss +++ b/skyquake/plugins/project_management/src/dashboard/projectMgmt.scss @@ -152,7 +152,7 @@ .userTable { .FormSection-body { max-width: 652px; - overflow-x: scroll; + overflow-x: auto; } } } diff --git a/skyquake/plugins/project_management/src/dashboard/projectMgmtStore.js b/skyquake/plugins/project_management/src/dashboard/projectMgmtStore.js index 17a5a5960..64761dceb 100644 --- a/skyquake/plugins/project_management/src/dashboard/projectMgmtStore.js +++ b/skyquake/plugins/project_management/src/dashboard/projectMgmtStore.js @@ -16,7 +16,6 @@ export default class ProjectManagementStore { this.selectedUser = null; this.selectedRole = null; this.roles = ['rw-project:project-admin', 'rw-project:project-oper', 'rw-project:project-create' - // ,'some_other_role', 'yet_another_role', 'operator_role', 'some_other_role', 'yet_another_role' ]; this.users = []; this.activeIndex = null; diff --git a/skyquake/plugins/user_management/config.json b/skyquake/plugins/user_management/config.json index 0e66f6f2b..0fe9e9663 100644 --- a/skyquake/plugins/user_management/config.json +++ b/skyquake/plugins/user_management/config.json @@ -15,5 +15,13 @@ "route": "platform", "component": "./platformRoleManagement/platformRoleManagement.jsx", "type": "internal" - }] + }, + { + "label": "User Profile", + "route": "user-profile", + "component": "./userProfile/userProfile.jsx", + "type": "internal", + "unique" : true + } + ] } diff --git a/skyquake/plugins/user_management/src/dashboard/dashboard.jsx b/skyquake/plugins/user_management/src/dashboard/dashboard.jsx index f946cbb22..28471b782 100644 --- a/skyquake/plugins/user_management/src/dashboard/dashboard.jsx +++ b/skyquake/plugins/user_management/src/dashboard/dashboard.jsx @@ -301,7 +301,8 @@ class UserManagementDashboard extends React.Component { } // onClick={this.Store.update.bind(null, Account)} UserManagementDashboard.contextTypes = { - router: React.PropTypes.object + router: React.PropTypes.object, + userProfile: React.PropTypes.object }; UserManagementDashboard.defaultProps = { diff --git a/skyquake/plugins/user_management/src/dashboard/userMgmt.scss b/skyquake/plugins/user_management/src/dashboard/userMgmt.scss index d00be889d..79511b0b7 100644 --- a/skyquake/plugins/user_management/src/dashboard/userMgmt.scss +++ b/skyquake/plugins/user_management/src/dashboard/userMgmt.scss @@ -90,65 +90,28 @@ padding: 0.5rem 0; border-top: #d3d3d3 1px solid; } -} - - - -.FormSection { - &-title { - color: #000; - background: lightgray; - padding: 0.5rem; - border-top: 1px solid #f1f1f1; - border-bottom: 1px solid #f1f1f1; - } - &-body { - padding: 0.5rem 0.75rem; - } - label { - -ms-flex: 1 0; - flex: 1 0; - } - /* label {*/ - /* display: -ms-flexbox;*/ - /* display: flex;*/ - /* -ms-flex-direction: column;*/ - /* flex-direction: column;*/ - /* width: 100%;*/ - /* margin: 0.5rem 0;*/ - /* -ms-flex-align: start;*/ - /* align-items: flex-start;*/ - /* -ms-flex-pack: start;*/ - /* justify-content: flex-start;*/ - /* }*/ - select { - font-size: 1rem; - min-width: 75%; - height: 35px; + table { + font-size: 0.8rem; + thead { + border-bottom:1px solid #d3d3d3; + td{ + font-weight:bold; + } + } + td{ + padding:0.25rem 0.5rem; + vertical-align: middle; + .checkbox { + -ms-flex-pack:center; + justify-content:center; + } + } } } -.InputCollection { - display:-ms-flexbox; - display:flex; - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - -ms-flex-align: center; - align-items: center; - button { - padding: 0.25rem; - height: 1.5rem; - font-size: 0.75rem; - } - select { - min-width: 100%; - } - margin-bottom:0.5rem; - &-wrapper { - } -} + .tableRow { display:-ms-flexbox; display:flex; diff --git a/skyquake/plugins/user_management/src/userProfile/userProfile.jsx b/skyquake/plugins/user_management/src/userProfile/userProfile.jsx new file mode 100644 index 000000000..67d55159e --- /dev/null +++ b/skyquake/plugins/user_management/src/userProfile/userProfile.jsx @@ -0,0 +1,402 @@ +/* + * STANDARD_RIFT_IO_COPYRIGHT + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import AppHeader from 'widgets/header/header.jsx'; +import UserProfileStore from './userProfileStore.js'; +import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx'; +import 'style/layout.scss'; +import '../dashboard/userMgmt.scss'; +import {Panel, PanelWrapper} from 'widgets/panel/panel'; + + +import TextInput from 'widgets/form_controls/textInput.jsx'; +import Input from 'widgets/form_controls/input.jsx'; +import Button, {ButtonGroup} from 'widgets/button/sq-button.jsx'; +import SelectOption from 'widgets/form_controls/selectOption.jsx'; +import 'widgets/form_controls/formControls.scss'; +import imgAdd from '../../node_modules/open-iconic/svg/plus.svg' +import imgRemove from '../../node_modules/open-iconic/svg/trash.svg'; + +class UserProfileDashboard extends React.Component { + constructor(props) { + super(props); + this.Store = this.props.flux.stores.hasOwnProperty('UserProfileStore') ? this.props.flux.stores.UserProfileStore : this.props.flux.createStore(UserProfileStore); + this.state = this.Store.getState(); + this.actions = this.state.actions; + + } + componentDidUpdate() { + let self = this; + ReactDOM.findDOMNode(this.UserList).addEventListener('transitionend', this.onTransitionEnd, false); + setTimeout(function() { + let element = self[`user-ref-${self.state.activeIndex}`] + element && !isElementInView(element) && element.scrollIntoView({block: 'end', behavior: 'smooth'}); + }) + } + componentWillMount() { + this.Store.listen(this.updateState); + this.Store.getUsers(); + } + componentWillUnmount() { + this.Store.unlisten(this.updateState); + } + updateState = (state) => { + this.setState(state); + } + updateInput = (key, e) => { + let property = key; + this.actions.handleUpdateInput({ + [property]:e.target.value + }) + } + disabledChange = (e) => { + this.actions.handleDisabledChange(e.target.checked); + } + platformChange = (platformRole, e) => { + this.actions.handlePlatformRoleUpdate(platformRole, e.currentTarget.checked); + } + addProjectRole = (e) => { + this.actions.handleAddProjectItem(); + } + removeProjectRole = (i, e) => { + this.actions.handleRemoveProjectItem(i); + } + updateProjectRole = (i, e) => { + this.actions.handleUpdateProjectRole(i, e) + } + addUser = () => { + this.actions.handleAddUser(); + } + viewUser = (un, index) => { + this.actions.viewUser(un, index); + } + editUser = () => { + this.actions.editUser(false); + } + cancelEditUser = () => { + this.actions.editUser(true) + } + osePanel = () => { + this.actions.handleCloseUserPanel(); + } + // updateUser = (e) => { + // e.preventDefault(); + // e.stopPropagation(); + + // this.Store.updateUser(); + // } + deleteUser = (e) => { + e.preventDefault(); + e.stopPropagation(); + this.Store.deleteUser({ + 'user-name': this.state['user-name'], + 'user-domain': this.state['user-domain'] + }); + } + createUser = (e) => { + e.preventDefault(); + e.stopPropagation(); + if(this.state['new-password'] != this.state['confirm-password']) { + this.props.actions.showNotification('Passwords do not match') + } else { + this.Store.createUser({ + 'user-name': this.state['user-name'], + 'user-domain': this.state['user-domain'], + 'password': this.state['new-password'] + // 'confirm-password': this.state['confirm-password'] + }); + } + } + updateUser = (e) => { + let self = this; + e.preventDefault(); + e.stopPropagation(); + let validatedPasswords = validatePasswordFields(this.state); + if(validatedPasswords) { + this.Store.updateUser(_.merge({ + 'user-name': this.context.userProfile.userId, + 'user-domain': this.state['user-domain'], + 'password': this.state['new-password'] + })); + } + function validatePasswordFields(state) { + let oldOne = state['old-password']; + let newOne = state['new-password']; + let confirmOne = state['confirm-password']; + if(true) { + if(oldOne == newOne) { + self.props.actions.showNotification('Your new password must not match your old one'); + return false; + } + if(newOne != confirmOne) { + self.props.actions.showNotification('Passwords do not match'); + return false; + } + return { + // 'old-password': oldOne, + 'new-password': newOne, + 'confirm-password': confirmOne + } + } else { + return {}; + } + } + } + evaluateSubmit = (e) => { + if (e.keyCode == 13) { + if (this.props.isEdit) { + this.updateUser(e); + } else { + this.createUser(e); + } + e.preventDefault(); + e.stopPropagation(); + } + } + onTransitionEnd = (e) => { + this.actions.handleHideColumns(e); + console.log('transition end') + } + disableChange = (e) => { + let value = e.target.value; + value = value.toUpperCase(); + if (value=="TRUE") { + value = true; + } else { + value = false; + } + console.log(value) + } + render() { + + let self = this; + let html; + let props = this.props; + let state = this.state; + let passwordSectionHTML = null; + let formButtonsHTML = ( + +