feat(exam): add SAT style testing component
This commit is contained in:
127
src/stores/useSatExam.ts
Normal file
127
src/stores/useSatExam.ts
Normal file
@ -0,0 +1,127 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { type ExamPhase } from "../types/sheet";
|
||||
import type { SessionModuleQuestions } from "../types/session";
|
||||
|
||||
// interface SatExamState {
|
||||
// modules: Module[];
|
||||
// phase: ExamPhase;
|
||||
// moduleIndex: number;
|
||||
// questionIndex: number;
|
||||
// endTime: number | null;
|
||||
|
||||
// startExam: () => void;
|
||||
// setModuleQuestionss: (modules: Module[]) => void;
|
||||
|
||||
// nextQuestion: () => void;
|
||||
// prevQuestion: () => void;
|
||||
// nextModule: () => void;
|
||||
// nextPhase: () => void;
|
||||
// skipBreak: () => void;
|
||||
// getRemainingTime: () => number;
|
||||
// finishExam: () => void;
|
||||
// resetExam: () => void;
|
||||
// replaceModules: (modules: Module[]) => void;
|
||||
// }
|
||||
|
||||
interface SatExamState {
|
||||
currentModuleQuestions: SessionModuleQuestions | null;
|
||||
phase: ExamPhase;
|
||||
questionIndex: number;
|
||||
endTime: number | null;
|
||||
|
||||
setModuleQuestions: (module: SessionModuleQuestions) => void;
|
||||
startExam: () => void;
|
||||
nextQuestion: () => void;
|
||||
prevQuestion: () => void;
|
||||
|
||||
startBreak: () => void;
|
||||
skipBreak: () => void;
|
||||
finishExam: () => void;
|
||||
resetExam: () => void;
|
||||
getRemainingTime: () => number;
|
||||
}
|
||||
|
||||
const BREAK_DURATION = 30; // seconds
|
||||
|
||||
export const useSatExam = create<SatExamState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
currentModuleQuestions: null,
|
||||
phase: "IDLE",
|
||||
questionIndex: 0,
|
||||
endTime: null,
|
||||
|
||||
setModuleQuestions: (module: SessionModuleQuestions) => {
|
||||
const endTime = Date.now() + module.time_limit_minutes * 1000;
|
||||
|
||||
set({
|
||||
currentModuleQuestions: module,
|
||||
questionIndex: 0,
|
||||
endTime,
|
||||
});
|
||||
},
|
||||
|
||||
startExam: () => {
|
||||
set({ phase: "MODULE", questionIndex: 0 });
|
||||
},
|
||||
|
||||
nextQuestion: () => {
|
||||
const { currentModuleQuestions, questionIndex } = get();
|
||||
const questions = currentModuleQuestions?.questions ?? [];
|
||||
|
||||
if (questionIndex < questions.length - 1) {
|
||||
set({ questionIndex: questionIndex + 1 });
|
||||
}
|
||||
},
|
||||
|
||||
prevQuestion: () => {
|
||||
const { questionIndex } = get();
|
||||
if (questionIndex > 0) {
|
||||
set({ questionIndex: questionIndex - 1 });
|
||||
}
|
||||
},
|
||||
|
||||
startBreak: () => {
|
||||
const endTime = Date.now() + BREAK_DURATION * 1000;
|
||||
|
||||
set((state) => ({
|
||||
phase: "BREAK",
|
||||
endTime,
|
||||
questionIndex: 0, // optional: reset question index for next module UX
|
||||
}));
|
||||
},
|
||||
|
||||
skipBreak: () => {
|
||||
const module = get().currentModuleQuestions;
|
||||
|
||||
if (!module) return;
|
||||
|
||||
const endTime = Date.now() + module.time_limit_minutes * 1000;
|
||||
|
||||
set({
|
||||
phase: "MODULE",
|
||||
endTime,
|
||||
questionIndex: 0,
|
||||
});
|
||||
},
|
||||
|
||||
finishExam: () => set({ phase: "FINISHED", endTime: null }),
|
||||
|
||||
getRemainingTime: () => {
|
||||
const { endTime } = get();
|
||||
if (!endTime) return 0;
|
||||
return Math.max(0, Math.floor((endTime - Date.now()) / 1000));
|
||||
},
|
||||
|
||||
resetExam: () =>
|
||||
set({
|
||||
currentModuleQuestions: null,
|
||||
phase: "IDLE",
|
||||
questionIndex: 0,
|
||||
endTime: null,
|
||||
}),
|
||||
}),
|
||||
{ name: "sat-exam-storage" },
|
||||
),
|
||||
);
|
||||
Reference in New Issue
Block a user