<template> <v-card> <v-card-title class="title"> <span v-if="isTutor">Your</span><span>All</span> feedback history <v-spacer/> <v-text-field append-icon="search" label="Search" single-line hide-details v-model="search" /> </v-card-title> <feedback-search-options class="mx-3"/> <v-data-table :headers="headers" :items="feedback" :search="search" :custom-filter="searchFunction" :filter="containsSearch" hide-actions > <template slot="items" slot-scope="props"> <tr @click="showSubmission(props.item.ofSubmission)" class="feedback-row" > <td>{{props.item.ofSubmissionType}}</td> <td>{{props.item.score}}</td> <td>{{new Date(props.item.created).toLocaleString()}}</td> <td> <v-icon v-if="props.item.isFinal">check</v-icon> <v-icon v-else>clear</v-icon> </td> </tr> </template> </v-data-table> </v-card> </template> <script lang="ts"> import Vue from 'vue' import { mapState, mapGetters } from 'vuex' import Component from 'vue-class-component' import { getObjectValueByPath } from '@/util/helpers' import FeedbackSearchOptions from '@/components/feedback_list/FeedbackSearchOptions.vue' import { FeedbackSearchOptions as OptionsModule } from '@/store/modules/feedback_list/feedback-search-options' import { FeedbackTable as FeedbackModule, FeedbackHistoryItem } from '@/store/modules/feedback_list/feedback-table' import { Subscription, Feedback } from '@/models' import { actions } from '@/store/actions' import { getters } from '@/store/getters' import { Authentication } from '../../store/modules/authentication'; @Component({ components: { FeedbackSearchOptions } }) export default class FeedbackTable extends Vue { get showFinal () { return OptionsModule.state.showFinal } get searchOtherUserComments () { return OptionsModule.state.searchOtherUserComments } get caseSensitive () { return OptionsModule.state.caseSensitive } get useRegex () { return OptionsModule.state.useRegex } get filterByTutors () { return OptionsModule.state.filterByTutors } get filterByStage () { return OptionsModule.state.filterByStage } get isTutor () { return Authentication.isTutor } get stageFilterString () { return OptionsModule.stageFilterString } get feedback () { return Object.values(FeedbackModule.state.feedbackHist).filter(feedback => { return this.checkFinal(feedback) && this.filterFeedbackByTutorStage(feedback) }) } search = '' prefetchWhenLessItems = 11 headers = [ { text: 'Type', align: 'left', value: 'ofSubmissionType' }, { text: 'score', value: 'score' }, { text: 'Created', value: 'created' }, { text: 'Final', value: 'final' } ] filterFeedbackByTutorStage (feedback: FeedbackHistoryItem) { if (!feedback.history) { // if feedback is not filtered by tutor or stage return true, otherwise false return this.filterByTutors.length === 0 } const associatedTutors = this.stageFilterString === 'all' ? Object.values(feedback.history).filter(histEntry => !!histEntry).map(histEntry => histEntry!.owner) : feedback.history[<Subscription.FeedbackStageEnum> this.stageFilterString] ? [feedback.history[<Subscription.FeedbackStageEnum> this.stageFilterString]!.owner] : [] return this.filterByTutors.length === 0 || associatedTutors.some(tutor => this.filterByTutors.includes(tutor)) } showSubmission (submissionPk: string) { this.$router.push(`/feedback/${submissionPk}`) } prefetchSubmission (submissionPk: string) { actions.getSubmissionFeedbackTest({ pk: submissionPk }) } prefetchFilteredItems (items: Feedback[]) { if (items.length < this.prefetchWhenLessItems) { for (let item of items) { if (item.ofSubmission && !getters.state.submissions[item.ofSubmission]) { this.prefetchSubmission(item.ofSubmission) } } } } containsSearch (val: {toString: () => string}, search: string | RegExp) { if (!val || typeof val === 'boolean') { return false } if (search instanceof RegExp) { return search.test(val.toString()) } if (this.caseSensitive) { return val.toString().indexOf(search) !== -1 } else { return val.toString().toLowerCase().indexOf(search.toLowerCase()) !== -1 } } checkFinal (feedback: Feedback) { return this.showFinal ? true : !feedback.isFinal } commentFilter (feedback: Feedback, search: string | RegExp, filter: any) { return Object.values(feedback.feedbackLines || {}).some(line => line.some(comment => { return filter(comment.text, search) })) } searchFunction (items: Feedback[], search: string, filter: any) { if (search.trim() === '') return items const props = this.headers.map(h => h.value) const searchVal = this.useRegex ? new RegExp(search, this.caseSensitive ? 'u' : 'iu') : search let filteredItems = items.filter(feedback => { return props.some(prop => filter(getObjectValueByPath(feedback, prop), searchVal) || this.commentFilter(feedback, searchVal, filter)) }) this.prefetchFilteredItems(filteredItems) return filteredItems } } </script> <style scoped> .feedback-row { cursor: pointer; } </style>