import { apiUrl, apiFetch } from "./api"; export interface ModuleInit { id: string; name: string; order: number; pass_threshold?: number; knowledge_points: { id: string; name: string; type: string; module_id: string; }[]; } export interface LearningKnowledgePoint { id: string; name: string; type: string; } export interface LearningModule { id: string; name: string; order: number; pass_threshold: number; knowledge_points: LearningKnowledgePoint[]; } export interface ProgressDetail { book_id: string; modules: LearningModule[]; mastery_levels: Record; current_module_id?: string; current_stage?: string; diagnostic?: unknown; } export async function fetchProgress(bookId: string): Promise { const res = await apiFetch(apiUrl(`/api/v1/learning/progress/${bookId}`)); if (!res.ok) throw new Error(`Failed to fetch progress: ${res.status}`); return res.json() as Promise; } export async function initModules(bookId: string, modules: ModuleInit[]) { const res = await apiFetch( apiUrl(`/api/v1/learning/progress/${bookId}/init-modules`), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ modules }), }, ); if (!res.ok) throw new Error(`Failed to init modules: ${res.status}`); return res.json(); } // ── Mastery map (the dashboard view) ────────────────────────────────────── // Mirrors deeptutor/learning/policy.py map_summary + next_objective. export type ObjectiveStatus = "new" | "learning" | "mastered"; export interface MapKnowledgePoint { id: string; name: string; type: string; status: ObjectiveStatus; mastery: number; } export interface MapModule { id: string; name: string; order: number; mastered: number; total: number; knowledge_points: MapKnowledgePoint[]; } export interface MasteryMap { counts: { mastered: number; learning: number; new: number; total: number }; due_reviews: number; complete: boolean; modules: MapModule[]; } export interface NextStep { action: string; knowledge_point_name: string; knowledge_point_type: string; status: string; mastery: number; threshold: number; reason: string; } export interface MasteryMapResult { book_id: string; next: NextStep; map: MasteryMap; } export async function fetchMasteryMap( pathId: string, ): Promise { const res = await apiFetch( apiUrl(`/api/v1/learning/progress/${encodeURIComponent(pathId)}/map`), ); if (!res.ok) throw new Error(`Failed to fetch mastery map: ${res.status}`); return res.json() as Promise; } export interface ProgressSummary { book_id: string; name: string; modules_count: number; kp_count: number; current_stage: string; avg_mastery_pct: number; updated_at: number; } export interface ProgressListResult { summaries: ProgressSummary[]; errors: { book_id: string; error: string }[]; } export async function fetchAllProgress(): Promise { const res = await apiFetch(apiUrl("/api/v1/learning/progress")); if (!res.ok) throw new Error(`Failed to fetch all progress: ${res.status}`); return res.json(); } export async function deleteProgress(bookId: string) { const res = await apiFetch( apiUrl(`/api/v1/learning/progress/${encodeURIComponent(bookId)}`), { method: "DELETE" }, ); if (!res.ok) throw new Error(`Failed to delete progress: ${res.status}`); return res.json(); } export async function redoProgress(bookId: string) { const res = await apiFetch( apiUrl(`/api/v1/learning/progress/${encodeURIComponent(bookId)}/redo`), { method: "POST" }, ); if (!res.ok) throw new Error(`Failed to redo progress: ${res.status}`); return res.json(); } export async function importFromBook( bookId: string, chapters: { title: string; knowledge_points: string[] }[], ) { const res = await apiFetch( apiUrl( `/api/v1/learning/progress/${encodeURIComponent(bookId)}/import-from-book`, ), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ chapters }), }, ); if (!res.ok) throw new Error(`Failed to import from book: ${res.status}`); return res.json(); } export async function generateModulesFromNotebook( bookId: string, notebookId: string, records: { id: string; type: string; title: string; output: string }[], ): Promise<{ modules: ModuleInit[] }> { const res = await apiFetch( apiUrl( `/api/v1/learning/progress/${encodeURIComponent(bookId)}/generate-from-notebook`, ), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ notebook_id: notebookId, records }), }, ); if (!res.ok) throw new Error(`Failed to generate modules from notebook: ${res.status}`); return res.json(); }