From 34e53cd7ecd14c43465eb7e69753ceb256fcbe4f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 13:44:59 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20scope=20lesson=20progress?= =?UTF-8?q?=20queries=20to=20active=20courses/lessons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Optimize database queries on dashboard, course index, and certificate pages by limiting the retrieval of lesson progress to only the IDs of the active/published lessons being displayed. This avoids fetching the user's entire history and reduces payload sizes and database load. Co-authored-by: projectamazonph <286085559+projectamazonph@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ .../courses/[courseSlug]/certificate/page.tsx | 20 +++++++++++++------ src/app/(dashboard)/courses/page.tsx | 13 +++++++----- src/app/(dashboard)/dashboard/page.tsx | 13 +++++++----- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index ba67a71..2cd600f 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -3,3 +3,7 @@ ## 2026-07-16 - [O(N*M) Nested Loop Lookups in Grading Engines] **Learning:** In interactive scenarios (such as Bid Elevator and STR Triage), grading engines frequently iterate over user decisions and match them against scenario properties (like keywords or search terms). Performing `array.find()` inside loop bodies or filter predicates results in costly O(N*M) lookups. **Action:** Convert arrays to `Map` lookups before entering loops/nested scans. Mapping keys once in O(M) time enables O(1) lookups during execution, transforming the time complexity of the grading logic to O(N + M). + +## 2026-07-17 - [Scoping User-Specific Lesson Progress Queries] +**Learning:** Querying a user's entire history of lesson progress on dashboard and course catalog pages creates large database payloads and high memory consumption, which grows quadratically as more courses/lessons are added. +**Action:** Always scope database queries for lesson progress to the relevant courses or specific lesson IDs (e.g., using `lessonId: { in: lessonIds }`) to load only what is being displayed. diff --git a/src/app/(dashboard)/courses/[courseSlug]/certificate/page.tsx b/src/app/(dashboard)/courses/[courseSlug]/certificate/page.tsx index 3640169..82b7e0a 100644 --- a/src/app/(dashboard)/courses/[courseSlug]/certificate/page.tsx +++ b/src/app/(dashboard)/courses/[courseSlug]/certificate/page.tsx @@ -194,13 +194,21 @@ async function PendingLessons({ take: 50, }); + const courseLessonIds = allLessons.map((l) => l.id); const completedSet = new Set( - ( - await db.lessonProgress.findMany({ - where: { userId, status: 'COMPLETED', deletedAt: null }, - select: { lessonId: true }, - }) - ).map((p) => p.lessonId), + courseLessonIds.length > 0 + ? ( + await db.lessonProgress.findMany({ + where: { + userId, + lessonId: { in: courseLessonIds }, + status: 'COMPLETED', + deletedAt: null, + }, + select: { lessonId: true }, + }) + ).map((p) => p.lessonId) + : [] ); const pending = allLessons.filter((l) => !completedSet.has(l.id)).slice(0, 8); diff --git a/src/app/(dashboard)/courses/page.tsx b/src/app/(dashboard)/courses/page.tsx index 1d0c6c3..ee9e641 100644 --- a/src/app/(dashboard)/courses/page.tsx +++ b/src/app/(dashboard)/courses/page.tsx @@ -37,11 +37,14 @@ export default async function CoursesIndexPage() { }, }); - // Get user's lesson progress - const lessonProgress = await db.lessonProgress.findMany({ - where: { userId: user.id, deletedAt: null }, - select: { lessonId: true, status: true }, - }); + // Get user's lesson progress scoped to published courses (Bolt performance optimization) + const allLessonIds = courses.flatMap((c) => c.modules.flatMap((m) => m.lessons.map((l) => l.id))); + const lessonProgress = allLessonIds.length > 0 + ? await db.lessonProgress.findMany({ + where: { userId: user.id, lessonId: { in: allLessonIds }, deletedAt: null }, + select: { lessonId: true, status: true }, + }) + : []; const progressMap = new Map(lessonProgress.map((p) => [p.lessonId, p.status])); return ( diff --git a/src/app/(dashboard)/dashboard/page.tsx b/src/app/(dashboard)/dashboard/page.tsx index 546298d..1844ec1 100644 --- a/src/app/(dashboard)/dashboard/page.tsx +++ b/src/app/(dashboard)/dashboard/page.tsx @@ -30,11 +30,14 @@ export default async function DashboardPage() { }, }); - // Get user's lesson progress - const lessonProgress = await db.lessonProgress.findMany({ - where: { userId: user.id, deletedAt: null }, - select: { lessonId: true, status: true }, - }); + // Get user's lesson progress scoped to published courses (Bolt performance optimization) + const allLessonIds = courses.flatMap((c) => c.modules.flatMap((m) => m.lessons.map((l) => l.id))); + const lessonProgress = allLessonIds.length > 0 + ? await db.lessonProgress.findMany({ + where: { userId: user.id, lessonId: { in: allLessonIds }, deletedAt: null }, + select: { lessonId: true, status: true }, + }) + : []; const progressMap = new Map(lessonProgress.map((p) => [p.lessonId, p.status])); // Compute aggregate stats