Skip to content
Snippets Groups Projects
Coursebook.vue 6.66 KiB
Newer Older
permcu's avatar
permcu committed
<template>
  <c-r-u-d-iterator
Hangzhi Yu's avatar
Hangzhi Yu committed
      i18n-key="alsijil.coursebook"
      :gql-query="gqlQuery"
      :gql-additional-query-args="gqlQueryArgs"
      :enable-create="false"
      :enable-edit="false"
      @lastQuery="lastQuery = $event"
      ref="iterator"
Hangzhi Yu's avatar
Hangzhi Yu committed
  >
    <template #additionalActions="{ attrs, on }">
      <v-autocomplete
          :items="selectable"
          item-text="name"
          clearable
          return-object
          filled
          dense
          hide-details
          :placeholder="$t('alsijil.coursebook.filter.filter_for_obj')"
          :loading="selectLoading"
          :value="currentObj"
          @input="changeSelection"
          @click:clear="changeSelection"
      />
      <v-switch
        :loading="selectLoading"
        :label="$t('alsijil.coursebook.filter.own')"
        :input-value="filterType === 'my'"
        @change="changeSelection({ filterType: $event ? 'my' : 'all', type: objType, id: objId })"
    <template #default="{ items }">
      <v-list-item v-for="day in groupDocsByDay(items)" two-line>
        <v-list-item-content>
          <v-list-item-title>{{ $d(day[0], "short") }}</v-list-item-title>
          <v-list max-width="100%">
            <v-list-item v-for="doc in day.slice(1)">
              <documentation-modal
                :documentation="doc"
                :affected-query="lastQuery"
              />
            </v-list-item>
          </v-list>
        </v-list-item-content>
      </v-list-item>
    </template>
    <template #loading>
      <v-list-item v-for="_ in 10">
        <v-list-item-content>
          <v-list-item-title>
            <v-skeleton-loader type="heading"/>
          </v-list-item-title>
          <v-list max-width="100%">
            <v-list-item v-for="_ in 5">
              <v-card class="my-2 full-width">
                <div class="full-width d-flex flex-column align-stretch flex-md-row">
                  <v-card-text>
                    <v-skeleton-loader
                      type="avatar, heading, chip"
                      class="d-flex full-width align-center gap"
                      height="100%"
                    />
                  </v-card-text>
                  <v-card-text>
                    <v-skeleton-loader
                      type="heading@2"
                      class="d-flex full-width align-center gap"
                      height="100%"
                    />
                  </v-card-text>
                  <v-card-text>
                    <v-skeleton-loader
                      type="chip@3"
                      class="d-flex full-width align-center justify-end gap"
                      height="100%"
                    />
                  </v-card-text>
                </div>
              </v-card>
            </v-list-item>
          </v-list>
        </v-list-item-content>
      </v-list-item>
    </template>

    <template #no-data>
      <v-list-item>
        <v-list-item-content class="d-flex justify-center align-center flex-column full-width">
          <div class="mb-4">
            <v-icon large color="primary">mdi-book-off-outline</v-icon>
          </div>
          <v-list-item-title>{{ $t("alsijil.coursebook.no_data") }}</v-list-item-title>
        </v-list-item-content>
      </v-list-item>
    </template>

    <template #no-results>
      <v-list-item>
        <v-list-item-content class="d-flex justify-center align-center flex-column full-width">
          <div class="mb-4">
            <v-icon large color="primary">mdi-book-alert-outline</v-icon>
          </div>
          <v-list-item-title>{{ $t("alsijil.coursebook.no_results", { search: $refs.iterator.search }) }}</v-list-item-title>
        </v-list-item-content>
      </v-list-item>
permcu's avatar
permcu committed
    </template>
  </c-r-u-d-iterator>
</template>

<script>
import CRUDIterator from "aleksis.core/components/generic/CRUDIterator.vue";
import DocumentationModal from "./documentation/DocumentationModal.vue";
import { DateTime } from "luxon";
import {
  groupsByOwner,
  coursesOfTeacher,
  documentationsForCoursebook,
} from "./coursebook.graphql";
permcu's avatar
permcu committed

export default {
  name: "Coursebook",
  components: {
    CRUDIterator,
    DocumentationModal,
permcu's avatar
permcu committed
  },
  props: {
    // Either as props OR route params
    // TODO: Remove default?
    filterType: {
      type: String,
      required: true,
    },
permcu's avatar
permcu committed
      type: [Number, String],
      required: false,
permcu's avatar
permcu committed
    // Next two in ISODate
    dateStart: {
      type: String,
      required: false,
      default: "",
    },
    dateEnd: {
      type: String,
      required: false,
      default: "",
    },
  },
  data() {
    return {
      gqlQuery: documentationsForCoursebook,
permcu's avatar
permcu committed
      lastQuery: null,
      // Placeholder values while query isn't completed yet
      groups: [],
      courses: [],
  apollo: {
    groups: {
      query: groupsByOwner,
    },
    courses: {
      query: coursesOfTeacher,
    },
  },
permcu's avatar
permcu committed
  computed: {
    gqlQueryArgs() {
      return {
        // Assure courseId is a number
        own: this.filterType === "all" ? false : true,
        objId: this.objId ? Number(this.objId) : null,
        objType: this.objType?.toUpperCase(),
permcu's avatar
permcu committed
        dateStart: this.dateStart,
        dateEnd: this.dateEnd,
      };
    },
    selectable() {
      return [
        { header: this.$t("alsijil.coursebook.filter.groups") },
        ...this.groups.map((group) => ({ type: "group", ...group })),
        { header: this.$t("alsijil.coursebook.filter.courses") },
        ...this.courses.map((course) => ({ type: "course", ...course })),
      ];
    },
    currentObj() {
      return this.selectable.find((o) => o.type === this.objType && o.id === this.objId);
    },
    selectLoading() {
      return this.$apollo.queries.groups.loading || this.$apollo.queries.courses.loading;
    }
    changeSelection(selection) {
      this.$router.push({
        name: "alsijil.coursebook_by_type_and_date",
          filterType: selection.filterType ? selection.filterType : this.filterType,
          objType: selection.type,
          objId: selection.id,
          dateStart: this.dateStart,
          dateEnd: this.dateEnd,
        },
      });
    },
    // => [[dt doc ...] ...]
Hangzhi Yu's avatar
Hangzhi Yu committed
      const byDay = docs.reduce((byDay, doc) => {
        // This works with dummy. Does actual doc have dateStart instead?
        const day = DateTime.fromISO(doc.datetimeStart).startOf("day");
        byDay[day] ??= [day];
        byDay[day].push(doc);
        return byDay;
      }, {});
Hangzhi Yu's avatar
Hangzhi Yu committed
      return Object.keys(byDay)
        .sort()
        .map((key) => byDay[key]);
permcu's avatar
permcu committed
};
</script>