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:
committed by
Quincy Larson
parent
848e04028e
commit
a76c8aff5a
@ -121,7 +121,9 @@ export function projectsSelector(state) {
|
||||
);
|
||||
return Object.keys(blocks)
|
||||
.filter(key =>
|
||||
key.includes('projects') && !key.includes('coding-interview')
|
||||
key.includes('projects') && !(
|
||||
key.includes('coding-interview') || key.includes('take-home')
|
||||
)
|
||||
)
|
||||
.map(key => blocks[key])
|
||||
.concat(legacyWithDashedNames)
|
||||
|
@ -58,7 +58,7 @@ function CamperHOC({
|
||||
location={ showLocation && location }
|
||||
name={ showName && name }
|
||||
picture={ picture }
|
||||
points={ showPoints && points }
|
||||
points={ showPoints ? points : 0 }
|
||||
username={ username }
|
||||
/>
|
||||
<hr />
|
||||
|
@ -9,6 +9,8 @@ import {
|
||||
Col
|
||||
} from 'react-bootstrap';
|
||||
|
||||
import { FullWidthRow } from '../../../helperComponents';
|
||||
|
||||
import { userByNameSelector } from '../../../redux';
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
@ -40,59 +42,59 @@ const mapStateToProps = createSelector(
|
||||
currentCerts: [
|
||||
{
|
||||
show: is2018FullStackCert,
|
||||
title: 'Full Stack Certificate:',
|
||||
title: 'Full Stack Certification:',
|
||||
showURL: '2018-full-stack'
|
||||
},
|
||||
{
|
||||
show: isRespWebDesignCert,
|
||||
title: 'Responsive Web Design Certificate:',
|
||||
title: 'Responsive Web Design Certification:',
|
||||
showURL: 'responsive-web-design'
|
||||
},
|
||||
{
|
||||
show: isJsAlgoDataStructCert,
|
||||
title: 'JavaScript Algorithms and Data Structures Certificate:',
|
||||
title: 'JavaScript Algorithms and Data Structures Certification:',
|
||||
showURL: 'javascript-algorithms-and-data-structures'
|
||||
},
|
||||
{
|
||||
show: isFrontEndLibsCert,
|
||||
title: 'Front End Libraries Certificate:',
|
||||
title: 'Front End Libraries Certification:',
|
||||
showURL: 'front-end-libraries'
|
||||
},
|
||||
{
|
||||
show: is2018DataVisCert,
|
||||
title: 'Data Visualization Certificate:',
|
||||
title: 'Data Visualization Certification:',
|
||||
showURL: 'data-visualization'
|
||||
},
|
||||
{
|
||||
show: isApisMicroservicesCert,
|
||||
title: 'APIs and Microservices Certificate:',
|
||||
title: 'APIs and Microservices Certification:',
|
||||
showURL: 'apis-and-microservices'
|
||||
},
|
||||
{
|
||||
show: isInfosecQaCert,
|
||||
title: 'Information Security and Quality Assurance Certificate:',
|
||||
title: 'Information Security and Quality Assurance Certification:',
|
||||
showURL: 'information-security-and-quality-assurance'
|
||||
}
|
||||
],
|
||||
legacyCerts: [
|
||||
{
|
||||
show: isFullStackCert,
|
||||
title: 'Full Stack Certificate:',
|
||||
title: 'Full Stack Certification:',
|
||||
showURL: 'legacy-full-stack'
|
||||
},
|
||||
{
|
||||
show: isFrontEndCert,
|
||||
title: 'Front End Certificate:',
|
||||
title: 'Front End Certification:',
|
||||
showURL: 'legacy-front-end'
|
||||
},
|
||||
{
|
||||
show: isBackEndCert,
|
||||
title: 'Back End Certificate:',
|
||||
title: 'Back End Certification:',
|
||||
showURL: 'legacy-back-end'
|
||||
},
|
||||
{
|
||||
show: isDataVisCert,
|
||||
title: 'Data Visualization Certificate:',
|
||||
title: 'Data Visualization Certification:',
|
||||
showURL: 'legacy-data-visualization'
|
||||
}
|
||||
]
|
||||
@ -123,7 +125,7 @@ function renderCertShow(username, cert) {
|
||||
return cert.show ? (
|
||||
<Row key={ cert.showURL }>
|
||||
<Col sm={ 8 }>
|
||||
<p>
|
||||
<p className='bigger-text'>
|
||||
<strong>
|
||||
{ cert.title }
|
||||
</strong>
|
||||
@ -134,7 +136,7 @@ function renderCertShow(username, cert) {
|
||||
block={ true }
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
href={ `/certificates/${username}/${cert.showURL}`}
|
||||
href={ `/certification/${username}/${cert.showURL}`}
|
||||
>
|
||||
Show
|
||||
</Button>
|
||||
@ -154,25 +156,27 @@ function Certificates({
|
||||
const renderCertShowWithUsername = _.curry(renderCertShow)(username);
|
||||
return (
|
||||
<div>
|
||||
<h2 className='text-center'>freeCodeCamp Certificates</h2>
|
||||
<br />
|
||||
{
|
||||
hasModernCert ?
|
||||
currentCerts.map(renderCertShowWithUsername) :
|
||||
<p className='text-center' >
|
||||
No certificates have been earned under the current curriculum
|
||||
</p>
|
||||
}
|
||||
{
|
||||
hasLegacyCert ?
|
||||
<div>
|
||||
<h3>Legacy Certifications</h3>
|
||||
{
|
||||
legacyCerts.map(renderCertShowWithUsername)
|
||||
}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
<FullWidthRow>
|
||||
<h2 className='text-center'>freeCodeCamp Certifications</h2>
|
||||
<br />
|
||||
{
|
||||
hasModernCert ?
|
||||
currentCerts.map(renderCertShowWithUsername) :
|
||||
<p className='text-center' >
|
||||
No certifications have been earned under the current curriculum
|
||||
</p>
|
||||
}
|
||||
{
|
||||
hasLegacyCert ?
|
||||
<div>
|
||||
<h3>Legacy Certifications</h3>
|
||||
{
|
||||
legacyCerts.map(renderCertShowWithUsername)
|
||||
}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
</FullWidthRow>
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
|
@ -3,8 +3,10 @@ import PropTypes from 'prop-types';
|
||||
import { createSelector } from 'reselect';
|
||||
import { connect } from 'react-redux';
|
||||
import format from 'date-fns/format';
|
||||
import { reverse, sortBy } from 'lodash';
|
||||
import { find, reverse, sortBy } from 'lodash';
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
Table
|
||||
} from 'react-bootstrap';
|
||||
|
||||
@ -12,18 +14,20 @@ import { challengeIdToNameMapSelector } from '../../../entities';
|
||||
import { userByNameSelector } from '../../../redux';
|
||||
import { homeURL } from '../../../../utils/constantStrings.json';
|
||||
import blockNameify from '../../../utils/blockNameify';
|
||||
import { FullWidthRow } from '../../../helperComponents';
|
||||
import { Link } from '../../../Router';
|
||||
import { FullWidthRow } from '../../../helperComponents';
|
||||
import SolutionViewer from '../../Settings/components/SolutionViewer.jsx';
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
challengeIdToNameMapSelector,
|
||||
userByNameSelector,
|
||||
(
|
||||
idToNameMap,
|
||||
{ completedChallenges: completedMap = [] }
|
||||
{ completedChallenges: completedMap = [], username }
|
||||
) => ({
|
||||
completedMap,
|
||||
idToNameMap
|
||||
idToNameMap,
|
||||
username
|
||||
})
|
||||
);
|
||||
|
||||
@ -32,38 +36,94 @@ const propTypes = {
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
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 {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
solutionToView: null,
|
||||
solutionOpen: false
|
||||
};
|
||||
|
||||
this.closeSolution = this.closeSolution.bind(this);
|
||||
this.renderCompletion = this.renderCompletion.bind(this);
|
||||
this.viewSolution = this.viewSolution.bind(this);
|
||||
}
|
||||
|
||||
renderCompletion(completed) {
|
||||
const { idToNameMap } = this.props;
|
||||
const { id, completedDate } = completed;
|
||||
const { id, completedDate, solution, files } = completed;
|
||||
const challengeDashedName = idToNameMap[id];
|
||||
return (
|
||||
<tr key={ id }>
|
||||
<td>{ blockNameify(idToNameMap[id]) }</td>
|
||||
<td>
|
||||
<a href={`/challenges/${challengeDashedName}`}>
|
||||
{ blockNameify(challengeDashedName) }
|
||||
</a>
|
||||
</td>
|
||||
<td className='text-center'>
|
||||
<time dateTime={ format(completedDate, 'YYYY-MM-DDTHH:MM:SSZ') }>
|
||||
{
|
||||
format(completedDate, 'MMMM DD YYYY')
|
||||
format(completedDate, 'MMMM D, YYYY')
|
||||
}
|
||||
</time>
|
||||
</td>
|
||||
<td>
|
||||
{/* eslint-disable no-nested-ternary */
|
||||
files ? (
|
||||
<Button
|
||||
block={ true }
|
||||
bsStyle='primary'
|
||||
onClick={ () => this.viewSolution(id) }
|
||||
>
|
||||
View Solution
|
||||
</Button>
|
||||
) : solution ? (
|
||||
<Button
|
||||
block={ true }
|
||||
bsStyle='primary'
|
||||
href={solution}
|
||||
target='_blank'
|
||||
>
|
||||
View Solution
|
||||
</Button>
|
||||
) : ''
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
viewSolution(id) {
|
||||
this.setState(state => ({
|
||||
...state,
|
||||
solutionToView: id,
|
||||
solutionOpen: true
|
||||
}));
|
||||
}
|
||||
|
||||
closeSolution() {
|
||||
this.setState(state => ({
|
||||
...state,
|
||||
solutionToView: null,
|
||||
solutionOpen: false
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { completedMap, idToNameMap } = this.props;
|
||||
const { completedMap, idToNameMap, username } = this.props;
|
||||
const { solutionToView: id, solutionOpen } = this.state;
|
||||
if (!Object.keys(idToNameMap).length) {
|
||||
return null;
|
||||
}
|
||||
@ -82,7 +142,7 @@ class Timeline extends PureComponent {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Challenge</th>
|
||||
<th>First Completed</th>
|
||||
<th className='text-center'>First Completed</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -98,6 +158,30 @@ class Timeline extends PureComponent {
|
||||
</tbody>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
@ -13,6 +13,18 @@
|
||||
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 {
|
||||
|
||||
@ -41,10 +53,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.bio {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2, h3 {
|
||||
&.name, &.username, &.points, &.location {
|
||||
margin-top: 0;
|
||||
|
@ -4,7 +4,6 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { Button } from 'react-bootstrap';
|
||||
import FA from 'react-fontawesome';
|
||||
|
||||
import ns from './ns.json';
|
||||
import { FullWidthRow, Spacer, Loader } from '../../helperComponents';
|
||||
@ -85,8 +84,7 @@ export class Settings extends React.Component {
|
||||
bsStyle='primary'
|
||||
className='btn-link-social'
|
||||
>
|
||||
<FA name='user' />
|
||||
Show me my public profile
|
||||
Show me my public portfolio
|
||||
</Button>
|
||||
</Link>
|
||||
<Button
|
||||
|
@ -12,7 +12,6 @@ import { FullWidthRow, Spacer } from '../../../helperComponents';
|
||||
import ThemeSettings from './ThemeSettings.jsx';
|
||||
import Camper from './Camper.jsx';
|
||||
import UsernameSettings from './UsernameSettings.jsx';
|
||||
import SectionHeader from './SectionHeader.jsx';
|
||||
import { userSelector, toggleNightMode } from '../../../redux';
|
||||
import { updateUserBackend } from '../redux';
|
||||
import {
|
||||
@ -165,9 +164,6 @@ class AboutSettings extends PureComponent {
|
||||
const toggleTheme = () => toggleNightMode(username, currentTheme);
|
||||
return (
|
||||
<div className='about-settings'>
|
||||
<SectionHeader>
|
||||
About Settings
|
||||
</SectionHeader>
|
||||
<UsernameSettings username={ username }/>
|
||||
<FullWidthRow>
|
||||
<Nav
|
||||
|
@ -180,7 +180,7 @@ class CertificationSettings extends PureComponent {
|
||||
<FullWidthRow key={superBlock}>
|
||||
<h3 className='project-heading'>{ projectBlockName }</h3>
|
||||
<Form
|
||||
buttonText={ fullForm ? 'Claim Certificate' : 'Save Progress' }
|
||||
buttonText={ fullForm ? 'Claim Certification' : 'Save Progress' }
|
||||
enableSubmit={ fullForm }
|
||||
formFields={ challengeTitles.concat([ 'id' ]) }
|
||||
hideButton={isCertClaimed}
|
||||
@ -198,10 +198,10 @@ class CertificationSettings extends PureComponent {
|
||||
block={ true }
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
href={ `/certificates/${username}/${superBlock}`}
|
||||
href={ `/certification/${username}/${superBlock}`}
|
||||
target='_blank'
|
||||
>
|
||||
Show Certificate
|
||||
Show Certification
|
||||
</Button> :
|
||||
null
|
||||
}
|
||||
@ -215,11 +215,11 @@ class CertificationSettings extends PureComponent {
|
||||
const { allProjects } = this.props;
|
||||
let project = _.find(allProjects, { superBlock: id });
|
||||
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(
|
||||
new Error(
|
||||
'Submitted projects do not belong to either current or ' +
|
||||
'legacy certificates'
|
||||
'legacy certifications'
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -268,7 +268,7 @@ class CertificationSettings extends PureComponent {
|
||||
<FullWidthRow>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</FullWidthRow>
|
||||
@ -276,7 +276,7 @@ class CertificationSettings extends PureComponent {
|
||||
modernProjects.map(this.buildProjectForms)
|
||||
}
|
||||
<SectionHeader>
|
||||
Legacy Certificate Settings
|
||||
Legacy Certification Settings
|
||||
</SectionHeader>
|
||||
{
|
||||
legacyProjects.map(this.buildProjectForms)
|
||||
|
@ -13,13 +13,29 @@ import {
|
||||
} from 'react-bootstrap';
|
||||
|
||||
import TB from '../Toggle-Button';
|
||||
import EmailForm from './EmailForm.jsx';
|
||||
// import EmailForm from './EmailForm.jsx';
|
||||
import { Link } from '../../../Router';
|
||||
import { FullWidthRow, Spacer } from '../../../helperComponents';
|
||||
import { FullWidthRow } from '../../../helperComponents';
|
||||
import SectionHeader from './SectionHeader.jsx';
|
||||
import { userSelector } 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(
|
||||
userSelector,
|
||||
({
|
||||
@ -101,9 +117,8 @@ class EmailSettings extends PureComponent {
|
||||
You do not have an email associated with this account.
|
||||
</p>
|
||||
</FullWidthRow>
|
||||
<FullWidthRow>
|
||||
<UpdateEmailButton />
|
||||
</FullWidthRow>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -124,12 +139,8 @@ class EmailSettings extends PureComponent {
|
||||
</HelpBlock>
|
||||
</FullWidthRow>
|
||||
}
|
||||
<FullWidthRow>
|
||||
<EmailForm
|
||||
initialValues={{ email, confrimEmail: ''}}
|
||||
/>
|
||||
</FullWidthRow>
|
||||
<Spacer />
|
||||
|
||||
|
||||
<FullWidthRow>
|
||||
<Row className='inline-form-field' key='sendQuincyEmail'>
|
||||
<Col sm={ 8 }>
|
||||
|
@ -45,7 +45,7 @@ class Honesty extends PureComponent {
|
||||
const isHonestAgreed = (
|
||||
<Panel bsStyle='info'>
|
||||
<p>
|
||||
You have already accepted our Academic Honesty Policy
|
||||
You have accepted our Academic Honesty Policy
|
||||
</p>
|
||||
</Panel>
|
||||
);
|
||||
|
@ -22,12 +22,17 @@ const mapStateToProps = createSelector(
|
||||
githubProfile,
|
||||
linkedin,
|
||||
twitter,
|
||||
website
|
||||
personalWebsite: website
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const formFields = [ 'githubProfile', 'linkedin', 'twitter', 'website' ];
|
||||
const formFields = [
|
||||
'githubProfile',
|
||||
'linkedin',
|
||||
'twitter',
|
||||
'personalWebsite'
|
||||
];
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators({
|
||||
@ -40,10 +45,10 @@ const propTypes = {
|
||||
githubProfile: PropTypes.string,
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
linkedin: PropTypes.string,
|
||||
personalWebsite: PropTypes.string,
|
||||
twitter: PropTypes.string,
|
||||
updateUserBackend: PropTypes.func.isRequired,
|
||||
username: PropTypes.string,
|
||||
website: PropTypes.string
|
||||
username: PropTypes.string
|
||||
};
|
||||
|
||||
class InternetSettings extends PureComponent {
|
||||
@ -53,8 +58,8 @@ class InternetSettings extends PureComponent {
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
handleSubmit(values) {
|
||||
this.props.updateUserBackend(values);
|
||||
handleSubmit({personalWebsite, ...others}) {
|
||||
this.props.updateUserBackend({ ...others, website: personalWebsite });
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -43,7 +43,7 @@ class JSAlgoAndDSForm extends PureComponent {
|
||||
e.preventDefault();
|
||||
const { username, superBlock, isCertClaimed } = this.props;
|
||||
if (isCertClaimed) {
|
||||
return this.props.hardGoTo(`/certificates/${username}/${superBlock}`);
|
||||
return this.props.hardGoTo(`/certification/${username}/${superBlock}`);
|
||||
}
|
||||
return this.props.claimCert(superBlock);
|
||||
}
|
||||
@ -107,7 +107,7 @@ class JSAlgoAndDSForm extends PureComponent {
|
||||
Object.keys(jsProjects).length === completeCount ?
|
||||
<form onSubmit={ this.handleSubmit }>
|
||||
<BlockSaveButton>
|
||||
{ isCertClaimed ? 'Show' : 'Claim'} Certificate
|
||||
{ isCertClaimed ? 'Show' : 'Claim'} Certification
|
||||
</BlockSaveButton>
|
||||
</form> :
|
||||
null
|
||||
|
@ -26,7 +26,7 @@ function ResetModal(props) {
|
||||
<Modal.Body>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { reduxForm } from 'redux-form';
|
||||
|
||||
import { FormFields, BlockSaveButton, BlockSaveWrapper } from './';
|
||||
import { FormFields, BlockSaveButton } from './';
|
||||
|
||||
const propTypes = {
|
||||
buttonText: PropTypes.string,
|
||||
@ -50,22 +50,20 @@ function DynamicForm({
|
||||
fields={ fields }
|
||||
options={ options }
|
||||
/>
|
||||
<BlockSaveWrapper>
|
||||
{
|
||||
hideButton ?
|
||||
null :
|
||||
<BlockSaveButton
|
||||
disabled={
|
||||
allPristine && !enableSubmit ||
|
||||
(!!Object.keys(errors).filter(key => errors[key]).length)
|
||||
}
|
||||
>
|
||||
{
|
||||
buttonText ? buttonText : null
|
||||
}
|
||||
</BlockSaveButton>
|
||||
}
|
||||
</BlockSaveWrapper>
|
||||
{
|
||||
hideButton ?
|
||||
null :
|
||||
<BlockSaveButton
|
||||
disabled={
|
||||
allPristine && !enableSubmit ||
|
||||
(!!Object.keys(errors).filter(key => errors[key]).length)
|
||||
}
|
||||
>
|
||||
{
|
||||
buttonText ? buttonText : null
|
||||
}
|
||||
</BlockSaveButton>
|
||||
}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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.',
|
||||
'I understand that plagiarism means copying someone else’s work and ' +
|
||||
'presenting the work as if it were my own, without clearly attributing ' +
|
||||
|
@ -138,7 +138,7 @@ function sendCertifiedEmail(
|
||||
from: 'team@freeCodeCamp.org',
|
||||
subject: dedent`
|
||||
Congratulations on completing all of the
|
||||
freeCodeCamp certificates!
|
||||
freeCodeCamp certifications!
|
||||
`,
|
||||
text: renderCertifedEmail({
|
||||
username,
|
||||
@ -190,16 +190,16 @@ export default function certificate(app) {
|
||||
verifyCert
|
||||
);
|
||||
router.get(
|
||||
'/certificates/:username/:cert',
|
||||
'/certification/:username/:cert',
|
||||
showCert
|
||||
);
|
||||
|
||||
app.use(router);
|
||||
|
||||
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.
|
||||
Then we can issue your certificate.
|
||||
Then we can issue your certification.
|
||||
`;
|
||||
|
||||
const notCertifiedMessage = name => dedent`
|
||||
|
@ -39,11 +39,12 @@ module.exports = function(app, done) {
|
||||
const homePage = user ? 'userHome' : 'noUserHome';
|
||||
const { quote, author} = getRandomQuote();
|
||||
const title = user ?
|
||||
`Welcome back ${user.name ? user.name : 'Camper'}` :
|
||||
`Welcome, ${user.name ? user.name : 'Camper'}!` :
|
||||
'Learn to Code and Help Nonprofits';
|
||||
const completedChallengeCount = user && user.completedChallengeCount || 0;
|
||||
const completedProjectCount = user && user.completedProjectCount || 0;
|
||||
const completedCertCount = user && user.completedCertCount || 0;
|
||||
const completedLegacyCertCount = user && user.completedLegacyCertCount || 0;
|
||||
Promise.all([
|
||||
// news.getFeed(),
|
||||
About.getActiveUsersForRendering()
|
||||
@ -60,6 +61,7 @@ module.exports = function(app, done) {
|
||||
completedChallengeCount,
|
||||
completedProjectCount,
|
||||
completedCertCount,
|
||||
completedLegacyCertCount,
|
||||
// feed,
|
||||
quote,
|
||||
title
|
||||
|
@ -22,7 +22,14 @@ function getCompletedCertCount(user) {
|
||||
'isJsAlgoDataStructCert',
|
||||
'isRespWebDesignCert'
|
||||
].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) {
|
||||
@ -72,6 +79,7 @@ PassportConfigurator.prototype.init = function passportInit(noSession) {
|
||||
user.completedChallengeCount = completedChallengeCount;
|
||||
user.completedProjectCount = completedProjectCount;
|
||||
user.completedCertCount = getCompletedCertCount(user);
|
||||
user.completedLegacyCertCount = getLegacyCertCount(user);
|
||||
user.completedChallenges = [];
|
||||
return done(null, user);
|
||||
});
|
||||
|
@ -20,7 +20,7 @@ include styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
@ -20,7 +20,7 @@ include styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -20,7 +20,7 @@ include styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -20,7 +20,7 @@ include styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -20,7 +20,7 @@ include styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -19,8 +19,8 @@ include styles
|
||||
strong= name
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
strong JavaScript Algorithms and Data Structures Certificate
|
||||
h4 1 of 6 freeCodeCamp certificates, representing approximately 300 hours of coursework
|
||||
strong JavaScript Algorithms and Data Structures Projects
|
||||
h4 1 of 6 freeCodeCamp certifications, representing approximately 300 hours of coursework
|
||||
|
||||
footer
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -20,7 +20,7 @@ include ../styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include ../styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -20,7 +20,7 @@ include ../styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include ../styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -20,7 +20,7 @@ include ../styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include ../styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -20,7 +20,7 @@ include ../styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include ../styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
@ -20,7 +20,7 @@ include styles
|
||||
h3 has successfully completed freeCodeCamp's
|
||||
h1
|
||||
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
|
||||
.row.signatures
|
||||
@ -29,4 +29,4 @@ include styles
|
||||
strong Quincy Larson
|
||||
p Executive Director, freeCodeCamp.org
|
||||
.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
|
||||
|
@ -1,8 +1,8 @@
|
||||
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.
|
||||
|
||||
|
@ -16,6 +16,15 @@
|
||||
strong
|
||||
span.green-text 30
|
||||
| projects
|
||||
if !!completedLegacyCertCount
|
||||
p.stats
|
||||
| You have earned
|
||||
strong
|
||||
span.green-text=completedLegacyCertCount
|
||||
| out of
|
||||
strong
|
||||
span.green-text 3
|
||||
| legacy certifications
|
||||
p.stats
|
||||
| You have earned
|
||||
strong
|
||||
|
@ -13,7 +13,7 @@ block content
|
||||
|
||||
.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')
|
||||
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
|
||||
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')
|
||||
|
@ -15,7 +15,7 @@ block content
|
||||
.big-break
|
||||
.row
|
||||
.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
|
||||
|
Reference in New Issue
Block a user