mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-17 00:21:20 -05:00
Merge pull request #730 from rschamp/feature/3679-student-flow
Add student registration flow
This commit is contained in:
commit
d3f1046a42
16 changed files with 798 additions and 527 deletions
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"curly": [2, "multi-line"],
|
||||
"eol-last": [2],
|
||||
|
@ -14,16 +15,12 @@
|
|||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"globals": {
|
||||
"formatMessage": true
|
||||
},
|
||||
"ecmaFeatures": {
|
||||
"arrowFunctions": true,
|
||||
"blockBindings": true,
|
||||
"jsx": true
|
||||
},
|
||||
"plugins": [
|
||||
"react",
|
||||
"json"
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"async": "1.5.2",
|
||||
"autoprefixer": "6.3.6",
|
||||
"babel-core": "6.10.4",
|
||||
"babel-eslint": "5.0.4",
|
||||
"babel-loader": "6.2.4",
|
||||
"babel-preset-es2015": "6.9.0",
|
||||
"babel-preset-react": "6.11.1",
|
||||
|
|
|
@ -5,4 +5,111 @@
|
|||
border-radius: 8px / $em;
|
||||
box-shadow: 0 0 0 .125rem $active-gray;
|
||||
background-color: $ui-white;
|
||||
|
||||
.card-button {
|
||||
display: block;
|
||||
border-radius: .5rem;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
box-shadow: none;
|
||||
background-color: $ui-aqua;
|
||||
width: 23.75rem;
|
||||
height: 4rem;
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
padding: 3rem 4rem;
|
||||
|
||||
.card-button {
|
||||
margin: 0 0 -3rem -4rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
&.has-error {
|
||||
.input {
|
||||
border: 1px solid $ui-orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.help-block {
|
||||
$arrow-border-width: 1rem;
|
||||
display: block;
|
||||
position: absolute;
|
||||
margin-left: $arrow-border-width;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
background-color: $ui-orange;
|
||||
padding: 1rem;
|
||||
max-width: 18.75rem;
|
||||
min-height: 1rem;
|
||||
max-height: 3rem;
|
||||
overflow: visible;
|
||||
color: $type-white;
|
||||
|
||||
&:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: -$arrow-border-width / 2;
|
||||
|
||||
transform: rotate(45deg);
|
||||
|
||||
border-bottom: 1px solid $active-gray;
|
||||
border-left: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
|
||||
background-color: $ui-orange;
|
||||
width: $arrow-border-width;
|
||||
height: $arrow-border-width;
|
||||
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $mobile - 1) {
|
||||
.card {
|
||||
width: 22.5rem;
|
||||
|
||||
.form {
|
||||
text-align: left;
|
||||
|
||||
.button {
|
||||
width: 22.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $tablet - 1) {
|
||||
.card {
|
||||
.input {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $desktop - 1) {
|
||||
.card {
|
||||
.help-block {
|
||||
position: relative;
|
||||
transform: none;
|
||||
margin: inherit;
|
||||
width: 100%;
|
||||
height: inherit;
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ var Deck = React.createClass({
|
|||
<div className={classNames(['deck', this.props.className])}>
|
||||
<div className="inner">
|
||||
<a href="/" aria-label="Scratch">
|
||||
<img src="/images/logo_sm.png" />
|
||||
<img className="logo" src="/images/logo_sm.png" />
|
||||
</a>
|
||||
{this.props.children}
|
||||
</div>
|
||||
|
|
|
@ -6,146 +6,22 @@
|
|||
.deck {
|
||||
min-height: 100vh;
|
||||
|
||||
img {
|
||||
.logo {
|
||||
margin-left: 2px;
|
||||
padding: 12px 0;
|
||||
width: 76px;
|
||||
}
|
||||
|
||||
.step-navigation {
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.slide {
|
||||
max-width: 28.75rem;
|
||||
|
||||
h2,
|
||||
.description {
|
||||
text-align: center;
|
||||
color: $type-white;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 0 auto;
|
||||
width: 23.75rem;
|
||||
}
|
||||
|
||||
.form {
|
||||
padding: 3rem 4rem;
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
&.has-error {
|
||||
.input {
|
||||
border: 1px solid $ui-orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
margin: 0 0 -3rem -4rem;
|
||||
border-radius: .5rem;
|
||||
box-shadow: none;
|
||||
width: 23.75rem;
|
||||
height: 4rem;
|
||||
|
||||
&.card-button {
|
||||
display: block;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
background-color: $ui-aqua;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
width: 23.75rem;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: $cols5;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
$arrow-border-width: 1rem;
|
||||
display: block;
|
||||
position: absolute;
|
||||
margin-left: $arrow-border-width;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
background-color: $ui-orange;
|
||||
padding: 1rem;
|
||||
max-width: 18.75rem;
|
||||
min-height: 1rem;
|
||||
max-height: 3rem;
|
||||
overflow: visible;
|
||||
color: $type-white;
|
||||
|
||||
&:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: -$arrow-border-width / 2;
|
||||
|
||||
transform: rotate(45deg);
|
||||
|
||||
border-bottom: 1px solid $active-gray;
|
||||
border-left: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
|
||||
background-color: $ui-orange;
|
||||
width: $arrow-border-width;
|
||||
height: $arrow-border-width;
|
||||
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $mobile - 1) {
|
||||
.deck {
|
||||
.card {
|
||||
width: 22.5rem;
|
||||
}
|
||||
|
||||
.form {
|
||||
text-align: left;
|
||||
|
||||
.button {
|
||||
width: 22.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $tablet - 1) {
|
||||
.deck {
|
||||
.input {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $desktop - 1) {
|
||||
.deck {
|
||||
.help-block {
|
||||
position: relative;
|
||||
transform: none;
|
||||
margin: inherit;
|
||||
width: 100%;
|
||||
height: inherit;
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ var intl = require('../../lib/intl.jsx');
|
|||
var log = require('../../lib/log');
|
||||
var smartyStreets = require('../../lib/smarty-streets');
|
||||
|
||||
var Avatar = require('../../components/avatar/avatar.jsx');
|
||||
var Button = require('../../components/forms/button.jsx');
|
||||
var Card = require('../../components/card/card.jsx');
|
||||
var CharCount = require('../../components/forms/charcount.jsx');
|
||||
|
@ -23,6 +24,8 @@ var StepNavigation = require('../../components/stepnavigation/stepnavigation.jsx
|
|||
var TextArea = require('../../components/forms/textarea.jsx');
|
||||
var Tooltip = require('../../components/tooltip/tooltip.jsx');
|
||||
|
||||
require('./steps.scss');
|
||||
|
||||
var DEFAULT_COUNTRY = 'us';
|
||||
|
||||
var NextStepButton = React.createClass({
|
||||
|
@ -34,7 +37,7 @@ var NextStepButton = React.createClass({
|
|||
},
|
||||
render: function () {
|
||||
return (
|
||||
<Button type="submit" disabled={this.props.waiting} className="card-button">
|
||||
<Button type="submit" disabled={this.props.waiting} className="card-button" {... this.props}>
|
||||
{this.props.waiting ?
|
||||
<Spinner /> :
|
||||
this.props.text
|
||||
|
@ -79,16 +82,16 @@ module.exports = {
|
|||
return this.props.onNextStep(formData);
|
||||
case 'username exists':
|
||||
return invalidate({
|
||||
'user.username': formatMessage({id: 'general.validationUsernameExists'})
|
||||
'user.username': formatMessage({id: 'registration.validationUsernameExists'})
|
||||
});
|
||||
case 'bad username':
|
||||
return invalidate({
|
||||
'user.username': formatMessage({id: 'general.validationUsernameVulgar'})
|
||||
'user.username': formatMessage({id: 'registration.validationUsernameVulgar'})
|
||||
});
|
||||
case 'invalid username':
|
||||
default:
|
||||
return invalidate({
|
||||
'user.username': formatMessage({id: 'general.validationUsernameInvalid'})
|
||||
'user.username': formatMessage({id: 'registration.validationUsernameInvalid'})
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
|
@ -96,14 +99,14 @@ module.exports = {
|
|||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="username-step">
|
||||
<h2><intl.FormattedMessage id="teacherRegistration.usernameStepTitle" /></h2>
|
||||
<Slide className="registration-step username-step">
|
||||
<h2><intl.FormattedMessage id="registration.usernameStepTitle" /></h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.usernameStepDescription" />
|
||||
<intl.FormattedMessage id="registration.usernameStepDescription" />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.onValidSubmit}>
|
||||
<Input label={formatMessage({id: 'general.createUsername'})}
|
||||
<Input label={formatMessage({id: 'registration.createUsername'})}
|
||||
className={this.state.validUsername}
|
||||
type="text"
|
||||
name="user.username"
|
||||
|
@ -114,13 +117,13 @@ module.exports = {
|
|||
}}
|
||||
validationErrors={{
|
||||
matchRegexp: formatMessage({
|
||||
id: 'teacherRegistration.validationUsernameRegexp'
|
||||
id: 'registration.validationUsernameRegexp'
|
||||
}),
|
||||
minLength: formatMessage({
|
||||
id: 'teacherRegistration.validationUsernameMinLength'
|
||||
id: 'registration.validationUsernameMinLength'
|
||||
}),
|
||||
maxLength: formatMessage({
|
||||
id: 'teacherRegistration.validationUsernameMaxLength'
|
||||
id: 'registration.validationUsernameMaxLength'
|
||||
})
|
||||
}}
|
||||
required />
|
||||
|
@ -134,24 +137,24 @@ module.exports = {
|
|||
}}
|
||||
validationErrors={{
|
||||
minLength: formatMessage({
|
||||
id: 'teacherRegistration.validationPasswordLength'
|
||||
id: 'registration.validationPasswordLength'
|
||||
}),
|
||||
notEquals: formatMessage({
|
||||
id: 'teacherRegistration.validationPasswordNotEquals'
|
||||
id: 'registration.validationPasswordNotEquals'
|
||||
}),
|
||||
notEqualsField: formatMessage({
|
||||
id: 'teacherRegistration.validationPasswordNotUsername'
|
||||
id: 'registration.validationPasswordNotUsername'
|
||||
})
|
||||
}}
|
||||
required />
|
||||
<Checkbox label={formatMessage({id: 'teacherRegistration.showPassword'})}
|
||||
<Checkbox label={formatMessage({id: 'registration.showPassword'})}
|
||||
value={this.state.showPassword}
|
||||
onChange={this.onChangeShowPassword}
|
||||
help={null}
|
||||
name="showPassword" />
|
||||
<GeneralError name="all" />
|
||||
<NextStepButton waiting={this.props.waiting || this.state.waiting}
|
||||
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} />
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -163,7 +166,8 @@ module.exports = {
|
|||
getDefaultProps: function () {
|
||||
return {
|
||||
defaultCountry: DEFAULT_COUNTRY,
|
||||
waiting: false
|
||||
waiting: false,
|
||||
description: null
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
|
@ -191,14 +195,18 @@ module.exports = {
|
|||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="demographics-step">
|
||||
<Slide className="registration-step demographics-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.personalStepTitle" />
|
||||
<intl.FormattedMessage id="registration.personalStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.personalStepDescription" />
|
||||
{this.props.description ?
|
||||
this.props.description
|
||||
:
|
||||
<intl.FormattedMessage id="registration.personalStepDescription" />
|
||||
}
|
||||
<Tooltip title={'?'}
|
||||
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} />
|
||||
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.props.onNextStep}>
|
||||
|
@ -234,7 +242,7 @@ module.exports = {
|
|||
label="I'm a robot!"
|
||||
name="user.isRobot" />
|
||||
<NextStepButton waiting={this.props.waiting}
|
||||
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} />
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -251,14 +259,14 @@ module.exports = {
|
|||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="name-step">
|
||||
<Slide className="registration-step name-step">
|
||||
<h2>
|
||||
<intl.FormattedHTMLMessage id="teacherRegistration.nameStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.nameStepDescription" />
|
||||
<Tooltip title={'?'}
|
||||
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} />
|
||||
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.props.onNextStep}>
|
||||
|
@ -271,7 +279,7 @@ module.exports = {
|
|||
name="user.name.last"
|
||||
required />
|
||||
<NextStepButton waiting={this.props.waiting}
|
||||
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} />
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -297,14 +305,14 @@ module.exports = {
|
|||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="phone-step">
|
||||
<Slide className="registration-step phone-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.phoneStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.phoneStepDescription" />
|
||||
<Tooltip title={'?'}
|
||||
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} />
|
||||
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.onValidSubmit}>
|
||||
|
@ -319,7 +327,7 @@ module.exports = {
|
|||
isFalse: formatMessage({id: 'teacherRegistration.validationPhoneConsent'})
|
||||
}} />
|
||||
<NextStepButton waiting={this.props.waiting}
|
||||
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} />
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -373,14 +381,14 @@ module.exports = {
|
|||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="organization-step">
|
||||
<Slide className="registration-step organization-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.orgStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.orgStepDescription" />
|
||||
<Tooltip title={'?'}
|
||||
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} />
|
||||
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.onValidSubmit}>
|
||||
|
@ -417,7 +425,7 @@ module.exports = {
|
|||
placeholder={'http://'} />
|
||||
</div>
|
||||
<NextStepButton waiting={this.props.waiting}
|
||||
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} />
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -487,14 +495,14 @@ module.exports = {
|
|||
return 0;
|
||||
}.bind(this));
|
||||
return (
|
||||
<Slide className="address-step">
|
||||
<Slide className="registration-step address-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.addressStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.addressStepDescription" />
|
||||
<Tooltip title={'?'}
|
||||
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} />
|
||||
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.onValidSubmit}>
|
||||
|
@ -528,7 +536,7 @@ module.exports = {
|
|||
required />
|
||||
<GeneralError name="all" />
|
||||
<NextStepButton waiting={this.props.waiting || this.state.waiting}
|
||||
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} />
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -558,14 +566,14 @@ module.exports = {
|
|||
var textAreaClass = (this.state.characterCount > this.props.maxCharacters) ? 'fail' : '';
|
||||
|
||||
return (
|
||||
<Slide className="usescratch-step">
|
||||
<Slide className="registration-step usescratch-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.useScratchStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" />
|
||||
<Tooltip title={'?'}
|
||||
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} />
|
||||
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.props.onNextStep}>
|
||||
|
@ -585,7 +593,7 @@ module.exports = {
|
|||
<CharCount maxCharacters={this.props.maxCharacters}
|
||||
currentCharacters={this.state.characterCount} />
|
||||
<NextStepButton waiting={this.props.waiting}
|
||||
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} />
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -625,14 +633,14 @@ module.exports = {
|
|||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="email-step">
|
||||
<Slide className="registration-step email-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.emailStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.emailStepDescription" />
|
||||
<Tooltip title={'?'}
|
||||
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} />
|
||||
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.onValidSubmit}>
|
||||
|
@ -652,7 +660,7 @@ module.exports = {
|
|||
required />
|
||||
<GeneralError name="all" />
|
||||
<NextStepButton waiting={this.props.waiting}
|
||||
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} />
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -669,7 +677,7 @@ module.exports = {
|
|||
},
|
||||
render: function () {
|
||||
return (
|
||||
<Slide className="last-step">
|
||||
<Slide className="registration-step last-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="registration.lastStepTitle" />
|
||||
</h2>
|
||||
|
@ -707,10 +715,99 @@ module.exports = {
|
|||
);
|
||||
}
|
||||
})),
|
||||
ClassInviteStep: React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
classroom: {
|
||||
title: '',
|
||||
thumbnail: '',
|
||||
educator: {
|
||||
username: '',
|
||||
profile: {
|
||||
images: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
'general.getStarted': 'Get Started',
|
||||
'registration.classroomInviteStepDescription': 'has invited you to join the class:'
|
||||
},
|
||||
waiting: false
|
||||
};
|
||||
},
|
||||
onNextStep: function () {
|
||||
this.props.onNextStep();
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<Slide className="registration-step class-invite-step">
|
||||
<Avatar className="invite-avatar" src={this.props.classroom.educator.profile.images['50x50']} />
|
||||
<h2>{this.props.classroom.educator.username}</h2>
|
||||
<p className="description">
|
||||
{this.props.messages['registration.classroomInviteStepDescription']}
|
||||
</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={this.props.messages['general.getStarted']} />
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
}),
|
||||
ClassWelcomeStep: React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
classroom: {
|
||||
title: '',
|
||||
thumbnail: '',
|
||||
educator: {
|
||||
username: '',
|
||||
profile: {
|
||||
images: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
'registration.goToClass': 'Go to Class',
|
||||
'registration.welcomeStepDescription': 'You have successfully set up a Scratch account! ' +
|
||||
'You are now a member of the class:',
|
||||
'registration.welcomeStepPrompt': 'To get started, click on the button below.',
|
||||
'registration.welcomeStepTitle': 'Hurray! Welcome to Scratch!'
|
||||
}
|
||||
};
|
||||
},
|
||||
onNextStep: function () {
|
||||
this.props.onNextStep();
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<Slide className="registration-step class-welcome-step">
|
||||
<h2>{this.props.messages['registration.welcomeStepTitle']}</h2>
|
||||
<p className="description">{this.props.messages['registration.welcomeStepDescription']}</p>
|
||||
<Card>
|
||||
<div className="contents">
|
||||
<h3>{this.props.classroom.title}</h3>
|
||||
<img className="class-image" src={this.props.classroom.images['250x150']} />
|
||||
<p>{this.props.messages['registration.welcomeStepPrompt']}</p>
|
||||
</div>
|
||||
<NextStepButton onClick={this.onNextStep}
|
||||
waiting={this.props.waiting}
|
||||
text={this.props.messages['registration.goToClass']} />
|
||||
</Card>
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
}),
|
||||
RegistrationError: intl.injectIntl(React.createClass({
|
||||
render: function () {
|
||||
return (
|
||||
<Slide className="error-step">
|
||||
<Slide className="registration-step error-step">
|
||||
<h2>Something went wrong</h2>
|
||||
<Card>
|
||||
<h4>There was an error while processing your registration</h4>
|
||||
|
|
227
src/components/registration/steps.scss
Normal file
227
src/components/registration/steps.scss
Normal file
|
@ -0,0 +1,227 @@
|
|||
@import "../../colors";
|
||||
@import "../../frameless";
|
||||
|
||||
.registration-step {
|
||||
.demographics-checkbox-is-robot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.invite-avatar {
|
||||
display: block;
|
||||
margin: 0 auto 1rem auto;
|
||||
border: 2px solid $ui-white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.gender-input,
|
||||
.other-input {
|
||||
float: right;
|
||||
width: 90%;
|
||||
|
||||
.row {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.class-invite-step,
|
||||
&.class-welcome-step {
|
||||
.card {
|
||||
text-align: center;
|
||||
|
||||
.contents {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.username-step,
|
||||
&.name-step,
|
||||
&.address-step,
|
||||
&.email-step {
|
||||
.help-block {
|
||||
transform: translate(15.75rem, -4rem);
|
||||
}
|
||||
}
|
||||
|
||||
&.demographics-step {
|
||||
.gender-input {
|
||||
margin-top: -5.5rem;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
transform: translate(13rem, -2rem);
|
||||
}
|
||||
|
||||
.radio {
|
||||
margin-right: 2.5rem;
|
||||
line-height: 3rem;
|
||||
|
||||
input {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.phone-step {
|
||||
.form-group {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
input {
|
||||
&[type=checkbox] {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.help-block {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.checkbox-row {
|
||||
.help-block {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.organization-step {
|
||||
.help-block {
|
||||
transform: translate(16rem, -4rem);
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
.help-block {
|
||||
transform: translate(16rem, -16rem);
|
||||
}
|
||||
}
|
||||
|
||||
.organization-type,
|
||||
.url-input {
|
||||
p {
|
||||
margin: .25rem 0;
|
||||
text-align: left;
|
||||
color: $ui-dark-gray;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
&[value="8"] {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.other-input {
|
||||
margin-top: -5.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.address-step {
|
||||
.select {
|
||||
.help-block {
|
||||
transform: translate(0, .5rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.usescratch-step {
|
||||
.form {
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
|
||||
&.has-error {
|
||||
.textarea {
|
||||
border: 1px solid $ui-orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.help-block {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
p {
|
||||
&.char-count {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.last-step,
|
||||
&.error-step {
|
||||
&.slide {
|
||||
max-width: 38.75rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1rem auto;
|
||||
padding: 1.5rem;
|
||||
width: initial;
|
||||
|
||||
h4,
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $mobile - 1) {
|
||||
.registration-step {
|
||||
&.demographics-step {
|
||||
.radio {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
&.last-step,
|
||||
&.error-step {
|
||||
.card {
|
||||
margin: 0 auto;
|
||||
width: 18.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $desktop - 1) {
|
||||
.registration-step {
|
||||
.form {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.username-step,
|
||||
&.demographics-step,
|
||||
&.name-step {
|
||||
.help-block {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.phone-step {
|
||||
.checkbox,
|
||||
.help-block {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.organization-step {
|
||||
.checkbox-group {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,28 @@
|
|||
@import "../../frameless";
|
||||
@import "../../colors";
|
||||
|
||||
.slide {
|
||||
padding: 10px;
|
||||
|
||||
> h2,
|
||||
> .description {
|
||||
text-align: center;
|
||||
color: $type-white;
|
||||
}
|
||||
|
||||
> .description {
|
||||
margin-top: 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.step-navigation {
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $tablet - 1) {
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"general.copyright": "Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab",
|
||||
"general.country": "Country",
|
||||
"general.create": "Create",
|
||||
"general.createUsername": "Create a Username",
|
||||
"general.credits": "Credits",
|
||||
"general.discuss": "Discuss",
|
||||
"general.dmca": "DMCA",
|
||||
|
@ -77,9 +76,6 @@
|
|||
"general.username": "Username",
|
||||
"general.validationEmail": "Please enter a valid email address",
|
||||
"general.validationEmailMatch": "The emails do not match",
|
||||
"general.validationUsernameExists": "Sorry, that username already exists",
|
||||
"general.validationUsernameVulgar": "Hmm, that looks inappropriate",
|
||||
"general.validationUsernameInvalid": "Invalid username",
|
||||
"general.viewAll": "View All",
|
||||
"general.website": "Website",
|
||||
"general.whatsHappening": "What's Happening?",
|
||||
|
@ -109,13 +105,34 @@
|
|||
"parents.FaqResourcesQ": "What resources are available for learning Scratch?",
|
||||
"parents.introDescription": "Scratch is a programming language and an online community where children can program and share interactive media such as stories, games, and animation with people from all over the world. As children create with Scratch, they learn to think creatively, work collaboratively, and reason systematically. Scratch is designed and maintained by the Lifelong Kindergarten group at the MIT Media Lab.",
|
||||
|
||||
"registration.lastStepTitle": "Thank you for requesting a Scratch Teacher Account",
|
||||
"registration.lastStepDescription": "We are currently processing your application. ",
|
||||
"registration.checkOutResources": "Get Started with Resources",
|
||||
"registration.checkOutResourcesDescription": "Explore materials for educators and facilitators written by the Scratch Team, including <a href='/educators#resources'>tips, tutorials, and guides</a>.",
|
||||
"registration.confirmYourEmail": "Confirm Your Email",
|
||||
"registration.confirmYourEmailDescription": "If you haven't already, please click the link in the confirmation email sent to:",
|
||||
"registration.createUsername": "Create a Username",
|
||||
"registration.goToClass": "Go to Class",
|
||||
"registration.lastStepTitle": "Thank you for requesting a Scratch Teacher Account",
|
||||
"registration.lastStepDescription": "We are currently processing your application. ",
|
||||
"registration.nameStepTooltip": "This information is used for verification and to aggregate usage statistics.",
|
||||
"registration.nextStep": "Next Step",
|
||||
"registration.notOnWebsite": "This information will not appear on the Scratch website.",
|
||||
"registration.personalStepTitle": "Personal Information",
|
||||
"registration.personalStepDescription": "Your individual responses will not be displayed publicly, and will be kept confidential and secure",
|
||||
"registration.showPassword": "Show password",
|
||||
"registration.usernameStepDescription": "Fill in the following forms to request an account. The approval process may take up to 24 hours.",
|
||||
"registration.usernameStepTitle": "Request a Teacher Account",
|
||||
"registration.validationPasswordLength": "Passwords must be at least six characters",
|
||||
"registration.validationPasswordNotEquals": "Your password may not be \"password\"",
|
||||
"registration.validationPasswordNotUsername": "Your password may not be your username",
|
||||
"registration.validationUsernameRegexp": "Your username may only contain letters, numbers, \"-\", and \"_\"",
|
||||
"registration.validationUsernameMinLength": "Usernames must be at least 3 characters",
|
||||
"registration.validationUsernameMaxLength": "Usernames must be at most 20 characters",
|
||||
"registration.validationUsernameExists": "Sorry, that username already exists",
|
||||
"registration.validationUsernameVulgar": "Hmm, that looks inappropriate",
|
||||
"registration.validationUsernameInvalid": "Invalid username",
|
||||
"registration.waitForApproval": "Wait for Approval",
|
||||
"registration.waitForApprovalDescription": "Your information is being reviewed. Please be patient, the approval process can take up to 24 hours. You will receive an email with your login information once your account has been created.",
|
||||
"registration.checkOutResources": "Get Started with Resources",
|
||||
"registration.checkOutResourcesDescription": "Explore materials for educators and facilitators written by the Scratch Team, including <a href='/educators#resources'>tips, tutorials, and guides</a>."
|
||||
|
||||
"registration.welcomeStepDescription": "You have successfully set up a Scratch account! You are now a member of the class:",
|
||||
"registration.welcomeStepPrompt": "To get started, click on the button below.",
|
||||
"registration.welcomeStepTitle": "Hurray! Welcome to Scratch!"
|
||||
}
|
||||
|
|
222
src/routes.json
222
src/routes.json
|
@ -12,109 +12,16 @@
|
|||
"title": "About"
|
||||
},
|
||||
{
|
||||
"name": "developers",
|
||||
"pattern": "^/developers/?$",
|
||||
"view": "developers/developers",
|
||||
"title": "Developers"
|
||||
"name": "guidelines",
|
||||
"pattern": "^/community_guidelines/?$",
|
||||
"view": "guidelines/guidelines",
|
||||
"title": "Scratch Community Guidelines"
|
||||
},
|
||||
{
|
||||
"name": "hoc",
|
||||
"pattern": "^/hoc/?$",
|
||||
"view": "hoc/hoc",
|
||||
"title": "Hour of Code"
|
||||
},
|
||||
{
|
||||
"name": "explore",
|
||||
"pattern": "^/explore/:projects/:all/?$",
|
||||
"routeAlias": "^/explore(?!/ajax)",
|
||||
"view": "explore/explore",
|
||||
"title": "Explore"
|
||||
},
|
||||
{
|
||||
"name": "explore-redirect",
|
||||
"pattern": "^/explore/?$",
|
||||
"routeAlias": "^/explore(?!/ajax)",
|
||||
"redirect": "/explore/projects/all"
|
||||
},
|
||||
{
|
||||
"name": "explore-projects-redirect",
|
||||
"pattern": "^/explore/projects/?$",
|
||||
"routeAlias": "^/explore(?!/ajax)",
|
||||
"redirect": "/explore/projects/all"
|
||||
},
|
||||
{
|
||||
"name": "explore-studios-redirect",
|
||||
"pattern": "^/explore/studios/?$",
|
||||
"routeAlias": "^/explore(?!/ajax)",
|
||||
"redirect": "/explore/studios/all"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"pattern": "^/search/:projects?$/?$",
|
||||
"routeAlias": "^/search",
|
||||
"view": "search/search",
|
||||
"title": "Search"
|
||||
},
|
||||
{
|
||||
"name": "credits",
|
||||
"pattern": "^/info/credits/?$",
|
||||
"view": "credits/credits",
|
||||
"title": "Credits"
|
||||
},
|
||||
{
|
||||
"name": "faq",
|
||||
"pattern": "^/info/faq/?$",
|
||||
"view": "faq/faq",
|
||||
"title": "FAQ"
|
||||
},
|
||||
{
|
||||
"name": "educator-landing",
|
||||
"pattern": "^/educators/?$",
|
||||
"view": "teachers/landing/landing",
|
||||
"title": "Educators"
|
||||
},
|
||||
{
|
||||
"name": "teacher-faq",
|
||||
"pattern": "^/educators/faq/?$",
|
||||
"view": "teachers/faq/faq",
|
||||
"title": "Teacher Accounts FAQ"
|
||||
},
|
||||
{
|
||||
"name": "cards",
|
||||
"pattern": "^/info/cards/?$",
|
||||
"view": "cards/cards",
|
||||
"title": "Cards"
|
||||
},
|
||||
{
|
||||
"name": "communityblocks-interviews",
|
||||
"pattern": "^/info/communityblocks-interviews/?$",
|
||||
"view": "communityblocks-interviews/communityblocks-interviews",
|
||||
"title": "Community Blocks Beta Tester Interviews"
|
||||
},
|
||||
{
|
||||
"name": "jobs",
|
||||
"pattern": "^/jobs/?$",
|
||||
"view": "jobs/jobs",
|
||||
"title": "Jobs"
|
||||
},
|
||||
{
|
||||
"name": "teacherregistration",
|
||||
"pattern": "^/educators/register$",
|
||||
"view": "teacherregistration/teacherregistration",
|
||||
"title": "Teacher Registration",
|
||||
"viewportWidth": "device-width"
|
||||
},
|
||||
{
|
||||
"name": "teacherwaitingroom",
|
||||
"pattern": "^/educators/waiting",
|
||||
"view": "teacherwaitingroom/teacherwaitingroom",
|
||||
"title": "Thank you for requesting a Scratch Teacher Account"
|
||||
},
|
||||
{
|
||||
"name": "wedo2",
|
||||
"pattern": "^/wedo/?$",
|
||||
"view": "wedo2/wedo2",
|
||||
"title": "LEGO WeDo 2.0"
|
||||
"name": "student-registration",
|
||||
"pattern": "^/classes/:id/register/:token",
|
||||
"view": "studentregistration/studentregistration",
|
||||
"title": "Class Registration"
|
||||
},
|
||||
{
|
||||
"name": "conference-index",
|
||||
|
@ -157,9 +64,10 @@
|
|||
"viewportWidth": "device-width"
|
||||
},
|
||||
{
|
||||
"name": "donate",
|
||||
"pattern": "^/info/donate/?",
|
||||
"redirect": "https://secure.donationpay.org/scratchfoundation/"
|
||||
"name": "developers",
|
||||
"pattern": "^/developers/?$",
|
||||
"view": "developers/developers",
|
||||
"title": "Developers"
|
||||
},
|
||||
{
|
||||
"name": "dmca",
|
||||
|
@ -168,10 +76,72 @@
|
|||
"title": "DMCA"
|
||||
},
|
||||
{
|
||||
"name": "guidelines",
|
||||
"pattern": "^/community_guidelines/?$",
|
||||
"view": "guidelines/guidelines",
|
||||
"title": "Scratch Community Guidelines"
|
||||
"name": "educator-landing",
|
||||
"pattern": "^/educators/?$",
|
||||
"view": "teachers/landing/landing",
|
||||
"title": "Educators"
|
||||
},
|
||||
{
|
||||
"name": "teacher-faq",
|
||||
"pattern": "^/educators/faq/?$",
|
||||
"view": "teachers/faq/faq",
|
||||
"title": "Teacher Accounts FAQ"
|
||||
},
|
||||
{
|
||||
"name": "teacherregistration",
|
||||
"pattern": "^/educators/register$",
|
||||
"view": "teacherregistration/teacherregistration",
|
||||
"title": "Teacher Registration",
|
||||
"viewportWidth": "device-width"
|
||||
},
|
||||
{
|
||||
"name": "teacherwaitingroom",
|
||||
"pattern": "^/educators/waiting",
|
||||
"view": "teacherwaitingroom/teacherwaitingroom",
|
||||
"title": "Thank you for requesting a Scratch Teacher Account"
|
||||
},
|
||||
{
|
||||
"name": "explore",
|
||||
"pattern": "^/explore/:projects/:all/?$",
|
||||
"routeAlias": "^/explore(?!/ajax)",
|
||||
"view": "explore/explore",
|
||||
"title": "Explore"
|
||||
},
|
||||
{
|
||||
"name": "hoc",
|
||||
"pattern": "^/hoc/?$",
|
||||
"view": "hoc/hoc",
|
||||
"title": "Hour of Code"
|
||||
},
|
||||
{
|
||||
"name": "cards",
|
||||
"pattern": "^/info/cards/?$",
|
||||
"view": "cards/cards",
|
||||
"title": "Cards"
|
||||
},
|
||||
{
|
||||
"name": "communityblocks-interviews",
|
||||
"pattern": "^/info/communityblocks-interviews/?$",
|
||||
"view": "communityblocks-interviews/communityblocks-interviews",
|
||||
"title": "Community Blocks Beta Tester Interviews"
|
||||
},
|
||||
{
|
||||
"name": "credits",
|
||||
"pattern": "^/info/credits/?$",
|
||||
"view": "credits/credits",
|
||||
"title": "Credits"
|
||||
},
|
||||
{
|
||||
"name": "faq",
|
||||
"pattern": "^/info/faq/?$",
|
||||
"view": "faq/faq",
|
||||
"title": "FAQ"
|
||||
},
|
||||
{
|
||||
"name": "jobs",
|
||||
"pattern": "^/jobs/?$",
|
||||
"view": "jobs/jobs",
|
||||
"title": "Jobs"
|
||||
},
|
||||
{
|
||||
"name": "privacypolicy",
|
||||
|
@ -179,10 +149,46 @@
|
|||
"view": "privacypolicy/privacypolicy",
|
||||
"title": "Privacy Policy"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"pattern": "^/search/:projects?$/?$",
|
||||
"routeAlias": "^/search",
|
||||
"view": "search/search",
|
||||
"title": "Search"
|
||||
},
|
||||
{
|
||||
"name": "terms",
|
||||
"pattern": "^/terms_of_use/?$",
|
||||
"view": "terms/terms",
|
||||
"title": "Scratch Terms of Use"
|
||||
},
|
||||
{
|
||||
"name": "wedo2",
|
||||
"pattern": "^/wedo/?$",
|
||||
"view": "wedo2/wedo2",
|
||||
"title": "LEGO WeDo 2.0"
|
||||
},
|
||||
{
|
||||
"name": "donate",
|
||||
"pattern": "^/info/donate/?",
|
||||
"redirect": "https://secure.donationpay.org/scratchfoundation/"
|
||||
},
|
||||
{
|
||||
"name": "explore-redirect",
|
||||
"pattern": "^/explore/?$",
|
||||
"routeAlias": "^/explore(?!/ajax)",
|
||||
"redirect": "/explore/projects/all"
|
||||
},
|
||||
{
|
||||
"name": "explore-projects-redirect",
|
||||
"pattern": "^/explore/projects/?$",
|
||||
"routeAlias": "^/explore(?!/ajax)",
|
||||
"redirect": "/explore/projects/all"
|
||||
},
|
||||
{
|
||||
"name": "explore-studios-redirect",
|
||||
"pattern": "^/explore/studios/?$",
|
||||
"routeAlias": "^/explore(?!/ajax)",
|
||||
"redirect": "/explore/studios/all"
|
||||
}
|
||||
]
|
||||
|
|
3
src/views/studentregistration/l10n.json
Normal file
3
src/views/studentregistration/l10n.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"studentRegistration.classroomApiGeneralError": "Sorry, we could not find the registration information for this class"
|
||||
}
|
124
src/views/studentregistration/studentregistration.jsx
Normal file
124
src/views/studentregistration/studentregistration.jsx
Normal file
|
@ -0,0 +1,124 @@
|
|||
var defaults = require('lodash.defaultsdeep');
|
||||
var React = require('react');
|
||||
var render = require('../../lib/render.jsx');
|
||||
|
||||
var api = require('../../lib/api');
|
||||
var intl = require('../../lib/intl.jsx');
|
||||
|
||||
var Deck = require('../../components/deck/deck.jsx');
|
||||
var Progression = require('../../components/progression/progression.jsx');
|
||||
var Steps = require('../../components/registration/steps.jsx');
|
||||
|
||||
require('./studentregistration.scss');
|
||||
|
||||
var StudentRegistration = intl.injectIntl(React.createClass({
|
||||
type: 'StudentRegistration',
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
classroomId: null,
|
||||
classroomToken: null
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
formData: {},
|
||||
registrationError: null,
|
||||
step: 0,
|
||||
waiting: false
|
||||
};
|
||||
},
|
||||
advanceStep: function (formData) {
|
||||
formData = formData || {};
|
||||
this.setState({
|
||||
step: this.state.step + 1,
|
||||
formData: defaults({}, formData, this.state.formData)
|
||||
});
|
||||
},
|
||||
componentDidMount: function () {
|
||||
api({
|
||||
uri: '/classrooms/' + this.props.classroomId + '/' + this.props.classroomToken
|
||||
}, function (err, body, res) {
|
||||
if (err) {
|
||||
return this.setState({
|
||||
registrationError: this.props.intl.formatMessage({
|
||||
id: 'studentRegistration.classroomApiGeneralError'
|
||||
})
|
||||
});
|
||||
}
|
||||
if (res.statusCode === 404) {
|
||||
// TODO: Use react-router for this
|
||||
return window.location = '/404';
|
||||
}
|
||||
this.setState({classroom: body});
|
||||
}.bind(this));
|
||||
},
|
||||
register: function (formData) {
|
||||
this.setState({waiting: true});
|
||||
formData = defaults({}, formData || {}, this.state.formData);
|
||||
api({
|
||||
host: '',
|
||||
uri: '/classes/register_new_student/',
|
||||
method: 'post',
|
||||
useCsrf: true,
|
||||
formData: {
|
||||
username: formData.user.username,
|
||||
password: formData.user.password,
|
||||
birth_month: formData.user.birth.month,
|
||||
birth_year: formData.user.birth.year,
|
||||
gender: (
|
||||
formData.user.gender === 'other' ?
|
||||
formData.user.genderOther :
|
||||
formData.user.gender
|
||||
),
|
||||
country: formData.user.country,
|
||||
is_robot: formData.user.isRobot,
|
||||
classroom_id: this.props.classroomId,
|
||||
classroom_token: this.props.classroomToken
|
||||
}
|
||||
}, function (err, res) {
|
||||
this.setState({waiting: false});
|
||||
if (err) return this.setState({registrationError: err});
|
||||
if (res[0].success) return this.advanceStep(formData);
|
||||
this.setState({registrationError: res[0].msg});
|
||||
}.bind(this));
|
||||
},
|
||||
goToClass: function () {
|
||||
window.location = '/classes/' + this.props.classroomId + '/';
|
||||
},
|
||||
render: function () {
|
||||
var demographicsDescription = this.props.intl.formatMessage({id: 'registration.notOnWebsite'});
|
||||
return (
|
||||
<Deck className="student-registration">
|
||||
{this.state.registrationError ?
|
||||
<Steps.RegistrationError {... this.state} />
|
||||
:
|
||||
<Progression {... this.state}>
|
||||
<Steps.ClassInviteStep classroom={this.state.classroom}
|
||||
messages={this.props.messages}
|
||||
onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.UsernameStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.DemographicsStep description={demographicsDescription}
|
||||
onNextStep={this.register}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.ClassWelcomeStep classroom={this.state.classroom}
|
||||
messages={this.props.messages}
|
||||
onNextStep={this.goToClass}
|
||||
waiting={this.state.waiting} />
|
||||
</Progression>
|
||||
}
|
||||
</Deck>
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
var [classroomId, _, classroomToken] = document.location.pathname.split('/')
|
||||
.filter(function (p) {
|
||||
if (p) return p;
|
||||
})
|
||||
.slice(-3);
|
||||
|
||||
var props = {classroomId, classroomToken};
|
||||
|
||||
render(<StudentRegistration {... props} />, document.getElementById('app'));
|
13
src/views/studentregistration/studentregistration.scss
Normal file
13
src/views/studentregistration/studentregistration.scss
Normal file
|
@ -0,0 +1,13 @@
|
|||
@import "../../colors";
|
||||
@import "../../frameless";
|
||||
|
||||
@include responsive-layout (".student-registration", ".slide");
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: darken($ui-purple, 8%);
|
||||
}
|
||||
|
||||
.teacher-registration {
|
||||
background-color: $ui-purple;
|
||||
}
|
|
@ -1,19 +1,6 @@
|
|||
{
|
||||
"teacherRegistration.usernameStepDescription": "Fill in the following forms to request an account. The approval process may take up to 24 hours.",
|
||||
"teacherRegistration.usernameStepTitle": "Request a Teacher Account",
|
||||
"teacherRegistration.validationUsernameRegexp": "Your username may only contain letters, numbers, \"-\", and \"_\"",
|
||||
"teacherRegistration.validationUsernameMinLength": "Usernames must be at least 3 characters",
|
||||
"teacherRegistration.validationUsernameMaxLength": "Usernames must be at most 20 characters",
|
||||
"teacherRegistration.validationPasswordLength": "Passwords must be at least six characters",
|
||||
"teacherRegistration.validationPasswordNotEquals": "Your password may not be \"password\"",
|
||||
"teacherRegistration.validationPasswordNotUsername": "Your password may not be your username",
|
||||
"teacherRegistration.showPassword": "Show password",
|
||||
"teacherRegistration.nextStep": "Next Step",
|
||||
"teacherRegistration.personalStepTitle": "Personal Information",
|
||||
"teacherRegistration.personalStepDescription": "Your individual responses will not be displayed publicly, and will be kept confidential and secure",
|
||||
"teacherRegistration.nameStepTitle": "First & Last Name",
|
||||
"teacherRegistration.nameStepDescription": "Your name will not be displayed publicly, and will be kept confidential and secure.",
|
||||
"teacherRegistration.nameStepTooltip": "This information is used for verification and to aggregate usage statistics.",
|
||||
"teacherRegistration.firstName": "First Name",
|
||||
"teacherRegistration.lastName": "Last Name",
|
||||
"teacherRegistration.phoneStepTitle": "Phone Number",
|
||||
|
|
|
@ -79,28 +79,28 @@ var TeacherRegistration = React.createClass({
|
|||
<Steps.RegistrationError {... this.state} />
|
||||
:
|
||||
<Progression {... this.state}>
|
||||
<Steps.UsernameStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.UsernameStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.DemographicsStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.NameStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.PhoneNumberStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting}
|
||||
defaultCountry={
|
||||
this.state.formData.user && this.state.formData.user.country
|
||||
} />
|
||||
<Steps.NameStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.PhoneNumberStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting}
|
||||
defaultCountry={
|
||||
this.state.formData.user && this.state.formData.user.country
|
||||
} />
|
||||
<Steps.OrganizationStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.AddressStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting}
|
||||
defaultCountry={
|
||||
this.state.formData.user && this.state.formData.user.country
|
||||
} />
|
||||
<Steps.UseScratchStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.EmailStep onNextStep={this.register}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.AddressStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting}
|
||||
defaultCountry={
|
||||
this.state.formData.user && this.state.formData.user.country
|
||||
} />
|
||||
<Steps.UseScratchStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.EmailStep onNextStep={this.register}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.TeacherApprovalStep email={this.state.formData.user && this.state.formData.user.email} />
|
||||
</Progression>
|
||||
}
|
||||
|
|
|
@ -10,209 +10,4 @@ body {
|
|||
|
||||
.teacher-registration {
|
||||
background-color: $ui-purple;
|
||||
|
||||
.demographics-checkbox-is-robot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gender-input,
|
||||
.other-input {
|
||||
float: right;
|
||||
width: 90%;
|
||||
|
||||
.row {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.username-step,
|
||||
.name-step,
|
||||
.address-step,
|
||||
.email-step {
|
||||
.help-block {
|
||||
transform: translate(15.75rem, -4rem);
|
||||
}
|
||||
}
|
||||
|
||||
.demographics-step {
|
||||
.gender-input {
|
||||
margin-top: -5.5rem;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
transform: translate(13rem, -2rem);
|
||||
}
|
||||
|
||||
.radio {
|
||||
margin-right: 2.5rem;
|
||||
line-height: 3rem;
|
||||
|
||||
input {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.phone-step {
|
||||
.form-group {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
input {
|
||||
&[type=checkbox] {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.help-block {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.checkbox-row {
|
||||
.help-block {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.organization-step {
|
||||
.help-block {
|
||||
transform: translate(16rem, -4rem);
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
.help-block {
|
||||
transform: translate(16rem, -16rem);
|
||||
}
|
||||
}
|
||||
|
||||
.organization-type,
|
||||
.url-input {
|
||||
p {
|
||||
margin: .25rem 0;
|
||||
text-align: left;
|
||||
color: $ui-dark-gray;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
&[value="8"] {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.other-input {
|
||||
margin-top: -5.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.address-step {
|
||||
.select {
|
||||
.help-block {
|
||||
transform: translate(0, .5rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.usescratch-step {
|
||||
.form {
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
|
||||
&.has-error {
|
||||
.textarea {
|
||||
border: 1px solid $ui-orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.help-block {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
p {
|
||||
&.char-count {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.last-step,
|
||||
.error-step {
|
||||
&.slide {
|
||||
max-width: 38.75rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1rem auto;
|
||||
padding: 1.5rem;
|
||||
width: initial;
|
||||
|
||||
h4,
|
||||
p {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $mobile - 1) {
|
||||
.teacher-registration {
|
||||
.demographics-step {
|
||||
.radio {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.last-step,
|
||||
.error-step {
|
||||
.card {
|
||||
margin: 0 auto;
|
||||
width: 18.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $desktop - 1) {
|
||||
.teacher-registration {
|
||||
.form {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.username-step,
|
||||
.demographics-step,
|
||||
.name-step {
|
||||
.help-block {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-step {
|
||||
.checkbox,
|
||||
.help-block {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.organization-step {
|
||||
.checkbox-group {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue