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:
Sem Bauke
2021-10-01 06:30:14 +02:00
committed by GitHub
parent 3ea2fe4f77
commit cfc9b7258d
5 changed files with 84 additions and 26 deletions

View File

@ -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;

View File

@ -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>

View File

@ -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>
); );
}; };

View File

@ -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'>

View File

@ -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 => {