diff --git a/components/QuestionResponseTable.tsx b/components/QuestionResponseTable.tsx new file mode 100644 index 0000000..afb4b30 --- /dev/null +++ b/components/QuestionResponseTable.tsx @@ -0,0 +1,43 @@ +import React from "react"; + +type Question = { + id: number; + text: string; + type: "MCQ" | "MSQ"; + inputtedAnswers: number[]; + correctAnswers: number[]; + options: { id: number; text: string }[]; +}; + +interface Props { + questions: Question[]; +} + +export const QuestionResponseTable: React.FC = ({ questions }) => { + return ( +
+ {questions.map((question, idx) => ( +
+
+
{question.text}
+
+ + {question.type === "MCQ" ? "Multiple Choice" : "Multi-Select"} + +
+
+
+ {question.inputtedAnswers + .map((optId) => question.options.find((o) => o.id === optId)?.text) + .join(", ") || "—"} +
+
+ {question.correctAnswers + .map((optId) => question.options.find((o) => o.id === optId)?.text) + .join(", ")} +
+
+ ))} +
+ ); +}; diff --git a/components/StudentAnalyticsDrawer.tsx b/components/StudentAnalyticsDrawer.tsx new file mode 100644 index 0000000..c21940d --- /dev/null +++ b/components/StudentAnalyticsDrawer.tsx @@ -0,0 +1,214 @@ +"use client"; + +import { VisuallyHidden } from "@radix-ui/react-visually-hidden"; +import { format } from "date-fns"; +import { useEffect, useState } from "react"; +import { Sheet, SheetContent, SheetTitle, SheetTrigger } from "./ui/sheet"; +import { QuestionResponseTable } from "@/components/QuestionResponseTable"; +import { DatePicker } from "@/components/ui/DatePicker"; +import DonutChart from "@/components/ui/DonutChart"; +import { useToast } from "@/hooks/use-toast"; +import { + studentAnalyticsAttendanceChartConfig, + studentAnalyticsScoreChartConfig, +} from "@/lib/constants"; +import { getQuestionsAndResponsesForDate, getStudentAnalytics } from "@/services/analytics"; + +type Props = { + studentId: string | null; + courseId: number; +}; + +export const StudentAnalyticsDrawer = ({ studentId, courseId }: Props) => { + const { toast } = useToast(); + const [selectedDate, setSelectedDate] = useState(new Date()); + const [analyticsData, setAnalyticsData] = useState<{ + fullName: string; + attendancePercentage: number; + totalCheckIns: number; + lastCheckInDate: string | null; + mcqScore: number; + msqScore: number; + averagePollScore: number; + } | null>(null); + + useEffect(() => { + if (!studentId) return; + getStudentAnalytics(courseId, studentId) + .then(setAnalyticsData) + .catch((err: unknown) => { + if (err instanceof Error) { + console.error("Failed to load analytics", err); + } else { + console.error("Unknown error occurred"); + } + toast({ + variant: "destructive", + title: "Error", + description: "Failed to load analytics", + }); + }); + }, [courseId, studentId]); + + type QuestionForDate = { + id: number; + text: string; + type: "MCQ" | "MSQ"; + inputtedAnswers: number[]; + correctAnswers: number[]; + options: { id: number; text: string }[]; + }; + + const [questionsForDate, setQuestionsForDate] = useState([]); + + useEffect(() => { + const fetchQuestions = async () => { + if (!studentId) return; + const data = await getQuestionsAndResponsesForDate(courseId, studentId, selectedDate); + setQuestionsForDate(data); + }; + void fetchQuestions(); + }, [selectedDate]); + + return ( + + + + + + + Student Analytics + + +
+ Student +
{analyticsData?.fullName ?? "Loading..."}
+
+ +
+ Student's Performance +
+
+
+ +
+
+
+ Multiple Choice: +
+ {analyticsData?.mcqScore ?? "--"}% +
+
+
+ Multi-Select: +
+ {analyticsData?.msqScore ?? "--"}% +
+
+
+
+
+
+ +
+
+
+ Last Check-in: +
+ {analyticsData?.lastCheckInDate ?? "--"} +
+
+
+ Check-ins: +
+ {analyticsData?.totalCheckIns ?? "--"} +
+
+
+
+
+
+ +
+
+ { + setSelectedDate(date); + }} + /> +
+
+ +
+
+ {format(selectedDate, "M/dd")} +
+
+ +
+ {questionsForDate.length === 0 ? ( +
+ No questions for this day +
+ ) : ( +
+
+
+ Question: +
+
+ Inputted: +
+
+ Correct Answer: +
+
+ + +
+ )} +
+
+
+
+ ); +}; diff --git a/components/ui/DatePicker.tsx b/components/ui/DatePicker.tsx index b346b87..0a22a6b 100644 --- a/components/ui/DatePicker.tsx +++ b/components/ui/DatePicker.tsx @@ -11,7 +11,7 @@ interface Props { export function DatePicker({ currentDate, onSelect }: Props) { return ( - +

{currentDate && format(currentDate, "PPP")}

diff --git a/components/ui/DonutChart.tsx b/components/ui/DonutChart.tsx index 629efc2..c20ed0d 100644 --- a/components/ui/DonutChart.tsx +++ b/components/ui/DonutChart.tsx @@ -34,6 +34,8 @@ export default function DonutChart({ nameKey={nameKey} innerRadius={"65%"} strokeWidth={15} + startAngle={90} + endAngle={-270} >