Skip to content

Commit c9e350f

Browse files
authored
Merge pull request #101 from gun9311/dev
Dev
2 parents 17d9757 + 80b2d06 commit c9e350f

17 files changed

+535
-274
lines changed

backend/controllers/quizResultController.js

+3-9
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,11 @@ const getQuizResultsByStudentId = async (req, res) => {
2929
const getQuizDetails = async (req, res) => {
3030
try {
3131
const { quizId } = req.params;
32-
const { studentId } = req.params;
33-
// console.log(quizId, studentId);
34-
35-
32+
const { studentId } = req.params;
3633

3734
// 퀴즈 결과에서 학생의 답변과 정답 여부 가져오기
3835
const quizResult = await QuizResult.findOne({ quizId, studentId })
3936
.select('results');
40-
41-
// console.log(quizResult);
4237

4338
if (!quizResult) {
4439
return res.status(404).json({ error: 'Quiz result not found' });
@@ -48,7 +43,6 @@ const getQuizDetails = async (req, res) => {
4843
const quizContent = await KahootQuizContent.findById(quizId)
4944
.select('questions');
5045

51-
// console.log(quizContent);
5246

5347
if (!quizContent) {
5448
return res.status(404).json({ error: 'Quiz content not found' });
@@ -61,8 +55,8 @@ const getQuizDetails = async (req, res) => {
6155
if (!question || !question.options[result.studentAnswer]) {
6256
return {
6357
questionText: question ? question.questionText : '문제를 찾을 수 없음',
64-
correctAnswer: '정답을 찾을 수 없음',
65-
studentAnswer: '답변을 찾을 수 없음',
58+
correctAnswer: question ? question.options[question.correctAnswer]?.text : '정답을 찾을 수 없음',
59+
studentAnswer: '답변 없음',
6660
isCorrect: result.isCorrect
6761
};
6862
}

frontend/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"@mui/icons-material": "^5.16.6",
1818
"@mui/lab": "^5.0.0-alpha.173",
1919
"@mui/material": "^5.16.6",
20+
"@react-spring/web": "^9.7.5",
2021
"@types/chart.js": "^2.9.41",
2122
"@types/eventsource": "^1.1.15",
2223
"@types/jwt-decode": "^3.1.0",

frontend/src/components/student/quiz/QuizResults.tsx

+97-47
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1-
import React, { useEffect, useState } from 'react';
2-
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Typography, Box, IconButton, Collapse, CircularProgress, useMediaQuery, useTheme } from '@mui/material';
3-
import { Visibility, VisibilityOff } from '@mui/icons-material';
4-
import api from '../../../utils/api';
1+
import React, { useEffect, useState } from "react";
2+
import {
3+
Table,
4+
TableBody,
5+
TableCell,
6+
TableContainer,
7+
TableHead,
8+
TableRow,
9+
Paper,
10+
Typography,
11+
Box,
12+
IconButton,
13+
Collapse,
14+
CircularProgress,
15+
useMediaQuery,
16+
useTheme,
17+
} from "@mui/material";
18+
import { Visibility, VisibilityOff } from "@mui/icons-material";
19+
import api from "../../../utils/api";
520

621
// interface QuizResult {
722
// _id: string;
@@ -55,7 +70,7 @@ const QuizResults: React.FC<QuizResultsProps> = ({
5570
selectedSubject,
5671
handleQuizResultClick,
5772
handleCloseDetails,
58-
isStudentView = false, // 기본값은 false로 설정
73+
isStudentView = false, // 기본값은 false로 설정
5974
}) => {
6075
const [quizResults, setQuizResults] = useState<QuizResult[]>([]);
6176
const [loading, setLoading] = useState<boolean>(false);
@@ -65,27 +80,27 @@ const QuizResults: React.FC<QuizResultsProps> = ({
6580
const [quizDetails, setQuizDetails] = useState<QuizDetail[]>([]);
6681

6782
const theme = useTheme();
68-
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
83+
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
6984

7085
// 학생일 경우 점수를 평가 문구 및 이모티콘으로 변환하는 함수
7186
const getEvaluation = (score: number) => {
7287
if (score >= 75) {
73-
return { text: '훌륭해요', emoji: '🏆' };
88+
return { text: "훌륭해요", emoji: "🏆" };
7489
} else if (score >= 55) {
75-
return { text: '잘했어요', emoji: '👍' };
90+
return { text: "잘했어요", emoji: "👍" };
7691
} else {
77-
return { text: '노력해요', emoji: '💪' };
92+
return { text: "노력해요", emoji: "💪" };
7893
}
7994
};
8095

8196
// 교사일 경우 단원별 총평을 상/중/하로 변환하는 함수
8297
const getTeacherUnitEvaluation = (score: number) => {
8398
if (score >= 75) {
84-
return '상 🟢';
99+
return "상 🟢";
85100
} else if (score >= 55) {
86-
return '중 🟡';
101+
return "중 🟡";
87102
} else {
88-
return '하 🔴';
103+
return "하 🔴";
89104
}
90105
};
91106

@@ -100,8 +115,8 @@ const QuizResults: React.FC<QuizResultsProps> = ({
100115
setQuizResults(response.data);
101116
setNoData(response.data.length === 0);
102117
} catch (err) {
103-
console.error('API 호출 실패:', err);
104-
setError('퀴즈 결과를 가져오는 중 오류가 발생했습니다.');
118+
console.error("API 호출 실패:", err);
119+
setError("퀴즈 결과를 가져오는 중 오류가 발생했습니다.");
105120
setNoData(true);
106121
} finally {
107122
setLoading(false);
@@ -114,12 +129,15 @@ const QuizResults: React.FC<QuizResultsProps> = ({
114129

115130
const results = studentId ? quizResults : filteredResults || [];
116131

117-
const filteredQuizResults = (selectedSemester && selectedSubject)
118-
? results.filter(result =>
119-
(selectedSemester === 'All' || result.semester === selectedSemester) &&
120-
(selectedSubject === 'All' || result.subject === selectedSubject)
121-
)
122-
: results;
132+
const filteredQuizResults =
133+
selectedSemester && selectedSubject
134+
? results.filter(
135+
(result) =>
136+
(selectedSemester === "All" ||
137+
result.semester === selectedSemester) &&
138+
(selectedSubject === "All" || result.subject === selectedSubject)
139+
)
140+
: results;
123141

124142
if (studentId && loading) {
125143
return <CircularProgress />;
@@ -131,15 +149,17 @@ const QuizResults: React.FC<QuizResultsProps> = ({
131149

132150
const fetchQuizDetails = async (quizId: string) => {
133151
try {
134-
const response = await api.get(`/quiz-results/details/${quizId}/${studentId}`);
152+
const response = await api.get(
153+
`/quiz-results/details/${quizId}/${studentId}`
154+
);
135155
setQuizDetails(response.data);
136156
} catch (err) {
137-
console.error('Failed to fetch quiz details:', err);
157+
console.error("Failed to fetch quiz details:", err);
138158
}
139159
};
140160

141161
// const toggleQuizDetails = (quizId: string) => {
142-
// setExpandedQuizId(prev => (prev === quizId ? null : quizId));
162+
// setExpandedQuizId(prev => (prev === quizId ? null : quizId));
143163
// };
144164

145165
const toggleQuizDetails = (quizId: string) => {
@@ -159,7 +179,9 @@ const QuizResults: React.FC<QuizResultsProps> = ({
159179
</Box>
160180
) : filteredQuizResults.length === 0 ? (
161181
<Box textAlign="center" sx={{ mt: 2 }}>
162-
<Typography>선택한 학기 또는 과목에 대한 퀴즈 내역이 없습니다.</Typography>
182+
<Typography>
183+
선택한 학기 또는 과목에 대한 퀴즈 내역이 없습니다.
184+
</Typography>
163185
</Box>
164186
) : (
165187
<TableContainer component={Paper} sx={{ mt: 2 }}>
@@ -170,63 +192,91 @@ const QuizResults: React.FC<QuizResultsProps> = ({
170192
{!isMobile && <TableCell>학기</TableCell>}
171193
<TableCell>과목</TableCell>
172194
{!isMobile && <TableCell>단원</TableCell>}
173-
<TableCell>총평</TableCell>
195+
<TableCell>점수</TableCell>
174196
<TableCell align="center">상세보기</TableCell>
175197
</TableRow>
176198
</TableHead>
177199
<TableBody>
178-
{filteredQuizResults.map(result => (
200+
{filteredQuizResults.map((result) => (
179201
<React.Fragment key={result._id}>
180-
<TableRow sx={{ cursor: 'pointer' }}>
181-
<TableCell>{new Date(result.createdAt).toLocaleDateString()}</TableCell>
202+
<TableRow sx={{ cursor: "pointer" }}>
203+
<TableCell>
204+
{new Date(result.createdAt).toLocaleDateString()}
205+
</TableCell>
182206
{!isMobile && <TableCell>{result.semester}</TableCell>}
183207
<TableCell>{result.subject}</TableCell>
184208
{!isMobile && <TableCell>{result.unit}</TableCell>}
185-
186-
<TableCell>
187-
{isStudentView
188-
? `${getEvaluation(result.score).text} ${getEvaluation(result.score).emoji}`
189-
: getTeacherUnitEvaluation(result.score)}
190-
</TableCell>
209+
210+
<TableCell>{Math.round(result.score)}</TableCell>
191211

192212
<TableCell align="center">
193-
<IconButton
213+
<IconButton
194214
onClick={() => toggleQuizDetails(result.quizId)}
195215
color="primary"
196216
aria-label="view details"
197217
>
198-
{expandedQuizId === result.quizId ? <VisibilityOff /> : <Visibility />}
218+
{expandedQuizId === result.quizId ? (
219+
<VisibilityOff />
220+
) : (
221+
<Visibility />
222+
)}
199223
</IconButton>
200224
</TableCell>
201225
</TableRow>
202226
<TableRow>
203-
<TableCell colSpan={isMobile ? 5 : 6} sx={{ padding: 0, borderBottom: 'none' }}>
204-
<Collapse in={expandedQuizId === result.quizId} timeout="auto" unmountOnExit>
227+
<TableCell
228+
colSpan={isMobile ? 5 : 6}
229+
sx={{ padding: 0, borderBottom: "none" }}
230+
>
231+
<Collapse
232+
in={expandedQuizId === result.quizId}
233+
timeout="auto"
234+
unmountOnExit
235+
>
205236
<Box sx={{ padding: 2 }}>
206237
<Typography variant="h6" gutterBottom>
207238
퀴즈 상세 내용
208239
</Typography>
209-
<Typography variant="body1">과목: {result.subject}</Typography>
210-
<Typography variant="body1">단원: {result.unit}</Typography>
211-
<Typography variant="body1">총평: {isStudentView ? `${getEvaluation(result.score).text} ${getEvaluation(result.score).emoji}` : getTeacherUnitEvaluation(result.score)}</Typography>
212-
240+
<Typography variant="body1">
241+
과목: {result.subject}
242+
</Typography>
243+
<Typography variant="body1">
244+
단원: {result.unit}
245+
</Typography>
246+
<Typography variant="body1">
247+
점수: {Math.round(result.score)}
248+
</Typography>
249+
213250
<TableContainer component={Paper} sx={{ mt: 2 }}>
214251
<Table>
215252
<TableHead>
216253
<TableRow>
217254
<TableCell>문제</TableCell>
218255
<TableCell>정답</TableCell>
219256
<TableCell>내 답변</TableCell>
220-
{!isStudentView && <TableCell>정답 여부</TableCell>}
257+
{!isStudentView && (
258+
<TableCell>정답 여부</TableCell>
259+
)}
221260
</TableRow>
222261
</TableHead>
223262
<TableBody>
224263
{quizDetails.map((detail, index) => (
225264
<TableRow key={index}>
226-
<TableCell>{detail.questionText || '문제를 찾을 수 없음'}</TableCell>
227-
<TableCell>{detail.correctAnswer}</TableCell>
228-
<TableCell>{detail.studentAnswer}</TableCell>
229-
{!isStudentView && <TableCell>{detail.isCorrect ? '정답' : '오답'}</TableCell>}
265+
<TableCell>
266+
{detail.questionText ||
267+
"문제를 찾을 수 없음"}
268+
</TableCell>
269+
<TableCell>
270+
{detail.correctAnswer}
271+
</TableCell>
272+
<TableCell>
273+
{detail.studentAnswer}
274+
</TableCell>
275+
{!isStudentView && (
276+
<TableCell>
277+
{detail.isCorrect ? "정답" : "오답"}
278+
</TableCell>
279+
)}
230280
</TableRow>
231281
))}
232282
</TableBody>

frontend/src/components/student/quiz/StudentQuizSession.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ const StudentQuizSessionPage: React.FC = () => {
355355
sx={{
356356
padding: 2,
357357
borderRadius: "16px",
358-
backgroundColor: "rgba(255, 255, 255, 0.3)",
358+
backgroundColor: "rgba(255, 255, 255, 0.5)",
359359
}}
360360
>
361361
{!isCharacterConfirmed ? (
@@ -455,13 +455,15 @@ const StudentQuizSessionPage: React.FC = () => {
455455
selectedAnswer={selectedAnswer}
456456
handleAnswerSelect={handleAnswerSelect}
457457
timeLeft={timeLeft}
458+
isAnswerSubmitted={isAnswerSubmitted}
458459
/>
459460
)}
460461

461462
{isFeedbackReceived && (
462463
<QuizFeedbackComponent
463464
feedbackMessage={feedbackMessage}
464465
isLastQuestion={isLastQuestion}
466+
score={score}
465467
/>
466468
)}
467469

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@keyframes pulse {
2+
0% {
3+
transform: scale(1);
4+
}
5+
50% {
6+
transform: scale(1.05);
7+
}
8+
100% {
9+
transform: scale(1);
10+
}
11+
}

0 commit comments

Comments
 (0)