generated from muhtadeetaron/nextjs-template
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import React, { createContext, useContext, useState } from "react";
|
|
import { Test, Answer } from "@/types/exam";
|
|
import { API_URL } from "@/lib/auth";
|
|
import { getToken } from "@/lib/auth";
|
|
|
|
interface ExamContextType {
|
|
test: Test | null;
|
|
answers: Answer[];
|
|
startExam: (testType: string, testId: string) => Promise<void>;
|
|
setAnswer: (questionIndex: number, answer: Answer) => void;
|
|
submitExam: () => Promise<void>;
|
|
cancelExam: () => void;
|
|
}
|
|
|
|
const ExamContext = createContext<ExamContextType | undefined>(undefined);
|
|
|
|
export const ExamProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
children,
|
|
}) => {
|
|
const [test, setTest] = useState<Test | null>(null);
|
|
const [answers, setAnswers] = useState<Answer[]>([]);
|
|
|
|
// start exam
|
|
const startExam = async (testType: string, testId: string) => {
|
|
try {
|
|
const token = await getToken(); // if needed
|
|
const res = await fetch(`${API_URL}/tests/${testType}/${testId}`, {
|
|
method: "GET",
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
});
|
|
|
|
if (!res.ok) throw new Error(`Failed to fetch test: ${res.status}`);
|
|
const data: Test = await res.json();
|
|
setTest(data);
|
|
setAnswers(Array(data.questions.length).fill(null));
|
|
} catch (err) {
|
|
console.error("startExam error:", err);
|
|
}
|
|
};
|
|
|
|
// update answer
|
|
const setAnswer = (questionIndex: number, answer: Answer) => {
|
|
setAnswers((prev) => {
|
|
const updated = [...prev];
|
|
updated[questionIndex] = answer;
|
|
return updated;
|
|
});
|
|
};
|
|
|
|
// submit exam
|
|
const submitExam = async () => {
|
|
if (!test) return;
|
|
const token = await getToken();
|
|
try {
|
|
const { type, test_id, attempt_id } = test.metadata;
|
|
const res = await fetch(
|
|
`${API_URL}/tests/${type}/${test_id}/${attempt_id}`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
authorization: `Bearer ${token}`,
|
|
},
|
|
body: JSON.stringify({ answers }),
|
|
}
|
|
);
|
|
if (!res.ok) throw new Error("Failed to submit exam");
|
|
|
|
// clear
|
|
setTest(null);
|
|
setAnswers([]);
|
|
} catch (err) {
|
|
console.error("Failed to submit exam. Reason:", err);
|
|
}
|
|
};
|
|
|
|
// cancel exam
|
|
const cancelExam = () => {
|
|
setTest(null);
|
|
setAnswers([]);
|
|
};
|
|
|
|
return (
|
|
<ExamContext.Provider
|
|
value={{ test, answers, startExam, setAnswer, submitExam, cancelExam }}
|
|
>
|
|
{children}
|
|
</ExamContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useExam = (): ExamContextType => {
|
|
const ctx = useContext(ExamContext);
|
|
if (!ctx) throw new Error("useExam must be used inside ExamProvider");
|
|
return ctx;
|
|
};
|