2016-06-02 16:21:14 -04:00
|
|
|
var React = require('react');
|
|
|
|
|
2016-06-14 17:26:29 -04:00
|
|
|
var api = require('../../lib/api');
|
2016-06-03 12:00:00 -04:00
|
|
|
var countryData = require('../../lib/country-data');
|
2016-06-07 18:04:18 -04:00
|
|
|
var intl = require('../../lib/intl.jsx');
|
2016-06-06 10:10:27 -04:00
|
|
|
var log = require('../../lib/log');
|
|
|
|
var smartyStreets = require('../../lib/smarty-streets');
|
2016-06-03 11:27:59 -04:00
|
|
|
|
2016-07-27 19:32:08 -04:00
|
|
|
var Avatar = require('../../components/avatar/avatar.jsx');
|
2016-06-02 16:21:14 -04:00
|
|
|
var Button = require('../../components/forms/button.jsx');
|
2016-06-15 15:08:56 -04:00
|
|
|
var Card = require('../../components/card/card.jsx');
|
2016-06-23 07:27:43 -04:00
|
|
|
var CharCount = require('../../components/forms/charcount.jsx');
|
2016-06-02 16:21:14 -04:00
|
|
|
var Checkbox = require('../../components/forms/checkbox.jsx');
|
|
|
|
var CheckboxGroup = require('../../components/forms/checkbox-group.jsx');
|
|
|
|
var Form = require('../../components/forms/form.jsx');
|
2016-07-05 12:05:54 -04:00
|
|
|
var GeneralError = require('../../components/forms/general-error.jsx');
|
2016-06-02 16:21:14 -04:00
|
|
|
var Input = require('../../components/forms/input.jsx');
|
|
|
|
var PhoneInput = require('../../components/forms/phone-input.jsx');
|
|
|
|
var RadioGroup = require('../../components/forms/radio-group.jsx');
|
|
|
|
var Select = require('../../components/forms/select.jsx');
|
2016-06-15 15:08:56 -04:00
|
|
|
var Slide = require('../../components/slide/slide.jsx');
|
2016-06-06 10:10:27 -04:00
|
|
|
var Spinner = require('../../components/spinner/spinner.jsx');
|
2016-06-16 10:54:36 -04:00
|
|
|
var StepNavigation = require('../../components/stepnavigation/stepnavigation.jsx');
|
2016-06-02 16:21:14 -04:00
|
|
|
var TextArea = require('../../components/forms/textarea.jsx');
|
2016-06-23 07:27:43 -04:00
|
|
|
var Tooltip = require('../../components/tooltip/tooltip.jsx');
|
2016-06-02 16:21:14 -04:00
|
|
|
|
2016-07-19 16:49:12 -04:00
|
|
|
require('./steps.scss');
|
|
|
|
|
2016-06-03 11:27:59 -04:00
|
|
|
var DEFAULT_COUNTRY = 'us';
|
2016-08-12 15:47:44 -04:00
|
|
|
|
2016-08-12 16:43:56 -04:00
|
|
|
/**
|
|
|
|
* Return a list of options to give to frc select
|
|
|
|
* @param {Object} intl react-intl, used to localize strings
|
|
|
|
* @param {String} defaultCountry optional string of default country to put at top of list
|
|
|
|
* @return {Object} ordered set of county options formatted for frc select
|
|
|
|
*/
|
|
|
|
var getCountryOptions = function (intl, defaultCountry) {
|
2016-07-28 09:56:01 -04:00
|
|
|
var options = countryData.countryOptions.concat({
|
2016-08-12 15:47:44 -04:00
|
|
|
label: intl.formatMessage({id: 'registration.selectCountry'}),
|
2016-07-28 09:56:01 -04:00
|
|
|
disabled: true,
|
|
|
|
selected: true
|
|
|
|
});
|
|
|
|
if (typeof defaultCountry !== 'undefined') {
|
|
|
|
return options.sort(function (a, b) {
|
|
|
|
if (a.disabled) return -1;
|
|
|
|
if (b.disabled) return 1;
|
|
|
|
if (a.value === defaultCountry) return -1;
|
|
|
|
if (b.value === defaultCountry) return 1;
|
|
|
|
return 0;
|
|
|
|
}.bind(this));
|
|
|
|
}
|
|
|
|
return options;
|
|
|
|
};
|
2016-06-02 16:21:14 -04:00
|
|
|
|
2016-06-16 17:25:14 -04:00
|
|
|
var NextStepButton = React.createClass({
|
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
|
|
|
waiting: false,
|
|
|
|
text: 'Next Step'
|
|
|
|
};
|
|
|
|
},
|
|
|
|
render: function () {
|
|
|
|
return (
|
2016-07-19 16:51:28 -04:00
|
|
|
<Button type="submit" disabled={this.props.waiting} className="card-button" {... this.props}>
|
2016-06-16 17:25:14 -04:00
|
|
|
{this.props.waiting ?
|
|
|
|
<Spinner /> :
|
|
|
|
this.props.text
|
|
|
|
}
|
|
|
|
</Button>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-06-02 16:21:14 -04:00
|
|
|
module.exports = {
|
2016-06-07 18:04:18 -04:00
|
|
|
UsernameStep: intl.injectIntl(React.createClass({
|
2016-06-17 12:22:11 -04:00
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
2016-07-21 21:07:02 -04:00
|
|
|
showPassword: false,
|
2016-06-17 12:22:11 -04:00
|
|
|
waiting: false
|
|
|
|
};
|
|
|
|
},
|
2016-06-07 09:57:05 -04:00
|
|
|
getInitialState: function () {
|
2016-06-14 17:26:29 -04:00
|
|
|
return {
|
2016-07-21 21:07:02 -04:00
|
|
|
showPassword: this.props.showPassword,
|
2016-06-23 07:27:43 -04:00
|
|
|
waiting: false,
|
|
|
|
validUsername: ''
|
2016-06-14 17:26:29 -04:00
|
|
|
};
|
2016-06-07 09:57:05 -04:00
|
|
|
},
|
|
|
|
onChangeShowPassword: function (field, value) {
|
|
|
|
this.setState({showPassword: value});
|
|
|
|
},
|
2016-08-03 12:11:55 -04:00
|
|
|
validateUsername: function (username, callback) {
|
2016-08-03 16:24:51 -04:00
|
|
|
callback = callback || function () {};
|
2016-09-01 16:22:45 -04:00
|
|
|
if (!username) {
|
2016-09-01 14:59:02 -04:00
|
|
|
this.refs.form.refs.formsy.updateInputsWithError({
|
2016-12-18 22:08:17 -05:00
|
|
|
'user.username': this.props.intl.formatMessage({id: 'form.validationRequired'})
|
2016-09-01 14:59:02 -04:00
|
|
|
});
|
|
|
|
return callback(false);
|
|
|
|
}
|
|
|
|
|
2016-06-14 17:26:29 -04:00
|
|
|
api({
|
|
|
|
host: '',
|
2016-08-02 15:23:09 -04:00
|
|
|
uri: '/accounts/check_username/' + username + '/'
|
2016-08-03 12:11:55 -04:00
|
|
|
}, function (err, body, res) {
|
2016-06-14 17:26:29 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-08-03 12:11:55 -04:00
|
|
|
if (err || res.statusCode !== 200) {
|
|
|
|
err = err || formatMessage({id: 'general.error'});
|
2016-08-02 15:23:09 -04:00
|
|
|
this.refs.form.refs.formsy.updateInputsWithError({all: err});
|
2016-08-03 16:24:51 -04:00
|
|
|
return callback(false);
|
2016-08-02 15:23:09 -04:00
|
|
|
}
|
2016-08-03 12:11:55 -04:00
|
|
|
body = body[0];
|
|
|
|
|
|
|
|
switch (body.msg) {
|
2016-06-14 17:26:29 -04:00
|
|
|
case 'valid username':
|
2016-06-23 07:27:43 -04:00
|
|
|
this.setState({
|
|
|
|
validUsername: 'pass'
|
|
|
|
});
|
2016-08-03 16:24:51 -04:00
|
|
|
return callback(true);
|
2016-06-14 17:26:29 -04:00
|
|
|
case 'username exists':
|
2016-08-02 15:23:09 -04:00
|
|
|
this.refs.form.refs.formsy.updateInputsWithError({
|
2016-07-19 16:51:28 -04:00
|
|
|
'user.username': formatMessage({id: 'registration.validationUsernameExists'})
|
2016-06-14 17:26:29 -04:00
|
|
|
});
|
2016-08-03 16:24:51 -04:00
|
|
|
return callback(false);
|
2016-06-14 17:26:29 -04:00
|
|
|
case 'bad username':
|
2016-08-02 15:23:09 -04:00
|
|
|
this.refs.form.refs.formsy.updateInputsWithError({
|
2016-07-19 16:51:28 -04:00
|
|
|
'user.username': formatMessage({id: 'registration.validationUsernameVulgar'})
|
2016-06-14 17:26:29 -04:00
|
|
|
});
|
2016-08-03 16:24:51 -04:00
|
|
|
return callback(false);
|
2016-06-14 17:26:29 -04:00
|
|
|
case 'invalid username':
|
|
|
|
default:
|
2016-08-02 15:23:09 -04:00
|
|
|
this.refs.form.refs.formsy.updateInputsWithError({
|
2016-07-19 16:51:28 -04:00
|
|
|
'user.username': formatMessage({id: 'registration.validationUsernameInvalid'})
|
2016-06-14 17:26:29 -04:00
|
|
|
});
|
2016-08-03 16:24:51 -04:00
|
|
|
return callback(false);
|
2016-06-14 17:26:29 -04:00
|
|
|
}
|
|
|
|
}.bind(this));
|
|
|
|
},
|
2016-08-02 15:23:09 -04:00
|
|
|
onUsernameBlur: function (event) {
|
2016-12-28 09:54:46 -05:00
|
|
|
if (this.refs.form.refs.formsy.inputs[0].isValidValue(event.currentTarget.value)) {
|
|
|
|
this.validateUsername(event.currentTarget.value);
|
|
|
|
}
|
2016-08-02 15:23:09 -04:00
|
|
|
},
|
|
|
|
onValidSubmit: function (formData) {
|
|
|
|
this.setState({waiting: true});
|
2016-08-03 12:11:55 -04:00
|
|
|
this.validateUsername(formData.user.username, function (isValid) {
|
|
|
|
this.setState({waiting: false});
|
|
|
|
if (isValid) return this.props.onNextStep(formData);
|
|
|
|
}.bind(this));
|
2016-08-02 15:23:09 -04:00
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
render: function () {
|
2016-06-07 18:04:18 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-06-02 16:21:14 -04:00
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step username-step">
|
2016-07-21 19:38:06 -04:00
|
|
|
<h2>
|
|
|
|
{this.props.title ? (
|
|
|
|
this.props.title
|
|
|
|
) : (
|
|
|
|
<intl.FormattedMessage id="registration.usernameStepTitle" />
|
|
|
|
)}
|
|
|
|
</h2>
|
2016-06-15 15:08:56 -04:00
|
|
|
<p className="description">
|
2016-07-21 19:38:06 -04:00
|
|
|
{this.props.description ? (
|
|
|
|
this.props.description
|
|
|
|
) : (
|
|
|
|
<intl.FormattedMessage id="registration.usernameStepDescription" />
|
|
|
|
)}
|
|
|
|
{this.props.tooltip ? (
|
|
|
|
<Tooltip title={'?'}
|
|
|
|
tipContent={this.props.tooltip} />
|
|
|
|
) : (
|
|
|
|
null
|
|
|
|
)}
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
|
|
|
<Card>
|
2016-08-02 15:23:09 -04:00
|
|
|
<Form onValidSubmit={this.onValidSubmit} ref="form">
|
2016-07-21 20:43:58 -04:00
|
|
|
<div>
|
2016-07-27 23:33:45 -04:00
|
|
|
<div className="username-label">
|
|
|
|
<b>{formatMessage({id: 'registration.createUsername'})}</b>
|
|
|
|
{this.props.usernameHelp ? (
|
|
|
|
<p className="help-text">{this.props.usernameHelp}</p>
|
|
|
|
):(
|
|
|
|
null
|
|
|
|
)}
|
|
|
|
</div>
|
2016-07-21 20:43:58 -04:00
|
|
|
<Input className={this.state.validUsername}
|
|
|
|
type="text"
|
|
|
|
name="user.username"
|
2016-08-02 15:23:09 -04:00
|
|
|
onBlur={this.onUsernameBlur}
|
2016-07-21 20:43:58 -04:00
|
|
|
validations={{
|
|
|
|
matchRegexp: /^[\w-]*$/,
|
|
|
|
minLength: 3,
|
|
|
|
maxLength: 20
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
matchRegexp: formatMessage({
|
|
|
|
id: 'registration.validationUsernameRegexp'
|
|
|
|
}),
|
|
|
|
minLength: formatMessage({
|
|
|
|
id: 'registration.validationUsernameMinLength'
|
|
|
|
}),
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationUsernameMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
required />
|
|
|
|
</div>
|
2016-06-15 15:08:56 -04:00
|
|
|
<Input label={formatMessage({id: 'general.password'})}
|
|
|
|
type={this.state.showPassword ? 'text' : 'password'}
|
|
|
|
name="user.password"
|
|
|
|
validations={{
|
|
|
|
minLength: 6,
|
|
|
|
notEquals: 'password',
|
|
|
|
notEqualsField: 'user.username'
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
minLength: formatMessage({
|
2016-07-19 16:51:28 -04:00
|
|
|
id: 'registration.validationPasswordLength'
|
2016-06-15 15:08:56 -04:00
|
|
|
}),
|
|
|
|
notEquals: formatMessage({
|
2016-07-19 16:51:28 -04:00
|
|
|
id: 'registration.validationPasswordNotEquals'
|
2016-06-15 15:08:56 -04:00
|
|
|
}),
|
|
|
|
notEqualsField: formatMessage({
|
2016-07-19 16:51:28 -04:00
|
|
|
id: 'registration.validationPasswordNotUsername'
|
2016-06-15 15:08:56 -04:00
|
|
|
})
|
|
|
|
}}
|
|
|
|
required />
|
2016-07-19 16:51:28 -04:00
|
|
|
<Checkbox label={formatMessage({id: 'registration.showPassword'})}
|
2016-06-15 15:08:56 -04:00
|
|
|
value={this.state.showPassword}
|
|
|
|
onChange={this.onChangeShowPassword}
|
|
|
|
help={null}
|
|
|
|
name="showPassword" />
|
2016-07-05 12:05:54 -04:00
|
|
|
<GeneralError name="all" />
|
2016-06-17 12:22:11 -04:00
|
|
|
<NextStepButton waiting={this.props.waiting || this.state.waiting}
|
2016-07-19 16:51:28 -04:00
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Form>
|
|
|
|
</Card>
|
2016-06-16 10:54:36 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-07 18:04:18 -04:00
|
|
|
})),
|
2016-07-21 16:56:01 -04:00
|
|
|
ChoosePasswordStep: intl.injectIntl(React.createClass({
|
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
2016-08-12 10:11:14 -04:00
|
|
|
username: null,
|
2016-07-21 21:07:02 -04:00
|
|
|
showPassword: false,
|
2016-07-21 16:56:01 -04:00
|
|
|
waiting: false
|
|
|
|
};
|
|
|
|
},
|
|
|
|
getInitialState: function () {
|
|
|
|
return {
|
2016-07-21 21:07:02 -04:00
|
|
|
showPassword: this.props.showPassword
|
2016-07-21 16:56:01 -04:00
|
|
|
};
|
|
|
|
},
|
|
|
|
onChangeShowPassword: function (field, value) {
|
|
|
|
this.setState({showPassword: value});
|
|
|
|
},
|
|
|
|
render: function () {
|
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
|
|
|
return (
|
|
|
|
<Slide className="registration-step choose-password-step">
|
|
|
|
<h2>{formatMessage({id: 'registration.choosePasswordStepTitle'})}</h2>
|
2016-07-21 17:27:07 -04:00
|
|
|
<p className="description">
|
|
|
|
<intl.FormattedMessage id="registration.choosePasswordStepDescription" />
|
|
|
|
<Tooltip title={'?'}
|
|
|
|
tipContent={formatMessage({id: 'registration.choosePasswordStepTooltip'})} />
|
|
|
|
</p>
|
|
|
|
|
2016-07-21 16:56:01 -04:00
|
|
|
<Card>
|
|
|
|
<Form onValidSubmit={this.props.onNextStep}>
|
2016-07-21 17:27:07 -04:00
|
|
|
<Input label={formatMessage({id: 'registration.newPassword'})}
|
2016-07-21 16:56:01 -04:00
|
|
|
type={this.state.showPassword ? 'text' : 'password'}
|
|
|
|
name="user.password"
|
|
|
|
validations={{
|
|
|
|
minLength: 6,
|
|
|
|
notEquals: 'password',
|
2016-08-12 10:11:14 -04:00
|
|
|
notEqualsUsername: this.props.username
|
2016-07-21 16:56:01 -04:00
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
minLength: formatMessage({
|
|
|
|
id: 'registration.validationPasswordLength'
|
|
|
|
}),
|
|
|
|
notEquals: formatMessage({
|
|
|
|
id: 'registration.validationPasswordNotEquals'
|
|
|
|
}),
|
2016-08-12 08:10:20 -04:00
|
|
|
notEqualsUsername: formatMessage({
|
2016-07-21 16:56:01 -04:00
|
|
|
id: 'registration.validationPasswordNotUsername'
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
required />
|
|
|
|
<Checkbox label={formatMessage({id: 'registration.showPassword'})}
|
|
|
|
value={this.state.showPassword}
|
|
|
|
onChange={this.onChangeShowPassword}
|
|
|
|
help={null}
|
|
|
|
name="showPassword" />
|
|
|
|
<NextStepButton waiting={this.props.waiting || this.state.waiting}
|
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
|
|
|
</Form>
|
|
|
|
</Card>
|
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
|
|
|
</Slide>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
})),
|
2016-06-07 18:04:18 -04:00
|
|
|
DemographicsStep: intl.injectIntl(React.createClass({
|
2016-06-03 11:27:59 -04:00
|
|
|
getDefaultProps: function () {
|
2016-06-17 12:22:11 -04:00
|
|
|
return {
|
2016-07-19 22:58:50 -04:00
|
|
|
waiting: false,
|
2017-04-14 14:18:32 -04:00
|
|
|
description: null,
|
|
|
|
birthOffset: 0
|
2016-06-17 12:22:11 -04:00
|
|
|
};
|
2016-06-03 11:27:59 -04:00
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
getInitialState: function () {
|
|
|
|
return {otherDisabled: true};
|
|
|
|
},
|
2016-06-07 18:04:18 -04:00
|
|
|
getMonthOptions: function () {
|
|
|
|
return [
|
2016-06-02 16:21:14 -04:00
|
|
|
'January', 'February', 'March', 'April', 'May', 'June', 'July',
|
|
|
|
'August', 'September', 'October', 'November', 'December'
|
|
|
|
].map(function (label, id) {
|
2016-06-07 18:04:18 -04:00
|
|
|
return {
|
2016-07-28 09:56:01 -04:00
|
|
|
value: id + 1,
|
2016-06-07 18:04:18 -04:00
|
|
|
label: this.props.intl.formatMessage({id: 'general.month' + label})};
|
|
|
|
}.bind(this));
|
|
|
|
},
|
|
|
|
getYearOptions: function () {
|
|
|
|
return Array.apply(null, Array(100)).map(function (v, id) {
|
2017-04-14 14:18:32 -04:00
|
|
|
var year = new Date().getFullYear() - (id + this.props.birthOffset);
|
2016-06-02 16:21:14 -04:00
|
|
|
return {value: year, label: year};
|
2017-04-14 14:18:32 -04:00
|
|
|
}.bind(this));
|
2016-06-07 18:04:18 -04:00
|
|
|
},
|
|
|
|
onChooseGender: function (name, gender) {
|
|
|
|
this.setState({otherDisabled: gender !== 'other'});
|
|
|
|
},
|
2017-04-12 17:41:43 -04:00
|
|
|
onValidSubmit: function (formData, reset, invalidate) {
|
|
|
|
var birthdate = new Date(
|
|
|
|
formData.user.birth.year,
|
|
|
|
formData.user.birth.month - 1,
|
|
|
|
1
|
|
|
|
);
|
2017-04-20 16:47:35 -04:00
|
|
|
if (((Date.now() - birthdate) / (24*3600*1000*365.25)) < this.props.birthOffset) {
|
2017-04-12 17:41:43 -04:00
|
|
|
return invalidate({
|
2017-04-21 07:51:03 -04:00
|
|
|
'user.birth.year': this.props.intl.formatMessage({id: 'teacherRegistration.validationAge'})
|
2017-04-12 17:41:43 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
return this.props.onNextStep(formData);
|
|
|
|
},
|
2016-06-07 18:04:18 -04:00
|
|
|
render: function () {
|
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-06-02 16:21:14 -04:00
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step demographics-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>
|
2016-07-19 16:51:28 -04:00
|
|
|
<intl.FormattedMessage id="registration.personalStepTitle" />
|
2016-06-23 07:27:43 -04:00
|
|
|
</h2>
|
2016-06-15 15:08:56 -04:00
|
|
|
<p className="description">
|
2016-07-19 22:58:50 -04:00
|
|
|
{this.props.description ?
|
|
|
|
this.props.description
|
|
|
|
:
|
|
|
|
<intl.FormattedMessage id="registration.personalStepDescription" />
|
|
|
|
}
|
2016-06-23 07:27:43 -04:00
|
|
|
<Tooltip title={'?'}
|
2016-07-19 16:51:28 -04:00
|
|
|
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
|
|
|
<Card>
|
2017-04-12 17:41:43 -04:00
|
|
|
<Form onValidSubmit={this.onValidSubmit}>
|
2016-06-15 15:08:56 -04:00
|
|
|
<Select label={formatMessage({id: 'general.birthMonth'})}
|
|
|
|
name="user.birth.month"
|
|
|
|
options={this.getMonthOptions()}
|
|
|
|
required />
|
|
|
|
<Select label={formatMessage({id: 'general.birthYear'})}
|
|
|
|
name="user.birth.year"
|
|
|
|
options={this.getYearOptions()} required />
|
|
|
|
<RadioGroup label={formatMessage({id: 'general.gender'})}
|
|
|
|
name="user.gender"
|
|
|
|
onChange={this.onChooseGender}
|
|
|
|
options={[
|
|
|
|
{value: 'female', label: formatMessage({id: 'general.female'})},
|
|
|
|
{value: 'male', label: formatMessage({id: 'general.male'})},
|
2016-12-05 14:05:27 -05:00
|
|
|
{value: 'other', label: <Input
|
|
|
|
className="demographics-step-input-other"
|
|
|
|
name="user.genderOther"
|
|
|
|
type="text"
|
|
|
|
validations={{
|
|
|
|
maxLength: 25
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
disabled={this.state.otherDisabled}
|
|
|
|
required={!this.state.otherDisabled}
|
|
|
|
help={null}
|
|
|
|
/>}
|
2016-06-15 15:08:56 -04:00
|
|
|
]}
|
|
|
|
required />
|
|
|
|
<Select label={formatMessage({id: 'general.country'})}
|
|
|
|
name="user.country"
|
2016-08-12 16:43:56 -04:00
|
|
|
options={getCountryOptions(this.props.intl, DEFAULT_COUNTRY)}
|
2016-06-02 16:21:14 -04:00
|
|
|
required />
|
2016-06-23 07:27:43 -04:00
|
|
|
<Checkbox className="demographics-checkbox-is-robot"
|
|
|
|
label="I'm a robot!"
|
|
|
|
name="user.isRobot" />
|
2016-06-17 12:22:11 -04:00
|
|
|
<NextStepButton waiting={this.props.waiting}
|
2016-07-19 16:51:28 -04:00
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Form>
|
|
|
|
</Card>
|
2016-06-16 10:54:36 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-07 18:04:18 -04:00
|
|
|
})),
|
|
|
|
NameStep: intl.injectIntl(React.createClass({
|
2016-06-17 12:22:11 -04:00
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
|
|
|
waiting: false
|
|
|
|
};
|
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
render: function () {
|
2016-06-07 18:04:18 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-06-02 16:21:14 -04:00
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step name-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>
|
2016-06-17 12:22:29 -04:00
|
|
|
<intl.FormattedHTMLMessage id="teacherRegistration.nameStepTitle" />
|
2016-06-23 07:27:43 -04:00
|
|
|
</h2>
|
2016-06-15 15:08:56 -04:00
|
|
|
<p className="description">
|
|
|
|
<intl.FormattedMessage id="teacherRegistration.nameStepDescription" />
|
2016-06-23 07:27:43 -04:00
|
|
|
<Tooltip title={'?'}
|
2016-07-19 16:51:28 -04:00
|
|
|
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
|
|
|
<Card>
|
|
|
|
<Form onValidSubmit={this.props.onNextStep}>
|
|
|
|
<Input label={formatMessage({id: 'teacherRegistration.firstName'})}
|
|
|
|
type="text"
|
|
|
|
name="user.name.first"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 50
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-15 15:08:56 -04:00
|
|
|
required />
|
|
|
|
<Input label={formatMessage({id: 'teacherRegistration.lastName'})}
|
|
|
|
type="text"
|
|
|
|
name="user.name.last"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 50
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-15 15:08:56 -04:00
|
|
|
required />
|
2016-06-17 12:22:11 -04:00
|
|
|
<NextStepButton waiting={this.props.waiting}
|
2016-07-19 16:51:28 -04:00
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Form>
|
|
|
|
</Card>
|
2016-06-16 10:54:36 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-07 18:04:18 -04:00
|
|
|
})),
|
|
|
|
PhoneNumberStep: intl.injectIntl(React.createClass({
|
2016-06-03 11:27:59 -04:00
|
|
|
getDefaultProps: function () {
|
2016-06-17 12:22:11 -04:00
|
|
|
return {
|
|
|
|
defaultCountry: DEFAULT_COUNTRY,
|
|
|
|
waiting: false
|
|
|
|
};
|
2016-06-03 11:27:59 -04:00
|
|
|
},
|
2016-07-07 16:56:51 -04:00
|
|
|
onValidSubmit: function (formData, reset, invalidate) {
|
2016-11-04 11:51:48 -04:00
|
|
|
if (!formData.phone || formData.phone.national_number === '+') {
|
2016-10-26 14:15:54 -04:00
|
|
|
return invalidate({
|
2016-12-15 15:32:40 -05:00
|
|
|
'phone': this.props.intl.formatMessage({id: 'form.validationRequired'})
|
2016-10-26 14:15:54 -04:00
|
|
|
});
|
2016-07-07 16:56:51 -04:00
|
|
|
}
|
|
|
|
return this.props.onNextStep(formData);
|
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
render: function () {
|
2016-06-07 18:04:18 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-06-02 16:21:14 -04:00
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step phone-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>
|
2017-01-19 18:23:56 -05:00
|
|
|
<intl.FormattedMessage id="teacherRegistration.phoneNumber" />
|
2016-06-23 07:27:43 -04:00
|
|
|
</h2>
|
2016-06-24 11:19:41 -04:00
|
|
|
<p className="description">
|
2016-06-15 15:08:56 -04:00
|
|
|
<intl.FormattedMessage id="teacherRegistration.phoneStepDescription" />
|
2016-06-23 07:27:43 -04:00
|
|
|
<Tooltip title={'?'}
|
2016-07-19 16:51:28 -04:00
|
|
|
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
|
|
|
<Card>
|
2016-07-07 16:56:51 -04:00
|
|
|
<Form onValidSubmit={this.onValidSubmit}>
|
2016-06-15 15:08:56 -04:00
|
|
|
<PhoneInput label={formatMessage({id: 'teacherRegistration.phoneNumber'})}
|
|
|
|
name="phone"
|
2016-06-21 18:45:41 -04:00
|
|
|
defaultCountry={this.props.defaultCountry}
|
2016-06-15 15:08:56 -04:00
|
|
|
required />
|
|
|
|
<Checkbox label={formatMessage({id: 'teacherRegistration.phoneConsent'})}
|
|
|
|
name="phoneConsent"
|
|
|
|
required="isFalse"
|
|
|
|
validationErrors={{
|
|
|
|
isFalse: formatMessage({id: 'teacherRegistration.validationPhoneConsent'})
|
|
|
|
}} />
|
2016-06-17 12:22:11 -04:00
|
|
|
<NextStepButton waiting={this.props.waiting}
|
2016-07-19 16:51:28 -04:00
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Form>
|
|
|
|
</Card>
|
2016-06-16 10:54:36 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-07 18:04:18 -04:00
|
|
|
})),
|
|
|
|
OrganizationStep: intl.injectIntl(React.createClass({
|
2016-06-02 16:21:14 -04:00
|
|
|
getInitialState: function () {
|
|
|
|
return {
|
|
|
|
otherDisabled: true
|
|
|
|
};
|
|
|
|
},
|
2016-06-17 12:22:11 -04:00
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
|
|
|
waiting: false
|
|
|
|
};
|
|
|
|
},
|
2016-06-17 11:00:48 -04:00
|
|
|
organizationL10nStems: [
|
|
|
|
'orgChoiceElementarySchool',
|
|
|
|
'orgChoiceMiddleSchool',
|
|
|
|
'orgChoiceHighSchool',
|
|
|
|
'orgChoiceUniversity',
|
|
|
|
'orgChoiceAfterschool',
|
|
|
|
'orgChoiceMuseum',
|
|
|
|
'orgChoiceLibrary',
|
2017-02-23 09:52:33 -05:00
|
|
|
'orgChoiceCamp'
|
2016-06-17 11:00:48 -04:00
|
|
|
],
|
2016-06-07 18:04:18 -04:00
|
|
|
getOrganizationOptions: function () {
|
2017-02-23 09:52:33 -05:00
|
|
|
var options = this.organizationL10nStems.map(function (choice, id) {
|
2016-06-07 18:04:18 -04:00
|
|
|
return {
|
2016-06-13 13:29:37 -04:00
|
|
|
value: id,
|
2016-06-07 18:04:18 -04:00
|
|
|
label: this.props.intl.formatMessage({
|
2016-06-13 13:29:37 -04:00
|
|
|
id: 'teacherRegistration.' + choice
|
2016-06-07 18:04:18 -04:00
|
|
|
})
|
|
|
|
};
|
|
|
|
}.bind(this));
|
2017-02-23 09:52:33 -05:00
|
|
|
|
|
|
|
// Add "Other" option with empty string, since input field is used
|
|
|
|
var otherId = options.length;
|
|
|
|
options.push({value: otherId, label: ' '});
|
|
|
|
|
|
|
|
return options;
|
2016-06-07 18:04:18 -04:00
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
onChooseOrganization: function (name, values) {
|
2017-02-23 10:57:27 -05:00
|
|
|
this.setState({otherDisabled: values.indexOf(this.organizationL10nStems.length) === -1});
|
2016-06-02 16:21:14 -04:00
|
|
|
},
|
|
|
|
render: function () {
|
2016-06-13 13:29:37 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-06-02 16:21:14 -04:00
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step organization-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>
|
2017-01-19 18:23:56 -05:00
|
|
|
<intl.FormattedMessage id="teacherRegistration.organization" />
|
2016-06-23 07:27:43 -04:00
|
|
|
</h2>
|
2016-06-24 11:19:41 -04:00
|
|
|
<p className="description">
|
2016-06-15 15:08:56 -04:00
|
|
|
<intl.FormattedMessage id="teacherRegistration.orgStepDescription" />
|
2016-06-23 07:27:43 -04:00
|
|
|
<Tooltip title={'?'}
|
2016-07-19 16:51:28 -04:00
|
|
|
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
|
|
|
<Card>
|
2016-08-03 15:20:58 -04:00
|
|
|
<Form onValidSubmit={this.props.onNextStep}>
|
2016-06-15 15:08:56 -04:00
|
|
|
<Input label={formatMessage({id: 'teacherRegistration.organization'})}
|
|
|
|
type="text"
|
|
|
|
name="organization.name"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 50
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-15 15:08:56 -04:00
|
|
|
required />
|
|
|
|
<Input label={formatMessage({id: 'teacherRegistration.orgTitle'})}
|
|
|
|
type="text"
|
|
|
|
name="organization.title"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 50
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-15 15:08:56 -04:00
|
|
|
required />
|
2016-06-23 07:27:43 -04:00
|
|
|
<div className="organization-type">
|
|
|
|
<b><intl.FormattedMessage id="teacherRegistration.orgType" /></b>
|
2016-07-21 20:43:58 -04:00
|
|
|
<p className="help-text">
|
|
|
|
<intl.FormattedMessage id="teacherRegistration.checkAll" />
|
|
|
|
</p>
|
2016-06-23 07:27:43 -04:00
|
|
|
<CheckboxGroup name="organization.type"
|
|
|
|
value={[]}
|
|
|
|
options={this.getOrganizationOptions()}
|
|
|
|
onChange={this.onChooseOrganization}
|
2016-08-03 12:11:55 -04:00
|
|
|
validations={{
|
2016-08-03 14:34:07 -04:00
|
|
|
minLength: 1
|
2016-08-03 12:11:55 -04:00
|
|
|
}}
|
|
|
|
validationErrors={{
|
2016-08-03 14:34:07 -04:00
|
|
|
minLength: formatMessage({
|
2016-12-15 15:32:40 -05:00
|
|
|
id: 'form.validationRequired'
|
2016-08-03 12:11:55 -04:00
|
|
|
})
|
|
|
|
}}
|
2016-06-23 07:27:43 -04:00
|
|
|
required />
|
|
|
|
</div>
|
|
|
|
<div className="other-input">
|
2016-08-02 15:23:09 -04:00
|
|
|
<Input name="organization.other"
|
|
|
|
type="text"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 50
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-23 07:27:43 -04:00
|
|
|
disabled={this.state.otherDisabled}
|
2016-08-02 15:23:09 -04:00
|
|
|
required={!this.state.otherDisabled}
|
|
|
|
help={null}
|
2016-06-23 07:27:43 -04:00
|
|
|
placeholder={formatMessage({id: 'general.other'})} />
|
|
|
|
</div>
|
|
|
|
<div className="url-input">
|
|
|
|
<b><intl.FormattedMessage id="general.website" /></b>
|
2016-07-21 20:43:58 -04:00
|
|
|
<p className="help-text">
|
|
|
|
<intl.FormattedMessage id="teacherRegistration.notRequired" />
|
|
|
|
</p>
|
2016-06-23 07:27:43 -04:00
|
|
|
<Input type="url"
|
|
|
|
name="organization.url"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 200
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-23 07:27:43 -04:00
|
|
|
required="isFalse"
|
|
|
|
placeholder={'http://'} />
|
|
|
|
</div>
|
2016-06-17 12:22:11 -04:00
|
|
|
<NextStepButton waiting={this.props.waiting}
|
2016-08-02 15:23:09 -04:00
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Form>
|
|
|
|
</Card>
|
2016-06-16 10:54:36 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-07 18:04:18 -04:00
|
|
|
})),
|
2016-06-13 13:29:37 -04:00
|
|
|
AddressStep: intl.injectIntl(React.createClass({
|
2016-06-03 11:27:59 -04:00
|
|
|
getDefaultProps: function () {
|
2016-06-17 12:22:11 -04:00
|
|
|
return {
|
|
|
|
defaultCountry: DEFAULT_COUNTRY,
|
|
|
|
waiting: false
|
|
|
|
};
|
2016-06-03 11:27:59 -04:00
|
|
|
},
|
2016-06-03 12:00:00 -04:00
|
|
|
getInitialState: function () {
|
|
|
|
return {
|
2016-06-21 18:45:41 -04:00
|
|
|
countryChoice: this.props.defaultCountry,
|
2016-06-06 10:10:27 -04:00
|
|
|
waiting: false
|
2016-06-03 12:00:00 -04:00
|
|
|
};
|
|
|
|
},
|
|
|
|
onChangeCountry: function (field, choice) {
|
|
|
|
this.setState({countryChoice: choice});
|
|
|
|
},
|
2016-06-06 10:10:27 -04:00
|
|
|
onValidSubmit: function (formData, reset, invalidate) {
|
|
|
|
if (formData.address.country !== 'us') {
|
|
|
|
return this.props.onNextStep(formData);
|
|
|
|
}
|
|
|
|
this.setState({waiting: true});
|
|
|
|
var address = {
|
|
|
|
street: formData.address.line1,
|
|
|
|
secondary: formData.address.line2 || '',
|
|
|
|
city: formData.address.city,
|
|
|
|
state: formData.address.state,
|
|
|
|
zipcode: formData.address.zip
|
|
|
|
};
|
|
|
|
smartyStreets(address, function (err, res) {
|
|
|
|
this.setState({waiting: false});
|
|
|
|
if (err) {
|
|
|
|
// We don't want to prevent registration because
|
|
|
|
// address validation isn't working. Log it and
|
|
|
|
// move on.
|
|
|
|
log.error(err);
|
|
|
|
return this.props.onNextStep(formData);
|
|
|
|
}
|
|
|
|
if (res && res.length > 0) {
|
|
|
|
return this.props.onNextStep(formData);
|
|
|
|
} else {
|
|
|
|
return invalidate({
|
2016-06-30 15:54:32 -04:00
|
|
|
'all': this.props.intl.formatMessage({id: 'teacherRegistration.addressValidationError'})
|
2016-06-06 10:10:27 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}.bind(this));
|
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
render: function () {
|
2016-06-13 13:29:37 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-06-03 12:00:00 -04:00
|
|
|
var stateOptions = countryData.subdivisionOptions[this.state.countryChoice];
|
2016-06-13 13:29:37 -04:00
|
|
|
stateOptions = [{}].concat(stateOptions);
|
2016-06-02 16:21:14 -04:00
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step address-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>
|
2016-06-15 15:08:56 -04:00
|
|
|
<intl.FormattedMessage id="teacherRegistration.addressStepTitle" />
|
2016-06-23 07:27:43 -04:00
|
|
|
</h2>
|
2016-06-15 15:08:56 -04:00
|
|
|
<p className="description">
|
|
|
|
<intl.FormattedMessage id="teacherRegistration.addressStepDescription" />
|
2016-06-23 07:27:43 -04:00
|
|
|
<Tooltip title={'?'}
|
2016-07-19 16:51:28 -04:00
|
|
|
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
|
|
|
<Card>
|
|
|
|
<Form onValidSubmit={this.onValidSubmit}>
|
|
|
|
<Select label={formatMessage({id: 'general.country'})}
|
|
|
|
name="address.country"
|
2016-08-12 15:47:44 -04:00
|
|
|
options={getCountryOptions(this.props.intl)}
|
2016-07-28 09:56:01 -04:00
|
|
|
value={this.props.defaultCountry}
|
2016-06-15 15:08:56 -04:00
|
|
|
onChange={this.onChangeCountry}
|
|
|
|
required />
|
|
|
|
<Input label={formatMessage({id: 'teacherRegistration.addressLine1'})}
|
|
|
|
type="text"
|
|
|
|
name="address.line1"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 100
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-15 15:08:56 -04:00
|
|
|
required />
|
|
|
|
<Input label={formatMessage({id: 'teacherRegistration.addressLine2'})}
|
|
|
|
type="text"
|
2016-06-23 07:27:43 -04:00
|
|
|
name="address.line2"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 100
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-23 07:27:43 -04:00
|
|
|
required="isFalse" />
|
2016-06-15 15:08:56 -04:00
|
|
|
<Input label={formatMessage({id: 'teacherRegistration.city'})}
|
|
|
|
type="text"
|
|
|
|
name="address.city"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 50
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-15 15:08:56 -04:00
|
|
|
required />
|
|
|
|
{stateOptions.length > 2 ?
|
|
|
|
<Select label={formatMessage({id: 'teacherRegistration.stateProvince'})}
|
|
|
|
name="address.state"
|
|
|
|
options={stateOptions}
|
|
|
|
required /> :
|
|
|
|
[]
|
2016-06-06 10:10:27 -04:00
|
|
|
}
|
2016-09-30 15:01:20 -04:00
|
|
|
<b className="row-label">
|
|
|
|
<intl.FormattedMessage id="teacherRegistration.zipCode" />
|
|
|
|
</b>
|
|
|
|
{this.state.countryChoice !== 'us' ?
|
|
|
|
<p className="help-text">
|
|
|
|
<intl.FormattedMessage id="teacherRegistration.notRequired" />
|
|
|
|
</p> : []
|
|
|
|
}
|
|
|
|
<Input type="text"
|
2016-06-15 15:08:56 -04:00
|
|
|
name="address.zip"
|
2016-08-12 15:39:39 -04:00
|
|
|
validations={{
|
|
|
|
maxLength: 10
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'registration.validationMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-09-30 15:01:20 -04:00
|
|
|
required={(this.state.countryChoice === 'us') ? true : 'isFalse'} />
|
2016-07-05 12:05:54 -04:00
|
|
|
<GeneralError name="all" />
|
2016-06-17 12:22:11 -04:00
|
|
|
<NextStepButton waiting={this.props.waiting || this.state.waiting}
|
2016-07-19 16:51:28 -04:00
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Form>
|
|
|
|
</Card>
|
2016-06-16 10:54:36 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-13 13:29:37 -04:00
|
|
|
})),
|
|
|
|
UseScratchStep: intl.injectIntl(React.createClass({
|
2016-06-17 12:22:11 -04:00
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
2016-06-23 07:27:43 -04:00
|
|
|
waiting: false,
|
|
|
|
maxCharacters: 300
|
2016-06-17 12:22:11 -04:00
|
|
|
};
|
|
|
|
},
|
2016-06-23 07:27:43 -04:00
|
|
|
getInitialState: function () {
|
|
|
|
return {
|
|
|
|
characterCount: 0
|
|
|
|
};
|
|
|
|
},
|
|
|
|
handleTyping: function (name, value) {
|
|
|
|
this.setState({
|
|
|
|
characterCount: value.length
|
|
|
|
});
|
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
render: function () {
|
2016-06-13 13:29:37 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-06-23 14:06:36 -04:00
|
|
|
var textAreaClass = (this.state.characterCount > this.props.maxCharacters) ? 'fail' : '';
|
2017-01-19 18:23:56 -05:00
|
|
|
|
2016-06-02 16:21:14 -04:00
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step usescratch-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>
|
2016-06-15 15:08:56 -04:00
|
|
|
<intl.FormattedMessage id="teacherRegistration.useScratchStepTitle" />
|
2016-06-23 07:27:43 -04:00
|
|
|
</h2>
|
2016-06-15 15:08:56 -04:00
|
|
|
<p className="description">
|
|
|
|
<intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" />
|
2016-06-23 07:27:43 -04:00
|
|
|
<Tooltip title={'?'}
|
2016-07-19 16:51:28 -04:00
|
|
|
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
|
|
|
<Card>
|
|
|
|
<Form onValidSubmit={this.props.onNextStep}>
|
|
|
|
<TextArea label={formatMessage({id: 'teacherRegistration.howUseScratch'})}
|
|
|
|
name="useScratch"
|
2016-06-23 14:06:36 -04:00
|
|
|
className={textAreaClass}
|
2016-06-23 07:27:43 -04:00
|
|
|
onChange={this.handleTyping}
|
|
|
|
validations={{
|
|
|
|
maxLength: this.props.maxCharacters
|
|
|
|
}}
|
|
|
|
validationErrors={{
|
|
|
|
maxLength: formatMessage({
|
|
|
|
id: 'teacherRegistration.useScratchMaxLength'
|
|
|
|
})
|
|
|
|
}}
|
2016-06-15 15:08:56 -04:00
|
|
|
required />
|
2016-06-23 07:27:43 -04:00
|
|
|
<CharCount maxCharacters={this.props.maxCharacters}
|
|
|
|
currentCharacters={this.state.characterCount} />
|
2016-06-17 12:22:11 -04:00
|
|
|
<NextStepButton waiting={this.props.waiting}
|
2016-07-19 16:51:28 -04:00
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Form>
|
|
|
|
</Card>
|
2016-06-16 10:54:36 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-13 13:29:37 -04:00
|
|
|
})),
|
|
|
|
EmailStep: intl.injectIntl(React.createClass({
|
2016-06-17 12:22:11 -04:00
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
|
|
|
waiting: false
|
|
|
|
};
|
2016-06-16 17:25:14 -04:00
|
|
|
},
|
2016-06-17 13:01:53 -04:00
|
|
|
getInitialState: function () {
|
|
|
|
return {
|
|
|
|
waiting: false
|
|
|
|
};
|
|
|
|
},
|
|
|
|
onValidSubmit: function (formData, reset, invalidate) {
|
|
|
|
this.setState({waiting: true});
|
|
|
|
api({
|
|
|
|
host: '',
|
|
|
|
uri: '/accounts/check_email/',
|
|
|
|
params: {email: formData.user.email}
|
|
|
|
}, function (err, res) {
|
|
|
|
this.setState({waiting: false});
|
|
|
|
if (err) return invalidate({all: err});
|
|
|
|
res = res[0];
|
|
|
|
switch (res.msg) {
|
|
|
|
case 'valid email':
|
|
|
|
return this.props.onNextStep(formData);
|
|
|
|
default:
|
|
|
|
return invalidate({'user.email': res.msg});
|
|
|
|
}
|
|
|
|
}.bind(this));
|
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
render: function () {
|
2016-06-13 13:29:37 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-06-02 16:21:14 -04:00
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step email-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>
|
2016-06-15 15:08:56 -04:00
|
|
|
<intl.FormattedMessage id="teacherRegistration.emailStepTitle" />
|
2016-06-23 07:27:43 -04:00
|
|
|
</h2>
|
2016-06-17 12:22:29 -04:00
|
|
|
<p className="description">
|
2016-06-15 15:08:56 -04:00
|
|
|
<intl.FormattedMessage id="teacherRegistration.emailStepDescription" />
|
2016-06-23 07:27:43 -04:00
|
|
|
<Tooltip title={'?'}
|
2016-07-19 16:51:28 -04:00
|
|
|
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
|
|
|
<Card>
|
2016-06-17 13:01:53 -04:00
|
|
|
<Form onValidSubmit={this.onValidSubmit}>
|
2017-01-19 18:37:11 -05:00
|
|
|
<Input
|
|
|
|
label={formatMessage({id: 'general.emailAddress'})}
|
|
|
|
type="text"
|
|
|
|
name="user.email"
|
|
|
|
validations="isEmail"
|
|
|
|
validationError={formatMessage({id: 'general.validationEmail'})}
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
<Input
|
|
|
|
label={formatMessage({id: 'general.confirmEmail'})}
|
|
|
|
type="text"
|
|
|
|
name="confirmEmail"
|
|
|
|
validations="equalsField:user.email"
|
|
|
|
validationErrors={{
|
|
|
|
equalsField: formatMessage({id: 'general.validationEmailMatch'})
|
|
|
|
}}
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label={formatMessage({id: 'registration.optIn'})}
|
|
|
|
value={true}
|
|
|
|
help={null}
|
|
|
|
name="subscribe"
|
|
|
|
/>
|
2016-07-05 12:05:54 -04:00
|
|
|
<GeneralError name="all" />
|
2017-01-19 18:37:11 -05:00
|
|
|
<NextStepButton
|
|
|
|
waiting={this.props.waiting}
|
|
|
|
text={<intl.FormattedMessage id="registration.nextStep" />}
|
|
|
|
/>
|
2016-06-15 15:08:56 -04:00
|
|
|
</Form>
|
|
|
|
</Card>
|
2016-06-16 10:54:36 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
2016-06-15 15:08:56 -04:00
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-13 13:29:37 -04:00
|
|
|
})),
|
2016-06-22 13:35:38 -04:00
|
|
|
TeacherApprovalStep: intl.injectIntl(React.createClass({
|
2016-06-21 18:45:41 -04:00
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
2016-06-22 13:35:38 -04:00
|
|
|
email: null,
|
2016-07-26 15:24:51 -04:00
|
|
|
invited: false,
|
2016-07-26 17:03:41 -04:00
|
|
|
confirmed: false
|
2016-06-21 18:45:41 -04:00
|
|
|
};
|
|
|
|
},
|
2016-06-02 16:21:14 -04:00
|
|
|
render: function () {
|
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step last-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>
|
2016-06-22 13:35:38 -04:00
|
|
|
<intl.FormattedMessage id="registration.lastStepTitle" />
|
2016-06-23 07:27:43 -04:00
|
|
|
</h2>
|
2016-06-17 12:22:29 -04:00
|
|
|
<p className="description">
|
2016-06-22 13:35:38 -04:00
|
|
|
<intl.FormattedMessage id="registration.lastStepDescription" />
|
2016-06-15 15:08:56 -04:00
|
|
|
</p>
|
2016-06-22 13:35:38 -04:00
|
|
|
{this.props.confirmed || !this.props.email ?
|
|
|
|
[]
|
|
|
|
:
|
|
|
|
(<Card className="confirm">
|
2016-06-23 07:35:06 -04:00
|
|
|
<h4><intl.FormattedMessage id="registration.confirmYourEmail" /></h4>
|
2016-06-22 13:35:38 -04:00
|
|
|
<p>
|
|
|
|
<intl.FormattedMessage id="registration.confirmYourEmailDescription" /><br />
|
|
|
|
<strong>{this.props.email}</strong>
|
|
|
|
</p>
|
|
|
|
</Card>)
|
|
|
|
}
|
|
|
|
{this.props.invited ?
|
|
|
|
<Card className="wait">
|
2016-06-23 07:35:06 -04:00
|
|
|
<h4><intl.FormattedMessage id="registration.waitForApproval" /></h4>
|
2016-06-22 13:35:38 -04:00
|
|
|
<p>
|
|
|
|
<intl.FormattedMessage id="registration.waitForApprovalDescription" />
|
|
|
|
</p>
|
|
|
|
</Card>
|
|
|
|
:
|
|
|
|
[]
|
|
|
|
}
|
2016-06-15 15:08:56 -04:00
|
|
|
<Card className="resources">
|
2016-06-23 07:35:06 -04:00
|
|
|
<h4><intl.FormattedMessage id="registration.checkOutResources" /></h4>
|
2016-06-02 16:21:14 -04:00
|
|
|
<p>
|
2016-06-22 13:35:38 -04:00
|
|
|
<intl.FormattedHTMLMessage id="registration.checkOutResourcesDescription" />
|
2016-06-02 16:21:14 -04:00
|
|
|
</p>
|
2016-06-15 15:08:56 -04:00
|
|
|
</Card>
|
|
|
|
</Slide>
|
2016-06-02 16:21:14 -04:00
|
|
|
);
|
|
|
|
}
|
2016-06-17 12:22:11 -04:00
|
|
|
})),
|
2016-07-27 19:32:08 -04:00
|
|
|
ClassInviteNewStudentStep: intl.injectIntl(React.createClass({
|
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
|
|
|
waiting: false
|
|
|
|
};
|
|
|
|
},
|
|
|
|
onNextStep: function () {
|
|
|
|
this.props.onNextStep();
|
|
|
|
},
|
|
|
|
render: function () {
|
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
|
|
|
return (
|
|
|
|
<Slide className="registration-step class-invite-step">
|
|
|
|
{this.props.waiting ? [
|
|
|
|
<Spinner />
|
|
|
|
] : [
|
|
|
|
<Avatar className="invite-avatar"
|
|
|
|
src={this.props.classroom.educator.profile.images['50x50']} />,
|
|
|
|
<h2>{this.props.classroom.educator.username}</h2>,
|
|
|
|
<p className="description">
|
|
|
|
{formatMessage({id: 'registration.classroomInviteNewStudentStepDescription'})}
|
|
|
|
</p>,
|
|
|
|
<Card>
|
|
|
|
<div className="contents">
|
|
|
|
<h3>{this.props.classroom.title}</h3>
|
|
|
|
<img className="class-image" src={this.props.classroom.images['250x150']} />
|
|
|
|
</div>
|
|
|
|
<NextStepButton onClick={this.onNextStep}
|
|
|
|
waiting={this.props.waiting}
|
|
|
|
text={formatMessage({id: 'general.getStarted'})} />
|
|
|
|
</Card>,
|
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
|
|
|
]}
|
|
|
|
</Slide>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
})),
|
|
|
|
ClassInviteExistingStudentStep: intl.injectIntl(React.createClass({
|
2016-07-19 16:51:28 -04:00
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
2016-07-27 10:24:37 -04:00
|
|
|
classroom: null,
|
|
|
|
onHandleLogOut: function () {},
|
|
|
|
studentUsername: null,
|
2016-07-19 16:51:28 -04:00
|
|
|
waiting: false
|
|
|
|
};
|
|
|
|
},
|
|
|
|
onNextStep: function () {
|
|
|
|
this.props.onNextStep();
|
|
|
|
},
|
|
|
|
render: function () {
|
2016-07-21 16:54:57 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-07-19 16:51:28 -04:00
|
|
|
return (
|
|
|
|
<Slide className="registration-step class-invite-step">
|
2016-07-21 18:11:25 -04:00
|
|
|
{this.props.waiting ? [
|
|
|
|
<Spinner />
|
|
|
|
] : [
|
2016-07-27 10:24:37 -04:00
|
|
|
<h2>{this.props.studentUsername}</h2>,
|
2016-07-21 18:11:25 -04:00
|
|
|
<p className="description">
|
2016-07-27 19:32:08 -04:00
|
|
|
{formatMessage({id: 'registration.classroomInviteExistingStudentStepDescription'})}
|
2016-07-21 18:11:25 -04:00
|
|
|
</p>,
|
|
|
|
<Card>
|
|
|
|
<div className="contents">
|
|
|
|
<h3>{this.props.classroom.title}</h3>
|
|
|
|
<img className="class-image" src={this.props.classroom.images['250x150']} />
|
2016-07-27 10:24:37 -04:00
|
|
|
<p>{formatMessage({id: 'registration.invitedBy'})}</p>
|
|
|
|
<p><strong>{this.props.classroom.educator.username}</strong></p>
|
2016-07-21 18:11:25 -04:00
|
|
|
</div>
|
|
|
|
<NextStepButton onClick={this.onNextStep}
|
|
|
|
waiting={this.props.waiting}
|
2016-07-21 16:54:57 -04:00
|
|
|
text={formatMessage({id: 'general.getStarted'})} />
|
2016-07-21 18:11:25 -04:00
|
|
|
</Card>,
|
2016-07-27 10:24:37 -04:00
|
|
|
<p><a onClick={this.props.onHandleLogOut}>{formatMessage({id: 'registration.notYou'})}</a></p>,
|
2016-07-21 18:11:25 -04:00
|
|
|
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
|
|
|
]}
|
2016-07-19 16:51:28 -04:00
|
|
|
</Slide>
|
|
|
|
);
|
|
|
|
}
|
2016-07-21 16:54:57 -04:00
|
|
|
})),
|
|
|
|
ClassWelcomeStep: intl.injectIntl(React.createClass({
|
2016-07-19 16:51:28 -04:00
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
2016-07-21 18:11:25 -04:00
|
|
|
waiting: false
|
2016-07-19 16:51:28 -04:00
|
|
|
};
|
|
|
|
},
|
|
|
|
onNextStep: function () {
|
|
|
|
this.props.onNextStep();
|
|
|
|
},
|
|
|
|
render: function () {
|
2016-07-21 16:54:57 -04:00
|
|
|
var formatMessage = this.props.intl.formatMessage;
|
2016-07-19 16:51:28 -04:00
|
|
|
return (
|
|
|
|
<Slide className="registration-step class-welcome-step">
|
2016-07-21 18:11:25 -04:00
|
|
|
{this.props.waiting ? [
|
|
|
|
<Spinner />
|
|
|
|
] : [
|
2016-07-21 16:54:57 -04:00
|
|
|
<h2>{formatMessage({id: 'registration.welcomeStepTitle'})}</h2>,
|
|
|
|
<p className="description">{formatMessage({id: 'registration.welcomeStepDescription'})}</p>,
|
2016-07-21 18:11:25 -04:00
|
|
|
<Card>
|
|
|
|
{this.props.classroom ? (
|
|
|
|
<div className="contents">
|
|
|
|
<h3>{this.props.classroom.title}</h3>
|
|
|
|
<img className="class-image" src={this.props.classroom.images['250x150']} />
|
2016-07-21 16:54:57 -04:00
|
|
|
<p>{formatMessage({id: 'registration.welcomeStepPrompt'})}</p>
|
2016-07-21 18:11:25 -04:00
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
null
|
|
|
|
)}
|
|
|
|
<NextStepButton onClick={this.onNextStep}
|
|
|
|
waiting={this.props.waiting}
|
2016-07-21 16:54:57 -04:00
|
|
|
text={formatMessage({id: 'registration.goToClass'})} />
|
2016-07-21 18:11:25 -04:00
|
|
|
</Card>
|
|
|
|
]}
|
2016-07-19 16:51:28 -04:00
|
|
|
</Slide>
|
|
|
|
);
|
|
|
|
}
|
2016-07-21 16:54:57 -04:00
|
|
|
})),
|
2016-06-17 12:22:11 -04:00
|
|
|
RegistrationError: intl.injectIntl(React.createClass({
|
|
|
|
render: function () {
|
|
|
|
return (
|
2016-07-19 16:49:12 -04:00
|
|
|
<Slide className="registration-step error-step">
|
2016-06-23 07:27:43 -04:00
|
|
|
<h2>Something went wrong</h2>
|
2016-06-17 12:22:11 -04:00
|
|
|
<Card>
|
2016-06-24 11:19:41 -04:00
|
|
|
<h4>There was an error while processing your registration</h4>
|
2016-06-17 12:22:11 -04:00
|
|
|
<p>
|
2016-07-22 09:38:37 -04:00
|
|
|
{this.props.children}
|
2016-06-17 12:22:11 -04:00
|
|
|
</p>
|
|
|
|
</Card>
|
|
|
|
</Slide>
|
|
|
|
);
|
|
|
|
}
|
2016-06-13 13:29:37 -04:00
|
|
|
}))
|
2016-06-02 16:21:14 -04:00
|
|
|
};
|