Skip to content

Commit 38d55b5

Browse files
committed
Refactoring
1 parent 6fec683 commit 38d55b5

File tree

1 file changed

+64
-52
lines changed

1 file changed

+64
-52
lines changed

src/components/QuizQuestion.tsx

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Box, Button, Typography } from '@mui/material';
1+
import { Box, Button, Typography, useTheme, Theme } from '@mui/material';
22
import EditIcon from '@mui/icons-material/Edit';
33
import shuffle from 'lodash/shuffle';
44
import { 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+
1950
interface QuizQuestionProps {
2051
questionData: QuestionData;
2152
questionId: string;
2253
onNextQuestion: () => void;
2354
}
2455

2556
export 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

Comments
 (0)