Add filter to learning path list view, refactor code

This commit is contained in:
Ramon Wenger 2024-07-17 16:28:58 +02:00 committed by Christian Cueni
parent 770dbc94ea
commit 1d2224e941
6 changed files with 123 additions and 62 deletions

View File

@ -0,0 +1,37 @@
<script lang="ts" setup>
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
import type { LearningContentWithCompletion, TopicType } from '@/types';
import { computed } from "vue";
interface Props {
topic: TopicType;
nextLearningContent?: LearningContentWithCompletion;
filter?: string;
}
const props = defineProps<Props>();
const filteredCircles = computed(() => {
if (props.filter === undefined || props.filter === "") {
return props.topic.circles;
}
return props.topic.circles.filter(
(circle) =>
circle.profiles.indexOf(props.filter as string) > -1 || circle.is_base_circle
);
});
</script>
<template>
<div>
<div class="pb-2 font-bold text-gray-700">
{{ topic.title }}
</div>
<LearningPathCircleListTile
v-for="circle in filteredCircles"
:key="circle.id"
:circle="circle"
:next-learning-content="nextLearningContent"
></LearningPathCircleListTile>
</div>
</template>

View File

@ -1,26 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
import { computed } from "vue"; import { computed } from "vue";
import type { LearningContentWithCompletion, LearningPathType } from "@/types"; import type { LearningContentWithCompletion, LearningPathType } from "@/types";
import LearningPathListTopic from "./LearningPathListTopic.vue";
const props = defineProps<{ const props = defineProps<{
learningPath: LearningPathType | undefined; learningPath: LearningPathType | undefined;
nextLearningContent: LearningContentWithCompletion | undefined; nextLearningContent: LearningContentWithCompletion | undefined;
filter?: string;
}>(); }>();
const topics = computed(() => props.learningPath?.topics ?? []); const topics = computed(() => props.learningPath?.topics ?? []);
</script> </script>
<template> <template>
<div v-for="topic in topics" :key="topic.title"> <LearningPathListTopic
<div class="pb-2 font-bold text-gray-700"> v-for="topic in topics"
{{ topic.title }} :key="topic.title"
</div> :topic="topic"
<LearningPathCircleListTile :next-learning-content="nextLearningContent"
v-for="circle in topic.circles" :filter="filter"
:key="circle.id" />
:circle="circle"
:next-learning-content="props.nextLearningContent"
></LearningPathCircleListTile>
</div>
</template> </template>

View File

