diff --git a/app/api/courses/progress/route.ts b/app/api/courses/progress/route.ts new file mode 100644 index 0000000..ef85cf3 --- /dev/null +++ b/app/api/courses/progress/route.ts @@ -0,0 +1,149 @@ +import { PrismaClient } from "@prisma/client"; +const prisma = new PrismaClient(); +import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server"; +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(req: NextRequest) { + const courseId = req.nextUrl.searchParams.get("cid"); + const courseSession = req.nextUrl.searchParams.get("cs"); + if (!courseId) { + return NextResponse.json( + { message: "Please provide a course id", error: true }, + { status: 400 } + ); + } + + try { + const session = await getKindeServerSession(req); + if (!session.isAuthenticated) { + return NextResponse.json( + { message: "You are not authenticated", error: true }, + { status: 401 } + ); + } + + const user = await session.getUser(); + if (!user) { + return NextResponse.json( + { message: "User not found", error: true }, + { status: 404 } + ); + } + + const course = await prisma.courses.findUnique({ + where: { + course_id: Number(courseId), + }, + }); + + if (!course) { + return NextResponse.json( + { message: "Course not found", error: true }, + { status: 404 } + ); + } + + const data = await prisma.categories.findMany({ + where: { + course_id: course.course_id, + }, + include: { + questions: true, + }, + }); + + const questions = data.flatMap((category) => + category.questions.map((question) => ({ + ...question, + category: category.name, + })) + ); + + if (questions.length === 0) { + return NextResponse.json( + { message: "No questions found for this course", error: true }, + { status: 404 } + ); + } + + let totalQuestions = questions.length; + let answeredQuestions = 0; + let correctAnswers = 0; + let incorrectAnswers = 0; + + // get state ids for "correct" and "incorrect" + const correctState = await prisma.course_progress_states.findFirst({ + where: { + name: "correct", + }, + }); + + if (!correctState) { + return NextResponse.json( + { message: "State not found", error: true }, + { status: 404 } + ); + } + + const incorrectState = await prisma.course_progress_states.findFirst({ + where: { + name: "incorrect", + }, + }); + + if (!incorrectState) { + return NextResponse.json( + { message: "State not found", error: true }, + { status: 404 } + ); + } + + if (courseSession) { + const courseSessionData = await prisma.course_sessions.findUnique({ + where: { + session: courseSession, + user_id: user.id, + }, + }); + + if (courseSessionData) { + const answeredQuestionsData = await prisma.course_progresses.findMany({ + where: { + session_id: courseSessionData.course_session_id, + }, + }); + + answeredQuestions = answeredQuestionsData.length; + + answeredQuestionsData.forEach((answeredQuestion) => { + if ( + answeredQuestion.state_id === correctState.course_progress_state_id + ) { + correctAnswers++; + } else if ( + answeredQuestion.state_id === + incorrectState.course_progress_state_id + ) { + incorrectAnswers++; + } + }); + } + } + + return NextResponse.json({ + message: "Course progress fetched", + data: { + totalQuestions: totalQuestions, + answeredQuestions: answeredQuestions, + correctAnswers: correctAnswers, + incorrectAnswers: incorrectAnswers, + }, + }); + } catch (error) { + console.error("Error:", error); + return NextResponse.json( + { message: "An error occurred", error: true }, + { status: 500 } + ); + } +} diff --git a/components/course/CourseHandler.tsx b/components/course/CourseHandler.tsx index 8b7354f..01e9df3 100644 --- a/components/course/CourseHandler.tsx +++ b/components/course/CourseHandler.tsx @@ -7,6 +7,7 @@ import { SimpleButtonWithLoader } from "../elements/Buttons"; import { mutate } from "swr"; import { useSearchParams } from "next/navigation"; import toast from "react-hot-toast"; +import { useGetCourseProgress } from "@/hooks/data/getCourseProgress"; export default function CourseHandlerComponent({ courseId, @@ -19,10 +20,13 @@ export default function CourseHandlerComponent({ const session = urlParams.get("session") as string; const { getQuestionData, getQuestionIsLoading, getQuestionError } = useGetQuestion({ courseId, session }); + const { getCourseProgressData, getCourseProgressIsLoading, getCourseProgressError } = + useGetCourseProgress({ courseId, session }); const [questionData, setQuestionData] = useState([]) as any; const [answer, setAnswer] = useState(""); const [correctAnswer, setCorrectAnswer] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); + const [courseProgress, setCourseProgress] = useState([]) as any; useEffect(() => { if (!getQuestionIsLoading && getQuestionData?.data) { @@ -30,6 +34,12 @@ export default function CourseHandlerComponent({ } }, [getQuestionData]); + useEffect(() => { + if (!getCourseProgressIsLoading && getCourseProgressData?.data) { + setCourseProgress(getCourseProgressData?.data); + } + }, [getCourseProgressData]); + // call submitAnswer when enter and control is pressed useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { @@ -92,8 +102,9 @@ export default function CourseHandlerComponent({ setAnswer(""); setIsSubmitting(false); mutate(`/api/questions/get?cid=${courseId}&cs=${session}`); + mutate(`/api/courses/progress?cid=${courseId}&cs=${session}`); } else { - toast.error("Leider falsch."); + toast.error("Falsch!"); setCorrectAnswer(checkAnswerResponse.correctAnswer); setIsSubmitting(false); } @@ -103,16 +114,23 @@ export default function CourseHandlerComponent({ setCorrectAnswer(""); setAnswer(""); mutate(`/api/questions/get?cid=${courseId}&cs=${session}`); + mutate(`/api/courses/progress?cid=${courseId}&cs=${session}`); }; return (
- {getQuestionIsLoading && !questionData ? ( + {getQuestionIsLoading && !questionData && !courseProgress && getCourseProgressIsLoading ? (
Loading...
) : (
-
{questionData.category}
+
+
{questionData.category}
+
+ {parseInt(courseProgress?.answeredQuestions) + 1} /{" "} + {parseInt(courseProgress?.totalQuestions)} Fragen +
+
{questionData.question}
diff --git a/hooks/data/getCourseProgress.ts b/hooks/data/getCourseProgress.ts new file mode 100644 index 0000000..c7570f9 --- /dev/null +++ b/hooks/data/getCourseProgress.ts @@ -0,0 +1,28 @@ +import useSWR from "swr"; +import swrGetFetcher from "../swrGetFetcher"; + +const sampleData = {}; + +export const useGetCourseProgress = ({ courseId, session }: { courseId: number; session: string }) => { + let fetchedData, error; + + const swrOptions = { + revalidateOnFocus: true, + }; + + if (process.env.NEXT_PUBLIC_DEV_MODE !== "true") { + ({ data: fetchedData, error } = useSWR( + `/api/courses/progress?cid=${courseId}&cs=${session}`, + swrGetFetcher, + swrOptions, + )); + } else { + fetchedData = sampleData; + } + + return { + getCourseProgressData: fetchedData, + getCourseProgressIsLoading: !fetchedData && !error, + getCourseProgressError: error, + }; +};