Skip to content

Commit

Permalink
Merge pull request #25 from tkhv/quizGrading
Browse files Browse the repository at this point in the history
Quiz grading
  • Loading branch information
tkhv authored Dec 6, 2023
2 parents e3acfb7 + af6fb0a commit 94dd17a
Show file tree
Hide file tree
Showing 9 changed files with 451 additions and 135 deletions.
14 changes: 10 additions & 4 deletions components/EditQuizDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import { QuizQuestion, Quiz } from "@/lib/types";
import { Button } from "@/components/ui/button";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
DialogContent,
Expand All @@ -12,7 +12,7 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { useState } from "react";
import { useEffect, useState } from "react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Textarea } from "@/components/ui/textarea";

Expand All @@ -25,8 +25,14 @@ export function EditQuizDialog({
}) {
const [questions, setQuestions] = useState<QuizQuestion[]>(propQuestions);
const [quizName, setQuizName] = useState(propQuizName);
const { data: session } = useSession();
const courseName = session?.user.courseList[0];
const [isQuizNew, setIsQuizNew] = useState(true);
const router = useRouter();
const { courseName } = router.query;

useEffect(() => {
setQuestions(propQuestions);
setIsQuizNew(false);
}, [propQuestions]);

const handleSubmit = async () => {
const quiz: Quiz = {
Expand Down
187 changes: 187 additions & 0 deletions components/GradingDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"use client";
import { QuizQuestion, Quiz, QuizSubmission } from "@/lib/types";
import { Button } from "@/components/ui/button";
import { useRouter } from "next/router";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogClose,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { useEffect, useState } from "react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Textarea } from "@/components/ui/textarea";

export function GradingDialog({
propQuestions,
studentEmail,
}: {
propQuestions: QuizQuestion[];
studentEmail: string;
}) {
// const [quizName, setQuizName] = useState(propQuizName);
// const [quizSubmission, setQuizSubmission] = useState(propQuiz.submissions);

//each question's score that student's got
const [scores, setScores] = useState<number[]>(
new Array(propQuestions.length).fill(0)
);

const [quizSubmission, setQuizSubmission] = useState<
Record<string, QuizSubmission>
>({
"[email protected]": { answers: ["1", "3", "I have no idea"], score: undefined },
});

const router = useRouter();
const courseName = Array.isArray(router.query.courseName)
? router.query.courseName[0]
: router.query.courseName || "";
const quizName = Array.isArray(router.query.quizName)
? router.query.quizName[0]
: router.query.quizName || "";

const handleSubmit = async () => {
const totalScore = scores.reduce((acc, score) => acc + score, 0);

// Update the student's submission with the total score
setQuizSubmission((current) => ({
...current,
[studentEmail]: {
...current[studentEmail],
score: totalScore,
},
}));

const quiz: Quiz = {
name: quizName,
questions: propQuestions,
totalPoints: propQuestions.reduce((acc, q) => acc + q.points, 0),
submissions: {
...quizSubmission,
[studentEmail]: {
...quizSubmission[studentEmail],
score: totalScore,
},
},
};
try {
const res = fetch(`/api/${courseName}/saveQuiz`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(quiz),
});
console.log(await res);
} catch (err) {
console.error(err);
}
if (quizName.length == 0) {
// If creating a new quiz, reset the questions and
// name fields whenever the dialog is closed.
}
};

const handleScoreChange = (index: number, newScore: number) => {
setScores((currentScores) => {
const updatedScores = [...currentScores];
updatedScores[index] = newScore;
return updatedScores;
});
};

return (
<DialogContent className="sm:max-w-[60%]">
<DialogHeader>
<DialogTitle>Grading Quiz</DialogTitle>
<DialogDescription>
Add details and create when your&apos;e done.
</DialogDescription>
</DialogHeader>
<div className="flex flex-row p-2 items-center gap-2">{quizName}</div>
<ScrollArea className="h-[600px] w-[800px] rounded-md border p-4">
<div>
{propQuestions.map((q, qIndex) => (
<div
className="flex flex-col rounded-md border m-5 p-4"
key={q.questionNum}
>
<div className="flex flex-row p-2 items-center gap-2">
{q.questionNum}. {q.question}
</div>
<div className="flex flex-row p-2 items-center gap-2">
points: {q.points}
</div>

{q.questionType === "MCQ" ? (
<div className="flex flex-col p-2">
{/* Display the options and indicate which one was selected by the student */}
{q.options.map((option, oIndex) => (
<div
className="flex items-center space-x-2 p-2"
key={`option-${q.questionNum}-${oIndex}`}
>
{/* Check if the student's answer matches the option index */}
{q.answer === oIndex ? (
<div className="bg-yellow-200 rounded-md">
{oIndex + 1} . {option}
</div> // This is a checkmark symbol to indicate the selected answer
) : (
<div>
{oIndex + 1} . {option}
</div>
)}
</div>
))}
<div>
student&apos;s answer :
{quizSubmission[studentEmail]?.answers[qIndex] ?? ""}
</div>
<Input
type="number"
value={scores[qIndex]}
onChange={(e) =>
handleScoreChange(qIndex, Number(e.target.value))
}
placeholder="Enter score"
/>
</div>
) : (
// For FRQ, display the student's answer directly
<div className="p-2">
<div className="bg-yellow-200 rounded-md">
answer :{q.answer}
</div>
<div>
student&apos;s answer :{" "}
{quizSubmission[studentEmail]?.answers[qIndex] ?? ""}
</div>
<Input
type="number"
value={scores[qIndex]}
onChange={(e) =>
handleScoreChange(qIndex, Number(e.target.value))
}
placeholder="Enter score"
/>
</div>
)}
</div>
))}
</div>
</ScrollArea>
<DialogClose asChild>
<DialogFooter>
<Button type="submit" onClick={handleSubmit}>
Finish Grading
</Button>
</DialogFooter>
</DialogClose>
</DialogContent>
);
}
2 changes: 1 addition & 1 deletion components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const Navbar = () => {
const { data: session } = useSession();
const username = session?.user.name;
const imageURL = session?.user.image;
const courseList = session?.user.courseList || ["cs4400"];
const courseList = session?.user.courseList || [];

const pathName = useRouter();

Expand Down
19 changes: 12 additions & 7 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ export type User = {
courseList: string[];
};

export type Quiz = {
name: string;
questions: QuizQuestion[];
totalPoints: number;
submissions: Record<string, QuizSubmission>;
};

export type QuizSubmission = {
answers: string[];
score: number | undefined;
};

export type QuizQuestion = {
questionNum: number;
questionType: "MCQ" | "FRQ";
Expand All @@ -26,10 +38,3 @@ export type QuizQuestion = {
answer: number | string;
points: number;
};

export type Quiz = {
name: string;
questions: QuizQuestion[];
totalPoints: number;
submissions: Record<string, number>; // key: username, value: score
};
Loading

0 comments on commit 94dd17a

Please sign in to comment.