Commit 1c5940cc authored by Ray Schamp's avatar Ray Schamp

Add student registration flow

parent 47ebef1b
...@@ -6,6 +6,7 @@ var intl = require('../../lib/intl.jsx'); ...@@ -6,6 +6,7 @@ var intl = require('../../lib/intl.jsx');
var log = require('../../lib/log'); var log = require('../../lib/log');
var smartyStreets = require('../../lib/smarty-streets'); var smartyStreets = require('../../lib/smarty-streets');
var Avatar = require('../../components/avatar/avatar.jsx');
var Button = require('../../components/forms/button.jsx'); var Button = require('../../components/forms/button.jsx');
var Card = require('../../components/card/card.jsx'); var Card = require('../../components/card/card.jsx');
var CharCount = require('../../components/forms/charcount.jsx'); var CharCount = require('../../components/forms/charcount.jsx');
...@@ -36,7 +37,7 @@ var NextStepButton = React.createClass({ ...@@ -36,7 +37,7 @@ var NextStepButton = React.createClass({
}, },
render: function () { render: function () {
return ( 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 ? {this.props.waiting ?
<Spinner /> : <Spinner /> :
this.props.text this.props.text
...@@ -81,16 +82,16 @@ module.exports = { ...@@ -81,16 +82,16 @@ module.exports = {
return this.props.onNextStep(formData); return this.props.onNextStep(formData);
case 'username exists': case 'username exists':
return invalidate({ return invalidate({
'user.username': formatMessage({id: 'general.validationUsernameExists'}) 'user.username': formatMessage({id: 'registration.validationUsernameExists'})
}); });
case 'bad username': case 'bad username':
return invalidate({ return invalidate({
'user.username': formatMessage({id: 'general.validationUsernameVulgar'}) 'user.username': formatMessage({id: 'registration.validationUsernameVulgar'})
}); });
case 'invalid username': case 'invalid username':
default: default:
return invalidate({ return invalidate({
'user.username': formatMessage({id: 'general.validationUsernameInvalid'}) 'user.username': formatMessage({id: 'registration.validationUsernameInvalid'})
}); });
} }
}.bind(this)); }.bind(this));
...@@ -98,14 +99,14 @@ module.exports = { ...@@ -98,14 +99,14 @@ module.exports = {
render: function () { render: function () {
var formatMessage = this.props.intl.formatMessage; var formatMessage = this.props.intl.formatMessage;
return ( return (
<h2><intl.FormattedMessage id="teacherRegistration.usernameStepTitle" /></h2>
<Slide className="registration-step username-step"> <Slide className="registration-step username-step">
<h2><intl.FormattedMessage id="registration.usernameStepTitle" /></h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.usernameStepDescription" /> <intl.FormattedMessage id="registration.usernameStepDescription" />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
<Input label={formatMessage({id: 'general.createUsername'})} <Input label={formatMessage({id: 'registration.createUsername'})}
className={this.state.validUsername} className={this.state.validUsername}
type="text" type="text"
name="user.username" name="user.username"
...@@ -116,13 +117,13 @@ module.exports = { ...@@ -116,13 +117,13 @@ module.exports = {
}} }}
validationErrors={{ validationErrors={{
matchRegexp: formatMessage({ matchRegexp: formatMessage({
id: 'teacherRegistration.validationUsernameRegexp' id: 'registration.validationUsernameRegexp'
}), }),
minLength: formatMessage({ minLength: formatMessage({
id: 'teacherRegistration.validationUsernameMinLength' id: 'registration.validationUsernameMinLength'
}), }),
maxLength: formatMessage({ maxLength: formatMessage({
id: 'teacherRegistration.validationUsernameMaxLength' id: 'registration.validationUsernameMaxLength'
}) })
}} }}
required /> required />
...@@ -136,24 +137,24 @@ module.exports = { ...@@ -136,24 +137,24 @@ module.exports = {
}} }}
validationErrors={{ validationErrors={{
minLength: formatMessage({ minLength: formatMessage({
id: 'teacherRegistration.validationPasswordLength' id: 'registration.validationPasswordLength'
}), }),
notEquals: formatMessage({ notEquals: formatMessage({
id: 'teacherRegistration.validationPasswordNotEquals' id: 'registration.validationPasswordNotEquals'
}), }),
notEqualsField: formatMessage({ notEqualsField: formatMessage({
id: 'teacherRegistration.validationPasswordNotUsername' id: 'registration.validationPasswordNotUsername'
}) })
}} }}
required /> required />
<Checkbox label={formatMessage({id: 'teacherRegistration.showPassword'})} <Checkbox label={formatMessage({id: 'registration.showPassword'})}
value={this.state.showPassword} value={this.state.showPassword}
onChange={this.onChangeShowPassword} onChange={this.onChangeShowPassword}
help={null} help={null}
name="showPassword" /> name="showPassword" />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton waiting={this.props.waiting || this.state.waiting} <NextStepButton waiting={this.props.waiting || this.state.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
...@@ -195,12 +196,12 @@ module.exports = { ...@@ -195,12 +196,12 @@ module.exports = {
return ( return (
<Slide className="registration-step demographics-step"> <Slide className="registration-step demographics-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.personalStepTitle" /> <intl.FormattedMessage id="registration.personalStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.personalStepDescription" /> <intl.FormattedMessage id="registration.personalStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.props.onNextStep}> <Form onValidSubmit={this.props.onNextStep}>
...@@ -236,7 +237,7 @@ module.exports = { ...@@ -236,7 +237,7 @@ module.exports = {
label="I'm a robot!" label="I'm a robot!"
name="user.isRobot" /> name="user.isRobot" />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
...@@ -260,7 +261,7 @@ module.exports = { ...@@ -260,7 +261,7 @@ module.exports = {
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.nameStepDescription" /> <intl.FormattedMessage id="teacherRegistration.nameStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.props.onNextStep}> <Form onValidSubmit={this.props.onNextStep}>
...@@ -273,7 +274,7 @@ module.exports = { ...@@ -273,7 +274,7 @@ module.exports = {
name="user.name.last" name="user.name.last"
required /> required />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
...@@ -306,7 +307,7 @@ module.exports = { ...@@ -306,7 +307,7 @@ module.exports = {
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.phoneStepDescription" /> <intl.FormattedMessage id="teacherRegistration.phoneStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
...@@ -321,7 +322,7 @@ module.exports = { ...@@ -321,7 +322,7 @@ module.exports = {
isFalse: formatMessage({id: 'teacherRegistration.validationPhoneConsent'}) isFalse: formatMessage({id: 'teacherRegistration.validationPhoneConsent'})
}} /> }} />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
...@@ -382,7 +383,7 @@ module.exports = { ...@@ -382,7 +383,7 @@ module.exports = {
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.orgStepDescription" /> <intl.FormattedMessage id="teacherRegistration.orgStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
...@@ -419,7 +420,7 @@ module.exports = { ...@@ -419,7 +420,7 @@ module.exports = {
placeholder={'http://'} /> placeholder={'http://'} />
</div> </div>
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
...@@ -496,7 +497,7 @@ module.exports = { ...@@ -496,7 +497,7 @@ module.exports = {
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.addressStepDescription" /> <intl.FormattedMessage id="teacherRegistration.addressStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
...@@ -530,7 +531,7 @@ module.exports = { ...@@ -530,7 +531,7 @@ module.exports = {
required /> required />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton waiting={this.props.waiting || this.state.waiting} <NextStepButton waiting={this.props.waiting || this.state.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
...@@ -567,7 +568,7 @@ module.exports = { ...@@ -567,7 +568,7 @@ module.exports = {
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" /> <intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.props.onNextStep}> <Form onValidSubmit={this.props.onNextStep}>
...@@ -587,7 +588,7 @@ module.exports = { ...@@ -587,7 +588,7 @@ module.exports = {
<CharCount maxCharacters={this.props.maxCharacters} <CharCount maxCharacters={this.props.maxCharacters}
currentCharacters={this.state.characterCount} /> currentCharacters={this.state.characterCount} />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
...@@ -634,7 +635,7 @@ module.exports = { ...@@ -634,7 +635,7 @@ module.exports = {
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.emailStepDescription" /> <intl.FormattedMessage id="teacherRegistration.emailStepDescription" />
<Tooltip title={'?'} <Tooltip title={'?'}
tipContent={formatMessage({id: 'teacherRegistration.nameStepTooltip'})} /> tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
</p> </p>
<Card> <Card>
<Form onValidSubmit={this.onValidSubmit}> <Form onValidSubmit={this.onValidSubmit}>
...@@ -654,7 +655,7 @@ module.exports = { ...@@ -654,7 +655,7 @@ module.exports = {
required /> required />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton waiting={this.props.waiting} <NextStepButton waiting={this.props.waiting}
text={<intl.FormattedMessage id="teacherRegistration.nextStep" />} /> text={<intl.FormattedMessage id="registration.nextStep" />} />
</Form> </Form>
</Card> </Card>
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} /> <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
...@@ -709,6 +710,96 @@ module.exports = { ...@@ -709,6 +710,96 @@ module.exports = {
); );
} }
})), })),
ClassInviteStep: intl.injectIntl(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 () {
console.log("onNextStep");
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.image} />
</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: intl.injectIntl(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.image} />
<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({ RegistrationError: intl.injectIntl(React.createClass({
render: function () { render: function () {
return ( return (
......
...@@ -110,6 +110,7 @@ ...@@ -110,6 +110,7 @@
"registration.confirmYourEmail": "Confirm Your Email", "registration.confirmYourEmail": "Confirm Your Email",
"registration.confirmYourEmailDescription": "If you haven't already, please click the link in the confirmation email sent to:", "registration.confirmYourEmailDescription": "If you haven't already, please click the link in the confirmation email sent to:",
"registration.createUsername": "Create a Username", "registration.createUsername": "Create a Username",
"registration.goToClass": "Go to Class",
"registration.lastStepTitle": "Thank you for requesting a Scratch Teacher Account", "registration.lastStepTitle": "Thank you for requesting a Scratch Teacher Account",
"registration.lastStepDescription": "We are currently processing your application. ", "registration.lastStepDescription": "We are currently processing your application. ",
"registration.nameStepTooltip": "This information is used for verification and to aggregate usage statistics.", "registration.nameStepTooltip": "This information is used for verification and to aggregate usage statistics.",
...@@ -130,5 +131,7 @@ ...@@ -130,5 +131,7 @@
"registration.validationUsernameInvalid": "Invalid username", "registration.validationUsernameInvalid": "Invalid username",
"registration.waitForApproval": "Wait for Approval", "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.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.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!"
} }
...@@ -12,60 +12,68 @@ ...@@ -12,60 +12,68 @@
"title": "About" "title": "About"
}, },
{ {
"name": "developers", "name": "guidelines",
"pattern": "^/developers/?$", "pattern": "^/community_guidelines/?$",
"view": "developers/developers", "view": "guidelines/guidelines",
"title": "Developers" "title": "Scratch Community Guidelines"
}, },
{ {
"name": "hoc", "name": "student-registration",
"pattern": "^/hoc/?$", "pattern": "^/classes/:id/register/:token",
"view": "hoc/hoc", "view": "studentregistration/studentregistration",
"title": "Hour of Code" "title": "Class Registration"
}, },
{ {
"name": "explore", "name": "conference-index",
"pattern": "^/explore/:projects/:all/?$", "pattern": "^/conference/?$",
"routeAlias": "^/explore(?!/ajax)", "routeAlias": "^/conference(?!/201[4-5])",
"view": "explore/explore", "view": "conference/index/index",
"title": "Explore" "title": "Scratch Conference",
"viewportWidth": "device-width"
}, },
{ {
"name": "explore-redirect", "name": "conference-plan",
"pattern": "^/explore/?$", "pattern": "^/conference/plan/?$",
"routeAlias": "^/explore(?!/ajax)", "routeAlias": "^/conference(?!/201[4-5])",
"redirect": "/explore/projects/all" "view": "conference/plan/plan",
"title": "Plan Your Visit",
"viewportWidth": "device-width"
}, },
{ {
"name": "explore-projects-redirect", "name": "conference-expectations",
"pattern": "^/explore/projects/?$", "pattern": "^/conference/expect/?$",
"routeAlias": "^/explore(?!/ajax)", "routeAlias": "^/conference(?!/201[4-5])",
"redirect": "/explore/projects/all" "view": "conference/expect/expect",
"title": "What to Expect",
"viewportWidth": "device-width"
}, },
{ {
"name": "explore-studios-redirect", "name": "conference-schedule",
"pattern": "^/explore/studios/?$", "pattern": "^/conference/schedule/?$",
"routeAlias": "^/explore(?!/ajax)", "routeAlias": "^/conference(?!/201[4-5])",
"redirect": "/explore/studios/all" "view": "conference/schedule/schedule",
"title": "Conference Schedule",
"viewportWidth": "device-width"
}, },
{ {
"name": "search", "name": "conference-details",
"pattern": "^/search/:projects?$/?$", "pattern": "^/conference/:id/details/?$",
"routeAlias": "^/search", "routeAlias": "^/conference(?!/201[4-5])",
"view": "search/search", "view": "conference/details/details",
"title": "Search" "title": "Event Details",
"viewportWidth": "device-width"
}, },
{ {
"name": "credits", "name": "developers",
"pattern": "^/info/credits/?$", "pattern": "^/developers/?$",
"view": "credits/credits", "view": "developers/developers",
"title": "Credits" "title": "Developers"
}, },
{ {
"name": "faq", "name": "dmca",
"pattern": "^/info/faq/?$", "pattern": "^/DMCA/?$",
"view": "faq/faq", "view": "dmca/dmca",
"title": "FAQ" "title": "DMCA"
}, },
{ {
"name": "educator-landing", "name": "educator-landing",
...@@ -79,24 +87,6 @@ ...@@ -79,24 +87,6 @@
"view": "teachers/faq/faq", "view": "teachers/faq/faq",
"title": "Teacher Accounts 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", "name": "teacherregistration",
"pattern": "^/educators/register$", "pattern": "^/educators/register$",
...@@ -111,67 +101,47 @@ ...@@ -111,67 +101,47 @@
"title": "Thank you for requesting a Scratch Teacher Account" "title": "Thank you for requesting a Scratch Teacher Account"
}, },
{ {
"name": "wedo2", "name": "explore",
"pattern": "^/wedo/?$", "pattern": "^/explore/:projects/:all/?$",
"view": "wedo2/wedo2", "routeAlias": "^/explore(?!/ajax)",
"title": "LEGO WeDo 2.0" "view": "explore/explore",
}, "title": "Explore"
{
"name": "conference-index",
"pattern": "^/conference/?$",
"routeAlias": "^/conference(?!/201[4-5])",
"view": "conference/index/index",
"title": "Scratch Conference",
"viewportWidth": "device-width"
},
{
"name": "conference-plan",
"pattern": "^/conference/plan/?$",
"routeAlias": "^/conference(?!/201[4-5])",
"view": "conference/plan/plan",
"title": "Plan Your Visit",
"viewportWidth": "device-width"
}, },
{ {
"name": "conference-expectations", "name": "hoc",
"pattern": "^/conference/expect/?$", "pattern": "^/hoc/?$",
"routeAlias": "^/conference(?!/201[4-5])", "view": "hoc/hoc",
"view": "conference/expect/expect", "title": "Hour of Code"
"title": "What to Expect",
"viewportWidth": "device-width"
}, },
{ {
"name": "conference-schedule", "name": "cards",
"pattern": "^/conference/schedule/?$", "pattern": "^/info/cards/?$",
"routeAlias": "^/conference(?!/201[4-5])", "view": "cards/cards",
"view": "conference/schedule/schedule", "title": "Cards"
"title": "Conference Schedule",
"viewportWidth": "device-width"
}, },
{ {
"name": "conference-details", "name": "communityblocks-interviews",
"pattern": "^/conference/:id/details/?$", "pattern": "^/info/communityblocks-interviews/?$",
"routeAlias": "^/conference(?!/201[4-5])", "view": "communityblocks-interviews/communityblocks-interviews",
"view": "conference/details/details", "title": "Community Blocks Beta Tester Interviews"
"title": "Event Details",
"viewportWidth": "device-width"
}, },
{ {
"name": "donate", "name": "credits",
"pattern": "^/info/donate/?", "pattern": "^/info/credits/?$",
"redirect": "https://secure.donationpay.org/scratchfoundation/" "view": "credits/credits",
"title": "Credits"
}, },
{ {
"name": "dmca", "name": "faq",
"pattern": "^/DMCA/?$", "pattern": "^/info/faq/?$",
"view": "dmca/dmca", "view": "faq/faq",
"title": "DMCA" "title": "FAQ"
}, },
{ {
"name": "guidelines", "name": "jobs",
"pattern": "^/community_guidelines/?$", "pattern": "^/jobs/?$",
"view": "guidelines/guidelines", "view": "jobs/jobs",
"title": "Scratch Community Guidelines" "title": "Jobs"
}, },
{ {
"name": "privacypolicy", "name": "privacypolicy",
...@@ -179,10 +149,46 @@ ...@@ -179,10 +149,46 @@
"view": "privacypolicy/privacypolicy", "view": "privacypolicy/privacypolicy",
"title": "Privacy Policy" "title": "Privacy Policy"
}, },
{
"name": "search",
"pattern": "^/search/:projects?$/?$",
"routeAlias": "^/search",
"view": "search/search",
"title": "Search"
},
{ {
"name": "terms", "name": "terms",
"pattern": "^/terms_of_use/?$", "pattern": "^/terms_of_use/?$",
"view": "terms/terms", "view": "terms/terms",
"title": "Scratch Terms of Use" "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"
} }
] ]
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',
defaultMessage: 'Sorry, we could not find the registration information for this class'
})
});
}
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 () {
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 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'));
@import "../../colors";
@import "../../frameless";
@include responsive-layout (".student-registration", ".slide");
html,
body {
background-color: darken($ui-purple, 8%);
}
.teacher-registration {
background-color: $ui-purple;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment