feat: theme-level weekly sessions and theme-grouped knowledge library
All checks were successful
On Push to Main / test (push) Successful in 46s
On Push to Main / publish (push) Successful in 1m28s
On Push to Main / deploy-dev (push) Successful in 2m9s

- New theme_sessions + theme_session_completions PocketBase collections
- Generate a detailed per-week theme summary covering every topic in the
  week's curriculum slot via EMIT_THEME_SESSION_TOOL; cache per
  (curriculum_version, week_number)
- Replace Leren.jsx "This Week's Topic" card with "This Week's Theme
  Session" that opens the new ThemeSessionView; per-topic micro-learnings
  remain reachable as optional practice from inside the session
- Group the Knowledge Library by topic.theme, pin the current week's
  theme on top, and badge topics already covered by the active session
- Mark week complete when the user finishes reading the theme session

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
RaymondVerhoef
2026-05-28 10:44:10 +02:00
parent 6a1f5556a9
commit d38c75beb1
7 changed files with 688 additions and 99 deletions

View File

@@ -242,6 +242,61 @@ export async function getLearnDone() { return false; }
/** @deprecated learn_progress collection has been dropped. */
export async function setLearnDone() { return null; }
// ── Theme Sessions ──────────────────────────────────────────────────────────
/**
* Look up the cached theme session for a given (curriculum_version, week_number).
* Returns null when no cache entry exists yet.
*/
export async function getThemeSession(curriculumVersionId, weekNumber) {
try {
const record = await pb.collection('theme_sessions').getFirstListItem(
`curriculum_version = "${curriculumVersionId}" && week_number = ${weekNumber}`
);
return record;
} catch {
return null;
}
}
/**
* Upsert the theme session for a given (curriculum_version, week_number).
*/
export async function setThemeSession(curriculumVersionId, weekNumber, theme, content) {
return pbUpsert(
'theme_sessions',
`curriculum_version = "${curriculumVersionId}" && week_number = ${weekNumber}`,
{ theme, content },
{ curriculum_version: curriculumVersionId, week_number: weekNumber, theme, content },
);
}
/**
* Has the user completed the theme session for a given personal week?
* Returns the completion record, or null.
*/
export async function getThemeSessionCompletion(userId, sessionWeek) {
try {
return await pb.collection('theme_session_completions').getFirstListItem(
`team_member_id = "${userId}" && session_week = ${sessionWeek}`
);
} catch {
return null;
}
}
/**
* Mark a theme session as completed for the user/week. Idempotent.
*/
export async function setThemeSessionCompletion(userId, themeSessionId, sessionWeek) {
return pbUpsert(
'theme_session_completions',
`team_member_id = "${userId}" && session_week = ${sessionWeek}`,
{ theme_session_id: themeSessionId },
{ team_member_id: userId, theme_session_id: themeSessionId, session_week: sessionWeek },
);
}
// ── Leaderboard ───────────────────────────────────────────────────────────────
export async function getLeaderboard() {
@@ -484,6 +539,8 @@ export async function resetForSmokeTest({
'topics',
'micro_learnings',
'micro_learning_completions',
'theme_session_completions',
'theme_sessions',
'test_results',
];
if (includeProgress) collections.push('leaderboard');