Skip to content
Snippets Groups Projects
Commit f39d2a2a authored by robinwilliam.hundt's avatar robinwilliam.hundt
Browse files

Type safe FeedbackTable components

parent 42bbbd7a
No related branches found
No related tags found
1 merge request!119Typesafe store
...@@ -18,9 +18,11 @@ ...@@ -18,9 +18,11 @@
</v-layout> </v-layout>
</template> </template>
<script> <script lang="ts">
export default { import {Vue, Component} from 'vue-property-decorator'
name: 'feedback-list-help-card'
@Component
export default class FeedbackListHelpCard extends Vue {
} }
</script> </script>
......
...@@ -60,66 +60,57 @@ ...@@ -60,66 +60,57 @@
</v-layout> </v-layout>
</template> </template>
<script> <script lang="ts">
import {Vue, Component, Prop} from 'vue-property-decorator'
import {mapState, mapGetters} from 'vuex' 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 {mapStateToComputedGetterSetter} from '@/util/helpers'
import {Authentication} from '@/store/modules/authentication' import {Authentication} from '@/store/modules/authentication'
import { actions } from '@/store/actions' import { actions } from '@/store/actions'
import { getters } from '@/store/getters';
export default { @Component
name: 'feedback-search-options', export default class FeedbackSearchOptions extends Vue {
data () { feedbackStages = ['Initial feedback', 'Validation']
return {
feedbackStages: ['Initial feedback', 'Validation'] get tutors () { return getters.state.tutors }
}
}, get isReviewer () { return Authentication.isReviewer }
computed: { get tutorNames () {
...mapState([ return this.tutors.map(tutor => tutor.username)
'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
}
] get showFinal () { return SearchOptions.state.showFinal}
}) get searchOtherUserComments () { return SearchOptions.state.searchOtherUserComments}
}, get caseSensitive () { return SearchOptions.state.caseSensitive}
methods: { get useRegex () { return SearchOptions.state.useRegex}
loadTutors () { get filterByTutors () { return SearchOptions.state.filterByTutors}
if (this.tutors.length === 0 && this.isReviewer) { get filterByStage () { return SearchOptions.state.filterByStage}
actions.getTutors()
} 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 () { created () {
this.loadTutors() this.loadTutors()
} }
......
...@@ -37,117 +37,120 @@ ...@@ -37,117 +37,120 @@
</v-card> </v-card>
</template> </template>
<script> <script lang="ts">
import {mapState, mapGetters} from 'vuex' import {mapState, mapGetters} from 'vuex'
import {Component, Prop, Vue} from 'vue-property-decorator'
import {getObjectValueByPath} from '@/util/helpers' 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 { 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 { @Component({
computed: { components: {
showFinal () { return OptionsModule.state.showFinal }, FeedbackSearchOptions
searchOtherUserComments () { return OptionsModule.state.searchOtherUserComments }, }
caseSensitive () { return OptionsModule.state.caseSensitive }, })
useRegex () { return OptionsModule.state.useRegex }, export default class FeedbackTable extends Vue {
filterByTutors () { return OptionsModule.state.filterByTutors }, get showFinal () { return OptionsModule.state.showFinal }
filterByStage () { return OptionsModule.state.filterByStage }, 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 () { get feedback () {
return Object.values(FeedbackTable.state.feedbackHist).filter(feedback => { return Object.values(FeedbackModule.state.feedbackHist).filter(feedback => {
return this.checkFinal(feedback) && this.filterFeedbackByTutorStage(feedback) return this.checkFinal(feedback) && this.filterFeedbackByTutorStage(feedback)
}) })
} }
},
components: {FeedbackSearchOptions}, search = ''
name: 'feedback-table', prefetchWhenLessItems = 11
data () { headers = [
return { {
search: '', text: 'Type',
prefetchWhenLessItems: 11, align: 'left',
headers: [ value: 'ofSubmissionType'
{
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
}, },
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) { text: 'Final',
for (let item of items) { value: 'final'
if (!this.$store.state.submissions[item.ofSubmission]) { }
this.prefetchSubmission(item.ofSubmission) ]
}
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> </script>
......
import {Module} from 'vuex' import {Module} from 'vuex'
import {RootState} from '@/store/store' import {RootState} from '@/store/store'
import { getStoreBuilder } from 'vuex-typex' import { getStoreBuilder } from 'vuex-typex'
import { Subscription } from '@/models';
export const namespace = 'feedbackSearchOptions' export const namespace = 'feedbackSearchOptions'
...@@ -27,8 +28,8 @@ function initialState (): FeedbackSearchOptionsState { ...@@ -27,8 +28,8 @@ function initialState (): FeedbackSearchOptionsState {
const mb = getStoreBuilder<RootState>().module('FeedbackSearchOptions', initialState()) const mb = getStoreBuilder<RootState>().module('FeedbackSearchOptions', initialState())
const mapStageDisplayToApiString: {[index: string]: string} = { const mapStageDisplayToApiString: {[index: string]: string} = {
'Initial feedback': 'feedback-creation', 'Initial feedback': Subscription.FeedbackStageEnum.Creation,
'Validation': 'feedback-validation' 'Validation': Subscription.FeedbackStageEnum.Validation
} }
const stateGetter = mb.state() const stateGetter = mb.state()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment