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 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}.

+
+ Back to previous step +
+
+
+ ); + } +}); 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} +
Add Invitation
+
+ ); + + 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} +
+ Back to previous step +
+
+ ); + } +}); 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