fix(client): cert rendering temporarily (#43335)
* fix: require login as step * fix: cert rendering temporary * fix: remove keys from other translation files * fix: cypress test * chore: shauns suggestions * fix: remove steps * fix: use a seperate UseEffect function * Suggestions from Shaun Co-authored-by: Shaun Hamilton <shauhami020@gmail.com> * fix: certification temp rendering * fix: cert temp rendering hopefully * fix: button visibility and cert test * fix: condition * test: improve claim cert tests Without npm run seed, retries will be in a different state. Check the scroll position to (hopefully) catch the weird cert claim bug. Co-authored-by: Shaun Hamilton <shauhami020@gmail.com> Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@ -9,6 +9,10 @@
|
|||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cert-btn {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page ul {
|
.landing-page ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
|
@ -17,6 +17,7 @@ import { Spacer } from '../../components/helpers';
|
|||||||
import {
|
import {
|
||||||
currentChallengeIdSelector,
|
currentChallengeIdSelector,
|
||||||
userFetchStateSelector,
|
userFetchStateSelector,
|
||||||
|
signInLoadingSelector,
|
||||||
isSignedInSelector,
|
isSignedInSelector,
|
||||||
tryToShowDonationModal,
|
tryToShowDonationModal,
|
||||||
userSelector
|
userSelector
|
||||||
@ -50,6 +51,7 @@ const propTypes = {
|
|||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
resetExpansion: PropTypes.func,
|
resetExpansion: PropTypes.func,
|
||||||
|
signInLoading: PropTypes.bool,
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
toggleBlock: PropTypes.func,
|
toggleBlock: PropTypes.func,
|
||||||
tryToShowDonationModal: PropTypes.func.isRequired,
|
tryToShowDonationModal: PropTypes.func.isRequired,
|
||||||
@ -62,11 +64,13 @@ const mapStateToProps = state => {
|
|||||||
return createSelector(
|
return createSelector(
|
||||||
currentChallengeIdSelector,
|
currentChallengeIdSelector,
|
||||||
isSignedInSelector,
|
isSignedInSelector,
|
||||||
|
signInLoadingSelector,
|
||||||
userFetchStateSelector,
|
userFetchStateSelector,
|
||||||
userSelector,
|
userSelector,
|
||||||
(currentChallengeId, isSignedIn, fetchState, user) => ({
|
(currentChallengeId, isSignedIn, signInLoading, fetchState, user) => ({
|
||||||
currentChallengeId,
|
currentChallengeId,
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
|
signInLoading,
|
||||||
fetchState,
|
fetchState,
|
||||||
user
|
user
|
||||||
})
|
})
|
||||||
@ -150,6 +154,7 @@ const SuperBlockIntroductionPage = props => {
|
|||||||
allChallengeNode: { edges }
|
allChallengeNode: { edges }
|
||||||
},
|
},
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
|
signInLoading,
|
||||||
t,
|
t,
|
||||||
user
|
user
|
||||||
} = props;
|
} = props;
|
||||||
@ -202,7 +207,7 @@ const SuperBlockIntroductionPage = props => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isSignedIn && (
|
{!isSignedIn && !signInLoading && (
|
||||||
<div>
|
<div>
|
||||||
<Spacer size={2} />
|
<Spacer size={2} />
|
||||||
<Login block={true}>{t('buttons.logged-out-cta-btn')}</Login>
|
<Login block={true}>{t('buttons.logged-out-cta-btn')}</Login>
|
||||||
|
@ -5,13 +5,16 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
certSlugTypeMap,
|
certSlugTypeMap,
|
||||||
superBlockCertTypeMap
|
superBlockCertTypeMap
|
||||||
} from '../../../../../config/certification-settings';
|
} from '../../../../../config/certification-settings';
|
||||||
import { createFlashMessage } from '../../../components/Flash/redux';
|
import { createFlashMessage } from '../../../components/Flash/redux';
|
||||||
import { stepsToClaimSelector } from '../../../redux';
|
import {
|
||||||
|
userFetchStateSelector,
|
||||||
|
stepsToClaimSelector,
|
||||||
|
isSignedInSelector
|
||||||
|
} from '../../../redux';
|
||||||
|
|
||||||
import { StepsType, User } from '../../../redux/prop-types';
|
import { StepsType, User } from '../../../redux/prop-types';
|
||||||
import { verifyCert } from '../../../redux/settings';
|
import { verifyCert } from '../../../redux/settings';
|
||||||
@ -22,6 +25,12 @@ import CertificationCard from './CertificationCard';
|
|||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
createFlashMessage: PropTypes.func.isRequired,
|
createFlashMessage: PropTypes.func.isRequired,
|
||||||
|
fetchState: PropTypes.shape({
|
||||||
|
pending: PropTypes.bool,
|
||||||
|
complete: PropTypes.bool,
|
||||||
|
errored: PropTypes.bool
|
||||||
|
}),
|
||||||
|
isSignedIn: PropTypes.bool,
|
||||||
steps: StepsType,
|
steps: StepsType,
|
||||||
superBlock: PropTypes.string,
|
superBlock: PropTypes.string,
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
@ -36,9 +45,16 @@ const honestyInfoMessage = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return createSelector(stepsToClaimSelector, steps => ({
|
return createSelector(
|
||||||
steps
|
stepsToClaimSelector,
|
||||||
}))(state);
|
userFetchStateSelector,
|
||||||
|
isSignedInSelector,
|
||||||
|
(steps, fetchState, isSignedIn) => ({
|
||||||
|
steps,
|
||||||
|
fetchState,
|
||||||
|
isSignedIn
|
||||||
|
})
|
||||||
|
)(state);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
@ -53,15 +69,21 @@ const CertChallenge = ({
|
|||||||
t,
|
t,
|
||||||
verifyCert,
|
verifyCert,
|
||||||
title,
|
title,
|
||||||
|
fetchState,
|
||||||
|
isSignedIn,
|
||||||
user: { isHonest, username }
|
user: { isHonest, username }
|
||||||
}) => {
|
}) => {
|
||||||
const [canClaim, setCanClaim] = useState({ status: false, result: '' });
|
const [canClaimCert, setCanClaimCert] = useState(false);
|
||||||
|
const [certVerificationMessage, setCertVerificationMessage] = useState('');
|
||||||
const [isCertified, setIsCertified] = useState(false);
|
const [isCertified, setIsCertified] = useState(false);
|
||||||
|
const [userLoaded, setUserLoaded] = useState(false);
|
||||||
|
const [verificationComplete, setVerificationComplete] = useState(false);
|
||||||
const [stepState, setStepState] = useState({
|
const [stepState, setStepState] = useState({
|
||||||
numberOfSteps: 0,
|
numberOfSteps: 0,
|
||||||
completedCount: 0
|
completedCount: 0
|
||||||
});
|
});
|
||||||
const [canViewCert, setCanViewCert] = useState(false);
|
const [hasCompletedRequiredSteps, setHasCompletedRequiredSteps] =
|
||||||
|
useState(false);
|
||||||
const [isProjectsCompleted, setIsProjectsCompleted] = useState(false);
|
const [isProjectsCompleted, setIsProjectsCompleted] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -70,7 +92,9 @@ const CertChallenge = ({
|
|||||||
try {
|
try {
|
||||||
const data = await getVerifyCanClaimCert(username, superBlock);
|
const data = await getVerifyCanClaimCert(username, superBlock);
|
||||||
const { status, result } = data?.response?.message;
|
const { status, result } = data?.response?.message;
|
||||||
setCanClaim({ status, result });
|
setCanClaimCert(status);
|
||||||
|
setCertVerificationMessage(result);
|
||||||
|
setVerificationComplete(true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO: How do we handle errors...?
|
// TODO: How do we handle errors...?
|
||||||
}
|
}
|
||||||
@ -81,6 +105,14 @@ const CertChallenge = ({
|
|||||||
|
|
||||||
const { certSlug } = certMap.find(x => x.title === title);
|
const { certSlug } = certMap.find(x => x.title === title);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { pending, complete } = fetchState;
|
||||||
|
|
||||||
|
if (complete && !pending) {
|
||||||
|
setUserLoaded(true);
|
||||||
|
}
|
||||||
|
}, [fetchState]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsCertified(
|
setIsCertified(
|
||||||
steps?.currentCerts?.find(
|
steps?.currentCerts?.find(
|
||||||
@ -90,18 +122,17 @@ const CertChallenge = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const projectsCompleted =
|
const projectsCompleted =
|
||||||
canClaim.status || canClaim.result === 'projects-completed';
|
canClaimCert || certVerificationMessage === 'projects-completed';
|
||||||
const completedCount =
|
const completedCount =
|
||||||
Object.values(steps).filter(
|
Object.values(steps).filter(
|
||||||
stepVal => typeof stepVal === 'boolean' && stepVal
|
stepVal => typeof stepVal === 'boolean' && stepVal
|
||||||
).length + projectsCompleted;
|
).length + projectsCompleted;
|
||||||
const numberOfSteps = Object.keys(steps).length;
|
const numberOfSteps = Object.keys(steps).length;
|
||||||
|
setHasCompletedRequiredSteps(completedCount === numberOfSteps);
|
||||||
setCanViewCert(completedCount === numberOfSteps);
|
|
||||||
setStepState({ numberOfSteps, completedCount });
|
setStepState({ numberOfSteps, completedCount });
|
||||||
setIsProjectsCompleted(projectsCompleted);
|
setIsProjectsCompleted(projectsCompleted);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [steps, canClaim]);
|
}, [steps, canClaimCert, certVerificationMessage]);
|
||||||
|
|
||||||
const certLocation = `/certification/${username}/${certSlug}`;
|
const certLocation = `/certification/${username}/${certSlug}`;
|
||||||
const i18nSuperBlock = t(`intro:${superBlock}.title`);
|
const i18nSuperBlock = t(`intro:${superBlock}.title`);
|
||||||
@ -109,6 +140,11 @@ const CertChallenge = ({
|
|||||||
cert: i18nSuperBlock
|
cert: i18nSuperBlock
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const showCertificationCard =
|
||||||
|
userLoaded &&
|
||||||
|
isSignedIn &&
|
||||||
|
(!isCertified || (!hasCompletedRequiredSteps && verificationComplete));
|
||||||
|
|
||||||
const createClickHandler = certSlug => e => {
|
const createClickHandler = certSlug => e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (isCertified) {
|
if (isCertified) {
|
||||||
@ -118,10 +154,9 @@ const CertChallenge = ({
|
|||||||
? verifyCert(certSlug)
|
? verifyCert(certSlug)
|
||||||
: createFlashMessage(honestyInfoMessage);
|
: createFlashMessage(honestyInfoMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='block'>
|
<div className='block'>
|
||||||
{(!isCertified || !canViewCert) && (
|
{showCertificationCard && (
|
||||||
<CertificationCard
|
<CertificationCard
|
||||||
i18nCertText={i18nCertText}
|
i18nCertText={i18nCertText}
|
||||||
isProjectsCompleted={isProjectsCompleted}
|
isProjectsCompleted={isProjectsCompleted}
|
||||||
@ -130,15 +165,24 @@ const CertChallenge = ({
|
|||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Button
|
<>
|
||||||
block={true}
|
{isSignedIn && (
|
||||||
bsStyle='primary'
|
<Button
|
||||||
disabled={!canClaim.status || (isCertified && !canViewCert)}
|
block={true}
|
||||||
href={certLocation}
|
bsStyle='primary'
|
||||||
onClick={createClickHandler(certSlug)}
|
className='cert-btn'
|
||||||
>
|
disabled={
|
||||||
{isCertified ? t('buttons.show-cert') : t('buttons.claim-cert')}
|
!canClaimCert || (isCertified && !hasCompletedRequiredSteps)
|
||||||
</Button>
|
}
|
||||||
|
href={certLocation}
|
||||||
|
onClick={createClickHandler(certSlug)}
|
||||||
|
>
|
||||||
|
{isCertified && userLoaded
|
||||||
|
? t('buttons.show-cert')
|
||||||
|
: t('buttons.claim-cert')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,6 @@ import { Link } from 'gatsby';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withTranslation, useTranslation } from 'react-i18next';
|
import { withTranslation, useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import GreenNotCompleted from '../../../assets/icons/green-not-completed';
|
import GreenNotCompleted from '../../../assets/icons/green-not-completed';
|
||||||
import GreenPass from '../../../assets/icons/green-pass';
|
import GreenPass from '../../../assets/icons/green-pass';
|
||||||
import { StepsType } from '../../../redux/prop-types';
|
import { StepsType } from '../../../redux/prop-types';
|
||||||
@ -39,6 +38,7 @@ const ClaimCertSteps = ({
|
|||||||
isShowCerts = false,
|
isShowCerts = false,
|
||||||
isShowProfile = false
|
isShowProfile = false
|
||||||
} = steps;
|
} = steps;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className='map-challenges-ul' data-cy='claim-cert-steps'>
|
<ul className='map-challenges-ul' data-cy='claim-cert-steps'>
|
||||||
<li className='map-challenge-title map-challenge-wrap'>
|
<li className='map-challenge-title map-challenge-wrap'>
|
||||||
|
@ -49,6 +49,7 @@ describe('Responsive Web Design Superblock', () => {
|
|||||||
});
|
});
|
||||||
describe('After submitting all 5 projects', () => {
|
describe('After submitting all 5 projects', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
|
cy.exec('npm run seed');
|
||||||
cy.login();
|
cy.login();
|
||||||
cy.toggleAll();
|
cy.toggleAll();
|
||||||
const { superBlock, block, challenges } = projects;
|
const { superBlock, block, challenges } = projects;
|
||||||
@ -82,6 +83,10 @@ describe('Responsive Web Design Superblock', () => {
|
|||||||
cy.get('.donation-modal').should('not.exist');
|
cy.get('.donation-modal').should('not.exist');
|
||||||
// directed to claim-cert-block section
|
// directed to claim-cert-block section
|
||||||
cy.url().should('include', '#claim-cert-block');
|
cy.url().should('include', '#claim-cert-block');
|
||||||
|
// make sure that the window has not snapped to the top (a weird bug that
|
||||||
|
// we never figured out and so could randomly reappear)
|
||||||
|
cy.window().its('scrollY').should('not.equal', 0);
|
||||||
|
cy.contains('Claim Your Certification');
|
||||||
cy.contains('Claim Certification').should('not.be.disabled').click();
|
cy.contains('Claim Certification').should('not.be.disabled').click();
|
||||||
cy.contains('Show Certification').click();
|
cy.contains('Show Certification').click();
|
||||||
cy.location().should(loc => {
|
cy.location().should(loc => {
|
||||||
|
Reference in New Issue
Block a user