mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-07-05 00:14:02 -04:00
Merge pull request #750 from LLK/release/2.2.11
[Master] Release 2.2.11
This commit is contained in:
commit
00c7dc9257
35 changed files with 1323 additions and 658 deletions
.eslintrcl10n.json
bin
package.jsonsrc
components
card
deck
forms
checkbox.jsxinput.jsxinput.scssphone-input.jsxphone-input.scssrow.scssselect.scsstextarea.jsxtextarea.scss
navigation/www
registration
slide
redux
routes.jsonviews
studentcompleteregistration
studentregistration
teacherregistration
teachers/landing
teacherwaitingroom
static/images/teachers
webpack.config.js
|
@ -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"
|
||||
|
|
|
@ -18,9 +18,9 @@ var fastly = require('./lib/fastly-extended')(process.env.FASTLY_API_KEY, FASTLY
|
|||
var extraAppRoutes = [
|
||||
// Homepage with querystring.
|
||||
// TODO: Should this be added for every route?
|
||||
'^/\\?',
|
||||
'/\\?',
|
||||
// View html
|
||||
'^/[^\/]*\.html$'
|
||||
'/[^\/]*\.html$'
|
||||
];
|
||||
|
||||
/*
|
||||
|
@ -35,7 +35,7 @@ var getStaticPaths = function (pathToStatic) {
|
|||
}).map(function (pathName) {
|
||||
// Reduce absolute path to relative paths like '/js'
|
||||
var base = path.dirname(path.resolve(__dirname, pathToStatic));
|
||||
return '^' + pathName.replace(base, '') + (path.extname(pathName) ? '' : '/');
|
||||
return pathName.replace(base, '') + (path.extname(pathName) ? '' : '/');
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -58,7 +58,7 @@ var getViewPaths = function (routes) {
|
|||
* all :arguments become .+?
|
||||
*/
|
||||
var expressPatternToRegex = function (pattern) {
|
||||
return pattern.replace(/(:[^/]+)\//gi, '.+?/');
|
||||
return pattern.replace(/(:[^/]+)/gi, '.+?');
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -66,9 +66,9 @@ var expressPatternToRegex = function (pattern) {
|
|||
* string suitable for a Fastly condition
|
||||
*/
|
||||
var pathsToCondition = function (paths) {
|
||||
return 'req.url~"' + paths.reduce(function (conditionString, pattern) {
|
||||
return 'req.url~"^(' + paths.reduce(function (conditionString, pattern) {
|
||||
return conditionString + (conditionString ? '|' : '') + pattern;
|
||||
}, '') + '"';
|
||||
}, '') + ')"';
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
"devDependencies": {
|
||||
"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",
|
||||
"classnames": "2.1.3",
|
||||
"cookie": "0.2.2",
|
||||
"copy-webpack-plugin": "0.2.0",
|
||||
|
@ -51,7 +56,6 @@
|
|||
"iso-3166-2": "0.4.0",
|
||||
"json-loader": "0.5.2",
|
||||
"json2po-stream": "1.0.3",
|
||||
"jsx-loader": "0.13.2",
|
||||
"keymirror": "0.1.1",
|
||||
"lodash.clone": "3.0.3",
|
||||
"lodash.defaultsdeep": "3.10.0",
|
||||
|
|
|
@ -5,4 +5,117 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.validation-message {
|
||||
$arrow-border-width: 1rem;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translate(16rem, 0);
|
||||
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: "";
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
padding: 3rem 4rem;
|
||||
|
||||
.card-button {
|
||||
margin: 0 0 -3rem -4rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
&.has-error {
|
||||
.input {
|
||||
border: 1px solid $ui-orange;
|
||||
}
|
||||
}
|
||||
|
||||
.col-sm-9 {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
.validation-message {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@ var Checkbox = React.createClass({
|
|||
this.props.className
|
||||
);
|
||||
return (
|
||||
<div className={classes}>
|
||||
<FRCCheckbox {... this.props} />
|
||||
</div>
|
||||
<FRCCheckbox rowClassName={classes} {... this.props} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -29,14 +29,14 @@ var Input = React.createClass({
|
|||
},
|
||||
render: function () {
|
||||
var classes = classNames(
|
||||
'input',
|
||||
this.state.status,
|
||||
this.props.className
|
||||
this.props.className,
|
||||
{'no-label': (typeof this.props.label === 'undefined')}
|
||||
);
|
||||
return (this.props.type === 'submit' || this.props.noformsy ?
|
||||
<input {... this.props} className={classes} /> :
|
||||
return (
|
||||
<FRCInput {... this.props}
|
||||
className={classes}
|
||||
className="input"
|
||||
rowClassName={classes}
|
||||
onValid={this.onValid}
|
||||
onInvalid={this.onInvalid} />
|
||||
);
|
||||
|
|
|
@ -12,7 +12,7 @@ $pass-bg: lighten($ui-aqua, 35%);
|
|||
|
||||
.input {
|
||||
transition: all .5s ease;
|
||||
margin: .75rem 0;
|
||||
margin-bottom: .75rem;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
background-color: $base-bg;
|
||||
|
@ -35,4 +35,9 @@ $pass-bg: lighten($ui-aqua, 35%);
|
|||
border: 1px solid $active-dark-gray;
|
||||
background-color: $pass-bg;
|
||||
}
|
||||
|
||||
/* IE10/11-specific style resets */
|
||||
&::-ms-reveal, &::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ var PhoneInput = React.createClass({
|
|||
return (
|
||||
<Row {... this.getRowProperties()}
|
||||
htmlFor={this.getId()}
|
||||
className={classNames('phone-input', this.props.className)}
|
||||
rowClassName={classNames('phone-input', this.props.className)}
|
||||
>
|
||||
<div className="input-group">
|
||||
<ReactPhoneInput className="form-control"
|
||||
|
@ -53,9 +53,9 @@ var PhoneInput = React.createClass({
|
|||
label={null}
|
||||
disabled={this.isFormDisabled() || this.props.disabled}
|
||||
/>
|
||||
</div>
|
||||
{this.renderHelp()}
|
||||
{this.renderErrorMessage()}
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@import "../../colors";
|
||||
|
||||
.input-group {
|
||||
margin: .75rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.react-tel-input {
|
||||
margin-bottom: .75rem;
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
|
|
|
@ -4,8 +4,19 @@
|
|||
* the formsy-react-components
|
||||
*/
|
||||
|
||||
.form-group {
|
||||
.row {
|
||||
.required-symbol {
|
||||
display: none;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
|
||||
&.no-label {
|
||||
label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
select {
|
||||
transition: all .5s ease;
|
||||
margin: .75rem 0;
|
||||
margin-bottom: .75rem;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
background: $ui-light-gray url("../../../static/svgs/forms/carot.svg") no-repeat right center;
|
||||
|
@ -23,6 +23,12 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
&::-ms-value {
|
||||
background-color: inherit;
|
||||
padding-left: 1rem;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
transition: all .5s ease;
|
||||
outline: none;
|
||||
|
|
|
@ -11,11 +11,13 @@ var TextArea = React.createClass({
|
|||
type: 'TextArea',
|
||||
render: function () {
|
||||
var classes = classNames(
|
||||
'textarea',
|
||||
'textarea-row',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<FRCTextarea {... this.props} className={classes} />
|
||||
<FRCTextarea {... this.props}
|
||||
className="textarea"
|
||||
rowClassName={classes} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.textarea {
|
||||
transition: all 1s ease;
|
||||
margin: .75rem 0;
|
||||
margin-bottom: .75rem;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
background-color: $ui-light-gray;
|
||||
|
|
|
@ -11,6 +11,7 @@ var api = require('../../../lib/api');
|
|||
var Avatar = require('../../avatar/avatar.jsx');
|
||||
var Button = require('../../forms/button.jsx');
|
||||
var Dropdown = require('../../dropdown/dropdown.jsx');
|
||||
var Form = require('../../forms/form.jsx');
|
||||
var Input = require('../../forms/input.jsx');
|
||||
var log = require('../../../lib/log.js');
|
||||
var Login = require('../../login/login.jsx');
|
||||
|
@ -170,6 +171,9 @@ var Navigation = React.createClass({
|
|||
this.props.dispatch(sessionActions.refreshSession());
|
||||
this.closeRegistration();
|
||||
},
|
||||
onSearchSubmit: function (formData) {
|
||||
window.location.href = '/search/projects?q=' + formData.q;
|
||||
},
|
||||
render: function () {
|
||||
var classes = classNames({
|
||||
'logged-in': this.props.session.session.user
|
||||
|
@ -216,14 +220,13 @@ var Navigation = React.createClass({
|
|||
</li>
|
||||
|
||||
<li className="search">
|
||||
<form action="/search/projects" method="get">
|
||||
<Form onSubmit={this.onSearchSubmit}>
|
||||
<Button type="submit" className="btn-search" />
|
||||
<Input type="text"
|
||||
aria-label={formatMessage({id: 'general.search'})}
|
||||
placeholder={formatMessage({id: 'general.search'})}
|
||||
name="q"
|
||||
noformsy />
|
||||
</form>
|
||||
name="q" />
|
||||
</Form>
|
||||
</li>
|
||||
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
||||
this.props.session.session.user ? [
|
||||
|
|
|
@ -47,12 +47,18 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
form {
|
||||
.form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
.row {
|
||||
.help-block {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.input,
|
||||
.button {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
outline: 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,7 +24,26 @@ 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 getCountryOptions = function (defaultCountry) {
|
||||
var options = countryData.countryOptions.concat({
|
||||
label: <intl.FormattedMessage id="registration.selectCountry" />,
|
||||
disabled: true,
|
||||
selected: true
|
||||
});
|
||||
if (typeof defaultCountry !== 'undefined') {
|
||||
return options.sort(function (a, b) {
|
||||
if (a.disabled) return -1;
|
||||
if (b.disabled) return 1;
|
||||
if (a.value === defaultCountry) return -1;
|
||||
if (b.value === defaultCountry) return 1;
|
||||
return 0;
|
||||
}.bind(this));
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
var NextStepButton = React.createClass({
|
||||
getDefaultProps: function () {
|
||||
|
@ -34,7 +54,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
|
||||
|
@ -48,12 +68,13 @@ module.exports = {
|
|||
UsernameStep: intl.injectIntl(React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
showPassword: false,
|
||||
waiting: false
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
showPassword: false,
|
||||
showPassword: this.props.showPassword,
|
||||
waiting: false,
|
||||
validUsername: ''
|
||||
};
|
||||
|
@ -79,16 +100,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,15 +117,39 @@ 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>
|
||||
{this.props.title ? (
|
||||
this.props.title
|
||||
) : (
|
||||
<intl.FormattedMessage id="registration.usernameStepTitle" />
|
||||
)}
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.usernameStepDescription" />
|
||||
{this.props.description ? (
|
||||
this.props.description
|
||||
) : (
|
||||
<intl.FormattedMessage id="registration.usernameStepDescription" />
|
||||
)}
|
||||
{this.props.tooltip ? (
|
||||
<Tooltip title={'?'}
|
||||
tipContent={this.props.tooltip} />
|
||||
) : (
|
||||
null
|
||||
)}
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.onValidSubmit}>
|
||||
<Input label={formatMessage({id: 'general.createUsername'})}
|
||||
className={this.state.validUsername}
|
||||
<div>
|
||||
<div className="username-label">
|
||||
<b>{formatMessage({id: 'registration.createUsername'})}</b>
|
||||
{this.props.usernameHelp ? (
|
||||
<p className="help-text">{this.props.usernameHelp}</p>
|
||||
):(
|
||||
null
|
||||
)}
|
||||
</div>
|
||||
<Input className={this.state.validUsername}
|
||||
type="text"
|
||||
name="user.username"
|
||||
validations={{
|
||||
|
@ -114,16 +159,17 @@ 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 />
|
||||
</div>
|
||||
<Input label={formatMessage({id: 'general.password'})}
|
||||
type={this.state.showPassword ? 'text' : 'password'}
|
||||
name="user.password"
|
||||
|
@ -134,24 +180,86 @@ 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} />
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
})),
|
||||
ChoosePasswordStep: intl.injectIntl(React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
showPassword: false,
|
||||
waiting: false
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
showPassword: this.props.showPassword
|
||||
};
|
||||
},
|
||||
onChangeShowPassword: function (field, value) {
|
||||
this.setState({showPassword: value});
|
||||
},
|
||||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="registration-step choose-password-step">
|
||||
<h2>{formatMessage({id: 'registration.choosePasswordStepTitle'})}</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="registration.choosePasswordStepDescription" />
|
||||
<Tooltip title={'?'}
|
||||
tipContent={formatMessage({id: 'registration.choosePasswordStepTooltip'})} />
|
||||
</p>
|
||||
|
||||
<Card>
|
||||
<Form onValidSubmit={this.props.onNextStep}>
|
||||
<Input label={formatMessage({id: 'registration.newPassword'})}
|
||||
type={this.state.showPassword ? 'text' : 'password'}
|
||||
name="user.password"
|
||||
validations={{
|
||||
minLength: 6,
|
||||
notEquals: 'password',
|
||||
notEqualsField: 'user.username'
|
||||
}}
|
||||
validationErrors={{
|
||||
minLength: formatMessage({
|
||||
id: 'registration.validationPasswordLength'
|
||||
}),
|
||||
notEquals: formatMessage({
|
||||
id: 'registration.validationPasswordNotEquals'
|
||||
}),
|
||||
notEqualsField: formatMessage({
|
||||
id: 'registration.validationPasswordNotUsername'
|
||||
})
|
||||
}}
|
||||
required />
|
||||
<Checkbox label={formatMessage({id: 'registration.showPassword'})}
|
||||
value={this.state.showPassword}
|
||||
onChange={this.onChangeShowPassword}
|
||||
help={null}
|
||||
name="showPassword" />
|
||||
<NextStepButton waiting={this.props.waiting || this.state.waiting}
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />} />
|
||||
</Form>
|
||||
</Card>
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
|
@ -162,8 +270,8 @@ module.exports = {
|
|||
DemographicsStep: intl.injectIntl(React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
defaultCountry: DEFAULT_COUNTRY,
|
||||
waiting: false
|
||||
waiting: false,
|
||||
description: null
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
|
@ -175,7 +283,7 @@ module.exports = {
|
|||
'August', 'September', 'October', 'November', 'December'
|
||||
].map(function (label, id) {
|
||||
return {
|
||||
value: id+1,
|
||||
value: id + 1,
|
||||
label: this.props.intl.formatMessage({id: 'general.month' + label})};
|
||||
}.bind(this));
|
||||
},
|
||||
|
@ -191,14 +299,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}>
|
||||
|
@ -227,14 +339,13 @@ module.exports = {
|
|||
</div>
|
||||
<Select label={formatMessage({id: 'general.country'})}
|
||||
name="user.country"
|
||||
options={countryData.countryOptions}
|
||||
value={this.props.defaultCountry}
|
||||
options={getCountryOptions(DEFAULT_COUNTRY)}
|
||||
required />
|
||||
<Checkbox className="demographics-checkbox-is-robot"
|
||||
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 +362,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 +382,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 +408,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 +430,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 +484,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}>
|
||||
|
@ -394,7 +505,9 @@ module.exports = {
|
|||
required />
|
||||
<div className="organization-type">
|
||||
<b><intl.FormattedMessage id="teacherRegistration.orgType" /></b>
|
||||
<p><intl.FormattedMessage id="teacherRegistration.checkAll" /></p>
|
||||
<p className="help-text">
|
||||
<intl.FormattedMessage id="teacherRegistration.checkAll" />
|
||||
</p>
|
||||
<CheckboxGroup name="organization.type"
|
||||
value={[]}
|
||||
options={this.getOrganizationOptions()}
|
||||
|
@ -410,14 +523,16 @@ module.exports = {
|
|||
</div>
|
||||
<div className="url-input">
|
||||
<b><intl.FormattedMessage id="general.website" /></b>
|
||||
<p><intl.FormattedMessage id="teacherRegistration.notRequired" /></p>
|
||||
<p className="help-text">
|
||||
<intl.FormattedMessage id="teacherRegistration.notRequired" />
|
||||
</p>
|
||||
<Input type="url"
|
||||
name="organization.url"
|
||||
required="isFalse"
|
||||
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} />
|
||||
|
@ -475,32 +590,22 @@ module.exports = {
|
|||
var formatMessage = this.props.intl.formatMessage;
|
||||
var stateOptions = countryData.subdivisionOptions[this.state.countryChoice];
|
||||
stateOptions = [{}].concat(stateOptions);
|
||||
var countryOptions = countryData.countryOptions.concat({
|
||||
label: formatMessage({id: 'teacherRegistration.selectCountry'}),
|
||||
disabled: true,
|
||||
selected: true
|
||||
}).sort(function (a, b) {
|
||||
if (a.disabled) return -1;
|
||||
if (b.disabled) return 1;
|
||||
if (a.value === this.props.defaultCountry) return -1;
|
||||
if (b.value === this.props.defaultCountry) return 1;
|
||||
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}>
|
||||
<Select label={formatMessage({id: 'general.country'})}
|
||||
name="address.country"
|
||||
options={countryOptions}
|
||||
options={getCountryOptions()}
|
||||
value={this.props.defaultCountry}
|
||||
onChange={this.onChangeCountry}
|
||||
required />
|
||||
<Input label={formatMessage({id: 'teacherRegistration.addressLine1'})}
|
||||
|
@ -528,7 +633,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 +663,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 +690,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 +730,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 +757,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} />
|
||||
|
@ -664,12 +769,13 @@ module.exports = {
|
|||
getDefaultProps: function () {
|
||||
return {
|
||||
email: null,
|
||||
invited: false
|
||||
invited: false,
|
||||
confirmed: false
|
||||
};
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<Slide className="last-step">
|
||||
<Slide className="registration-step last-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="registration.lastStepTitle" />
|
||||
</h2>
|
||||
|
@ -707,15 +813,130 @@ module.exports = {
|
|||
);
|
||||
}
|
||||
})),
|
||||
ClassInviteNewStudentStep: intl.injectIntl(React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
waiting: false
|
||||
};
|
||||
},
|
||||
onNextStep: function () {
|
||||
this.props.onNextStep();
|
||||
},
|
||||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="registration-step class-invite-step">
|
||||
{this.props.waiting ? [
|
||||
<Spinner />
|
||||
] : [
|
||||
<Avatar className="invite-avatar"
|
||||
src={this.props.classroom.educator.profile.images['50x50']} />,
|
||||
<h2>{this.props.classroom.educator.username}</h2>,
|
||||
<p className="description">
|
||||
{formatMessage({id: 'registration.classroomInviteNewStudentStepDescription'})}
|
||||
</p>,
|
||||
<Card>
|
||||
<div className="contents">
|
||||
<h3>{this.props.classroom.title}</h3>
|
||||
<img className="class-image" src={this.props.classroom.images['250x150']} />
|
||||
</div>
|
||||
<NextStepButton onClick={this.onNextStep}
|
||||
waiting={this.props.waiting}
|
||||
text={formatMessage({id: 'general.getStarted'})} />
|
||||
</Card>,
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
]}
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
})),
|
||||
ClassInviteExistingStudentStep: intl.injectIntl(React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
classroom: null,
|
||||
onHandleLogOut: function () {},
|
||||
studentUsername: null,
|
||||
waiting: false
|
||||
};
|
||||
},
|
||||
onNextStep: function () {
|
||||
this.props.onNextStep();
|
||||
},
|
||||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="registration-step class-invite-step">
|
||||
{this.props.waiting ? [
|
||||
<Spinner />
|
||||
] : [
|
||||
<h2>{this.props.studentUsername}</h2>,
|
||||
<p className="description">
|
||||
{formatMessage({id: 'registration.classroomInviteExistingStudentStepDescription'})}
|
||||
</p>,
|
||||
<Card>
|
||||
<div className="contents">
|
||||
<h3>{this.props.classroom.title}</h3>
|
||||
<img className="class-image" src={this.props.classroom.images['250x150']} />
|
||||
<p>{formatMessage({id: 'registration.invitedBy'})}</p>
|
||||
<p><strong>{this.props.classroom.educator.username}</strong></p>
|
||||
</div>
|
||||
<NextStepButton onClick={this.onNextStep}
|
||||
waiting={this.props.waiting}
|
||||
text={formatMessage({id: 'general.getStarted'})} />
|
||||
</Card>,
|
||||
<p><a onClick={this.props.onHandleLogOut}>{formatMessage({id: 'registration.notYou'})}</a></p>,
|
||||
<StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
|
||||
]}
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
})),
|
||||
ClassWelcomeStep: intl.injectIntl(React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
waiting: false
|
||||
};
|
||||
},
|
||||
onNextStep: function () {
|
||||
this.props.onNextStep();
|
||||
},
|
||||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
<Slide className="registration-step class-welcome-step">
|
||||
{this.props.waiting ? [
|
||||
<Spinner />
|
||||
] : [
|
||||
<h2>{formatMessage({id: 'registration.welcomeStepTitle'})}</h2>,
|
||||
<p className="description">{formatMessage({id: 'registration.welcomeStepDescription'})}</p>,
|
||||
<Card>
|
||||
{this.props.classroom ? (
|
||||
<div className="contents">
|
||||
<h3>{this.props.classroom.title}</h3>
|
||||
<img className="class-image" src={this.props.classroom.images['250x150']} />
|
||||
<p>{formatMessage({id: 'registration.welcomeStepPrompt'})}</p>
|
||||
</div>
|
||||
) : (
|
||||
null
|
||||
)}
|
||||
<NextStepButton onClick={this.onNextStep}
|
||||
waiting={this.props.waiting}
|
||||
text={formatMessage({id: '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>
|
||||
<p>
|
||||
{this.props.registrationError}
|
||||
{this.props.children}
|
||||
</p>
|
||||
</Card>
|
||||
</Slide>
|
||||
|
|
216
src/components/registration/steps.scss
Normal file
216
src/components/registration/steps.scss
Normal file
|
@ -0,0 +1,216 @@
|
|||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
.help-text {
|
||||
margin: .25rem 0;
|
||||
text-align: left;
|
||||
color: $ui-dark-gray;
|
||||
}
|
||||
|
||||
&.class-invite-step {
|
||||
text-align: center;
|
||||
|
||||
> p a {
|
||||
text-decoration: underline;
|
||||
color: $ui-white;
|
||||
font-weight: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&.class-invite-step,
|
||||
&.class-welcome-step {
|
||||
.card {
|
||||
text-align: center;
|
||||
|
||||
.contents {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.username-step {
|
||||
.username-label {
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.demographics-step {
|
||||
.gender-input {
|
||||
margin-top: -5.5rem;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.organization-step {
|
||||
.checkbox-group {
|
||||
.validation-message {
|
||||
transform: translate(16rem, 8rem);
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
&[value="8"] {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.other-input {
|
||||
margin-top: -5.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.usescratch-step {
|
||||
.form {
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
|
||||
&.has-error {
|
||||
.textarea {
|
||||
border: 1px solid $ui-orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
&.phone-step {
|
||||
.checkbox,
|
||||
.validation-message {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.organization-step {
|
||||
.checkbox-group {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* IE10 and IE11 fallback */
|
||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||
.registration-step {
|
||||
&.username-step,
|
||||
&.demographics-step,
|
||||
&.name-step,
|
||||
&.phone-step,
|
||||
&.organization-step,
|
||||
&.address-step,
|
||||
&.email-step {
|
||||
.validation-message {
|
||||
position: relative;
|
||||
transform: none;
|
||||
margin: inherit;
|
||||
width: 100%;
|
||||
height: inherit;
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
@ -23,6 +22,7 @@
|
|||
"general.forParents": "For Parents",
|
||||
"general.forEducators": "For Educators",
|
||||
"general.forDevelopers": "For Developers",
|
||||
"general.getStarted": "Get Started",
|
||||
"general.gender": "Gender",
|
||||
"general.guidelines": "Community Guidelines",
|
||||
"general.help": "Help",
|
||||
|
@ -77,9 +77,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 +106,51 @@
|
|||
"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.choosePasswordStepDescription": "Type in a new password for your account. You will use this password the next time you log into Scratch.",
|
||||
"registration.choosePasswordStepTitle": "Create a password",
|
||||
"registration.choosePasswordStepTooltip": "Don't use your name or anything that's easy for someone else to guess.",
|
||||
"registration.classroomApiGeneralError": "Sorry, we could not find the registration information for this class",
|
||||
"registration.classroomInviteExistingStudentStepDescription": "you have been invited to join the class:",
|
||||
"registration.classroomInviteNewStudentStepDescription": "has invited you to join the class:",
|
||||
"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.invitedBy": "invited by",
|
||||
"registration.lastStepTitle": "Thank you for requesting a Scratch Teacher Account",
|
||||
"registration.lastStepDescription": "We are currently processing your application. ",
|
||||
"registration.mustBeNewStudent": "You must be a new student to complete your registration",
|
||||
"registration.nameStepTooltip": "This information is used for verification and to aggregate usage statistics.",
|
||||
"registration.newPassword": "New Password",
|
||||
"registration.nextStep": "Next Step",
|
||||
"registration.notYou": "Not you? Log in as another user",
|
||||
"registration.personalStepTitle": "Personal Information",
|
||||
"registration.personalStepDescription": "Your individual responses will not be displayed publicly, and will be kept confidential and secure",
|
||||
"registration.selectCountry": "select country",
|
||||
"registration.studentPersonalStepDescription": "This information will not appear on the Scratch website.",
|
||||
"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.studentUsernameStepDescription": "You can make games, animations, and stories using Scratch. Setting up an account is easy and it's free. Fill in the form below to get started.",
|
||||
"registration.studentUsernameStepHelpText": "Already have a Scratch account?",
|
||||
"registration.studentUsernameStepTitle": "Create a Scratch Account",
|
||||
"registration.studentUsernameStepTooltip": "You'll need to create a new Scratch account to join this class.",
|
||||
"registration.studentUsernameFieldHelpText": "For safety, don't use your real name!",
|
||||
"registration.usernameStepTitle": "Request a Teacher Account",
|
||||
"registration.usernameStepTitleScratcher": "Create a Scratch 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.waitForApprovalDescription": "You can log into your Scratch Account now, but the features specific to Teachers are not yet available. Your information is being reviewed. Please be patient, the approval process can take up to 24 hours. You will receive an email indicating your account has been upgraded once your account has been approved.",
|
||||
"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!"
|
||||
}
|
||||
|
|
|
@ -68,10 +68,23 @@ module.exports.refreshSession = function () {
|
|||
uri: '/session/'
|
||||
}, function (err, body) {
|
||||
if (err) return dispatch(module.exports.setSessionError(err));
|
||||
|
||||
if (typeof body !== 'undefined') {
|
||||
if (body.banned) {
|
||||
return window.location = body.url;
|
||||
if (typeof body === 'undefined') return dispatch(module.exports.setSessionError('No session content'));
|
||||
if (
|
||||
body.user &&
|
||||
body.user.banned &&
|
||||
window.location.pathname !== '/accounts/banned-response/') {
|
||||
return window.location = '/accounts/banned-response/';
|
||||
} else if (
|
||||
body.flags &&
|
||||
body.flags.must_complete_registration &&
|
||||
window.location.pathname !== '/classes/complete_registration') {
|
||||
return window.location = '/classes/complete_registration';
|
||||
} else if (
|
||||
body.flags &&
|
||||
body.flags.must_reset_password &&
|
||||
!body.flags.must_complete_registration &&
|
||||
window.location.pathname !== '/classes/student_password_reset/') {
|
||||
return window.location = '/classes/student_password_reset/';
|
||||
} else {
|
||||
dispatch(tokenActions.getToken());
|
||||
dispatch(module.exports.setSession(body));
|
||||
|
@ -81,7 +94,6 @@ module.exports.refreshSession = function () {
|
|||
dispatch(permissionsActions.getPermissions());
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
255
src/routes.json
255
src/routes.json
|
@ -2,124 +2,42 @@
|
|||
{
|
||||
"name": "splash",
|
||||
"pattern": "^/?$",
|
||||
"routeAlias": "/?$",
|
||||
"view": "splash/splash",
|
||||
"title": "Imagine, Program, Share"
|
||||
},
|
||||
{
|
||||
"name": "about",
|
||||
"pattern": "^/about/?$",
|
||||
"routeAlias": "/about/?$",
|
||||
"view": "about/about",
|
||||
"title": "About"
|
||||
},
|
||||
{
|
||||
"name": "developers",
|
||||
"pattern": "^/developers/?$",
|
||||
"view": "developers/developers",
|
||||
"title": "Developers"
|
||||
"name": "guidelines",
|
||||
"pattern": "^/community_guidelines/?$",
|
||||
"routeAlias": "/community_guidelines/?$",
|
||||
"view": "guidelines/guidelines",
|
||||
"title": "Scratch Community Guidelines"
|
||||
},
|
||||
{
|
||||
"name": "hoc",
|
||||
"pattern": "^/hoc/?$",
|
||||
"view": "hoc/hoc",
|
||||
"title": "Hour of Code"
|
||||
"name": "student-complete-registration",
|
||||
"pattern": "^/classes/complete_registration",
|
||||
"routeAlias": "/classes/(complete_registration|.+/register/.+)",
|
||||
"view": "studentcompleteregistration/studentcompleteregistration",
|
||||
"title": "Complete your Registration"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"routeAlias": "/classes/(complete_registration|.+/register/.+)",
|
||||
"view": "studentregistration/studentregistration",
|
||||
"title": "Class Registration"
|
||||
},
|
||||
{
|
||||
"name": "conference-index",
|
||||
"pattern": "^/conference/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"routeAlias": "/conference(?!/201[4-5])",
|
||||
"view": "conference/index/index",
|
||||
"title": "Scratch Conference",
|
||||
"viewportWidth": "device-width"
|
||||
|
@ -127,7 +45,7 @@
|
|||
{
|
||||
"name": "conference-plan",
|
||||
"pattern": "^/conference/plan/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"routeAlias": "/conference(?!/201[4-5])",
|
||||
"view": "conference/plan/plan",
|
||||
"title": "Plan Your Visit",
|
||||
"viewportWidth": "device-width"
|
||||
|
@ -135,7 +53,7 @@
|
|||
{
|
||||
"name": "conference-expectations",
|
||||
"pattern": "^/conference/expect/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"routeAlias": "/conference(?!/201[4-5])",
|
||||
"view": "conference/expect/expect",
|
||||
"title": "What to Expect",
|
||||
"viewportWidth": "device-width"
|
||||
|
@ -143,7 +61,7 @@
|
|||
{
|
||||
"name": "conference-schedule",
|
||||
"pattern": "^/conference/schedule/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"routeAlias": "/conference(?!/201[4-5])",
|
||||
"view": "conference/schedule/schedule",
|
||||
"title": "Conference Schedule",
|
||||
"viewportWidth": "device-width"
|
||||
|
@ -151,38 +69,153 @@
|
|||
{
|
||||
"name": "conference-details",
|
||||
"pattern": "^/conference/:id/details/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"routeAlias": "/conference(?!/201[4-5])",
|
||||
"view": "conference/details/details",
|
||||
"title": "Event Details",
|
||||
"viewportWidth": "device-width"
|
||||
},
|
||||
{
|
||||
"name": "donate",
|
||||
"pattern": "^/info/donate/?",
|
||||
"redirect": "https://secure.donationpay.org/scratchfoundation/"
|
||||
"name": "developers",
|
||||
"pattern": "^/developers/?$",
|
||||
"routeAlias": "/developers/?$",
|
||||
"view": "developers/developers",
|
||||
"title": "Developers"
|
||||
},
|
||||
{
|
||||
"name": "dmca",
|
||||
"pattern": "^/DMCA/?$",
|
||||
"routeAlias": "/DMCA/?$",
|
||||
"view": "dmca/dmca",
|
||||
"title": "DMCA"
|
||||
},
|
||||
{
|
||||
"name": "guidelines",
|
||||
"pattern": "^/community_guidelines/?$",
|
||||
"view": "guidelines/guidelines",
|
||||
"title": "Scratch Community Guidelines"
|
||||
"name": "educator-landing",
|
||||
"pattern": "^/educators/?$",
|
||||
"routeAlias": "/educators(?:/(faq|register|waiting))?/?$",
|
||||
"view": "teachers/landing/landing",
|
||||
"title": "Educators"
|
||||
},
|
||||
{
|
||||
"name": "teacher-faq",
|
||||
"pattern": "^/educators/faq/?$",
|
||||
"routeAlias": "/educators(?:/(faq|register|waiting))?/?$",
|
||||
"view": "teachers/faq/faq",
|
||||
"title": "Teacher Accounts FAQ"
|
||||
},
|
||||
{
|
||||
"name": "teacherregistration",
|
||||
"pattern": "^/educators/register$",
|
||||
"routeAlias": "/educators(?:/(faq|register|waiting))?/?$",
|
||||
"view": "teacherregistration/teacherregistration",
|
||||
"title": "Teacher Registration",
|
||||
"viewportWidth": "device-width"
|
||||
},
|
||||
{
|
||||
"name": "teacherwaitingroom",
|
||||
"pattern": "^/educators/waiting",
|
||||
"routeAlias": "/educators(?:/(faq|register|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/?$",
|
||||
"routeAlias": "/hoc/?$",
|
||||
"view": "hoc/hoc",
|
||||
"title": "Hour of Code"
|
||||
},
|
||||
{
|
||||
"name": "cards",
|
||||
"pattern": "^/info/cards/?$",
|
||||
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq)/?$",
|
||||
"view": "cards/cards",
|
||||
"title": "Cards"
|
||||
},
|
||||
{
|
||||
"name": "communityblocks-interviews",
|
||||
"pattern": "^/info/communityblocks-interviews/?$",
|
||||
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq|donate)/?$",
|
||||
"view": "communityblocks-interviews/communityblocks-interviews",
|
||||
"title": "Community Blocks Beta Tester Interviews"
|
||||
},
|
||||
{
|
||||
"name": "credits",
|
||||
"pattern": "^/info/credits/?$",
|
||||
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq)/?$",
|
||||
"view": "credits/credits",
|
||||
"title": "Credits"
|
||||
},
|
||||
{
|
||||
"name": "faq",
|
||||
"pattern": "^/info/faq/?$",
|
||||
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq)/?$",
|
||||
"view": "faq/faq",
|
||||
"title": "FAQ"
|
||||
},
|
||||
{
|
||||
"name": "jobs",
|
||||
"pattern": "^/jobs/?$",
|
||||
"routeAlias": "/jobs/?$",
|
||||
"view": "jobs/jobs",
|
||||
"title": "Jobs"
|
||||
},
|
||||
{
|
||||
"name": "privacypolicy",
|
||||
"pattern": "^/privacy_policy/?$",
|
||||
"routeAlias": "/privacy_policy/?$",
|
||||
"view": "privacypolicy/privacypolicy",
|
||||
"title": "Privacy Policy"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"pattern": "^/search/:projects?$/?$",
|
||||
"routeAlias": "/search",
|
||||
"view": "search/search",
|
||||
"title": "Search"
|
||||
},
|
||||
{
|
||||
"name": "terms",
|
||||
"pattern": "^/terms_of_use/?$",
|
||||
"routeAlias": "/terms_of_use/?$",
|
||||
"view": "terms/terms",
|
||||
"title": "Scratch Terms of Use"
|
||||
},
|
||||
{
|
||||
"name": "wedo2",
|
||||
"pattern": "^/wedo/?$",
|
||||
"routeAlias": "/wedo/?$",
|
||||
"view": "wedo2/wedo2",
|
||||
"title": "LEGO WeDo 2.0"
|
||||
},
|
||||
{
|
||||
"name": "donate",
|
||||
"pattern": "^/info/donate/?",
|
||||
"routeAlias": "/info/(cards|communityblocks-interviews|credits|faq|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"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
var connect = require('react-redux').connect;
|
||||
var defaults = require('lodash.defaultsdeep');
|
||||
var React = require('react');
|
||||
var render = require('../../lib/render.jsx');
|
||||
|
||||
var sessionStatus = require('../../redux/session').Status;
|
||||
var api = require('../../lib/api');
|
||||
var intl = require('../../lib/intl.jsx');
|
||||
var log = require('../../lib/log.js');
|
||||
|
||||
var Deck = require('../../components/deck/deck.jsx');
|
||||
var Progression = require('../../components/progression/progression.jsx');
|
||||
var Spinner = require('../../components/spinner/spinner.jsx');
|
||||
var Steps = require('../../components/registration/steps.jsx');
|
||||
|
||||
require('./studentcompleteregistration.scss');
|
||||
|
||||
var StudentCompleteRegistration = intl.injectIntl(React.createClass({
|
||||
type: 'StudentCompleteRegistration',
|
||||
getInitialState: function () {
|
||||
return {
|
||||
classroom: null,
|
||||
formData: {},
|
||||
registrationErrors: null,
|
||||
step: 0,
|
||||
waiting: false
|
||||
};
|
||||
},
|
||||
advanceStep: function (formData) {
|
||||
formData = formData || {};
|
||||
this.setState({
|
||||
step: this.state.step + 1,
|
||||
formData: defaults({}, formData, this.state.formData)
|
||||
});
|
||||
},
|
||||
componentDidUpdate: function (prevProps) {
|
||||
if (prevProps.studentUsername !== this.props.studentUsername && this.props.newStudent) {
|
||||
this.setState({waiting: true});
|
||||
api({
|
||||
uri: '/classrooms/' + this.props.classroomId
|
||||
}, function (err, body, res) {
|
||||
this.setState({waiting: false});
|
||||
if (err || res.statusCode !== 200) {
|
||||
return this.setState({
|
||||
registrationErrors: {
|
||||
__all__: this.props.intl.formatMessage({id: 'registration.classroomApiGeneralError'})
|
||||
}
|
||||
});
|
||||
}
|
||||
this.setState({classroom: body});
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
handleLogOut: function (e) {
|
||||
e.preventDefault();
|
||||
api({
|
||||
host: '',
|
||||
method: 'post',
|
||||
uri: '/accounts/logout/',
|
||||
useCsrf: true
|
||||
}, function (err) {
|
||||
if (err) return log.error(err);
|
||||
window.location = '/';
|
||||
}.bind(this));
|
||||
},
|
||||
register: function (formData) {
|
||||
this.setState({waiting: true});
|
||||
formData = defaults({}, formData || {}, this.state.formData);
|
||||
var submittedData = {
|
||||
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
|
||||
};
|
||||
if (this.props.must_reset_password) {
|
||||
submittedData.password = formData.user.password;
|
||||
}
|
||||
api({
|
||||
host: '',
|
||||
uri: '/classes/student_update_registration/',
|
||||
method: 'post',
|
||||
useCsrf: true,
|
||||
formData: submittedData
|
||||
}, function (err, body) {
|
||||
this.setState({waiting: false});
|
||||
if (err) return this.setState({registrationError: err});
|
||||
if (body.success) return this.advanceStep(formData);
|
||||
this.setState({registrationErrors: body.errors});
|
||||
}.bind(this));
|
||||
},
|
||||
goToClass: function () {
|
||||
window.location = '/classes/' + this.state.classroom.id + '/';
|
||||
},
|
||||
render: function () {
|
||||
var demographicsDescription = this.props.intl.formatMessage({
|
||||
id: 'registration.studentPersonalStepDescription'});
|
||||
var registrationErrors = this.state.registrationErrors;
|
||||
if (!this.props.newStudent) {
|
||||
registrationErrors = {
|
||||
__all__: this.props.intl.formatMessage({id: 'registration.mustBeNewStudent'})
|
||||
};
|
||||
}
|
||||
return (
|
||||
<Deck className="student-registration">
|
||||
{registrationErrors ? (
|
||||
<Steps.RegistrationError>
|
||||
<ul>
|
||||
{Object.keys(registrationErrors).map(function (field) {
|
||||
var label = field + ': ';
|
||||
if (field === '__all__') {
|
||||
label = '';
|
||||
}
|
||||
return (<li>{label}{registrationErrors[field]}</li>);
|
||||
})}
|
||||
</ul>
|
||||
</Steps.RegistrationError>
|
||||
) : (
|
||||
this.state.waiting || !this.state.classroom ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<Progression {... this.state}>
|
||||
<Steps.ClassInviteExistingStudentStep classroom={this.state.classroom}
|
||||
onHandleLogOut={this.handleLogOut}
|
||||
onNextStep={this.advanceStep}
|
||||
studentUsername={this.props.studentUsername}
|
||||
waiting={this.state.waiting} />
|
||||
{this.props.must_reset_password ?
|
||||
<Steps.ChoosePasswordStep onNextStep={this.advanceStep}
|
||||
showPassword={true}
|
||||
waiting={this.state.waiting} />
|
||||
:
|
||||
[]
|
||||
}
|
||||
<Steps.DemographicsStep description={demographicsDescription}
|
||||
onNextStep={this.register}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.ClassWelcomeStep classroom={this.state.classroom}
|
||||
onNextStep={this.goToClass}
|
||||
waiting={this.state.waiting} />
|
||||
</Progression>
|
||||
)
|
||||
)}
|
||||
</Deck>
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
var mapStateToProps = function (state) {
|
||||
return {
|
||||
classroomId: state.session.session.user && state.session.session.user.classroomId,
|
||||
must_reset_password: state.session.session.flags && state.session.session.flags.must_reset_password,
|
||||
newStudent: (
|
||||
state.session.session.permissions &&
|
||||
state.session.session.permissions.student &&
|
||||
state.session.session.flags.must_complete_registration),
|
||||
sessionFetched: state.session.status === sessionStatus.FETCHED,
|
||||
studentUsername: state.session.session.user && state.session.session.user.username
|
||||
};
|
||||
};
|
||||
|
||||
var ConnectedStudentCompleteRegistration = connect(mapStateToProps)(StudentCompleteRegistration);
|
||||
|
||||
render(<ConnectedStudentCompleteRegistration />, document.getElementById('app'));
|
|
@ -0,0 +1,13 @@
|
|||
@import "../../colors";
|
||||
@import "../../frameless";
|
||||
|
||||
@include responsive-layout (".student-registration", ".slide");
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: darken($ui-purple, 8%);
|
||||
}
|
||||
|
||||
.student-complete-registration {
|
||||
background-color: $ui-purple;
|
||||
}
|
139
src/views/studentregistration/studentregistration.jsx
Normal file
139
src/views/studentregistration/studentregistration.jsx
Normal file
|
@ -0,0 +1,139 @@
|
|||
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 () {
|
||||
this.setState({waiting: true});
|
||||
api({
|
||||
uri: '/classrooms/' + this.props.classroomId,
|
||||
params: {token: this.props.classroomToken}
|
||||
}, function (err, body, res) {
|
||||
this.setState({waiting: false});
|
||||
if (err) {
|
||||
return this.setState({
|
||||
registrationError: this.props.intl.formatMessage({
|
||||
id: 'registration.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.studentPersonalStepDescription'});
|
||||
var usernameTitle = this.props.intl.formatMessage({id: 'registration.studentUsernameStepTitle'});
|
||||
var usernameHelp = this.props.intl.formatMessage({id: 'registration.studentUsernameFieldHelpText'});
|
||||
var usernameDescription = (
|
||||
this.props.intl.formatMessage({id: 'registration.studentUsernameStepDescription'}) + ' ' +
|
||||
this.props.intl.formatMessage({id: 'registration.studentUsernameStepHelpText'})
|
||||
);
|
||||
var usernameTooltip = this.props.intl.formatMessage({id: 'registration.studentUsernameStepTooltip'});
|
||||
return (
|
||||
<Deck className="student-registration">
|
||||
{this.state.registrationError ?
|
||||
<Steps.RegistrationError>
|
||||
{this.state.registrationError}
|
||||
</Steps.RegistrationError>
|
||||
:
|
||||
<Progression {... this.state}>
|
||||
<Steps.ClassInviteNewStudentStep classroom={this.state.classroom}
|
||||
onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting || !this.state.classroom} />
|
||||
<Steps.UsernameStep onNextStep={this.advanceStep}
|
||||
title={usernameTitle}
|
||||
description={usernameDescription}
|
||||
tooltip={usernameTooltip}
|
||||
usernameHelp={usernameHelp}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.DemographicsStep description={demographicsDescription}
|
||||
onNextStep={this.register}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.ClassWelcomeStep classroom={this.state.classroom}
|
||||
onNextStep={this.goToClass}
|
||||
waiting={this.state.waiting || !this.state.classroom} />
|
||||
</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%);
|
||||
}
|
||||
|
||||
.student-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 characters 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",
|
||||
|
@ -32,13 +19,12 @@
|
|||
"teacherRegistration.orgChoiceMiddleSchool": "Middle School",
|
||||
"teacherRegistration.orgChoiceHighSchool": "High School",
|
||||
"teacherRegistration.orgChoiceUniversity": "College/University",
|
||||
"teacherRegistration.orgChoiceAfterschool": "Afterscool Program",
|
||||
"teacherRegistration.orgChoiceAfterschool": "Afterschool Program",
|
||||
"teacherRegistration.orgChoiceMuseum": "Museum",
|
||||
"teacherRegistration.orgChoiceLibrary": "Library",
|
||||
"teacherRegistration.orgChoiceCamp": "Camp",
|
||||
"teacherRegistration.orgChoiceOther": " ",
|
||||
"teacherRegistration.notRequired": "Not Required",
|
||||
"teacherRegistration.selectCountry": "select country",
|
||||
"teacherRegistration.addressValidationError": "This doesn't look like a real address",
|
||||
"teacherRegistration.addressLine1": "Address Line 1",
|
||||
"teacherRegistration.addressLine2": "Address Line 2 (Optional)",
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
var connect = require('react-redux').connect;
|
||||
var defaults = require('lodash.defaultsdeep');
|
||||
var React = require('react');
|
||||
var render = require('../../lib/render.jsx');
|
||||
|
||||
var api = require('../../lib/api');
|
||||
var sessionActions = require('../../redux/session.js');
|
||||
|
||||
var Deck = require('../../components/deck/deck.jsx');
|
||||
var Progression = require('../../components/progression/progression.jsx');
|
||||
|
@ -67,16 +69,22 @@ var TeacherRegistration = React.createClass({
|
|||
}, function (err, res) {
|
||||
this.setState({waiting: false});
|
||||
if (err) return this.setState({registrationError: err});
|
||||
if (res[0].success) return this.advanceStep(formData);
|
||||
if (res[0].success) {
|
||||
this.props.dispatch(sessionActions.refreshSession());
|
||||
return this.advanceStep(formData);
|
||||
}
|
||||
this.setState({registrationError: res[0].msg});
|
||||
}.bind(this));
|
||||
|
||||
},
|
||||
render: function () {
|
||||
var permissions = this.props.session.permissions || {};
|
||||
return (
|
||||
<Deck className="teacher-registration">
|
||||
{this.state.registrationError ?
|
||||
<Steps.RegistrationError {... this.state} />
|
||||
<Steps.RegistrationError>
|
||||
{this.state.registrationError}
|
||||
</Steps.RegistrationError>
|
||||
:
|
||||
<Progression {... this.state}>
|
||||
<Steps.UsernameStep onNextStep={this.advanceStep}
|
||||
|
@ -101,7 +109,10 @@ var TeacherRegistration = React.createClass({
|
|||
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} />
|
||||
<Steps.TeacherApprovalStep email={this.state.formData.user && this.state.formData.user.email}
|
||||
confirmed={permissions.social}
|
||||
invited={permissions.educator_invitee}
|
||||
educator={permissions.educator} />
|
||||
</Progression>
|
||||
}
|
||||
</Deck>
|
||||
|
@ -109,4 +120,12 @@ var TeacherRegistration = React.createClass({
|
|||
}
|
||||
});
|
||||
|
||||
render(<TeacherRegistration />, document.getElementById('app'));
|
||||
var mapStateToProps = function (state) {
|
||||
return {
|
||||
session: state.session.session
|
||||
};
|
||||
};
|
||||
|
||||
var ConnectedTeacherRegistration = connect(mapStateToProps)(TeacherRegistration);
|
||||
|
||||
render(<ConnectedTeacherRegistration />, document.getElementById('app'));
|
||||
|
|
|
@ -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 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
{
|
||||
"teacherlanding.title": "Scratch for Educators",
|
||||
"teacherlanding.intro": "Your students can use Scratch to code their own interactive stories, animations, and games. In the process, they learn to think creatively, reason systematically, and work collaboratively — essential skills for everyone in today’s society.",
|
||||
"teacherlanding.inPracticeAnchor": "In Practice",
|
||||
"teacherlanding.inPracticeAnchor": "Who Uses Scratch?",
|
||||
"teacherlanding.resourcesAnchor": "Resources",
|
||||
"teacherlanding.inPracticeTitle": "Who Uses Scratch?",
|
||||
"teacherlanding.inPracticeIntro": "Educators are using Scratch in a wide variety of: ",
|
||||
"teacherlanding.generalUsageSettings": "<b>Settings:</b> schools, museums, libraries, community centers",
|
||||
"teacherlanding.generalUsageGradeLevels": "<b>Grade Levels:</b> elementary, middle, and high school (and some colleges too!)",
|
||||
"teacherlanding.generalUsageSubjectAreas": "<b>Subject Areas:</b> language arts, science, social studies, math, computer science, foreign languages, and the arts",
|
||||
"teacherlanding.ingridTitle": "Instructional Technology Specialist",
|
||||
"teacherlanding.dylanTitle": "Educational Technologist",
|
||||
"teacherlanding.afterSchoolTitle": "After-School Program",
|
||||
"teacherlanding.resourcesTitle": "Educator Resources",
|
||||
"teacherlanding.scratchEdTitle": "A Community for Educators",
|
||||
"teacherlanding.scratchEdDescription": "<a href=\"http://scratched.gse.harvard.edu/\">ScratchEd</a> is an online community where Scratch educators <a href=\"http://scratched.gse.harvard.edu/stories\">share stories</a>, exchange resources, ask questions, and find people. ScratchEd is developed and supported by the Harvard Graduate School of Education.",
|
||||
"teacherlanding.meetupTitle": "In-Person Gatherings",
|
||||
"teacherlanding.meetupDescription": "<a href=\"http://www.meetup.com/pro/scratched/\">Scratch Educator Meetups</a> from each other, sharing their ideas and strategies for supporting computational creativity in all its forms.",
|
||||
"teacherlanding.meetupDescription": "<a href=\"http://www.meetup.com/pro/scratched/\">Scratch Educator Meetups</a> are gatherings of Scratch Educators who want to learn with and from each other, sharing their ideas and strategies for supporting computational creativity in all its forms.",
|
||||
"teacherlanding.guidesTitle": "Guides & Tutorials",
|
||||
"teacherlanding.helpPage": "On the <a href=\"/help\">Help Page</a>, you can find workshop guides, Scratch Cards, videos, and other resources.",
|
||||
"teacherlanding.tipsWindow" : "The <a href=\"/projects/editor/?tip_bar=home\">Tips Window</a> features step-by-step tutorials for getting started in Scratch.",
|
||||
|
|
|
@ -61,38 +61,6 @@ var Landing = injectIntl(React.createClass({
|
|||
<p><FormattedHTMLMessage id="teacherlanding.generalUsageGradeLevels" /></p>
|
||||
<p><FormattedHTMLMessage id="teacherlanding.generalUsageSubjectAreas"/></p>
|
||||
</FlexRow>
|
||||
<FlexRow className="stories">
|
||||
<a href="//bit.ly/28SBsa9" className="story">
|
||||
<img src="/images/teachers/stories/ingrid.jpg" alt="ingrid's story" />
|
||||
<div className="story-info">
|
||||
<p className="name">Ingrid Gustafson</p>
|
||||
<p><FormattedMessage id="teacherlanding.ingridTitle" /></p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="//bit.ly/28Q5l6P" className="story">
|
||||
<img src="/images/teachers/stories/dylan.jpg" alt="dylan's story" />
|
||||
<div className="story-info">
|
||||
<p className="name">Dylan Ryder</p>
|
||||
<p><FormattedMessage id="teacherlanding.dylanTitle" /></p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="//bit.ly/28SC1AY" className="story">
|
||||
<img src="/images/teachers/stories/plug-in-studio.jpg"
|
||||
alt="plug in studio's story" />
|
||||
<div className="story-info">
|
||||
<p className="name">Plug-In Studios</p>
|
||||
<p><FormattedMessage id="teacherlanding.afterSchoolTitle" /></p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="//bit.ly/28UzapJ" className="story">
|
||||
<img src="/images/teachers/stories/ghana-code-club.jpg"
|
||||
alt="ghana code club's story" />
|
||||
<div className="story-info">
|
||||
<p className="name">Ghana Code Club</p>
|
||||
<p><FormattedMessage id="teacherlanding.afterSchoolTitle" /></p>
|
||||
</div>
|
||||
</a>
|
||||
</FlexRow>
|
||||
</section>
|
||||
<section id="resources">
|
||||
<span className="nav-spacer"></span>
|
||||
|
|
|
@ -10,6 +10,11 @@ require('./teacherwaitingroom.scss');
|
|||
|
||||
var TeacherWaitingRoom = React.createClass({
|
||||
displayName: 'TeacherWaitingRoom',
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
if (nextProps.session.permissions.educator && nextProps.session.permissions.social) {
|
||||
window.location.href = '/educators/classes/';
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
var permissions = this.props.session.permissions || {};
|
||||
var user = this.props.session.user || {};
|
||||
|
|
Binary file not shown.
Before ![]() (image error) Size: 345 KiB After ![]() (image error) Size: 356 KiB ![]() ![]() |
|
@ -68,7 +68,10 @@ module.exports = {
|
|||
loaders: [
|
||||
{
|
||||
test: /\.jsx$/,
|
||||
loader: 'jsx-loader',
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['es2015','react']
|
||||
},
|
||||
include: path.resolve(__dirname, 'src')
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue