feat(donate):add donation modal and certification message (#37822)

Co-Authored-By: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Ahmad Abdolsaheb
2019-12-02 15:48:53 +03:00
committed by GitHub
parent 9866d2f241
commit a9bbcda211
17 changed files with 503 additions and 89 deletions

View File

@ -0,0 +1,38 @@
import React, { Fragment } from 'react';
const propTypes = {};
function Heart(props) {
return (
<Fragment>
<span className='sr-only'>Heart</span>
<svg
height={184}
version='1.1'
viewBox='0 0 200 184'
width={200}
xmlns='http://www.w3.org/2000/svg'
{...props}
>
<g fill='none' fillRule='evenodd'>
<g fill='var(--love-color)'>
<ellipse cx='140.5' cy={59} id='a' rx='59.5' ry={59} />
<circle cx={59} cy={59} r={59} />
<rect
height={118}
transform='translate(100 100) rotate(-45) translate(-100 -100)'
width={118}
x={41}
y={41}
/>
</g>
</g>
</svg>
</Fragment>
);
}
Heart.displayName = 'Heart';
Heart.propTypes = propTypes;
export default Heart;

View File

@ -8,7 +8,12 @@ import { Grid, Row, Col, Image } from '@freecodecamp/react-bootstrap';
import {
showCertSelector,
showCertFetchStateSelector,
showCert
showCert,
userFetchStateSelector,
usernameSelector,
isDonatingSelector,
isDonationRequestedSelector,
preventDonationRequests
} from '../redux';
import validCertNames from '../../utils/validCertNames';
import { createFlashMessage } from '../components/Flash/redux';
@ -16,7 +21,7 @@ import standardErrorMessage from '../utils/standardErrorMessage';
import reallyWeirdErrorMessage from '../utils/reallyWeirdErrorMessage';
import RedirectHome from '../components/RedirectHome';
import { Loader } from '../components/helpers';
import { Loader, Link } from '../components/helpers';
const propTypes = {
cert: PropTypes.shape({
@ -35,8 +40,15 @@ const propTypes = {
complete: PropTypes.bool,
errored: PropTypes.bool
}),
isDonating: PropTypes.bool,
isDonationRequested: PropTypes.bool,
issueDate: PropTypes.string,
preventDonationRequests: PropTypes.func,
showCert: PropTypes.func.isRequired,
signedInUserName: PropTypes.string,
userFetchState: PropTypes.shape({
complete: PropTypes.bool
}),
userFullName: PropTypes.string,
username: PropTypes.string,
validCertName: PropTypes.bool
@ -47,16 +59,34 @@ const mapStateToProps = (state, { certName }) => {
return createSelector(
showCertSelector,
showCertFetchStateSelector,
(cert, fetchState) => ({
usernameSelector,
userFetchStateSelector,
isDonatingSelector,
isDonationRequestedSelector,
(
cert,
fetchState,
validCertName
signedInUserName,
userFetchState,
isDonating,
isDonationRequested
) => ({
cert,
fetchState,
validCertName,
signedInUserName,
userFetchState,
isDonating,
isDonationRequested
})
);
};
const mapDispatchToProps = dispatch =>
bindActionCreators({ createFlashMessage, showCert }, dispatch);
bindActionCreators(
{ createFlashMessage, showCert, preventDonationRequests },
dispatch
);
class ShowCertification extends Component {
componentDidMount() {
@ -72,7 +102,12 @@ class ShowCertification extends Component {
fetchState,
validCertName,
createFlashMessage,
certName
certName,
preventDonationRequests,
signedInUserName,
isDonating,
isDonationRequested,
userFetchState
} = this.props;
if (!validCertName) {
@ -81,6 +116,7 @@ class ShowCertification extends Component {
}
const { pending, complete, errored } = fetchState;
const { complete: userComplete } = userFetchState;
if (pending) {
return <Loader fullScreen={true} />;
@ -103,8 +139,40 @@ class ShowCertification extends Component {
certTitle,
completionTime
} = cert;
let conditionalDonationMessage = '';
if (
userComplete &&
signedInUserName === username &&
!isDonating &&
!isDonationRequested
) {
conditionalDonationMessage = (
<Grid>
<Row className='certification-donation text-center'>
<p>
Only you can see this message. Congratulations on earning this
certification. Its no easy task. Running freeCodeCamp isnt easy
either. Nor is it cheap. Help us help you and many other people
around the world. Make a tax-deductible supporting donation to our
nonprofit today.
</p>
<Link
className={'btn'}
onClick={preventDonationRequests}
to={'/donate'}
>
Check out our donation dashboard
</Link>
</Row>
</Grid>
);
}
return (
<div className='certificate-outer-wrapper'>
{conditionalDonationMessage}
<Grid className='certificate-wrapper certification-namespace'>
<Row>
<header>

View File

@ -162,3 +162,59 @@ li.disabled > a {
.servicebot-embed-panel .panel {
padding: 20px;
}
.heart-icon-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 40px;
}
.heart-icon {
width: 150px;
height: auto;
transform: scale(1.5);
opacity: 0;
animation: heart-icon-animation 1s linear 100ms forwards;
}
@keyframes heart-icon-animation {
33% {
transform: scale(1.2);
}
66% {
transform: scale(1.25);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.donation-modal p {
margin: 0;
text-align: center;
font-weight: 700;
font-size: 1.2rem;
}
.donation-modal .modal-title {
text-align: center;
font-weight: 700;
font-size: 1.5rem;
}
@media screen and (max-width: 991px) {
.heart-icon-container {
margin: 30px;
}
.donation-modal p {
font-weight: 400;
font-size: 1rem;
}
.donation-modal .modal-title {
font-weight: 600;
font-size: 1.2rem;
}
}

View File

@ -0,0 +1,98 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { Modal, Button } from '@freecodecamp/react-bootstrap';
import { Link } from '../../../components/helpers';
import { blockNameify } from '../../../../utils/blockNameify';
import Heart from '../../../assets/icons/Heart';
import ga from '../../../analytics';
import {
closeDonationModal,
isDonationModalOpenSelector
} from '../../../redux';
import { challengeMetaSelector } from '../../../templates/Challenges/redux';
import '../Donation.css';
const mapStateToProps = createSelector(
isDonationModalOpenSelector,
challengeMetaSelector,
(show, { block }) => ({
show,
block
})
);
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
closeDonationModal
},
dispatch
);
const propTypes = {
block: PropTypes.string,
closeDonationModal: PropTypes.func.isRequired,
show: PropTypes.bool
};
class DonateModal extends Component {
render() {
const { show, block } = this.props;
if (show) {
ga.modalview('/donation-modal');
}
return (
<Modal bsSize='lg' className='donation-modal' show={show}>
<Modal.Header className='fcc-modal'>
<Modal.Title className='modal-title text-center'>
<strong>Support freeCodeCamp.org</strong>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p className='text-center'>
Nicely done. You just completed {blockNameify(block)}.
</p>
<div className='heart-icon-container'>
<Heart className='heart-icon' />
</div>
<p className='text-center'>
Help us create even more learning resources like this.
</p>
</Modal.Body>
<Modal.Footer>
<Link
className='btn-invert btn btn-lg btn-primary btn-block btn-cta'
onClick={this.props.closeDonationModal}
to={`/donate`}
>
Support our nonprofit
</Link>
<Button
block={true}
bsSize='lg'
bsStyle='primary'
className='btn-invert'
onClick={this.props.closeDonationModal}
>
Ask me later
</Button>
</Modal.Footer>
</Modal>
);
}
}
DonateModal.displayName = 'DonateModal';
DonateModal.propTypes = propTypes;
export default connect(
mapStateToProps,
mapDispatchToProps
)(DonateModal);

View File

@ -1,11 +1,42 @@
import React, { Fragment } from 'react';
import React, { Fragment, Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
function CertificationLayout({ children }) {
return <Fragment>{children}</Fragment>;
import ga from '../../analytics';
import { fetchUser, isSignedInSelector } from '../../redux';
import { createSelector } from 'reselect';
const mapStateToProps = createSelector(
isSignedInSelector,
isSignedIn => ({
isSignedIn
})
);
const mapDispatchToProps = { fetchUser };
class CertificationLayout extends Component {
componentDidMount() {
const { isSignedIn, fetchUser, pathname } = this.props;
if (!isSignedIn) {
fetchUser();
}
ga.pageview(pathname);
}
render() {
return <Fragment>{this.props.children}</Fragment>;
}
}
CertificationLayout.displayName = 'CertificationLayout';
CertificationLayout.propTypes = { children: PropTypes.any };
CertificationLayout.propTypes = {
children: PropTypes.any,
fetchUser: PropTypes.func.isRequired,
isSignedIn: PropTypes.bool,
pathname: PropTypes.string.isRequired
};
export default CertificationLayout;
export default connect(
mapStateToProps,
mapDispatchToProps
)(CertificationLayout);

View File

@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import React, { Fragment, Component } from 'react';
import PropTypes from 'prop-types';
import { createSelector } from 'reselect';
import { connect } from 'react-redux';
@ -7,9 +7,11 @@ import { Loader } from '../../components/helpers';
import {
userSelector,
userFetchStateSelector,
isSignedInSelector
isSignedInSelector,
tryToShowDonationModal
} from '../../redux';
import createRedirect from '../../components/createRedirect';
import DonateModal from '../Donation/components/DonationModal';
import 'prismjs/themes/prism.css';
import './prism.css';
@ -28,14 +30,25 @@ const mapStateToProps = createSelector(
})
);
const mapDispatchToProps = {
tryToShowDonationModal
};
const RedirectAcceptPrivacyTerm = createRedirect('/accept-privacy-terms');
function LearnLayout({
class LearnLayout extends Component {
componentDidMount() {
this.props.tryToShowDonationModal();
}
render() {
const {
fetchState: { pending, complete },
isSignedIn,
user: { acceptedPrivacyTerms },
children
}) {
} = this.props;
if (pending && !complete) {
return <Loader fullScreen={true} />;
}
@ -47,9 +60,11 @@ function LearnLayout({
return (
<Fragment>
<main id='learn-app-wrapper'>{children}</main>
<DonateModal />
</Fragment>
);
}
}
LearnLayout.displayName = 'LearnLayout';
LearnLayout.propTypes = {
@ -60,9 +75,13 @@ LearnLayout.propTypes = {
errored: PropTypes.bool
}),
isSignedIn: PropTypes.bool,
tryToShowDonationModal: PropTypes.func.isRequired,
user: PropTypes.shape({
acceptedPrivacyTerms: PropTypes.bool
})
};
export default connect(mapStateToProps)(LearnLayout);
export default connect(
mapStateToProps,
mapDispatchToProps
)(LearnLayout);

View File

@ -208,18 +208,19 @@ fieldset[disabled] .btn-primary.focus {
}
.btn-cta {
background-color: #ffac33;
background-image: linear-gradient(#ffcc4d, #ffac33);
border-color: #f1a02a;
background-color: #feac32;
background-image: linear-gradient(#fecc4c, #ffac33);
border-width: 3px;
border-color: #feac32;
color: #0a0a23 !important;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
border: none;
}
.btn-cta:hover,
.btn-cta:focus,
.btn-cta:active:hover {
background-color: #e99110;
background-image: linear-gradient(#ffcc4d, #e99110);
background-color: #fecc4c;
border-width: 3px;
border-color: #f1a02a;
background-image: none;
color: #0a0a23 !important;
}
.btn-cta:active {

View File

@ -19,6 +19,8 @@
--green-dark: #00471b;
--red-light: #ffadad;
--red-dark: #850000;
--love-light: #f8577c;
--love-dark: #f82153;
}
.dark-palette {
@ -36,6 +38,7 @@
--success-background: var(--green-dark);
--danger-color: var(--red-light);
--danger-background: var(--red-dark);
--love-color: var(--love-light);
}
.light-palette {
@ -53,4 +56,5 @@
--success-background: var(--green-light);
--danger-color: var(--red-dark);
--danger-background: var(--red-light);
--love-color: var(--love-dark);
}

View File

@ -35,6 +35,23 @@
display: flex;
align-items: center;
min-height: 100vh;
flex-direction: column;
}
.certificate-outer-wrapper .certification-donation {
padding: 15px 0px;
}
.certificate-outer-wrapper .certification-donation .btn {
background-color: var(--gray-15);
border-color: var(--gray-85);
color: var(--gray-85);
}
.certificate-outer-wrapper .certification-donation .btn:hover {
border-color: var(--gray-85);
background-color: var(--gray-85);
color: var(--gray-05);
}
.certification-namespace header {

View File

@ -0,0 +1,20 @@
import { put, select, takeEvery, delay } from 'redux-saga/effects';
import {
openDonationModal,
shouldRequestDonationSelector,
preventDonationRequests
} from './';
function* showDonateModalSaga() {
let shouldRequestDonation = yield select(shouldRequestDonationSelector);
if (shouldRequestDonation) {
yield delay(200);
yield put(openDonationModal());
yield put(preventDonationRequests());
}
}
export function createDonationSaga(types) {
return [takeEvery(types.tryToShowDonationModal, showDonateModalSaga)];
}

View File

@ -10,6 +10,7 @@ import { createAppMountSaga } from './app-mount-saga';
import { createReportUserSaga } from './report-user-saga';
import { createShowCertSaga } from './show-cert-saga';
import { createNightModeSaga } from './night-mode-saga';
import { createDonationSaga } from './donation-saga';
import hardGoToEpic from './hard-go-to-epic';
import failedUpdatesEpic from './failed-updates-epic';
@ -31,6 +32,7 @@ export const defaultFetchState = {
const initialState = {
appUsername: '',
canRequestDonation: false,
completionCount: 0,
currentChallengeId: store.get(CURRENT_CHALLENGE_KEY),
donationRequested: false,
@ -53,12 +55,14 @@ const initialState = {
export const types = createTypes(
[
'appMount',
'closeDonationModal',
'donationRequested',
'hardGoTo',
'allowDonationRequests',
'closeDonationModal',
'preventDonationRequests',
'openDonationModal',
'onlineStatusChange',
'resetUserData',
'tryToShowDonationModal',
'submitComplete',
'updateComplete',
'updateCurrentChallengeId',
@ -77,6 +81,7 @@ export const epics = [hardGoToEpic, failedUpdatesEpic, updateCompleteEpic];
export const sagas = [
...createAcceptTermsSaga(types),
...createAppMountSaga(types),
...createDonationSaga(types),
...createFetchUserSaga(types),
...createShowCertSaga(types),
...createReportUserSaga(types),
@ -85,9 +90,15 @@ export const sagas = [
export const appMount = createAction(types.appMount);
export const tryToShowDonationModal = createAction(
types.tryToShowDonationModal
);
export const allowDonationRequests = createAction(types.allowDonationRequests);
export const closeDonationModal = createAction(types.closeDonationModal);
export const openDonationModal = createAction(types.openDonationModal);
export const donationRequested = createAction(types.donationRequested);
export const preventDonationRequests = createAction(
types.preventDonationRequests
);
export const onlineStatusChange = createAction(types.onlineStatusChange);
@ -134,7 +145,8 @@ export const completedChallengesSelector = state =>
userSelector(state).completedChallenges || [];
export const completionCountSelector = state => state[ns].completionCount;
export const currentChallengeIdSelector = state => state[ns].currentChallengeId;
export const donationRequestedSelector = state => state[ns].donationRequested;
export const isDonationRequestedSelector = state => state[ns].donationRequested;
export const isDonatingSelector = state => userSelector(state).isDonating;
export const isOnlineSelector = state => state[ns].isOnline;
export const isSignedInSelector = state => !!state[ns].appUsername;
@ -145,21 +157,14 @@ export const signInLoadingSelector = state =>
export const showCertSelector = state => state[ns].showCert;
export const showCertFetchStateSelector = state => state[ns].showCertFetchState;
export const showDonationSelector = state => {
const completedChallenges = completedChallengesSelector(state);
const completionCount = completionCountSelector(state);
const currentCompletedLength = completedChallenges.length;
const donationRequested = donationRequestedSelector(state);
// the user has not completed 9 challenges in total yet
if (currentCompletedLength < 9) {
return false;
}
// this will mean we are on the 10th submission in total for the user
if (completedChallenges.length === 9 && donationRequested === false) {
return true;
}
// this will mean we are on the 3rd submission for this browser session
if (completionCount === 2 && donationRequested === false) {
export const shouldRequestDonationSelector = state => {
const isDonationRequested = isDonationRequestedSelector(state);
const isDonating = isDonatingSelector(state);
if (
isDonationRequested === false &&
isDonating === false &&
state[ns].canRequestDonation
) {
return true;
}
return false;
@ -209,6 +214,10 @@ function spreadThePayloadOnUser(state, payload) {
export const reducer = handleActions(
{
[types.allowDonationRequests]: state => ({
...state,
canRequestDonation: true
}),
[types.fetchUser]: state => ({
...state,
userFetchState: { ...defaultFetchState }
@ -288,7 +297,7 @@ export const reducer = handleActions(
...state,
showDonationModal: true
}),
[types.donationRequested]: state => ({
[types.preventDonationRequests]: state => ({
...state,
donationRequested: true
}),

View File

@ -21,7 +21,8 @@ import {
isCompletionModalOpenSelector,
successMessageSelector,
challengeFilesSelector,
challengeMetaSelector
challengeMetaSelector,
lastBlockChalSubmitted
} from '../redux';
import { isSignedInSelector } from '../../../redux';
@ -54,17 +55,11 @@ const mapStateToProps = createSelector(
const mapDispatchToProps = function(dispatch) {
const dispatchers = {
close: () => dispatch(closeModal('completion')),
handleKeypress: e => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
// Since Hotkeys also listens to Ctrl + Enter we have to stop this event
// getting to it.
e.stopPropagation();
dispatch(submitChallenge());
}
},
submitChallenge: () => {
dispatch(submitChallenge());
},
lastBlockChalSubmitted: () => {
dispatch(lastBlockChalSubmitted());
}
};
return () => dispatchers;
@ -76,18 +71,18 @@ const propTypes = {
completedChallengesIds: PropTypes.array,
currentBlockIds: PropTypes.array,
files: PropTypes.object.isRequired,
handleKeypress: PropTypes.func.isRequired,
id: PropTypes.string,
isOpen: PropTypes.bool,
isSignedIn: PropTypes.bool.isRequired,
lastBlockChalSubmitted: PropTypes.func,
message: PropTypes.string,
submitChallenge: PropTypes.func.isRequired,
title: PropTypes.string
};
export function getCompletedPercent(
completedChallengesIds,
currentBlockIds,
completedChallengesIds = [],
currentBlockIds = [],
currentChallengeId
) {
completedChallengesIds = completedChallengesIds.includes(currentChallengeId)
@ -106,8 +101,15 @@ export function getCompletedPercent(
}
export class CompletionModalInner extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleKeypress = this.handleKeypress.bind(this);
}
state = {
downloadURL: null
downloadURL: null,
completedPercent: 0
};
static getDerivedStateFromProps(props, state) {
@ -135,7 +137,37 @@ export class CompletionModalInner extends Component {
});
newURL = URL.createObjectURL(blob);
}
return { downloadURL: newURL };
const { completedChallengesIds, currentBlockIds, id, isSignedIn } = props;
let completedPercent = isSignedIn
? getCompletedPercent(completedChallengesIds, currentBlockIds, id)
: 0;
return { downloadURL: newURL, completedPercent: completedPercent };
}
handleKeypress(e) {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
// Since Hotkeys also listens to Ctrl + Enter we have to stop this event
// getting to it.
e.stopPropagation();
this.handleSubmit();
}
}
handleSubmit() {
this.props.submitChallenge();
this.checkBlockCompletion();
}
// check block completion for donation
checkBlockCompletion() {
if (
this.state.completedPercent === 100 &&
!this.props.completedChallengesIds.includes(this.props.id)
) {
this.props.lastBlockChalSubmitted();
}
}
componentWillUnmount() {
@ -149,20 +181,13 @@ export class CompletionModalInner extends Component {
const {
blockName = '',
close,
completedChallengesIds = [],
currentBlockIds = [],
id = '',
isOpen,
isSignedIn,
submitChallenge,
handleKeypress,
message,
title
title,
isSignedIn
} = this.props;
const completedPercent = !isSignedIn
? 0
: getCompletedPercent(completedChallengesIds, currentBlockIds, id);
const { completedPercent } = this.state;
if (isOpen) {
ga.modalview('/completion-modal');
@ -175,7 +200,7 @@ export class CompletionModalInner extends Component {
dialogClassName='challenge-success-modal'
keyboard={true}
onHide={close}
onKeyDown={isOpen ? handleKeypress : noop}
onKeyDown={isOpen ? this.handleKeypress : noop}
show={isOpen}
>
<Modal.Header
@ -195,7 +220,7 @@ export class CompletionModalInner extends Component {
block={true}
bsSize='large'
bsStyle='primary'
onClick={submitChallenge}
onClick={this.handleSubmit}
>
{isSignedIn ? 'Submit and g' : 'G'}o to next challenge{' '}
<span className='hidden-xs'>(Ctrl + Enter)</span>

View File

@ -132,6 +132,7 @@ export default function completionEpic(action$, state$) {
const meta = challengeMetaSelector(state);
const { nextChallengePath, introPath, challengeType } = meta;
const closeChallengeModal = of(closeModal('completion'));
let submitter = () => of({ type: 'no-user-signed-in' });
if (
!(challengeType in submitTypes) ||

View File

@ -4,7 +4,8 @@ import store from 'store';
import {
isSignedInSelector,
updateComplete,
updateFailed
updateFailed,
allowDonationRequests
} from '../../../redux';
import { post } from '../../../utils/ajax';
@ -33,13 +34,18 @@ export function* currentChallengeSaga({ payload: id }) {
}
}
function* updateSuccessMessageSaga() {
export function* updateSuccessMessageSaga() {
yield put(updateSuccessMessage(randomCompliment()));
}
export function* allowDonationRequestsSaga() {
yield put(allowDonationRequests());
}
export function createCurrentChallengeSaga(types) {
return [
takeEvery(types.challengeMounted, currentChallengeSaga),
takeEvery(types.challengeMounted, updateSuccessMessageSaga)
takeEvery(types.challengeMounted, updateSuccessMessageSaga),
takeEvery(types.lastBlockChalSubmitted, allowDonationRequestsSaga)
];
}

View File

@ -0,0 +1,12 @@
/* global expect */
import { allowDonationRequestsSaga } from './current-challenge-saga';
import { types as appTypes } from '../../../redux';
describe('allowDonationRequestsSaga', () => {
it('should call allowDonationRequests', () => {
const gen = allowDonationRequestsSaga();
expect(gen.next().value.payload.action.type).toEqual(
appTypes.allowDonationRequests
);
});
});

View File

@ -30,6 +30,7 @@ const initialState = {
},
challengeTests: [],
consoleOut: '',
hasCompletedBlock: false,
inAccessibilityMode: false,
isCodeLocked: false,
isBuildEnabled: true,
@ -81,7 +82,9 @@ export const types = createTypes(
'moveToTab',
'setEditorFocusability',
'setAccessibilityMode'
'setAccessibilityMode',
'lastBlockChalSubmitted'
],
ns
);
@ -157,6 +160,10 @@ export const moveToTab = createAction(types.moveToTab);
export const setEditorFocusability = createAction(types.setEditorFocusability);
export const setAccessibilityMode = createAction(types.setAccessibilityMode);
export const lastBlockChalSubmitted = createAction(
types.lastBlockChalSubmitted
);
export const currentTabSelector = state => state[ns].currentTab;
export const challengeFilesSelector = state => state[ns].challengeFiles;
export const challengeMetaSelector = state => state[ns].challengeMeta;

View File

@ -15,7 +15,9 @@ export default function layoutSelector({ element, props }) {
return <DefaultLayout pathname={pathname}>{element}</DefaultLayout>;
}
if (/^\/certification(\/.*)*/.test(pathname)) {
return <CertificationLayout>{element}</CertificationLayout>;
return (
<CertificationLayout pathname={pathname}>{element}</CertificationLayout>
);
}
if (/^\/guide(\/.*)*/.test(pathname)) {
console.log('Hitting guide for some reason. Need a redirect.');