Fix: Settings/Portfolio UI (#17202)

* chore(names): Certificate -> Certification

* fix(style): Profile Certs alignment and font-size

* fix(button): Remove user icon

* fix(style): Adjust profile timeline UI

* fix(style): General account settings style fixes

* chore(updateEmail): Remove update email until we sort out auth0

* fix(copy): website -> personal website

* fix(copy): profile -> portfolio

* feat(home): Add legacy certs to user home page

* fix(copy): Change Honest settings copy

* fix(cert-settings): Filter out 'take home's

* fix(copy): next lesson -> next coding challenge

* fix(alignment): Align portfolio item settings' buttons
This commit is contained in:
Stuart Taylor
2018-05-21 16:21:15 +01:00
committed by Quincy Larson
parent 848e04028e
commit a76c8aff5a
33 changed files with 259 additions and 134 deletions

View File

@ -121,7 +121,9 @@ export function projectsSelector(state) {
); );
return Object.keys(blocks) return Object.keys(blocks)
.filter(key => .filter(key =>
key.includes('projects') && !key.includes('coding-interview') key.includes('projects') && !(
key.includes('coding-interview') || key.includes('take-home')
)
) )
.map(key => blocks[key]) .map(key => blocks[key])
.concat(legacyWithDashedNames) .concat(legacyWithDashedNames)

View File

