feat(client): ts-migration for Introduction templates (#43752)

* chore: rename the file CertificationCard.js to tsx

* refactor: refactor the file CertificationCard to tsx

refactor: refactor the file CertificationCard to tsx

refactor: refactor the file CertificationCard to tsx

* chore: rename the file ClaimCertSteps.js to tsx

* refactor: refactor the file ClaimCertSteps to TS

* chore: rename the file SuperBlockIntro.js to tsx

* refactor: refactor the file SuperBlockIntro to TS

* chore: rename the file Block.js to tsx

* refactor: refactor the file Block to TS

refactor: refactor the file Block to TS

refactor: refactor the file Block to TS

* chore: rename the file CertChallenge.js to tsx

* refactor: refactor the file CertChallenge to TS

* fix typing

* ignore missing redux store connection

Co-authored-by: ismail <i.tlemcani@quinten-maroc.com>
Co-authored-by: Ismail Tlemcani <ismail.tlemcani@gmail.com>
Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
This commit is contained in:
Ismail Tlemcani
2021-11-30 19:36:33 +01:00
committed by GitHub
parent 513f27e408
commit 845659105f
8 changed files with 164 additions and 124 deletions

View File

@ -1,6 +1,7 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { HandlerProps } from 'react-reflex'; import { HandlerProps } from 'react-reflex';
import { SuperBlocks } from '../../../config/certification-settings'; import { SuperBlocks } from '../../../config/certification-settings';
import { certMap } from '../resources/cert-and-project-map';
export const UserPropType = PropTypes.shape({ export const UserPropType = PropTypes.shape({
about: PropTypes.string, about: PropTypes.string,
@ -63,12 +64,13 @@ export const CurrentCertsPropType = PropTypes.arrayOf(
}) })
); );
export const StepsPropType = PropTypes.shape({ export type Steps = {
currentCerts: CurrentCertsPropType, isHonest?: boolean;
isShowCerts: PropTypes.bool, currentCerts?: Array<CurrentCert>;
isShowName: PropTypes.bool, isShowCerts?: boolean;
isShowProfile: PropTypes.bool isShowName?: boolean;
}); isShowProfile?: boolean;
};
export type CurrentCert = { export type CurrentCert = {
show: boolean; show: boolean;
@ -83,7 +85,7 @@ export type MarkdownRemark = {
block: string; block: string;
isBeta: boolean; isBeta: boolean;
superBlock: SuperBlocks; superBlock: SuperBlocks;
title: string; title: typeof certMap[number]['title'];
}; };
headings: [ headings: [
{ {

View File

@ -1,9 +1,8 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { withTranslation } from 'react-i18next'; import { withTranslation, TFunction } from 'react-i18next';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import ScrollableAnchor from 'react-scrollable-anchor'; import ScrollableAnchor from 'react-scrollable-anchor';
import { bindActionCreators } from 'redux'; import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import store from 'store'; import store from 'store';
@ -12,60 +11,69 @@ import { isAuditedCert } from '../../../../../utils/is-audited';
import Caret from '../../../assets/icons/caret'; import Caret from '../../../assets/icons/caret';
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 { Link } from '../../../components/helpers/'; import { Link } from '../../../components/helpers';
import { completedChallengesSelector, executeGA } from '../../../redux'; import { completedChallengesSelector, executeGA } from '../../../redux';
import { ChallengeNode, CompletedChallenge } from '../../../redux/prop-types';
import { makeExpandedBlockSelector, toggleBlock } from '../redux'; import { makeExpandedBlockSelector, toggleBlock } from '../redux';
import Challenges from './Challenges'; import Challenges from './Challenges';
const { curriculumLocale } = envData; const { curriculumLocale } = envData;
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (
state: unknown,
ownProps: { blockDashedName: string } & unknown
) => {
const expandedSelector = makeExpandedBlockSelector(ownProps.blockDashedName); const expandedSelector = makeExpandedBlockSelector(ownProps.blockDashedName);
return createSelector( return createSelector(
expandedSelector, expandedSelector,
completedChallengesSelector, completedChallengesSelector,
(isExpanded, completedChallenges) => ({ (isExpanded: boolean, completedChallenges: CompletedChallenge[]) => ({
isExpanded, isExpanded,
completedChallenges: completedChallenges.map(({ id }) => id) completedChallengeIds: completedChallenges.map(({ id }) => id)
}) })
)(state); )(state);
}; };
const mapDispatchToProps = dispatch => const mapDispatchToProps = (dispatch: Dispatch) =>
bindActionCreators({ toggleBlock, executeGA }, dispatch); bindActionCreators({ toggleBlock, executeGA }, dispatch);
const propTypes = { interface BlockProps {
blockDashedName: PropTypes.string, blockDashedName: string;
challenges: PropTypes.array, challenges: ChallengeNode[];
completedChallenges: PropTypes.arrayOf(PropTypes.string), completedChallengeIds: string[];
executeGA: PropTypes.func, executeGA: typeof executeGA;
isExpanded: PropTypes.bool, isExpanded: boolean;
superBlock: PropTypes.string, superBlock: string;
t: PropTypes.func, t: TFunction;
toggleBlock: PropTypes.func.isRequired toggleBlock: typeof toggleBlock;
}; }
const mapIconStyle = { height: '15px', marginRight: '10px', width: '15px' }; const mapIconStyle = { height: '15px', marginRight: '10px', width: '15px' };
export class Block extends Component { export class Block extends Component<BlockProps> {
constructor(...props) { static displayName: string;
super(...props); constructor(props: BlockProps) {
super(props);
this.handleBlockClick = this.handleBlockClick.bind(this); this.handleBlockClick = this.handleBlockClick.bind(this);
} }
handleBlockClick() { handleBlockClick(): void {
const { blockDashedName, toggleBlock, executeGA } = this.props; const { blockDashedName, toggleBlock, executeGA } = this.props;
const playSound = store.get('fcc-sound'); const playSound = store.get('fcc-sound') as boolean;
if (playSound) { if (playSound) {
void import('tone').then(tone => { void (async () => {
const tone = await import('tone');
const player = new tone.Player( const player = new tone.Player(
'https://tonejs.github.io/audio/berklee/guitar_chord1.mp3' 'https://tonejs.github.io/audio/berklee/guitar_chord1.mp3'
).toDestination(); ).toDestination();
if (tone.context.state !== 'running') tone.context.resume(); if (tone.context.state !== 'running') {
void tone.context.resume();
}
player.autostart = playSound; player.autostart = playSound;
}); })();
} }
executeGA({ executeGA({
type: 'event', type: 'event',
@ -74,10 +82,10 @@ export class Block extends Component {
action: blockDashedName action: blockDashedName
} }
}); });
return toggleBlock(blockDashedName); toggleBlock(blockDashedName);
} }
renderCheckMark(isCompleted) { renderCheckMark(isCompleted: boolean): JSX.Element {
return isCompleted ? ( return isCompleted ? (
<GreenPass style={mapIconStyle} /> <GreenPass style={mapIconStyle} />
) : ( ) : (
@ -85,7 +93,7 @@ export class Block extends Component {
); );
} }
renderBlockIntros(arr) { renderBlockIntros(arr: string[]): JSX.Element {
return ( return (
<div className='block-description'> <div className='block-description'>
{arr.map((str, i) => ( {arr.map((str, i) => (
@ -95,10 +103,10 @@ export class Block extends Component {
); );
} }
render() { render(): JSX.Element {
const { const {
blockDashedName, blockDashedName,
completedChallenges, completedChallengeIds,
challenges, challenges,
isExpanded, isExpanded,
superBlock, superBlock,
@ -108,8 +116,8 @@ export class Block extends Component {
let completedCount = 0; let completedCount = 0;
const challengesWithCompleted = challenges.map(challenge => { const challengesWithCompleted = challenges.map(challenge => {
const { id } = challenge; const { id } = challenge;
const isCompleted = completedChallenges.some( const isCompleted = completedChallengeIds.some(
completedId => id === completedId (completedChallengeId: string) => completedChallengeId === id
); );
if (isCompleted) { if (isCompleted) {
completedCount++; completedCount++;
@ -134,13 +142,19 @@ export class Block extends Component {
); );
}); });
const blockIntroObj = t(`intro:${superBlock}.blocks.${blockDashedName}`); const blockIntroObj: { title?: string; intro: string[] } = t(
`intro:${superBlock}.blocks.${blockDashedName}`
);
const blockTitle = blockIntroObj ? blockIntroObj.title : null; const blockTitle = blockIntroObj ? blockIntroObj.title : null;
const blockIntroArr = blockIntroObj ? blockIntroObj.intro : []; const blockIntroArr = blockIntroObj ? blockIntroObj.intro : [];
const { const {
expand: expandText, expand: expandText,
collapse: collapseText, collapse: collapseText,
courses: coursesText courses: coursesText
}: {
expand: string;
collapse: string;
courses: string;
} = t('intro:misc-text'); } = t('intro:misc-text');
return isProjectBlock ? ( return isProjectBlock ? (
@ -196,7 +210,9 @@ export class Block extends Component {
<button <button
aria-expanded={isExpanded} aria-expanded={isExpanded}
className='map-title' className='map-title'
onClick={this.handleBlockClick} onClick={() => {
this.handleBlockClick();
}}
> >
<Caret /> <Caret />
<h4 className='course-title'> <h4 className='course-title'>
@ -224,7 +240,6 @@ export class Block extends Component {
} }
Block.displayName = 'Block'; Block.displayName = 'Block';
Block.propTypes = propTypes;
export default connect( export default connect(
mapStateToProps, mapStateToProps,

View File

@ -1,13 +1,13 @@
import { Button } from '@freecodecamp/react-bootstrap'; import { Button } from '@freecodecamp/react-bootstrap';
import { navigate } from 'gatsby-link'; import { navigate } from 'gatsby-link';
import PropTypes from 'prop-types';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { withTranslation } from 'react-i18next'; import { TFunction, 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,
SuperBlocks
} from '../../../../../config/certification-settings'; } from '../../../../../config/certification-settings';
import { createFlashMessage } from '../../../components/Flash/redux'; import { createFlashMessage } from '../../../components/Flash/redux';
import { import {
@ -15,41 +15,40 @@ import {
stepsToClaimSelector, stepsToClaimSelector,
isSignedInSelector isSignedInSelector
} from '../../../redux'; } from '../../../redux';
import { User, Steps } from '../../../redux/prop-types';
import { StepsPropType, UserPropType } from '../../../redux/prop-types';
import { verifyCert } from '../../../redux/settings'; import { verifyCert } from '../../../redux/settings';
import { certMap } from '../../../resources/cert-and-project-map'; import { certMap } from '../../../resources/cert-and-project-map';
import { getVerifyCanClaimCert } from '../../../utils/ajax'; import { getVerifyCanClaimCert } from '../../../utils/ajax';
import CertificationCard from './CertificationCard'; import CertificationCard from './certification-card';
const propTypes = { interface CertChallengeProps {
createFlashMessage: PropTypes.func.isRequired, createFlashMessage: typeof createFlashMessage;
fetchState: PropTypes.shape({ fetchState: {
pending: PropTypes.bool, pending: boolean;
complete: PropTypes.bool, complete: boolean;
errored: PropTypes.bool errored: boolean;
}), error: null | string;
isSignedIn: PropTypes.bool,
steps: StepsPropType,
superBlock: PropTypes.string,
t: PropTypes.func,
title: PropTypes.string,
user: UserPropType,
verifyCert: PropTypes.func.isRequired
}; };
isSignedIn: boolean;
steps: Steps;
superBlock: SuperBlocks;
t: TFunction;
title: typeof certMap[number]['title'];
user: User;
verifyCert: typeof verifyCert;
}
const honestyInfoMessage = { const honestyInfoMessage = {
type: 'info', type: 'info',
message: 'flash.honest-first' message: 'flash.honest-first'
}; };
const mapStateToProps = state => { const mapStateToProps = (state: unknown) => {
return createSelector( return createSelector(
stepsToClaimSelector, stepsToClaimSelector,
userFetchStateSelector, userFetchStateSelector,
isSignedInSelector, isSignedInSelector,
(steps, fetchState, isSignedIn) => ({ (steps, fetchState: CertChallengeProps['fetchState'], isSignedIn) => ({
steps, steps,
fetchState, fetchState,
isSignedIn isSignedIn
@ -72,7 +71,7 @@ const CertChallenge = ({
fetchState, fetchState,
isSignedIn, isSignedIn,
user: { isHonest, username } user: { isHonest, username }
}) => { }: CertChallengeProps): JSX.Element => {
const [canClaimCert, setCanClaimCert] = useState(false); const [canClaimCert, setCanClaimCert] = useState(false);
const [certVerificationMessage, setCertVerificationMessage] = useState(''); const [certVerificationMessage, setCertVerificationMessage] = useState('');
const [isCertified, setIsCertified] = useState(false); const [isCertified, setIsCertified] = useState(false);
@ -88,7 +87,7 @@ const CertChallenge = ({
useEffect(() => { useEffect(() => {
if (username) { if (username) {
(async () => { void (async () => {
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;
@ -103,7 +102,8 @@ const CertChallenge = ({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [username]); }, [username]);
const { certSlug } = certMap.find(x => x.title === title); // @ts-expect-error Typescript is confused
const certSlug = certMap.find(x => x.title === title).certSlug;
useEffect(() => { useEffect(() => {
const { pending, complete } = fetchState; const { pending, complete } = fetchState;
@ -113,20 +113,26 @@ const CertChallenge = ({
} }
}, [fetchState]); }, [fetchState]);
const certSlugTypeMapTyped: { [key: string]: string } = certSlugTypeMap;
const superBlockCertTypeMapTyped: { [key: string]: string } =
superBlockCertTypeMap;
useEffect(() => { useEffect(() => {
setIsCertified( setIsCertified(
steps?.currentCerts?.find( steps?.currentCerts?.find(
cert => (cert: { certSlug: string }) =>
certSlugTypeMap[cert.certSlug] === superBlockCertTypeMap[superBlock] certSlugTypeMapTyped[cert.certSlug] ===
superBlockCertTypeMapTyped[superBlock]
)?.show ?? false )?.show ?? false
); );
const projectsCompleted = const projectsCompleted =
canClaimCert || certVerificationMessage === 'projects-completed'; canClaimCert || certVerificationMessage === 'projects-completed';
const projectsCompletedNumber = projectsCompleted ? 1 : 0;
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 + projectsCompletedNumber;
const numberOfSteps = Object.keys(steps).length; const numberOfSteps = Object.keys(steps).length;
setHasCompletedRequiredSteps(completedCount === numberOfSteps); setHasCompletedRequiredSteps(completedCount === numberOfSteps);
setStepState({ numberOfSteps, completedCount }); setStepState({ numberOfSteps, completedCount });
@ -145,7 +151,8 @@ const CertChallenge = ({
isSignedIn && isSignedIn &&
(!isCertified || (!hasCompletedRequiredSteps && verificationComplete)); (!isCertified || (!hasCompletedRequiredSteps && verificationComplete));
const createClickHandler = certSlug => e => { const createClickHandler =
(certSlug: string | undefined) => (e: { preventDefault: () => void }) => {
e.preventDefault(); e.preventDefault();
if (isCertified) { if (isCertified) {
return navigate(certLocation); return navigate(certLocation);
@ -188,7 +195,6 @@ const CertChallenge = ({
}; };
CertChallenge.displayName = 'CertChallenge'; CertChallenge.displayName = 'CertChallenge';
CertChallenge.propTypes = propTypes;
export { CertChallenge }; export { CertChallenge };

View File

@ -1,26 +1,24 @@
import PropTypes from 'prop-types';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import ScrollableAnchor from 'react-scrollable-anchor'; import ScrollableAnchor from 'react-scrollable-anchor';
import { SuperBlocks } from '../../../../../config/certification-settings';
import Caret from '../../../assets/icons/caret'; import Caret from '../../../assets/icons/caret';
import GreenNotCompleted from '../../../assets/icons/green-not-completed'; import GreenNotCompleted from '../../../assets/icons/green-not-completed';
// import { navigate } from 'gatsby';
import GreenPass from '../../../assets/icons/green-pass'; import GreenPass from '../../../assets/icons/green-pass';
import { StepsPropType } from '../../../redux/prop-types'; import { Steps } from '../../../redux/prop-types';
import ClaimCertSteps from './ClaimCertSteps'; import ClaimCertSteps from './claim-cert-steps';
const propTypes = { interface CertificationCardProps {
i18nCertText: PropTypes.string, i18nCertText: string;
isProjectsCompleted: PropTypes.bool, isProjectsCompleted: boolean;
stepState: PropTypes.shape({ stepState: {
numberOfSteps: PropTypes.number, numberOfSteps: number;
completedCount: PropTypes.number completedCount: number;
}),
steps: StepsPropType,
superBlock: PropTypes.string
}; };
steps: Steps;
superBlock: SuperBlocks;
}
const mapIconStyle = { height: '15px', marginRight: '10px', width: '15px' }; const mapIconStyle = { height: '15px', marginRight: '10px', width: '15px' };
const CertificationCard = ({ const CertificationCard = ({
@ -29,7 +27,7 @@ const CertificationCard = ({
i18nCertText, i18nCertText,
stepState: { completedCount, numberOfSteps }, stepState: { completedCount, numberOfSteps },
steps steps
}) => { }: CertificationCardProps): JSX.Element => {
const { t } = useTranslation(); const { t } = useTranslation();
const [isExpanded, setIsExpanded] = useState(true); const [isExpanded, setIsExpanded] = useState(true);
@ -41,6 +39,10 @@ const CertificationCard = ({
expand: expandText, expand: expandText,
collapse: collapseText, collapse: collapseText,
courses: coursesText courses: coursesText
}: {
expand: string;
collapse: string;
courses: string;
} = t('intro:misc-text'); } = t('intro:misc-text');
return ( return (
<ScrollableAnchor id='claim-cert-block'> <ScrollableAnchor id='claim-cert-block'>
@ -90,6 +92,5 @@ const CertificationCard = ({
}; };
CertificationCard.displayName = 'CertStatus'; CertificationCard.displayName = 'CertStatus';
CertificationCard.propTypes = propTypes;
export default CertificationCard; export default CertificationCard;

View File

@ -1,28 +1,28 @@
import { Link } from 'gatsby'; import { Link } from 'gatsby';
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 { SuperBlocks } from '../../../../../config/certification-settings';
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 { StepsPropType } from '../../../redux/prop-types'; import { Steps } from '../../../redux/prop-types';
const mapIconStyle = { height: '15px', marginRight: '10px', width: '15px' }; const mapIconStyle = { height: '15px', marginRight: '10px', width: '15px' };
const propTypes = { interface ClaimCertStepsProps {
i18nCertText: PropTypes.string, i18nCertText: string;
isProjectsCompleted: PropTypes.bool, isProjectsCompleted: boolean;
steps: StepsPropType, steps: Steps;
superBlock: PropTypes.string superBlock: SuperBlocks;
}; }
const ClaimCertSteps = ({ const ClaimCertSteps = ({
isProjectsCompleted, isProjectsCompleted,
i18nCertText, i18nCertText,
steps, steps,
superBlock superBlock
}) => { }: ClaimCertStepsProps): JSX.Element => {
const { t } = useTranslation(); const { t } = useTranslation();
const renderCheckMark = isCompleted => { const renderCheckMark = (isCompleted: boolean) => {
return isCompleted ? ( return isCompleted ? (
<GreenPass style={mapIconStyle} /> <GreenPass style={mapIconStyle} />
) : ( ) : (
@ -84,6 +84,5 @@ const ClaimCertSteps = ({
}; };
ClaimCertSteps.displayName = 'ClaimCertSteps'; ClaimCertSteps.displayName = 'ClaimCertSteps';
ClaimCertSteps.propTypes = propTypes;
export default withTranslation()(ClaimCertSteps); export default withTranslation()(ClaimCertSteps);

View File

@ -1,19 +1,22 @@
import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { SuperBlocks } from '../../../../../config/certification-settings';
import { generateIconComponent } from '../../../assets/icons'; import { generateIconComponent } from '../../../assets/icons';
import { Spacer } from '../../../components/helpers'; import { Spacer } from '../../../components/helpers';
const propTypes = { interface SuperBlockIntroProps {
superBlock: PropTypes.string superBlock: SuperBlocks;
}; }
function SuperBlockIntro(props) { function SuperBlockIntro(props: SuperBlockIntroProps): JSX.Element {
const { t } = useTranslation(); const { t } = useTranslation();
const { superBlock } = props; const { superBlock } = props;
const superBlockIntroObj = t(`intro:${superBlock}`); const superBlockIntroObj: {
title: string;
intro: string[];
note: string[];
} = t(`intro:${superBlock}`);
const { const {
title: i18nSuperBlock, title: i18nSuperBlock,
intro: superBlockIntroText, intro: superBlockIntroText,
@ -39,6 +42,5 @@ function SuperBlockIntro(props) {
} }
SuperBlockIntro.displayName = 'SuperBlockIntro'; SuperBlockIntro.displayName = 'SuperBlockIntro';
SuperBlockIntro.propTypes = propTypes;
export default SuperBlockIntro; export default SuperBlockIntro;

View File

@ -25,9 +25,9 @@ import {
userSelector userSelector
} from '../../redux'; } from '../../redux';
import { MarkdownRemark, AllChallengeNode, User } from '../../redux/prop-types'; import { MarkdownRemark, AllChallengeNode, User } from '../../redux/prop-types';
import Block from './components/Block'; import Block from './components/block';
import CertChallenge from './components/CertChallenge'; import CertChallenge from './components/cert-challenge';
import SuperBlockIntro from './components/SuperBlockIntro'; import SuperBlockIntro from './components/super-block-intro';
import { resetExpansion, toggleBlock } from './redux'; import { resetExpansion, toggleBlock } from './redux';
import './intro.css'; import './intro.css';

View File

@ -3,6 +3,7 @@ import envData from '../../../config/env.json';
import type { import type {
ChallengeFile, ChallengeFile,
ClaimedCertifications,
CompletedChallenge, CompletedChallenge,
User User
} from '../redux/prop-types'; } from '../redux/prop-types';
@ -158,11 +159,25 @@ export function getUsernameExists(username: string): Promise<boolean> {
return get(`/api/users/exists?username=${username}`); return get(`/api/users/exists?username=${username}`);
} }
// TODO: Does a GET return a bolean? export interface GetVerifyCanClaimCert {
response: {
type: string;
message: {
status: boolean;
result: string;
};
variables: {
name: string;
};
};
isCertMap: ClaimedCertifications;
completedChallenges: CompletedChallenge[];
}
export function getVerifyCanClaimCert( export function getVerifyCanClaimCert(
username: string, username: string,
superBlock: string superBlock: string
): Promise<boolean> { ): Promise<GetVerifyCanClaimCert> {
return get( return get(
`/certificate/verify-can-claim-cert?username=${username}&superBlock=${superBlock}` `/certificate/verify-can-claim-cert?username=${username}&superBlock=${superBlock}`
); );