diff --git a/apps/i18n/common/en_us.json b/apps/i18n/common/en_us.json index 650a55f4002ac..8643cf568a208 100644 --- a/apps/i18n/common/en_us.json +++ b/apps/i18n/common/en_us.json @@ -516,7 +516,6 @@ "downloadCSV": "Download CSV", "downloadAssessmentCSV": "Download CSV of student responses", "downloadFeedbackCSV": "Download CSV of Feedback", - "downloadParentLetter": "Download parent letter", "downloadReplayVideoButtonDownload": "Animation", "downloadReplayVideoButtonError": "Sorry, we were unable to download your animation. Please try re-running your project and trying again.", "dragBlocksToMatch": "Drag the blocks to match", @@ -1686,6 +1685,7 @@ "viewMyProjects": "View my projects", "viewMore": "View more", "viewPageAs": "View page as:", + "viewParentLetter": "View parent letter", "viewSection": "View section", "viewUnitOverview": "View Unit Overview", "visible": "Visible", diff --git a/apps/src/lib/ui/ParentLetter.jsx b/apps/src/lib/ui/ParentLetter.jsx index 3685a3eef16d1..e1118d44da35d 100644 --- a/apps/src/lib/ui/ParentLetter.jsx +++ b/apps/src/lib/ui/ParentLetter.jsx @@ -1,5 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import {connect} from 'react-redux'; +import _ from 'lodash'; import {studio, pegasus} from '../util/urlHelpers'; import {SectionLoginType} from '../../util/sharedConstants'; import color from '../../util/color'; @@ -13,7 +15,7 @@ const ENGAGEMENT_URL = const LOGIN_TYPE_NAMES = { [SectionLoginType.clever]: 'Clever accounts', [SectionLoginType.google_classroom]: 'Google Classroom accounts', - [SectionLoginType.picture]: 'picture passwosrds', + [SectionLoginType.picture]: 'picture passwords', [SectionLoginType.word]: 'secret words', [SectionLoginType.email]: 'personal logins' }; @@ -32,106 +34,168 @@ const LOGIN_TYPE_NAMES = { * secretPicturePath * secretWords */ -export default function ParentLetter({ - loginType, - secretPicturePath, - secretWords, - sectionCode, - studentName, - teacherName -}) { - return ( -
-
-
-

Hello!

-

- In my class, your child {studentName} is learning computer science on{' '} - Code.org, a fun, creative platform for - learning computer science and basic coding to create interactive - animations, games, or apps. Your interest in what your child is - learning is critical, and Code.org makes it easy to stay involved. -

-

- Step 1 - Encourage your child, show interest in computer science -

-

- One of the best ways is to ask your child to explain what they’re - learning and show you a project they are proud of ( - see details - ). Or watch one of{' '} - these videos{' '} - together. -

-

Step 2 - Get your child set up to use Code.org at home

- -

- At the top of their homepage, {studentName || 'your student'} can - continue the course they are doing with their classroom at school. - They can also create their own{' '} - - games or artwork in the Project Gallery - {' '} - or check out code.org/athome for - ideas for things to work on at home. -

-

Step 3 - Connect your email to your student’s account

-

- Keep up to date with what your student is working on and receive - updates from Code.org. Have your child sign in to Code.org and then - enter your email in Account Settings or{' '} - click here. -

-

Join the weekly “Code Break” every Wednesday

-

- The team at Code.org hosts a live interactive classroom for students - of all ages. Anybody can participate, and learn computer science in a - fun format starring very special guests.{' '} - More info. -

-

Why computer science

-

- Computer science teaches students critical thinking, problem solving, - and digital citizenship, and benefits all students, no matter what - opportunities they pursue in the future. And learning to make - interactive animations, code-art, games, and apps on Code.org - encourages creativity and makes learning fun. -

-

Code.org’s commitment to student privacy

-

- Code.org assigns utmost importance to student safety and security. - Code.org has signed the{' '} - Student Privacy Pledge and their{' '} - privacy practices have received{' '} - - one of the highest overall scores from Common Sense Media - - . You can find further details by viewing Code.org’s{' '} - Privacy Policy. -

-

- Please let me know if you have any questions and thank you for your - continued support of your child and of our classroom! -

-

{teacherName}

-
-
- ); +class ParentLetter extends React.Component { + static propTypes = { + studentId: PropTypes.string, + autoPrint: PropTypes.bool, + // Provided by Redux + section: PropTypes.shape({ + id: PropTypes.number.isRequired, + loginType: PropTypes.oneOf(Object.values(SectionLoginType)).isRequired, + code: PropTypes.string.isRequired + }).isRequired, + students: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + secret_picture_path: PropTypes.string, + secret_words: PropTypes.string + }) + ), + teacherName: PropTypes.string.isRequired + }; + + static defaultProps = { + students: [] + }; + + componentDidMount() { + if (this.props.autoPrint) { + this.printParentLetter(); + } + } + + printParentLetter = () => { + const printArea = document.getElementById('printArea').outerHTML; + // Adding a unique ID to the window name allows for multiple instances of this window + // to be open at once without affecting each other. + const windowName = `printWindow-${_.uniqueId()}`; + let printWindow = window.open('', windowName, ''); + + printWindow.document.open(); + printWindow.addEventListener('load', event => { + printWindow.print(); + }); + + printWindow.document.write( + `` + ); + printWindow.document.write(''); + printWindow.document.write(printArea); + printWindow.document.write(''); + printWindow.document.close(); + }; + + render() { + const {students, teacherName, section, studentId} = this.props; + const sectionCode = section.code; + const loginType = section.loginType; + const student = + students.length !== 0 && studentId + ? students + .filter(student => student.id.toString() === studentId) + .shift() + : null; + const studentName = student ? student.name : null; + const secretPicturePath = student ? student.secret_picture_path : null; + const secretWords = student ? student.secret_words : null; + + return ( +
+
+
+

Hello!

+

+ In my class, your child {studentName} is learning computer science + on Code.org, a fun, creative platform for + learning computer science and basic coding to create interactive + animations, games, or apps. Your interest in what your child is + learning is critical, and Code.org makes it easy to stay involved. +

+

+ Step 1 - Encourage your child, show interest in computer science +

+

+ One of the best ways is to ask your child to explain what they’re + learning and show you a project they are proud of ( + see details + ). Or watch one of{' '} + + these videos + {' '} + together. +

+

Step 2 - Get your child set up to use Code.org at home

+ +

+ At the top of their homepage, {studentName || 'your student'} can + continue the course they are doing with their classroom at school. + They can also create their own{' '} + + games or artwork in the Project Gallery + {' '} + or check out code.org/athome for + ideas for things to work on at home. +

+

Step 3 - Connect your email to your student’s account

+

+ Keep up to date with what your student is working on and receive + updates from Code.org. Have your child sign in to Code.org and then + enter your email in Account Settings or{' '} + click here. +

+

Join the weekly “Code Break” every Wednesday

+

+ The team at Code.org hosts a live interactive classroom for students + of all ages. Anybody can participate, and learn computer science in + a fun format starring very special guests.{' '} + More info. +

+

Why computer science

+

+ Computer science teaches students critical thinking, problem + solving, and digital citizenship, and benefits all students, no + matter what opportunities they pursue in the future. And learning to + make interactive animations, code-art, games, and apps on Code.org + encourages creativity and makes learning fun. +

+

Code.org’s commitment to student privacy

+

+ Code.org assigns utmost importance to student safety and security. + Code.org has signed the{' '} + Student Privacy Pledge and their{' '} + privacy practices have received{' '} + + one of the highest overall scores from Common Sense Media + + . You can find further details by viewing Code.org’s{' '} + Privacy Policy. +

+

+ Please let me know if you have any questions and thank you for your + continued support of your child and of our classroom! +

+

{teacherName}

+
+
+ ); + } } -ParentLetter.propTypes = { - loginType: PropTypes.oneOf(Object.values(SectionLoginType)).isRequired, - secretPicturePath: PropTypes.string, - secretWords: PropTypes.string, - sectionCode: PropTypes.string, // TODO: Conditionally-required - studentName: PropTypes.string, - teacherName: PropTypes.string.isRequired -}; + +export const UnconnectedParentLetter = ParentLetter; + +export default connect(state => ({ + section: + state.teacherSections.sections[state.teacherSections.selectedSectionId], + students: state.sectionData.section.students, + teacherName: state.currentUser.userName +}))(ParentLetter); const Header = () => { return ( @@ -193,7 +257,10 @@ const SignInInstructions = ({ {secretPicturePath && (
- +
)} diff --git a/apps/src/lib/ui/ParentLetter.story.jsx b/apps/src/lib/ui/ParentLetter.story.jsx index d60c19d3a49ff..63fcd22741c71 100644 --- a/apps/src/lib/ui/ParentLetter.story.jsx +++ b/apps/src/lib/ui/ParentLetter.story.jsx @@ -1,20 +1,42 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ParentLetter from './ParentLetter'; +import {UnconnectedParentLetter as ParentLetter} from './ParentLetter'; import {SectionLoginType} from '../../util/sharedConstants'; import wizardPng from '../../../static/skins/studio/wizard_thumb.png'; export default storybook => { storybook = storybook.storiesOf('ParentLetter', module); + const sampleSection = { + id: 7, + code: 'ABCDEF' + }; + + const sampleStudents = [ + { + id: 100, + name: 'Neville', + secret_picture_path: wizardPng, + secret_words: 'wizarding world' + }, + { + id: 101, + name: 'Hermione', + secret_picture_path: wizardPng, + secret_words: 'wizarding world' + } + ]; + // Make stories for generic letters and personalized letters // Make a story for every login type Object.values(SectionLoginType).forEach(loginType => { storybook = storybook.add(`Generic / ${loginType}`, () => ( @@ -23,12 +45,13 @@ export default storybook => { storybook = storybook.add(`Personalized / ${loginType}`, () => ( )); diff --git a/apps/src/templates/manageStudents/ManageStudentsActionsCell.jsx b/apps/src/templates/manageStudents/ManageStudentsActionsCell.jsx index 6ffa15118f17d..27b0c56b9ac2f 100644 --- a/apps/src/templates/manageStudents/ManageStudentsActionsCell.jsx +++ b/apps/src/templates/manageStudents/ManageStudentsActionsCell.jsx @@ -193,8 +193,11 @@ class ManageStudentActionsCell extends Component { navigateToHref(url); }; - onDownloadParentLetter = () => { + onViewParentLetter = () => { const {id, sectionId} = this.props; + const url = + teacherDashboardUrl(sectionId, '/parent_letter') + `?studentId=${id}`; + window.open(url, '_blank'); firehoseClient.putRecord( { study: 'teacher-dashboard', @@ -238,8 +241,8 @@ class ManageStudentActionsCell extends Component { )} {showWordPictureOptions && experiments.isEnabled(experiments.PARENT_LETTER) && ( - - {i18n.downloadParentLetter()} + + {i18n.viewParentLetter()} )} {this.props.canEdit && canDelete && } diff --git a/apps/src/templates/teacherDashboard/TeacherDashboard.jsx b/apps/src/templates/teacherDashboard/TeacherDashboard.jsx index 580b074fbf101..2521029557de2 100644 --- a/apps/src/templates/teacherDashboard/TeacherDashboard.jsx +++ b/apps/src/templates/teacherDashboard/TeacherDashboard.jsx @@ -16,6 +16,8 @@ import EmptySection from './EmptySection'; import _ from 'lodash'; import firehoseClient from '../../lib/util/firehose'; import StandardsReport from '../sectionProgress/standards/StandardsReport'; +import ParentLetter from '@cdo/apps/lib/ui/ParentLetter'; +import {queryParams} from '@cdo/apps/code-studio/utils'; class TeacherDashboard extends Component { static propTypes = { @@ -70,10 +72,11 @@ class TeacherDashboard extends Component { location.pathname = TeacherDashboardPath.progress; } - // Include header components unless we are on the /login_info or /standards_report page. + // Include header components unless we are on the /login_info, /standards_report, or /parent_letter page. const includeHeader = location.pathname !== TeacherDashboardPath.loginInfo && - location.pathname !== TeacherDashboardPath.standardsReport; + location.pathname !== TeacherDashboardPath.standardsReport && + location.pathname !== TeacherDashboardPath.parentLetter; return (
@@ -104,6 +107,15 @@ class TeacherDashboard extends Component { path={TeacherDashboardPath.standardsReport} component={props => } /> + ( + + )} + /> {/* Break out of Switch if we have 0 students. Display EmptySection component instead. */} {studentCount === 0 && (