@@ -7,28 +7,45 @@ import {
77 Checkbox ,
88 Code ,
99 Progress ,
10+ Spinner ,
1011} from "@chakra-ui/react" ;
1112import { createRoute , useNavigate } from "@tanstack/react-router" ;
1213import { Button , Spacer } from "@freecodecamp/ui" ;
1314import { useEffect , useState } from "react" ;
14- import { useMutation } from "@tanstack/react-query" ;
15+ import { useMutation , useQuery } from "@tanstack/react-query" ;
1516
1617import { ProtectedRoute } from "../components/protected-route" ;
1718import { Header } from "../components/header" ;
1819import { rootRoute } from "./root" ;
1920import { ExamRoute } from "./exam" ;
20- import { checkForUpdate } from "../utils/fetch" ;
21+ import { checkForUpdate , getExams } from "../utils/fetch" ;
2122import { restartApp } from "../utils/commands" ;
2223import { captureException } from "@sentry/react" ;
23- import { getErrorMessage } from "../utils/errors" ;
24+ import { captureAndNavigate , getErrorMessage } from "../utils/errors" ;
25+ import { PrismFormatted } from "../components/prism-formatted" ;
26+ import { parseMarkdown } from "../utils/markdown" ;
2427
2528export function ExamLanding ( ) {
2629 const [ hasAgreed , setHasAgreed ] = useState ( false ) ;
2730 const [ progress , setProgress ] = useState ( 0 ) ;
2831 const navigate = useNavigate ( ) ;
2932
3033 const { examId } = ExamLandingRoute . useParams ( ) ;
31- const { note } = ExamLandingRoute . useSearch ( ) ;
34+
35+ const noteQuery = useQuery ( {
36+ queryKey : [ "exams" ] ,
37+ queryFn : getExams ,
38+ retry : false ,
39+ refetchOnWindowFocus : false ,
40+ select : ( data ) => {
41+ const exam = data . find ( ( e ) => e . id === examId ) ;
42+ if ( ! exam ) {
43+ throw new Error ( `Invalid exam id ${ examId } .` ) ;
44+ }
45+
46+ return exam . config . note ;
47+ } ,
48+ } ) ;
3249
3350 const updateMutation = useMutation ( {
3451 mutationKey : [ "checkForUpdate" ] ,
@@ -94,6 +111,10 @@ export function ExamLanding() {
94111 }
95112 } , [ ] ) ;
96113
114+ if ( noteQuery . isError ) {
115+ captureAndNavigate ( noteQuery . error . message , navigate ) ;
116+ }
117+
97118 return (
98119 < >
99120 < Header />
@@ -121,13 +142,20 @@ export function ExamLanding() {
121142 < Text >
122143 If you run out of time, your attempt will be auto-submitted.
123144 </ Text >
124- { ! ! note && (
125- < >
126- < Heading as = "h2" size = { "md" } >
127- Exam Note
128- </ Heading >
129- < Text > { note } </ Text >
130- </ >
145+ { noteQuery . isFetching ? (
146+ < Spinner />
147+ ) : (
148+ ! ! noteQuery . data && (
149+ < >
150+ < Heading as = "h2" size = { "md" } >
151+ Exam Note
152+ </ Heading >
153+ < PrismFormatted
154+ text = { parseMarkdown ( noteQuery . data ) }
155+ getCodeBlockAriaLabel = { ( c ) => `${ c } code` }
156+ />
157+ </ >
158+ )
131159 ) }
132160 < Checkbox
133161 id = "terms-agreement"
@@ -141,6 +169,8 @@ export function ExamLanding() {
141169 variant = "primary"
142170 disabled = {
143171 ! hasAgreed ||
172+ noteQuery . isFetching ||
173+ noteQuery . isError ||
144174 updateMutation . isPending ||
145175 updateMutation . isError ||
146176 startExamMutation . isPending ||
0 commit comments