Load stuff

This commit is contained in:
Daniel Egger 2023-10-12 18:38:14 +02:00
parent 8621d4af07
commit 8544898bbf
9 changed files with 88 additions and 96 deletions

View File

@ -1,15 +1,15 @@
<script setup lang="ts">
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
import type { LearningPath } from "@/services/learningPath";
import { computed } from "vue";
import type { LearningPathType } from "@/types";
import { flatCircles } from "@/composables";
export type DiagramType = "horizontal" | "horizontalSmall" | "singleSmall";
export interface Props {
diagramType?: DiagramType;
learningPath: LearningPath;
// set to undefined (default) to show all circles
learningPath: LearningPathType;
showCircleSlugs?: string[];
}
@ -20,11 +20,11 @@ const props = withDefaults(defineProps<Props>(), {
const circles = computed(() => {
if (props.showCircleSlugs?.length) {
return props.learningPath.circles.filter(
return flatCircles(props.learningPath).filter(
(c) => props.showCircleSlugs?.includes(c.slug) ?? true
);
}
return props.learningPath.circles;
return flatCircles(props.learningPath);
});
const wrapperClasses = computed(() => {

View File

@ -1,31 +1,25 @@
<script setup lang="ts">
import { useLearningPathStore } from "@/stores/learningPath";
import * as log from "loglevel";
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
import type { LearningPath } from "@/services/learningPath";
import { ref } from "vue";
import { useLearningPathWithCompletion } from "@/composables";
log.debug("LearningPathDiagramSmall created");
const props = defineProps<{
courseSlug: string;
courseSessionId: string;
}>();
const learningPathData = ref<LearningPath | undefined>(undefined);
const learningPathStore = useLearningPathStore();
learningPathStore
.loadLearningPath(props.courseSlug + "-lp", undefined, false, false)
.then((data) => {
learningPathData.value = data;
});
const lpQueryResult = useLearningPathWithCompletion(
props.courseSlug,
props.courseSessionId
);
</script>
<template>
<LearningPathDiagram
v-if="learningPathData"
:learning-path="learningPathData"
v-if="lpQueryResult.learningPath.value"
:learning-path="lpQueryResult.learningPath.value"
diagram-type="horizontalSmall"
></LearningPathDiagram>
</template>

View File

@ -1,5 +1,5 @@
import { COURSE_SESSION_DETAIL_QUERY, LEARNING_PATH_QUERY } from "@/graphql/queries";
import { circleFlatChildren } from "@/services/circle";
import { circleFlatChildren, circleFlatLearningContents } from "@/services/circle";
import { useCompletionStore } from "@/stores/completion";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useUserStore } from "@/stores/user";
@ -133,6 +133,10 @@ export function useCourseSessionDetailQuery(courSessionId?: string) {
};
}
export function flatCircles(learningPath: LearningPathType) {
return learningPath.topics.flatMap((t) => t.circles);
}
export function useLearningPathQuery(courseSlug: string) {
const queryResult = useQuery({
query: LEARNING_PATH_QUERY,
@ -147,7 +151,7 @@ export function useLearningPathQuery(courseSlug: string) {
const circles = computed(() => {
if (learningPath.value) {
return learningPath.value.topics.flatMap((t) => t.circles);
return flatCircles(learningPath.value);
}
return undefined;
});
@ -158,7 +162,20 @@ export function useLearningPathQuery(courseSlug: string) {
});
}
return { ...queryResult, learningPath, circles, findCircle };
const dataLoaded = ref(false);
function waitForData() {
return new Promise((resolve) => {
watchEffect(() => {
if (queryResult.data.value) {
dataLoaded.value = true;
resolve(queryResult.data.value);
}
});
});
}
return { ...queryResult, waitForData, learningPath, circles, findCircle };
}
export function useLearningPathWithCompletion(
@ -178,6 +195,7 @@ export function useLearningPathWithCompletion(
const lpQueryResult = useLearningPathQuery(courseSlug);
const completionStore = useCompletionStore();
const nextLearningContent = ref<LearningContentWithCompletion | null>(null);
function updateCompletionData() {
if (userId && courseSessionId) {
@ -209,6 +227,13 @@ export function useLearningPathWithCompletion(
});
});
}
// FIXME calculate nextLearningContent
if (lpQueryResult.circles.value?.length) {
nextLearningContent.value = circleFlatLearningContents(
lpQueryResult.circles.value[0]
)[0];
}
}
async function markCompletion(
@ -226,5 +251,10 @@ export function useLearningPathWithCompletion(
}
}
return { ...lpQueryResult, updateCompletionData, markCompletion };
return {
...lpQueryResult,
updateCompletionData,
markCompletion,
nextLearningContent,
};
}

View File

@ -50,6 +50,7 @@ const getNextStepLink = (courseSession: CourseSession) => {
<LearningPathDiagramSmall
class="mb-4"
:course-slug="courseSession.course.slug"
:course-session-id="courseSession.id"
></LearningPathDiagramSmall>
</div>
<div>

View File

@ -1,11 +1,11 @@
<script setup lang="ts">
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
import type { OldCircle } from "@/services/oldCircle";
import type { LearningPath } from "@/services/learningPath";
import { computed } from "vue";
import type { LearningPathType } from "@/types";
const props = defineProps<{
learningPath: LearningPath | undefined;
learningPath: LearningPathType | undefined;
}>();
const topics = computed(() => props.learningPath?.topics ?? []);

View File

@ -6,19 +6,16 @@ import CircleProgress from "@/pages/learningPath/learningPathPage/LearningPathPr
import LearningPathTopics from "@/pages/learningPath/learningPathPage/LearningPathTopics.vue";
import type { ViewType } from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
import LearningPathViewSwitch from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
import { useLearningPathStore } from "@/stores/learningPath";
import { useUserStore } from "@/stores/user";
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
import * as log from "loglevel";
import { computed, onMounted, ref } from "vue";
import { computed, ref } from "vue";
import { useLearningPathWithCompletion } from "@/composables";
import { someFinishedInLearningSequence } from "@/services/circle";
const props = defineProps<{
courseSlug: string;
}>();
const breakpoints = useBreakpoints(breakpointsTailwind);
const learningPathStore = useLearningPathStore();
const userStore = useUserStore();
// Layout state
const useMobileLayout = breakpoints.smaller("sm");
@ -26,38 +23,20 @@ const selectedView = ref<ViewType>(
(window.localStorage.getItem("learningPathView") as ViewType) || "path"
);
onMounted(async () => {
log.debug("LearningPathPage mounted");
const lpQueryResult = useLearningPathWithCompletion(props.courseSlug);
try {
await learningPathStore.loadLearningPath(props.courseSlug + "-lp");
} catch (error) {
log.error(error);
}
});
const learningPath = computed(() => {
if (userStore.loggedIn && learningPathStore.state.learningPaths.size > 0) {
const learningPathKey = `${props.courseSlug}-lp-${userStore.id}`;
return learningPathStore.state.learningPaths.get(learningPathKey);
}
return undefined;
});
const learningPath = computed(() => lpQueryResult.learningPath.value);
const circlesCount = computed(() => {
if (learningPath.value) {
return learningPath.value.circles.length;
}
return 0;
return lpQueryResult.circles.value?.length ?? 0;
});
const inProgressCirclesCount = computed(() => {
if (learningPath.value) {
return learningPath.value.circles.filter(
if (lpQueryResult.circles.value?.length) {
return lpQueryResult.circles.value.filter(
(circle) =>
circle.learningSequences.filter((ls) =>
circle.someFinishedInLearningSequence(ls.translation_key)
).length
circle.learning_sequences.filter((ls) => someFinishedInLearningSequence(ls))
.length
).length;
}
return 0;

View File

@ -2,12 +2,12 @@
import LearningPathCircleColumn from "@/pages/learningPath/learningPathPage/LearningPathCircleColumn.vue";
import LearningPathScrollButton from "@/pages/learningPath/learningPathPage/LearningPathScrollButton.vue";
import type { OldCircle } from "@/services/oldCircle";
import type { LearningPath } from "@/services/learningPath";
import { useScroll } from "@vueuse/core";
import { ref } from "vue";
import type { LearningPathType } from "@/types";
const props = defineProps<{
learningPath: LearningPath | undefined;
learningPath: LearningPathType | undefined;
useMobileLayout: boolean;
hideButtons?: boolean;
overrideCircleUrlBase?: string;

View File

@ -2,14 +2,18 @@ import type {
CircleSectorData,
CircleSectorProgress,
} from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
import type { OldCircle } from "@/services/oldCircle";
import {
allFinishedInLearningSequence,
someFinishedInLearningSequence,
} from "@/services/circle";
import type { CircleType } from "@/types";
export function calculateCircleSectorData(circle: OldCircle): CircleSectorData[] {
return circle.learningSequences.map((ls) => {
export function calculateCircleSectorData(circle: CircleType): CircleSectorData[] {
return circle.learning_sequences.map((ls) => {
let progress: CircleSectorProgress = "none";
if (circle.allFinishedInLearningSequence(ls.translation_key)) {
if (allFinishedInLearningSequence(ls)) {
progress = "finished";
} else if (circle.someFinishedInLearningSequence(ls.translation_key)) {
} else if (someFinishedInLearningSequence(ls)) {
progress = "in_progress";
}
return {

View File

@ -1,4 +1,4 @@
import { useLearningPathStore } from "@/stores/learningPath";
import { useLearningPathQuery } from "@/composables";
import { useUserStore } from "@/stores/user";
import type { CircleLight, CourseSessionUser, ExpertSessionUser } from "@/types";
import log from "loglevel";
@ -12,7 +12,6 @@ export type CockpitStoreState = {
courseSessionMembers: CourseSessionUser[] | undefined;
circles: CircleCockpit[] | undefined;
currentCircle: CircleCockpit | undefined;
currentCourseSlug: string | undefined;
};
export const useCockpitStore = defineStore({
@ -22,7 +21,6 @@ export const useCockpitStore = defineStore({
courseSessionMembers: undefined,
circles: [],
currentCircle: undefined,
currentCourseSlug: undefined,
} as CockpitStoreState;
},
actions: {
@ -31,21 +29,8 @@ export const useCockpitStore = defineStore({
currentCourseSessionUser: CourseSessionUser | undefined
) {
log.debug("loadCircles called", courseSlug);
this.currentCourseSlug = courseSlug;
const circles = await courseCircles(
this.currentCourseSlug,
currentCourseSessionUser
);
this.circles = circles.map((c) => {
return {
id: c.id,
slug: c.slug,
title: c.title,
name: c.title,
} as const;
});
this.circles = await courseCircles(courseSlug, currentCourseSessionUser);
if (this.circles?.length) {
await this.setCurrentCourseCircle(this.circles[0].slug);
@ -64,28 +49,27 @@ async function courseCircles(
courseSlug: string,
currentCourseSessionUser: CourseSessionUser | undefined
) {
const userStore = useUserStore();
const userId = userStore.id;
if (currentCourseSessionUser && currentCourseSessionUser.role === "EXPERT") {
const expert = currentCourseSessionUser as ExpertSessionUser;
return expert.circles;
return expert.circles.map((c) => {
return { ...c, name: c.title };
});
}
const userStore = useUserStore();
// Return all circles from learning path for admin users
if (userStore.is_superuser) {
const learningPathStore = useLearningPathStore();
const learningPathCircles = learningPathStore
.learningPathForUser(courseSlug, userId)
?.circles.map((c) => {
const lpQueryResult = useLearningPathQuery(courseSlug);
await lpQueryResult.waitForData();
return (lpQueryResult.circles.value ?? []).map((c) => {
return {
id: c.id,
title: c.title,
slug: c.slug,
translation_key: c.translation_key,
};
title: c.title,
name: c.title,
} as const;
});
return learningPathCircles || [];
}
return [];