From 2c2528521816732982239b21ebc14d67b4898a5c Mon Sep 17 00:00:00 2001 From: sruju333 Date: Fri, 9 Oct 2026 01:51:11 -0400 Subject: [PATCH 1/2] week3: call checkAndAward and activities PUT on lesson/puzzle completion --- .../src/core/services/activitiesApi.ts | 20 ++++++++++++++++++ .../lesson-overlay/Lesson-overlay.tsx | 21 +++++++++++++++++++ .../src/features/puzzles/Puzzles.tsx | 16 ++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/react-ystemandchess/src/core/services/activitiesApi.ts b/react-ystemandchess/src/core/services/activitiesApi.ts index 1297fa94..72dba59b 100644 --- a/react-ystemandchess/src/core/services/activitiesApi.ts +++ b/react-ystemandchess/src/core/services/activitiesApi.ts @@ -24,4 +24,24 @@ export async function completeActivity(username: string, token: string, activity ); if (!res.ok) throw new Error("Failed to update activity"); return res.json(); +} + +/** + * Resolves the activity name matching a taskId by fetching the user's + * activities. Needed because the ?taskId= deep link carries the catalog + * taskId, but PUT /activities/:username/activity matches on activities.name. + * + * @param {string} username + * @param {string} token + * @param {string} taskId + * @returns {Promise} Matching activity name, if found + */ +export async function getActivityNameByTaskId(username: string, token: string, taskId: string) { + const res = await fetch(`${environment.urls.middlewareURL}/activities/${username}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (!res.ok) throw new Error("Failed to fetch activities"); + const json = await res.json(); + const match = json.activities?.activities?.find((a: any) => a.taskId === taskId); + return match?.name; } \ No newline at end of file diff --git a/react-ystemandchess/src/features/lessons/piece-lessons/lesson-overlay/Lesson-overlay.tsx b/react-ystemandchess/src/features/lessons/piece-lessons/lesson-overlay/Lesson-overlay.tsx index abe1de91..3e6a2ddd 100644 --- a/react-ystemandchess/src/features/lessons/piece-lessons/lesson-overlay/Lesson-overlay.tsx +++ b/react-ystemandchess/src/features/lessons/piece-lessons/lesson-overlay/Lesson-overlay.tsx @@ -25,6 +25,10 @@ import { ReactComponent as BackIconInactive } from '../../../../assets/images/ic import { ReactComponent as NextIcon } from '../../../../assets/images/icons/icon_next.svg'; import { ReactComponent as NextIconInactive } from '../../../../assets/images/icons/icon_next_inactive.svg'; +import { SetPermissionLevel } from "../../../../globals"; +import { checkAndAward } from "../../../../core/services/badgesApi"; +import { completeActivity, getActivityNameByTaskId } from "../../../../core/services/activitiesApi"; + type LessonOverlayProps = { propPieceName?: any; propLessonNumber?: any; @@ -907,6 +911,23 @@ const LessonOverlay: React.FC = ({ const nextNum = lessonNum + 1; await updateCompletion(); // backend update for logged-in users + try { + const uInfo = await SetPermissionLevel(cookies); + if (!uInfo?.error && uInfo.username) { + const taskId = new URLSearchParams(location.search).get("taskId"); + if (taskId) { + getActivityNameByTaskId(uInfo.username, cookies.login, taskId) + .then((name) => name && completeActivity(uInfo.username, cookies.login, name)) + .catch((err) => console.error("Failed to update activity:", err)); + } + checkAndAward(uInfo.username, cookies.login).catch((err) => + console.error("Failed to check/award badges:", err) + ); + } +} catch (err) { + console.error("Post-completion calls failed:", err); +} + if (nextNum >= totalLessons) { setAllLessonsDone(true); return; diff --git a/react-ystemandchess/src/features/puzzles/Puzzles.tsx b/react-ystemandchess/src/features/puzzles/Puzzles.tsx index fa64ae49..8d859f77 100644 --- a/react-ystemandchess/src/features/puzzles/Puzzles.tsx +++ b/react-ystemandchess/src/features/puzzles/Puzzles.tsx @@ -13,6 +13,9 @@ import ChessBoard, { } from "../../components/ChessBoard/ChessBoard"; import { useChessSocket } from "../lessons/piece-lessons/lesson-overlay/hooks/useChessSocket"; import { Move } from "../../core/types/chess"; +import { useSearchParams } from "react-router"; +import { checkAndAward } from "../../core/services/badgesApi"; +import { completeActivity, getActivityNameByTaskId } from "../../core/services/activitiesApi"; type PuzzlesProps = { student?: any; @@ -95,6 +98,9 @@ const Puzzles: React.FC = ({ const studentId = student || cookies.login?.studentId || uuidv4(); const mentorId = mentor || "puzzle_mentor_" + studentId; + const [searchParams] = useSearchParams(); + const taskId = searchParams.get("taskId"); + // ============================================================================ // PUZZLE LOADING // ============================================================================ @@ -378,6 +384,16 @@ const Puzzles: React.FC = ({ if (moveListRef.current.length === 0) { isPuzzleEndRef.current = true; socket.sendMessage("puzzle completed"); + if (role === "student" && username) { + if (taskId) { + getActivityNameByTaskId(username, cookies.login, taskId) + .then((name) => name && completeActivity(username, cookies.login, name)) + .catch((err) => console.error("Failed to update activity:", err)); + } + checkAndAward(username, cookies.login).catch((err) => + console.error("Failed to check/award badges:", err) + ); + } reportPuzzleSolved(); setTimeout(() => { setModal({ From 5b092ff26ad351cce1d8854b1e5a8e01a8f9843c Mon Sep 17 00:00:00 2001 From: sruju333 Date: Fri, 9 Oct 2026 02:15:40 -0400 Subject: [PATCH 2/2] fix: correct useSearchParams import --- react-ystemandchess/src/features/puzzles/Puzzles.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-ystemandchess/src/features/puzzles/Puzzles.tsx b/react-ystemandchess/src/features/puzzles/Puzzles.tsx index 8d859f77..9e180815 100644 --- a/react-ystemandchess/src/features/puzzles/Puzzles.tsx +++ b/react-ystemandchess/src/features/puzzles/Puzzles.tsx @@ -13,7 +13,7 @@ import ChessBoard, { } from "../../components/ChessBoard/ChessBoard"; import { useChessSocket } from "../lessons/piece-lessons/lesson-overlay/hooks/useChessSocket"; import { Move } from "../../core/types/chess"; -import { useSearchParams } from "react-router"; +import { useSearchParams } from "react-router-dom"; import { checkAndAward } from "../../core/services/badgesApi"; import { completeActivity, getActivityNameByTaskId } from "../../core/services/activitiesApi";