diff options
Diffstat (limited to 'web/react/components')
-rw-r--r-- | web/react/components/rename_channel_modal.jsx | 17 | ||||
-rw-r--r-- | web/react/components/rename_team_modal.jsx | 95 | ||||
-rw-r--r-- | web/react/components/sidebar_header.jsx | 3 | ||||
-rw-r--r-- | web/react/components/team_feature_tab.jsx | 65 | ||||
-rw-r--r-- | web/react/components/team_general_tab.jsx | 180 | ||||
-rw-r--r-- | web/react/components/team_settings.jsx | 10 | ||||
-rw-r--r-- | web/react/components/team_settings_modal.jsx | 9 | ||||
-rw-r--r-- | web/react/components/team_signup_send_invites_page.jsx | 17 | ||||
-rw-r--r-- | web/react/components/user_settings_appearance.jsx | 3 | ||||
-rw-r--r-- | web/react/components/user_settings_general.jsx | 24 | ||||
-rw-r--r-- | web/react/components/user_settings_notifications.jsx | 3 | ||||
-rw-r--r-- | web/react/components/user_settings_security.jsx | 3 |
12 files changed, 288 insertions, 141 deletions
diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx index 93cb6ef21..2fe6dd96b 100644 --- a/web/react/components/rename_channel_modal.jsx +++ b/web/react/components/rename_channel_modal.jsx @@ -125,8 +125,8 @@ module.exports = React.createClass({ </button> <h4 className="modal-title">Rename Channel</h4> </div> - <div className="modal-body"> - <form role="form"> + <form role="form"> + <div className="modal-body"> <div className={ this.state.display_name_error ? "form-group has-error" : "form-group" }> <label className='control-label'>Display Name</label> <input onKeyUp={this.displayNameKeyUp} onChange={this.onDisplayNameChange} type="text" ref="display_name" className="form-control" placeholder="Enter display name" value={this.state.display_name} maxLength="64" /> @@ -138,16 +138,15 @@ module.exports = React.createClass({ { name_error } </div> { server_error } - </form> - </div> - <div className="modal-footer"> - <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button> - <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Save</button> - </div> + </div> + <div className="modal-footer"> + <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button> + <button onClick={this.handleSubmit} type="submit" className="btn btn-primary">Save</button> + </div> + </form> </div> </div> </div> ); } }); - diff --git a/web/react/components/rename_team_modal.jsx b/web/react/components/rename_team_modal.jsx deleted file mode 100644 index bebdd6662..000000000 --- a/web/react/components/rename_team_modal.jsx +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var Client = require('../utils/client.jsx'); -var utils = require('../utils/utils.jsx'); - -module.exports = React.createClass({ - handleSubmit: function(e) { - e.preventDefault(); - - var state = { server_error: "" }; - var valid = true; - - var name = this.state.name.trim(); - if (!name) { - state.name_error = "This field is required"; - valid = false; - } else { - state.name_error = ""; - } - - this.setState(state); - - if (!valid) - return; - - if (this.props.teamDisplayName === name) - return; - - var data = {}; - data["new_name"] = name; - - Client.updateTeamDisplayName(data, - function(data) { - $('#rename_team_link').modal('hide'); - window.location.reload(); - }.bind(this), - function(err) { - state.server_error = err.message; - this.setState(state); - }.bind(this) - ); - }, - onNameChange: function() { - this.setState({ name: this.refs.name.getDOMNode().value }) - }, - handleClose: function() { - this.setState({ name: this.props.teamDisplayName, name_error: "", server_error: ""}); - }, - componentDidMount: function() { - $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', this.handleClose); - }, - componentWillUnmount: function() { - $(this.refs.modal.getDOMNode()).off('hidden.bs.modal', this.handleClose); - }, - getInitialState: function() { - return { name: this.props.teamDisplayName }; - }, - render: function() { - - var name_error = this.state.name_error ? <label className='control-label'>{ this.state.name_error }</label> : null; - var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null; - - return ( - <div className="modal fade" ref="modal" id="rename_team_link" tabIndex="-1" role="dialog" aria-hidden="true"> - <div className="modal-dialog"> - <div className="modal-content"> - <div className="modal-header"> - <button type="button" className="close" data-dismiss="modal"> - <span aria-hidden="true">×</span> - <span className="sr-only">Close</span> - </button> - <h4 className="modal-title">{"Rename " + utils.toTitleCase(strings.Team)}</h4> - </div> - <div className="modal-body"> - <form role="form" onSubmit={this.handleSubmit}> - <div className={ this.state.name_error ? "form-group has-error" : "form-group" }> - <label className='control-label'>Name</label> - <input onChange={this.onNameChange} type="text" ref="name" className="form-control" placeholder={"Enter "+strings.Team+" name"} value={this.state.name} maxLength="64" /> - { name_error } - </div> - { server_error } - </form> - </div> - <div className="modal-footer"> - <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button> - <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Save</button> - </div> - </div> - </div> - </div> - ); - } -}); - diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx index 761c06e74..d5d16816f 100644 --- a/web/react/components/sidebar_header.jsx +++ b/web/react/components/sidebar_header.jsx @@ -51,7 +51,6 @@ var NavbarDropdown = React.createClass({ var teamLink = ''; var inviteLink = ''; var manageLink = ''; - var renameLink = ''; var currentUser = UserStore.getCurrentUser(); var isAdmin = false; var teamSettings = null; @@ -72,7 +71,6 @@ var NavbarDropdown = React.createClass({ if (isAdmin) { manageLink = (<li> <a href='#' data-toggle='modal' data-target='#team_members'>Manage Team</a> </li>); - renameLink = (<li> <a href='#' data-toggle='modal' data-target='#rename_team_link'>Rename</a> </li>); teamSettings = (<li> <a href='#' data-toggle='modal' data-target='#team_settings'>Team Settings</a> </li>); } @@ -101,7 +99,6 @@ var NavbarDropdown = React.createClass({ {inviteLink} {teamLink} {manageLink} - {renameLink} <li><a href='#' onClick={this.handleLogoutClick}>Logout</a></li> {teams} <li className='divider'></li> diff --git a/web/react/components/team_feature_tab.jsx b/web/react/components/team_feature_tab.jsx index 4f28d84c6..4f8f0b2cf 100644 --- a/web/react/components/team_feature_tab.jsx +++ b/web/react/components/team_feature_tab.jsx @@ -16,7 +16,7 @@ module.exports = React.createClass({ }, submitValetFeature: function() { var data = {}; - data.allowValet = this.state.allowValet; + data.allow_valet = this.state.allowValet; client.updateValetFeature(data, function() { @@ -38,7 +38,7 @@ module.exports = React.createClass({ var team = newProps.team; var allowValet = 'false'; - if (team && team.allowValet) { + if (team && team.allow_valet) { allowValet = 'true'; } @@ -48,17 +48,18 @@ module.exports = React.createClass({ var team = this.props.team; var allowValet = 'false'; - if (team && team.allowValet) { + if (team && team.allow_valet) { allowValet = 'true'; } return {allowValet: allowValet}; }, - onUpdateSection: function() { + onUpdateSection: function(e) { + e.preventDefault(); if (this.props.activeSection === 'valet') { - self.props.updateSection('valet'); + this.props.updateSection(''); } else { - self.props.updateSection(''); + this.props.updateSection('valet'); } }, render: function() { @@ -75,31 +76,43 @@ module.exports = React.createClass({ var self = this; if (this.props.activeSection === 'valet') { - var valetActive = ['', '']; + var valetActive = [false, false]; if (this.state.allowValet === 'false') { - valetActive[1] = 'active'; + valetActive[1] = true; } else { - valetActive[0] = 'active'; + valetActive[0] = true; } - var inputs = []; - - function valetActivate() { - self.handleValetRadio('true'); - } - - function valetDeactivate() { - self.handleValetRadio('false'); - } + let inputs = []; inputs.push( - <div> - <div className='btn-group' data-toggle='buttons-radio'> - <button className={'btn btn-default ' + valetActive[0]} onClick={valetActivate}>On</button> - <button className={'btn btn-default ' + valetActive[1]} onClick={valetDeactivate}>Off</button> + <div key='teamValetSetting'> + <div className='radio'> + <label> + <input + type='radio' + checked={valetActive[0]} + onChange={self.handleValetRadio.bind(this, 'true')} + > + On + </input> + </label> + <br/> </div> - <div><br/>Valet is a preview feature for enabling a non-user account limited to basic member permissions that can be manipulated by 3rd parties.<br/><br/>IMPORTANT: The preview version of Valet should not be used without a secure connection and a trusted 3rd party, since user credentials are used to connect. OAuth2 will be used in the final release.</div> - </div> + <div className='radio'> + <label> + <input + type='radio' + checked={valetActive[1]} + onChange={self.handleValetRadio.bind(this, 'false')} + > + Off + </input> + </label> + <br/> + </div> + <div><br/>Valet is a preview feature for enabling a non-user account limited to basic member permissions that can be manipulated by 3rd parties.<br/><br/>IMPORTANT: The preview version of Valet should not be used without a secure connection and a trusted 3rd party, since user credentials are used to connect. OAuth2 will be used in the final release.</div> + </div> ); valetSection = ( @@ -107,8 +120,8 @@ module.exports = React.createClass({ title='Valet (Preview - EXPERTS ONLY)' inputs={inputs} submit={this.submitValetFeature} - serverError={serverError} - clientError={clientError} + server_error={serverError} + client_error={clientError} updateSection={this.onUpdateSection} /> ); diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx new file mode 100644 index 000000000..fd2a22731 --- /dev/null +++ b/web/react/components/team_general_tab.jsx @@ -0,0 +1,180 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var SettingItemMin = require('./setting_item_min.jsx'); +var SettingItemMax = require('./setting_item_max.jsx'); + +var client = require('../utils/client.jsx'); +var utils = require('../utils/utils.jsx'); + +export default class GeneralTab extends React.Component { + constructor(props) { + super(props); + + this.handleNameSubmit = this.handleNameSubmit.bind(this); + this.handleClose = this.handleClose.bind(this); + this.onUpdateSection = this.onUpdateSection.bind(this); + this.updateName = this.updateName.bind(this); + + this.state = {name: this.props.teamDisplayName, serverError: '', clientError: ''}; + } + handleNameSubmit(e) { + e.preventDefault(); + + var state = {serverError: '', clientError: ''}; + var valid = true; + + var name = this.state.name.trim(); + if (!name) { + state.clientError = 'This field is required'; + valid = false; + } else if (name === this.props.teamDisplayName) { + state.clientError = 'Please choose a new name for your ' + strings.Team; + valid = false; + } else { + state.clientError = ''; + } + + this.setState(state); + + if (!valid) { + return; + } + + var data = {}; + data.new_name = name; + + client.updateTeamDisplayName(data, + function nameChangeSuccess() { + this.props.updateSection(''); + $('#team_settings').modal('hide'); + window.location.reload(); + }.bind(this), + function nameChangeFail(err) { + state.serverError = err.message; + this.setState(state); + }.bind(this) + ); + } + componentWillReceiveProps(newProps) { + if (newProps.team && newProps.teamDisplayName) { + this.setState({name: newProps.teamDisplayName}); + } + } + handleClose() { + this.setState({clientError: '', serverError: ''}); + this.props.updateSection(''); + } + componentDidMount() { + $('#team_settings').on('hidden.bs.modal', this.handleClose); + } + componentWillUnmount() { + $('#team_settings').off('hidden.bs.modal', this.handleClose); + } + onUpdateSection(e) { + e.preventDefault(); + if (this.props.activeSection === 'name') { + this.props.updateSection(''); + } else { + this.props.updateSection('name'); + } + } + updateName(e) { + e.preventDefault(); + this.setState({name: e.target.value}); + } + render() { + var clientError = null; + var serverError = null; + if (this.state.clientError) { + clientError = this.state.clientError; + } + if (this.state.serverError) { + serverError = this.state.serverError; + } + + var nameSection; + + if (this.props.activeSection === 'name') { + let inputs = []; + + let teamNameLabel = utils.toTitleCase(strings.Team) + ' Name'; + if (utils.isMobile()) { + teamNameLabel = ''; + } + + inputs.push( + <div key='teamNameSetting' className='form-group'> + <label className='col-sm-5 control-label'>{teamNameLabel}</label> + <div className='col-sm-7'> + <input + className='form-control' + type='text' + onChange={this.updateName} + value={this.state.name} + /> + </div> + </div> + ); + + nameSection = ( + <SettingItemMax + title={utils.toTitleCase(strings.Team) + ' Name'} + inputs={inputs} + submit={this.handleNameSubmit} + server_error={serverError} + client_error={clientError} + updateSection={this.onUpdateSection} + /> + ); + } else { + let describe = this.state.name; + + nameSection = ( + <SettingItemMin + title={utils.toTitleCase(strings.Team) + ' Name'} + describe={describe} + updateSection={this.onUpdateSection} + /> + ); + } + + return ( + <div> + <div className='modal-header'> + <button + type='button' + className='close' + data-dismiss='modal' + aria-label='Close' + > + <span aria-hidden='true'>×</span> + </button> + <h4 + className='modal-title' + ref='title' + > + <i className='modal-back'></i> + General Settings + </h4> + </div> + <div + ref='wrapper' + className='user-settings' + > + <h3 className='tab-header'>General Settings</h3> + <div className='divider-dark first'/> + {nameSection} + <div className='divider-dark'/> + </div> + </div> + ); + } +} + +GeneralTab.propTypes = { + updateSection: React.PropTypes.func.isRequired, + team: React.PropTypes.object.isRequired, + activeSection: React.PropTypes.string.isRequired, + teamDisplayName: React.PropTypes.string.isRequired +}; diff --git a/web/react/components/team_settings.jsx b/web/react/components/team_settings.jsx index 94d536651..1a79eef1d 100644 --- a/web/react/components/team_settings.jsx +++ b/web/react/components/team_settings.jsx @@ -4,6 +4,7 @@ var TeamStore = require('../stores/team_store.jsx'); var ImportTab = require('./team_import_tab.jsx'); var FeatureTab = require('./team_feature_tab.jsx'); +var GeneralTab = require('./team_general_tab.jsx'); var utils = require('../utils/utils.jsx'); module.exports = React.createClass({ @@ -11,7 +12,8 @@ module.exports = React.createClass({ propTypes: { activeTab: React.PropTypes.string.isRequired, activeSection: React.PropTypes.string.isRequired, - updateSection: React.PropTypes.func.isRequired + updateSection: React.PropTypes.func.isRequired, + teamDisplayName: React.PropTypes.string.isRequired }, componentDidMount: function() { TeamStore.addChangeListener(this.onChange); @@ -34,6 +36,12 @@ module.exports = React.createClass({ case 'general': result = ( <div> + <GeneralTab + team={this.state.team} + activeSection={this.props.activeSection} + updateSection={this.props.updateSection} + teamDisplayName={this.props.teamDisplayName} + /> </div> ); break; diff --git a/web/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx index ef2564de0..7e65e8cab 100644 --- a/web/react/components/team_settings_modal.jsx +++ b/web/react/components/team_settings_modal.jsx @@ -6,6 +6,9 @@ var TeamSettings = require('./team_settings.jsx'); module.exports = React.createClass({ displayName: 'Team Settings Modal', + propTypes: { + teamDisplayName: React.PropTypes.string.isRequired + }, componentDidMount: function() { $('body').on('click', '.modal-back', function onClick() { $(this).closest('.modal-dialog').removeClass('display--content'); @@ -17,16 +20,17 @@ module.exports = React.createClass({ }); }, updateTab: function(tab) { - this.setState({activeTab: tab}); + this.setState({activeTab: tab, activeSection: ''}); }, updateSection: function(section) { this.setState({activeSection: section}); }, getInitialState: function() { - return {activeTab: 'feature', activeSection: ''}; + return {activeTab: 'general', activeSection: ''}; }, render: function() { var tabs = []; + tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'}); tabs.push({name: 'import', uiName: 'Import', icon: 'glyphicon glyphicon-upload'}); tabs.push({name: 'feature', uiName: 'Advanced', icon: 'glyphicon glyphicon-wrench'}); @@ -52,6 +56,7 @@ module.exports = React.createClass({ activeTab={this.state.activeTab} activeSection={this.state.activeSection} updateSection={this.updateSection} + teamDisplayName={this.props.teamDisplayName} /> </div> </div> diff --git a/web/react/components/team_signup_send_invites_page.jsx b/web/react/components/team_signup_send_invites_page.jsx index a1e12661e..646a742ba 100644 --- a/web/react/components/team_signup_send_invites_page.jsx +++ b/web/react/components/team_signup_send_invites_page.jsx @@ -13,6 +13,7 @@ export default class TeamSignupSendInvitesPage extends React.Component { this.submitNext = this.submitNext.bind(this); this.submitAddInvite = this.submitAddInvite.bind(this); this.submitSkip = this.submitSkip.bind(this); + this.keySubmit = this.keySubmit.bind(this); this.state = { emailEnabled: !ConfigStore.getSettingAsBoolean('ByPassEmail', false) }; @@ -68,12 +69,28 @@ export default class TeamSignupSendInvitesPage extends React.Component { this.props.state.wizard = 'username'; this.props.updateParent(this.props.state); } + keySubmit(e) { + if (e && e.keyCode === 13) { + this.submitNext(e) + } + } componentWillMount() { if (!this.state.emailEnabled) { this.props.state.wizard = 'username'; this.props.updateParent(this.props.state); } } + componentDidMount() { + if (!this.state.emailEnabled) { + // Must use keypress not keyup due to event chain of pressing enter + $('body').keypress(this.keySubmit); + } + } + componentWillUnmount() { + if (!this.state.emailEnabled) { + $('body').off('keypress', this.keySubmit); + } + } render() { client.track('signup', 'signup_team_05_send_invites'); diff --git a/web/react/components/user_settings_appearance.jsx b/web/react/components/user_settings_appearance.jsx index 0a17f1687..ba2d97ea8 100644 --- a/web/react/components/user_settings_appearance.jsx +++ b/web/react/components/user_settings_appearance.jsx @@ -1,3 +1,6 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + var UserStore = require('../stores/user_store.jsx'); var SettingItemMin = require('./setting_item_min.jsx'); var SettingItemMax = require('./setting_item_max.jsx'); diff --git a/web/react/components/user_settings_general.jsx b/web/react/components/user_settings_general.jsx index 5e7bbcb51..fed11fbe9 100644 --- a/web/react/components/user_settings_general.jsx +++ b/web/react/components/user_settings_general.jsx @@ -1,4 +1,8 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + var UserStore = require('../stores/user_store.jsx'); +var ConfigStore = require('../stores/config_store.jsx'); var SettingItemMin = require('./setting_item_min.jsx'); var SettingItemMax = require('./setting_item_max.jsx'); var SettingPicture = require('./setting_picture.jsx'); @@ -183,9 +187,10 @@ module.exports = React.createClass({ }, getInitialState: function() { var user = this.props.user; + var emailEnabled = !ConfigStore.getSettingAsBoolean('ByPassEmail', false); return {username: user.username, firstName: user.first_name, lastName: user.last_name, nickname: user.nickname, - email: user.email, picture: null, loadingPicture: false}; + email: user.email, picture: null, loadingPicture: false, emailEnabled: emailEnabled}; }, render: function() { var user = this.props.user; @@ -334,12 +339,21 @@ module.exports = React.createClass({ } var emailSection; if (this.props.activeSection === 'email') { + let helpText = <div>Email is used for notifications, and requires verification if changed.</div>; + + if (!this.state.emailEnabled) { + helpText = <div className='text-danger'><br />Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.</div>; + } + inputs.push( - <div className='form-group'> - <label className='col-sm-5 control-label'>Primary Email</label> - <div className='col-sm-7'> - <input className='form-control' type='text' onChange={this.updateEmail} value={this.state.email}/> + <div> + <div className='form-group'> + <label className='col-sm-5 control-label'>Primary Email</label> + <div className='col-sm-7'> + <input className='form-control' type='text' onChange={this.updateEmail} value={this.state.email}/> + </div> </div> + {helpText} </div> ); diff --git a/web/react/components/user_settings_notifications.jsx b/web/react/components/user_settings_notifications.jsx index 6637bbcb5..b89f72987 100644 --- a/web/react/components/user_settings_notifications.jsx +++ b/web/react/components/user_settings_notifications.jsx @@ -1,3 +1,6 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + var UserStore = require('../stores/user_store.jsx'); var SettingItemMin = require('./setting_item_min.jsx'); var SettingItemMax = require('./setting_item_max.jsx'); diff --git a/web/react/components/user_settings_security.jsx b/web/react/components/user_settings_security.jsx index 39d707d90..ae8a5f0bc 100644 --- a/web/react/components/user_settings_security.jsx +++ b/web/react/components/user_settings_security.jsx @@ -1,3 +1,6 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + var SettingItemMin = require('./setting_item_min.jsx'); var SettingItemMax = require('./setting_item_max.jsx'); var client = require('../utils/client.jsx'); |