From df5324509ba04b614e90e5b84d19c3b396f5094e Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Mon, 17 Aug 2015 09:25:51 -0700 Subject: Team URLs no longer accept a number as the first character; updated error message on URL compliance failure --- web/react/components/signup_team_complete.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'web/react/components') diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx index e27fcd19d..2859c0c02 100644 --- a/web/react/components/signup_team_complete.jsx +++ b/web/react/components/signup_team_complete.jsx @@ -222,9 +222,9 @@ TeamURLPage = React.createClass({ var cleanedName = utils.cleanUpUrlable(name); - var urlRegex = /^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g; + var urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g; if (cleanedName !== name || !urlRegex.test(name)) { - this.setState({nameError: 'Must be lowercase alphanumeric characters'}); + this.setState({nameError: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."}); return; } else if (cleanedName.length <= 3 || cleanedName.length > 15) { this.setState({nameError: 'Name must be 4 or more characters up to a maximum of 15'}); -- cgit v1.2.3-1-g7c22 From 0b4d97aedb26d223063d06765791ca8f1499a0ca Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Mon, 17 Aug 2015 10:32:41 -0700 Subject: Initial cosmetic reformatting of signup_team_complete.jsx file --- web/react/components/signup_team_complete.jsx | 124 +++++++++++++++++--------- 1 file changed, 83 insertions(+), 41 deletions(-) (limited to 'web/react/components') diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx index 2859c0c02..b3ea5a6d6 100644 --- a/web/react/components/signup_team_complete.jsx +++ b/web/react/components/signup_team_complete.jsx @@ -4,12 +4,16 @@ var utils = require('../utils/utils.jsx'); var ConfigStore = require('../stores/config_store.jsx'); var client = require('../utils/client.jsx'); -var UserStore = require('../stores/user_store.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); var constants = require('../utils/constants.jsx'); WelcomePage = React.createClass({ - submitNext: function (e) { + displayName: 'WelcomePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitNext: function(e) { if (!BrowserStore.isLocalStorageSupported()) { this.setState({storageError: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'}); return; @@ -18,11 +22,11 @@ WelcomePage = React.createClass({ this.props.state.wizard = 'team_display_name'; this.props.updateParent(this.props.state); }, - handleDiffEmail: function (e) { + handleDiffEmail: function(e) { e.preventDefault(); this.setState({useDiff: true}); }, - handleDiffSubmit: function (e) { + handleDiffSubmit: function(e) { e.preventDefault(); var state = {useDiff: true, serverError: ''}; @@ -36,21 +40,20 @@ WelcomePage = React.createClass({ state.emailError = 'This service requires local storage to be enabled. Please enable it or exit private browsing.'; this.setState(state); return; - } else { - state.emailError = ''; } + state.emailError = ''; client.signupTeam(email, - function(data) { - if (data['follow_link']) { - window.location.href = data['follow_link']; + function success(data) { + if (data.follow_link) { + window.location.href = data.follow_link; } else { this.props.state.wizard = 'finished'; this.props.updateParent(this.props.state); - window.location.href = '/signup_team_confirm/?email=' + encodeURIComponent(team.email); + window.location.href = '/signup_team_confirm/?email=' + encodeURIComponent(email); } }.bind(this), - function(err) { + function error(err) { this.state.serverError = err.message; this.setState(this.state); }.bind(this) @@ -134,7 +137,7 @@ WelcomePage = React.createClass({ {emailError} {serverError} - + Use a different email @@ -143,23 +146,28 @@ WelcomePage = React.createClass({ }); TeamDisplayNamePage = React.createClass({ - submitBack: function (e) { + displayName: 'TeamDisplayNamePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { e.preventDefault(); this.props.state.wizard = 'welcome'; this.props.updateParent(this.props.state); }, - submitNext: function (e) { + submitNext: function(e) { e.preventDefault(); - var display_name = this.refs.name.getDOMNode().value.trim(); - if (!display_name) { + var displayName = this.refs.name.getDOMNode().value.trim(); + if (!displayName) { this.setState({nameError: 'This field is required'}); return; } this.props.state.wizard = 'team_url'; - this.props.state.team.display_name = display_name; - this.props.state.team.name = utils.cleanUpUrlable(display_name); + this.props.state.team.display_name = displayName; + this.props.state.team.name = utils.cleanUpUrlable(displayName); this.props.updateParent(this.props.state); }, getInitialState: function() { @@ -167,7 +175,6 @@ TeamDisplayNamePage = React.createClass({ }, handleFocus: function(e) { e.preventDefault(); - e.currentTarget.select(); }, render: function() { @@ -206,12 +213,17 @@ TeamDisplayNamePage = React.createClass({ }); TeamURLPage = React.createClass({ - submitBack: function (e) { + displayName: 'TeamURLPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { e.preventDefault(); this.props.state.wizard = 'team_display_name'; this.props.updateParent(this.props.state); }, - submitNext: function (e) { + submitNext: function(e) { e.preventDefault(); var name = this.refs.name.getDOMNode().value.trim(); @@ -239,7 +251,7 @@ TeamURLPage = React.createClass({ } client.findTeamByName(name, - function(data) { + function success(data) { if (!data) { if (config.AllowSignupDomainsWizard) { this.props.state.wizard = 'allowed_domains'; @@ -255,7 +267,7 @@ TeamURLPage = React.createClass({ this.setState(this.state); } }.bind(this), - function(err) { + function error(err) { this.state.nameError = err.message; this.setState(this.state); }.bind(this) @@ -270,7 +282,7 @@ TeamURLPage = React.createClass({ e.currentTarget.select(); }, render: function() { - $('body').tooltip( {selector: '[data-toggle=tooltip]', trigger: 'hover click'} ); + $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'}); client.track('signup', 'signup_team_03_url'); @@ -314,12 +326,17 @@ TeamURLPage = React.createClass({ }); AllowedDomainsPage = React.createClass({ - submitBack: function (e) { + displayName: 'AllowedDomainsPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { e.preventDefault(); this.props.state.wizard = 'team_url'; this.props.updateParent(this.props.state); }, - submitNext: function (e) { + submitNext: function(e) { e.preventDefault(); if (this.refs.open_network.getDOMNode().checked) { @@ -403,6 +420,10 @@ AllowedDomainsPage = React.createClass({ }); EmailItem = React.createClass({ + displayName: 'EmailItem', + propTypes: { + focus: React.PropTypes.bool + }, getInitialState: function() { return {}; }, @@ -424,17 +445,16 @@ EmailItem = React.createClass({ this.state.emailError = 'Please use a different email than the one used at signup'; this.setState(this.state); return false; - } else { - this.state.emailError = ''; - this.setState(this.state); - return true; } + this.state.emailError = ''; + this.setState(this.state); + return true; }, render: function() { var emailError = null; var emailDivClass = 'form-group'; if (this.state.emailError) { - emailError = ; + emailError = ; emailDivClass += ' has-error'; } @@ -448,7 +468,12 @@ EmailItem = React.createClass({ }); SendInivtesPage = React.createClass({ - submitBack: function (e) { + displayName: 'SendInivtesPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { e.preventDefault(); if (config.AllowSignupDomainsWizard) { @@ -459,7 +484,7 @@ SendInivtesPage = React.createClass({ this.props.updateParent(this.props.state); }, - submitNext: function (e) { + submitNext: function(e) { e.preventDefault(); var valid = true; @@ -485,7 +510,7 @@ SendInivtesPage = React.createClass({ this.props.updateParent(this.props.state); } }, - submitAddInvite: function (e) { + submitAddInvite: function(e) { e.preventDefault(); this.props.state.wizard = 'send_invites'; if (!this.props.state.invites) { @@ -494,7 +519,7 @@ SendInivtesPage = React.createClass({ this.props.state.invites.push(''); this.props.updateParent(this.props.state); }, - submitSkip: function (e) { + submitSkip: function(e) { e.preventDefault(); this.props.state.wizard = 'username'; this.props.updateParent(this.props.state); @@ -533,7 +558,7 @@ SendInivtesPage = React.createClass({ ); } else { content = ( -
Email is currently disabled for your team, and emails cannot be sent. Contact your system administrator to enable email and email invitations.
+
{'Email is currently disabled for your ' + strings.Team + ', and emails cannot be sent. Contact your system administrator to enable email and email invitations.'}
); } @@ -557,6 +582,11 @@ SendInivtesPage = React.createClass({ }); UsernamePage = React.createClass({ + displayName: 'UsernamePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, submitBack: function(e) { e.preventDefault(); this.props.state.wizard = 'send_invites'; @@ -622,12 +652,17 @@ UsernamePage = React.createClass({ }); PasswordPage = React.createClass({ - submitBack: function (e) { + displayName: 'PasswordPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { e.preventDefault(); this.props.state.wizard = 'username'; this.props.updateParent(this.props.state); }, - submitNext: function (e) { + submitNext: function(e) { e.preventDefault(); var password = this.refs.password.getDOMNode().value.trim(); @@ -642,10 +677,11 @@ PasswordPage = React.createClass({ teamSignup.user.password = password; teamSignup.user.allow_marketing = true; delete teamSignup.wizard; - var ctl = this; + + // var ctl = this; client.createTeamFromSignup(teamSignup, - function(data) { + function success() { client.track('signup', 'signup_team_08_complete'); var props = this.props; @@ -668,7 +704,7 @@ PasswordPage = React.createClass({ // }.bind(ctl) // ); }.bind(this), - function(err) { + function error(err) { this.setState({serverError: err.message}); $('#sign-up-button').button('reset'); }.bind(this) @@ -727,6 +763,12 @@ PasswordPage = React.createClass({ }); module.exports = React.createClass({ + displayName: 'SignupTeamComplete', + propTypes: { + hash: React.PropTypes.string, + email: React.PropTypes.string, + data: React.PropTypes.string + }, updateParent: function(state, skipSet) { BrowserStore.setGlobalItem(this.props.hash, state); -- cgit v1.2.3-1-g7c22 From 5dbfa999494e7569e064cb1bc9f2dd5ab414eb9e Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Mon, 17 Aug 2015 10:58:42 -0700 Subject: Refactors the signup_team_complete.jsx file into multiple files for each component, rather than have all components put in one file --- web/react/components/allowed_domains_page.jsx | 98 +++ web/react/components/email_item.jsx | 53 ++ web/react/components/password_page.jsx | 116 ++++ web/react/components/send_invites_page.jsx | 121 ++++ web/react/components/signup_team_complete.jsx | 766 +----------------------- web/react/components/team_display_name_page.jsx | 72 +++ web/react/components/team_url_page.jsx | 119 ++++ web/react/components/username_page.jsx | 75 +++ web/react/components/welcome_page.jsx | 144 +++++ 9 files changed, 805 insertions(+), 759 deletions(-) create mode 100644 web/react/components/allowed_domains_page.jsx create mode 100644 web/react/components/email_item.jsx create mode 100644 web/react/components/password_page.jsx create mode 100644 web/react/components/send_invites_page.jsx create mode 100644 web/react/components/team_display_name_page.jsx create mode 100644 web/react/components/team_url_page.jsx create mode 100644 web/react/components/username_page.jsx create mode 100644 web/react/components/welcome_page.jsx (limited to 'web/react/components') diff --git a/web/react/components/allowed_domains_page.jsx b/web/react/components/allowed_domains_page.jsx new file mode 100644 index 000000000..56900e1df --- /dev/null +++ b/web/react/components/allowed_domains_page.jsx @@ -0,0 +1,98 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'AllowedDomainsPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'team_url'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + if (this.refs.open_network.getDOMNode().checked) { + this.props.state.wizard = 'send_invites'; + this.props.state.team.type = 'O'; + this.props.updateParent(this.props.state); + return; + } + + if (this.refs.allow.getDOMNode().checked) { + var name = this.refs.name.getDOMNode().value.trim(); + var domainRegex = /^\w+\.\w+$/; + if (!name) { + this.setState({nameError: 'This field is required'}); + return; + } + + if (!name.trim().match(domainRegex)) { + this.setState({nameError: 'The domain doesn\'t appear valid'}); + return; + } + + this.props.state.wizard = 'send_invites'; + this.props.state.team.allowed_domains = name; + this.props.state.team.type = 'I'; + this.props.updateParent(this.props.state); + } else { + this.props.state.wizard = 'send_invites'; + this.props.state.team.type = 'I'; + this.props.updateParent(this.props.state); + } + }, + getInitialState: function() { + return {}; + }, + render: function() { + client.track('signup', 'signup_team_04_allow_domains'); + + var nameError = null; + var nameDivClass = 'form-group'; + if (this.state.nameError) { + nameError = ; + nameDivClass += ' has-error'; + } + + return ( +
+
+ +

Email Domain

+

+

+ +
+

+

{'Check this box to allow your ' + strings.Team + ' members to sign up using their ' + strings.Company + ' email addresses if you share the same domain--otherwise, you need to invite everyone yourself.'}

+

{'Your ' + strings.Team + '\'s domain for emails'}

+
+
+
+
+ @ + +
+
+
+ {nameError} +
+

To allow signups from multiple domains, separate each with a comma.

+

+

+ +
+

+   + +
+
+ ); + } +}); diff --git a/web/react/components/email_item.jsx b/web/react/components/email_item.jsx new file mode 100644 index 000000000..c0ade40cc --- /dev/null +++ b/web/react/components/email_item.jsx @@ -0,0 +1,53 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); + +module.exports = React.createClass({ + displayName: 'EmailItem', + propTypes: { + focus: React.PropTypes.bool, + email: React.PropTypes.string + }, + getInitialState: function() { + return {}; + }, + getValue: function() { + return this.refs.email.getDOMNode().value.trim(); + }, + validate: function(teamEmail) { + var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); + + if (!email) { + return true; + } + + if (!utils.isEmail(email)) { + this.state.emailError = 'Please enter a valid email address'; + this.setState(this.state); + return false; + } else if (email === teamEmail) { + this.state.emailError = 'Please use a different email than the one used at signup'; + this.setState(this.state); + return false; + } + this.state.emailError = ''; + this.setState(this.state); + return true; + }, + render: function() { + var emailError = null; + var emailDivClass = 'form-group'; + if (this.state.emailError) { + emailError = ; + emailDivClass += ' has-error'; + } + + return ( +
+ + {emailError} +
+ ); + } +}); diff --git a/web/react/components/password_page.jsx b/web/react/components/password_page.jsx new file mode 100644 index 000000000..bb5eba682 --- /dev/null +++ b/web/react/components/password_page.jsx @@ -0,0 +1,116 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'PasswordPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'username'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var password = this.refs.password.getDOMNode().value.trim(); + if (!password || password.length < 5) { + this.setState({passwordError: 'Please enter at least 5 characters'}); + return; + } + + this.setState({passwordError: null, serverError: null}); + $('#finish-button').button('loading'); + var teamSignup = JSON.parse(JSON.stringify(this.props.state)); + teamSignup.user.password = password; + teamSignup.user.allow_marketing = true; + delete teamSignup.wizard; + + // var ctl = this; + + client.createTeamFromSignup(teamSignup, + function success() { + client.track('signup', 'signup_team_08_complete'); + + var props = this.props; + + $('#sign-up-button').button('reset'); + props.state.wizard = 'finished'; + props.updateParent(props.state, true); + + window.location.href = utils.getWindowLocationOrigin() + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email); + + // client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password, + // function(data) { + // TeamStore.setLastName(teamSignup.team.domain); + // UserStore.setLastEmail(teamSignup.team.email); + // UserStore.setCurrentUser(data); + // window.location.href = '/channels/town-square'; + // }.bind(ctl), + // function(err) { + // this.setState({nameError: err.message}); + // }.bind(ctl) + // ); + }.bind(this), + function error(err) { + this.setState({serverError: err.message}); + $('#sign-up-button').button('reset'); + }.bind(this) + ); + }, + getInitialState: function() { + return {}; + }, + render: function() { + client.track('signup', 'signup_team_07_password'); + + var passwordError = null; + var passwordDivStyle = 'form-group'; + if (this.state.passwordError) { + passwordError =
; + passwordDivStyle = ' has-error'; + } + + var serverError = null; + if (this.state.serverError) { + serverError =
; + } + + return ( +
+
+ +

Your password

+
Select a password that you'll use to login with your email address:
+
+
Email
+
{this.props.state.team.email}
+
+
+
+
Choose your password
+ +
Passwords must contain 5 to 50 characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.
+
+
+ {passwordError} + {serverError} +
+
+
+ +
+

By proceeding to create your account and use {config.SiteName}, you agree to our Terms of Service and Privacy Policy. If you do not agree, you cannot use {config.SiteName}.

+ +
+
+ ); + } +}); diff --git a/web/react/components/send_invites_page.jsx b/web/react/components/send_invites_page.jsx new file mode 100644 index 000000000..2aa44c970 --- /dev/null +++ b/web/react/components/send_invites_page.jsx @@ -0,0 +1,121 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var EmailItem = require('./email_item.jsx'); +var utils = require('../utils/utils.jsx'); +var ConfigStore = require('../stores/config_store.jsx'); +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'SendInivtesPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + + if (config.AllowSignupDomainsWizard) { + this.props.state.wizard = 'allowed_domains'; + } else { + this.props.state.wizard = 'team_url'; + } + + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var valid = true; + + if (this.state.emailEnabled) { + var emails = []; + + for (var i = 0; i < this.props.state.invites.length; i++) { + if (!this.refs['email_' + i].validate(this.props.state.team.email)) { + valid = false; + } else { + emails.push(this.refs['email_' + i].getValue()); + } + } + + if (valid) { + this.props.state.invites = emails; + } + } + + if (valid) { + this.props.state.wizard = 'username'; + this.props.updateParent(this.props.state); + } + }, + submitAddInvite: function(e) { + e.preventDefault(); + this.props.state.wizard = 'send_invites'; + if (!this.props.state.invites) { + this.props.state.invites = []; + } + this.props.state.invites.push(''); + this.props.updateParent(this.props.state); + }, + submitSkip: function(e) { + e.preventDefault(); + this.props.state.wizard = 'username'; + this.props.updateParent(this.props.state); + }, + getInitialState: function() { + return { + emailEnabled: !ConfigStore.getSettingAsBoolean('ByPassEmail', false) + }; + }, + render: function() { + client.track('signup', 'signup_team_05_send_invites'); + + var content = null; + var bottomContent = null; + + if (this.state.emailEnabled) { + var emails = []; + + for (var i = 0; i < this.props.state.invites.length; i++) { + if (i === 0) { + emails.push(); + } else { + emails.push(); + } + } + + content = ( +
+ {emails} + +
+ ); + + bottomContent = ( +

{'if you prefer, you can invite ' + strings.Team + ' members later'}
and skip this step for now.

+ ); + } else { + content = ( +
{'Email is currently disabled for your ' + strings.Team + ', and emails cannot be sent. Contact your system administrator to enable email and email invitations.'}
+ ); + } + + return ( +
+
+ +

{'Invite ' + utils.toTitleCase(strings.Team) + ' Members'}

+ {content} +
+ +
+
+ {bottomContent} + +
+ ); + } +}); diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx index b3ea5a6d6..0cfd8b4e0 100644 --- a/web/react/components/signup_team_complete.jsx +++ b/web/react/components/signup_team_complete.jsx @@ -1,766 +1,14 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. -var utils = require('../utils/utils.jsx'); -var ConfigStore = require('../stores/config_store.jsx'); -var client = require('../utils/client.jsx'); +var WelcomePage = require('./welcome_page.jsx'); +var TeamDisplayNamePage = require('./team_display_name_page.jsx'); +var TeamURLPage = require('./team_url_page.jsx'); +var AllowedDomainsPage = require('./allowed_domains_page.jsx'); +var SendInivtesPage = require('./send_invites_page.jsx'); +var UsernamePage = require('./username_page.jsx'); +var PasswordPage = require('./password_page.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); -var constants = require('../utils/constants.jsx'); - -WelcomePage = React.createClass({ - displayName: 'WelcomePage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitNext: function(e) { - if (!BrowserStore.isLocalStorageSupported()) { - this.setState({storageError: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'}); - return; - } - e.preventDefault(); - this.props.state.wizard = 'team_display_name'; - this.props.updateParent(this.props.state); - }, - handleDiffEmail: function(e) { - e.preventDefault(); - this.setState({useDiff: true}); - }, - handleDiffSubmit: function(e) { - e.preventDefault(); - - var state = {useDiff: true, serverError: ''}; - - var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); - if (!email || !utils.isEmail(email)) { - state.emailError = 'Please enter a valid email address'; - this.setState(state); - return; - } else if (!BrowserStore.isLocalStorageSupported()) { - state.emailError = 'This service requires local storage to be enabled. Please enable it or exit private browsing.'; - this.setState(state); - return; - } - state.emailError = ''; - - client.signupTeam(email, - function success(data) { - if (data.follow_link) { - window.location.href = data.follow_link; - } else { - this.props.state.wizard = 'finished'; - this.props.updateParent(this.props.state); - window.location.href = '/signup_team_confirm/?email=' + encodeURIComponent(email); - } - }.bind(this), - function error(err) { - this.state.serverError = err.message; - this.setState(this.state); - }.bind(this) - ); - }, - getInitialState: function() { - return {useDiff: false}; - }, - handleKeyPress: function(event) { - if (event.keyCode === 13) { - this.submitNext(event); - } - }, - componentWillMount: function() { - document.addEventListener('keyup', this.handleKeyPress, false); - }, - componentWillUnmount: function() { - document.removeEventListener('keyup', this.handleKeyPress, false); - }, - render: function() { - client.track('signup', 'signup_team_01_welcome'); - - var storageError = null; - if (this.state.storageError) { - storageError = ; - } - - var emailError = null; - var emailDivClass = 'form-group'; - if (this.state.emailError) { - emailError = ; - emailDivClass += ' has-error'; - } - - var serverError = null; - if (this.state.serverError) { - serverError = ( -
- -
- ); - } - - var differentEmailLinkClass = ''; - var emailDivContainerClass = 'hidden'; - if (this.state.useDiff) { - differentEmailLinkClass = 'hidden'; - emailDivContainerClass = ''; - } - - return ( -
-

- -

Welcome to:

-

{config.SiteName}

-

-

Let's set up your new team

-

- Please confirm your email address:
-

-
{this.props.state.team.email}
-
-

-

- Your account will administer the new team site.
- You can add other administrators later. -

-
- - {storageError} -
-
-
-
-
-
- -
-
- {emailError} -
- {serverError} - -
- Use a different email -
- ); - } -}); - -TeamDisplayNamePage = React.createClass({ - displayName: 'TeamDisplayNamePage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'welcome'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var displayName = this.refs.name.getDOMNode().value.trim(); - if (!displayName) { - this.setState({nameError: 'This field is required'}); - return; - } - - this.props.state.wizard = 'team_url'; - this.props.state.team.display_name = displayName; - this.props.state.team.name = utils.cleanUpUrlable(displayName); - this.props.updateParent(this.props.state); - }, - getInitialState: function() { - return {}; - }, - handleFocus: function(e) { - e.preventDefault(); - e.currentTarget.select(); - }, - render: function() { - client.track('signup', 'signup_team_02_name'); - - var nameError = null; - var nameDivClass = 'form-group'; - if (this.state.nameError) { - nameError = ; - nameDivClass += ' has-error'; - } - - return ( -
-
- - -

{utils.toTitleCase(strings.Team) + ' Name'}

-
-
-
- -
-
- {nameError} -
-
{'Name your ' + strings.Team + ' in any language. Your ' + strings.Team + ' name shows in menus and headings.'}
- - -
-
- ); - } -}); - -TeamURLPage = React.createClass({ - displayName: 'TeamURLPage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'team_display_name'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var name = this.refs.name.getDOMNode().value.trim(); - if (!name) { - this.setState({nameError: 'This field is required'}); - return; - } - - var cleanedName = utils.cleanUpUrlable(name); - - var urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g; - if (cleanedName !== name || !urlRegex.test(name)) { - this.setState({nameError: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."}); - return; - } else if (cleanedName.length <= 3 || cleanedName.length > 15) { - this.setState({nameError: 'Name must be 4 or more characters up to a maximum of 15'}); - return; - } - - for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) { - if (cleanedName.indexOf(constants.RESERVED_TEAM_NAMES[index]) === 0) { - this.setState({nameError: 'This team name is unavailable'}); - return; - } - } - - client.findTeamByName(name, - function success(data) { - if (!data) { - if (config.AllowSignupDomainsWizard) { - this.props.state.wizard = 'allowed_domains'; - } else { - this.props.state.wizard = 'send_invites'; - this.props.state.team.type = 'O'; - } - - this.props.state.team.name = name; - this.props.updateParent(this.props.state); - } else { - this.state.nameError = 'This URL is unavailable. Please try another.'; - this.setState(this.state); - } - }.bind(this), - function error(err) { - this.state.nameError = err.message; - this.setState(this.state); - }.bind(this) - ); - }, - getInitialState: function() { - return {}; - }, - handleFocus: function(e) { - e.preventDefault(); - - e.currentTarget.select(); - }, - render: function() { - $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'}); - - client.track('signup', 'signup_team_03_url'); - - var nameError = null; - var nameDivClass = 'form-group'; - if (this.state.nameError) { - nameError = ; - nameDivClass += ' has-error'; - } - - return ( -
-
- -

{utils.toTitleCase(strings.Team) + ' URL'}

-
-
-
-
- {utils.getWindowLocationOrigin() + '/'} - -
-
-
- {nameError} -
-

{'Choose the web address of your new ' + strings.Team + ':'}

-
    -
  • Short and memorable is best
  • -
  • Use lowercase letters, numbers and dashes
  • -
  • Must start with a letter and can't end in a dash
  • -
- - -
-
- ); - } -}); - -AllowedDomainsPage = React.createClass({ - displayName: 'AllowedDomainsPage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'team_url'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - if (this.refs.open_network.getDOMNode().checked) { - this.props.state.wizard = 'send_invites'; - this.props.state.team.type = 'O'; - this.props.updateParent(this.props.state); - return; - } - - if (this.refs.allow.getDOMNode().checked) { - var name = this.refs.name.getDOMNode().value.trim(); - var domainRegex = /^\w+\.\w+$/; - if (!name) { - this.setState({nameError: 'This field is required'}); - return; - } - - if (!name.trim().match(domainRegex)) { - this.setState({nameError: 'The domain doesn\'t appear valid'}); - return; - } - - this.props.state.wizard = 'send_invites'; - this.props.state.team.allowed_domains = name; - this.props.state.team.type = 'I'; - this.props.updateParent(this.props.state); - } else { - this.props.state.wizard = 'send_invites'; - this.props.state.team.type = 'I'; - this.props.updateParent(this.props.state); - } - }, - getInitialState: function() { - return {}; - }, - render: function() { - client.track('signup', 'signup_team_04_allow_domains'); - - var nameError = null; - var nameDivClass = 'form-group'; - if (this.state.nameError) { - nameError = ; - nameDivClass += ' has-error'; - } - - return ( -
-
- -

Email Domain

-

-

- -
-

-

{'Check this box to allow your ' + strings.Team + ' members to sign up using their ' + strings.Company + ' email addresses if you share the same domain--otherwise, you need to invite everyone yourself.'}

-

{'Your ' + strings.Team + '\'s domain for emails'}

-
-
-
-
- @ - -
-
-
- {nameError} -
-

To allow signups from multiple domains, separate each with a comma.

-

-

- -
-

-   - -
-
- ); - } -}); - -EmailItem = React.createClass({ - displayName: 'EmailItem', - propTypes: { - focus: React.PropTypes.bool - }, - getInitialState: function() { - return {}; - }, - getValue: function() { - return this.refs.email.getDOMNode().value.trim(); - }, - validate: function(teamEmail) { - var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); - - if (!email) { - return true; - } - - if (!utils.isEmail(email)) { - this.state.emailError = 'Please enter a valid email address'; - this.setState(this.state); - return false; - } else if (email === teamEmail) { - this.state.emailError = 'Please use a different email than the one used at signup'; - this.setState(this.state); - return false; - } - this.state.emailError = ''; - this.setState(this.state); - return true; - }, - render: function() { - var emailError = null; - var emailDivClass = 'form-group'; - if (this.state.emailError) { - emailError = ; - emailDivClass += ' has-error'; - } - - return ( -
- - {emailError} -
- ); - } -}); - -SendInivtesPage = React.createClass({ - displayName: 'SendInivtesPage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - - if (config.AllowSignupDomainsWizard) { - this.props.state.wizard = 'allowed_domains'; - } else { - this.props.state.wizard = 'team_url'; - } - - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var valid = true; - - if (this.state.emailEnabled) { - var emails = []; - - for (var i = 0; i < this.props.state.invites.length; i++) { - if (!this.refs['email_' + i].validate(this.props.state.team.email)) { - valid = false; - } else { - emails.push(this.refs['email_' + i].getValue()); - } - } - - if (valid) { - this.props.state.invites = emails; - } - } - - if (valid) { - this.props.state.wizard = 'username'; - this.props.updateParent(this.props.state); - } - }, - submitAddInvite: function(e) { - e.preventDefault(); - this.props.state.wizard = 'send_invites'; - if (!this.props.state.invites) { - this.props.state.invites = []; - } - this.props.state.invites.push(''); - this.props.updateParent(this.props.state); - }, - submitSkip: function(e) { - e.preventDefault(); - this.props.state.wizard = 'username'; - this.props.updateParent(this.props.state); - }, - getInitialState: function() { - return { - emailEnabled: !ConfigStore.getSettingAsBoolean('ByPassEmail', false) - }; - }, - render: function() { - client.track('signup', 'signup_team_05_send_invites'); - - var content = null; - var bottomContent = null; - - if (this.state.emailEnabled) { - var emails = []; - - for (var i = 0; i < this.props.state.invites.length; i++) { - if (i === 0) { - emails.push(); - } else { - emails.push(); - } - } - - content = ( -
- {emails} - -
- ); - - bottomContent = ( -

{'if you prefer, you can invite ' + strings.Team + ' members later'}
and skip this step for now.

- ); - } else { - content = ( -
{'Email is currently disabled for your ' + strings.Team + ', and emails cannot be sent. Contact your system administrator to enable email and email invitations.'}
- ); - } - - return ( -
-
- -

{'Invite ' + utils.toTitleCase(strings.Team) + ' Members'}

- {content} -
- -
-
- {bottomContent} - -
- ); - } -}); - -UsernamePage = React.createClass({ - displayName: 'UsernamePage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'send_invites'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var name = this.refs.name.getDOMNode().value.trim(); - - var usernameError = utils.isValidUsername(name); - if (usernameError === 'Cannot use a reserved word as a username.') { - this.setState({nameError: 'This username is reserved, please choose a new one.'}); - return; - } else if (usernameError) { - this.setState({nameError: 'Username must begin with a letter, and contain 3 to 15 characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''}); - return; - } - - this.props.state.wizard = 'password'; - this.props.state.user.username = name; - this.props.updateParent(this.props.state); - }, - getInitialState: function() { - return {}; - }, - render: function() { - client.track('signup', 'signup_team_06_username'); - - var nameError = null; - var nameDivClass = 'form-group'; - if (this.state.nameError) { - nameError = ; - nameDivClass += ' has-error'; - } - - return ( -
-
- -

Your username

-
{'Select a memorable username that makes it easy for ' + strings.Team + 'mates to identify you:'}
-
-
-
-
-
Choose your username
- -
Usernames must begin with a letter and contain 3 to 15 characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'
-
-
- {nameError} -
-
- - -
-
- ); - } -}); - -PasswordPage = React.createClass({ - displayName: 'PasswordPage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'username'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var password = this.refs.password.getDOMNode().value.trim(); - if (!password || password.length < 5) { - this.setState({passwordError: 'Please enter at least 5 characters'}); - return; - } - - this.setState({passwordError: null, serverError: null}); - $('#finish-button').button('loading'); - var teamSignup = JSON.parse(JSON.stringify(this.props.state)); - teamSignup.user.password = password; - teamSignup.user.allow_marketing = true; - delete teamSignup.wizard; - - // var ctl = this; - - client.createTeamFromSignup(teamSignup, - function success() { - client.track('signup', 'signup_team_08_complete'); - - var props = this.props; - - $('#sign-up-button').button('reset'); - props.state.wizard = 'finished'; - props.updateParent(props.state, true); - - window.location.href = utils.getWindowLocationOrigin() + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email); - - // client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password, - // function(data) { - // TeamStore.setLastName(teamSignup.team.domain); - // UserStore.setLastEmail(teamSignup.team.email); - // UserStore.setCurrentUser(data); - // window.location.href = '/channels/town-square'; - // }.bind(ctl), - // function(err) { - // this.setState({nameError: err.message}); - // }.bind(ctl) - // ); - }.bind(this), - function error(err) { - this.setState({serverError: err.message}); - $('#sign-up-button').button('reset'); - }.bind(this) - ); - }, - getInitialState: function() { - return {}; - }, - render: function() { - client.track('signup', 'signup_team_07_password'); - - var passwordError = null; - var passwordDivStyle = 'form-group'; - if (this.state.passwordError) { - passwordError =
; - passwordDivStyle = ' has-error'; - } - - var serverError = null; - if (this.state.serverError) { - serverError =
; - } - - return ( -
-
- -

Your password

-
Select a password that you'll use to login with your email address:
-
-
Email
-
{this.props.state.team.email}
-
-
-
-
Choose your password
- -
Passwords must contain 5 to 50 characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.
-
-
- {passwordError} - {serverError} -
-
-
- -
-

By proceeding to create your account and use {config.SiteName}, you agree to our Terms of Service and Privacy Policy. If you do not agree, you cannot use {config.SiteName}.

- -
-
- ); - } -}); module.exports = React.createClass({ displayName: 'SignupTeamComplete', diff --git a/web/react/components/team_display_name_page.jsx b/web/react/components/team_display_name_page.jsx new file mode 100644 index 000000000..eb9fa79c3 --- /dev/null +++ b/web/react/components/team_display_name_page.jsx @@ -0,0 +1,72 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamDisplayNamePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'welcome'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var displayName = this.refs.name.getDOMNode().value.trim(); + if (!displayName) { + this.setState({nameError: 'This field is required'}); + return; + } + + this.props.state.wizard = 'team_url'; + this.props.state.team.display_name = displayName; + this.props.state.team.name = utils.cleanUpUrlable(displayName); + this.props.updateParent(this.props.state); + }, + getInitialState: function() { + return {}; + }, + handleFocus: function(e) { + e.preventDefault(); + e.currentTarget.select(); + }, + render: function() { + client.track('signup', 'signup_team_02_name'); + + var nameError = null; + var nameDivClass = 'form-group'; + if (this.state.nameError) { + nameError = ; + nameDivClass += ' has-error'; + } + + return ( +
+
+ + +

{utils.toTitleCase(strings.Team) + ' Name'}

+
+
+
+ +
+
+ {nameError} +
+
{'Name your ' + strings.Team + ' in any language. Your ' + strings.Team + ' name shows in menus and headings.'}
+ + +
+
+ ); + } +}); diff --git a/web/react/components/team_url_page.jsx b/web/react/components/team_url_page.jsx new file mode 100644 index 000000000..cefa196ef --- /dev/null +++ b/web/react/components/team_url_page.jsx @@ -0,0 +1,119 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); +var constants = require('../utils/constants.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamURLPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'team_display_name'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var name = this.refs.name.getDOMNode().value.trim(); + if (!name) { + this.setState({nameError: 'This field is required'}); + return; + } + + var cleanedName = utils.cleanUpUrlable(name); + + var urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g; + if (cleanedName !== name || !urlRegex.test(name)) { + this.setState({nameError: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."}); + return; + } else if (cleanedName.length <= 3 || cleanedName.length > 15) { + this.setState({nameError: 'Name must be 4 or more characters up to a maximum of 15'}); + return; + } + + for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) { + if (cleanedName.indexOf(constants.RESERVED_TEAM_NAMES[index]) === 0) { + this.setState({nameError: 'This team name is unavailable'}); + return; + } + } + + client.findTeamByName(name, + function success(data) { + if (!data) { + if (config.AllowSignupDomainsWizard) { + this.props.state.wizard = 'allowed_domains'; + } else { + this.props.state.wizard = 'send_invites'; + this.props.state.team.type = 'O'; + } + + this.props.state.team.name = name; + this.props.updateParent(this.props.state); + } else { + this.state.nameError = 'This URL is unavailable. Please try another.'; + this.setState(this.state); + } + }.bind(this), + function error(err) { + this.state.nameError = err.message; + this.setState(this.state); + }.bind(this) + ); + }, + getInitialState: function() { + return {}; + }, + handleFocus: function(e) { + e.preventDefault(); + + e.currentTarget.select(); + }, + render: function() { + $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'}); + + client.track('signup', 'signup_team_03_url'); + + var nameError = null; + var nameDivClass = 'form-group'; + if (this.state.nameError) { + nameError = ; + nameDivClass += ' has-error'; + } + + return ( +
+
+ +

{utils.toTitleCase(strings.Team) + ' URL'}

+
+
+
+
+ {utils.getWindowLocationOrigin() + '/'} + +
+
+
+ {nameError} +
+

{'Choose the web address of your new ' + strings.Team + ':'}

+
    +
  • Short and memorable is best
  • +
  • Use lowercase letters, numbers and dashes
  • +
  • Must start with a letter and can't end in a dash
  • +
+ + +
+
+ ); + } +}); diff --git a/web/react/components/username_page.jsx b/web/react/components/username_page.jsx new file mode 100644 index 000000000..a83a8f599 --- /dev/null +++ b/web/react/components/username_page.jsx @@ -0,0 +1,75 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'UsernamePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'send_invites'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var name = this.refs.name.getDOMNode().value.trim(); + + var usernameError = utils.isValidUsername(name); + if (usernameError === 'Cannot use a reserved word as a username.') { + this.setState({nameError: 'This username is reserved, please choose a new one.'}); + return; + } else if (usernameError) { + this.setState({nameError: 'Username must begin with a letter, and contain 3 to 15 characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''}); + return; + } + + this.props.state.wizard = 'password'; + this.props.state.user.username = name; + this.props.updateParent(this.props.state); + }, + getInitialState: function() { + return {}; + }, + render: function() { + client.track('signup', 'signup_team_06_username'); + + var nameError = null; + var nameDivClass = 'form-group'; + if (this.state.nameError) { + nameError = ; + nameDivClass += ' has-error'; + } + + return ( +
+
+ +

Your username

+
{'Select a memorable username that makes it easy for ' + strings.Team + 'mates to identify you:'}
+
+
+
+
+
Choose your username
+ +
Usernames must begin with a letter and contain 3 to 15 characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'
+
+
+ {nameError} +
+
+ + +
+
+ ); + } +}); diff --git a/web/react/components/welcome_page.jsx b/web/react/components/welcome_page.jsx new file mode 100644 index 000000000..1f8576ac7 --- /dev/null +++ b/web/react/components/welcome_page.jsx @@ -0,0 +1,144 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); +var BrowserStore = require('../stores/browser_store.jsx'); + +module.exports = React.createClass({ + displayName: 'WelcomePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitNext: function(e) { + if (!BrowserStore.isLocalStorageSupported()) { + this.setState({storageError: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'}); + return; + } + e.preventDefault(); + this.props.state.wizard = 'team_display_name'; + this.props.updateParent(this.props.state); + }, + handleDiffEmail: function(e) { + e.preventDefault(); + this.setState({useDiff: true}); + }, + handleDiffSubmit: function(e) { + e.preventDefault(); + + var state = {useDiff: true, serverError: ''}; + + var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); + if (!email || !utils.isEmail(email)) { + state.emailError = 'Please enter a valid email address'; + this.setState(state); + return; + } else if (!BrowserStore.isLocalStorageSupported()) { + state.emailError = 'This service requires local storage to be enabled. Please enable it or exit private browsing.'; + this.setState(state); + return; + } + state.emailError = ''; + + client.signupTeam(email, + function success(data) { + if (data.follow_link) { + window.location.href = data.follow_link; + } else { + this.props.state.wizard = 'finished'; + this.props.updateParent(this.props.state); + window.location.href = '/signup_team_confirm/?email=' + encodeURIComponent(email); + } + }.bind(this), + function error(err) { + this.state.serverError = err.message; + this.setState(this.state); + }.bind(this) + ); + }, + getInitialState: function() { + return {useDiff: false}; + }, + handleKeyPress: function(event) { + if (event.keyCode === 13) { + this.submitNext(event); + } + }, + componentWillMount: function() { + document.addEventListener('keyup', this.handleKeyPress, false); + }, + componentWillUnmount: function() { + document.removeEventListener('keyup', this.handleKeyPress, false); + }, + render: function() { + client.track('signup', 'signup_team_01_welcome'); + + var storageError = null; + if (this.state.storageError) { + storageError = ; + } + + var emailError = null; + var emailDivClass = 'form-group'; + if (this.state.emailError) { + emailError = ; + emailDivClass += ' has-error'; + } + + var serverError = null; + if (this.state.serverError) { + serverError = ( +
+ +
+ ); + } + + var differentEmailLinkClass = ''; + var emailDivContainerClass = 'hidden'; + if (this.state.useDiff) { + differentEmailLinkClass = 'hidden'; + emailDivContainerClass = ''; + } + + return ( +
+

+ +

Welcome to:

+

{config.SiteName}

+

+

Let's set up your new team

+

+ Please confirm your email address:
+

+
{this.props.state.team.email}
+
+

+

+ Your account will administer the new team site.
+ You can add other administrators later. +

+
+ + {storageError} +
+
+
+
+
+
+ +
+
+ {emailError} +
+ {serverError} + +
+ Use a different email +
+ ); + } +}); -- cgit v1.2.3-1-g7c22 From 84c82e8d7a1a7e4ec8808b86ff5bea78138d1886 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Tue, 18 Aug 2015 09:15:16 -0700 Subject: Renamed new component files for easier identification --- web/react/components/allowed_domains_page.jsx | 98 -------------- web/react/components/email_item.jsx | 53 -------- web/react/components/password_page.jsx | 116 ----------------- web/react/components/send_invites_page.jsx | 121 ----------------- web/react/components/signup_team_complete.jsx | 14 +- web/react/components/team_display_name_page.jsx | 72 ----------- .../team_signup_allowed_domains_page.jsx | 98 ++++++++++++++ .../components/team_signup_display_name_page.jsx | 72 +++++++++++ web/react/components/team_signup_email_item.jsx | 53 ++++++++ web/react/components/team_signup_password_page.jsx | 116 +++++++++++++++++ .../components/team_signup_send_invites_page.jsx | 121 +++++++++++++++++ web/react/components/team_signup_url_page.jsx | 119 +++++++++++++++++ web/react/components/team_signup_username_page.jsx | 75 +++++++++++ web/react/components/team_signup_welcome_page.jsx | 144 +++++++++++++++++++++ web/react/components/team_url_page.jsx | 119 ----------------- web/react/components/username_page.jsx | 75 ----------- web/react/components/welcome_page.jsx | 144 --------------------- 17 files changed, 805 insertions(+), 805 deletions(-) delete mode 100644 web/react/components/allowed_domains_page.jsx delete mode 100644 web/react/components/email_item.jsx delete mode 100644 web/react/components/password_page.jsx delete mode 100644 web/react/components/send_invites_page.jsx delete mode 100644 web/react/components/team_display_name_page.jsx create mode 100644 web/react/components/team_signup_allowed_domains_page.jsx create mode 100644 web/react/components/team_signup_display_name_page.jsx create mode 100644 web/react/components/team_signup_email_item.jsx create mode 100644 web/react/components/team_signup_password_page.jsx create mode 100644 web/react/components/team_signup_send_invites_page.jsx create mode 100644 web/react/components/team_signup_url_page.jsx create mode 100644 web/react/components/team_signup_username_page.jsx create mode 100644 web/react/components/team_signup_welcome_page.jsx delete mode 100644 web/react/components/team_url_page.jsx delete mode 100644 web/react/components/username_page.jsx delete mode 100644 web/react/components/welcome_page.jsx (limited to 'web/react/components') diff --git a/web/react/components/allowed_domains_page.jsx b/web/react/components/allowed_domains_page.jsx deleted file mode 100644 index 56900e1df..000000000 --- a/web/react/components/allowed_domains_page.jsx +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var client = require('../utils/client.jsx'); - -module.exports = React.createClass({ - displayName: 'AllowedDomainsPage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'team_url'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - if (this.refs.open_network.getDOMNode().checked) { - this.props.state.wizard = 'send_invites'; - this.props.state.team.type = 'O'; - this.props.updateParent(this.props.state); - return; - } - - if (this.refs.allow.getDOMNode().checked) { - var name = this.refs.name.getDOMNode().value.trim(); - var domainRegex = /^\w+\.\w+$/; - if (!name) { - this.setState({nameError: 'This field is required'}); - return; - } - - if (!name.trim().match(domainRegex)) { - this.setState({nameError: 'The domain doesn\'t appear valid'}); - return; - } - - this.props.state.wizard = 'send_invites'; - this.props.state.team.allowed_domains = name; - this.props.state.team.type = 'I'; - this.props.updateParent(this.props.state); - } else { - this.props.state.wizard = 'send_invites'; - this.props.state.team.type = 'I'; - this.props.updateParent(this.props.state); - } - }, - getInitialState: function() { - return {}; - }, - render: function() { - client.track('signup', 'signup_team_04_allow_domains'); - - var nameError = null; - var nameDivClass = 'form-group'; - if (this.state.nameError) { - nameError = ; - nameDivClass += ' has-error'; - } - - return ( -
-
- -

Email Domain

-

-

- -
-

-

{'Check this box to allow your ' + strings.Team + ' members to sign up using their ' + strings.Company + ' email addresses if you share the same domain--otherwise, you need to invite everyone yourself.'}

-

{'Your ' + strings.Team + '\'s domain for emails'}

-
-
-
-
- @ - -
-
-
- {nameError} -
-

To allow signups from multiple domains, separate each with a comma.

-

-

- -
-

-   - -
-
- ); - } -}); diff --git a/web/react/components/email_item.jsx b/web/react/components/email_item.jsx deleted file mode 100644 index c0ade40cc..000000000 --- a/web/react/components/email_item.jsx +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var utils = require('../utils/utils.jsx'); - -module.exports = React.createClass({ - displayName: 'EmailItem', - propTypes: { - focus: React.PropTypes.bool, - email: React.PropTypes.string - }, - getInitialState: function() { - return {}; - }, - getValue: function() { - return this.refs.email.getDOMNode().value.trim(); - }, - validate: function(teamEmail) { - var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); - - if (!email) { - return true; - } - - if (!utils.isEmail(email)) { - this.state.emailError = 'Please enter a valid email address'; - this.setState(this.state); - return false; - } else if (email === teamEmail) { - this.state.emailError = 'Please use a different email than the one used at signup'; - this.setState(this.state); - return false; - } - this.state.emailError = ''; - this.setState(this.state); - return true; - }, - render: function() { - var emailError = null; - var emailDivClass = 'form-group'; - if (this.state.emailError) { - emailError = ; - emailDivClass += ' has-error'; - } - - return ( -
- - {emailError} -
- ); - } -}); diff --git a/web/react/components/password_page.jsx b/web/react/components/password_page.jsx deleted file mode 100644 index bb5eba682..000000000 --- a/web/react/components/password_page.jsx +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var utils = require('../utils/utils.jsx'); -var client = require('../utils/client.jsx'); - -module.exports = React.createClass({ - displayName: 'PasswordPage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'username'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var password = this.refs.password.getDOMNode().value.trim(); - if (!password || password.length < 5) { - this.setState({passwordError: 'Please enter at least 5 characters'}); - return; - } - - this.setState({passwordError: null, serverError: null}); - $('#finish-button').button('loading'); - var teamSignup = JSON.parse(JSON.stringify(this.props.state)); - teamSignup.user.password = password; - teamSignup.user.allow_marketing = true; - delete teamSignup.wizard; - - // var ctl = this; - - client.createTeamFromSignup(teamSignup, - function success() { - client.track('signup', 'signup_team_08_complete'); - - var props = this.props; - - $('#sign-up-button').button('reset'); - props.state.wizard = 'finished'; - props.updateParent(props.state, true); - - window.location.href = utils.getWindowLocationOrigin() + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email); - - // client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password, - // function(data) { - // TeamStore.setLastName(teamSignup.team.domain); - // UserStore.setLastEmail(teamSignup.team.email); - // UserStore.setCurrentUser(data); - // window.location.href = '/channels/town-square'; - // }.bind(ctl), - // function(err) { - // this.setState({nameError: err.message}); - // }.bind(ctl) - // ); - }.bind(this), - function error(err) { - this.setState({serverError: err.message}); - $('#sign-up-button').button('reset'); - }.bind(this) - ); - }, - getInitialState: function() { - return {}; - }, - render: function() { - client.track('signup', 'signup_team_07_password'); - - var passwordError = null; - var passwordDivStyle = 'form-group'; - if (this.state.passwordError) { - passwordError =
; - passwordDivStyle = ' has-error'; - } - - var serverError = null; - if (this.state.serverError) { - serverError =
; - } - - return ( -
-
- -

Your password

-
Select a password that you'll use to login with your email address:
-
-
Email
-
{this.props.state.team.email}
-
-
-
-
Choose your password
- -
Passwords must contain 5 to 50 characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.
-
-
- {passwordError} - {serverError} -
-
-
- -
-

By proceeding to create your account and use {config.SiteName}, you agree to our Terms of Service and Privacy Policy. If you do not agree, you cannot use {config.SiteName}.

- -
-
- ); - } -}); diff --git a/web/react/components/send_invites_page.jsx b/web/react/components/send_invites_page.jsx deleted file mode 100644 index 2aa44c970..000000000 --- a/web/react/components/send_invites_page.jsx +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var EmailItem = require('./email_item.jsx'); -var utils = require('../utils/utils.jsx'); -var ConfigStore = require('../stores/config_store.jsx'); -var client = require('../utils/client.jsx'); - -module.exports = React.createClass({ - displayName: 'SendInivtesPage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - - if (config.AllowSignupDomainsWizard) { - this.props.state.wizard = 'allowed_domains'; - } else { - this.props.state.wizard = 'team_url'; - } - - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var valid = true; - - if (this.state.emailEnabled) { - var emails = []; - - for (var i = 0; i < this.props.state.invites.length; i++) { - if (!this.refs['email_' + i].validate(this.props.state.team.email)) { - valid = false; - } else { - emails.push(this.refs['email_' + i].getValue()); - } - } - - if (valid) { - this.props.state.invites = emails; - } - } - - if (valid) { - this.props.state.wizard = 'username'; - this.props.updateParent(this.props.state); - } - }, - submitAddInvite: function(e) { - e.preventDefault(); - this.props.state.wizard = 'send_invites'; - if (!this.props.state.invites) { - this.props.state.invites = []; - } - this.props.state.invites.push(''); - this.props.updateParent(this.props.state); - }, - submitSkip: function(e) { - e.preventDefault(); - this.props.state.wizard = 'username'; - this.props.updateParent(this.props.state); - }, - getInitialState: function() { - return { - emailEnabled: !ConfigStore.getSettingAsBoolean('ByPassEmail', false) - }; - }, - render: function() { - client.track('signup', 'signup_team_05_send_invites'); - - var content = null; - var bottomContent = null; - - if (this.state.emailEnabled) { - var emails = []; - - for (var i = 0; i < this.props.state.invites.length; i++) { - if (i === 0) { - emails.push(); - } else { - emails.push(); - } - } - - content = ( -
- {emails} - -
- ); - - bottomContent = ( -

{'if you prefer, you can invite ' + strings.Team + ' members later'}
and skip this step for now.

- ); - } else { - content = ( -
{'Email is currently disabled for your ' + strings.Team + ', and emails cannot be sent. Contact your system administrator to enable email and email invitations.'}
- ); - } - - return ( -
-
- -

{'Invite ' + utils.toTitleCase(strings.Team) + ' Members'}

- {content} -
- -
-
- {bottomContent} - -
- ); - } -}); diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx index 0cfd8b4e0..756aae638 100644 --- a/web/react/components/signup_team_complete.jsx +++ b/web/react/components/signup_team_complete.jsx @@ -1,13 +1,13 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. -var WelcomePage = require('./welcome_page.jsx'); -var TeamDisplayNamePage = require('./team_display_name_page.jsx'); -var TeamURLPage = require('./team_url_page.jsx'); -var AllowedDomainsPage = require('./allowed_domains_page.jsx'); -var SendInivtesPage = require('./send_invites_page.jsx'); -var UsernamePage = require('./username_page.jsx'); -var PasswordPage = require('./password_page.jsx'); +var WelcomePage = require('./team_signup_welcome_page.jsx'); +var TeamDisplayNamePage = require('./team_signup_display_name_page.jsx'); +var TeamURLPage = require('./team_signup_url_page.jsx'); +var AllowedDomainsPage = require('./team_signup_allowed_domains_page.jsx'); +var SendInivtesPage = require('./team_signup_send_invites_page.jsx'); +var UsernamePage = require('./team_signup_username_page.jsx'); +var PasswordPage = require('./team_signup_password_page.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); module.exports = React.createClass({ diff --git a/web/react/components/team_display_name_page.jsx b/web/react/components/team_display_name_page.jsx deleted file mode 100644 index eb9fa79c3..000000000 --- a/web/react/components/team_display_name_page.jsx +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var utils = require('../utils/utils.jsx'); -var client = require('../utils/client.jsx'); - -module.exports = React.createClass({ - displayName: 'TeamDisplayNamePage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'welcome'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var displayName = this.refs.name.getDOMNode().value.trim(); - if (!displayName) { - this.setState({nameError: 'This field is required'}); - return; - } - - this.props.state.wizard = 'team_url'; - this.props.state.team.display_name = displayName; - this.props.state.team.name = utils.cleanUpUrlable(displayName); - this.props.updateParent(this.props.state); - }, - getInitialState: function() { - return {}; - }, - handleFocus: function(e) { - e.preventDefault(); - e.currentTarget.select(); - }, - render: function() { - client.track('signup', 'signup_team_02_name'); - - var nameError = null; - var nameDivClass = 'form-group'; - if (this.state.nameError) { - nameError = ; - nameDivClass += ' has-error'; - } - - return ( -
-
- - -

{utils.toTitleCase(strings.Team) + ' Name'}

-
-
-
- -
-
- {nameError} -
-
{'Name your ' + strings.Team + ' in any language. Your ' + strings.Team + ' name shows in menus and headings.'}
- - -
-
- ); - } -}); diff --git a/web/react/components/team_signup_allowed_domains_page.jsx b/web/react/components/team_signup_allowed_domains_page.jsx new file mode 100644 index 000000000..90c7ff668 --- /dev/null +++ b/web/react/components/team_signup_allowed_domains_page.jsx @@ -0,0 +1,98 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamSignupAllowedDomainsPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'team_url'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + if (this.refs.open_network.getDOMNode().checked) { + this.props.state.wizard = 'send_invites'; + this.props.state.team.type = 'O'; + this.props.updateParent(this.props.state); + return; + } + + if (this.refs.allow.getDOMNode().checked) { + var name = this.refs.name.getDOMNode().value.trim(); + var domainRegex = /^\w+\.\w+$/; + if (!name) { + this.setState({nameError: 'This field is required'}); + return; + } + + if (!name.trim().match(domainRegex)) { + this.setState({nameError: 'The domain doesn\'t appear valid'}); + return; + } + + this.props.state.wizard = 'send_invites'; + this.props.state.team.allowed_domains = name; + this.props.state.team.type = 'I'; + this.props.updateParent(this.props.state); + } else { + this.props.state.wizard = 'send_invites'; + this.props.state.team.type = 'I'; + this.props.updateParent(this.props.state); + } + }, + getInitialState: function() { + return {}; + }, + render: function() { + client.track('signup', 'signup_team_04_allow_domains'); + + var nameError = null; + var nameDivClass = 'form-group'; + if (this.state.nameError) { + nameError = ; + nameDivClass += ' has-error'; + } + + return ( +
+
+ +

Email Domain

+

+

+ +
+

+

{'Check this box to allow your ' + strings.Team + ' members to sign up using their ' + strings.Company + ' email addresses if you share the same domain--otherwise, you need to invite everyone yourself.'}

+

{'Your ' + strings.Team + '\'s domain for emails'}

+
+
+
+
+ @ + +
+
+
+ {nameError} +
+

To allow signups from multiple domains, separate each with a comma.

+

+

+ +
+

+   + +
+
+ ); + } +}); diff --git a/web/react/components/team_signup_display_name_page.jsx b/web/react/components/team_signup_display_name_page.jsx new file mode 100644 index 000000000..b5e93de1b --- /dev/null +++ b/web/react/components/team_signup_display_name_page.jsx @@ -0,0 +1,72 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamSignupDisplayNamePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'welcome'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var displayName = this.refs.name.getDOMNode().value.trim(); + if (!displayName) { + this.setState({nameError: 'This field is required'}); + return; + } + + this.props.state.wizard = 'team_url'; + this.props.state.team.display_name = displayName; + this.props.state.team.name = utils.cleanUpUrlable(displayName); + this.props.updateParent(this.props.state); + }, + getInitialState: function() { + return {}; + }, + handleFocus: function(e) { + e.preventDefault(); + e.currentTarget.select(); + }, + render: function() { + client.track('signup', 'signup_team_02_name'); + + var nameError = null; + var nameDivClass = 'form-group'; + if (this.state.nameError) { + nameError = ; + nameDivClass += ' has-error'; + } + + return ( +
+
+ + +

{utils.toTitleCase(strings.Team) + ' Name'}

+
+
+
+ +
+
+ {nameError} +
+
{'Name your ' + strings.Team + ' in any language. Your ' + strings.Team + ' name shows in menus and headings.'}
+ + +
+
+ ); + } +}); diff --git a/web/react/components/team_signup_email_item.jsx b/web/react/components/team_signup_email_item.jsx new file mode 100644 index 000000000..11cd17e74 --- /dev/null +++ b/web/react/components/team_signup_email_item.jsx @@ -0,0 +1,53 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamSignupEmailItem', + propTypes: { + focus: React.PropTypes.bool, + email: React.PropTypes.string + }, + getInitialState: function() { + return {}; + }, + getValue: function() { + return this.refs.email.getDOMNode().value.trim(); + }, + validate: function(teamEmail) { + var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); + + if (!email) { + return true; + } + + if (!utils.isEmail(email)) { + this.state.emailError = 'Please enter a valid email address'; + this.setState(this.state); + return false; + } else if (email === teamEmail) { + this.state.emailError = 'Please use a different email than the one used at signup'; + this.setState(this.state); + return false; + } + this.state.emailError = ''; + this.setState(this.state); + return true; + }, + render: function() { + var emailError = null; + var emailDivClass = 'form-group'; + if (this.state.emailError) { + emailError = ; + emailDivClass += ' has-error'; + } + + return ( +
+ + {emailError} +
+ ); + } +}); diff --git a/web/react/components/team_signup_password_page.jsx b/web/react/components/team_signup_password_page.jsx new file mode 100644 index 000000000..e4f35f100 --- /dev/null +++ b/web/react/components/team_signup_password_page.jsx @@ -0,0 +1,116 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamSignupPasswordPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'username'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var password = this.refs.password.getDOMNode().value.trim(); + if (!password || password.length < 5) { + this.setState({passwordError: 'Please enter at least 5 characters'}); + return; + } + + this.setState({passwordError: null, serverError: null}); + $('#finish-button').button('loading'); + var teamSignup = JSON.parse(JSON.stringify(this.props.state)); + teamSignup.user.password = password; + teamSignup.user.allow_marketing = true; + delete teamSignup.wizard; + + // var ctl = this; + + client.createTeamFromSignup(teamSignup, + function success() { + client.track('signup', 'signup_team_08_complete'); + + var props = this.props; + + $('#sign-up-button').button('reset'); + props.state.wizard = 'finished'; + props.updateParent(props.state, true); + + window.location.href = utils.getWindowLocationOrigin() + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email); + + // client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password, + // function(data) { + // TeamStore.setLastName(teamSignup.team.domain); + // UserStore.setLastEmail(teamSignup.team.email); + // UserStore.setCurrentUser(data); + // window.location.href = '/channels/town-square'; + // }.bind(ctl), + // function(err) { + // this.setState({nameError: err.message}); + // }.bind(ctl) + // ); + }.bind(this), + function error(err) { + this.setState({serverError: err.message}); + $('#sign-up-button').button('reset'); + }.bind(this) + ); + }, + getInitialState: function() { + return {}; + }, + render: function() { + client.track('signup', 'signup_team_07_password'); + + var passwordError = null; + var passwordDivStyle = 'form-group'; + if (this.state.passwordError) { + passwordError =
; + passwordDivStyle = ' has-error'; + } + + var serverError = null; + if (this.state.serverError) { + serverError =
; + } + + return ( +
+
+ +

Your password

+
Select a password that you'll use to login with your email address:
+
+
Email
+
{this.props.state.team.email}
+
+
+
+
Choose your password
+ +
Passwords must contain 5 to 50 characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.
+
+
+ {passwordError} + {serverError} +
+
+
+ +
+

By proceeding to create your account and use {config.SiteName}, you agree to our Terms of Service and Privacy Policy. If you do not agree, you cannot use {config.SiteName}.

+ +
+
+ ); + } +}); diff --git a/web/react/components/team_signup_send_invites_page.jsx b/web/react/components/team_signup_send_invites_page.jsx new file mode 100644 index 000000000..4bc03798b --- /dev/null +++ b/web/react/components/team_signup_send_invites_page.jsx @@ -0,0 +1,121 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var EmailItem = require('./team_signup_email_item.jsx'); +var utils = require('../utils/utils.jsx'); +var ConfigStore = require('../stores/config_store.jsx'); +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamSignupSendInivtesPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + + if (config.AllowSignupDomainsWizard) { + this.props.state.wizard = 'allowed_domains'; + } else { + this.props.state.wizard = 'team_url'; + } + + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var valid = true; + + if (this.state.emailEnabled) { + var emails = []; + + for (var i = 0; i < this.props.state.invites.length; i++) { + if (!this.refs['email_' + i].validate(this.props.state.team.email)) { + valid = false; + } else { + emails.push(this.refs['email_' + i].getValue()); + } + } + + if (valid) { + this.props.state.invites = emails; + } + } + + if (valid) { + this.props.state.wizard = 'username'; + this.props.updateParent(this.props.state); + } + }, + submitAddInvite: function(e) { + e.preventDefault(); + this.props.state.wizard = 'send_invites'; + if (!this.props.state.invites) { + this.props.state.invites = []; + } + this.props.state.invites.push(''); + this.props.updateParent(this.props.state); + }, + submitSkip: function(e) { + e.preventDefault(); + this.props.state.wizard = 'username'; + this.props.updateParent(this.props.state); + }, + getInitialState: function() { + return { + emailEnabled: !ConfigStore.getSettingAsBoolean('ByPassEmail', false) + }; + }, + render: function() { + client.track('signup', 'signup_team_05_send_invites'); + + var content = null; + var bottomContent = null; + + if (this.state.emailEnabled) { + var emails = []; + + for (var i = 0; i < this.props.state.invites.length; i++) { + if (i === 0) { + emails.push(); + } else { + emails.push(); + } + } + + content = ( +
+ {emails} + +
+ ); + + bottomContent = ( +

{'if you prefer, you can invite ' + strings.Team + ' members later'}
and skip this step for now.

+ ); + } else { + content = ( +
{'Email is currently disabled for your ' + strings.Team + ', and emails cannot be sent. Contact your system administrator to enable email and email invitations.'}
+ ); + } + + return ( +
+
+ +

{'Invite ' + utils.toTitleCase(strings.Team) + ' Members'}

+ {content} +
+ +
+
+ {bottomContent} + +
+ ); + } +}); diff --git a/web/react/components/team_signup_url_page.jsx b/web/react/components/team_signup_url_page.jsx new file mode 100644 index 000000000..beef725e2 --- /dev/null +++ b/web/react/components/team_signup_url_page.jsx @@ -0,0 +1,119 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); +var constants = require('../utils/constants.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamSignupURLPage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'team_display_name'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var name = this.refs.name.getDOMNode().value.trim(); + if (!name) { + this.setState({nameError: 'This field is required'}); + return; + } + + var cleanedName = utils.cleanUpUrlable(name); + + var urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g; + if (cleanedName !== name || !urlRegex.test(name)) { + this.setState({nameError: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."}); + return; + } else if (cleanedName.length <= 3 || cleanedName.length > 15) { + this.setState({nameError: 'Name must be 4 or more characters up to a maximum of 15'}); + return; + } + + for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) { + if (cleanedName.indexOf(constants.RESERVED_TEAM_NAMES[index]) === 0) { + this.setState({nameError: 'This team name is unavailable'}); + return; + } + } + + client.findTeamByName(name, + function success(data) { + if (!data) { + if (config.AllowSignupDomainsWizard) { + this.props.state.wizard = 'allowed_domains'; + } else { + this.props.state.wizard = 'send_invites'; + this.props.state.team.type = 'O'; + } + + this.props.state.team.name = name; + this.props.updateParent(this.props.state); + } else { + this.state.nameError = 'This URL is unavailable. Please try another.'; + this.setState(this.state); + } + }.bind(this), + function error(err) { + this.state.nameError = err.message; + this.setState(this.state); + }.bind(this) + ); + }, + getInitialState: function() { + return {}; + }, + handleFocus: function(e) { + e.preventDefault(); + + e.currentTarget.select(); + }, + render: function() { + $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'}); + + client.track('signup', 'signup_team_03_url'); + + var nameError = null; + var nameDivClass = 'form-group'; + if (this.state.nameError) { + nameError = ; + nameDivClass += ' has-error'; + } + + return ( +
+
+ +

{utils.toTitleCase(strings.Team) + ' URL'}

+
+
+
+
+ {utils.getWindowLocationOrigin() + '/'} + +
+
+
+ {nameError} +
+

{'Choose the web address of your new ' + strings.Team + ':'}

+
    +
  • Short and memorable is best
  • +
  • Use lowercase letters, numbers and dashes
  • +
  • Must start with a letter and can't end in a dash
  • +
+ + +
+
+ ); + } +}); diff --git a/web/react/components/team_signup_username_page.jsx b/web/react/components/team_signup_username_page.jsx new file mode 100644 index 000000000..56882e6a1 --- /dev/null +++ b/web/react/components/team_signup_username_page.jsx @@ -0,0 +1,75 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamSignupUsernamePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitBack: function(e) { + e.preventDefault(); + this.props.state.wizard = 'send_invites'; + this.props.updateParent(this.props.state); + }, + submitNext: function(e) { + e.preventDefault(); + + var name = this.refs.name.getDOMNode().value.trim(); + + var usernameError = utils.isValidUsername(name); + if (usernameError === 'Cannot use a reserved word as a username.') { + this.setState({nameError: 'This username is reserved, please choose a new one.'}); + return; + } else if (usernameError) { + this.setState({nameError: 'Username must begin with a letter, and contain 3 to 15 characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''}); + return; + } + + this.props.state.wizard = 'password'; + this.props.state.user.username = name; + this.props.updateParent(this.props.state); + }, + getInitialState: function() { + return {}; + }, + render: function() { + client.track('signup', 'signup_team_06_username'); + + var nameError = null; + var nameDivClass = 'form-group'; + if (this.state.nameError) { + nameError = ; + nameDivClass += ' has-error'; + } + + return ( +
+
+ +

Your username

+
{'Select a memorable username that makes it easy for ' + strings.Team + 'mates to identify you:'}
+
+
+
+
+
Choose your username
+ +
Usernames must begin with a letter and contain 3 to 15 characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'
+
+
+ {nameError} +
+
+ + +
+
+ ); + } +}); diff --git a/web/react/components/team_signup_welcome_page.jsx b/web/react/components/team_signup_welcome_page.jsx new file mode 100644 index 000000000..f0c680bd8 --- /dev/null +++ b/web/react/components/team_signup_welcome_page.jsx @@ -0,0 +1,144 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var utils = require('../utils/utils.jsx'); +var client = require('../utils/client.jsx'); +var BrowserStore = require('../stores/browser_store.jsx'); + +module.exports = React.createClass({ + displayName: 'TeamSignupWelcomePage', + propTypes: { + state: React.PropTypes.object, + updateParent: React.PropTypes.func + }, + submitNext: function(e) { + if (!BrowserStore.isLocalStorageSupported()) { + this.setState({storageError: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'}); + return; + } + e.preventDefault(); + this.props.state.wizard = 'team_display_name'; + this.props.updateParent(this.props.state); + }, + handleDiffEmail: function(e) { + e.preventDefault(); + this.setState({useDiff: true}); + }, + handleDiffSubmit: function(e) { + e.preventDefault(); + + var state = {useDiff: true, serverError: ''}; + + var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); + if (!email || !utils.isEmail(email)) { + state.emailError = 'Please enter a valid email address'; + this.setState(state); + return; + } else if (!BrowserStore.isLocalStorageSupported()) { + state.emailError = 'This service requires local storage to be enabled. Please enable it or exit private browsing.'; + this.setState(state); + return; + } + state.emailError = ''; + + client.signupTeam(email, + function success(data) { + if (data.follow_link) { + window.location.href = data.follow_link; + } else { + this.props.state.wizard = 'finished'; + this.props.updateParent(this.props.state); + window.location.href = '/signup_team_confirm/?email=' + encodeURIComponent(email); + } + }.bind(this), + function error(err) { + this.state.serverError = err.message; + this.setState(this.state); + }.bind(this) + ); + }, + getInitialState: function() { + return {useDiff: false}; + }, + handleKeyPress: function(event) { + if (event.keyCode === 13) { + this.submitNext(event); + } + }, + componentWillMount: function() { + document.addEventListener('keyup', this.handleKeyPress, false); + }, + componentWillUnmount: function() { + document.removeEventListener('keyup', this.handleKeyPress, false); + }, + render: function() { + client.track('signup', 'signup_team_01_welcome'); + + var storageError = null; + if (this.state.storageError) { + storageError = ; + } + + var emailError = null; + var emailDivClass = 'form-group'; + if (this.state.emailError) { + emailError = ; + emailDivClass += ' has-error'; + } + + var serverError = null; + if (this.state.serverError) { + serverError = ( +
+ +
+ ); + } + + var differentEmailLinkClass = ''; + var emailDivContainerClass = 'hidden'; + if (this.state.useDiff) { + differentEmailLinkClass = 'hidden'; + emailDivContainerClass = ''; + } + + return ( +
+

+ +

Welcome to:

+

{config.SiteName}

+

+

Let's set up your new team

+

+ Please confirm your email address:
+

+
{this.props.state.team.email}
+
+

+

+ Your account will administer the new team site.
+ You can add other administrators later. +

+
+ + {storageError} +
+
+
+
+
+
+ +
+
+ {emailError} +
+ {serverError} + +
+ Use a different email +
+ ); + } +}); diff --git a/web/react/components/team_url_page.jsx b/web/react/components/team_url_page.jsx deleted file mode 100644 index cefa196ef..000000000 --- a/web/react/components/team_url_page.jsx +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var utils = require('../utils/utils.jsx'); -var client = require('../utils/client.jsx'); -var constants = require('../utils/constants.jsx'); - -module.exports = React.createClass({ - displayName: 'TeamURLPage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'team_display_name'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var name = this.refs.name.getDOMNode().value.trim(); - if (!name) { - this.setState({nameError: 'This field is required'}); - return; - } - - var cleanedName = utils.cleanUpUrlable(name); - - var urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g; - if (cleanedName !== name || !urlRegex.test(name)) { - this.setState({nameError: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."}); - return; - } else if (cleanedName.length <= 3 || cleanedName.length > 15) { - this.setState({nameError: 'Name must be 4 or more characters up to a maximum of 15'}); - return; - } - - for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) { - if (cleanedName.indexOf(constants.RESERVED_TEAM_NAMES[index]) === 0) { - this.setState({nameError: 'This team name is unavailable'}); - return; - } - } - - client.findTeamByName(name, - function success(data) { - if (!data) { - if (config.AllowSignupDomainsWizard) { - this.props.state.wizard = 'allowed_domains'; - } else { - this.props.state.wizard = 'send_invites'; - this.props.state.team.type = 'O'; - } - - this.props.state.team.name = name; - this.props.updateParent(this.props.state); - } else { - this.state.nameError = 'This URL is unavailable. Please try another.'; - this.setState(this.state); - } - }.bind(this), - function error(err) { - this.state.nameError = err.message; - this.setState(this.state); - }.bind(this) - ); - }, - getInitialState: function() { - return {}; - }, - handleFocus: function(e) { - e.preventDefault(); - - e.currentTarget.select(); - }, - render: function() { - $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'}); - - client.track('signup', 'signup_team_03_url'); - - var nameError = null; - var nameDivClass = 'form-group'; - if (this.state.nameError) { - nameError = ; - nameDivClass += ' has-error'; - } - - return ( -
-
- -

{utils.toTitleCase(strings.Team) + ' URL'}

-
-
-
-
- {utils.getWindowLocationOrigin() + '/'} - -
-
-
- {nameError} -
-

{'Choose the web address of your new ' + strings.Team + ':'}

-
    -
  • Short and memorable is best
  • -
  • Use lowercase letters, numbers and dashes
  • -
  • Must start with a letter and can't end in a dash
  • -
- - -
-
- ); - } -}); diff --git a/web/react/components/username_page.jsx b/web/react/components/username_page.jsx deleted file mode 100644 index a83a8f599..000000000 --- a/web/react/components/username_page.jsx +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var utils = require('../utils/utils.jsx'); -var client = require('../utils/client.jsx'); - -module.exports = React.createClass({ - displayName: 'UsernamePage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitBack: function(e) { - e.preventDefault(); - this.props.state.wizard = 'send_invites'; - this.props.updateParent(this.props.state); - }, - submitNext: function(e) { - e.preventDefault(); - - var name = this.refs.name.getDOMNode().value.trim(); - - var usernameError = utils.isValidUsername(name); - if (usernameError === 'Cannot use a reserved word as a username.') { - this.setState({nameError: 'This username is reserved, please choose a new one.'}); - return; - } else if (usernameError) { - this.setState({nameError: 'Username must begin with a letter, and contain 3 to 15 characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''}); - return; - } - - this.props.state.wizard = 'password'; - this.props.state.user.username = name; - this.props.updateParent(this.props.state); - }, - getInitialState: function() { - return {}; - }, - render: function() { - client.track('signup', 'signup_team_06_username'); - - var nameError = null; - var nameDivClass = 'form-group'; - if (this.state.nameError) { - nameError = ; - nameDivClass += ' has-error'; - } - - return ( -
-
- -

Your username

-
{'Select a memorable username that makes it easy for ' + strings.Team + 'mates to identify you:'}
-
-
-
-
-
Choose your username
- -
Usernames must begin with a letter and contain 3 to 15 characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'
-
-
- {nameError} -
-
- - -
-
- ); - } -}); diff --git a/web/react/components/welcome_page.jsx b/web/react/components/welcome_page.jsx deleted file mode 100644 index 1f8576ac7..000000000 --- a/web/react/components/welcome_page.jsx +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -var utils = require('../utils/utils.jsx'); -var client = require('../utils/client.jsx'); -var BrowserStore = require('../stores/browser_store.jsx'); - -module.exports = React.createClass({ - displayName: 'WelcomePage', - propTypes: { - state: React.PropTypes.object, - updateParent: React.PropTypes.func - }, - submitNext: function(e) { - if (!BrowserStore.isLocalStorageSupported()) { - this.setState({storageError: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'}); - return; - } - e.preventDefault(); - this.props.state.wizard = 'team_display_name'; - this.props.updateParent(this.props.state); - }, - handleDiffEmail: function(e) { - e.preventDefault(); - this.setState({useDiff: true}); - }, - handleDiffSubmit: function(e) { - e.preventDefault(); - - var state = {useDiff: true, serverError: ''}; - - var email = this.refs.email.getDOMNode().value.trim().toLowerCase(); - if (!email || !utils.isEmail(email)) { - state.emailError = 'Please enter a valid email address'; - this.setState(state); - return; - } else if (!BrowserStore.isLocalStorageSupported()) { - state.emailError = 'This service requires local storage to be enabled. Please enable it or exit private browsing.'; - this.setState(state); - return; - } - state.emailError = ''; - - client.signupTeam(email, - function success(data) { - if (data.follow_link) { - window.location.href = data.follow_link; - } else { - this.props.state.wizard = 'finished'; - this.props.updateParent(this.props.state); - window.location.href = '/signup_team_confirm/?email=' + encodeURIComponent(email); - } - }.bind(this), - function error(err) { - this.state.serverError = err.message; - this.setState(this.state); - }.bind(this) - ); - }, - getInitialState: function() { - return {useDiff: false}; - }, - handleKeyPress: function(event) { - if (event.keyCode === 13) { - this.submitNext(event); - } - }, - componentWillMount: function() { - document.addEventListener('keyup', this.handleKeyPress, false); - }, - componentWillUnmount: function() { - document.removeEventListener('keyup', this.handleKeyPress, false); - }, - render: function() { - client.track('signup', 'signup_team_01_welcome'); - - var storageError = null; - if (this.state.storageError) { - storageError = ; - } - - var emailError = null; - var emailDivClass = 'form-group'; - if (this.state.emailError) { - emailError = ; - emailDivClass += ' has-error'; - } - - var serverError = null; - if (this.state.serverError) { - serverError = ( -
- -
- ); - } - - var differentEmailLinkClass = ''; - var emailDivContainerClass = 'hidden'; - if (this.state.useDiff) { - differentEmailLinkClass = 'hidden'; - emailDivContainerClass = ''; - } - - return ( -
-

- -

Welcome to:

-

{config.SiteName}

-

-

Let's set up your new team

-

- Please confirm your email address:
-

-
{this.props.state.team.email}
-
-

-

- Your account will administer the new team site.
- You can add other administrators later. -

-
- - {storageError} -
-
-
-
-
-
- -
-
- {emailError} -
- {serverError} - -
- Use a different email -
- ); - } -}); -- cgit v1.2.3-1-g7c22