diff --git a/frontend/src/components/feedback_list/FeedbackListHelpCard.vue b/frontend/src/components/feedback_list/FeedbackListHelpCard.vue index 0bfd473796cd22077f66e6e559c53f24feaf66a1..6302a0d86d47cdf7ace60727afbee5ae207b8e76 100644 --- a/frontend/src/components/feedback_list/FeedbackListHelpCard.vue +++ b/frontend/src/components/feedback_list/FeedbackListHelpCard.vue @@ -18,9 +18,11 @@ </v-layout> </template> -<script> -export default { - name: 'feedback-list-help-card' +<script lang="ts"> +import {Vue, Component} from 'vue-property-decorator' + +@Component +export default class FeedbackListHelpCard extends Vue { } </script> diff --git a/frontend/src/components/feedback_list/FeedbackSearchOptions.vue b/frontend/src/components/feedback_list/FeedbackSearchOptions.vue index 7996bbdc78555986f3ba05afe41e54014f3cd8f9..439e9bae7a99fe27b4c338cc8f7bbfaffdb6ac38 100644 --- a/frontend/src/components/feedback_list/FeedbackSearchOptions.vue +++ b/frontend/src/components/feedback_list/FeedbackSearchOptions.vue @@ -60,66 +60,57 @@ </v-layout> </template> -<script> +<script lang="ts"> +import {Vue, Component, Prop} from 'vue-property-decorator' import {mapState, mapGetters} from 'vuex' -import {FeedbackSearchOptions} from '@/store/modules/feedback_list/feedback-search-options' +import {FeedbackSearchOptions as SearchOptions} from '@/store/modules/feedback_list/feedback-search-options' import {mapStateToComputedGetterSetter} from '@/util/helpers' import {Authentication} from '@/store/modules/authentication' import { actions } from '@/store/actions' +import { getters } from '@/store/getters'; -export default { - name: 'feedback-search-options', - data () { - return { - feedbackStages: ['Initial feedback', 'Validation'] - } - }, - computed: { - ...mapState([ - 'tutors' - ]), - isReviewer () { return Authentication.isReviewer }, - tutorNames () { - return this.tutors.map(tutor => tutor.username) - }, - ...mapStateToComputedGetterSetter({ - pathPrefix: 'FeedbackSearchOptions', - items: [ - { - name: 'showFinal', - mutation: FeedbackSearchOptions.SET_SHOW_FINAL - }, - { - name: 'searchOtherUserComments', - mutation: FeedbackSearchOptions.SET_SEARCH_OTHER_USER_COMMENTS - }, - { - name: 'caseSensitive', - mutation: FeedbackSearchOptions.SET_CASE_SENSITIVE - }, - { - name: 'useRegex', - mutation: FeedbackSearchOptions.SET_USE_REGEX - }, - { - name: 'filterByTutors', - mutation: FeedbackSearchOptions.SET_FILTER_BY_TUTORS - }, - { - name: 'filterByStage', - mutation: FeedbackSearchOptions.SET_FILTER_BY_STAGE - } +@Component +export default class FeedbackSearchOptions extends Vue { + feedbackStages = ['Initial feedback', 'Validation'] + + get tutors () { return getters.state.tutors } + + get isReviewer () { return Authentication.isReviewer } + get tutorNames () { + return this.tutors.map(tutor => tutor.username) + } - ] - }) - }, - methods: { - loadTutors () { - if (this.tutors.length === 0 && this.isReviewer) { - actions.getTutors() - } + get showFinal () { return SearchOptions.state.showFinal} + get searchOtherUserComments () { return SearchOptions.state.searchOtherUserComments} + get caseSensitive () { return SearchOptions.state.caseSensitive} + get useRegex () { return SearchOptions.state.useRegex} + get filterByTutors () { return SearchOptions.state.filterByTutors} + get filterByStage () { return SearchOptions.state.filterByStage} + + set showFinal (val) { + SearchOptions.SET_SHOW_FINAL(val) + } + set searchOtherUserComments (val) { + SearchOptions.SET_SEARCH_OTHER_USER_COMMENTS(val) + } + set caseSensitive (val) { + SearchOptions.SET_CASE_SENSITIVE(val) + } + set useRegex (val) { + SearchOptions.SET_USE_REGEX(val) + } + set filterByTutors (val) { + SearchOptions.SET_FILTER_BY_TUTORS(val) + } + set filterByStage (val) { + SearchOptions.SET_FILTER_BY_STAGE(val) + } + + loadTutors () { + if (this.tutors.length === 0 && this.isReviewer) { + actions.getTutors() } - }, + } created () { this.loadTutors() } diff --git a/frontend/src/components/feedback_list/FeedbackTable.vue b/frontend/src/components/feedback_list/FeedbackTable.vue index 1925c5f9532a881c0462699837c321a14b7088de..445173ef6233ac949e8b339de2356f707852c27e 100644 --- a/frontend/src/components/feedback_list/FeedbackTable.vue +++ b/frontend/src/components/feedback_list/FeedbackTable.vue @@ -37,117 +37,120 @@ </v-card> </template> -<script> +<script lang="ts"> import {mapState, mapGetters} from 'vuex' +import {Component, Prop, Vue} from 'vue-property-decorator' import {getObjectValueByPath} from '@/util/helpers' -import FeedbackSearchOptions from '@/components/feedback_list/FeedbackSearchOptions' +import FeedbackSearchOptions from '@/components/feedback_list/FeedbackSearchOptions.vue' import { FeedbackSearchOptions as OptionsModule } from '@/store/modules/feedback_list/feedback-search-options' -import { FeedbackTable } from '@/store/modules/feedback_list/feedback-table' +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'; -export default { - computed: { - showFinal () { return OptionsModule.state.showFinal }, - searchOtherUserComments () { return OptionsModule.state.searchOtherUserComments }, - caseSensitive () { return OptionsModule.state.caseSensitive }, - useRegex () { return OptionsModule.state.useRegex }, - filterByTutors () { return OptionsModule.state.filterByTutors }, - filterByStage () { return OptionsModule.state.filterByStage }, +@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 } - stageFilterString () { return OptionsModule.stageFilterString }, + get stageFilterString () { return OptionsModule.stageFilterString } - feedback () { - return Object.values(FeedbackTable.state.feedbackHist).filter(feedback => { - return this.checkFinal(feedback) && this.filterFeedbackByTutorStage(feedback) - }) - } - }, - components: {FeedbackSearchOptions}, - name: 'feedback-table', - data () { - return { - search: '', - prefetchWhenLessItems: 11, - headers: [ - { - text: 'Type', - align: 'left', - value: 'ofSubmissionType' - }, - { - text: 'score', - value: 'score' - }, - { - text: 'Created', - value: 'created' - }, - { - text: 'Final', - value: 'final' - } - ] - } - }, - methods: { - filterFeedbackByTutorStage (feedback) { - if (feedback.hasOwnProperty('history')) { - const associatedTutors = this.stageFilterString === 'all' - ? Object.values(feedback.history).map(histEntry => histEntry.owner) - : feedback.history[this.stageFilterString] ? [feedback.history[this.stageFilterString].owner] : [] - return this.filterByTutors.length === 0 || - associatedTutors.some(tutor => this.filterByTutors.includes(tutor)) - } - // if feedback is not filtered by tutor or stage return true, otherwise false - return this.filterByTutors.length === 0 + 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' }, - showSubmission (submissionPk) { - this.$router.push(`/feedback/${submissionPk}`) + { + text: 'score', + value: 'score' }, - prefetchSubmission (submissionPk) { - this.$store.dispatch('getSubmissionFeedbackTest', {pk: submissionPk}) + { + text: 'Created', + value: 'created' }, - prefetchFilteredItems (items) { - if (items.length < this.prefetchWhenLessItems) { - for (let item of items) { - if (!this.$store.state.submissions[item.ofSubmission]) { - this.prefetchSubmission(item.ofSubmission) - } + { + 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, search) { - 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) { - return this.showFinal ? true : !feedback.isFinal - }, - commentFilter (feedback, search, filter) { - return Object.values(feedback.feedbackLines).some(line => line.some(comment => { - return filter(comment.text, search) - })) - }, - searchFunction (items, search, filter) { - if (search.trim() === '') return items - const props = this.headers.map(h => h.value) - search = this.useRegex ? new RegExp(search, this.caseSensitive ? 'u' : 'iu') : search - let filteredItems = items.filter(feedback => { - return props.some(prop => filter(getObjectValueByPath(feedback, prop), search) || - this.commentFilter(feedback, search, filter)) - }) - this.prefetchFilteredItems(filteredItems) - return filteredItems } } + 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> diff --git a/frontend/src/store/modules/feedback_list/feedback-search-options.ts b/frontend/src/store/modules/feedback_list/feedback-search-options.ts index 6a69437c8c5e137d99096d8362e827cf1a76a9bc..b93895a5d4d55fb5b3fdc57c70e9f8feae5607cf 100644 --- a/frontend/src/store/modules/feedback_list/feedback-search-options.ts +++ b/frontend/src/store/modules/feedback_list/feedback-search-options.ts @@ -1,6 +1,7 @@ import {Module} from 'vuex' import {RootState} from '@/store/store' import { getStoreBuilder } from 'vuex-typex' +import { Subscription } from '@/models'; export const namespace = 'feedbackSearchOptions' @@ -27,8 +28,8 @@ function initialState (): FeedbackSearchOptionsState { const mb = getStoreBuilder<RootState>().module('FeedbackSearchOptions', initialState()) const mapStageDisplayToApiString: {[index: string]: string} = { - 'Initial feedback': 'feedback-creation', - 'Validation': 'feedback-validation' + 'Initial feedback': Subscription.FeedbackStageEnum.Creation, + 'Validation': Subscription.FeedbackStageEnum.Validation } const stateGetter = mb.state()