Skip to content
Snippets Groups Projects
SubstitutionCard.vue 11.2 KiB
Newer Older
<script setup>
import DeleteButton from "aleksis.core/components/generic/buttons/DeleteButton.vue";
Hangzhi Yu's avatar
Hangzhi Yu committed
import SubstitutionInformation from "./SubstitutionInformation.vue";
import LessonRelatedObjectChip from "../LessonRelatedObjectChip.vue";
Hangzhi Yu's avatar
Hangzhi Yu committed
import TeacherField from "aleksis.apps.cursus/components/TeacherField.vue";
Hangzhi Yu's avatar
Hangzhi Yu committed
import SubjectField from "aleksis.apps.cursus/components/SubjectField.vue";
Hangzhi Yu's avatar
Hangzhi Yu committed
import { gqlRooms } from "../amendLesson.graphql";

import createOrPatchMixin from "aleksis.core/mixins/createOrPatchMixin.js";
</script>

<template>
  <v-card class="my-1 full-width" :loading="loading">
    <!-- flex-md-row zeile ab medium -->
    <!-- align-stretch - stretch full-width -->
Hangzhi Yu's avatar
Hangzhi Yu committed
    <v-card-text
      class="full-width main-body pa-2"
      :class="{
Hangzhi Yu's avatar
Hangzhi Yu committed
        vertical: $vuetify.breakpoint.mobile,
Hangzhi Yu's avatar
Hangzhi Yu committed
      }"
Hangzhi Yu's avatar
Hangzhi Yu committed
      <substitution-information :substitution="substitution" />
Hangzhi Yu's avatar
Hangzhi Yu committed
      <v-spacer />

      <v-text-field
        dense
        outlined
        hide-details
        :label="$t('chronos.substitutions.overview.comment')"
        :value="substitution.comment"
        @input="comment = $event"
        @focusout="save"
        @keydown.enter="save"
      />

Hangzhi Yu's avatar
Hangzhi Yu committed
      <subject-field
        :enable-create="false"
        dense
        outlined
        hide-details
        :value="subject"
        :disabled="loading"
        @input="subjectInput"
      />

      <v-autocomplete
        :value="substitutionRoomIDs"
        multiple
        chips
        deletable-chips
        dense
        hide-details
        outlined
        :items="amendableRooms"
        item-text="name"
        item-value="id"
        :disabled="loading"
        @input="roomsInput"
      >
        <template #prepend-inner>
          <v-chip
Hangzhi Yu's avatar
Hangzhi Yu committed
            v-if="roomsWithStatus.filter((t) => t.status === 'regular').length"
Hangzhi Yu's avatar
Hangzhi Yu committed
            v-for="room in roomsWithStatus.filter(
              (t) => t.status === 'regular',
            )"
Hangzhi Yu's avatar
Hangzhi Yu committed
            class="mb-1"
Hangzhi Yu's avatar
Hangzhi Yu committed
            small
          >
            {{ room.shortName }}
          </v-chip>
          <v-chip
Hangzhi Yu's avatar
Hangzhi Yu committed
            v-if="roomsWithStatus.filter((t) => t.status === 'removed').length"
            v-for="room in roomsWithStatus.filter(
              (t) => t.status === 'removed',
            )"
            outlined
            color="error"
Hangzhi Yu's avatar
Hangzhi Yu committed
            class="mb-1"
            small
          >
            <v-icon left>mdi-cancel</v-icon>
            <div class="text-decoration-line-through">
Hangzhi Yu's avatar
Hangzhi Yu committed
              {{ room.shortName ? room.shortName : room.name }}
            </div>
          </v-chip>
        </template>
        <template #selection="data">
          <v-chip
            v-bind="data.attrs"
            :input-value="data.selected"
Hangzhi Yu's avatar
Hangzhi Yu committed
            v-if="getRoomStatus(data.item) === 'new'"
Hangzhi Yu's avatar
Hangzhi Yu committed
            class="mb-1 mt-1"
Hangzhi Yu's avatar
Hangzhi Yu committed
            outlined
            color="success"
            @click:close="removeRoom(data.item)"
          >
Hangzhi Yu's avatar
Hangzhi Yu committed
            <v-icon left> mdi-plus-circle-outline </v-icon>
Hangzhi Yu's avatar
Hangzhi Yu committed
            {{ data.item.shortName ? data.item.shortName : data.item.name }}
          </v-chip>
        </template>
      </v-autocomplete>
Hangzhi Yu's avatar
Hangzhi Yu committed

