2026-01-20 09:02:26 -06:00

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" } },
);
}
},
});