Started to update the exam to work with Zustand for the history review
This commit is contained in:
@@ -69,6 +69,10 @@ export interface WritingExercise {
|
||||
info: string; //* The information about the task, like the amount of time they should spend on it
|
||||
prompt: string; //* The context given to the user containing what they should write about
|
||||
wordCounter: WordCounter; //* The minimum or maximum amount of words that should be written
|
||||
userSolutions: {
|
||||
id: string;
|
||||
solution: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface SpeakingExercise {
|
||||
@@ -77,6 +81,10 @@ export interface SpeakingExercise {
|
||||
title: string;
|
||||
text: string;
|
||||
prompts: string[];
|
||||
userSolutions: {
|
||||
id: string;
|
||||
solution: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface FillBlanksExercise {
|
||||
|
||||
31
src/pages/api/exam/[module]/[id].ts
Normal file
31
src/pages/api/exam/[module]/[id].ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type {NextApiRequest, NextApiResponse} from "next";
|
||||
import {app} from "@/firebase";
|
||||
import {getFirestore, doc, getDoc} from "firebase/firestore";
|
||||
import {withIronSessionApiRoute} from "iron-session/next";
|
||||
import {sessionOptions} from "@/lib/session";
|
||||
|
||||
const db = getFirestore(app);
|
||||
|
||||
export default withIronSessionApiRoute(handler, sessionOptions);
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!req.session.user) {
|
||||
res.status(401).json({ok: false});
|
||||
return;
|
||||
}
|
||||
|
||||
const {module, id} = req.query as {module: string; id: string};
|
||||
|
||||
const docRef = doc(db, module, id);
|
||||
const docSnap = await getDoc(docRef);
|
||||
|
||||
if (docSnap.exists()) {
|
||||
res.status(200).json({
|
||||
id: docSnap.id,
|
||||
...docSnap.data(),
|
||||
});
|
||||
} else {
|
||||
res.status(404).json(undefined);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import {Stat, User} from "@/interfaces/user";
|
||||
import Speaking from "@/exams/Speaking";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
import useUser from "@/hooks/useUser";
|
||||
import useExamStore, {ExamState} from "@/stores/examStore";
|
||||
|
||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||
const user = req.session.user;
|
||||
@@ -42,30 +43,44 @@ export default function Page() {
|
||||
const [userSolutions, setUserSolutions] = useState<UserSolution[]>([]);
|
||||
const [selectedModules, setSelectedModules] = useState<Module[]>([]);
|
||||
const [hasBeenUploaded, setHasBeenUploaded] = useState(false);
|
||||
const [showSolutions, setShowSolutions] = useState(false);
|
||||
const [moduleIndex, setModuleIndex] = useState(0);
|
||||
const [sessionId, setSessionId] = useState("");
|
||||
const [exam, setExam] = useState<Exam>();
|
||||
const [timer, setTimer] = useState(-1);
|
||||
|
||||
const [showSolutions, setShowSolutions] = useExamStore((state) => [state.showSolutions, state.setShowSolutions]);
|
||||
const [exams, setExams] = useExamStore((state) => [state.exams, state.setExams]);
|
||||
|
||||
const {user} = useUser({redirectTo: "/login"});
|
||||
|
||||
useEffect(() => setSessionId(uuidv4()), []);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (selectedModules.length > 0 && moduleIndex < selectedModules.length) {
|
||||
const nextExam = await getExam(selectedModules[moduleIndex]);
|
||||
if (selectedModules.length > 0 && exams.length > 0 && moduleIndex < selectedModules.length) {
|
||||
const nextExam = exams[moduleIndex];
|
||||
setExam(nextExam ? updateExamWithUserSolutions(nextExam) : undefined);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedModules, moduleIndex]);
|
||||
}, [selectedModules, moduleIndex, exams]);
|
||||
|
||||
useEffect(() => {
|
||||
async () => {
|
||||
if (selectedModules.length > 0) {
|
||||
const examPromises = selectedModules.map(getExam);
|
||||
Promise.all(examPromises).then((values) => {
|
||||
if (values.every((x) => !!x)) {
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [selectedModules, setExams]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (selectedModules.length > 0 && moduleIndex >= selectedModules.length && !hasBeenUploaded) {
|
||||
const stats: Stat[] = userSolutions.map((solution) => ({
|
||||
const newStats: Stat[] = userSolutions.map((solution) => ({
|
||||
...solution,
|
||||
session: sessionId,
|
||||
exam: solution.exam!,
|
||||
@@ -75,7 +90,7 @@ export default function Page() {
|
||||
}));
|
||||
|
||||
axios
|
||||
.post<{ok: boolean}>("/api/stats", stats)
|
||||
.post<{ok: boolean}>("/api/stats", newStats)
|
||||
.then((response) => setHasBeenUploaded(response.data.ok))
|
||||
.catch(() => setHasBeenUploaded(false));
|
||||
}
|
||||
@@ -116,7 +131,9 @@ export default function Page() {
|
||||
};
|
||||
|
||||
const updateExamWithUserSolutions = (exam: Exam): Exam => {
|
||||
const exercises = exam.exercises.map((x) => Object.assign(x, {userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions}));
|
||||
const exercises = exam.exercises.map((x) =>
|
||||
Object.assign(x, !x.userSolutions ? {userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions} : x.userSolutions),
|
||||
);
|
||||
|
||||
return Object.assign(exam, exercises);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
import {Module} from "@/interfaces";
|
||||
import {Exam} from "@/interfaces/exam";
|
||||
import {Stat} from "@/interfaces/user";
|
||||
import {getExamsBySession} from "@/utils/stats";
|
||||
import {create} from "zustand";
|
||||
|
||||
const useExamStore = create((set) => ({}));
|
||||
export interface ExamState {
|
||||
exams: Exam[];
|
||||
stats: Stat[];
|
||||
showSolutions: boolean;
|
||||
setStats: (stats: Stat[]) => void;
|
||||
setExams: (exams: Exam[]) => void;
|
||||
setShowSolutions: (showSolutions: boolean) => void;
|
||||
}
|
||||
|
||||
const useExamStore = create<ExamState>((set) => ({
|
||||
exams: [],
|
||||
stats: [],
|
||||
showSolutions: false,
|
||||
setStats: (stats: Stat[]) => set(() => ({stats})),
|
||||
setExams: (exams: Exam[]) => set(() => ({exams})),
|
||||
setShowSolutions: (showSolutions: boolean) => set(() => ({showSolutions})),
|
||||
}));
|
||||
|
||||
export default useExamStore;
|
||||
|
||||
@@ -91,5 +91,10 @@ export const formatExerciseAverageScoreStats = (stats: Stat[]): {label: string;
|
||||
});
|
||||
};
|
||||
|
||||
export const getExamsBySession = (stats: Stat[], session: string) => {
|
||||
const grouped = groupBySession(stats);
|
||||
return grouped[session].map((exam) => exam.exam);
|
||||
};
|
||||
|
||||
export const groupBySession = (stats: Stat[]) => groupBy(stats, "session");
|
||||
export const groupByDate = (stats: Stat[]) => groupBy(stats, "date");
|
||||
|
||||
Reference in New Issue
Block a user