Load stuff
This commit is contained in:
parent
8621d4af07
commit
8544898bbf
|
|
@ -1,15 +1,15 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
||||||
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
|
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import type { LearningPathType } from "@/types";
|
||||||
|
import { flatCircles } from "@/composables";
|
||||||
|
|
||||||
export type DiagramType = "horizontal" | "horizontalSmall" | "singleSmall";
|
export type DiagramType = "horizontal" | "horizontalSmall" | "singleSmall";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
diagramType?: DiagramType;
|
diagramType?: DiagramType;
|
||||||
learningPath: LearningPath;
|
learningPath: LearningPathType;
|
||||||
// set to undefined (default) to show all circles
|
|
||||||
showCircleSlugs?: string[];
|
showCircleSlugs?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,11 +20,11 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
||||||
const circles = computed(() => {
|
const circles = computed(() => {
|
||||||
if (props.showCircleSlugs?.length) {
|
if (props.showCircleSlugs?.length) {
|
||||||
return props.learningPath.circles.filter(
|
return flatCircles(props.learningPath).filter(
|
||||||
(c) => props.showCircleSlugs?.includes(c.slug) ?? true
|
(c) => props.showCircleSlugs?.includes(c.slug) ?? true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return props.learningPath.circles;
|
return flatCircles(props.learningPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
const wrapperClasses = computed(() => {
|
const wrapperClasses = computed(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,25 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useLearningPathStore } from "@/stores/learningPath";
|
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
|
|
||||||
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
import { useLearningPathWithCompletion } from "@/composables";
|
||||||
import { ref } from "vue";
|
|
||||||
|
|
||||||
log.debug("LearningPathDiagramSmall created");
|
log.debug("LearningPathDiagramSmall created");
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
courseSlug: string;
|
courseSlug: string;
|
||||||
|
courseSessionId: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const learningPathData = ref<LearningPath | undefined>(undefined);
|
const lpQueryResult = useLearningPathWithCompletion(
|
||||||
|
props.courseSlug,
|
||||||
const learningPathStore = useLearningPathStore();
|
props.courseSessionId
|
||||||
|
);
|
||||||
learningPathStore
|
|
||||||
.loadLearningPath(props.courseSlug + "-lp", undefined, false, false)
|
|
||||||
.then((data) => {
|
|
||||||
learningPathData.value = data;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<LearningPathDiagram
|
<LearningPathDiagram
|
||||||
v-if="learningPathData"
|
v-if="lpQueryResult.learningPath.value"
|
||||||
:learning-path="learningPathData"
|
:learning-path="lpQueryResult.learningPath.value"
|
||||||
diagram-type="horizontalSmall"
|
diagram-type="horizontalSmall"
|
||||||
></LearningPathDiagram>
|
></LearningPathDiagram>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { COURSE_SESSION_DETAIL_QUERY, LEARNING_PATH_QUERY } from "@/graphql/queries";
|
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 { useCompletionStore } from "@/stores/completion";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
import { useUserStore } from "@/stores/user";
|
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) {
|
export function useLearningPathQuery(courseSlug: string) {
|
||||||
const queryResult = useQuery({
|
const queryResult = useQuery({
|
||||||
query: LEARNING_PATH_QUERY,
|
query: LEARNING_PATH_QUERY,
|
||||||
|
|
@ -147,7 +151,7 @@ export function useLearningPathQuery(courseSlug: string) {
|
||||||
|
|
||||||
const circles = computed(() => {
|
const circles = computed(() => {
|
||||||
if (learningPath.value) {
|
if (learningPath.value) {
|
||||||
return learningPath.value.topics.flatMap((t) => t.circles);
|
return flatCircles(learningPath.value);
|
||||||
}
|
}
|
||||||
return undefined;
|
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(
|
export function useLearningPathWithCompletion(
|
||||||
|
|
@ -178,6 +195,7 @@ export function useLearningPathWithCompletion(
|
||||||
|
|
||||||
const lpQueryResult = useLearningPathQuery(courseSlug);
|
const lpQueryResult = useLearningPathQuery(courseSlug);
|
||||||
const completionStore = useCompletionStore();
|
const completionStore = useCompletionStore();
|
||||||
|
const nextLearningContent = ref<LearningContentWithCompletion | null>(null);
|
||||||
|
|
||||||
function updateCompletionData() {
|
function updateCompletionData() {
|
||||||
if (userId && courseSessionId) {
|
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(
|
async function markCompletion(
|
||||||
|
|
@ -226,5 +251,10 @@ export function useLearningPathWithCompletion(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...lpQueryResult, updateCompletionData, markCompletion };
|
return {
|
||||||
|
...lpQueryResult,
|
||||||
|
updateCompletionData,
|
||||||
|
markCompletion,
|
||||||
|
nextLearningContent,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ const getNextStepLink = (courseSession: CourseSession) => {
|
||||||
<LearningPathDiagramSmall
|
<LearningPathDiagramSmall
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
:course-slug="courseSession.course.slug"
|
:course-slug="courseSession.course.slug"
|
||||||
|
:course-session-id="courseSession.id"
|
||||||
></LearningPathDiagramSmall>
|
></LearningPathDiagramSmall>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
|
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
|
||||||
import type { OldCircle } from "@/services/oldCircle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import type { LearningPathType } from "@/types";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPath: LearningPath | undefined;
|
learningPath: LearningPathType | undefined;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const topics = computed(() => props.learningPath?.topics ?? []);
|
const topics = computed(() => props.learningPath?.topics ?? []);
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,16 @@ import CircleProgress from "@/pages/learningPath/learningPathPage/LearningPathPr
|
||||||
import LearningPathTopics from "@/pages/learningPath/learningPathPage/LearningPathTopics.vue";
|
import LearningPathTopics from "@/pages/learningPath/learningPathPage/LearningPathTopics.vue";
|
||||||
import type { ViewType } from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
|
import type { ViewType } from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
|
||||||
import LearningPathViewSwitch 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 { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
|
||||||
import * as log from "loglevel";
|
import { computed, ref } from "vue";
|
||||||
import { computed, onMounted, ref } from "vue";
|
import { useLearningPathWithCompletion } from "@/composables";
|
||||||
|
import { someFinishedInLearningSequence } from "@/services/circle";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
courseSlug: string;
|
courseSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const breakpoints = useBreakpoints(breakpointsTailwind);
|
const breakpoints = useBreakpoints(breakpointsTailwind);
|
||||||
const learningPathStore = useLearningPathStore();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
|
|
||||||
// Layout state
|
// Layout state
|
||||||
const useMobileLayout = breakpoints.smaller("sm");
|
const useMobileLayout = breakpoints.smaller("sm");
|
||||||
|
|
@ -26,38 +23,20 @@ const selectedView = ref<ViewType>(
|
||||||
(window.localStorage.getItem("learningPathView") as ViewType) || "path"
|
(window.localStorage.getItem("learningPathView") as ViewType) || "path"
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(async () => {
|
const lpQueryResult = useLearningPathWithCompletion(props.courseSlug);
|
||||||
log.debug("LearningPathPage mounted");
|
|
||||||
|
|
||||||
try {
|
const learningPath = computed(() => lpQueryResult.learningPath.value);
|
||||||
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 circlesCount = computed(() => {
|
const circlesCount = computed(() => {
|
||||||
if (learningPath.value) {
|
return lpQueryResult.circles.value?.length ?? 0;
|
||||||
return learningPath.value.circles.length;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const inProgressCirclesCount = computed(() => {
|
const inProgressCirclesCount = computed(() => {
|
||||||
if (learningPath.value) {
|
if (lpQueryResult.circles.value?.length) {
|
||||||
return learningPath.value.circles.filter(
|
return lpQueryResult.circles.value.filter(
|
||||||
(circle) =>
|
(circle) =>
|
||||||
circle.learningSequences.filter((ls) =>
|
circle.learning_sequences.filter((ls) => someFinishedInLearningSequence(ls))
|
||||||
circle.someFinishedInLearningSequence(ls.translation_key)
|
.length
|
||||||
).length
|
|
||||||
).length;
|
).length;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
import LearningPathCircleColumn from "@/pages/learningPath/learningPathPage/LearningPathCircleColumn.vue";
|
import LearningPathCircleColumn from "@/pages/learningPath/learningPathPage/LearningPathCircleColumn.vue";
|
||||||
import LearningPathScrollButton from "@/pages/learningPath/learningPathPage/LearningPathScrollButton.vue";
|
import LearningPathScrollButton from "@/pages/learningPath/learningPathPage/LearningPathScrollButton.vue";
|
||||||
import type { OldCircle } from "@/services/oldCircle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
|
||||||
import { useScroll } from "@vueuse/core";
|
import { useScroll } from "@vueuse/core";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import type { LearningPathType } from "@/types";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPath: LearningPath | undefined;
|
learningPath: LearningPathType | undefined;
|
||||||
useMobileLayout: boolean;
|
useMobileLayout: boolean;
|
||||||
hideButtons?: boolean;
|
hideButtons?: boolean;
|
||||||
overrideCircleUrlBase?: string;
|
overrideCircleUrlBase?: string;
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,18 @@ import type {
|
||||||
CircleSectorData,
|
CircleSectorData,
|
||||||
CircleSectorProgress,
|
CircleSectorProgress,
|
||||||
} from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
} 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[] {
|
export function calculateCircleSectorData(circle: CircleType): CircleSectorData[] {
|
||||||
return circle.learningSequences.map((ls) => {
|
return circle.learning_sequences.map((ls) => {
|
||||||
let progress: CircleSectorProgress = "none";
|
let progress: CircleSectorProgress = "none";
|
||||||
if (circle.allFinishedInLearningSequence(ls.translation_key)) {
|
if (allFinishedInLearningSequence(ls)) {
|
||||||
progress = "finished";
|
progress = "finished";
|
||||||
} else if (circle.someFinishedInLearningSequence(ls.translation_key)) {
|
} else if (someFinishedInLearningSequence(ls)) {
|
||||||
progress = "in_progress";
|
progress = "in_progress";
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useLearningPathStore } from "@/stores/learningPath";
|
import { useLearningPathQuery } from "@/composables";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import type { CircleLight, CourseSessionUser, ExpertSessionUser } from "@/types";
|
import type { CircleLight, CourseSessionUser, ExpertSessionUser } from "@/types";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
|
|
@ -12,7 +12,6 @@ export type CockpitStoreState = {
|
||||||
courseSessionMembers: CourseSessionUser[] | undefined;
|
courseSessionMembers: CourseSessionUser[] | undefined;
|
||||||
circles: CircleCockpit[] | undefined;
|
circles: CircleCockpit[] | undefined;
|
||||||
currentCircle: CircleCockpit | undefined;
|
currentCircle: CircleCockpit | undefined;
|
||||||
currentCourseSlug: string | undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCockpitStore = defineStore({
|
export const useCockpitStore = defineStore({
|
||||||
|
|
@ -22,7 +21,6 @@ export const useCockpitStore = defineStore({
|
||||||
courseSessionMembers: undefined,
|
courseSessionMembers: undefined,
|
||||||
circles: [],
|
circles: [],
|
||||||
currentCircle: undefined,
|
currentCircle: undefined,
|
||||||
currentCourseSlug: undefined,
|
|
||||||
} as CockpitStoreState;
|
} as CockpitStoreState;
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
@ -31,21 +29,8 @@ export const useCockpitStore = defineStore({
|
||||||
currentCourseSessionUser: CourseSessionUser | undefined
|
currentCourseSessionUser: CourseSessionUser | undefined
|
||||||
) {
|
) {
|
||||||
log.debug("loadCircles called", courseSlug);
|
log.debug("loadCircles called", courseSlug);
|
||||||
this.currentCourseSlug = courseSlug;
|
|
||||||
|
|
||||||
const circles = await courseCircles(
|
this.circles = await courseCircles(courseSlug, currentCourseSessionUser);
|
||||||
this.currentCourseSlug,
|
|
||||||
currentCourseSessionUser
|
|
||||||
);
|
|
||||||
|
|
||||||
this.circles = circles.map((c) => {
|
|
||||||
return {
|
|
||||||
id: c.id,
|
|
||||||
slug: c.slug,
|
|
||||||
title: c.title,
|
|
||||||
name: c.title,
|
|
||||||
} as const;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.circles?.length) {
|
if (this.circles?.length) {
|
||||||
await this.setCurrentCourseCircle(this.circles[0].slug);
|
await this.setCurrentCourseCircle(this.circles[0].slug);
|
||||||
|
|
@ -64,28 +49,27 @@ async function courseCircles(
|
||||||
courseSlug: string,
|
courseSlug: string,
|
||||||
currentCourseSessionUser: CourseSessionUser | undefined
|
currentCourseSessionUser: CourseSessionUser | undefined
|
||||||
) {
|
) {
|
||||||
const userStore = useUserStore();
|
|
||||||
const userId = userStore.id;
|
|
||||||
|
|
||||||
if (currentCourseSessionUser && currentCourseSessionUser.role === "EXPERT") {
|
if (currentCourseSessionUser && currentCourseSessionUser.role === "EXPERT") {
|
||||||
const expert = currentCourseSessionUser as ExpertSessionUser;
|
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
|
// Return all circles from learning path for admin users
|
||||||
if (userStore.is_superuser) {
|
if (userStore.is_superuser) {
|
||||||
const learningPathStore = useLearningPathStore();
|
const lpQueryResult = useLearningPathQuery(courseSlug);
|
||||||
const learningPathCircles = learningPathStore
|
await lpQueryResult.waitForData();
|
||||||
.learningPathForUser(courseSlug, userId)
|
|
||||||
?.circles.map((c) => {
|
return (lpQueryResult.circles.value ?? []).map((c) => {
|
||||||
return {
|
return {
|
||||||
id: c.id,
|
id: c.id,
|
||||||
title: c.title,
|
|
||||||
slug: c.slug,
|
slug: c.slug,
|
||||||
translation_key: c.translation_key,
|
title: c.title,
|
||||||
};
|
name: c.title,
|
||||||
|
} as const;
|
||||||
});
|
});
|
||||||
return learningPathCircles || [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue