1- import { Box , Button , Typography } from '@mui/material' ;
1+ import { Box , Button , Typography , useTheme , Theme } from '@mui/material' ;
22import EditIcon from '@mui/icons-material/Edit' ;
33import shuffle from 'lodash/shuffle' ;
44import { useMemo , useState } from 'react' ;
@@ -10,31 +10,63 @@ const shuffleAnswers = (answers: string[], correctIndex: number): [string[], num
1010 const indexed = answers . map ( ( answer , index ) => ( { answer, index } ) ) ;
1111 const shuffled = shuffle ( indexed ) ;
1212
13- const shuffledAnswers = shuffled . map ( ( item : { answer : string ; index : number } ) => item . answer ) ;
14- const newCorrectIndex = shuffled . findIndex ( ( item : { answer : string ; index : number } ) => item . index === correctIndex ) ;
13+ const shuffledAnswers = shuffled . map ( ( item ) => item . answer ) ;
14+ const newCorrectIndex = shuffled . findIndex ( ( item ) => item . index === correctIndex ) ;
1515
1616 return [ shuffledAnswers , newCorrectIndex ] ;
1717} ;
1818
19+ const getStyles = ( theme : Theme ) => ( {
20+ answerBox : ( isAnswered : boolean , isSelected : boolean , isCorrect : boolean ) => ( {
21+ display : 'flex' ,
22+ alignItems : 'center' ,
23+ gap : 2 ,
24+ marginBottom : 2 ,
25+ padding : '0 10px' ,
26+ border : isSelected
27+ ? isCorrect
28+ ? `2px solid ${ theme . palette . success . main } `
29+ : `2px solid ${ theme . palette . error . main } `
30+ : '2px solid transparent' ,
31+ cursor : isAnswered ? 'default' : 'pointer' ,
32+ ...( ! isAnswered && {
33+ '&:hover' : {
34+ backgroundColor : 'action.hover' ,
35+ } ,
36+ } ) ,
37+ } ) ,
38+ editLink : {
39+ color : 'primary.main' ,
40+ textDecoration : 'none' ,
41+ display : 'inline-flex' ,
42+ alignItems : 'center' ,
43+ gap : 0.5 ,
44+ '&:hover' : {
45+ textDecoration : 'underline' ,
46+ } ,
47+ } ,
48+ } ) ;
49+
1950interface QuizQuestionProps {
2051 questionData : QuestionData ;
2152 questionId : string ;
2253 onNextQuestion : ( ) => void ;
2354}
2455
2556export function QuizQuestion ( { questionData, questionId, onNextQuestion } : QuizQuestionProps ) {
57+ const styles = getStyles ( useTheme ( ) ) ;
2658 const labels = [ 'A' , 'B' , 'C' , 'D' ] ;
2759 const [ selectedAnswer , setSelectedAnswer ] = useState < number | null > ( null ) ;
2860 const recordAnswer = useQuizStore ( ( state ) => state . recordAnswer ) ;
2961
30- const [ shuffledAnswers , correctAnswerIndex ] = useMemo (
62+ const [ shuffledAnswers , correctAnswer ] = useMemo (
3163 ( ) => shuffleAnswers ( questionData . answers , questionData . correct_answer ) ,
3264 [ questionData ] ,
3365 ) ;
3466
3567 const handleAnswerClick = ( index : number ) => {
68+ const isCorrect = index === correctAnswer ;
3669 setSelectedAnswer ( index ) ;
37- const isCorrect = index === correctAnswerIndex ;
3870 recordAnswer ( questionId , isCorrect ) ;
3971 } ;
4072
@@ -43,57 +75,46 @@ export function QuizQuestion({ questionData, questionId, onNextQuestion }: QuizQ
4375 onNextQuestion ( ) ;
4476 } ;
4577
78+ const isAnswered = selectedAnswer !== null ;
79+
4680 return (
4781 < >
4882 < MarkdownBlock > { questionData . question } </ MarkdownBlock >
4983
5084 < Box sx = { { marginTop : 3 } } >
51- { shuffledAnswers . map ( ( answer , index ) => (
52- < Box
53- key = { index }
54- onClick = { ( ) => selectedAnswer === null && handleAnswerClick ( index ) }
55- sx = { {
56- display : 'flex' ,
57- alignItems : 'center' ,
58- gap : 2 ,
59- marginBottom : 2 ,
60- padding : '0 10px' ,
61- border :
62- selectedAnswer === index
63- ? index === correctAnswerIndex
64- ? '2px solid green'
65- : '2px solid red'
66- : '2px solid transparent' ,
67- cursor : selectedAnswer === null ? 'pointer' : 'default' ,
68- '&:hover' : selectedAnswer === null
69- ? {
70- backgroundColor : 'action.hover' ,
71- }
72- : { } ,
73- } }
74- >
75- < Button
76- variant = "outlined"
77- sx = { { minWidth : 50 } }
78- onClick = { ( ) => handleAnswerClick ( index ) }
79- disabled = { selectedAnswer !== null }
85+ { shuffledAnswers . map ( ( answer , index ) => {
86+ const isSelected = selectedAnswer === index ;
87+ const isCorrect = correctAnswer === index ;
88+
89+ return (
90+ < Box
91+ key = { index }
92+ onClick = { ( ) => ! isAnswered && handleAnswerClick ( index ) }
93+ sx = { styles . answerBox ( isAnswered , isSelected , isCorrect ) }
8094 >
81- { labels [ index ] }
82- </ Button >
83- < MarkdownBlock > { answer } </ MarkdownBlock >
84- </ Box >
85- ) ) }
95+ < Button
96+ variant = "outlined"
97+ sx = { { minWidth : 50 } }
98+ onClick = { ( ) => handleAnswerClick ( index ) }
99+ disabled = { isAnswered }
100+ >
101+ { labels [ index ] }
102+ </ Button >
103+ < MarkdownBlock > { answer } </ MarkdownBlock >
104+ </ Box >
105+ ) ;
106+ } ) }
86107 </ Box >
87108
88- { selectedAnswer !== null && (
109+ { isAnswered && (
89110 < Box sx = { { marginTop : 3 } } >
90- { selectedAnswer === correctAnswerIndex ? (
111+ { selectedAnswer === correctAnswer ? (
91112 < Typography variant = "h6" color = "success.main" >
92113 🎉 Correct! Well done!
93114 </ Typography >
94115 ) : (
95116 < Typography variant = "h6" color = "error.main" >
96- ❌ Incorrect. The correct answer is { labels [ correctAnswerIndex ] } .
117+ ❌ Incorrect. The correct answer is { labels [ correctAnswer ] } .
97118 </ Typography >
98119 ) }
99120
@@ -106,16 +127,7 @@ export function QuizQuestion({ questionData, questionId, onNextQuestion }: QuizQ
106127 target = "_blank"
107128 rel = "noopener noreferrer"
108129 variant = "body2"
109- sx = { {
110- color : 'primary.main' ,
111- textDecoration : 'none' ,
112- display : 'inline-flex' ,
113- alignItems : 'center' ,
114- gap : 0.5 ,
115- '&:hover' : {
116- textDecoration : 'underline' ,
117- } ,
118- } }
130+ sx = { styles . editLink }
119131 >
120132 < EditIcon fontSize = "small" />
121133 Edit on GitHub
0 commit comments