import { pb } from './pb'; // ── Topics ────────────────────────────────────────────────────────────────── export async function getTopics() { return pb.collection('topics').getFullList(); } export async function saveTopics(topics) { const existing = await pb.collection('topics').getFullList({ fields: 'id' }); for (const r of existing) { await pb.collection('topics').delete(r.id, { requestKey: null }); } for (const t of topics) { await pb.collection('topics').create({ id: t.id, label: t.label, type: t.type, description: t.description, learning_relevance: t.learning_relevance || 'standard', }, { requestKey: null }); } } export async function upsertTopic(topic) { try { await pb.collection('topics').getOne(topic.id); return await pb.collection('topics').update(topic.id, topic); } catch { return await pb.collection('topics').create({ id: topic.id, ...topic }); } } // ── Relations ──────────────────────────────────────────────────────────────── export async function getRelations() { return pb.collection('relations').getFullList(); } export async function saveRelations(relations) { const existing = await pb.collection('relations').getFullList({ fields: 'id' }); for (const r of existing) { await pb.collection('relations').delete(r.id, { requestKey: null }); } for (const r of relations) { await pb.collection('relations').create(r, { requestKey: null }); } } export async function addRelation(relation) { return pb.collection('relations').create(relation); } export async function removeRelation(source, target, type) { const records = await pb.collection('relations').getFullList({ filter: `source="${source}" && target="${target}" && type="${type}"`, }); return Promise.all(records.map(r => pb.collection('relations').delete(r.id, { requestKey: null }))); } // ── Content ────────────────────────────────────────────────────────────────── export async function getContent(topicId) { try { const r = await pb.collection('content').getFirstListItem(`topic_id="${topicId}"`); return r.data; } catch { return null; } } export async function setContent(topicId, data) { try { const r = await pb.collection('content').getFirstListItem(`topic_id="${topicId}"`); return pb.collection('content').update(r.id, { data }); } catch { return pb.collection('content').create({ topic_id: topicId, data }); } } export async function deleteContent(topicId) { try { const r = await pb.collection('content').getFirstListItem(`topic_id="${topicId}"`); return pb.collection('content').delete(r.id); } catch { /* no record, nothing to do */ } } // ── Quiz Banks ─────────────────────────────────────────────────────────────── export async function getQuizBank(topicId) { try { const r = await pb.collection('quiz_banks').getFirstListItem(`topic_id="${topicId}"`); return r.questions || []; } catch { return []; } } export async function setQuizBank(topicId, questions) { try { const r = await pb.collection('quiz_banks').getFirstListItem(`topic_id="${topicId}"`); return pb.collection('quiz_banks').update(r.id, { questions }); } catch { return pb.collection('quiz_banks').create({ topic_id: topicId, questions }); } } export async function deleteQuestionFromBank(topicId, questionId) { const questions = await getQuizBank(topicId); return setQuizBank(topicId, questions.filter(q => q.id !== questionId)); } // ── Sources ────────────────────────────────────────────────────────────────── export async function getSources() { return pb.collection('sources').getFullList(); } export async function addSource(source) { return pb.collection('sources').create(source); } export async function updateSourceStatus(id, status, error = '') { return pb.collection('sources').update(id, { status, error }); } export async function deleteSource(id) { return pb.collection('sources').delete(id); } // ── Team Members ───────────────────────────────────────────────────────────── export async function getTeamMembers() { return pb.collection('team_members').getFullList(); } export async function addTeamMember(member) { return pb.collection('team_members').create(member); } export async function updateTeamMember(id, data) { return pb.collection('team_members').update(id, data); } export async function deleteTeamMember(id) { return pb.collection('team_members').delete(id); } // ── Quiz Results ───────────────────────────────────────────────────────────── export async function getQuizResult(userId, weekNumber) { try { return await pb.collection('quiz_results').getFirstListItem( `user_id="${userId}" && week_number=${weekNumber}` ); } catch { return null; } } export async function saveQuizResult(userId, weekNumber, result) { return pb.collection('quiz_results').create({ user_id: userId, week_number: weekNumber, score: result.score, total: result.total, percentage: result.percentage, time_used: result.timeUsed, completed_at: result.completedAt, breakdown: result.breakdown, points_earned: result.pointsEarned, }); } // ── Quiz Cache ──────────────────────────────────────────────────────────────── export async function getCachedQuiz(userId, weekNumber) { try { return await pb.collection('quiz_cache').getFirstListItem( `user_id="${userId}" && week_number=${weekNumber}` ); } catch { return null; } } export async function setCachedQuiz(userId, weekNumber, quiz) { try { const r = await pb.collection('quiz_cache').getFirstListItem( `user_id="${userId}" && week_number=${weekNumber}` ); return pb.collection('quiz_cache').update(r.id, { questions: quiz.questions, meta: quiz.meta }); } catch { return pb.collection('quiz_cache').create({ user_id: userId, week_number: weekNumber, questions: quiz.questions, meta: quiz.meta, }); } } // ── Learn Progress ──────────────────────────────────────────────────────────── export async function getLearnDone(userId, weekNumber) { try { const r = await pb.collection('learn_progress').getFirstListItem( `user_id="${userId}" && week_number=${weekNumber}` ); return r.done; } catch { return false; } } export async function setLearnDone(userId, weekNumber) { try { const r = await pb.collection('learn_progress').getFirstListItem( `user_id="${userId}" && week_number=${weekNumber}` ); return pb.collection('learn_progress').update(r.id, { done: true }); } catch { return pb.collection('learn_progress').create({ user_id: userId, week_number: weekNumber, done: true }); } } // ── Leaderboard ─────────────────────────────────────────────────────────────── export async function getLeaderboard() { const entries = await pb.collection('leaderboard').getFullList(); return entries.sort((a, b) => (b.points || 0) - (a.points || 0)); } export async function upsertLeaderboardEntry(userId, name, pointsDelta, testsCompletedDelta = 0) { try { const r = await pb.collection('leaderboard').getFirstListItem(`user_id="${userId}"`); return pb.collection('leaderboard').update(r.id, { points: (r.points || 0) + pointsDelta, tests_completed: (r.tests_completed || 0) + testsCompletedDelta, }); } catch { return pb.collection('leaderboard').create({ user_id: userId, name, points: pointsDelta, tests_completed: testsCompletedDelta, learnings_completed: 0, }); } } // ── Settings ────────────────────────────────────────────────────────────────── export async function getSetting(key, defaultValue = null) { try { const r = await pb.collection('settings').getFirstListItem(`key="${key}"`); return r.value ?? defaultValue; } catch { return defaultValue; } } export async function setSetting(key, value) { try { const r = await pb.collection('settings').getFirstListItem(`key="${key}"`); return pb.collection('settings').update(r.id, { value: String(value) }); } catch { return pb.collection('settings').create({ key, value: String(value) }); } } // ── Curriculum ──────────────────────────────────────────────────────────────── export async function getCurriculum(year) { try { return await pb.collection('curriculum').getFullList({ filter: `year=${year}`, sort: 'week_number', }); } catch { return []; } } export async function getCurriculumWeek(year, weekNumber) { try { return await pb.collection('curriculum').getFirstListItem( `year=${year} && week_number=${weekNumber}` ); } catch { return null; } } export async function setCurriculumWeek(year, weekNumber, data) { try { const r = await pb.collection('curriculum').getFirstListItem( `year=${year} && week_number=${weekNumber}` ); return pb.collection('curriculum').update(r.id, data); } catch { return pb.collection('curriculum').create({ year, week_number: weekNumber, ...data, }); } } export async function deleteCurriculumWeek(year, weekNumber) { try { const r = await pb.collection('curriculum').getFirstListItem( `year=${year} && week_number=${weekNumber}` ); return pb.collection('curriculum').delete(r.id); } catch { /* nothing to delete */ } } export async function bulkSetCurriculum(year, weeks) { // Delete all existing entries for this year first const existing = await getCurriculum(year); await Promise.all( existing.map(r => pb.collection('curriculum').delete(r.id, { requestKey: null })) ); // Create all new entries return Promise.all( weeks.map(w => pb.collection('curriculum').create({ year, week_number: w.week_number, topic_id: w.topic_id || '', theme: w.theme || '', quarter: w.quarter || Math.ceil(w.week_number / 13), is_review_week: w.is_review_week || false, sort_order: w.sort_order ?? w.week_number, }, { requestKey: null })) ); } // ── Handbook Sync State ─────────────────────────────────────────────────────── export async function getHandbookSyncStates() { try { return await pb.collection('handbook_sync_state').getFullList(); } catch { return []; } } export async function updateHandbookSyncState(path, sha) { try { const r = await pb.collection('handbook_sync_state').getFirstListItem(`path="${path}"`); return await pb.collection('handbook_sync_state').update(r.id, { sha }); } catch { return await pb.collection('handbook_sync_state').create({ path, sha }); } }