Skip to content

Commit 84f9d65

Browse files
authored
functionality to skip quizzes & be able to submit without responses (#16)
* add a feature to submit quiz automatically if no responses are made * add a skip button at top right of quiz card * trigger the CI
1 parent 578b692 commit 84f9d65

File tree

1 file changed

+104
-49
lines changed

1 file changed

+104
-49
lines changed

frontend/src/components/QuizPage.js

Lines changed: 104 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import {
1212
LinearProgress,
1313
Grid2 as Grid,
1414
Alert,
15+
IconButton,
1516
} from '@mui/material';
17+
18+
import CloseIcon from '@mui/icons-material/Close';
1619
import MainLayout from './QuizLayout';
1720
import Mascot1 from '../Mascot1.svg';
1821
import Mascot2 from '../Mascot2.svg';
@@ -29,6 +32,7 @@ function QuizPage() {
2932
const [isSubmitting, setIsSubmitting] = useState(false);
3033
const timerRef = useRef(null);
3134
const questionStartTimeRef = useRef(null);
35+
const hasSubmittedRef = useRef(false);
3236

3337
useEffect(() => {
3438
if (!quizID || !username) {
@@ -52,41 +56,77 @@ function QuizPage() {
5256
});
5357
}, [quizID, username, navigate]);
5458

55-
const handleSubmit = useCallback(() => {
56-
setIsSubmitting(true);
57-
const submissionData = {
58-
Username: username,
59-
QuizID: quizID,
60-
Answers: answers,
61-
};
62-
if (email) {
63-
submissionData.Email = email;
64-
}
59+
const handleSubmit = useCallback(
60+
(timerExceeded = false) => {
61+
if (hasSubmittedRef.current) return;
62+
hasSubmittedRef.current = true;
6563

66-
fetch(`${process.env.REACT_APP_API_ENDPOINT}/submitquiz`, {
67-
method: 'POST',
68-
headers: { 'Content-Type': 'application/json' },
69-
body: JSON.stringify(submissionData),
70-
})
71-
.then((res) => res.json())
72-
.then((data) => {
73-
navigate('/result', {
74-
state: { submissionID: data.SubmissionID, quizID },
75-
});
64+
setIsSubmitting(true);
65+
const submissionData = {
66+
Username: username,
67+
QuizID: quizID,
68+
Answers: answers,
69+
};
70+
if (email) {
71+
submissionData.Email = email;
72+
}
73+
if (timerExceeded) {
74+
submissionData.TimerExceeded = true;
75+
}
76+
77+
fetch(`${process.env.REACT_APP_API_ENDPOINT}/submitquiz`, {
78+
method: 'POST',
79+
headers: { 'Content-Type': 'application/json' },
80+
body: JSON.stringify(submissionData),
7681
})
77-
.catch((err) => {
78-
console.error('Error submitting quiz:', err);
79-
alert('Failed to submit quiz. Please try again.');
80-
setIsSubmitting(false);
81-
});
82-
}, [username, quizID, answers, email, navigate]);
82+
.then((res) => res.json())
83+
.then((data) => {
84+
navigate('/result', {
85+
state: { submissionID: data.SubmissionID, quizID },
86+
});
87+
})
88+
.catch((err) => {
89+
console.error('Error submitting quiz:', err);
90+
alert('Failed to submit quiz. Please try again.');
91+
setIsSubmitting(false);
92+
hasSubmittedRef.current = false;
93+
});
94+
},
95+
[username, quizID, answers, email, navigate]
96+
);
8397

8498
const moveToNextQuestion = useCallback(() => {
8599
if (quizData && currentQuestionIndex < quizData.Questions.length - 1) {
86100
setCurrentQuestionIndex((prevIndex) => prevIndex + 1);
87101
}
88102
}, [quizData, currentQuestionIndex]);
89103

104+
const handleSkip = () => {
105+
const timeTaken =
106+
quizData && quizData.EnableTimer ? quizData.TimerSeconds - timeLeft : 0;
107+
108+
setAnswers((prevAnswers) => ({
109+
...prevAnswers,
110+
[currentQuestionIndex]: {
111+
Answer: '',
112+
TimeTaken: timeTaken,
113+
Skipped: true,
114+
},
115+
}));
116+
117+
if (quizData && quizData.EnableTimer && timerRef.current) {
118+
clearInterval(timerRef.current);
119+
}
120+
121+
if (currentQuestionIndex < quizData.Questions.length - 1) {
122+
moveToNextQuestion();
123+
} else {
124+
if (!hasSubmittedRef.current) {
125+
handleSubmit();
126+
}
127+
}
128+
};
129+
90130
useEffect(() => {
91131
if (quizData && quizData.EnableTimer) {
92132
if (timerRef.current) {
@@ -109,6 +149,9 @@ function QuizPage() {
109149
if (currentQuestionIndex < quizData.Questions.length - 1) {
110150
moveToNextQuestion();
111151
} else {
152+
if (!hasSubmittedRef.current) {
153+
handleSubmit(true);
154+
}
112155
}
113156
};
114157

@@ -125,7 +168,7 @@ function QuizPage() {
125168

126169
return () => clearInterval(timerRef.current);
127170
}
128-
}, [currentQuestionIndex, quizData, moveToNextQuestion]);
171+
}, [currentQuestionIndex, quizData, moveToNextQuestion, handleSubmit]);
129172

130173
const handleOptionChange = (e) => {
131174
const selectedOption = e.target.value;
@@ -140,11 +183,16 @@ function QuizPage() {
140183
},
141184
}));
142185

186+
if (quizData && quizData.EnableTimer && timerRef.current) {
187+
clearInterval(timerRef.current);
188+
}
189+
143190
if (currentQuestionIndex < quizData.Questions.length - 1) {
144-
if (quizData && quizData.EnableTimer && timerRef.current) {
145-
clearInterval(timerRef.current);
146-
}
147191
moveToNextQuestion();
192+
} else {
193+
if (!hasSubmittedRef.current) {
194+
handleSubmit();
195+
}
148196
}
149197
};
150198

@@ -168,7 +216,19 @@ function QuizPage() {
168216

169217
return (
170218
<MainLayout>
171-
<Container maxWidth="sm" className="main-quiz-container">
219+
<Container maxWidth="sm" className="main-quiz-container" sx={{ position: 'relative' }}>
220+
{/* Skip Button at Top Right */}
221+
{!isSubmitting && (
222+
<IconButton
223+
onClick={handleSkip}
224+
disabled={isSubmitting || isTimeUp}
225+
sx={{ position: 'absolute', top: 8, right: 8 }}
226+
aria-label="Skip"
227+
>
228+
<CloseIcon />
229+
</IconButton>
230+
)}
231+
172232
<Typography variant="h6" gutterBottom align="left" width={'100%'}>
173233
Question {currentQuestionIndex + 1} / {quizData.Questions.length}
174234
</Typography>
@@ -227,32 +287,27 @@ function QuizPage() {
227287
))}
228288
</RadioGroup>
229289

230-
{isSubmitting && (
290+
{!isSubmitting && currentQuestionIndex === quizData.Questions.length - 1 && (
231291
<Box sx={{ textAlign: 'center', marginTop: 4 }}>
232-
<CircularProgress />
233-
<Typography variant="h6" sx={{ marginTop: 2 }}>
234-
Submitting your answers...
235-
</Typography>
236-
</Box>
237-
)}
238-
239-
{!isSubmitting &&
240-
quizData &&
241-
currentQuestionIndex === quizData.Questions.length - 1 && (
242292
<Button
243293
variant="contained"
244294
color="primary"
245295
onClick={handleSubmit}
246-
disabled={
247-
Object.keys(answers).length !== quizData.Questions.length ||
248-
isSubmitting ||
249-
isTimeUp
250-
}
251-
sx={{ marginTop: 4 }}
296+
disabled={isSubmitting || isTimeUp}
252297
>
253298
Submit Quiz
254299
</Button>
255-
)}
300+
</Box>
301+
)}
302+
303+
{isSubmitting && (
304+
<Box sx={{ textAlign: 'center', marginTop: 4 }}>
305+
<CircularProgress />
306+
<Typography variant="h6" sx={{ marginTop: 2 }}>
307+
Submitting your answers...
308+
</Typography>
309+
</Box>
310+
)}
256311
</Container>
257312
<div className="character-container">
258313
{currentQuestionIndex % 3 === 0 && (

0 commit comments

Comments
 (0)