167 lines
5.8 KiB
Vue
167 lines
5.8 KiB
Vue
<script setup lang="ts">
|
|
import LearningPathListView from "@/pages/learningPath/learningPathPage/LearningPathListView.vue";
|
|
import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue";
|
|
import CircleProgress from "@/pages/learningPath/learningPathPage/LearningPathProgress.vue";
|
|
import LearningPathTopics from "@/pages/learningPath/learningPathPage/LearningPathTopics.vue";
|
|
import LearningPathProfileFilter from "@/pages/learningPath/learningPathPage/LearningPathProfileFilter.vue";
|
|
import type { ViewType } from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
|
|
import LearningPathViewSwitch from "@/pages/learningPath/learningPathPage/LearningPathViewSwitch.vue";
|
|
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
|
|
import { computed, ref } from "vue";
|
|
import {
|
|
useCourseCircleProgress,
|
|
useCourseDataWithCompletion,
|
|
useCurrentCourseSession,
|
|
} from "@/composables";
|
|
import CourseSessionDueDatesList from "@/components/dueDates/CourseSessionDueDatesList.vue";
|
|
import { useMutation, useQuery } from "@urql/vue";
|
|
import { UPDATE_COURSE_PROFILE_MUTATION } from "@/graphql/mutations";
|
|
import { COURSE_QUERY } from "@/graphql/queries";
|
|
|
|
const props = defineProps<{
|
|
courseSlug: string;
|
|
}>();
|
|
|
|
const breakpoints = useBreakpoints(breakpointsTailwind);
|
|
|
|
// Layout state
|
|
const useMobileLayout = breakpoints.smaller("sm");
|
|
const selectedView = ref<ViewType>(
|
|
(window.localStorage.getItem("learningPathView") as ViewType) || "path"
|
|
);
|
|
|
|
const lpQueryResult = useCourseDataWithCompletion(props.courseSlug);
|
|
|
|
const learningPath = computed(() => lpQueryResult.learningPath.value);
|
|
const course = computed(() => lpQueryResult.course.value);
|
|
|
|
const courseSession = useCurrentCourseSession();
|
|
|
|
// todo: we do not use the courseStore, because the returned object has lost its reactivity and does not reflect cache changes. maybe this could be fixed in there, then we can use the same object here
|
|
// todo: this could maybe go somewhere else, but useQuery must be used inside of a setup function. is there a better place?
|
|
const courseReactiveResult = useQuery({
|
|
query: COURSE_QUERY,
|
|
variables: { slug: props.courseSlug },
|
|
});
|
|
const courseReactive = computed(() => courseReactiveResult.data.value?.course);
|
|
const courseSessionUser = computed(() => {
|
|
return courseReactive.value?.course_session_users.find(
|
|
(e) => e?.course_session.id === courseSession.value.id
|
|
);
|
|
});
|
|
const filter = computed(() => {
|
|
return courseSessionUser.value?.chosen_profile || "";
|
|
});
|
|
|
|
const { inProgressCirclesCount, circlesCount } = useCourseCircleProgress(
|
|
lpQueryResult.circles
|
|
);
|
|
|
|
const updateCourseProfileMutation = useMutation(UPDATE_COURSE_PROFILE_MUTATION);
|
|
|
|
const updateCourseProfile = (profile: string) => {
|
|
updateCourseProfileMutation.executeMutation({
|
|
input: {
|
|
course_profile: profile,
|
|
course_slug: props.courseSlug,
|
|
},
|
|
});
|
|
};
|
|
|
|
const changeViewType = (viewType: ViewType) => {
|
|
selectedView.value = viewType;
|
|
window.localStorage.setItem("learningPathView", viewType);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex flex-col">
|
|
<!-- Top -->
|
|
<div class="flex flex-row justify-between space-x-8 bg-gray-200 p-6 sm:p-12">
|
|
<!-- Left -->
|
|
<div class="flex flex-col justify-between lg:w-1/2">
|
|
<div>
|
|
<p class="font-bold">
|
|
{{ $t("learningPathPage.welcomeBack") }}
|
|
</p>
|
|
<h2 data-cy="learning-path-title">
|
|
{{ course?.title }}
|
|
</h2>
|
|
</div>
|
|
|
|
<CircleProgress
|
|
:all-count="circlesCount"
|
|
:in-progress-count="inProgressCirclesCount"
|
|
></CircleProgress>
|
|
</div>
|
|
|
|
<!-- todo: find out when to display CourseSessionDueDatesList -->
|
|
<div v-if="!useMobileLayout && false" class="flex-grow">
|
|
<CourseSessionDueDatesList
|
|
:course-session-id="courseSession.id"
|
|
:max-count="2"
|
|
></CourseSessionDueDatesList>
|
|
</div>
|
|
<!-- Right -->
|
|
<LearningPathProfileFilter
|
|
v-if="!useMobileLayout && course?.configuration.is_vv"
|
|
:profiles="course?.profiles"
|
|
:selected="filter"
|
|
@select="updateCourseProfile"
|
|
/>
|
|
</div>
|
|
<!-- Bottom -->
|
|
<div class="bg-white">
|
|
<div v-if="lpQueryResult.learningPath">
|
|
<div class="flex flex-col justify-between px-6 sm:flex-row sm:px-12">
|
|
<!-- Topics -->
|
|
<div
|
|
v-if="selectedView == 'path'"
|
|
class="order-2 pb-8 sm:order-1 sm:pb-0 sm:pt-4"
|
|
>
|
|
<LearningPathTopics
|
|
:topics="learningPath?.topics ?? []"
|
|
></LearningPathTopics>
|
|
</div>
|
|
<div v-else class="flex-grow"></div>
|
|
|
|
<!-- View switch -->
|
|
<LearningPathViewSwitch
|
|
class="order-1 py-8 sm:order-2 sm:py-0 sm:pt-4"
|
|
:initial-view="selectedView"
|
|
@select-view="changeViewType($event)"
|
|
></LearningPathViewSwitch>
|
|
</div>
|
|
|
|
<!-- Path view -->
|
|
<div v-if="selectedView == 'path'" class="flex flex-col" data-cy="lp-path-view">
|
|
<LearningPathPathView
|
|
:learning-path="learningPath"
|
|
:use-mobile-layout="useMobileLayout"
|
|
:filter="filter"
|
|
:next-learning-content="lpQueryResult.nextLearningContent.value"
|
|
></LearningPathPathView>
|
|
</div>
|
|
|
|
<!-- List view -->
|
|
<div
|
|
v-if="selectedView == 'list'"
|
|
class="flex flex-col pl-6 sm:pl-24"
|
|
data-cy="lp-list-view"
|
|
>
|
|
<LearningPathListView
|
|
:learning-path="learningPath"
|
|
:next-learning-content="lpQueryResult.nextLearningContent.value"
|
|
:filter="filter"
|
|
></LearningPathListView>
|
|
</div>
|
|
<div
|
|
v-if="useMobileLayout"
|
|
class="p-6"
|
|
:class="useMobileLayout ? 'bg-gray-200' : ''"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|