Hangzhi Yu's avatar
Hangzhi Yu committed
      <teacher-field
        :priority-subject="subject"
        :show-subjects="true"
        :value="substitutionTeacherIDs"
        chips
        deletable-chips
        dense
        hide-details
        outlined
        @input="teachersInput"
      >
        <template #prepend-inner>
          <v-tooltip bottom>
            <template #activator="{ on, attrs }">
              <v-chip
                v-for="teacher in teachersWithStatus.filter(
                  (t) => t.status === 'removed',
                )"
                outlined
                color="error"
                class="mb-1"
                small
                v-on="on"
                v-bind="attrs"
              >
                <v-icon left>mdi-account-off-outline</v-icon>
                <div class="text-decoration-line-through">
                  {{ teacher.fullName }}
                </div>
              </v-chip>
            </template>
Hangzhi Yu's avatar
Hangzhi Yu committed
            <span>{{
              $t("chronos.substitutions.overview.teacher.status.absent")
            }}</span>
          </v-tooltip>
        </template>
        <template #selection="data">
          <v-tooltip bottom>
            <template #activator="{ on, attrs }">
              <v-chip
                v-bind="{ ...data.attrs, ...attrs }"
                :input-value="data.selected"
                close
                class="mb-1 mt-1"
                small
                :outlined="getTeacherStatus(data.item) === 'new'"
                :color="getTeacherStatus(data.item) === 'new' ? 'success' : ''"
                @click:close="removeTeacher(data.item)"
                v-on="on"
              >
                <v-icon left v-if="getTeacherStatus(data.item) === 'new'">
                  mdi-account-plus-outline
                </v-icon>
                {{ data.item.fullName }}
              </v-chip>
            </template>
Hangzhi Yu's avatar
Hangzhi Yu committed
            <span>{{
              getTeacherStatus(data.item) === "new"
                ? $t("chronos.substitutions.overview.teacher.status.new")
                : $t("chronos.substitutions.overview.teacher.status.regular")
            }}</span>
          </v-tooltip>
Hangzhi Yu's avatar
Hangzhi Yu committed
      </teacher-field>
      <v-chip-group
        column
        mandatory
        :disabled="loading"
        class="justify-self-end"
        :value="substitution.cancelled"
        @change="save"
      >
        <v-chip
          filter
          outlined
          color="success"
          :value="false"
          @click="cancelled = false"
        >
          {{ $t("chronos.substitutions.overview.cancel.not_cancelled") }}
        </v-chip>
        <v-chip
          filter
          outlined
          color="error"
          :value="true"
          @click="cancelled = true"
        >
          {{ $t("chronos.substitutions.overview.cancel.cancelled") }}
        </v-chip>
      </v-chip-group>
Hangzhi Yu's avatar
Hangzhi Yu committed
    </v-card-text>
  </v-card>
</template>

