mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
done
This commit is contained in:
@@ -614,9 +614,9 @@ export function CoursesProvider({ children }) {
|
||||
);
|
||||
|
||||
const bulkSyncQuizQuestions = useCallback(
|
||||
(courseId, unitId, quizId, questions, updatedBy) =>
|
||||
(courseId, unitId, quizId, questions, updatedBy, confirmFullReplace = false) =>
|
||||
request(async () => {
|
||||
const { data } = await api.put(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/bulk-sync`, { questions, updatedBy });
|
||||
const { data } = await api.put(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/bulk-sync`, { questions, updatedBy, confirmFullReplace });
|
||||
const result = data?.data?.data ?? [];
|
||||
setQuestions(result);
|
||||
toast("Quiz saved.");
|
||||
@@ -975,9 +975,9 @@ export function CoursesProvider({ children }) {
|
||||
);
|
||||
|
||||
const bulkSyncAssessmentQuestions = useCallback(
|
||||
(courseId, assessmentId, questions, updatedBy) =>
|
||||
(courseId, assessmentId, questions, updatedBy, confirmFullReplace = false) =>
|
||||
request(async () => {
|
||||
const { data } = await api.put(`${BASE}/${courseId}/assessment/${assessmentId}/questions/bulk-sync`, { questions, updatedBy });
|
||||
const { data } = await api.put(`${BASE}/${courseId}/assessment/${assessmentId}/questions/bulk-sync`, { questions, updatedBy, confirmFullReplace });
|
||||
const result = data?.data?.data ?? [];
|
||||
setQuestions(result);
|
||||
toast("Assessment saved.");
|
||||
|
||||
@@ -233,6 +233,7 @@ export default function CourseAssessment() {
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const pendingSaveRef = useRef(null); // stores the meta+questions payload until confirmed
|
||||
const initialSnapshot = useRef(null);
|
||||
const originalQuestionIdsRef = useRef([]); // ids loaded from the server — used to detect a full-pool wipe on save
|
||||
|
||||
const questionRefs = useRef([]);
|
||||
const navItemRefs = useRef([]);
|
||||
@@ -271,6 +272,7 @@ export default function CourseAssessment() {
|
||||
setTitle(t); setPassingScore(ps); setTimeLimit(tl); setIsRequired(ir);
|
||||
setMaxQuestions(mq); setMaxAttempts(ma); setCooldownHours(ch); setShuffleQuestions(sq); setQuestions(qs);
|
||||
initialSnapshot.current = snapAssessment({ title: t, passingScore: ps, timeLimit: tl, isRequired: ir, maxQuestions: mq, maxAttempts: ma, cooldownHours: ch, shuffleQuestions: sq, questions: qs });
|
||||
originalQuestionIdsRef.current = qs.map((q) => q.question_id).filter(Boolean);
|
||||
}, [localAssessment]);
|
||||
|
||||
// ── Measure sticky header → --assessment-h ────────────────────────────────
|
||||
@@ -430,14 +432,28 @@ export default function CourseAssessment() {
|
||||
let id = assessmentId;
|
||||
if (!id) {
|
||||
const res = await createAssessment(courseId, meta);
|
||||
id = res?.data?.data?.data?.assessment_id;
|
||||
// res is the raw response body: { status, message, data: { data: assessment } }
|
||||
// (createAssessment() itself unwraps the same object as data?.data?.data to get
|
||||
// the assessment row — this needs the same two levels, not three).
|
||||
id = res?.data?.data?.assessment_id;
|
||||
if (!id) return;
|
||||
setLocalAssessment((prev) => ({ ...prev, assessment_id: id }));
|
||||
} else {
|
||||
await updateAssessment(courseId, id, meta);
|
||||
}
|
||||
|
||||
await bulkSyncAssessmentQuestions(courseId, id, questions, user?.user_id);
|
||||
// A save that keeps none of the previously-loaded question ids would archive the
|
||||
// entire existing pool in one call — could be a deliberate "start over", but is
|
||||
// exactly the signature of a stale/incomplete state bug. Require explicit confirmation.
|
||||
const retainedIds = questions.map((q) => q.question_id).filter(Boolean);
|
||||
const wipesPool = originalQuestionIdsRef.current.length > 0 && retainedIds.length === 0;
|
||||
if (wipesPool && !window.confirm(
|
||||
`This will permanently remove all ${originalQuestionIdsRef.current.length} existing question(s) from this assessment. Continue?`
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await bulkSyncAssessmentQuestions(courseId, id, questions, user?.user_id, wipesPool);
|
||||
|
||||
initialSnapshot.current = snapAssessment({ title, passingScore, timeLimit, isRequired, maxQuestions, maxAttempts, cooldownHours, shuffleQuestions, questions });
|
||||
};
|
||||
|
||||
@@ -222,6 +222,7 @@ export default function ModifyQuiz() {
|
||||
const navContainerRef = useRef(null);
|
||||
const headerRef = useRef(null);
|
||||
const initialSnapshot = useRef(null);
|
||||
const originalQuestionIdsRef = useRef([]); // ids loaded from the server — used to detect a full-pool wipe on save
|
||||
|
||||
// Junction revamp — this builder runs course-scoped AND from the standalone
|
||||
// Unit Library (/admin/units/:unitId/quiz/edit, no :courseId param).
|
||||
@@ -273,6 +274,7 @@ export default function ModifyQuiz() {
|
||||
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 });
|
||||
originalQuestionIdsRef.current = qs.map((q) => q.question_id).filter(Boolean);
|
||||
}, [localQuiz]);
|
||||
|
||||
// ── Measure sticky header → --quiz-h ──────────────────────────────────────
|
||||
@@ -416,7 +418,18 @@ export default function ModifyQuiz() {
|
||||
await updateQuiz(courseId, unitId, quizId, meta);
|
||||
}
|
||||
|
||||
await bulkSyncQuizQuestions(courseId, unitId, quizId, questions, user?.user_id);
|
||||
// A save that keeps none of the previously-loaded question ids would archive the
|
||||
// entire existing pool in one call — could be a deliberate "start over", but is
|
||||
// exactly the signature of a stale/incomplete state bug. Require explicit confirmation.
|
||||
const retainedIds = questions.map((q) => q.question_id).filter(Boolean);
|
||||
const wipesPool = originalQuestionIdsRef.current.length > 0 && retainedIds.length === 0;
|
||||
if (wipesPool && !window.confirm(
|
||||
`This will permanently remove all ${originalQuestionIdsRef.current.length} existing question(s) from this quiz. Continue?`
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await bulkSyncQuizQuestions(courseId, unitId, quizId, questions, user?.user_id, wipesPool);
|
||||
|
||||
initialSnapshot.current = snapQuiz({ title, passingScore, isRequired, maxQuestions, shuffleQuestions, questions });
|
||||
navigate(`${scopeBase}/view`);
|
||||
|
||||
Reference in New Issue
Block a user