add: more commits

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-07-03 16:21:27 +08:00
parent 17326b2c2e
commit 7e964f2432
112 changed files with 9160 additions and 3461 deletions
@@ -1,9 +1,11 @@
import { useEffect, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { ArrowLeft, House, Plus, Save, HelpCircle, ChevronUp, ChevronDown } from "lucide-react";
import { toast } from "sonner";
import { useCourses } from "@/contexts/AdminCoursesContext";
import { useAuth } from "@/contexts/AuthContext";
import api from "@/utils/api.util";
import { PageMeta } from "@/contexts/MetadataContext";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -196,10 +198,12 @@ export default function ModifyQuiz() {
const navigate = useNavigate();
const { courseId, unitId } = useParams();
const {
fetchQuiz, createQuiz, updateQuiz,
createQuizQuestion, updateQuizQuestion,
course, unit, quiz, loading,
createQuiz, updateQuiz,
bulkSyncQuizQuestions,
course, unit, loading,
} = useCourses();
const [localQuiz, setLocalQuiz] = useState(null);
const { user } = useAuth();
const [initializing, setInitializing] = useState(true);
@@ -227,26 +231,36 @@ export default function ModifyQuiz() {
{ label: "Quiz" },
];
// ── Fetch ──────────────────────────────────────────────────────────────────
// ── Fetch — silently treat 404 as "no quiz yet" (create mode) ─────────────
useEffect(() => {
(async () => {
await fetchQuiz(courseId, unitId);
setInitializing(false);
try {
const { data } = await api.get(`/admin/courses/${courseId}/units/${unitId}/quiz`);
const result = data?.data?.data ?? null;
setLocalQuiz(result);
} catch (err) {
if (err?.response?.status !== 404) {
toast.error(err?.response?.data?.message ?? "Could not load quiz.");
}
// 404 → no quiz yet, stay in create mode with localQuiz = null
} finally {
setInitializing(false);
}
})();
}, [courseId, unitId]);
// ── Seed ──────────────────────────────────────────────────────────────────
// ── Seed form from fetched quiz ────────────────────────────────────────────
useEffect(() => {
if (!quiz) return;
const t = quiz.title ?? "";
const ps = quiz.passing_score ?? 70;
const ir = quiz.is_required === true || quiz.is_required === 1;
const mq = quiz.max_questions ?? "";
const sq = quiz.shuffle_questions === true || quiz.shuffle_questions === 1;
const qs = (quiz.questions ?? []).map((q) => ({ ...q, _tempId: q.question_id, options: q.options ?? [] }));
if (!localQuiz) return;
const t = localQuiz.title ?? "";
const ps = localQuiz.passing_score ?? 70;
const ir = localQuiz.is_required === true || localQuiz.is_required === 1;
const mq = localQuiz.max_questions ?? "";
const sq = localQuiz.shuffle_questions === true || localQuiz.shuffle_questions === 1;
const qs = (localQuiz.questions ?? []).map((q) => ({ ...q, _tempId: q.question_id, options: q.options ?? [] }));
setTitle(t); setPassingScore(ps); setIsRequired(ir); setMaxQuestions(mq); setShuffleQuestions(sq); setQuestions(qs);
initialSnapshot.current = snapQuiz({ title: t, passingScore: ps, isRequired: ir, maxQuestions: mq, shuffleQuestions: sq, questions: qs });
}, [quiz]);
}, [localQuiz]);
// ── Measure sticky header → --quiz-h ──────────────────────────────────────
useEffect(() => {
@@ -366,7 +380,7 @@ export default function ModifyQuiz() {
return;
}
let quizId = quiz?.quiz_id;
let quizId = localQuiz?.quiz_id;
const meta = {
title: title || "Unit Quiz",
passing_score: passingScore,
@@ -381,18 +395,12 @@ export default function ModifyQuiz() {
const res = await createQuiz(courseId, unitId, meta);
quizId = res?.data?.data?.data?.quiz_id;
if (!quizId) return;
setLocalQuiz((prev) => ({ ...prev, quiz_id: quizId }));
} else {
await updateQuiz(courseId, unitId, quizId, meta);
}
for (let i = 0; i < questions.length; i++) {
const q = { ...questions[i], order_index: i, updatedBy: user?.user_id };
if (q.question_id) {
await updateQuizQuestion(courseId, unitId, quizId, q.question_id, q);
} else {
await createQuizQuestion(courseId, unitId, quizId, q);
}
}
await bulkSyncQuizQuestions(courseId, unitId, quizId, questions, user?.user_id);
initialSnapshot.current = snapQuiz({ title, passingScore, isRequired, maxQuestions, shuffleQuestions, questions });
navigate(-1);