import orderBy from "lodash/orderBy"; import { Circle } from "@/services/circle"; import { useCourseSessionsStore } from "@/stores/courseSessions"; import { useLearningPathStore } from "@/stores/learningPath"; import type { Course, CourseCompletion, LearningContentInterface, LearningPathChild, Topic, WagtailLearningPath, } from "@/types"; export interface ContinueData { url: string; has_no_progress: boolean; } function getLastCompleted(courseSlug: string, completionData: CourseCompletion[]) { if (completionData.length === 0) { return undefined; } const courseSessionsStore = useCourseSessionsStore(); const courseSession = courseSessionsStore.courseSessionForCourse(courseSlug); return orderBy(completionData, ["updated_at"], "desc").find((c: CourseCompletion) => { return ( c.completion_status === "SUCCESS" && c.course_session_id === courseSession?.id && c.page_type.startsWith("learnpath.LearningContent") ); }); } export class LearningPath implements WagtailLearningPath { readonly content_type = "learnpath.LearningPath"; public topics: Topic[]; public circles: Circle[]; public nextLearningContent?: LearningContentInterface; public static fromJson( json: WagtailLearningPath, completionData: CourseCompletion[], userId: string | undefined ): LearningPath { return new LearningPath( json.id, json.slug, json.course.title, json.translation_key, json.frontend_url, json.course, json.children, userId, completionData ); } constructor( public readonly id: string, public readonly slug: string, public readonly title: string, public readonly translation_key: string, public readonly frontend_url: string, public readonly course: Course, public children: LearningPathChild[], public userId: string | undefined, completionData?: CourseCompletion[] ) { // parse children this.topics = []; this.circles = []; let topic: Topic | undefined; this.children.forEach((page) => { if (page.content_type === "learnpath.Topic") { if (topic) { this.topics.push(topic); } topic = Object.assign(page, { circles: [] }); } if (page.content_type === "learnpath.Circle") { const circle = Circle.fromJson(page, this); if (completionData && completionData.length > 0) { circle.parseCompletionData(completionData); } if (topic) { topic.circles.push(circle); } circle.previousCircle = this.circles[this.circles.length - 1]; if (circle.previousCircle) { circle.previousCircle.nextCircle = circle; } this.circles.push(circle); } }); if (topic) { this.topics.push(topic); } if (completionData) { this.calcNextLearningContent(completionData); } } public async reloadCompletionData() { const learningPathStore = useLearningPathStore(); const completionData = await learningPathStore.loadCourseSessionCompletionData( this.course.slug, this.userId ); for (const circle of this.circles) { circle.parseCompletionData(completionData); } } public calcNextLearningContent(completionData: CourseCompletion[]): void { this.nextLearningContent = undefined; const lastCompletedLearningContent = getLastCompleted( this.course.slug, completionData ); if (lastCompletedLearningContent) { const lastCircle = this.circles.find((circle) => { return circle.flatLearningContents.find( (learningContent) => learningContent.id === lastCompletedLearningContent.page_id ); }); if (lastCircle) { const lastLearningContent = lastCircle.flatLearningContents.find( (learningContent) => learningContent.id === lastCompletedLearningContent.page_id ); if (lastLearningContent && lastLearningContent.nextLearningContent) { this.nextLearningContent = lastLearningContent.nextLearningContent; } else { if (lastCircle.nextCircle) { this.nextLearningContent = lastCircle.nextCircle.flatLearningContents[0]; } } } } else { if (this.circles[0]) { this.nextLearningContent = this.circles[0].flatLearningContents[0]; } } } public get continueData(): ContinueData { if (this.nextLearningContent) { const circle = this.nextLearningContent.parentCircle; const url = this.nextLearningContent.parentLearningSequence?.frontend_url || circle.frontend_url; const isFirst = this.nextLearningContent.translation_key === this.circles[0].flatLearningContents[0].translation_key; return { url, has_no_progress: isFirst, }; } return { url: "", has_no_progress: true, }; } }