<script>
export default {
  name: "SubstitutionCard",
  emits: ["open", "close"],
  mixins: [createOrPatchMixin],
  data() {
    return {
      loading: false,
      teachers: [],
      rooms: [],
Hangzhi Yu's avatar
Hangzhi Yu committed
      substitutionSubject: null,
      comment: null,
      cancelled: null,
      type: Object,
      required: true,
    },
  },
    handleUpdateAfterCreateOrPatch(itemId) {
      return (cached, incoming) => {
        for (const object of incoming) {
          console.log("summary: handleUpdateAfterCreateOrPatch", object);
          // Replace the current substitution
          const index = cached.findIndex(
            (o) => o[itemId] === this.substitution.id,
          );
          // merged with the incoming partial substitution
          // if creation of proper substitution from dummy one, set ID of substitution currently being edited as oldID so that key in overview doesn't change
          cached[index] = {
            ...this.substitution,
            ...object,
            oldId:
              this.substitution.id !== object.id
                ? this.substitution.id
                : this.substitution.oldId,
          };
        }
        return cached;
      };
    },
    getTeacherStatus(teacher) {
      return this.teachersWithStatus.find((t) => t.id === teacher.id)?.status;
    },
    getRoomStatus(room) {
      return this.roomsWithStatus.find((r) => r.id === room.id)?.status;
    removeTeacher(teacher) {
      this.teachers = this.substitutionTeacherIDs.filter((t) => t !== teacher.id);
      this.save(true);
    },
    removeRoom(room) {
      this.rooms = this.substitutionRoomIDs.filter((r) => r !== room.id);
      this.save(true);
Hangzhi Yu's avatar
Hangzhi Yu committed
    subjectInput(subject) {
      this.substitutionSubject = subject.id;
      this.save();
    },
    teachersInput(teachers) {
      this.teachers = teachers;
      this.save();
Hangzhi Yu's avatar
Hangzhi Yu committed
    roomsInput(rooms) {
      this.rooms = rooms;
      this.save();
Hangzhi Yu's avatar
Hangzhi Yu committed
    },
Hangzhi Yu's avatar
Hangzhi Yu committed
    cancelledInput(cancelled) {
      this.cancelled = cancelled;
      this.save();
    },
    save(allowEmpty = false) {
        allowEmpty ||
        this.teachers.length ||
        this.rooms.length ||
Hangzhi Yu's avatar
Hangzhi Yu committed
        this.substitutionSubject !== null ||
        this.comment !== null ||
        this.cancelled !== null
      ) {
        this.createOrPatch([
          {
            id: this.substitution.id,
            ...((allowEmpty || this.teachers.length) && { teachers: this.teachers }),
            ...((allowEmpty || this.rooms.length) && { rooms: this.rooms }),
Hangzhi Yu's avatar
Hangzhi Yu committed
            ...(this.substitutionSubject !== null && { subject: this.substitutionSubject }),
            ...(this.comment !== null && { comment: this.comment }),
            ...(this.cancelled !== null && { cancelled: this.cancelled }),
        this.teachers = [];
        this.rooms = [];
Hangzhi Yu's avatar
Hangzhi Yu committed
        this.substitutionSubject = null;
        this.comment = null;
        this.cancelled = null;
  computed: {
    substitutionTeacherIDs() {
      return this.substitution.teachers.map((teacher) => teacher.id);
    },
    substitutionRoomIDs() {
      return this.substitution.rooms.map((room) => room.id);
    },
    teachersWithStatus() {
Hangzhi Yu's avatar
Hangzhi Yu committed
      const oldIds = this.substitution.amends.teachers.map(
        (teacher) => teacher.id,
      );
      const newIds = this.substitution.teachers.map((teacher) => teacher.id);
Hangzhi Yu's avatar
Hangzhi Yu committed
      const allTeachers = new Set(
        this.substitution.amends.teachers.concat(this.substitution.teachers),
      );
      let teachersWithStatus = Array.from(allTeachers).map((teacher) => {
        let status = "regular";
        if (newIds.includes(teacher.id) && !oldIds.includes(teacher.id)) {
          status = "new";
        } else if (
          !newIds.includes(teacher.id) &&
          oldIds.includes(teacher.id)
        ) {
          status = "removed";
        }
        return { ...teacher, status: status };
      });
      return teachersWithStatus;
    },
    roomsWithStatus() {
Hangzhi Yu's avatar
Hangzhi Yu committed
      const oldIds = this.substitution.amends.rooms.map((room) => room.id);
      const newIds = this.substitution.rooms.map((room) => room.id);
      const allRooms = new Set(
        this.substitution.amends.rooms.concat(this.substitution.rooms),
      );
      let roomsWithStatus = Array.from(allRooms).map((room) => {
        let status = "regular";
        if (newIds.includes(room.id) && !oldIds.includes(room.id)) {
          status = "new";
        } else if (
Hangzhi Yu's avatar
Hangzhi Yu committed
          newIds.length &&
          !newIds.includes(room.id) &&
          oldIds.includes(room.id)
        ) {
          status = "removed";
        }
        return { ...room, status: status };
      });
      return roomsWithStatus;
    },
Hangzhi Yu's avatar
Hangzhi Yu committed
    subject() {
      return this.substitution.subject
        ? this.substitution.subject
        : this.substitution.course?.subject
          ? this.substitution.course.subject
          : this.substitution.amends?.subject
            ? this.substitution.amends.subject
            : undefined;
    },
    amendableRooms: gqlRooms,
Hangzhi Yu's avatar
Hangzhi Yu committed

<style scoped>
.main-body {
  display: grid;
  align-items: center;
Hangzhi Yu's avatar
Hangzhi Yu committed
  grid-template-columns: 2fr 1fr 1fr 1fr 2fr 3fr 2fr;
Hangzhi Yu's avatar
Hangzhi Yu committed
  gap: 1em;
}
.vertical {
  grid-template-columns: 1fr;
}
.justify-self-end {
  justify-self: end;
}
Hangzhi Yu's avatar
Hangzhi Yu committed
</style>