From a546a57c1778080c3124051d0b7f392b4243ebc6 Mon Sep 17 00:00:00 2001 From: rgrgogu Date: Fri, 24 Jul 2026 23:34:26 +0800 Subject: [PATCH] fix bugs Signed-off-by: rgrgogu --- src/modules/client/pages/CourseCheckout.jsx | 9 ++++++++- src/modules/client/pages/UnitReader.jsx | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/modules/client/pages/CourseCheckout.jsx b/src/modules/client/pages/CourseCheckout.jsx index 92c5f26..1bc6e64 100644 --- a/src/modules/client/pages/CourseCheckout.jsx +++ b/src/modules/client/pages/CourseCheckout.jsx @@ -50,7 +50,14 @@ export default function CourseCheckout() { // Handle PayPal return — capture useEffect(() => { - if (!returnToken || capturingRef.current) return; + // A real PayPal cancel redirect carries BOTH `cancelled=true` (our own + // cancelUrl) and `token=` (PayPal always appends its own token + // to whatever return/cancel URL it's given) — without this guard, both + // this effect and the cancellation effect below fired on the same load, + // racing a capture call against the cancel and surfacing a confusing + // "Pending purchase not found" error toast instead of a clean + // cancellation message. + if (!returnToken || wasCancelled || capturingRef.current) return; capturingRef.current = true; setCapturing(true); captureCourseOrder(returnToken).then((result) => { diff --git a/src/modules/client/pages/UnitReader.jsx b/src/modules/client/pages/UnitReader.jsx index 9dedd98..f3719f4 100644 --- a/src/modules/client/pages/UnitReader.jsx +++ b/src/modules/client/pages/UnitReader.jsx @@ -175,8 +175,14 @@ const UnitReader = () => { const stub = lessons.find((l) => l.lesson_id === selectedLessonId); if (stub?.status === 'completed') return; completedSessionRef.current.add(selectedLessonId); + // Trust the server's evaluated unit status, not a local "all fetched + // lessons read" re-derivation — that drifted from the real completion + // requirement (e.g. a unit needing its quiz passed too) and fired a + // premature "Unit complete!" toast. Same fix already applied to + // upsertLessonProgress's own unitDetail update; UnitList.jsx's + // handleMarkComplete already uses this authoritative pattern. upsertLessonProgress(lesson.uuid, 'completed', uuid).then((result) => { - if (result?.unit && unitDetail && lessons.every((l) => l.lesson_id === selectedLessonId || l.status === 'completed')) { + if (result?.unit?.status === 'completed') { toast.success('Unit complete!', { duration: 4000 }); } });