@ -152,6 +152,7 @@ const changeViewType = (viewType: ViewType) => {
<LearningPathListView <LearningPathListView
:learning-path="learningPath" :learning-path="learningPath"
:next-learning-content="lpQueryResult.nextLearningContent.value" :next-learning-content="lpQueryResult.nextLearningContent.value"
:filter="filter"
></LearningPathListView> ></LearningPathListView>
</div> </div>
<div <div

View File

@ -0,0 +1,63 @@
<script setup lang="ts">
import {computed} from "vue";
import type { LearningContentWithCompletion, TopicType } from '@/types';
import LearningPathCircleColumn from './LearningPathCircleColumn.vue';
interface Props {
topic: TopicType;
topicIndex: number;
nextLearningContent?: LearningContentWithCompletion;
overrideCircleUrlBase?: string;
filter?: string;
isLastTopic: boolean;
}
const props = defineProps<Props>();
const isFirstCircle = (circleIndex: number) =>
props.topicIndex === 0 && circleIndex === 0;
const isLastCircle = (circleIndex: number, numCircles: number) =>
props.isLastTopic && circleIndex === numCircles - 1;
const filteredCircles = computed(() => {
if (props.filter === undefined || props.filter === "") {
return props.topic.circles;
}
return props.topic.circles.filter(
(circle) =>
circle.profiles.indexOf(props.filter as string) > -1 || circle.is_base_circle
);
});
</script>
<template>
<div
class="border-l border-gray-500"
:class="topicIndex == 0 ? 'ml-6 sm:ml-12' : ''"
>
<p
:id="`topic-${topic.slug}`"
class="inline-block h-12 self-start px-4 font-bold text-gray-800"
data-cy="lp-topic"
>
{{ topic.title }}
</p>
<div class="flex flex-row pt-6">
<LearningPathCircleColumn
v-for="(circle, circleIndex) in filteredCircles"
:key="circle.id"
:circle="circle"
:next-learning-content="nextLearningContent"
:is-first-circle="isFirstCircle(circleIndex)"
:is-last-circle="
isLastCircle(circleIndex, topic.circles.length)
"
:override-circle-url="
overrideCircleUrlBase ? `${overrideCircleUrlBase}/${circle.slug}` : undefined
"
></LearningPathCircleColumn>
</div>
</div>
</template>

View File

@ -2,13 +2,13 @@
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 { useScroll } from "@vueuse/core"; import { useScroll } from "@vueuse/core";
import { nextTick, ref, watch } from "vue"; import { computed, nextTick, ref, watch } from "vue";
import type { import type {
LearningContentWithCompletion, LearningContentWithCompletion,
LearningPathType, LearningPathType,
TopicType,
} from "@/types"; } from "@/types";
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue"; import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
import LearningPathPathTopic from "./LearningPathPathTopic.vue";
const props = defineProps<{ const props = defineProps<{
learningPath: LearningPathType | undefined; learningPath: LearningPathType | undefined;
@ -24,13 +24,6 @@ const scrollIncrement = 600;
const learnPathDiagram = ref<HTMLElement | null>(null); const learnPathDiagram = ref<HTMLElement | null>(null);
const { x, arrivedState } = useScroll(learnPathDiagram, { behavior: "smooth" }); const { x, arrivedState } = useScroll(learnPathDiagram, { behavior: "smooth" });
const isFirstCircle = (topicIndex: number, circleIndex: number) =>
topicIndex === 0 && circleIndex === 0;
const isLastCircle = (topicIndex: number, circleIndex: number, numCircles: number) =>
topicIndex === (props.learningPath?.topics ?? []).length - 1 &&
circleIndex === numCircles - 1;
const scrollRight = () => scrollLearnPathDiagram(scrollIncrement); const scrollRight = () => scrollLearnPathDiagram(scrollIncrement);
const scrollLeft = () => scrollLearnPathDiagram(-scrollIncrement); const scrollLeft = () => scrollLearnPathDiagram(-scrollIncrement);
@ -39,15 +32,7 @@ const scrollLearnPathDiagram = (offset: number) => {
x.value += offset; x.value += offset;
}; };
const filterCircles = (topic: TopicType) => { const topics = computed(() => props.learningPath?.topics ?? []);
if (props.filter === undefined || props.filter === "") {
return topic.circles;
}
return topic.circles.filter(
(circle) =>
circle.profiles.indexOf(props.filter as string) > -1 || circle.is_base_circle
);
};
watch(()=> props.filter, () => { watch(()=> props.filter, () => {
// we need to update the scroll state of the element, otherwise the arrows won't match the scroll state // we need to update the scroll state of the element, otherwise the arrows won't match the scroll state
@ -76,37 +61,16 @@ watch(()=> props.filter, () => {
ref="learnPathDiagram" ref="learnPathDiagram"
class="no-scrollbar flex h-96 snap-x flex-row overflow-auto py-5 sm:py-10" class="no-scrollbar flex h-96 snap-x flex-row overflow-auto py-5 sm:py-10"
> >
<div <LearningPathPathTopic
v-for="(topic, topicIndex) in props.learningPath?.topics ?? []" v-for="(topic, topicIndex) in props.learningPath?.topics ?? []"
:key="topic.title" :key="topic.title"
class="border-l border-gray-500" :topic-index="topicIndex"
:class="topicIndex == 0 ? 'ml-6 sm:ml-12' : ''" :topic="topic"
> :next-learning-content="nextLearningContent"
<p :override-circle-url-base="overrideCircleUrlBase"
:id="`topic-${topic.slug}`" :filter="filter"
class="inline-block h-12 self-start px-4 font-bold text-gray-800" :is-last-topic="topicIndex === (topics.length - 1)"
data-cy="lp-topic" />
>
{{ topic.title }}
</p>
<div class="flex flex-row pt-6">
<LearningPathCircleColumn
v-for="(circle, circleIndex) in filterCircles(topic)"
:key="circle.id"
:circle="circle"
:next-learning-content="props.nextLearningContent"
:is-first-circle="isFirstCircle(topicIndex, circleIndex)"
:is-last-circle="
isLastCircle(topicIndex, circleIndex, topic.circles.length)
"
:override-circle-url="
props.overrideCircleUrlBase
? `${props.overrideCircleUrlBase}/${circle.slug}`
: undefined
"
></LearningPathCircleColumn>
</div>
</div>
</div> </div>
<LearningPathScrollButton <LearningPathScrollButton
v-show="!arrivedState.right" v-show="!arrivedState.right"

View File

@ -93,7 +93,7 @@ textarea {
} }
.link { .link {
@apply underline underline-offset-2; @apply underline underline-offset-2 cursor-pointer;
} }
.link-large { .link-large {
@ -117,8 +117,7 @@ textarea {
} }
.filter-blue-900 { .filter-blue-900 {
filter: invert(9%) sepia(38%) saturate(5684%) hue-rotate(200deg) brightness(95%) filter: invert(9%) sepia(38%) saturate(5684%) hue-rotate(200deg) brightness(95%) contrast(105%);
contrast(105%);
} }
} }