176 lines
4.9 KiB
TypeScript
176 lines
4.9 KiB
TypeScript
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,
|
|
};
|
|
}
|
|
}
|