Refactor learningPath loading

This commit is contained in:
Daniel Egger 2022-12-06 10:12:55 +01:00
parent 00c2217ad1
commit 2c17012686
11 changed files with 100 additions and 79 deletions

View File

@ -12,6 +12,10 @@ export default {
type: String,
default: "horizontal",
},
postfix: {
type: String,
default: "",
},
learningPath: {
required: true,
type: Object,
@ -25,7 +29,7 @@ export default {
},
computed: {
svgId() {
return `learningpath-diagram-${this.learningPath.slug}-${this.diagramType}`;
return `learningpath-diagram-${this.learningPath.slug}-${this.diagramType}${this.postfix}`;
},
viewBox() {
return `0 0 ${this.width} ${this.height}`;

View File

@ -9,7 +9,7 @@ import { ref } from "vue";
log.debug("LearningPathDiagramSmall created");
const props = defineProps<{
learningPathUrl: string;
courseSlug: string;
}>();
const learningPathData = ref<LearningPath | undefined>(undefined);
@ -17,11 +17,7 @@ const learningPathData = ref<LearningPath | undefined>(undefined);
const learningPathStore = useLearningPathStore();
learningPathStore
.loadLearningPath(
props.learningPathUrl.replace(/\/learn$/, "-lp").replace(/^\/course\//, ""),
false,
false
)
.loadLearningPath(props.courseSlug + "-lp", undefined, false, false)
.then((data) => {
learningPathData.value = data;
});

View File

@ -1,31 +1,29 @@
<script setup lang="ts">
import { useLearningPathStore } from "@/stores/learningPath";
import * as log from "loglevel";
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
import type { LearningPath } from "@/services/learningPath";
log.debug("LearningPathView created");
const props = defineProps<{
learningPathSlug: string;
learningPath: LearningPath | undefined;
show: boolean;
}>();
const learningPathStore = useLearningPathStore();
const emits = defineEmits(["closemodal"]);
</script>
<template>
<ItFullScreenModal :show="show" @closemodal="$emit('closemodal')">
<div v-if="learningPathStore.learningPath" class="container-medium">
<h1>{{ learningPathStore.learningPath.title }}</h1>
<div v-if="learningPath" class="container-medium">
<h1>{{ learningPath.title }}</h1>
<div class="learningpath flex flex-col">
<div class="flex flex-col h-max">
<LearningPathDiagram
v-if="learningPathStore.learningPath"
v-if="learningPath"
class="w-full"
:learning-path="learningPathStore.learningPath"
:learning-path="learningPath"
diagram-type="vertical"
></LearningPathDiagram>
</div>

View File

@ -42,7 +42,7 @@ onMounted(async () => {
<div>
<LearningPathDiagramSmall
class="mb-4"
:learning-path-url="courseSession.learning_path_url"
:course-slug="courseSession.course.slug"
></LearningPathDiagramSmall>
</div>
<div>

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import { useCockpitStore } from "@/stores/cockpit";
import { useCompetenceStore } from "@/stores/competence";
import { useLearningPathStore } from "@/stores/learningPath";
import * as log from "loglevel";
import { onMounted } from "vue";
@ -12,6 +13,7 @@ const props = defineProps<{
const cockpitStore = useCockpitStore();
const competenceStore = useCompetenceStore();
const learningPathStore = useLearningPathStore();
onMounted(async () => {
log.debug("CockpitParentPage mounted", props.courseSlug);
@ -23,6 +25,8 @@ onMounted(async () => {
props.courseSlug + "-competence",
csu.user_id
);
learningPathStore.loadLearningPath(props.courseSlug + "-lp", csu.user_id);
});
} catch (error) {
log.error(error);

View File

@ -1,9 +1,8 @@
<script setup lang="ts">
import { useCockpitStore } from "@/stores/cockpit";
import { useCompetenceStore } from "@/stores/competence";
import { useLearningPathStore } from "@/stores/learningPath";
import * as log from "loglevel";
import { onMounted } from "vue";
import { computed, onMounted } from "vue";
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
@ -15,23 +14,19 @@ const props = defineProps<{
log.debug("CockpitUserProfilePage created", props.userId);
const cockpitStore = useCockpitStore();
const competenceStore = useCompetenceStore();
const learningPathStore = useLearningPathStore();
function userCountStatus(userId: number) {
return competenceStore.calcStatusCount(
competenceStore.flatPerformanceCriteria(userId)
);
}
onMounted(async () => {
log.debug("CockpitUserProfilePage mounted");
});
try {
await learningPathStore.loadLearningPath(props.courseSlug + "-lp");
} catch (error) {
log.error(error);
const learningPath = computed(() => {
if (learningPathStore.learningPaths.size > 0) {
const learningPathKey = `${props.courseSlug}-lp-${props.userId}`;
return learningPathStore.learningPaths.get(learningPathKey);
}
return undefined;
});
function courseSessionUser() {
@ -50,11 +45,12 @@ function courseSessionUser() {
:src="courseSessionUser()?.avatar_url"
/>
</div>
<div v-if="learningPathStore.learningPath">
<div v-if="learningPath">
<LearningPathDiagram
class="mx-auto max-w-[1920px] max-h-[90px] lg:max-h-[380px] w-full px-4"
diagram-type="horizontal"
:learning-path="learningPathStore.learningPath"
:learning-path="learningPath"
:postfix="userId"
></LearningPathDiagram>
</div>
</div>

View File

@ -3,7 +3,7 @@ import * as log from "loglevel";
import { useLearningPathStore } from "@/stores/learningPath";
import { useUserStore } from "@/stores/user";
import { onMounted } from "vue";
import { computed, onMounted } from "vue";
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
import LearningPathViewVertical from "@/components/learningPath/LearningPathViewVertical.vue";
@ -18,6 +18,15 @@ const props = defineProps<{
const learningPathStore = useLearningPathStore();
const userStore = useUserStore();
const learningPath = computed(() => {
if (userStore.loggedIn && learningPathStore.learningPaths.size > 0) {
const learningPathKey = `${props.courseSlug}-lp-${userStore.id}`;
return learningPathStore.learningPaths.get(learningPathKey);
}
return undefined;
});
onMounted(async () => {
log.debug("LearningPathView mounted");
@ -29,7 +38,7 @@ onMounted(async () => {
});
const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
if (learningPath.nextLearningContent) {
if (learningPath?.nextLearningContent) {
const circle = learningPath.nextLearningContent.parentCircle;
const url =
learningPath.nextLearningContent.parentLearningSequence?.frontend_url ||
@ -45,11 +54,11 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
</script>
<template>
<div v-if="learningPathStore.learningPath" class="bg-gray-200">
<div v-if="learningPath" class="bg-gray-200">
<Teleport to="body">
<LearningPathViewVertical
:show="learningPathStore.page === 'OVERVIEW'"
:learning-path-slug="props.courseSlug + '-lp'"
:learning-path="learningPath"
@closemodal="learningPathStore.page = 'INDEX'"
/>
</Teleport>
@ -70,13 +79,13 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
<LearningPathDiagram
class="mx-auto max-w-[1920px] max-h-[90px] lg:max-h-[380px] w-full px-4"
diagram-type="horizontal"
:learning-path="learningPathStore.learningPath"
:learning-path="learningPath"
></LearningPathDiagram>
</div>
<div class="container-large pt-0 lg:pt-4">
<h1 data-cy="learning-path-title" class="mt-6 lg:mt-12 mb-6">
{{ learningPathStore.learningPath.title }}
{{ learningPath.title }}
</h1>
<div
class="bg-white p-4 lg:mb-16 flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-gray-500 justify-start"
@ -87,27 +96,19 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
</h2>
<p class="mt-4 text-xl"></p>
</div>
<div
v-if="learningPathStore.learningPath.nextLearningContent"
class="p-4 lg:p-8 flex-2"
>
<div v-if="learningPath.nextLearningContent" class="p-4 lg:p-8 flex-2">
{{ $t("learningPathPage.nextStep") }}
<h3>
{{
learningPathStore.learningPath.nextLearningContent.parentCircle.title
}}:
{{
learningPathStore.learningPath.nextLearningContent
.parentLearningSequence?.title
}}
{{ learningPath.nextLearningContent.parentCircle.title }}:
{{ learningPath.nextLearningContent.parentLearningSequence?.title }}
</h3>
<router-link
class="mt-4 btn-blue"
:to="createContinueUrl(learningPathStore.learningPath)[0]"
:to="createContinueUrl(learningPath)[0]"
data-cy="lp-continue-button"
translate
>
<span v-if="createContinueUrl(learningPathStore.learningPath)[1]">
<span v-if="createContinueUrl(learningPath)[1]">
{{ $t("general.start") }}
</span>
<span v-else>{{ $t("general.nextStep") }}</span>

View File

@ -3,6 +3,7 @@ import * as log from "loglevel";
import type { Circle } from "@/services/circle";
import { useCompletionStore } from "@/stores/completion";
import { useLearningPathStore } from "@/stores/learningPath";
import { useUserStore } from "@/stores/user";
import type {
CourseCompletionStatus,
LearningContent,
@ -37,13 +38,25 @@ export const useCircleStore = defineStore({
},
getters: {},
actions: {
async loadCircle(courseSlug: string, circleSlug: string): Promise<Circle> {
async loadCircle(
courseSlug: string,
circleSlug: string,
userId: number | undefined = undefined
): Promise<Circle> {
if (!userId) {
const userStore = useUserStore();
userId = userStore.id;
}
this.circle = undefined;
const learningPathSlug = courseSlug + "-lp";
const learningPathStore = useLearningPathStore();
await learningPathStore.loadLearningPath(learningPathSlug);
if (learningPathStore.learningPath) {
this.circle = learningPathStore.learningPath.circles.find((circle) => {
const learningPath = await learningPathStore.loadLearningPath(
learningPathSlug,
userId
);
if (learningPath) {
this.circle = learningPath.circles.find((circle) => {
return circle.slug.endsWith(circleSlug);
});
}

View File

@ -124,7 +124,7 @@ export const useCompetenceStore = defineStore({
userId = userStore.id;
}
if (this.competenceProfilePages.get(userId) && !reload) {
if (this.competenceProfilePages.has(userId) && !reload) {
const competenceProfilePage = this.competenceProfilePages.get(userId);
await this.parseCompletionData(userId);
return competenceProfilePage;

View File

@ -2,52 +2,60 @@ import { itGetCached } from "@/fetchHelpers";
import { LearningPath } from "@/services/learningPath";
import { useCompletionStore } from "@/stores/completion";
import { useUserStore } from "@/stores/user";
import _ from "lodash";
import { defineStore } from "pinia";
export type LearningPathStoreState = {
learningPath: LearningPath | undefined;
page: "INDEX" | "OVERVIEW";
loading: boolean;
};
learningPaths: Map<string, LearningPath>;
let lastSlug = "";
page: "INDEX" | "OVERVIEW";
};
export const useLearningPathStore = defineStore({
id: "learningPath",
state: () => {
return {
learningPath: undefined,
learningPaths: new Map<string, LearningPath>(),
page: "INDEX",
loading: false,
} as LearningPathStoreState;
},
getters: {},
actions: {
async loadLearningPath(slug: string, reload = false, fail = true) {
this.loading = true;
const completionStore = useCompletionStore();
if (this.learningPath && !reload && slug === lastSlug) {
return this.learningPath;
async loadLearningPath(
slug: string,
userId: number | undefined = undefined,
reload = false,
fail = true
) {
if (!userId) {
const userStore = useUserStore();
userId = userStore.id;
}
this.learningPath = undefined;
const learningPathData = await itGetCached(`/api/course/page/${slug}/`);
const key = `${slug}-${userId}`;
if (this.learningPaths.has(key) && !reload) {
return this.learningPaths.get(key);
}
const learningPathData = await itGetCached(`/api/course/page/${slug}/`);
if (!learningPathData && fail) {
throw `No learning path found with: ${slug}`;
}
const userStore = useUserStore();
if (learningPathData && userStore.loggedIn) {
lastSlug = slug;
const completionData = await completionStore.loadCompletionData(
learningPathData.course.id,
userStore.id
);
const completionStore = useCompletionStore();
const completionData = await completionStore.loadCompletionData(
learningPathData.course.id,
userId
);
this.learningPath = LearningPath.fromJson(learningPathData, completionData);
this.loading = false;
return this.learningPath;
}
const learningPath = LearningPath.fromJson(
_.cloneDeep(learningPathData),
completionData
);
this.learningPaths.set(key, learningPath);
return learningPath;
},
},
});

View File

@ -194,6 +194,7 @@ export interface Course {
id: number;
title: string;
category_name: string;
slug: string;
}
export interface CourseCategory {