@ -58,7 +58,7 @@ function CamperHOC({
location={ showLocation && location } location={ showLocation && location }
name={ showName && name } name={ showName && name }
picture={ picture } picture={ picture }
points={ showPoints && points } points={ showPoints ? points : 0 }
username={ username } username={ username }
/> />
<hr /> <hr />

View File

@ -9,6 +9,8 @@ import {
Col Col
} from 'react-bootstrap'; } from 'react-bootstrap';
import { FullWidthRow } from '../../../helperComponents';
import { userByNameSelector } from '../../../redux'; import { userByNameSelector } from '../../../redux';
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
@ -40,59 +42,59 @@ const mapStateToProps = createSelector(
currentCerts: [ currentCerts: [
{ {
show: is2018FullStackCert, show: is2018FullStackCert,
title: 'Full Stack Certificate:', title: 'Full Stack Certification:',
showURL: '2018-full-stack' showURL: '2018-full-stack'
}, },
{ {
show: isRespWebDesignCert, show: isRespWebDesignCert,
title: 'Responsive Web Design Certificate:', title: 'Responsive Web Design Certification:',
showURL: 'responsive-web-design' showURL: 'responsive-web-design'
}, },
{ {
show: isJsAlgoDataStructCert, show: isJsAlgoDataStructCert,
title: 'JavaScript Algorithms and Data Structures Certificate:', title: 'JavaScript Algorithms and Data Structures Certification:',
showURL: 'javascript-algorithms-and-data-structures' showURL: 'javascript-algorithms-and-data-structures'
}, },
{ {
show: isFrontEndLibsCert, show: isFrontEndLibsCert,
title: 'Front End Libraries Certificate:', title: 'Front End Libraries Certification:',
showURL: 'front-end-libraries' showURL: 'front-end-libraries'
}, },
{ {
show: is2018DataVisCert, show: is2018DataVisCert,
title: 'Data Visualization Certificate:', title: 'Data Visualization Certification:',
showURL: 'data-visualization' showURL: 'data-visualization'
}, },
{ {
show: isApisMicroservicesCert, show: isApisMicroservicesCert,
title: 'APIs and Microservices Certificate:', title: 'APIs and Microservices Certification:',
showURL: 'apis-and-microservices' showURL: 'apis-and-microservices'
}, },
{ {
show: isInfosecQaCert, show: isInfosecQaCert,
title: 'Information Security and Quality Assurance Certificate:', title: 'Information Security and Quality Assurance Certification:',
showURL: 'information-security-and-quality-assurance' showURL: 'information-security-and-quality-assurance'
} }
], ],
legacyCerts: [ legacyCerts: [
{ {
show: isFullStackCert, show: isFullStackCert,
title: 'Full Stack Certificate:', title: 'Full Stack Certification:',
showURL: 'legacy-full-stack' showURL: 'legacy-full-stack'
}, },
{ {
show: isFrontEndCert, show: isFrontEndCert,
title: 'Front End Certificate:', title: 'Front End Certification:',
showURL: 'legacy-front-end' showURL: 'legacy-front-end'
}, },
{ {
show: isBackEndCert, show: isBackEndCert,
title: 'Back End Certificate:', title: 'Back End Certification:',
showURL: 'legacy-back-end' showURL: 'legacy-back-end'
}, },
{ {
show: isDataVisCert, show: isDataVisCert,
title: 'Data Visualization Certificate:', title: 'Data Visualization Certification:',
showURL: 'legacy-data-visualization' showURL: 'legacy-data-visualization'
} }
] ]
@ -123,7 +125,7 @@ function renderCertShow(username, cert) {
return cert.show ? ( return cert.show ? (
<Row key={ cert.showURL }> <Row key={ cert.showURL }>
<Col sm={ 8 }> <Col sm={ 8 }>
<p> <p className='bigger-text'>
<strong> <strong>
{ cert.title } { cert.title }
</strong> </strong>
@ -134,7 +136,7 @@ function renderCertShow(username, cert) {
block={ true } block={ true }
bsSize='lg' bsSize='lg'
bsStyle='primary' bsStyle='primary'
href={ `/certificates/${username}/${cert.showURL}`} href={ `/certification/${username}/${cert.showURL}`}
> >
Show Show
</Button> </Button>
@ -154,25 +156,27 @@ function Certificates({
const renderCertShowWithUsername = _.curry(renderCertShow)(username); const renderCertShowWithUsername = _.curry(renderCertShow)(username);
return ( return (
<div> <div>
<h2 className='text-center'>freeCodeCamp Certificates</h2> <FullWidthRow>
<br /> <h2 className='text-center'>freeCodeCamp Certifications</h2>
{ <br />
hasModernCert ? {
currentCerts.map(renderCertShowWithUsername) : hasModernCert ?
<p className='text-center' > currentCerts.map(renderCertShowWithUsername) :
No certificates have been earned under the current curriculum <p className='text-center' >
</p> No certifications have been earned under the current curriculum
} </p>
{ }
hasLegacyCert ? {
<div> hasLegacyCert ?
<h3>Legacy Certifications</h3> <div>
{ <h3>Legacy Certifications</h3>
legacyCerts.map(renderCertShowWithUsername) {
} legacyCerts.map(renderCertShowWithUsername)
</div> : }
null </div> :
} null
}
</FullWidthRow>
<hr /> <hr />
</div> </div>
); );

View File

@ -3,8 +3,10 @@ import PropTypes from 'prop-types';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import format from 'date-fns/format'; import format from 'date-fns/format';
import { reverse, sortBy } from 'lodash'; import { find, reverse, sortBy } from 'lodash';
import { import {
Button,
Modal,
Table Table
} from 'react-bootstrap'; } from 'react-bootstrap';
@ -12,18 +14,20 @@ import { challengeIdToNameMapSelector } from '../../../entities';
import { userByNameSelector } from '../../../redux'; import { userByNameSelector } from '../../../redux';
import { homeURL } from '../../../../utils/constantStrings.json'; import { homeURL } from '../../../../utils/constantStrings.json';
import blockNameify from '../../../utils/blockNameify'; import blockNameify from '../../../utils/blockNameify';
import { FullWidthRow } from '../../../helperComponents';
import { Link } from '../../../Router'; import { Link } from '../../../Router';
import { FullWidthRow } from '../../../helperComponents';
import SolutionViewer from '../../Settings/components/SolutionViewer.jsx';
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
challengeIdToNameMapSelector, challengeIdToNameMapSelector,
userByNameSelector, userByNameSelector,
( (
idToNameMap, idToNameMap,
{ completedChallenges: completedMap = [] } { completedChallenges: completedMap = [], username }
) => ({ ) => ({
completedMap, completedMap,
idToNameMap idToNameMap,
username
}) })
); );
@ -32,38 +36,94 @@ const propTypes = {
PropTypes.shape({ PropTypes.shape({
id: PropTypes.string, id: PropTypes.string,
completedDate: PropTypes.number, completedDate: PropTypes.number,
challengeType: PropTypes.number challengeType: PropTypes.number,
solution: PropTypes.string,
files: PropTypes.shape({
ext: PropTypes.string,
contents: PropTypes.string
})
}) })
), ),
idToNameMap: PropTypes.objectOf(PropTypes.string) idToNameMap: PropTypes.objectOf(PropTypes.string),
username: PropTypes.string
}; };
class Timeline extends PureComponent { class Timeline extends PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
solutionToView: null,
solutionOpen: false
};
this.closeSolution = this.closeSolution.bind(this);
this.renderCompletion = this.renderCompletion.bind(this); this.renderCompletion = this.renderCompletion.bind(this);
this.viewSolution = this.viewSolution.bind(this);
} }
renderCompletion(completed) { renderCompletion(completed) {
const { idToNameMap } = this.props; const { idToNameMap } = this.props;
const { id, completedDate } = completed; const { id, completedDate, solution, files } = completed;
const challengeDashedName = idToNameMap[id];
return ( return (
<tr key={ id }> <tr key={ id }>
<td>{ blockNameify(idToNameMap[id]) }</td>
<td> <td>
<a href={`/challenges/${challengeDashedName}`}>
{ blockNameify(challengeDashedName) }
</a>
</td>
<td className='text-center'>
<time dateTime={ format(completedDate, 'YYYY-MM-DDTHH:MM:SSZ') }> <time dateTime={ format(completedDate, 'YYYY-MM-DDTHH:MM:SSZ') }>
{ {
format(completedDate, 'MMMM DD YYYY') format(completedDate, 'MMMM D, YYYY')
} }
</time> </time>
</td> </td>
<td>
{/* eslint-disable no-nested-ternary */
files ? (
<Button
block={ true }
bsStyle='primary'
onClick={ () => this.viewSolution(id) }
>
View&nbsp;Solution
</Button>
) : solution ? (
<Button
block={ true }
bsStyle='primary'
href={solution}
target='_blank'
>
View&nbsp;Solution
</Button>
) : ''
}
</td>
</tr> </tr>
); );
} }
viewSolution(id) {
this.setState(state => ({
...state,
solutionToView: id,
solutionOpen: true
}));
}
closeSolution() {
this.setState(state => ({
...state,
solutionToView: null,
solutionOpen: false
}));
}
render() { render() {
const { completedMap, idToNameMap } = this.props; const { completedMap, idToNameMap, username } = this.props;
const { solutionToView: id, solutionOpen } = this.state;
if (!Object.keys(idToNameMap).length) { if (!Object.keys(idToNameMap).length) {
return null; return null;
} }
@ -82,7 +142,7 @@ class Timeline extends PureComponent {
<thead> <thead>
<tr> <tr>
<th>Challenge</th> <th>Challenge</th>
<th>First Completed</th> <th className='text-center'>First Completed</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -98,6 +158,30 @@ class Timeline extends PureComponent {
</tbody> </tbody>
</Table> </Table>
} }
{
id &&
<Modal
aria-labelledby='contained-modal-title'
onHide={this.closeSolution}
show={ solutionOpen }
>
<Modal.Header closeButton={ true }>
<Modal.Title id='contained-modal-title'>
{ `${username}'s Solution to ${blockNameify(idToNameMap[id])}` }
</Modal.Title>
</Modal.Header>
<Modal.Body>
<SolutionViewer
solution={
find(completedMap, ({id: completedId}) => completedId === id)
}
/>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.closeSolution}>Close</Button>
</Modal.Footer>
</Modal>
}
</FullWidthRow> </FullWidthRow>
); );
} }

