import { api } from "@/lib/api-client"; import type { CoursePlan, CoursePlanAssignment, CoursePlanDeliverables, CoursePlanGenerateBrief, CoursePlanMaterial, CoursePlanMedia, CoursePlanSource, CoursePlanSourceKind, } from "@/types"; /** * REST helpers for the AI course plan generator. * * The backend routes are mounted under `/api/ai/course-plan`; see * `backend/custom_addons/encoach_ai_course/controllers/course_plan.py`. */ export const coursePlanService = { // --------------------------------------------------------------------- // Plan lifecycle // --------------------------------------------------------------------- async list(params?: { page?: number; size?: number; search?: string; }): Promise<{ items: CoursePlan[]; page: { page: number; size: number; total: number } }> { return api.get("/ai/course-plan", params); }, async get(planId: number): Promise<{ data: CoursePlan }> { return api.get(`/ai/course-plan/${planId}`); }, async generate(brief: CoursePlanGenerateBrief): Promise<{ data: CoursePlan }> { return api.post("/ai/course-plan", brief); }, async remove(planId: number): Promise<{ success: boolean }> { return api.delete(`/ai/course-plan/${planId}`); }, async generateWeekMaterials( planId: number, weekNumber: number, ): Promise<{ items: CoursePlanMaterial[]; count: number }> { return api.post(`/ai/course-plan/${planId}/weeks/${weekNumber}/materials`); }, async listWeekMaterials( planId: number, weekNumber: number, ): Promise<{ items: CoursePlanMaterial[]; count: number }> { return api.get(`/ai/course-plan/${planId}/weeks/${weekNumber}/materials`); }, // --------------------------------------------------------------------- // Phase A — Sources // --------------------------------------------------------------------- async listSources(planId: number): Promise<{ items: CoursePlanSource[]; count: number }> { return api.get(`/ai/course-plan/${planId}/sources`); }, async createSource( planId: number, payload: | { kind: "url"; url: string; name?: string; auto_index?: boolean } | { kind: "text"; inline_text: string; name?: string; auto_index?: boolean }, ): Promise<{ data: CoursePlanSource }> { return api.post(`/ai/course-plan/${planId}/sources`, payload); }, async uploadSource( planId: number, file: File, opts?: { name?: string; auto_index?: boolean }, ): Promise<{ data: CoursePlanSource }> { const fd = new FormData(); fd.append("file", file); fd.append("kind", "file" satisfies CoursePlanSourceKind); if (opts?.name) fd.append("name", opts.name); if (opts?.auto_index !== undefined) { fd.append("auto_index", opts.auto_index ? "1" : "0"); } return api.upload(`/ai/course-plan/${planId}/sources`, fd); }, async reindexSource( planId: number, sourceId: number, ): Promise<{ data: CoursePlanSource }> { return api.post( `/ai/course-plan/${planId}/sources/${sourceId}/index`, ); }, async deleteSource( planId: number, sourceId: number, ): Promise<{ success: boolean }> { return api.delete(`/ai/course-plan/${planId}/sources/${sourceId}`); }, // --------------------------------------------------------------------- // Phase B — Deliverables / progress // --------------------------------------------------------------------- async deliverables(planId: number): Promise { return api.get(`/ai/course-plan/${planId}/deliverables`); }, // --------------------------------------------------------------------- // Phase C — Multimedia // --------------------------------------------------------------------- async listMaterialMedia( materialId: number, ): Promise<{ items: CoursePlanMedia[]; count: number }> { return api.get(`/ai/course-plan/material/${materialId}/media`); }, async generateAudio( materialId: number, payload?: { voice?: string; language?: string; gender?: "male" | "female"; provider?: "polly" | "elevenlabs"; }, ): Promise<{ data: CoursePlanMedia }> { return api.post( `/ai/course-plan/material/${materialId}/media/audio`, payload ?? {}, ); }, async generateImage( materialId: number, payload?: { prompt?: string; size?: "1024x1024" | "1792x1024" | "1024x1792"; style?: "natural" | "vivid"; quality?: "standard" | "hd"; }, ): Promise<{ data: CoursePlanMedia }> { return api.post( `/ai/course-plan/material/${materialId}/media/image`, payload ?? {}, ); }, async composeVideo(materialId: number): Promise<{ data: CoursePlanMedia }> { return api.post( `/ai/course-plan/material/${materialId}/media/video`, ); }, async deleteMedia(mediaId: number): Promise<{ success: boolean }> { return api.delete(`/ai/course-plan/media/${mediaId}`); }, async generateWeekMedia( planId: number, weekNumber: number, payload?: { kinds?: Array<"audio" | "image" | "video"> }, ): Promise<{ items: Array; count: number }> { return api.post( `/ai/course-plan/${planId}/weeks/${weekNumber}/media`, payload ?? {}, ); }, // --------------------------------------------------------------------- // Phase D — Assignments // --------------------------------------------------------------------- async listAssignments( planId: number, ): Promise<{ items: CoursePlanAssignment[]; count: number }> { return api.get(`/ai/course-plan/${planId}/assignments`); }, async createAssignment( planId: number, payload: | { mode: "batch"; batch_id: number; due_date?: string | null; message?: string; } | { mode: "students"; student_user_ids: number[]; due_date?: string | null; message?: string; }, ): Promise<{ data: CoursePlanAssignment }> { return api.post(`/ai/course-plan/${planId}/assignments`, payload); }, async deleteAssignment( planId: number, assignmentId: number, ): Promise<{ success: boolean }> { return api.delete( `/ai/course-plan/${planId}/assignments/${assignmentId}`, ); }, // --------------------------------------------------------------------- // Phase E — Student-side // --------------------------------------------------------------------- async studentList(): Promise<{ items: CoursePlan[]; count: number }> { return api.get("/student/course-plans"); }, async studentGet(planId: number): Promise<{ data: CoursePlan }> { return api.get(`/student/course-plans/${planId}`); }, };