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

Add student registration flow

parent 47ebef1b
This diff is collapsed.
...@@ -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