vbv/client/src/pages/AppointmentsPage.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>