159 lines
4.4 KiB
Vue
159 lines
4.4 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from "vue";
|
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
import { useLearningPathStore } from "@/stores/learningPath";
|
|
import { useTranslation } from "i18next-vue";
|
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
|
import type { DueDate } from "@/types";
|
|
import DueDatesList from "@/components/dueDates/DueDatesList.vue";
|
|
|
|
const { t } = useTranslation();
|
|
|
|
const UNFILTERED = Number.MAX_SAFE_INTEGER;
|
|
const courseSessionsStore = useCourseSessionsStore();
|
|
const learningPathStore = useLearningPathStore();
|
|
|
|
type Item = {
|
|
id: number;
|
|
name: string;
|
|
};
|
|
|
|
const initialItemCircle: Item = {
|
|
id: UNFILTERED,
|
|
name: t("a.AlleCircle"),
|
|
};
|
|
|
|
const circles = ref<Item[]>([initialItemCircle]);
|
|
const courseSessions: Item[] = [
|
|
{
|
|
id: UNFILTERED,
|
|
name: t("a.AlleDurchführungen"),
|
|
},
|
|
...courseSessionsStore.allCourseSessions.map((cs) => ({ id: cs.id, name: cs.title })),
|
|
];
|
|
|
|
const selectedSession = ref<Item>(courseSessions[0]);
|
|
const selectedCircle = ref<Item>(circles.value[0]);
|
|
|
|
if (courseSessionsStore.currentCourseSession) {
|
|
selectedSession.value = {
|
|
id: courseSessionsStore.currentCourseSession.id,
|
|
name: courseSessionsStore.currentCourseSession.title,
|
|
};
|
|
}
|
|
|
|
watch(selectedSession, async () => {
|
|
// We don't have all circles for the unfiltered session,
|
|
// we fetch them from the learning path when the session changes
|
|
const isUnfiltered = selectedSession.value.id === UNFILTERED;
|
|
const courseSession = courseSessionsStore.allCourseSessions.find(
|
|
(item) => item.id === selectedSession.value.id
|
|
);
|
|
|
|
if (!courseSession || isUnfiltered) {
|
|
resetCircles();
|
|
return;
|
|
}
|
|
|
|
const data = await learningPathStore.loadLearningPath(
|
|
`${courseSession.course.slug}-lp`,
|
|
undefined,
|
|
false,
|
|
false
|
|
);
|
|
|
|
if (data) {
|
|
updateCircles(data.circles);
|
|
} else {
|
|
resetCircles();
|
|
}
|
|
});
|
|
|
|
const resetCircles = () => {
|
|
circles.value = [initialItemCircle];
|
|
selectedCircle.value = circles.value[0];
|
|
};
|
|
|
|
const updateCircles = (newCircles: { id: number; title: string }[]) => {
|
|
circles.value = [
|
|
initialItemCircle,
|
|
...newCircles.map((circle) => ({ id: circle.id, name: circle.title })),
|
|
];
|
|
selectedCircle.value = circles.value[0];
|
|
};
|
|
|
|
const appointments = computed(() => {
|
|
return courseSessionsStore
|
|
.allDueDates()
|
|
.filter((dueDate) => isMatchingSession(dueDate) && isMatchingCircle(dueDate));
|
|
});
|
|
|
|
const isMatchingSession = (dueDate: DueDate) =>
|
|
selectedSession.value.id === UNFILTERED ||
|
|
dueDate.course_session === selectedSession.value.id;
|
|
|
|
const isMatchingCircle = (dueDate: DueDate) =>
|
|
selectedCircle.value.id === UNFILTERED ||
|
|
dueDate.circle?.id === selectedCircle.value.id;
|
|
|
|
const numAppointmentsToShow = ref(7);
|
|
const canLoadMore = computed(() => {
|
|
return numAppointmentsToShow.value < appointments.value.length;
|
|
});
|
|
|
|
async function loadAdditionalAppointments() {
|
|
numAppointmentsToShow.value *= 2;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="bg-gray-200">
|
|
<div class="container-large px-8 py-8">
|
|
<header class="mb-6">
|
|
<h1>{{ $t("a.AlleTermine") }}</h1>
|
|
</header>
|
|
<main>
|
|
<div class="flex flex-col space-y-2">
|
|
<div class="flex flex-col space-x-0 bg-white lg:flex-row lg:space-x-3">
|
|
<ItDropdownSelect
|
|
v-model="selectedSession"
|
|
:items="courseSessions"
|
|
borderless
|
|
></ItDropdownSelect>
|
|
<template v-if="selectedSession.id !== UNFILTERED">
|
|
<ItDropdownSelect
|
|
v-model="selectedCircle"
|
|
:items="circles"
|
|
borderless
|
|
></ItDropdownSelect>
|
|
</template>
|
|
</div>
|
|
<div class="bg-white px-5">
|
|
<DueDatesList
|
|
:show-top-border="false"
|
|
:show-bottom-border="canLoadMore"
|
|
:due-dates="appointments"
|
|
:show-all-due-dates-link="false"
|
|
:max-count="numAppointmentsToShow"
|
|
/>
|
|
<button
|
|
v-if="canLoadMore"
|
|
class="py-4 underline"
|
|
data-cy="load-more-notifications"
|
|
@click="loadAdditionalAppointments()"
|
|
>
|
|
{{ $t("notifications.load_more") }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="postcss" scoped>
|
|
.no-border-last li:last-child {
|
|
border-bottom: none !important;
|
|
}
|
|
</style>
|