From bc099d4971aee8b765dad1d63c5fe4e5186d338a Mon Sep 17 00:00:00 2001 From: Ahmad Abdolsaheb Date: Thu, 28 Mar 2019 11:18:26 +0300 Subject: [PATCH] fix: add cert claim logic for legacy cert --- client/src/components/formHelpers/Form.js | 7 +- .../src/components/settings/Certification.js | 115 ++++++++++++++---- client/src/redux/index.js | 2 +- client/src/redux/settings/index.js | 18 ++- .../update-legacy-certificate-saga.js | 85 +++---------- client/src/utils/ajax.js | 2 +- 6 files changed, 119 insertions(+), 110 deletions(-) diff --git a/client/src/components/formHelpers/Form.js b/client/src/components/formHelpers/Form.js index 8a33fd9181..76bf71d5ab 100644 --- a/client/src/components/formHelpers/Form.js +++ b/client/src/components/formHelpers/Form.js @@ -51,7 +51,12 @@ export function DynamicForm({ onSubmit={handleSubmit(submit)} style={{ width: '100%' }} > - + {hideButton ? null : ( - bindActionCreators({ updateLegacyCertificate }, dispatch); + bindActionCreators({ updateLegacyCert }, dispatch); const propTypes = { completedChallenges: PropTypes.arrayOf( @@ -51,7 +52,7 @@ const propTypes = { isInfosecQaCert: PropTypes.bool, isJsAlgoDataStructCert: PropTypes.bool, isRespWebDesignCert: PropTypes.bool, - updateLegacyCertificate: PropTypes.func.isRequired, + updateLegacyCert: PropTypes.func.isRequired, username: PropTypes.string, 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 = { solutionViewer: { projectTitle: '', @@ -249,12 +257,7 @@ class CertificationSettings extends Component { } return isHonest ? verifyCert(superBlock) - : createFlashMessage({ - type: 'info', - message: - 'To claim a certification, you must first accept our academic ' + - 'honesty policy' - }); + : createFlashMessage(honestyInfoMessage); }; return projectMap[certName] .map(({ link, title, id }) => ( @@ -284,18 +287,83 @@ class CertificationSettings extends Component { }; // 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) { - const { updateLegacyCertificate } = this.props; - updateLegacyCertificate(values); + // make an object with keys as challenge ids and values as solutions + let idsToSolutions = {}; + 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 => { - const { username, isHonest, createFlashMessage, verifyCert } = this.props; + const { username, createFlashMessage, completedChallenges } = this.props; const { superBlock } = first(legacyProjectMap[certName]); const certLocation = `/certification/${username}/${superBlock}`; const challengeTitles = legacyProjectMap[certName].map(item => item.title); - const { completedChallenges } = this.props; const isCertClaimed = this.getUserIsCertMap()[certName]; const initialObject = {}; let filledforms = 0; @@ -321,19 +389,12 @@ class CertificationSettings extends Component { const fullForm = filledforms === challengeTitles.length; - const createClickHandler = superBlock => e => { + const createClickHandler = certLocation => e => { e.preventDefault(); if (isCertClaimed) { return navigate(certLocation); } - return isHonest - ? verifyCert(superBlock) - : createFlashMessage({ - type: 'info', - message: - 'To claim a certification, you must first accept our academic ' + - 'honesty policy' - }); + return createFlashMessage(reallyWeirdErrorMessage); }; const buttonStyle = { @@ -341,7 +402,7 @@ class CertificationSettings extends Component { }; return ( - +

{certName}

diff --git a/client/src/redux/index.js b/client/src/redux/index.js index 5ffd32836f..64802f255f 100644 --- a/client/src/redux/index.js +++ b/client/src/redux/index.js @@ -316,7 +316,7 @@ export const reducer = handleActions( } }; }, - [settingsTypes.updateLegacyCertificateComplete]: (state, { payload }) => { + [settingsTypes.updateLegacyCertComplete]: (state, { payload }) => { const { appUsername } = state; return { ...state, diff --git a/client/src/redux/settings/index.js b/client/src/redux/settings/index.js index 5204b9c9f2..6cc36ae9ce 100644 --- a/client/src/redux/settings/index.js +++ b/client/src/redux/settings/index.js @@ -6,7 +6,7 @@ import { createSettingsSagas } from './settings-sagas'; import { createUpdateMyEmailSaga } from './update-email-saga'; // prettier-ignore -import { createUpdateLegacyCertificateSaga } from +import { createUpdateLegacyCertSaga } from './update-legacy-certificate-saga'; export const ns = 'settings'; @@ -31,7 +31,7 @@ export const types = createTypes( ...createAsyncTypes('submitNewAbout'), ...createAsyncTypes('submitNewUsername'), ...createAsyncTypes('updateMyEmail'), - ...createAsyncTypes('updateLegacyCertificate'), + ...createAsyncTypes('updateLegacyCert'), ...createAsyncTypes('updateUserFlag'), ...createAsyncTypes('submitProfileUI'), ...createAsyncTypes('verifyCert'), @@ -45,7 +45,7 @@ export const sagas = [ ...createSettingsSagas(types), ...createUpdateMyEmailSaga(types), ...createDangerZoneSaga(types), - ...createUpdateLegacyCertificateSaga(types) + ...createUpdateLegacyCertSaga(types) ]; const checkForSuccessPayload = ({ type, payload }) => @@ -78,15 +78,11 @@ export const updateMyEmail = createAction(types.updateMyEmail); export const updateMyEmailComplete = createAction(types.updateMyEmailComplete); export const updateMyEmailError = createAction(types.updateMyEmailError); -export const updateLegacyCertificate = createAction( - types.updateLegacyCertificate -); -export const updateLegacyCertificateComplete = createAction( - types.updateLegacyCertificateComplete -); -export const updateLegacyCertificateError = createAction( - types.updateLegacyCertificateError +export const updateLegacyCert = createAction(types.updateLegacyCert); +export const updateLegacyCertComplete = createAction( + types.updateLegacyCertComplete ); +export const updateLegacyCertError = createAction(types.updateLegacyCertError); export const updateUserFlag = createAction(types.updateUserFlag); export const updateUserFlagComplete = createAction( diff --git a/client/src/redux/settings/update-legacy-certificate-saga.js b/client/src/redux/settings/update-legacy-certificate-saga.js index 690c67eb6c..fae649c5c3 100644 --- a/client/src/redux/settings/update-legacy-certificate-saga.js +++ b/client/src/redux/settings/update-legacy-certificate-saga.js @@ -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 { completedChallengesSelector, submitComplete } from '../'; -import { legacyProjectMap } from '../../resources/certProjectMap'; +import { putUpdateLegacyCert } from '../../utils/ajax'; +import { submitComplete } from '../'; import { createFlashMessage } from '../../components/Flash/redux'; -import standardErrorMessage from '../../utils/reallyWeirdErrorMessage'; -import { updateLegacyCertificateError } 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; - } +import reallyWeirdErrorMessage from '../../utils/reallyWeirdErrorMessage'; +import { updateLegacyCertError } from './'; +function* updateLegacyCertSaga({ + payload: { superBlock, challengesToUpdate } +}) { // shape the body of the http call so it is consumable by api const body = { projects: { - [legacyCert]: challengesToUpdate + [superBlock]: challengesToUpdate } }; - - // shape to update completed challenges + // shape to update completed challenges in redux store let reduxShape = []; for (let obj in challengesToUpdate) { if (challengesToUpdate.hasOwnProperty(obj)) { @@ -76,17 +24,16 @@ function* updateLegacyCertificateSaga({ payload }) { } try { - const { data: response } = yield call(putUpdateLegacyCertificate, body); + const { data: response } = yield call(putUpdateLegacyCert, body); yield put(submitComplete({ challArray: reduxShape })); yield put(createFlashMessage(response)); + console.log(response); } catch (e) { - yield put(updateLegacyCertificateError(e)); - yield put(createFlashMessage(standardErrorMessage)); + yield put(updateLegacyCertError(e)); + yield put(createFlashMessage(reallyWeirdErrorMessage)); } } -export function createUpdateLegacyCertificateSaga(types) { - return [ - takeEvery(types.updateLegacyCertificate, updateLegacyCertificateSaga) - ]; +export function createUpdateLegacyCertSaga(types) { + return [takeEvery(types.updateLegacyCert, updateLegacyCertSaga)]; } diff --git a/client/src/utils/ajax.js b/client/src/utils/ajax.js index 44407df33b..77d8bfbbc5 100644 --- a/client/src/utils/ajax.js +++ b/client/src/utils/ajax.js @@ -45,7 +45,7 @@ export function getArticleById(shortId) { } /** POST **/ -export function putUpdateLegacyCertificate(body) { +export function putUpdateLegacyCert(body) { return post('/update-my-projects', body); }