109 lines
4.1 KiB
TypeScript
109 lines
4.1 KiB
TypeScript
// routes/api/brain/queue.ts
|
|
// API endpoint for building Brain study queue with mode progression
|
|
|
|
import { define, State } from "../../../utils.ts";
|
|
import { SessionCookie } from "../../auth/callback.ts";
|
|
import { brainDataClient } from "../../../utils/BrainDataClient.ts";
|
|
import { getAllKvQuizConfigs } from "../../../utils/QuizDataClient.ts";
|
|
import { DEFAULT_QUIZ_MODE, QUIZ_MODES } from "../../../utils/quizModes.ts";
|
|
|
|
export const handler = define.handlers({
|
|
// GET: Build and return study queue for user
|
|
async GET(ctx) {
|
|
const state = ctx.state as State;
|
|
const stateData: SessionCookie = state.data ?? {
|
|
started_on: 0,
|
|
user_name: "",
|
|
user_id: "",
|
|
role: "",
|
|
user_encrypted: false,
|
|
};
|
|
|
|
if (!state.stateId || !stateData.user_id) {
|
|
return new Response(
|
|
JSON.stringify({ error: "Unauthorized" }),
|
|
{ status: 401, headers: { "Content-Type": "application/json" } },
|
|
);
|
|
}
|
|
|
|
const url = new URL(ctx.req.url);
|
|
// Mode parameter is now deprecated - each card has its own mode based on progression
|
|
// We still accept it for backwards compatibility but it's ignored
|
|
const newCardsLimit = parseInt(
|
|
url.searchParams.get("newLimit") || "10",
|
|
10,
|
|
);
|
|
const totalLimit = parseInt(url.searchParams.get("limit") || "50", 10);
|
|
|
|
// Parse tab_index from query parameters for filtering
|
|
const rawTabIndex = url.searchParams.get("tab_index");
|
|
let tabIndex: number | undefined = undefined;
|
|
if (rawTabIndex !== null) {
|
|
const parsed = Number.parseInt(rawTabIndex, 10);
|
|
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
tabIndex = parsed;
|
|
}
|
|
}
|
|
|
|
try {
|
|
// Get all quiz configs for the user
|
|
let quizConfigs = await getAllKvQuizConfigs(stateData.user_id);
|
|
|
|
// Filter by tab_index if provided
|
|
if (tabIndex !== undefined) {
|
|
quizConfigs = quizConfigs.filter((kvObj) => {
|
|
const quizTabIndex = kvObj.value?.tab_index ?? 0;
|
|
return quizTabIndex === tabIndex;
|
|
});
|
|
}
|
|
|
|
// Build the study queue (mode is now per-card based on progression)
|
|
const queue = await brainDataClient.buildStudyQueue(
|
|
stateData.user_id,
|
|
quizConfigs,
|
|
DEFAULT_QUIZ_MODE, // This is now ignored internally but kept for API compatibility
|
|
newCardsLimit,
|
|
totalLimit,
|
|
);
|
|
|
|
// Get stats
|
|
const stats = await brainDataClient.getStats(stateData.user_id);
|
|
|
|
// Calculate counts based on new locked-set behavior:
|
|
// - newCount: cards still in active progression (either truly new or in-progress through modes)
|
|
// - dueCount: cards that have completed all modes and are now in spaced repetition review
|
|
// - reviewOnlyCount: same as dueCount (review-only cards)
|
|
const activeSetCards = queue.filter((q) => q.is_new && !q.is_review_only);
|
|
const reviewCards = queue.filter((q) => q.is_review_only);
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
success: true,
|
|
queue,
|
|
stats,
|
|
// Return default mode for backwards compatibility
|
|
mode: DEFAULT_QUIZ_MODE,
|
|
// Include the full mode list so client knows the progression order
|
|
modeOrder: QUIZ_MODES,
|
|
totalInQueue: queue.length,
|
|
// Active set count: cards currently being mastered through 6 modes
|
|
activeSetCount: activeSetCards.length,
|
|
// New count: for UI, same as active set (cards not yet fully mastered)
|
|
newCount: activeSetCards.length,
|
|
// Review-only count: cards that have completed all 6 modes
|
|
reviewOnlyCount: reviewCards.length,
|
|
// Due count: for backwards compatibility, set to review-only count
|
|
dueCount: reviewCards.length,
|
|
}),
|
|
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
);
|
|
} catch (error) {
|
|
console.error("Error building brain queue:", error);
|
|
return new Response(
|
|
JSON.stringify({ error: "Failed to build queue" }),
|
|
{ status: 500, headers: { "Content-Type": "application/json" } },
|
|
);
|
|
}
|
|
},
|
|
});
|