Skip to content
Snippets Groups Projects
Commit 89ccc3f1 authored by Thilo Wischmeyer's avatar Thilo Wischmeyer Committed by Thilo Wischmeyer
Browse files

Rewrote feedback history search

parent b45d9fe8
No related branches found
No related tags found
1 merge request!247Resolve "Update Vuetify"
<template>
<v-row>
<v-col
md="6"
lg="3"
>
<v-checkbox
id="show-final-checkbox"
v-model="showFinal"
:ripple="false"
label="show final"
/>
</v-col>
<v-col
md="6"
lg="3"
>
<v-checkbox
v-model="searchOtherUserComments"
label="search all comments"
/>
</v-col>
<v-col
md="6"
lg="3"
>
<v-checkbox
v-model="caseSensitive"
label="case sensitive"
/>
</v-col>
<v-col
md="6"
lg="3"
>
<v-row>
<v-container class="pt-0">
<v-row>
<v-col class="py-0">
<v-text-field
v-model="model.searchString"
append-icon="search"
label="Search"
single-line
hide-details
class="py-0 my-0"
@input="$emit('input', model)"
/>
</v-col>
</v-row>
<v-row>
<v-col class="pt-0">
<v-checkbox
v-model="useRegex"
label="use RegEx"
style="max-width: 60%"
id="show-final-checkbox"
v-model="model.showFinal"
label="show final"
hide-details
@click="$emit('input', model)"
/>
<v-tooltip top>
<template #activator="{ on }">
<a
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#character-classes"
target="_blank"
v-on="on"
>
<v-icon>help</v-icon>
</a>
</template>
<span>Go to RegEx documentation</span>
</v-tooltip>
</v-row>
</v-col>
<v-container class="pa-0">
<v-row>
<v-col md="5">
<v-select
v-model="filterByLabels"
label="Label"
:items="labels"
multiple
hint="Filter by label"
persistent-hint
clearable
/>
</v-col>
<v-col
md="5"
offset-md="1"
>
<v-select
v-model="filterByExcludingLabels"
label="Exclude label"
:items="labels"
multiple
hint="Filter by excluding labels"
persistent-hint
clearable
/>
</v-col>
</v-row>
</v-container>
<v-container class="pa-0">
<v-row>
<v-col
v-if="isReviewer"
md="12"
lg="5"
>
<v-select
v-model="filterByTutors"
label="Tutors"
:items="tutorNames"
multiple
hint="Filter by tutors"
persistent-hint
clearable
/>
</v-col>
<v-col
v-if="filterByTutors.length > 0"
md="12"
lg="5"
offset-lg="1"
</v-col>
<v-col class="pt-0">
<v-checkbox
v-model="model.caseSensitive"
label="case sensitive"
hide-details
@click="$emit('input', model)"
/>
</v-col>
<v-col class="pt-0">
<v-checkbox
v-model="model.useRegex"
hide-details
@click="$emit('input', model)"
>
<v-select
v-model="filterByStage"
label="Stage"
:items="feedbackStages"
placeholder="All"
hint="Filter after initial feedback or validated"
persistent-hint
clearable
/>
</v-col>
</v-row>
</v-container>
</v-row>
<template #label>
use RegEx
<v-tooltip top>
<template #activator="{ on }">
<a
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#character-classes"
target="_blank"
v-on="on"
>
<v-icon>help</v-icon>
</a>
</template>
<span>Go to RegEx documentation</span>
</v-tooltip>
</template>
</v-checkbox>
</v-col>
</v-row>
<v-row>
<v-col>
<v-select
v-model="model.filterByLabels"
label="Label"
:items="labels"
return-object
item-text="name"
multiple
hint="Filter by label"
persistent-hint
clearable
@change="$emit('input', model)"
/>
</v-col>
<v-col>
<v-select
v-model="model.filterByExcludingLabels"
label="Exclude label"
:items="labels"
item-text="name"
return-object
multiple
hint="Filter by excluding labels"
persistent-hint
clearable
@change="$emit('input', model)"
/>
</v-col>
</v-row>
<v-row>
<v-col
v-if="isReviewer"
md="6"
>
<v-select
v-model="model.filterByTutors"
label="Tutors"
:items="tutors"
item-text="username"
return-object
multiple
hint="Filter by tutors"
persistent-hint
clearable
@change="$emit('input', model)"
/>
</v-col>
<v-col
v-if="model.filterByTutors.length > 0"
md="6"
>
<v-select
v-model="model.filterByStage"
label="Stage"
:items="feedbackStages"
placeholder="All"
hint="Filter after initial feedback or validated"
persistent-hint
clearable
@change="$emit('input', model)"
/>
</v-col>
</v-row>
</v-container>
</template>
<script lang="ts">
import Vue from 'vue'
import Vue, { PropType } from 'vue'
import Component from 'vue-class-component'
import { mapState, mapGetters } from 'vuex'
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'
import { TutorOverview } from '@/store/modules/tutor-overview'
import { FeedbackLabels } from '../../store/modules/feedback-labels'
@Component
export default class FeedbackSearchOptions extends Vue {
feedbackStages = ['Initial feedback', 'Validation']
import { FeedbackLabels } from '@/store/modules/feedback-labels'
import { Tutor, FeedbackLabel, FeedbackStageEnum } from '@/models'
get tutors () { return TutorOverview.state.tutors }
get isReviewer () { return Authentication.isReviewer }
get tutorNames () {
return this.tutors.map(tutor => tutor.username)
}
export type FeedbackSearchOptionsModel = {
searchString: string,
showFinal: boolean,
caseSensitive: boolean,
useRegex: boolean,
filterByTutors: Tutor[],
filterByStage: FeedbackStageEnum | undefined,
filterByLabels: FeedbackLabel[],
filterByExcludingLabels: FeedbackLabel[],
}
get labels () {
return FeedbackLabels.state.labels.map(label => label.name)
const FeedbackSearchOptionsProps = Vue.extend({
props: {
model: {
type: Object as PropType<FeedbackSearchOptionsModel>,
default: () => {
return {
searchString: '',
showFinal: true,
caseSensitive: false,
useRegex: false,
filterByTutors: [],
filterByStage: undefined,
filterByLabels: [],
filterByExcludingLabels: [],
}
}
}
}
})
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 }
get filterByLabels () { return SearchOptions.state.filterByLabels }
get filterByExcludingLabels () { return SearchOptions.state.filterByExcludingLabels }
@Component
export default class FeedbackSearchOptions extends FeedbackSearchOptionsProps {
feedbackStages = Object.entries(FeedbackStageEnum).map(([key, value]) => ({text: key, value}))
get tutors() { return TutorOverview.state.tutors }
get isReviewer() { return Authentication.isReviewer }
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)
}
set filterByLabels (val) {
SearchOptions.SET_FILTER_BY_LABELS(val)
}
set filterByExcludingLabels (val) {
SearchOptions.SET_FILTER_BY_EXCLUDING_LABELS(val)
get labels() {
return FeedbackLabels.state.labels
}
loadTutors () {
loadTutors() {
if (this.tutors.length === 0 && this.isReviewer) {
TutorOverview.getTutors()
}
}
created () {
created() {
this.loadTutors()
}
}
......@@ -196,4 +187,4 @@ export default class FeedbackSearchOptions extends Vue {
<style scoped>
</style>
`
......@@ -2,24 +2,16 @@
<v-card>
<v-card-title class="title">
<span v-if="isTutor">Your</span><span>All</span>&nbsp;feedback history
<v-spacer />
<v-text-field
v-model="search"
append-icon="search"
label="Search"
single-line
hide-details
/>
</v-card-title>
<feedback-search-options class="mx-3" />
<v-data-table
:headers="headers"
:items="feedback"
:search="search"
:custom-filter="searchFunction"
:filter="containsSearch"
:pagination.sync="pagination"
:items="filteredFeedback"
>
<template slot="top">
<feedback-search-options
v-model="searchOptions"
/>
</template>
<template
slot="items"
slot-scope="props"
......@@ -61,213 +53,144 @@ 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 FeedbackSearchOptions, { FeedbackSearchOptionsModel } from '@/components/feedback_list/FeedbackSearchOptions.vue'
import { FeedbackTable as FeedbackModule, FeedbackHistoryItem } from '@/store/modules/feedback_list/feedback-table'
import { FeedbackStageEnum, Feedback } from '@/models'
import { FeedbackStageEnum, Feedback, FeedbackLabel } from '@/models'
import { actions } from '@/store/actions'
import { getters } from '@/store/getters'
import { Authentication } from '../../store/modules/authentication'
import { ConfigModule } from '../../store/modules/config'
function extractLabelsFromFeedback(feedback: FeedbackHistoryItem): Set<number> {
let labels: Set<number> = new Set(feedback.labels)
Object.values(feedback.feedbackLines || {}).forEach(comments => {
comments.forEach(comment => {
if (!comment.visibleToStudent) {
return
}
comment.labels.forEach(label => {
labels.add(label)
})
})
})
return labels
}
@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 filterByLabels () { return OptionsModule.state.filterByLabels }
get filterByExcludingLabels () { return OptionsModule.state.filterByExcludingLabels }
get isTutor () { return Authentication.isTutor }
get exerciseMode () { return ConfigModule.state.config.instanceSettings.exerciseMode }
get stageFilterString () { return OptionsModule.stageFilterString }
get headers(): {text: string, value: string, align?: string}[] {
return [
{ text: 'Type', align: 'left', value: 'ofSubmissionType' },
...(this.exerciseMode ? [{ text: 'Student', value: 'ofStudent' }] : []),
{ text: 'score', value: 'score' },
{ text: 'Created', value: 'created' },
{ text: 'Modified', value: 'modified', },
{ text: 'Final', value: 'final' },
{ text: 'Mark', value: 'mark' }
]
}
get feedback () {
return Object.values(FeedbackModule.state.feedbackHist).filter(feedback => {
return this.checkFinal(feedback) &&
this.filterFeedbackByTutorStage(feedback) &&
this.filterFeedbackByLabels(feedback)
})
return Object.values(FeedbackModule.state.feedbackHist)
}
searchOptions: FeedbackSearchOptionsModel = {
searchString: '',
showFinal: true,
caseSensitive: false,
useRegex: false,
filterByTutors: [],
filterByStage: undefined,
filterByLabels: [],
filterByExcludingLabels: [],
}
get queryFoundInString(): (s: string) => boolean {
if (this.searchOptions.useRegex) {
const flags = this.searchOptions.caseSensitive ? 'u' : 'iu'
try {
const re = new RegExp(this.searchOptions.searchString , flags)
return s => re.test(s)
} catch {
return _ => true
}
} else {
if (this.searchOptions.caseSensitive)
return s => s.includes(this.searchOptions.searchString)
else
return s => s.toLowerCase().includes(this.searchOptions.searchString.toLowerCase())
}
}
search = ''
prefetchWhenLessItems = 11
pagination = {
sortBy: 'student',
rowsPerPage: 25
queryFoundInFields(f: Feedback): boolean {
return f.ofSubmissionType !== undefined && this.queryFoundInString(f.ofSubmissionType) ||
f.created !== undefined && this.queryFoundInString(f.created) ||
f.modified !== undefined && this.queryFoundInString(f.modified)
}
get headers() {
let headers: {text: string, value: string, align?: string}[] = [
{
text: 'Type',
align: 'left',
value: 'ofSubmissionType'
},
]
if (this.exerciseMode) {
headers.push({
text: 'Student',
value: 'ofStudent'
})
}
headers = headers.concat(
[{
text: 'score',
value: 'score'
},
{
text: 'Created',
value: 'created'
},
{
text: 'Modified',
value: 'modified',
},
{
text: 'Final',
value: 'final'
},
{
text: 'Mark',
value: 'mark'
}]
)
return headers
queryFoundInComments(feedback: Feedback): boolean {
return Object.values(feedback.feedbackLines ?? {})
.some(line => line.map(comment => comment.text).some(this.queryFoundInString))
}
allLabelsFromFilterFoundOn(f: Feedback): boolean {
const fLabels = extractLabelsFromFeedback(f)
return this.searchOptions.filterByLabels.every(l => fLabels.has(l.pk))
}
noExcludedLabelFoundOn(f: Feedback): boolean {
const fLabels = extractLabelsFromFeedback(f)
return this.searchOptions.filterByExcludingLabels.every(l => !fLabels.has(l.pk))
}
// TODO: it is possible that a user is not assigned to the feedback, but still made a comment on it
// this happens when a reviewer adds a comment to a feedback that he has never corrected himself
// we could either ignore this case or add every user that has made a comment to a feedback
// to the list of associated users in the filterFeedbackByTutorStage method
// to the list of associated users in the filteredTutorsContributedToFeedback method
/**
* Used to determine wether or not a single Feedback should appear in the history based on
* the tutor and stage selection.
*
* Returns true if the given feedback is not filtered and should thus be displayed in the
* history. Returns false if the given feedback should be excluded from the history.
*/
filterFeedbackByTutorStage (feedback: FeedbackHistoryItem) {
if (!feedback.history) {
return this.filterByTutors.length === 0
}
const associatedTutors = this.stageFilterString === 'all'
? Object.values(feedback.history).filter(histEntry => !!histEntry).map(histEntry => histEntry!.ofTutor)
: feedback.history[this.stageFilterString as FeedbackStageEnum]
? [feedback.history[this.stageFilterString as FeedbackStageEnum]!.ofTutor] : []
filteredTutorsContributedToFeedback(f: FeedbackHistoryItem): boolean {
if (this.searchOptions.filterByTutors.length === 0)
return true
// select if tutor is in the filterByTutors array
return this.filterByTutors.length === 0 ||
associatedTutors.some(tutor => this.filterByTutors.includes(tutor))
const stages = this.searchOptions.filterByStage ? [this.searchOptions.filterByStage]
: Object.values(FeedbackStageEnum)
const associatedTutors = stages.map(stage => f.history?.[stage]?.ofTutor).filter(x => !!x)
return this.searchOptions.filterByTutors.some(tutor => associatedTutors.includes(tutor.username))
}
extractLabelsFromFeedback(feedback: FeedbackHistoryItem): Set<number> {
let labels: Set<number> = new Set(feedback.labels)
Object.values(feedback.feedbackLines || {}).forEach(comments => {
comments.forEach(comment => {
if (!comment.visibleToStudent) {
return
}
comment.labels.forEach(label => {
labels.add(label)
})
})
get filteredFeedback() {
return this.feedback.filter(f => {
return (!f.isFinal || this.searchOptions.showFinal) &&
(this.queryFoundInFields(f) || this.queryFoundInComments(f)) &&
this.allLabelsFromFilterFoundOn(f) &&
this.noExcludedLabelFoundOn(f) &&
this.filteredTutorsContributedToFeedback(f)
})
return labels
}
/**
* Used to determine wether or not a single Feedback should appear in the history based on
* the included and excluded labels filter.
*
* Returns true if the given feedback is not filtered and should thus be displayed in the
* history. Returns false if the given feedback should be excluded from the history.
*/
filterFeedbackByLabels (feedback: FeedbackHistoryItem): boolean {
if (this.filterByLabels.length === 0 && this.filterByExcludingLabels.length === 0) {
return true
}
const labelsInFeedback = this.extractLabelsFromFeedback(feedback)
const containsFilterLabels = OptionsModule.labelFilterMapped.every(label => {
// ignore if a mapped label is undefined
return !label || labelsInFeedback.has(label.pk)
})
const excludedLabels = OptionsModule.labelExcludeFilterMapped
const containsExcludedLabels = excludedLabels.length && excludedLabels.every(label => {
return !label || labelsInFeedback.has(label.pk)
})
return containsFilterLabels && !containsExcludedLabels
pagination = {
sortBy: 'student',
rowsPerPage: 25
}
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
}
changeMark(submissionPk: string, currColor: string) {
const colorArr = ['red lighten-2', 'blue lighten-2', 'green lighten-2', 'transparent']
const newColor = colorArr[(colorArr.indexOf(currColor) + 1) % colorArr.length]
FeedbackModule.SET_MARK_COLOR({submissionPk, color: newColor})
}
}
</script>
......
import { Module } from 'vuex'
import { RootState } from '@/store/store'
import { getStoreBuilder } from 'vuex-typex'
import { FeedbackStageEnum } from '@/models'
import { FeedbackLabels } from '../feedback-labels'
export const namespace = 'feedbackSearchOptions'
export interface FeedbackSearchOptionsState {
showFinal: boolean
searchOtherUserComments: boolean
caseSensitive: boolean
useRegex: boolean
filterByTutors: string[]
filterByStage: string,
filterByLabels: string[],
filterByExcludingLabels: string[]
}
function initialState (): FeedbackSearchOptionsState {
return {
showFinal: true,
searchOtherUserComments: false,
caseSensitive: false,
useRegex: false,
filterByTutors: [],
filterByStage: '',
filterByLabels: [],
filterByExcludingLabels: []
}
}
const mb = getStoreBuilder<RootState>().module('FeedbackSearchOptions', initialState())
const mapStageDisplayToApiString: {[index: string]: string} = {
'Initial feedback': FeedbackStageEnum.Creation,
'Validation': FeedbackStageEnum.Validation
}
const stateGetter = mb.state()
const stageFilterStringGetter = mb.read(function stageFilterString (state: FeedbackSearchOptionsState) {
return mapStageDisplayToApiString[state.filterByStage] || 'all'
})
const labelFilterMappedGetter = mb.read(function labelFilterMapped (state: FeedbackSearchOptionsState) {
return state.filterByLabels.map(labelName => {
return FeedbackLabels.state.labels.find(label => label.name === labelName)
})
})
const labelExcludeFilterMappedGetter = mb.read(function labelExcludeFilterMapped (state: FeedbackSearchOptionsState) {
return state.filterByExcludingLabels.map(labelName => {
return FeedbackLabels.state.labels.find(label => label.name === labelName)
})
})
function SET_SHOW_FINAL (state: FeedbackSearchOptionsState, val: boolean) {
state.showFinal = val
}
function SET_SEARCH_OTHER_USER_COMMENTS (state: FeedbackSearchOptionsState, val: boolean) {
state.searchOtherUserComments = val
}
function SET_CASE_SENSITIVE (state: FeedbackSearchOptionsState, val: boolean) {
state.caseSensitive = val
}
function SET_USE_REGEX (state: FeedbackSearchOptionsState, val: boolean) {
state.useRegex = val
}
function SET_FILTER_BY_TUTORS (state: FeedbackSearchOptionsState, val: string[]) {
state.filterByTutors = val
}
function SET_FILTER_BY_STAGE (state: FeedbackSearchOptionsState, val: string) {
state.filterByStage = val
}
function SET_FILTER_BY_LABELS (state: FeedbackSearchOptionsState, val: string[]) {
state.filterByLabels = val
}
function SET_FILTER_BY_EXCLUDING_LABELS (state: FeedbackSearchOptionsState, val: string[]) {
state.filterByExcludingLabels = val
}
export const FeedbackSearchOptions = {
get state () { return stateGetter() },
get stageFilterString () { return stageFilterStringGetter() },
get labelFilterMapped () { return labelFilterMappedGetter() },
get labelExcludeFilterMapped () { return labelExcludeFilterMappedGetter() },
SET_SHOW_FINAL: mb.commit(SET_SHOW_FINAL),
SET_SEARCH_OTHER_USER_COMMENTS: mb.commit(SET_SEARCH_OTHER_USER_COMMENTS),
SET_CASE_SENSITIVE: mb.commit(SET_CASE_SENSITIVE),
SET_USE_REGEX: mb.commit(SET_USE_REGEX),
SET_FILTER_BY_TUTORS: mb.commit(SET_FILTER_BY_TUTORS),
SET_FILTER_BY_STAGE: mb.commit(SET_FILTER_BY_STAGE),
SET_FILTER_BY_LABELS: mb.commit(SET_FILTER_BY_LABELS),
SET_FILTER_BY_EXCLUDING_LABELS: mb.commit(SET_FILTER_BY_EXCLUDING_LABELS)
}
import { fetchAllFeedback, fetchAllAssignments } from '@/api'
import { objectifyArray } from '@/util/helpers'
import { Assignment, Feedback, FeedbackStageEnum, SubmissionType } from '@/models'
import { Module } from 'vuex'
import { RootState } from '@/store/store'
import { getters } from '@/store/getters'
import { getStoreBuilder, BareActionContext } from 'vuex-typex'
......
......@@ -7,8 +7,6 @@ import './modules/ui'
// @ts-ignore
import './modules/authentication'
// @ts-ignore
import './modules/feedback_list/feedback-search-options'
// @ts-ignore
import './modules/feedback_list/feedback-table'
// @ts-ignore
import './modules/assignments'
......@@ -27,7 +25,6 @@ import './getters'
import { UIState } from './modules/ui'
import { AuthState } from './modules/authentication'
import { FeedbackSearchOptionsState } from './modules/feedback_list/feedback-search-options'
import { AssignmentsState } from './modules/assignments'
import { FeedbackTableState } from './modules/feedback_list/feedback-table'
import { SubmissionNotesState } from './modules/submission-notes'
......@@ -60,7 +57,6 @@ export interface RootInitialState {
export interface RootState extends RootInitialState{
UI: UIState,
Authentication: AuthState,
FeedbackSearchOptions: FeedbackSearchOptionsState,
FeedbackTable: FeedbackTableState,
Assignments: AssignmentsState,
SubmissionNotes: SubmissionNotesState,
......
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