Skip to content

Commit

Permalink
Merge pull request #23560 from code-dot-org/dtl_candidate_7bc5e393
Browse files Browse the repository at this point in the history
  • Loading branch information
deploy-code-org committed Jul 6, 2018
2 parents 26c0582 + 7bc5e39 commit cbace27
Show file tree
Hide file tree
Showing 80 changed files with 2,235 additions and 493 deletions.
2 changes: 2 additions & 0 deletions apps/Gruntfile.js
Expand Up @@ -513,6 +513,8 @@ describe('entry tests', () => {
'pd/professional_learning_landing/index': './src/sites/studio/pages/pd/professional_learning_landing/index.js',
'pd/regional_partner_contact/new': './src/sites/studio/pages/pd/regional_partner_contact/new.js',

'pd/international_opt_in/new': './src/sites/studio/pages/pd/international_opt_in/new.js',

'peer_reviews/dashboard': './src/sites/studio/pages/peer_reviews/dashboard.js',

'code.org/public/teacher-dashboard/index': './src/sites/code.org/pages/public/teacher-dashboard/index.js',
Expand Down
7 changes: 7 additions & 0 deletions apps/i18n/common/en_us.json
Expand Up @@ -29,6 +29,7 @@
"allStudents": "All",
"allowEditing": "Allow editing",
"allowEditingInstructions": "\"Allow editing\" while students should be taking the assessment.",
"allStudents": "All students",
"and": "and",
"animationMode": "Animation",
"announcements": "Announcements",
Expand Down Expand Up @@ -479,6 +480,7 @@
"emptyBlockInVariable": "The variable {name} has an unfilled input.",
"emptyBlocksErrorMsg": "The \"Repeat\" or \"If\" block needs to have other blocks inside it to work. Make sure the inner block fits properly inside the containing block.",
"emptyExampleBlockErrorMsg": "You need at least two examples in function {functionName}. Make sure each example has a call and a result.",
"emptyFreeResponse": "No response given for this question",
"emptyFunctionBlocksErrorMsg": "The function block needs to have other blocks inside it to work.",
"emptyFunctionalBlock": "You have a block with an unfilled input.",
"emptySurveyOverviewTable": "Because this survey is anonymous, we can only show aggregated results once there are at least 5 submissions.",
Expand All @@ -493,6 +495,7 @@
"enableMakerDialogDescription": "Maker Toolkit is a feature used in the Computer Science Discoveries curriculum. See the setup page for more details:",
"enableMakerDialogSetupPageLinkText": "Maker Toolkit Setup",
"enablePairProgramming": "Enable Pair Programming",
"encrypted": "encrypted",
"end": "end",
"englishOnlyWarning": "Sorry! This stage is not available in your language. The puzzles in this stage use a mix of English words and characters that can’t be translated right now. You can move on to Stage {nextStage}.",
"enterSectionCode": "Enter section code",
Expand Down Expand Up @@ -543,6 +546,8 @@
"findLocalClassDescription": "Find a local after-school program, summer camp, or school to learn in person.",
"findLocalClassButton": "Find a class",
"finish": "Finish",
"formErrorsBelow": "Please correct the errors below.",
"formServerError": "Something went wrong on our end; please try again later.",
"forTeachersOnly": "For Teachers Only",
"fromWhen": "(From {when}):",
"gdprDialogHeader": "Do you agree to using a website based in the United States?",
Expand Down Expand Up @@ -1019,7 +1024,9 @@
"secret": "Secret",
"seeAllTutorials": "See all tutorials",
"selectACourse": "Select a course or unit",
"selectAnOption": "Please select an option...",
"selectAssessment": "Select an assessment or survey",
"selectStudent": "Filter by student",
"selectGoogleClassroom": "Select a Google Classroom",
"selectCleverSection": "Select a Clever section",
"selectSection": "Select Section",
Expand Down
7 changes: 4 additions & 3 deletions apps/src/code-studio/pd/form_components/FormController.jsx
Expand Up @@ -6,6 +6,7 @@ import {
FormGroup,
Pagination,
} from 'react-bootstrap';
import i18n from '@cdo/locale';

const styles = {
pageButtons: {
Expand Down Expand Up @@ -211,14 +212,14 @@ export default class FormController extends React.Component {
// and display the generic error header
this.setState({
errors: data.responseJSON.errors.form_data,
errorHeader: "Please correct the errors below."
errorHeader: i18n.formErrorsBelow()
});
}
} else {
// Otherwise, something unknown went wrong on the server
this.setState({
globalError: true,
errorHeader: "Something went wrong on our end; please try again later."
errorHeader: i18n.formServerError()
});
}
this.setState({
Expand Down Expand Up @@ -405,7 +406,7 @@ export default class FormController extends React.Component {
shouldShowSubmit() {
return this.state.currentPage === this.getPageComponents().length - 1;
}
static submitButtonText = "Submit";
static submitButtonText = i18n.submit();

/**
* @returns {Element}
Expand Down
268 changes: 268 additions & 0 deletions apps/src/code-studio/pd/international_opt_in/InternationalOptIn.jsx
@@ -0,0 +1,268 @@
import React, {PropTypes} from 'react';
import FormController from '../form_components/FormController';
import FormComponent from '../form_components/FormComponent';
import DatePicker from '../workshop_dashboard/components/date_picker';
import moment from 'moment';
import {DATE_FORMAT} from '../workshop_dashboard/workshopConstants';
import {
Row,
Col,
ControlLabel,
FormGroup
} from 'react-bootstrap';
import i18n from '@cdo/locale';

export default class InternationalOptIn extends FormController {
static propTypes = {
accountEmail: PropTypes.string.isRequired,
labels: PropTypes.object.isRequired
};

/**
* @override
*/
onSuccessfulSubmit(data) {
window.location = `/pd/international_workshop/${data.id}/thanks`;
}

/**
* @override
*/
serializeFormData() {
const formData = super.serializeFormData();
formData.form_data.email = this.props.accountEmail;
return formData;
}

/**
* @override
*/
getPageComponents() {
return [
InternationalOptInComponent
];
}

/**
* @override
*/
getPageProps() {
return {
...super.getPageProps(),
accountEmail: this.props.accountEmail,
labels: this.props.labels
};
}
}


class InternationalOptInComponent extends FormComponent {
static propTypes = {
accountEmail: PropTypes.string.isRequired
};

handleDateChange = (date) => {
// Don't allow null. If the date is cleared, default again to today.
date = date || moment();
super.handleChange({date: date.format(DATE_FORMAT)});
};

render() {
const labels = this.props.labels;
const date = (this.props.data && this.props.data.date) ?
moment(this.props.data.date, DATE_FORMAT) : moment();

const lastSubjectsKey = this.props.options.subjects.slice(-1)[0];
const textFieldMapSubjects = {[lastSubjectsKey]: "other"};

const lastResourcesKey = this.props.options.resources.slice(-1)[0];
const textFieldMapResources = {[lastResourcesKey]: "other"};

const lastRoboticsKey = this.props.options.robotics.slice(-1)[0];
const textFieldMapRobotics = {[lastRoboticsKey]: "other"};

return (
<FormGroup>
{
this.buildFieldGroup({
name: 'firstName',
label: labels.firstName,
type: 'text',
required: true
})
}
{
this.buildFieldGroup({
name: 'firstNamePreferred',
label: labels.firstNamePreferred,
type: 'text',
required: false
})
}
{
this.buildFieldGroup({
name: 'lastName',
label: labels.lastName,
type: 'text',
required: true
})
}
{
this.buildFieldGroup({
name: 'email',
label: labels.email,
type: 'text',
value: this.props.accountEmail,
readOnly: true
})
}
{
this.buildFieldGroup({
name: 'emailAlternate',
label: labels.emailAlternate,
type: 'text'
})
}
{
this.buildButtonsFromOptions({
name: 'gender',
label: labels.gender,
type: 'radio',
required: true
})
}
{
this.buildFieldGroup({
name: 'schoolName',
label: labels.schoolName,
type: 'text',
required: true
})
}
{
this.buildFieldGroup({
name: 'schoolCity',
label: labels.schoolCity,
type: 'text',
required: true
})
}
{
this.buildButtonsFromOptions({
name: 'schoolCountry',
label: labels.schoolCountry,
type: 'radio',
required: true
})
}
{
this.buildButtonsFromOptions({
name: 'ages',
label: labels.ages,
type: 'check',
required: true
})
}
{
this.buildButtonsWithAdditionalTextFieldsFromOptions({
name: 'subjects',
label: labels.subjects,
type: 'check',
required: true,
textFieldMap: textFieldMapSubjects
})
}
{
this.buildButtonsWithAdditionalTextFieldsFromOptions({
name: 'resources',
label: labels.resources,
type: 'check',
required: false,
textFieldMap: textFieldMapResources
})
}
{
this.buildButtonsWithAdditionalTextFieldsFromOptions({
name: 'robotics',
label: labels.robotics,
type: 'check',
required: false,
textFieldMap: textFieldMapRobotics
})
}

<FormGroup
id="date"
controlId="date"
validationState={this.getValidationState("date")}
>
<Row>
<Col md={6}>
<ControlLabel>
Date
<span style={{color: 'red'}}> *</span>
</ControlLabel>
</Col>
</Row>
<Row>
<Col md={6}>
<DatePicker
date={date}
minDate={moment()}
onChange={this.handleDateChange}
readOnly={false}
/>
</Col>
</Row>
</FormGroup>
{
this.buildSelectFieldGroupFromOptions({
name: 'workshopOrganizer',
label: labels.workshopOrganizer,
required: true,
placeholder: i18n.selectAnOption()
})
}
{
this.buildSelectFieldGroupFromOptions({
name: 'workshopFacilitator',
label: labels.workshopFacilitator,
required: true,
placeholder: i18n.selectAnOption()
})
}
{
this.buildSelectFieldGroupFromOptions({
name: 'workshopCourse',
label: labels.workshopCourse,
required: true,
placeholder: i18n.selectAnOption()
})
}
{
this.buildButtonsFromOptions({
name: 'emailOptIn',
label: labels.emailOptIn,
type: 'radio',
required: true,
placeholder: i18n.selectAnOption()
})
}
{
this.buildSingleCheckbox({
name: 'legalOptIn',
label: labels.legalOptIn,
required: true
})
}
</FormGroup>
);
}
}

InternationalOptInComponent.associatedFields = [
'firstName', 'firstNamePreferred', 'lastName', 'email', 'emailAlternate', 'gender',
'schoolName', 'schoolCity', 'schoolCountry', 'ages', 'subjects', 'resources',
'robotics', 'workshopOrganizer', 'workshopFacilitator', 'workshopCourse',
'emailOptIn', 'legalOptIn'
];
Expand Up @@ -45,10 +45,10 @@ export default class SingleChoiceResponses extends React.Component {
}
</td>
<td style={{paddingLeft: '20px'}}>
{possibleAnswer}
{count}
</td>
<td style={{paddingLeft: '20px'}}>
{count}
{possibleAnswer}
</td>
</tr>
);
Expand Down
@@ -1,6 +1,7 @@
import React, {PropTypes} from 'react';
import {Well} from 'react-bootstrap';
import _ from 'lodash';
import he from 'he';

export default class TextResponses extends React.Component {
static propTypes = {
Expand Down Expand Up @@ -64,7 +65,7 @@ export default class TextResponses extends React.Component {
}

renderBullet(text, key) {
const trimmedText = _.trim(text);
const trimmedText = _.trim(he.decode(text));
if (trimmedText) {
return (
<li key={key}>
Expand Down

0 comments on commit cbace27

Please sign in to comment.