feat: implement knowledge testing system with leaderboard, quiz generation, and PocketBase integration
This commit is contained in:
245
src/lib/db.js
Normal file
245
src/lib/db.js
Normal file
@@ -0,0 +1,245 @@
|
||||
import { pb } from './pb';
|
||||
|
||||
// ── Topics ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getTopics() {
|
||||
return pb.collection('topics').getFullList({ sort: 'created' });
|
||||
}
|
||||
|
||||
export async function saveTopics(topics) {
|
||||
const existing = await pb.collection('topics').getFullList({ fields: 'id' });
|
||||
await Promise.all(existing.map(r => pb.collection('topics').delete(r.id)));
|
||||
return Promise.all(topics.map(t => pb.collection('topics').create({
|
||||
id: t.id,
|
||||
label: t.label,
|
||||
type: t.type,
|
||||
description: t.description,
|
||||
})));
|
||||
}
|
||||
|
||||
export async function upsertTopic(topic) {
|
||||
try {
|
||||
await pb.collection('topics').getOne(topic.id);
|
||||
return pb.collection('topics').update(topic.id, topic);
|
||||
} catch {
|
||||
return 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' });
|
||||
await Promise.all(existing.map(r => pb.collection('relations').delete(r.id)));
|
||||
return Promise.all(relations.map(r => pb.collection('relations').create(r)));
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
// ── 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({ sort: '-created' });
|
||||
}
|
||||
|
||||
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({ sort: 'created' });
|
||||
}
|
||||
|
||||
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() {
|
||||
return pb.collection('leaderboard').getFullList({ sort: '-points' });
|
||||
}
|
||||
|
||||
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) });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user