@@ -27,11 +27,12 @@ const questionsQuery = gql`
2727 }
2828` ;
2929
30- const Exam : NextPage < { searchParams : { url : string ; name : string } } > = ( {
31- searchParams,
32- } ) => {
30+ const Exam : NextPage = ( ) => {
3331 const { isAccessBlocked, isInTrial } = useTrialAccess ( ) ;
34- const { url } = searchParams ;
32+ const [ searchParams , setSearchParams ] = useState < URLSearchParams | null > (
33+ null ,
34+ ) ;
35+ const url = searchParams ?. get ( "url" ) || "" ;
3536 const { minutes, seconds } = {
3637 minutes : 15 ,
3738 seconds : 0 ,
@@ -45,16 +46,23 @@ const Exam: NextPage<{ searchParams: { url: string; name: string } }> = ({
4546 const [ countAnswered , setCountAnswered ] = useState < number > ( 0 ) ;
4647 const { data, loading, error } = useQuery ( questionsQuery , {
4748 variables : { range : 30 , link : url } ,
49+ skip : ! url , // Skip query if URL is not available
4850 } ) ;
4951 const [ resultPoints , setResultPoints ] = useState < number > ( 0 ) ;
5052 const [ passed , setPassed ] = useState < boolean > ( false ) ;
5153 const [ windowWidth , setWindowWidth ] = useState < number > ( 0 ) ;
52- const editedUrl = url . substring ( 0 , url . lastIndexOf ( "/" ) + 1 ) ;
54+ const editedUrl =
55+ url && url . includes ( "/" ) ? url . substring ( 0 , url . lastIndexOf ( "/" ) + 1 ) : "" ;
5356 const elapsedSeconds =
5457 totalTimeInSeconds -
5558 ( parseInt ( remainingTime . split ( ":" ) [ 0 ] ) * 60 +
5659 parseInt ( remainingTime . split ( ":" ) [ 1 ] ) ) ;
5760
61+ useEffect ( ( ) => {
62+ const param = new URLSearchParams ( window . location . search ) ;
63+ setSearchParams ( param ) ;
64+ } , [ ] ) ;
65+
5866 const handleCountAnswered = ( ) => {
5967 setCountAnswered ( countAnswered + 1 ) ;
6068 } ;
@@ -89,35 +97,97 @@ const Exam: NextPage<{ searchParams: { url: string; name: string } }> = ({
8997 setCurrentQuestion ( data ?. randomQuestions [ 0 ] ) ;
9098 } , [ data ] ) ;
9199
92- // Show loading while checking trial access
93- if ( isAccessBlocked === undefined ) {
100+ // Show loading while checking trial access or waiting for URL
101+ if ( isAccessBlocked === undefined || ! searchParams ) {
94102 return < LoadingIndicator /> ;
95103 }
96104
105+ // Check if URL is missing
106+ if ( ! url ) {
107+ return (
108+ < div className = "min-h-[calc(100vh-8rem)] flex items-center justify-center px-4" >
109+ < div className = "py-10 px-5 mb-6 mx-auto w-[90vw] lg:w-[60vw] 2xl:w-[45%] bg-white dark:bg-gray-800 border-2 border-gray-200 dark:border-gray-700 rounded-lg text-center" >
110+ < div className = "text-red-500 dark:text-red-400 text-lg mb-4" >
111+ ⚠️ Exam URL is missing. Please select an exam from the home page.
112+ </ div >
113+ < button
114+ onClick = { ( ) => ( window . location . href = "/" ) }
115+ className = "btn-primary text-white px-6 py-2 rounded-lg"
116+ >
117+ Go to Home
118+ </ button >
119+ </ div >
120+ </ div >
121+ ) ;
122+ }
123+
97124 // Block access if trial expired
98125 if ( isAccessBlocked ) {
99126 return (
100- < div className = "py-10 px-5 mb-6 mx-auto w-[90vw] lg:w-[60vw] 2xl:w-[45%] bg-slate-800 border-2 border-slate-700 rounded-lg text-center" >
101- < div className = "text-red-400 text-lg mb-4" >
102- ⏰ Trial expired. Please sign in to continue taking exams.
127+ < div className = "min-h-[calc(100vh-8rem)] flex items-center justify-center px-4" >
128+ < div className = "py-10 px-5 mb-6 mx-auto w-[90vw] lg:w-[60vw] 2xl:w-[45%] bg-white dark:bg-gray-800 border-2 border-gray-200 dark:border-gray-700 rounded-lg text-center" >
129+ < div className = "text-red-500 dark:text-red-400 text-lg mb-4" >
130+ ⏰ Trial expired. Please sign in to continue taking exams.
131+ </ div >
132+ < button
133+ onClick = { ( ) => ( window . location . href = "/" ) }
134+ className = "btn-primary text-white px-6 py-2 rounded-lg"
135+ >
136+ Go to Home
137+ </ button >
103138 </ div >
104- < button
105- onClick = { ( ) => ( window . location . href = "/" ) }
106- className = "btn-primary text-white px-6 py-2 rounded-lg"
107- >
108- Go to Home
109- </ button >
110139 </ div >
111140 ) ;
112141 }
113142
114143 if ( loading ) return < LoadingIndicator /> ;
115- if ( error ) return < p > Oh no... { error . message } </ p > ;
144+ if ( error ) {
145+ return (
146+ < div className = "min-h-[calc(100vh-8rem)] flex items-center justify-center px-4" >
147+ < div className = "py-10 px-5 mb-6 mx-auto w-[90vw] lg:w-[60vw] 2xl:w-[45%] bg-white dark:bg-gray-800 border-2 border-red-200 dark:border-red-700 rounded-lg text-center" >
148+ < div className = "text-red-500 dark:text-red-400 text-lg mb-4" >
149+ ⚠️ Error loading exam questions
150+ </ div >
151+ < p className = "text-gray-700 dark:text-gray-300 mb-4" >
152+ { error . message }
153+ </ p >
154+ < button
155+ onClick = { ( ) => ( window . location . href = "/" ) }
156+ className = "btn-primary text-white px-6 py-2 rounded-lg"
157+ >
158+ Go to Home
159+ </ button >
160+ </ div >
161+ </ div >
162+ ) ;
163+ }
164+
165+ if ( ! data ?. randomQuestions || data . randomQuestions . length === 0 ) {
166+ return (
167+ < div className = "min-h-[calc(100vh-8rem)] flex items-center justify-center px-4" >
168+ < div className = "py-10 px-5 mb-6 mx-auto w-[90vw] lg:w-[60vw] 2xl:w-[45%] bg-white dark:bg-gray-800 border-2 border-yellow-200 dark:border-yellow-700 rounded-lg text-center" >
169+ < div className = "text-yellow-600 dark:text-yellow-400 text-lg mb-4" >
170+ ⚠️ No questions found for this exam
171+ </ div >
172+ < p className = "text-gray-700 dark:text-gray-300 mb-4" >
173+ The exam questions could not be loaded. Please try again later or
174+ select a different exam.
175+ </ p >
176+ < button
177+ onClick = { ( ) => ( window . location . href = "/" ) }
178+ className = "btn-primary text-white px-6 py-2 rounded-lg"
179+ >
180+ Go to Home
181+ </ button >
182+ </ div >
183+ </ div >
184+ ) ;
185+ }
116186
117187 const numberOfQuestions = data . randomQuestions . length || 0 ;
118188
119189 return (
120- < div className = "py-10 px-5 mb-6 mx-auto w-[90vw] lg:w-[60vw] 2xl:w-[45%] bg-slate- 800 border-2 border-slate- 700 rounded-lg" >
190+ < div className = "py-10 px-5 mb-6 mx-auto w-[90vw] lg:w-[60vw] 2xl:w-[45%] bg-white dark:bg-gray- 800 border-2 border-gray-200 dark:border-gray- 700 rounded-lg mt-8 " >
121191 { isInTrial && (
122192 < div className = "mb-6 p-4 bg-amber-600/20 border border-amber-600/40 rounded-lg" >
123193 < div className = "flex items-center gap-2 text-amber-300" >
@@ -142,13 +212,13 @@ const Exam: NextPage<{ searchParams: { url: string; name: string } }> = ({
142212 ) }
143213 < div >
144214 < div className = "px-2 sm:px-10 w-full flex flex-row justify-between items-center" >
145- < p className = "text-white font-bold text-sm sm:text-2xl" >
215+ < p className = "text-gray-900 dark:text- white font-bold text-sm sm:text-2xl" >
146216 ✅ { countAnswered } /{ numberOfQuestions }
147217 </ p >
148- < h1 className = "text-white font-bold text-lg sm:text-3xl" >
218+ < h1 className = "text-gray-900 dark:text- white font-bold text-lg sm:text-3xl" >
149219 PRACTICE EXAM
150220 </ h1 >
151- < p className = "text-white font-bold text-sm sm:text-2xl" >
221+ < p className = "text-gray-900 dark:text- white font-bold text-sm sm:text-2xl" >
152222 { remainingTime }
153223 </ p >
154224 </ div >
@@ -202,7 +272,7 @@ const Exam: NextPage<{ searchParams: { url: string; name: string } }> = ({
202272 c0.53,0,0.97,0.43,0.97,0.97V29.35z"
203273 />
204274 </ svg >
205- < p className = "text-white text-center pt-6 px-6" >
275+ < p className = "text-gray-900 dark:text- white text-center pt-6 px-6" >
206276 Practice Exam help you practice skills, assess your knowledge,
207277 and identify the areas where you need additional preparation
208278 to accelerate your chances of succeeding on certification
@@ -211,7 +281,7 @@ const Exam: NextPage<{ searchParams: { url: string; name: string } }> = ({
211281 are likely to experience on Azure Fundamentals real exam.
212282 </ p >
213283 </ div >
214- < p className = "text-white font-bold text-xl text-center pt-20 px-6 mb-40" >
284+ < p className = "text-gray-900 dark:text- white font-bold text-xl text-center pt-20 px-6 mb-40" >
215285 This Practice Exam contains { numberOfQuestions } random questions
216286 (seen in upper left corner) and has a completion time limit of{ " " }
217287 { remainingTime . split ( ":" ) [ 0 ] } minutes (seen in upper right
@@ -221,7 +291,7 @@ const Exam: NextPage<{ searchParams: { url: string; name: string } }> = ({
221291 < div className = "flex flex-col sm:flex-row justify-center" >
222292 < Button
223293 type = "button"
224- intent = "secondary "
294+ intent = "primary "
225295 size = "medium"
226296 onClick = { ( ) => startTimer ( ) }
227297 >
0 commit comments