Merged develop into feature/bugfix-cockpit-link
This commit is contained in:
commit
e2ccc8ecb9
|
|
@ -13,6 +13,7 @@
|
|||
"@sentry/tracing": "^7.20.0",
|
||||
"@sentry/vue": "^7.20.0",
|
||||
"@urql/vue": "^1.0.2",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"d3": "^7.6.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"graphql": "^16.6.0",
|
||||
|
|
@ -3365,6 +3366,11 @@
|
|||
"integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
|
|
@ -3822,6 +3828,89 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core/node_modules/vue-demi": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared/node_modules/vue-demi": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
|
|
@ -14412,6 +14501,11 @@
|
|||
"integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/web-bluetooth": {
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
|
|
@ -14738,6 +14832,46 @@
|
|||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@vueuse/core": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"requires": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue-demi": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@vueuse/metadata": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ=="
|
||||
},
|
||||
"@vueuse/shared": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"requires": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue-demi": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"@sentry/tracing": "^7.20.0",
|
||||
"@sentry/vue": "^7.20.0",
|
||||
"@urql/vue": "^1.0.2",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"d3": "^7.6.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"graphql": "^16.6.0",
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ async function changeLocale(language: AvailableLanguages) {
|
|||
<MenuItem v-for="locale in SUPPORT_LOCALES" :key="locale" class="py-1">
|
||||
<button
|
||||
class=""
|
||||
@click="changeLocale(locale)"
|
||||
:data-cy="`language-selector-${locale}`"
|
||||
@click="changeLocale(locale)"
|
||||
>
|
||||
<span class="ml-2 inline">{{ $t(`language.${locale}`) }}</span>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { calculateCircleSectorData } from "@/components/learningPath/page/utils"
|
|||
import type { Circle } from "@/services/circle";
|
||||
import type { LearningPath } from "@/services/learningPath";
|
||||
import type { Topic } from "@/types";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
learningPath: LearningPath | undefined;
|
||||
|
|
@ -15,10 +16,29 @@ const props = defineProps<{
|
|||
isLastCircle: boolean;
|
||||
isCurrentCircle: boolean;
|
||||
}>();
|
||||
|
||||
const circleElement = ref<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.isCurrentCircle) {
|
||||
setTimeout(() => {
|
||||
circleElement?.value?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
inline: "center",
|
||||
block: "nearest",
|
||||
});
|
||||
}, 400);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-row items-center pb-2">
|
||||
<router-link
|
||||
:to="props.circle.frontend_url"
|
||||
:data-cy="`circle-${props.circle.title}`"
|
||||
class="flex flex-col items-center pb-6"
|
||||
>
|
||||
<div ref="circleElement" class="flex flex-row items-center pb-2">
|
||||
<div class="w-12">
|
||||
<hr v-if="!props.isFirstCircle" class="h-[1.6px] w-full border-0 bg-gray-500" />
|
||||
</div>
|
||||
|
|
@ -45,4 +65,5 @@ const props = defineProps<{
|
|||
:url="props.learningPath?.continueData?.url"
|
||||
></LearningPathContinueButton>
|
||||
</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
<script setup lang="ts">
|
||||
import LearningPathCircle from "@/components/learningPath/page/LearningPathCircle.vue";
|
||||
import LearningPathContinueButton from "@/components/learningPath/page/LearningPathContinueButton.vue";
|
||||
import { calculateCircleSectorData } from "@/components/learningPath/page/utils";
|
||||
import type { Circle } from "@/services/circle";
|
||||
import type { LearningPath } from "@/services/learningPath";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
learningPath: LearningPath | undefined;
|
||||
circle: Circle;
|
||||
isCurrentCircle: boolean;
|
||||
}>();
|
||||
|
||||
const circleElement = ref<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.isCurrentCircle) {
|
||||
setTimeout(() => {
|
||||
circleElement?.value?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
inline: "nearest",
|
||||
block: "center",
|
||||
});
|
||||
}, 400);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-link
|
||||
:key="props.circle.id"
|
||||
:to="props.circle.frontend_url"
|
||||
class="flex flex-row items-center pb-2"
|
||||
>
|
||||
<LearningPathCircle
|
||||
:sectors="calculateCircleSectorData(circle)"
|
||||
class="h-12 w-12"
|
||||
></LearningPathCircle>
|
||||
<div ref="circleElement" class="pl-3 font-bold text-blue-900">
|
||||
{{ props.circle.title }}
|
||||
</div>
|
||||
|
||||
<div v-if="isCurrentCircle" class="whitespace-nowrap pl-4">
|
||||
<LearningPathContinueButton
|
||||
:has-progress="!props.learningPath?.continueData?.has_no_progress"
|
||||
:url="props.learningPath?.continueData?.url"
|
||||
></LearningPathContinueButton>
|
||||
</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
|
@ -1,33 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import type { CircleSectorData } from "@/components/learningPath/page/LearningPathCircle.vue";
|
||||
import LearningPathCircle from "@/components/learningPath/page/LearningPathCircle.vue";
|
||||
import LearningPathContinueButton from "@/components/learningPath/page/LearningPathContinueButton.vue";
|
||||
import { calculateCircleSectorData } from "@/components/learningPath/page/utils";
|
||||
import LearningPathCircleListTile from "@/components/learningPath/page/LearningPathCircleListTile.vue";
|
||||
import type { Circle } from "@/services/circle";
|
||||
import type { LearningPath } from "@/services/learningPath";
|
||||
import { computed, onMounted } from "vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
learningPath: LearningPath | undefined;
|
||||
}>();
|
||||
|
||||
onMounted(() => {
|
||||
const initialCircle = props.learningPath?.nextLearningContent?.parentCircle;
|
||||
if (initialCircle) {
|
||||
scrollToCircle(initialCircle);
|
||||
}
|
||||
});
|
||||
|
||||
const topics = computed(() => props.learningPath?.topics ?? []);
|
||||
|
||||
const shouldContinueHere = (circle: Circle) =>
|
||||
const isCurrentCircle = (circle: Circle) =>
|
||||
props.learningPath?.nextLearningContent?.parentCircle === circle;
|
||||
|
||||
const scrollToCircle = (circle: Circle) => {
|
||||
const id = `circle-${circle.slug}`;
|
||||
const el = document.getElementById(id);
|
||||
el?.scrollIntoView({ behavior: "smooth", inline: "center", block: "end" });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -35,27 +20,12 @@ const scrollToCircle = (circle: Circle) => {
|
|||
<div class="pb-2 font-bold text-gray-700">
|
||||
{{ topic.title }}
|
||||
</div>
|
||||
<router-link
|
||||
<LearningPathCircleListTile
|
||||
v-for="circle in topic.circles"
|
||||
:id="`circle-${circle.slug}`"
|
||||
:key="circle.id"
|
||||
:to="circle.frontend_url"
|
||||
class="flex flex-row items-center pb-2"
|
||||
>
|
||||
<LearningPathCircle
|
||||
:sectors="calculateCircleSectorData(circle)"
|
||||
class="h-12 w-12"
|
||||
></LearningPathCircle>
|
||||
<div class="pl-3 font-bold text-blue-900">
|
||||
{{ circle.title }}
|
||||
</div>
|
||||
|
||||
<div v-if="shouldContinueHere(circle)" class="whitespace-nowrap pl-4">
|
||||
<LearningPathContinueButton
|
||||
:has-progress="!props.learningPath?.continueData?.has_no_progress"
|
||||
:url="props.learningPath?.continueData?.url"
|
||||
></LearningPathContinueButton>
|
||||
</div>
|
||||
</router-link>
|
||||
:learning-path="learningPath"
|
||||
:circle="circle"
|
||||
:is-current-circle="isCurrentCircle(circle)"
|
||||
></LearningPathCircleListTile>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,29 +4,18 @@ import LearningPathCircleColumn from "@/components/learningPath/page/LearningPat
|
|||
import LearningPathScrollButton from "@/components/learningPath/page/LearningPathScrollButton.vue";
|
||||
import type { Circle } from "@/services/circle";
|
||||
import type { LearningPath } from "@/services/learningPath";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useScroll } from "@vueuse/core";
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
learningPath: LearningPath | undefined;
|
||||
useMobileLayout: boolean;
|
||||
}>();
|
||||
|
||||
// Scroll bounds
|
||||
const isAtLeftEnd = ref(true);
|
||||
const isAtRightEnd = ref(false);
|
||||
|
||||
// Constants
|
||||
const scrollIncrement = 600;
|
||||
const learnPathDiagramId = "learnpath-diagram";
|
||||
|
||||
onMounted(async () => {
|
||||
watchScrollBounds();
|
||||
|
||||
const initialCircle = props.learningPath?.nextLearningContent?.parentCircle;
|
||||
if (initialCircle) {
|
||||
scrollToCircle(initialCircle);
|
||||
}
|
||||
});
|
||||
const learnPathDiagram = ref<HTMLElement | null>(null);
|
||||
const { x, arrivedState } = useScroll(learnPathDiagram, { behavior: "smooth" });
|
||||
|
||||
const isFirstCircle = (topicIndex: number, circleIndex: number) =>
|
||||
topicIndex === 0 && circleIndex === 0;
|
||||
|
|
@ -38,52 +27,26 @@ const isLastCircle = (topicIndex: number, circleIndex: number, numCircles: numbe
|
|||
const isCurrentCircle = (circle: Circle) =>
|
||||
props.learningPath?.nextLearningContent?.parentCircle === circle;
|
||||
|
||||
const watchScrollBounds = () => {
|
||||
// We need to monitor the scroll bounds of the diagram to hide the scroll buttons when we're at the end
|
||||
const el = document.getElementById(learnPathDiagramId);
|
||||
el?.addEventListener("scroll", () => recalculateScrollBounds());
|
||||
};
|
||||
|
||||
const recalculateScrollBounds = () => {
|
||||
const el = document.getElementById(learnPathDiagramId);
|
||||
const scrollLeft = el?.scrollLeft ?? 0;
|
||||
const scrollWidth = el?.scrollWidth ?? 0;
|
||||
isAtLeftEnd.value = scrollLeft === 0;
|
||||
isAtRightEnd.value = scrollLeft > scrollWidth - (el?.clientWidth ?? 0) - 1;
|
||||
};
|
||||
|
||||
const scrollRight = () => scrollLearnPathDiagram(scrollIncrement);
|
||||
|
||||
const scrollLeft = () => scrollLearnPathDiagram(-scrollIncrement);
|
||||
|
||||
const scrollLearnPathDiagram = (offset: number) => {
|
||||
const el = document.getElementById(learnPathDiagramId);
|
||||
const newScrollOffset = (el?.scrollLeft ?? 0) + offset;
|
||||
el?.scroll({
|
||||
left: newScrollOffset,
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
const scrollToCircle = (circle: Circle) => {
|
||||
const id = `circle-${circle.slug}`;
|
||||
const el = document.getElementById(id);
|
||||
el?.scrollIntoView({ behavior: "smooth", inline: "center", block: "nearest" });
|
||||
x.value += offset;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative flex flex-row items-center">
|
||||
<LearningPathScrollButton
|
||||
v-show="!isAtLeftEnd"
|
||||
v-show="!arrivedState.left"
|
||||
direction="left"
|
||||
:hidden="props.useMobileLayout"
|
||||
@click="scrollLeft"
|
||||
></LearningPathScrollButton>
|
||||
<div
|
||||
:id="learnPathDiagramId"
|
||||
class="flex h-96 snap-x flex-row overflow-auto py-5 sm:overflow-hidden sm:py-10"
|
||||
ref="learnPathDiagram"
|
||||
class="no-scrollbar flex h-96 snap-x flex-row overflow-auto py-5 sm:py-10"
|
||||
>
|
||||
<div
|
||||
v-for="(topic, topicIndex) in props.learningPath?.topics ?? []"
|
||||
|
|
@ -98,15 +61,9 @@ const scrollToCircle = (circle: Circle) => {
|
|||
{{ topic.title }}
|
||||
</p>
|
||||
<div class="flex flex-row pt-6">
|
||||
<router-link
|
||||
v-for="(circle, circleIndex) in topic.circles"
|
||||
:id="`circle-${circle.slug}`"
|
||||
:key="circle.id"
|
||||
:to="circle.frontend_url"
|
||||
:data-cy="`circle-${circle.title}`"
|
||||
class="flex flex-col items-center pb-6"
|
||||
>
|
||||
<LearningPathCircleColumn
|
||||
v-for="(circle, circleIndex) in topic.circles"
|
||||
:key="circle.id"
|
||||
:learning-path="learningPath"
|
||||
:circle="circle"
|
||||
:topic="topic"
|
||||
|
|
@ -116,12 +73,11 @@ const scrollToCircle = (circle: Circle) => {
|
|||
"
|
||||
:is-current-circle="isCurrentCircle(circle)"
|
||||
></LearningPathCircleColumn>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LearningPathScrollButton
|
||||
v-show="!isAtRightEnd"
|
||||
v-show="!arrivedState.right"
|
||||
direction="right"
|
||||
:hidden="props.useMobileLayout"
|
||||
@click="scrollRight"
|
||||
|
|
|
|||
|
|
@ -10,17 +10,19 @@ import type { ViewType } from "@/components/learningPath/page/LearningPathViewSw
|
|||
import LearningPathViewSwitch from "@/components/learningPath/page/LearningPathViewSwitch.vue";
|
||||
import { useLearningPathStore } from "@/stores/learningPath";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind);
|
||||
const learningPathStore = useLearningPathStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// Layout state
|
||||
const useMobileLayout = ref(false);
|
||||
const useMobileLayout = breakpoints.smaller("sm");
|
||||
const selectedView = ref<ViewType>(
|
||||
(window.localStorage.getItem("learningPathView") as ViewType) || "path"
|
||||
);
|
||||
|
|
@ -33,9 +35,6 @@ onMounted(async () => {
|
|||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
|
||||
// Detect if we're on a mobile layout
|
||||
watchLayoutChanges();
|
||||
});
|
||||
|
||||
const learningPath = computed(() => {
|
||||
|
|
@ -65,14 +64,6 @@ const inProgressCirclesCount = computed(() => {
|
|||
return 0;
|
||||
});
|
||||
|
||||
const watchLayoutChanges = () => {
|
||||
const mobileLayoutQuery = window.matchMedia("(min-width: 640px)");
|
||||
useMobileLayout.value = !mobileLayoutQuery.matches;
|
||||
mobileLayoutQuery.onchange = (event) => {
|
||||
useMobileLayout.value = !event.matches;
|
||||
};
|
||||
};
|
||||
|
||||
const changeViewType = (viewType: ViewType) => {
|
||||
selectedView.value = viewType;
|
||||
window.localStorage.setItem("learningPathView", viewType);
|
||||
|
|
|
|||
|
|
@ -11,20 +11,29 @@ export type LearningPathStoreState = {
|
|||
page: "INDEX" | "OVERVIEW";
|
||||
};
|
||||
|
||||
type LearningPathKey = string;
|
||||
|
||||
function getLearningPathKey(
|
||||
slug: string,
|
||||
userId: string | number | undefined
|
||||
): LearningPathKey {
|
||||
return `${slug}-${userId}`;
|
||||
}
|
||||
|
||||
export const useLearningPathStore = defineStore({
|
||||
id: "learningPath",
|
||||
state: () => {
|
||||
return {
|
||||
learningPaths: new Map<string, LearningPath>(),
|
||||
learningPaths: new Map<LearningPathKey, LearningPath>(),
|
||||
page: "INDEX",
|
||||
loading: false,
|
||||
} as LearningPathStoreState;
|
||||
},
|
||||
getters: {
|
||||
learningPathForUser: (state) => {
|
||||
return (courseSlug: string, userId: number | string) => {
|
||||
return (courseSlug: string, userId: string | number | undefined) => {
|
||||
if (state.learningPaths.size > 0) {
|
||||
const learningPathKey = `${courseSlug}-lp-${userId}`;
|
||||
const learningPathKey = getLearningPathKey(courseSlug, userId);
|
||||
return state.learningPaths.get(learningPathKey);
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +53,7 @@ export const useLearningPathStore = defineStore({
|
|||
userId = userStore.id;
|
||||
}
|
||||
|
||||
const key = `${slug}-${userId}`;
|
||||
const key = getLearningPathKey(slug, userId);
|
||||
|
||||
if (this.learningPaths.has(key) && !reload) {
|
||||
return this.learningPaths.get(key);
|
||||
|
|
|
|||
|
|
@ -133,4 +133,14 @@ svg {
|
|||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Chrome, Safari and Opera */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue