<template> <infinite-scrolling-date-sorted-c-r-u-d-iterator i18n-key="alsijil.coursebook" :gql-query="gqlQuery" :gql-additional-query-args="gqlQueryArgs" :enable-create="false" :enable-edit="false" :elevated="false" @lastQuery="lastQuery = $event" ref="iterator" fixed-header disable-pagination hide-default-footer use-deep-search > <template #additionalActions="{ attrs, on }"> <coursebook-filters v-model="filters" /> <absence-reason-buttons v-if="pageType === 'absences' && selectedParticipations.length" allow-empty empty-value="present" /> </template> <template #item="{ item, lastQuery }"> <component :is="itemComponent" :documentation="item" :affectedQuery="lastQuery" @select="addSelectedParticipation" @deselect="removeSelectedParticipation" /> </template> <template #loading> <coursebook-loader :number-of-days="10" :number-of-docs="5" /> </template> <template #itemLoader> <coursebook-loader /> </template> <template #no-data> <CoursebookEmptyMessage icon="mdi-book-off-outline"> {{ $t("alsijil.coursebook.no_data") }} </CoursebookEmptyMessage> </template> <template #no-results> <CoursebookEmptyMessage icon="mdi-book-alert-outline"> {{ $t("alsijil.coursebook.no_results", { search: $refs.iterator.search }) }} </CoursebookEmptyMessage> </template> </infinite-scrolling-date-sorted-c-r-u-d-iterator> </template> <script> import InfiniteScrollingDateSortedCRUDIterator from "aleksis.core/components/generic/InfiniteScrollingDateSortedCRUDIterator.vue"; import { DateTime, Interval } from "luxon"; import { documentationsForCoursebook } from "./coursebook.graphql"; import AbsenceReasonButtons from "aleksis.apps.kolego/components/AbsenceReasonButtons.vue"; import CoursebookFilters from "./CoursebookFilters.vue"; import CoursebookLoader from "./CoursebookLoader.vue"; import CoursebookEmptyMessage from "./CoursebookEmptyMessage.vue"; import DocumentationModal from "./documentation/DocumentationModal.vue"; import DocumentationAbsencesModal from "./absences/DocumentationAbsencesModal.vue"; export default { name: "Coursebook", components: { AbsenceReasonButtons, CoursebookEmptyMessage, CoursebookFilters, CoursebookLoader, DocumentationModal, DocumentationAbsencesModal, InfiniteScrollingDateSortedCRUDIterator, }, props: { filterType: { type: String, required: true, }, objId: { type: [Number, String], required: false, default: null, }, objType: { type: String, required: false, default: null, }, pageType: { type: String, required: false, default: "documentations", }, /** * Number of consecutive to load at once * This number of days is initially loaded and loaded * incrementally while scrolling. */ dayIncrement: { type: Number, required: false, default: 7, }, /** * Margin from coursebook list to top of viewport in pixels */ topMargin: { type: Number, required: false, default: 165, }, }, data() { return { gqlQuery: documentationsForCoursebook, lastQuery: null, dateStart: "", dateEnd: "", // Placeholder values while query isn't completed yet groups: [], courses: [], incomplete: false, ready: false, initDate: false, currentDate: "", hashUpdater: false, selectedParticipations: [], }; }, computed: { // Assertion: Should only fire on page load or selection change. // Resets date range. gqlQueryArgs() { return { own: this.filterType === "all" ? false : true, objId: this.objId ? Number(this.objId) : undefined, objType: this.objType?.toUpperCase(), dateStart: this.dateStart, dateEnd: this.dateEnd, incomplete: !!this.incomplete, }; }, filters: { get() { return { objType: this.objType, objId: this.objId, filterType: this.filterType, incomplete: this.incomplete, pageType: this.pageType, }; }, set(selectedFilters) { if (Object.hasOwn(selectedFilters, "incomplete")) { this.incomplete = selectedFilters.incomplete; } else if ( Object.hasOwn(selectedFilters, "filterType") || Object.hasOwn(selectedFilters, "objId") || Object.hasOwn(selectedFilters, "objType") || Object.hasOwn(selectedFilters, "pageType") ) { this.$router.push({ name: "alsijil.coursebook", params: { filterType: selectedFilters.filterType ? selectedFilters.filterType : this.filterType, objType: selectedFilters.objType, objId: selectedFilters.objId, pageType: selectedFilters.pageType ? selectedFilters.pageType : this.pageType, }, hash: this.$route.hash, }); // computed should not have side effects // but this was actually done before filters was refactored into // its own component this.$refs.iterator.resetDate(); // might skip query until both set = atomic } }, }, itemComponent() { if (this.pageType === 'documentations') { return "DocumentationModal"; } else { return "DocumentationAbsencesModal"; } }, }, methods: { addSelectedParticipation(participation) { this.selectedParticipations = [...new Set([...this.selectedParticipations, participation])]; }, removeSelectedParticipation(participation) { const index = this.selectedParticipations.indexOf(participation); if (index>=0) { this.selectedParticipations.splice(index, 1); } } }, }; </script> <style> .max-width { max-width: 25rem; } </style>