33import { useState , useEffect , useRef } from 'react' ;
44import Link from 'next/link' ;
55import { allQuestions } from '@/lib/questionsData' ;
6- import { saveResponse , deleteResponse } from '@/lib/responseStorage' ;
7- import { saveEvaluation } from '@/lib/evaluationStorage' ;
6+ import { saveResponse , deleteResponse , clearAllResponses } from '@/lib/responseStorage' ;
7+ import { saveEvaluation , getAllEvaluations , SelfEvaluation } from '@/lib/evaluationStorage' ;
88
99interface Question {
1010 id : string ;
@@ -26,14 +26,25 @@ export default function PracticePage() {
2626 const [ questionsInRound , setQuestionsInRound ] = useState ( 0 ) ;
2727 const [ extraTime , setExtraTime ] = useState ( 0 ) ;
2828 const [ showEvaluation , setShowEvaluation ] = useState ( false ) ;
29- const [ evaluation , setEvaluation ] = useState ( {
30- confidence : 3 ,
31- effectiveness : 3 ,
32- knowledge : 3 ,
29+ const [ evaluation , setEvaluation ] = useState < {
30+ confidence : number | null ;
31+ effectiveness : number | null ;
32+ knowledge : number | null ;
33+ } > ( {
34+ confidence : null ,
35+ effectiveness : null ,
36+ knowledge : null ,
3337 } ) ;
38+ const [ previousEvaluations , setPreviousEvaluations ] = useState < SelfEvaluation [ ] > ( [ ] ) ;
39+ const [ showPreviousEvaluations , setShowPreviousEvaluations ] = useState ( false ) ;
3440
3541 const skipQuestionRef = useRef < ( ( ) => void ) | null > ( null ) ;
3642
43+ // Load previous evaluations on mount
44+ useEffect ( ( ) => {
45+ setPreviousEvaluations ( getAllEvaluations ( ) ) ;
46+ } , [ ] ) ;
47+
3748 useEffect ( ( ) => {
3849 let interval : NodeJS . Timeout | null = null ;
3950
@@ -148,7 +159,21 @@ export default function PracticePage() {
148159 } ;
149160
150161 const handleSubmitEvaluation = ( ) => {
151- saveEvaluation ( evaluation ) ;
162+ // Validate that all evaluations are filled
163+ if ( evaluation . confidence === null || evaluation . effectiveness === null || evaluation . knowledge === null ) {
164+ alert ( 'Please rate all three categories before submitting.' ) ;
165+ return ;
166+ }
167+
168+ saveEvaluation ( {
169+ confidence : evaluation . confidence ,
170+ effectiveness : evaluation . effectiveness ,
171+ knowledge : evaluation . knowledge ,
172+ } ) ;
173+
174+ // Reload evaluations to show the new one
175+ setPreviousEvaluations ( getAllEvaluations ( ) ) ;
176+
152177 // Reset for new round
153178 setShowEvaluation ( false ) ;
154179 setQuestionsInRound ( 0 ) ;
@@ -157,6 +182,11 @@ export default function PracticePage() {
157182 setCurrentQuestion ( null ) ;
158183 setTimer ( TIMER_DURATION ) ;
159184 setIsActive ( false ) ;
185+ setEvaluation ( {
186+ confidence : null ,
187+ effectiveness : null ,
188+ knowledge : null ,
189+ } ) ;
160190 } ;
161191
162192 const handleStartNewRound = ( ) => {
@@ -166,6 +196,14 @@ export default function PracticePage() {
166196 getRandomQuestion ( ) ;
167197 } ;
168198
199+ const handleClearAllResponses = ( ) => {
200+ if ( window . confirm ( 'Are you sure you want to delete all your recorded answers? This action cannot be undone.' ) ) {
201+ clearAllResponses ( ) ;
202+ setResponse ( '' ) ;
203+ alert ( 'All responses have been cleared.' ) ;
204+ }
205+ } ;
206+
169207 return (
170208 < div className = "min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800" >
171209 < div className = "container mx-auto px-4 py-8 max-w-4xl" >
@@ -194,7 +232,7 @@ export default function PracticePage() {
194232 < div className = "space-y-6" >
195233 < div >
196234 < label className = "block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2" >
197- Confidence (1-5)
235+ Confidence (1-5) { evaluation . confidence === null && < span className = "text-red-600 dark:text-red-400" > * </ span > }
198236 </ label >
199237 < div className = "flex gap-2 justify-center" >
200238 { [ 1 , 2 , 3 , 4 , 5 ] . map ( ( val ) => (
@@ -215,7 +253,7 @@ export default function PracticePage() {
215253
216254 < div >
217255 < label className = "block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2" >
218- Effectiveness (1-5)
256+ Effectiveness (1-5) { evaluation . effectiveness === null && < span className = "text-red-600 dark:text-red-400" > * </ span > }
219257 </ label >
220258 < div className = "flex gap-2 justify-center" >
221259 { [ 1 , 2 , 3 , 4 , 5 ] . map ( ( val ) => (
@@ -236,7 +274,7 @@ export default function PracticePage() {
236274
237275 < div >
238276 < label className = "block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2" >
239- Knowledge (1-5)
277+ Knowledge (1-5) { evaluation . knowledge === null && < span className = "text-red-600 dark:text-red-400" > * </ span > }
240278 </ label >
241279 < div className = "flex gap-2 justify-center" >
242280 { [ 1 , 2 , 3 , 4 , 5 ] . map ( ( val ) => (
@@ -262,6 +300,11 @@ export default function PracticePage() {
262300 >
263301 Submit Evaluation & Start New Round
264302 </ button >
303+ { ( evaluation . confidence === null || evaluation . effectiveness === null || evaluation . knowledge === null ) && (
304+ < p className = "text-sm text-red-600 dark:text-red-400 text-center" >
305+ * Please rate all three categories before submitting
306+ </ p >
307+ ) }
265308 </ div >
266309 ) : ! currentQuestion ? (
267310 < div className = "text-center space-y-8" >
@@ -271,13 +314,77 @@ export default function PracticePage() {
271314 < p className = "text-md text-slate-500 dark:text-slate-400" >
272315 Finish questions early? Your extra time rolls over to the next question!
273316 </ p >
274- < button
275- onClick = { handleStartNewRound }
276- disabled = { questions . length === 0 }
277- className = "px-8 py-4 bg-green-600 hover:bg-green-700 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 text-lg disabled:opacity-50 disabled:cursor-not-allowed"
278- >
279- Start Practicing
280- </ button >
317+
318+ < div className = "flex flex-col sm:flex-row gap-4 justify-center items-center" >
319+ < button
320+ onClick = { handleStartNewRound }
321+ disabled = { questions . length === 0 }
322+ className = "px-8 py-4 bg-green-600 hover:bg-green-700 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 text-lg disabled:opacity-50 disabled:cursor-not-allowed"
323+ >
324+ Start Practicing
325+ </ button >
326+
327+ < button
328+ onClick = { handleClearAllResponses }
329+ className = "px-6 py-3 bg-red-600 hover:bg-red-700 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl transition-all duration-300"
330+ >
331+ Clear All Responses
332+ </ button >
333+ </ div >
334+
335+ { previousEvaluations . length > 0 && (
336+ < div className = "mt-8" >
337+ < button
338+ onClick = { ( ) => setShowPreviousEvaluations ( ! showPreviousEvaluations ) }
339+ className = "text-blue-600 dark:text-blue-400 hover:underline font-medium"
340+ >
341+ { showPreviousEvaluations ? '▼ Hide' : '▶' } Previous Evaluations ({ previousEvaluations . length } )
342+ </ button >
343+
344+ { showPreviousEvaluations && (
345+ < div className = "mt-4 space-y-4 max-w-3xl mx-auto" >
346+ { previousEvaluations . slice ( ) . reverse ( ) . map ( ( evalItem ) => (
347+ < div
348+ key = { evalItem . timestamp }
349+ className = "bg-white dark:bg-slate-800 rounded-lg shadow-md p-6 text-left"
350+ >
351+ < div className = "flex justify-between items-center mb-4" >
352+ < h3 className = "text-lg font-semibold text-slate-900 dark:text-slate-100" >
353+ Round { evalItem . roundNumber }
354+ </ h3 >
355+ < span className = "text-sm text-slate-500 dark:text-slate-400" >
356+ { new Date ( evalItem . timestamp ) . toLocaleDateString ( ) } { new Date ( evalItem . timestamp ) . toLocaleTimeString ( ) }
357+ </ span >
358+ </ div >
359+ < div className = "grid grid-cols-3 gap-4" >
360+ < div >
361+ < p className = "text-sm text-slate-600 dark:text-slate-400 mb-1" > Confidence</ p >
362+ < div className = "flex items-center" >
363+ < span className = "text-2xl font-bold text-blue-600 dark:text-blue-400" > { evalItem . confidence } </ span >
364+ < span className = "text-sm text-slate-500 dark:text-slate-500 ml-1" > /5</ span >
365+ </ div >
366+ </ div >
367+ < div >
368+ < p className = "text-sm text-slate-600 dark:text-slate-400 mb-1" > Effectiveness</ p >
369+ < div className = "flex items-center" >
370+ < span className = "text-2xl font-bold text-green-600 dark:text-green-400" > { evalItem . effectiveness } </ span >
371+ < span className = "text-sm text-slate-500 dark:text-slate-500 ml-1" > /5</ span >
372+ </ div >
373+ </ div >
374+ < div >
375+ < p className = "text-sm text-slate-600 dark:text-slate-400 mb-1" > Knowledge</ p >
376+ < div className = "flex items-center" >
377+ < span className = "text-2xl font-bold text-purple-600 dark:text-purple-400" > { evalItem . knowledge } </ span >
378+ < span className = "text-sm text-slate-500 dark:text-slate-500 ml-1" > /5</ span >
379+ </ div >
380+ </ div >
381+ </ div >
382+ </ div >
383+ ) ) }
384+ </ div >
385+ ) }
386+ </ div >
387+ ) }
281388 </ div >
282389 ) : (
283390 < div className = "space-y-6" >
0 commit comments