fix: add cert claim logic for legacy cert

This commit is contained in:
Ahmad Abdolsaheb
2019-03-28 11:18:26 +03:00
committed by Mrugesh Mohapatra
parent c2ffd6471b
commit bc099d4971
6 changed files with 119 additions and 110 deletions

View File

@ -51,7 +51,12 @@ export function DynamicForm({
onSubmit={handleSubmit(submit)} onSubmit={handleSubmit(submit)}
style={{ width: '100%' }} style={{ width: '100%' }}
> >
<FormFields errors={errors} fields={fields} options={options} /> <FormFields
errors={errors}
fields={fields}
formId={id}
options={options}
/>
<BlockSaveWrapper> <BlockSaveWrapper>
{hideButton ? null : ( {hideButton ? null : (
<BlockSaveButton <BlockSaveButton

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { find, first } from 'lodash'; import { find, first, values, isString } from 'lodash';
import { import {
Table, Table,
Button, Button,
@ -13,7 +13,7 @@ import {
import { Link, navigate } from 'gatsby'; import { Link, navigate } from 'gatsby';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { updateLegacyCertificate } from '../../redux/settings'; import { updateLegacyCert } from '../../redux/settings';
import { projectMap, legacyProjectMap } from '../../resources/certProjectMap'; import { projectMap, legacyProjectMap } from '../../resources/certProjectMap';
import SectionHeader from './SectionHeader'; import SectionHeader from './SectionHeader';
@ -22,11 +22,12 @@ import { FullWidthRow, Spacer } from '../helpers';
import { Form } from '../formHelpers'; import { Form } from '../formHelpers';
import { maybeUrlRE } from '../../utils'; import { maybeUrlRE } from '../../utils';
import reallyWeirdErrorMessage from '../../utils/reallyWeirdErrorMessage';
import './certification.css'; import './certification.css';
const mapDispatchToProps = dispatch => const mapDispatchToProps = dispatch =>
bindActionCreators({ updateLegacyCertificate }, dispatch); bindActionCreators({ updateLegacyCert }, dispatch);
const propTypes = { const propTypes = {
completedChallenges: PropTypes.arrayOf( completedChallenges: PropTypes.arrayOf(
@ -51,7 +52,7 @@ const propTypes = {
isInfosecQaCert: PropTypes.bool, isInfosecQaCert: PropTypes.bool,
isJsAlgoDataStructCert: PropTypes.bool, isJsAlgoDataStructCert: PropTypes.bool,
isRespWebDesignCert: PropTypes.bool, isRespWebDesignCert: PropTypes.bool,
updateLegacyCertificate: PropTypes.func.isRequired, updateLegacyCert: PropTypes.func.isRequired,
username: PropTypes.string, username: PropTypes.string,
verifyCert: PropTypes.func.isRequired verifyCert: PropTypes.func.isRequired
}; };
@ -107,6 +108,13 @@ const isCertMapSelector = createSelector(
}) })
); );
const honestyInfoMessage = {
type: 'info',
message:
'To claim a certification, you must first accept our academic ' +
'honesty policy'
};
const initialState = { const initialState = {
solutionViewer: { solutionViewer: {
projectTitle: '', projectTitle: '',
@ -249,12 +257,7 @@ class CertificationSettings extends Component {
} }
return isHonest return isHonest
? verifyCert(superBlock) ? verifyCert(superBlock)
: createFlashMessage({ : createFlashMessage(honestyInfoMessage);
type: 'info',
message:
'To claim a certification, you must first accept our academic ' +
'honesty policy'
});
}; };
return projectMap[certName] return projectMap[certName]
.map(({ link, title, id }) => ( .map(({ link, title, id }) => (
@ -284,18 +287,83 @@ class CertificationSettings extends Component {
}; };
// legacy projects rendering // legacy projects rendering
handleSubmit(formChalObj) {
const {
isHonest,
createFlashMessage,
verifyCert,
updateLegacyCert
} = this.props;
let legacyTitle;
let superBlock;
let certs = Object.keys(legacyProjectMap);
let loopBreak = false;
for (let certTitle of certs) {
for (let chalTitle of legacyProjectMap[certTitle]) {
if (chalTitle.title === Object.keys(formChalObj)[0]) {
superBlock = chalTitle.superBlock;
loopBreak = true;
legacyTitle = certTitle;
break;
}
}
if (loopBreak) {
break;
}
}
handleSubmit(values) { // make an object with keys as challenge ids and values as solutions
const { updateLegacyCertificate } = this.props; let idsToSolutions = {};
updateLegacyCertificate(values); for (let i of Object.keys(formChalObj)) {
for (let j of legacyProjectMap[legacyTitle]) {
if (i === j.title) {
idsToSolutions[j.id] = formChalObj[i];
break;
}
}
}
// filter the new solutions that need to be updated
const completedChallenges = this.props.completedChallenges;
let challengesToUpdate = {};
let newChalleneFound = true;
let oldSubmissions = 0;
for (let submittedChal of Object.keys(idsToSolutions)) {
for (let i of completedChallenges) {
if (i.id === submittedChal) {
if (idsToSolutions[submittedChal] !== i.solution) {
challengesToUpdate[submittedChal] = idsToSolutions[submittedChal];
}
oldSubmissions++;
newChalleneFound = false;
break;
}
}
if (newChalleneFound && idsToSolutions[submittedChal] !== '') {
challengesToUpdate[submittedChal] = idsToSolutions[submittedChal];
}
newChalleneFound = true;
}
const valuesSaved = values(formChalObj)
.filter(Boolean)
.filter(isString);
const isProjectSectionComplete = valuesSaved.length === oldSubmissions;
if (isProjectSectionComplete) {
return isHonest
? verifyCert(superBlock)
: createFlashMessage(honestyInfoMessage);
}
return updateLegacyCert({ challengesToUpdate, superBlock });
} }
renderLegacyCertifications = certName => { renderLegacyCertifications = certName => {
const { username, isHonest, createFlashMessage, verifyCert } = this.props; const { username, createFlashMessage, completedChallenges } = this.props;
const { superBlock } = first(legacyProjectMap[certName]); const { superBlock } = first(legacyProjectMap[certName]);
const certLocation = `/certification/${username}/${superBlock}`; const certLocation = `/certification/${username}/${superBlock}`;
const challengeTitles = legacyProjectMap[certName].map(item => item.title); const challengeTitles = legacyProjectMap[certName].map(item => item.title);
const { completedChallenges } = this.props;
const isCertClaimed = this.getUserIsCertMap()[certName]; const isCertClaimed = this.getUserIsCertMap()[certName];
const initialObject = {}; const initialObject = {};
let filledforms = 0; let filledforms = 0;
@ -321,19 +389,12 @@ class CertificationSettings extends Component {
const fullForm = filledforms === challengeTitles.length; const fullForm = filledforms === challengeTitles.length;
const createClickHandler = superBlock => e => { const createClickHandler = certLocation => e => {
e.preventDefault(); e.preventDefault();
if (isCertClaimed) { if (isCertClaimed) {
return navigate(certLocation); return navigate(certLocation);
} }
return isHonest return createFlashMessage(reallyWeirdErrorMessage);
? verifyCert(superBlock)
: createFlashMessage({
type: 'info',
message:
'To claim a certification, you must first accept our academic ' +
'honesty policy'
});
}; };
const buttonStyle = { const buttonStyle = {
@ -341,7 +402,7 @@ class CertificationSettings extends Component {
}; };
return ( return (
<FullWidthRow key={certName}> <FullWidthRow key={superBlock}>
<Spacer /> <Spacer />
<h3>{certName}</h3> <h3>{certName}</h3>
<Form <Form
@ -349,7 +410,7 @@ class CertificationSettings extends Component {
enableSubmit={fullForm} enableSubmit={fullForm}
formFields={challengeTitles} formFields={challengeTitles}
hideButton={isCertClaimed} hideButton={isCertClaimed}
id={certName} id={superBlock}
initialValues={{ initialValues={{
...initialObject ...initialObject
}} }}
@ -363,7 +424,7 @@ class CertificationSettings extends Component {
bsStyle='primary' bsStyle='primary'
className={'col-xs-12'} className={'col-xs-12'}
href={certLocation} href={certLocation}
onClick={createClickHandler(superBlock)} onClick={createClickHandler(certLocation)}
style={buttonStyle} style={buttonStyle}
target='_blank' target='_blank'
> >

View File

@ -316,7 +316,7 @@ export const reducer = handleActions(
} }
}; };
}, },
[settingsTypes.updateLegacyCertificateComplete]: (state, { payload }) => { [settingsTypes.updateLegacyCertComplete]: (state, { payload }) => {
const { appUsername } = state; const { appUsername } = state;
return { return {
...state, ...state,

View File

@ -6,7 +6,7 @@ import { createSettingsSagas } from './settings-sagas';
import { createUpdateMyEmailSaga } from './update-email-saga'; import { createUpdateMyEmailSaga } from './update-email-saga';
// prettier-ignore // prettier-ignore
import { createUpdateLegacyCertificateSaga } from import { createUpdateLegacyCertSaga } from
'./update-legacy-certificate-saga'; './update-legacy-certificate-saga';
export const ns = 'settings'; export const ns = 'settings';
@ -31,7 +31,7 @@ export const types = createTypes(
...createAsyncTypes('submitNewAbout'), ...createAsyncTypes('submitNewAbout'),
...createAsyncTypes('submitNewUsername'), ...createAsyncTypes('submitNewUsername'),
...createAsyncTypes('updateMyEmail'), ...createAsyncTypes('updateMyEmail'),
...createAsyncTypes('updateLegacyCertificate'), ...createAsyncTypes('updateLegacyCert'),
...createAsyncTypes('updateUserFlag'), ...createAsyncTypes('updateUserFlag'),
...createAsyncTypes('submitProfileUI'), ...createAsyncTypes('submitProfileUI'),
...createAsyncTypes('verifyCert'), ...createAsyncTypes('verifyCert'),
@ -45,7 +45,7 @@ export const sagas = [
...createSettingsSagas(types), ...createSettingsSagas(types),
...createUpdateMyEmailSaga(types), ...createUpdateMyEmailSaga(types),
...createDangerZoneSaga(types), ...createDangerZoneSaga(types),
...createUpdateLegacyCertificateSaga(types) ...createUpdateLegacyCertSaga(types)
]; ];
const checkForSuccessPayload = ({ type, payload }) => const checkForSuccessPayload = ({ type, payload }) =>
@ -78,15 +78,11 @@ export const updateMyEmail = createAction(types.updateMyEmail);
export const updateMyEmailComplete = createAction(types.updateMyEmailComplete); export const updateMyEmailComplete = createAction(types.updateMyEmailComplete);
export const updateMyEmailError = createAction(types.updateMyEmailError); export const updateMyEmailError = createAction(types.updateMyEmailError);
export const updateLegacyCertificate = createAction( export const updateLegacyCert = createAction(types.updateLegacyCert);
types.updateLegacyCertificate export const updateLegacyCertComplete = createAction(
); types.updateLegacyCertComplete
export const updateLegacyCertificateComplete = createAction(
types.updateLegacyCertificateComplete
);
export const updateLegacyCertificateError = createAction(
types.updateLegacyCertificateError
); );
export const updateLegacyCertError = createAction(types.updateLegacyCertError);
export const updateUserFlag = createAction(types.updateUserFlag); export const updateUserFlag = createAction(types.updateUserFlag);
export const updateUserFlagComplete = createAction( export const updateUserFlagComplete = createAction(

View File

@ -1,73 +1,21 @@
import { takeEvery, select, call, put } from 'redux-saga/effects'; import { takeEvery, call, put } from 'redux-saga/effects';
import { putUpdateLegacyCertificate } from '../../utils/ajax'; import { putUpdateLegacyCert } from '../../utils/ajax';
import { completedChallengesSelector, submitComplete } from '../'; import { submitComplete } from '../';
import { legacyProjectMap } from '../../resources/certProjectMap';
import { createFlashMessage } from '../../components/Flash/redux'; import { createFlashMessage } from '../../components/Flash/redux';
import standardErrorMessage from '../../utils/reallyWeirdErrorMessage'; import reallyWeirdErrorMessage from '../../utils/reallyWeirdErrorMessage';
import { updateLegacyCertificateError } from './'; import { updateLegacyCertError } from './';
const completedChallenges = state => completedChallengesSelector(state);
function* updateLegacyCertificateSaga({ payload }) {
// find which certificate the challenges belong to
let legacyCert;
let certs = Object.keys(legacyProjectMap);
let loopBreak = false;
for (let i of certs) {
for (let j of legacyProjectMap[i]) {
if (j.title === Object.keys(payload)[0]) {
console.log(j.title);
loopBreak = true;
legacyCert = i;
break;
}
}
if (loopBreak) {
break;
}
}
// make an object with keys as challenge ids and values as solutions
let idsToSolutions = {};
for (let i of Object.keys(payload)) {
for (let j of legacyProjectMap[legacyCert]) {
if (i === j.title) {
console.log(payload[i]);
idsToSolutions[j.id] = payload[i];
break;
}
}
}
// find how many challnegs have been updated and how many are new
let completed = yield select(completedChallenges);
let challengesToUpdate = {};
let newChalleneFound = true;
for (let j of Object.keys(idsToSolutions)) {
for (let i of completed) {
if (i.id === j) {
if (idsToSolutions[j] !== i.solution) {
challengesToUpdate[j] = idsToSolutions[j];
}
newChalleneFound = false;
break;
}
}
if (newChalleneFound && idsToSolutions[j] !== '') {
challengesToUpdate[j] = idsToSolutions[j];
}
newChalleneFound = true;
}
function* updateLegacyCertSaga({
payload: { superBlock, challengesToUpdate }
}) {
// shape the body of the http call so it is consumable by api // shape the body of the http call so it is consumable by api
const body = { const body = {
projects: { projects: {
[legacyCert]: challengesToUpdate [superBlock]: challengesToUpdate
} }
}; };
// shape to update completed challenges in redux store
// shape to update completed challenges
let reduxShape = []; let reduxShape = [];
for (let obj in challengesToUpdate) { for (let obj in challengesToUpdate) {
if (challengesToUpdate.hasOwnProperty(obj)) { if (challengesToUpdate.hasOwnProperty(obj)) {
@ -76,17 +24,16 @@ function* updateLegacyCertificateSaga({ payload }) {
} }
try { try {
const { data: response } = yield call(putUpdateLegacyCertificate, body); const { data: response } = yield call(putUpdateLegacyCert, body);
yield put(submitComplete({ challArray: reduxShape })); yield put(submitComplete({ challArray: reduxShape }));
yield put(createFlashMessage(response)); yield put(createFlashMessage(response));
console.log(response);
} catch (e) { } catch (e) {
yield put(updateLegacyCertificateError(e)); yield put(updateLegacyCertError(e));
yield put(createFlashMessage(standardErrorMessage)); yield put(createFlashMessage(reallyWeirdErrorMessage));
} }
} }
export function createUpdateLegacyCertificateSaga(types) { export function createUpdateLegacyCertSaga(types) {
return [ return [takeEvery(types.updateLegacyCert, updateLegacyCertSaga)];
takeEvery(types.updateLegacyCertificate, updateLegacyCertificateSaga)
];
} }

View File

@ -45,7 +45,7 @@ export function getArticleById(shortId) {
} }
/** POST **/ /** POST **/
export function putUpdateLegacyCertificate(body) { export function putUpdateLegacyCert(body) {
return post('/update-my-projects', body); return post('/update-my-projects', body);
} }