View File

@ -13,6 +13,18 @@
background-color: #fff; background-color: #fff;
} }
.bigger-text {
font-size: 1.7rem;
}
.bio {
text-align: center;
}
.name, .location, .bio, .points {
font-size: 2rem;
margin-bottom: 0;
}
.@{ns}-container { .@{ns}-container {
@ -41,10 +53,6 @@
} }
} }
.bio {
text-align: center;
}
h2, h3 { h2, h3 {
&.name, &.username, &.points, &.location { &.name, &.username, &.points, &.location {
margin-top: 0; margin-top: 0;

View File

@ -4,7 +4,6 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { Button } from 'react-bootstrap'; import { Button } from 'react-bootstrap';
import FA from 'react-fontawesome';
import ns from './ns.json'; import ns from './ns.json';
import { FullWidthRow, Spacer, Loader } from '../../helperComponents'; import { FullWidthRow, Spacer, Loader } from '../../helperComponents';
@ -85,8 +84,7 @@ export class Settings extends React.Component {
bsStyle='primary' bsStyle='primary'
className='btn-link-social' className='btn-link-social'
> >
<FA name='user' /> Show me my public portfolio
Show me my public profile
</Button> </Button>
</Link> </Link>
<Button <Button

View File

@ -12,7 +12,6 @@ import { FullWidthRow, Spacer } from '../../../helperComponents';
import ThemeSettings from './ThemeSettings.jsx'; import ThemeSettings from './ThemeSettings.jsx';
import Camper from './Camper.jsx'; import Camper from './Camper.jsx';
import UsernameSettings from './UsernameSettings.jsx'; import UsernameSettings from './UsernameSettings.jsx';
import SectionHeader from './SectionHeader.jsx';
import { userSelector, toggleNightMode } from '../../../redux'; import { userSelector, toggleNightMode } from '../../../redux';
import { updateUserBackend } from '../redux'; import { updateUserBackend } from '../redux';
import { import {
@ -165,9 +164,6 @@ class AboutSettings extends PureComponent {
const toggleTheme = () => toggleNightMode(username, currentTheme); const toggleTheme = () => toggleNightMode(username, currentTheme);
return ( return (
<div className='about-settings'> <div className='about-settings'>
<SectionHeader>
About Settings
</SectionHeader>
<UsernameSettings username={ username }/> <UsernameSettings username={ username }/>
<FullWidthRow> <FullWidthRow>
<Nav <Nav

View File

@ -180,7 +180,7 @@ class CertificationSettings extends PureComponent {
<FullWidthRow key={superBlock}> <FullWidthRow key={superBlock}>
<h3 className='project-heading'>{ projectBlockName }</h3> <h3 className='project-heading'>{ projectBlockName }</h3>
<Form <Form
buttonText={ fullForm ? 'Claim Certificate' : 'Save Progress' } buttonText={ fullForm ? 'Claim Certification' : 'Save Progress' }
enableSubmit={ fullForm } enableSubmit={ fullForm }
formFields={ challengeTitles.concat([ 'id' ]) } formFields={ challengeTitles.concat([ 'id' ]) }
hideButton={isCertClaimed} hideButton={isCertClaimed}
@ -198,10 +198,10 @@ class CertificationSettings extends PureComponent {
block={ true } block={ true }
bsSize='lg' bsSize='lg'
bsStyle='primary' bsStyle='primary'
href={ `/certificates/${username}/${superBlock}`} href={ `/certification/${username}/${superBlock}`}
target='_blank' target='_blank'
> >
Show Certificate Show Certification
</Button> : </Button> :
null null
} }
@ -215,11 +215,11 @@ class CertificationSettings extends PureComponent {
const { allProjects } = this.props; const { allProjects } = this.props;
let project = _.find(allProjects, { superBlock: id }); let project = _.find(allProjects, { superBlock: id });
if (!project) { if (!project) {
// the submitted projects do not belong to current/legacy certificates // the submitted projects do not belong to current/legacy certifications
return this.props.createError( return this.props.createError(
new Error( new Error(
'Submitted projects do not belong to either current or ' + 'Submitted projects do not belong to either current or ' +
'legacy certificates' 'legacy certifications'
) )
); );
} }
@ -268,7 +268,7 @@ class CertificationSettings extends PureComponent {
<FullWidthRow> <FullWidthRow>
<p> <p>
Add links to the live demos of your projects as you finish them. Add links to the live demos of your projects as you finish them.
Then, once you have added all 5 projects required for a certificate, Then, once you have added all 5 projects required for a certification,
you can claim it. you can claim it.
</p> </p>
</FullWidthRow> </FullWidthRow>
@ -276,7 +276,7 @@ class CertificationSettings extends PureComponent {
modernProjects.map(this.buildProjectForms) modernProjects.map(this.buildProjectForms)
} }
<SectionHeader> <SectionHeader>
Legacy Certificate Settings Legacy Certification Settings
</SectionHeader> </SectionHeader>
{ {
legacyProjects.map(this.buildProjectForms) legacyProjects.map(this.buildProjectForms)

View File

@ -13,13 +13,29 @@ import {
} from 'react-bootstrap'; } from 'react-bootstrap';
import TB from '../Toggle-Button'; import TB from '../Toggle-Button';
import EmailForm from './EmailForm.jsx'; // import EmailForm from './EmailForm.jsx';
import { Link } from '../../../Router'; import { Link } from '../../../Router';
import { FullWidthRow, Spacer } from '../../../helperComponents'; import { FullWidthRow } from '../../../helperComponents';
import SectionHeader from './SectionHeader.jsx'; import SectionHeader from './SectionHeader.jsx';
import { userSelector } from '../../../redux'; import { userSelector } from '../../../redux';
import { onRouteUpdateEmail, updateMyEmail, updateUserBackend } from '../redux'; import { onRouteUpdateEmail, updateMyEmail, updateUserBackend } from '../redux';
/**
Removed functionality until we can update auth0 at the same time
<FullWidthRow>
<UpdateEmailButton />
</FullWidthRow>
<FullWidthRow>
<EmailForm
initialValues={{ email, confrimEmail: ''}}
/>
</FullWidthRow>
<Spacer />
*/
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
userSelector, userSelector,
({ ({
@ -101,9 +117,8 @@ class EmailSettings extends PureComponent {
You do not have an email associated with this account. You do not have an email associated with this account.
</p> </p>
</FullWidthRow> </FullWidthRow>
<FullWidthRow>
<UpdateEmailButton />
</FullWidthRow>
</div> </div>
); );
} }
@ -124,12 +139,8 @@ class EmailSettings extends PureComponent {
</HelpBlock> </HelpBlock>
</FullWidthRow> </FullWidthRow>
} }
<FullWidthRow>
<EmailForm
initialValues={{ email, confrimEmail: ''}}
/>
</FullWidthRow>
<Spacer />
<FullWidthRow> <FullWidthRow>
<Row className='inline-form-field' key='sendQuincyEmail'> <Row className='inline-form-field' key='sendQuincyEmail'>
<Col sm={ 8 }> <Col sm={ 8 }>

View File

@ -45,7 +45,7 @@ class Honesty extends PureComponent {
const isHonestAgreed = ( const isHonestAgreed = (
<Panel bsStyle='info'> <Panel bsStyle='info'>
<p> <p>
You have already accepted our Academic Honesty Policy You have accepted our Academic Honesty Policy
</p> </p>
</Panel> </Panel>
); );

View File

@ -22,12 +22,17 @@ const mapStateToProps = createSelector(
githubProfile, githubProfile,
linkedin, linkedin,
twitter, twitter,
website personalWebsite: website
} }
}) })
); );
const formFields = [ 'githubProfile', 'linkedin', 'twitter', 'website' ]; const formFields = [
'githubProfile',
'linkedin',
'twitter',
'personalWebsite'
];
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return bindActionCreators({ return bindActionCreators({
@ -40,10 +45,10 @@ const propTypes = {
githubProfile: PropTypes.string, githubProfile: PropTypes.string,
handleSubmit: PropTypes.func.isRequired, handleSubmit: PropTypes.func.isRequired,
linkedin: PropTypes.string, linkedin: PropTypes.string,
personalWebsite: PropTypes.string,
twitter: PropTypes.string, twitter: PropTypes.string,
updateUserBackend: PropTypes.func.isRequired, updateUserBackend: PropTypes.func.isRequired,
username: PropTypes.string, username: PropTypes.string
website: PropTypes.string
}; };
class InternetSettings extends PureComponent { class InternetSettings extends PureComponent {
@ -53,8 +58,8 @@ class InternetSettings extends PureComponent {
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
} }
handleSubmit(values) { handleSubmit({personalWebsite, ...others}) {
this.props.updateUserBackend(values); this.props.updateUserBackend({ ...others, website: personalWebsite });
} }
render() { render() {

View File

@ -43,7 +43,7 @@ class JSAlgoAndDSForm extends PureComponent {
e.preventDefault(); e.preventDefault();
const { username, superBlock, isCertClaimed } = this.props; const { username, superBlock, isCertClaimed } = this.props;
if (isCertClaimed) { if (isCertClaimed) {
return this.props.hardGoTo(`/certificates/${username}/${superBlock}`); return this.props.hardGoTo(`/certification/${username}/${superBlock}`);
} }
return this.props.claimCert(superBlock); return this.props.claimCert(superBlock);
} }
@ -107,7 +107,7 @@ class JSAlgoAndDSForm extends PureComponent {
Object.keys(jsProjects).length === completeCount ? Object.keys(jsProjects).length === completeCount ?
<form onSubmit={ this.handleSubmit }> <form onSubmit={ this.handleSubmit }>
<BlockSaveButton> <BlockSaveButton>
{ isCertClaimed ? 'Show' : 'Claim'} Certificate { isCertClaimed ? 'Show' : 'Claim'} Certification
</BlockSaveButton> </BlockSaveButton>
</form> : </form> :
null null

View File

@ -26,7 +26,7 @@ function ResetModal(props) {
<Modal.Body> <Modal.Body>
<p> <p>
This will really delete all of your progress, points, completed This will really delete all of your progress, points, completed
challenges, our records of your projects, any certificates you have, challenges, our records of your projects, any certifications you have,
everything. everything.
</p> </p>
<p> <p>

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { reduxForm } from 'redux-form'; import { reduxForm } from 'redux-form';
import { FormFields, BlockSaveButton, BlockSaveWrapper } from './'; import { FormFields, BlockSaveButton } from './';
const propTypes = { const propTypes = {
buttonText: PropTypes.string, buttonText: PropTypes.string,
@ -50,22 +50,20 @@ function DynamicForm({
fields={ fields } fields={ fields }
options={ options } options={ options }
/> />
<BlockSaveWrapper> {
{ hideButton ?
hideButton ? null :
null : <BlockSaveButton
<BlockSaveButton disabled={
disabled={ allPristine && !enableSubmit ||
allPristine && !enableSubmit || (!!Object.keys(errors).filter(key => errors[key]).length)
(!!Object.keys(errors).filter(key => errors[key]).length) }
} >
> {
{ buttonText ? buttonText : null
buttonText ? buttonText : null }
} </BlockSaveButton>
</BlockSaveButton> }
}
</BlockSaveWrapper>
</form> </form>
); );
} }

View File

@ -1,5 +1,5 @@
const policy = [ const policy = [
'Before you can claim a verified certificate, you must accept the ' + 'Before you can claim a verified certification, you must accept the ' +
'Academic Honesty Policy below.', 'Academic Honesty Policy below.',
'I understand that plagiarism means copying someone elses work and ' + 'I understand that plagiarism means copying someone elses work and ' +
'presenting the work as if it were my own, without clearly attributing ' + 'presenting the work as if it were my own, without clearly attributing ' +

View File

@ -138,7 +138,7 @@ function sendCertifiedEmail(
from: 'team@freeCodeCamp.org', from: 'team@freeCodeCamp.org',
subject: dedent` subject: dedent`
Congratulations on completing all of the Congratulations on completing all of the
freeCodeCamp certificates! freeCodeCamp certifications!
`, `,
text: renderCertifedEmail({ text: renderCertifedEmail({
username, username,
@ -190,16 +190,16 @@ export default function certificate(app) {
verifyCert verifyCert
); );
router.get( router.get(
'/certificates/:username/:cert', '/certification/:username/:cert',
showCert showCert
); );
app.use(router); app.use(router);
const noNameMessage = dedent` const noNameMessage = dedent`
We need your name so we can put it on your certificate. We need your name so we can put it on your certification.
Add your name to your account settings and click the save button. Add your name to your account settings and click the save button.
Then we can issue your certificate. Then we can issue your certification.
`; `;
const notCertifiedMessage = name => dedent` const notCertifiedMessage = name => dedent`

View File

@ -39,11 +39,12 @@ module.exports = function(app, done) {
const homePage = user ? 'userHome' : 'noUserHome'; const homePage = user ? 'userHome' : 'noUserHome';
const { quote, author} = getRandomQuote(); const { quote, author} = getRandomQuote();
const title = user ? const title = user ?
`Welcome back ${user.name ? user.name : 'Camper'}` : `Welcome, ${user.name ? user.name : 'Camper'}!` :
'Learn to Code and Help Nonprofits'; 'Learn to Code and Help Nonprofits';
const completedChallengeCount = user && user.completedChallengeCount || 0; const completedChallengeCount = user && user.completedChallengeCount || 0;
const completedProjectCount = user && user.completedProjectCount || 0; const completedProjectCount = user && user.completedProjectCount || 0;
const completedCertCount = user && user.completedCertCount || 0; const completedCertCount = user && user.completedCertCount || 0;
const completedLegacyCertCount = user && user.completedLegacyCertCount || 0;
Promise.all([ Promise.all([
// news.getFeed(), // news.getFeed(),
About.getActiveUsersForRendering() About.getActiveUsersForRendering()
@ -60,6 +61,7 @@ module.exports = function(app, done) {
completedChallengeCount, completedChallengeCount,
completedProjectCount, completedProjectCount,
completedCertCount, completedCertCount,
completedLegacyCertCount,
// feed, // feed,
quote, quote,
title title

View File

@ -22,7 +22,14 @@ function getCompletedCertCount(user) {
'isJsAlgoDataStructCert', 'isJsAlgoDataStructCert',
'isRespWebDesignCert' 'isRespWebDesignCert'
].reduce((sum, key) => user[key] ? sum + 1 : sum, 0); ].reduce((sum, key) => user[key] ? sum + 1 : sum, 0);
}
function getLegacyCertCount(user) {
return [
'isFrontEndCert',
'isBackEndCert',
'isDataVisCert'
].reduce((sum, key) => user[key] ? sum + 1 : sum, 0);
} }
PassportConfigurator.prototype.init = function passportInit(noSession) { PassportConfigurator.prototype.init = function passportInit(noSession) {
@ -72,6 +79,7 @@ PassportConfigurator.prototype.init = function passportInit(noSession) {
user.completedChallengeCount = completedChallengeCount; user.completedChallengeCount = completedChallengeCount;
user.completedProjectCount = completedProjectCount; user.completedProjectCount = completedProjectCount;
user.completedCertCount = getCompletedCertCount(user); user.completedCertCount = getCompletedCertCount(user);
user.completedLegacyCertCount = getLegacyCertCount(user);
user.completedChallenges = []; user.completedChallenges = [];
return done(null, user); return done(null, user);
}); });

View File

@ -20,7 +20,7 @@ include styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Advanced Frontend Projects strong Advanced Frontend Projects
h4 1 of 3 legacy freeCodeCamp certificates, representing approximately 400 hours of coursework h4 1 of 3 legacy freeCodeCamp certification, representing approximately 400 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/advanced-front-end p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/advanced-front-end

View File

@ -20,7 +20,7 @@ include styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong APIs and Microservices Projects strong APIs and Microservices Projects
h4 1 of 6 freeCodeCamp certificates, representing approximately 300 hours of coursework h4 1 of 6 freeCodeCamp certifications, representing approximately 300 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/apis-and-microservices p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/apis-and-microservices

View File

@ -20,7 +20,7 @@ include styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Data Visualization Projects strong Data Visualization Projects
h4 1 of 6 freeCodeCamp certificates, representing approximately 300 hours of coursework h4 1 of 6 freeCodeCamp certifications, representing approximately 300 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/data-visualization p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/data-visualization

View File

@ -20,7 +20,7 @@ include styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Front End Libraries Projects strong Front End Libraries Projects
h4 1 of 6 freeCodeCamp certificates, representing approximately 300 hours of coursework h4 1 of 6 freeCodeCamp certifications, representing approximately 300 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/front-end-libraries p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/front-end-libraries

View File

@ -20,7 +20,7 @@ include styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Information Security and Quality Assurance Projects strong Information Security and Quality Assurance Projects
h4 1 of 6 freeCodeCamp certificates, representing approximately 300 hours of coursework h4 1 of 6 freeCodeCamp certifications, representing approximately 300 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/information-security-and-quality-assurance p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/information-security-and-quality-assurance

View File

@ -19,8 +19,8 @@ include styles
strong= name strong= name
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong JavaScript Algorithms and Data Structures Certificate strong JavaScript Algorithms and Data Structures Projects
h4 1 of 6 freeCodeCamp certificates, representing approximately 300 hours of coursework h4 1 of 6 freeCodeCamp certifications, representing approximately 300 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/javascript-algorithms-and-data-structures p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/javascript-algorithms-and-data-structures

View File

@ -20,7 +20,7 @@ include ../styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Back End Development Projects strong Back End Development Projects
h4 1 of 3 legacy freeCodeCamp certificates, representing approximately 400 hours of coursework h4 1 of 3 legacy freeCodeCamp certifications, representing approximately 400 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include ../styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-back-end p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/legacy-back-end

View File

@ -20,7 +20,7 @@ include ../styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Data Visualization Projects strong Data Visualization Projects
h4 1 of 3 legacy freeCodeCamp certificates, representing approximately 400 hours of coursework h4 1 of 3 legacy freeCodeCamp certifications, representing approximately 400 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include ../styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-data-visualization p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/legacy-data-visualization

View File

@ -20,7 +20,7 @@ include ../styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Front End Development Projects strong Front End Development Projects
h4 1 of 3 legacy freeCodeCamp certificates, representing approximately 400 hours of coursework h4 1 of 3 legacy freeCodeCamp certifications, representing approximately 400 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include ../styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-front-end p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/legacy-front-end

View File

@ -20,7 +20,7 @@ include ../styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Legacy Full Stack Development Program strong Legacy Full Stack Development Program
h4 All three of the legacy freeCodeCamp certificates, representing approximately 12000 hours of coursework h4 All three of the legacy freeCodeCamp certifications, representing approximately 12000 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include ../styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-full-stack p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/legacy-full-stack

View File

@ -20,7 +20,7 @@ include styles
h3 has successfully completed freeCodeCamp's h3 has successfully completed freeCodeCamp's
h1 h1
strong Responsive Web Design Projects strong Responsive Web Design Projects
h4 1 of 6 freeCodeCamp certificates, representing approximately 300 hours of coursework h4 1 of 6 freeCodeCamp certifications, representing approximately 300 hours of coursework
footer footer
.row.signatures .row.signatures
@ -29,4 +29,4 @@ include styles
strong Quincy Larson strong Quincy Larson
p Executive Director, freeCodeCamp.org p Executive Director, freeCodeCamp.org
.row .row
p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/responsive-web-design p.verify Verify this certification at: https://freecodecamp.org/certification/#{username}/responsive-web-design

View File

@ -1,8 +1,8 @@
Hi <%= name || username %>, Hi <%= name || username %>,
Congratulations on completing all of the freeCodeCamp certificates! Congratulations on completing all of the freeCodeCamp certifications!
All of your certificates are now live at at: https://www.freecodecamp.org/<%= username %> All of your certifications are now live at at: https://www.freecodecamp.org/<%= username %>
Please tell me a bit more about you and your near-term goals. Please tell me a bit more about you and your near-term goals.

View File

@ -16,6 +16,15 @@
strong strong
span.green-text 30 span.green-text 30
| projects | projects
if !!completedLegacyCertCount
p.stats
| You have earned&nbsp;
strong
span.green-text=completedLegacyCertCount
| out of&nbsp;
strong
span.green-text 3
| legacy certifications
p.stats p.stats
| You have earned&nbsp; | You have earned&nbsp;
strong strong

View File

@ -13,7 +13,7 @@ block content
.col-xs-12.col-sm-12.col-md-4 .col-xs-12.col-sm-12.col-md-4
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landing-icon-certificate.svg', alt='Help nonprofits with bro bono code projects') img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landing-icon-certificate.svg', alt='Help nonprofits with bro bono code projects')
p.large-p Build projects and earn free certificates. p.large-p Build projects and earn free certifications.
.col-xs-12.col-sm-12.col-md-4 .col-xs-12.col-sm-12.col-md-4
img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landing-icon-experience.svg', alt='Get hired as a developer and start your software engineer career') img.img-responsive.landing-icon.img-center(src= 'https://s3.amazonaws.com/freecodecamp/landing-icon-experience.svg', alt='Get hired as a developer and start your software engineer career')

View File

@ -15,7 +15,7 @@ block content
.big-break .big-break
.row .row
.col-xs-12.col-sm-8.col-sm-offset-2 .col-xs-12.col-sm-8.col-sm-offset-2
a.btn.btn-cta.btn-block.btn-lg.btn-primary(href="/challenges/current-challenge") Go to my next lesson a.btn.btn-cta.btn-block.btn-lg.btn-primary(href="/challenges/current-challenge") Go to my next coding challenge
.big-break .big-break
.big-break .big-break
.big-break .big-break