From 8238495c8b3e6f88a22f5781798164dd5e3dd0fb Mon Sep 17 00:00:00 2001 From: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com> Date: Wed, 8 Oct 2025 13:31:59 +0700 Subject: [PATCH] fix(client): add challenge title to exam download page (#62584) --- .../Challenges/exam-download/show.tsx | 147 +++++++++++------- 1 file changed, 88 insertions(+), 59 deletions(-) diff --git a/client/src/templates/Challenges/exam-download/show.tsx b/client/src/templates/Challenges/exam-download/show.tsx index 45836b221ee..3ae6465ea56 100644 --- a/client/src/templates/Challenges/exam-download/show.tsx +++ b/client/src/templates/Challenges/exam-download/show.tsx @@ -1,15 +1,24 @@ import React, { useEffect, useState } from 'react'; import { graphql } from 'gatsby'; -import { Button, Dropdown, MenuItem, Spacer } from '@freecodecamp/ui'; +import Helmet from 'react-helmet'; +import { + Button, + Dropdown, + MenuItem, + Spacer, + Container +} from '@freecodecamp/ui'; import { isEmpty } from 'lodash'; import { useTranslation, withTranslation } from 'react-i18next'; import { createSelector } from 'reselect'; import { connect } from 'react-redux'; -import { FullWidthRow } from '../../../components/helpers'; +import LearnLayout from '../../../components/layouts/learn'; +import ChallengeTitle from '../components/challenge-title'; import useDetectOS from '../utils/use-detect-os'; import { ChallengeNode } from '../../../redux/prop-types'; import { isSignedInSelector } from '../../../redux/selectors'; +import { isChallengeCompletedSelector } from '../redux/selectors'; import { Attempts } from './attempts'; interface GitProps { @@ -20,23 +29,27 @@ interface GitProps { } const mapStateToProps = createSelector( + isChallengeCompletedSelector, isSignedInSelector, - (isSignedIn: boolean) => ({ + (isChallengeCompleted: boolean, isSignedIn: boolean) => ({ + isChallengeCompleted, isSignedIn }) ); interface ShowExamDownloadProps { data: { challengeNode: ChallengeNode }; + isChallengeCompleted: boolean; isSignedIn: boolean; } function ShowExamDownload({ data: { challengeNode: { - challenge: { id } + challenge: { id, title, translationPending } } }, + isChallengeCompleted, isSignedIn }: ShowExamDownloadProps): JSX.Element { const [latestVersion, setLatestVersion] = useState(null); @@ -122,61 +135,75 @@ function ShowExamDownload({ }, [os]); return ( - - -

{t('exam.download-header')}

-

{t('exam.explanation')}

- - {isSignedIn && ( - <> -

{t('exam.attempts')}

- - - - )} -

- {t('exam.version', { - version: latestVersion || '...' - })} -

- - {!downloadLink && ( - <> - - {t('exam.unable-to-detect-os')} - - )} - - - {t('exam.download-details')} - - {downloadLinks - .filter(link => !link.match(/\.sig|\.json/)) - .map((link, index) => { - return ( - - {link} - - ); - })} - - - - {t('exam.download-trouble')}{' '} - support@freecodecamp.org -
+ + + + {title ? `${title} | freeCodeCamp.org` : 'freeCodeCamp.org'} + + + + + + {title} + + +

{t('exam.download-header')}

+

{t('exam.explanation')}

+ + {isSignedIn && ( + <> +

{t('exam.attempts')}

+ + + + )} +

+ {t('exam.version', { + version: latestVersion || '...' + })} +

+ + {!downloadLink && ( + <> + + {t('exam.unable-to-detect-os')} + + )} + + + {t('exam.download-details')} + + {downloadLinks + .filter(link => !link.match(/\.sig|\.json/)) + .map((link, index) => { + return ( + + {link} + + ); + })} + + + + {t('exam.download-trouble')}{' '} + support@freecodecamp.org +
+
); } @@ -188,6 +215,8 @@ export const query = graphql` challengeNode(id: { eq: $id }) { challenge { id + title + translationPending } } }