From 3518ff9e8e6ec0a725505fbc6a06a2c1f2267269 Mon Sep 17 00:00:00 2001 From: NitronR Date: Thu, 2 Aug 2018 20:13:10 +0530 Subject: [PATCH] feat(full-stack): Full Stack Certification --- common/app/entities/index.js | 8 ++ .../Settings/components/Cert-Settings.jsx | 89 +++++++++++++------ server/boot/certificate.js | 63 +++++++------ server/utils/certTypes.json | 6 +- server/utils/constantStrings.json | 3 +- server/utils/superBlockCertTypeMap.js | 4 +- server/views/certificate/full-stack.jade | 32 +++++++ 7 files changed, 145 insertions(+), 60 deletions(-) create mode 100644 server/views/certificate/full-stack.jade diff --git a/common/app/entities/index.js b/common/app/entities/index.js index 90e2b3aa68..5d95742299 100644 --- a/common/app/entities/index.js +++ b/common/app/entities/index.js @@ -119,6 +119,14 @@ export function projectsSelector(state) { ]), [] ); + + blocks['full-stack-projects'] = { + dashedName: 'full-stack', + title: 'Full Stack Certification', + time: '1800 hours', + challenges: [], + superBlock: 'full-stack' + }; return Object.keys(blocks) .filter(key => key.includes('projects') && !( diff --git a/common/app/routes/Settings/components/Cert-Settings.jsx b/common/app/routes/Settings/components/Cert-Settings.jsx index 2b56e23e94..3abd8347f1 100644 --- a/common/app/routes/Settings/components/Cert-Settings.jsx +++ b/common/app/routes/Settings/components/Cert-Settings.jsx @@ -37,34 +37,41 @@ const mapStateToProps = createSelector( isFrontEndCert, isBackEndCert, isDataVisCert, + isFullStackCert, username }, projects - ) => ({ - allProjects: projects, - legacyProjects: projects.filter(p => p.superBlock.includes('legacy')), - modernProjects: projects.filter(p => !p.superBlock.includes('legacy')), - userProjects: projects - .map(block => buildUserProjectsMap(block, completedChallenges)) - .reduce((projects, current) => ({ - ...projects, - ...current - }), {}), - blockNameIsCertMap: { - 'Responsive Web Design Projects': isRespWebDesignCert, - /* eslint-disable max-len */ - 'JavaScript Algorithms and Data Structures Projects': isJsAlgoDataStructCert, - /* eslint-enable max-len */ - 'Front End Libraries Projects': isFrontEndLibsCert, - 'Data Visualization Projects': is2018DataVisCert, - 'APIs and Microservices Projects': isApisMicroservicesCert, - 'Information Security and Quality Assurance Projects': isInfosecQaCert, - 'Legacy Front End Projects': isFrontEndCert, - 'Legacy Back End Projects': isBackEndCert, - 'Legacy Data Visualization Projects': isDataVisCert - }, - username - }) + ) => { + let modernProjects = projects.filter(p => !p.superBlock.includes('legacy')); + modernProjects.push(modernProjects.shift()); + + return { + allProjects: projects, + legacyProjects: projects.filter(p => p.superBlock.includes('legacy')), + modernProjects: modernProjects, + userProjects: projects + .map(block => buildUserProjectsMap(block, completedChallenges)) + .reduce((projects, current) => ({ + ...projects, + ...current + }), {}), + blockNameIsCertMap: { + 'Responsive Web Design Projects': isRespWebDesignCert, + /* eslint-disable max-len */ + 'JavaScript Algorithms and Data Structures Projects': isJsAlgoDataStructCert, + /* eslint-enable max-len */ + 'Front End Libraries Projects': isFrontEndLibsCert, + 'Data Visualization Projects': is2018DataVisCert, + 'APIs and Microservices Projects': isApisMicroservicesCert, + 'Information Security and Quality Assurance Projects': isInfosecQaCert, + 'Full Stack Certification': isFullStackCert, + 'Legacy Front End Projects': isFrontEndCert, + 'Legacy Back End Projects': isBackEndCert, + 'Legacy Data Visualization Projects': isDataVisCert + }, + username + }; + } ); function mapDispatchToProps(dispatch) { @@ -178,12 +185,40 @@ class CertificationSettings extends PureComponent { .length - 1; const fullForm = completedProjects === challengeTitles.length; + + let isFullStack = superBlock === 'full-stack'; + let isFullStackClaimable = false; + let description = ''; + if (isFullStack) { + isFullStackClaimable = Object.keys(blockNameIsCertMap).every(function(e) { + if (e.indexOf('Full Stack') !== -1 || e.indexOf('Legacy') !== -1) { + return true; + } + return blockNameIsCertMap[e]; + }); + + description = (
+ Once you've earned the following freeCodeCamp certifications, + you'll be able to claim The Full Stack Developer Certification: + +
); + } + return (

{ projectBlockName }

+ {description}
res.redirect( - `/certification/${req.params.username}/legacy-full-stack` + `/certification/${req.params.username}/full-stack` ) ); @@ -254,39 +257,45 @@ export default function certificate(app) { return user.getCompletedChallenges$() .flatMap(() => certTypeIds[certType]) .flatMap(challenge => { - const { - id, - tests, - challengeType - } = challenge; const certName = certText[certType]; if (user[certType]) { return Observable.just(alreadyClaimedMessage(certName)); } - if (!user[certType] && !isCertified(tests, user.completedChallenges)) { - return Observable.just(notCertifiedMessage(certName)); - } - if (!user.name) { - return Observable.just(noNameMessage); - } - const updateData = { - $push: { + + let updateData = { + $set: { + [certType]: true + } + }; + + if (challenge) { + const { + id, + tests, + challengeType + } = challenge; + if (!user[certType] && + !isCertified(tests, user.completedChallenges)) { + return Observable.just(notCertifiedMessage(certName)); + } + updateData['$push'] = { completedChallenges: { id, completedDate: new Date(), challengeType } - }, - $set: { - [certType]: true - } - }; + }; + user.completedChallenges[ + user.completedChallenges.length - 1 + ] = { id, completedDate: new Date() }; + } + + if (!user.name) { + return Observable.just(noNameMessage); + } // set here so sendCertifiedEmail works properly // not used otherwise user[certType] = true; - user.completedChallenges[ - user.completedChallenges.length - 1 - ] = { id, completedDate: new Date() }; return Observable.combineLatest( // update user data user.update$(updateData), diff --git a/server/utils/certTypes.json b/server/utils/certTypes.json index 9f29be3a27..b34f294eeb 100644 --- a/server/utils/certTypes.json +++ b/server/utils/certTypes.json @@ -2,11 +2,11 @@ "frontEnd": "isFrontEndCert", "backEnd": "isBackEndCert", "dataVis": "isDataVisCert", - "fullStack": "isFullStackCert", "respWebDesign": "isRespWebDesignCert", "frontEndLibs": "isFrontEndLibsCert", "dataVis2018": "is2018DataVisCert", "jsAlgoDataStruct": "isJsAlgoDataStructCert", "apisMicroservices": "isApisMicroservicesCert", - "infosecQa": "isInfosecQaCert" -} \ No newline at end of file + "infosecQa": "isInfosecQaCert", + "fullStack": "isFullStackCert" +} diff --git a/server/utils/constantStrings.json b/server/utils/constantStrings.json index 74303fe654..d7105394af 100644 --- a/server/utils/constantStrings.json +++ b/server/utils/constantStrings.json @@ -10,5 +10,6 @@ "dataVis2018Id": "5a553ca864b52e1d8bceea14", "jsAlgoDataStructId": "561abd10cb81ac38a17513bc", "apisMicroservicesId": "561add10cb82ac38a17523bc", - "infosecQaId": "561add10cb82ac38a17213bc" + "infosecQaId": "561add10cb82ac38a17213bc", + "fullStackId": "561add10cb82ac38a17213bd" } diff --git a/server/utils/superBlockCertTypeMap.js b/server/utils/superBlockCertTypeMap.js index 9d62df67be..10f95f8b05 100644 --- a/server/utils/superBlockCertTypeMap.js +++ b/server/utils/superBlockCertTypeMap.js @@ -5,7 +5,6 @@ const superBlockCertTypeMap = { 'legacy-front-end': certTypes.frontEnd, 'legacy-back-end': certTypes.backEnd, 'legacy-data-visualization': certTypes.dataVis, - 'legacy-full-stack': certTypes.fullStack, // modern 'responsive-web-design': certTypes.respWebDesign, @@ -13,7 +12,8 @@ const superBlockCertTypeMap = { 'front-end-libraries': certTypes.frontEndLibs, 'data-visualization': certTypes.dataVis2018, 'apis-and-microservices': certTypes.apisMicroservices, - 'information-security-and-quality-assurance': certTypes.infosecQa + 'information-security-and-quality-assurance': certTypes.infosecQa, + 'full-stack': certTypes.fullStack }; export default superBlockCertTypeMap; diff --git a/server/views/certificate/full-stack.jade b/server/views/certificate/full-stack.jade new file mode 100644 index 0000000000..1cd0214ba5 --- /dev/null +++ b/server/views/certificate/full-stack.jade @@ -0,0 +1,32 @@ +meta(name='viewport', content='width=device-width, initial-scale=1') +link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css') +include styles + +.certificate-wrapper.container + .row + header + .col-md-5.col-sm-12 + .logo + img(class='img-responsive', src='https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg', alt="freeCodeCamp's Logo") + .col-md-7.col-sm-12 + .issue-date Issued  + strong #{date} + + section.information + .information-container + h3 This certifies that + h1 + strong= name + h3 has successfully completed freeCodeCamp's + h1 + strong Full Stack + h4 Developer Certification, representing approximately 1800 hours of coursework + + footer + .row.signatures + img(class='img-responsive', src='https://i.imgur.com/OJFVJKg.png', alt="Quincy Larson's Signature") + p + strong Quincy Larson + p Executive Director, freeCodeCamp.org + .row + p.verify Verify this certification at: https://www.freecodecamp.org/certification/#{username}/responsive-web-design