feat(settings): Expand Settings page functionality (#16664)

* fix(layout): Fix Settings layout in firefox

* chore(availableForHire): Remove available for hire setting

* feat(helpers): Use helper components for Settings layout

* fix(map): Fix undefined lang requested

* feat(settings): Expand Settings page functionality

* chore(pledge): Remove pledge from Settings

* fix(about): Adjust AboutSettings layout

* fix(portfolio): Improve PortfolioSettings layout

* fix(email): Improve EmailSettings layout

* fix(settings): Align save buttons with form fields

* fix(AHP): Format AHP

* fix(DangerZone): Adjust DangerZone layout

* fix(projectSettings): Change Button Copy

* fix(CertSettings): Fix certificate claim logic

* chore(lint): Lint
This commit is contained in:
Stuart Taylor
2018-02-16 23:18:53 +00:00
committed by Quincy Larson
parent 9f034f4f79
commit 24ef69cf7a
78 changed files with 4395 additions and 1724 deletions

View File

@@ -2,6 +2,7 @@ import dedent from 'dedent';
import moment from 'moment-timezone';
import { Observable } from 'rx';
import debugFactory from 'debug';
// import { curry } from 'lodash';
import emoji from 'node-emoji';
import {
@@ -11,10 +12,12 @@ import {
frontEndLibsId,
jsAlgoDataStructId,
dataVisId,
dataVis2018Id,
apisMicroservicesId,
infosecQaId
} from '../utils/constantStrings.json';
import certTypes from '../utils/certTypes.json';
import superBlockCertTypeMap from '../utils/superBlockCertTypeMap';
import {
ifNoUser401,
ifNoUserRedirectTo,
@@ -32,6 +35,7 @@ import { getChallengeInfo, cachedMap } from '../utils/map';
const debug = debugFactory('fcc:boot:user');
const sendNonUserToMap = ifNoUserRedirectTo('/map');
// const sendNonUserToMapWithMessage = curry(ifNoUserRedirectTo, 2)('/map');
const certIds = {
[certTypes.frontEnd]: frontEndChallengeId,
[certTypes.backEnd]: backEndChallengeId,
@@ -39,6 +43,7 @@ const certIds = {
[certTypes.frontEndLibs]: frontEndLibsId,
[certTypes.jsAlgoDataStruct]: jsAlgoDataStructId,
[certTypes.dataVis]: dataVisId,
[certTypes.dataVis2018]: dataVis2018Id,
[certTypes.apisMicroservices]: apisMicroservicesId,
[certTypes.infosecQa]: infosecQaId
};
@@ -52,6 +57,7 @@ const certViews = {
[certTypes.jsAlgoDataStruct]:
'certificate/javascript-algorithms-and-data-structures.jade',
[certTypes.dataVis]: 'certificate/data-visualization.jade',
[certTypes.dataVis2018]: 'certificate/data-visualization-2018.jade',
[certTypes.apisMicroservices]: 'certificate/apis-and-microservices.jade',
[certTypes.infosecQa]:
'certificate/information-security-and-quality-assurance.jade'
@@ -66,6 +72,7 @@ const certText = {
[certTypes.jsAlgoDataStruct]:
'JavaScript Algorithms and Data Structures Certified',
[certTypes.dataVis]: 'Data Visualization Certified',
[certTypes.dataVis2018]: 'Data Visualization Certified',
[certTypes.apisMicroservices]: 'APIs and Microservices Certified',
[certTypes.infosecQa]: 'Information Security and Quality Assurance Certified'
};
@@ -160,11 +167,6 @@ module.exports = function(app) {
);
}
router.get(
'/delete-my-account',
sendNonUserToMap,
showDelete
);
api.post(
'/account/delete',
ifNoUser401,
@@ -175,17 +177,11 @@ module.exports = function(app) {
sendNonUserToMap,
getAccount
);
router.get(
'/reset-my-progress',
sendNonUserToMap,
showResetProgress
);
api.post(
'/account/resetprogress',
'/account/reset-progress',
ifNoUser401,
postResetProgress
);
api.get(
'/account/unlink/:social',
sendNonUserToMap,
@@ -194,48 +190,8 @@ module.exports = function(app) {
// Ensure these are the last routes!
api.get(
'/:username/front-end-certification',
showCert.bind(null, certTypes.frontEnd)
);
api.get(
'/:username/back-end-certification',
showCert.bind(null, certTypes.backEnd)
);
api.get(
'/:username/full-stack-certification',
(req, res) => res.redirect(req.url.replace('full-stack', 'back-end'))
);
api.get(
'/:username/responsive-web-design-certification',
showCert.bind(null, certTypes.respWebDesign)
);
api.get(
'/:username/front-end-libraries-certification',
showCert.bind(null, certTypes.frontEndLibs)
);
api.get(
'/:username/javascript-algorithms-data-structures-certification',
showCert.bind(null, certTypes.jsAlgoDataStruct)
);
api.get(
'/:username/data-visualization-certification',
showCert.bind(null, certTypes.dataVis)
);
api.get(
'/:username/apis-microservices-certification',
showCert.bind(null, certTypes.apisMicroservices)
);
api.get(
'/:username/information-security-quality-assurance-certification',
showCert.bind(null, certTypes.infosecQa)
'/c/:username/:cert',
showCert
);
router.get('/:username', showUserProfile);
@@ -410,14 +366,14 @@ module.exports = function(app) {
);
}
function showCert(certType, req, res, next) {
const username = req.params.username.toLowerCase();
function showCert(req, res, next) {
let { username, cert } = req.params;
username = username.toLowerCase();
const certType = superBlockCertTypeMap[cert];
const certId = certIds[certType];
return findUserByUsername$(username, {
isGithubCool: true,
isCheater: true,
isLocked: true,
isAvailableForHire: true,
isFrontEndCert: true,
isBackEndCert: true,
isFullStackCert: true,
@@ -425,6 +381,7 @@ module.exports = function(app) {
isFrontEndLibsCert: true,
isJsAlgoDataStructCert: true,
isDataVisCert: true,
is2018DataVisCert: true,
isApisMicroservicesCert: true,
isInfosecQaCert: true,
isHonest: true,
@@ -434,6 +391,7 @@ module.exports = function(app) {
})
.subscribe(
user => {
const profile = `/${user.username}`;
if (!user) {
req.flash(
'danger',
@@ -441,15 +399,16 @@ module.exports = function(app) {
);
return res.redirect('/');
}
if (!user.isGithubCool) {
if (!user.name) {
req.flash(
'danger',
dedent`
This user needs to link GitHub with their account
This user needs to add their name to their account
in order for others to be able to view their certificate.
`
);
return res.redirect('back');
return res.redirect(profile);
}
if (user.isCheater) {
@@ -465,20 +424,20 @@ module.exports = function(app) {
in order for others to be able to view their certificate.
`
);
return res.redirect('back');
return res.redirect('/');
}
if (!user.isHonest) {
req.flash(
'danger',
dedent`
dedent`
${username} has not yet agreed to our Academic Honesty Pledge.
`
);
return res.redirect('back');
return res.redirect(profile);
}
if (user[certType]) {
const { challengeMap = {} } = user;
const { completedDate = new Date() } = challengeMap[certId] || {};
@@ -495,51 +454,49 @@ module.exports = function(app) {
'danger',
`Looks like user ${username} is not ${certText[certType]}`
);
return res.redirect('back');
return res.redirect(profile);
},
next
);
}
function showDelete(req, res) {
return res.render('account/delete', { title: 'Delete My Account!' });
}
function postDeleteAccount(req, res, next) {
User.destroyById(req.user.id, function(err) {
if (err) { return next(err); }
req.logout();
req.flash('info', 'You\'ve successfully deleted your account.');
return res.redirect('/');
});
}
function showResetProgress(req, res) {
return res.render('account/reset-progress', { title: 'Reset My Progress!'
req.flash('success', 'You have successfully deleted your account.');
return res.status(200).end();
});
}
function postResetProgress(req, res, next) {
User.findById(req.user.id, function(err, user) {
if (err) { return next(err); }
return user.updateAttributes({
return user.update$({
progressTimestamps: [{
timestamp: Date.now()
}],
currentStreak: 0,
longestStreak: 0,
currentChallengeId: '',
isBackEndCert: false,
isFullStackCert: false,
isDataVisCert: false,
isRespWebDesignCert: false,
is2018DataVisCert: false,
isFrontEndLibsCert: false,
isJsAlgoDataStructCert: false,
isApisMicroservicesCert: false,
isInfosecQaCert: false,
is2018FullStackCert: false,
isFrontEndCert: false,
challengeMap: {},
challegesCompleted: []
}, function(err) {
if (err) { return next(err); }
req.flash('info', 'You\'ve successfully reset your progress.');
return res.redirect('/');
});
isBackEndCert: false,
isDataVisCert: false,
isFullStackCert: false,
challengeMap: {}
})
.subscribe(
() => {
req.flash('success', 'You have successfully reset your progress.');
return res.status(200).end();
},
next
);
});
}