From 1da17e1c095f062bd04096dec0e277e7933efd5e Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Sun, 18 Feb 2018 23:19:19 +0100 Subject: [PATCH] Comments can be deleted Fixed subscription deletion Added welcome Jumbotron with some nice info/links Some work on start page Added feedback_stage_for_user to feedback serializer This field will contain the feedback stage of the subscription for which the requesting user has created this feedback. This is needed to correctly give the choice to set the final attribute in the frontend. Fix final checkbox frontend Fix disappearing names in feedback list --- core/serializers/feedback.py | 14 +++++- frontend/src/api.js | 10 +++++ .../src/components/CorrectionStatistics.vue | 6 +-- frontend/src/components/SubmissionTests.vue | 8 ++-- frontend/src/components/WelcomeJumbotron.vue | 44 ++++++++++++++++++ .../submission_notes/SubmissionCorrection.vue | 24 +++++----- .../submission_notes/base/FeedbackComment.vue | 45 ++++++++++++++++--- .../AnnotatedSubmissionBottomToolbar.vue | 14 +++--- .../subscriptions/SubscriptionForList.vue | 4 +- .../subscriptions/SubscriptionList.vue | 19 ++++---- frontend/src/pages/LayoutSelector.vue | 2 +- frontend/src/pages/SubscriptionWorkPage.vue | 2 +- .../src/pages/reviewer/ReviewerStartPage.vue | 26 ++++++++--- frontend/src/pages/tutor/TutorStartPage.vue | 16 ++++++- frontend/src/store/actions.js | 16 ++++++- .../src/store/modules/submission-notes.js | 35 ++++++++++----- frontend/src/store/mutations.js | 12 +++-- 17 files changed, 229 insertions(+), 68 deletions(-) create mode 100644 frontend/src/components/WelcomeJumbotron.vue diff --git a/core/serializers/feedback.py b/core/serializers/feedback.py index 3f7c1335..d5e48833 100644 --- a/core/serializers/feedback.py +++ b/core/serializers/feedback.py @@ -85,6 +85,18 @@ class FeedbackSerializer(DynamicFieldsModelSerializer): feedback_lines = FeedbackCommentSerializer(many=True, required=False) of_submission_type = serializers.ReadOnlyField( source='of_submission.type.pk') + feedback_stage_for_user = serializers.SerializerMethodField() + + def get_feedback_stage_for_user(self, obj): + if 'request' in self.context: + assignments = obj.of_submission.assignments.filter( # noqa + subscription__owner=self.context['request'].user + ) + if len(assignments) == 0: + return None + else: + return None + return assignments[0].subscription.feedback_stage @transaction.atomic def create(self, validated_data) -> Feedback: @@ -166,4 +178,4 @@ class FeedbackSerializer(DynamicFieldsModelSerializer): class Meta: model = Feedback fields = ('pk', 'of_submission', 'is_final', 'score', 'feedback_lines', - 'created', 'of_submission_type') + 'created', 'of_submission_type', 'feedback_stage_for_user') diff --git a/frontend/src/api.js b/frontend/src/api.js index d11c5dbf..0e040c64 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -157,4 +157,14 @@ export async function deleteAssignment ({assignment}) { return (await ax.delete(url)).data } +export async function deleteComment (comment = {pk: undefined}) { + const url = `/api/feedback-comment/${comment.pk}/` + return (await ax.delete(url)).data +} + +export async function patchComment (comment = {pk: undefined}) { + const url = `/api/feedback-comment/${comment.pk}/` + return (await ax.patch(url, comment)).data +} + export default ax diff --git a/frontend/src/components/CorrectionStatistics.vue b/frontend/src/components/CorrectionStatistics.vue index 1b7140c0..d72cdeea 100644 --- a/frontend/src/components/CorrectionStatistics.vue +++ b/frontend/src/components/CorrectionStatistics.vue @@ -6,7 +6,7 @@ <ul class="inline-list mx-3"> <li>Submissions per student: <span>{{statistics.submissions_per_student}}</span></li> <li>Submissions per type: <span>{{statistics.submissions_per_type}}</span></li> - <li>Curr. mean score: <span>{{statistics.current_mean_score}}</span></li> + <li>Curr. mean score: <span>{{statistics.current_mean_score.toFixed(2)}}</span></li> </ul> <v-divider class="mx-2 my-2"></v-divider> <div v-for="(progress, index) in statistics.submission_type_progress" :key="index"> @@ -16,8 +16,8 @@ <div class="mx-3"> <v-progress-linear v-model="progress.percentage" - :color="progress.precentage === 100 ? 'green' : 'blue'" - ></v-progress-linear> + :color="progress.percentage === 100 ? 'green' : 'blue'" + /> </div> </div> </v-card> diff --git a/frontend/src/components/SubmissionTests.vue b/frontend/src/components/SubmissionTests.vue index ea1c8731..9e6e025f 100644 --- a/frontend/src/components/SubmissionTests.vue +++ b/frontend/src/components/SubmissionTests.vue @@ -21,9 +21,7 @@ </v-layout> </div> <v-card> - <v-card-text> - {{item.annotation}} - </v-card-text> + <v-card-text class="test-output">{{item.annotation}}</v-card-text> </v-card> </v-expansion-panel-content> </v-expansion-panel> @@ -52,5 +50,7 @@ </script> <style scoped> - + .test-output { + white-space: pre-wrap; + } </style> diff --git a/frontend/src/components/WelcomeJumbotron.vue b/frontend/src/components/WelcomeJumbotron.vue new file mode 100644 index 00000000..0398e647 --- /dev/null +++ b/frontend/src/components/WelcomeJumbotron.vue @@ -0,0 +1,44 @@ +<template> + <v-jumbotron :gradient="gradient" dark class="elevation-10"> + <v-container fill-height> + <v-layout align-center> + <v-flex> + <h1 class="display-2 mb-2">Welcome to the Grady correction software</h1> + <span class="subheading"> + The intention of this tool is to simplify the exam correcting process at the + University of Goettingen. It is deployed as a + <a href="http://www.django-rest-framework.org/" target="_blank">Django Rest</a> + backend with a <a href="https://vuejs.org/" target="_blank">Vue.js</a> frontend + using the awesome <a href="https://next.vuetifyjs.com/en/" target="_blank">Vuetify</a> library.<br/><br/> + To get started with correcting just create a subscription by clicking one of the plus signs in the + <i>Your subscriptions</i> card. + Subscriptions are the mechanism by which the student submissions are distributed. + </span> + <v-divider class="my-5"></v-divider> + <span class="title mx-4">Check out our + <a href="https://gitlab.gwdg.de/j.michal/grady" target="_blank">Git!</a></span> + <span class="title mx-4">Why the name + <a href="https://www.youtube.com/watch?v=VjdgXqKjHvY" target="_blank">Grady?</a></span> + </v-flex> + </v-layout> + </v-container> + </v-jumbotron> +</template> + +<script> + export default { + name: 'welcome-jumbotron', + data () { + return { + gradient: 'to bottom, #1A237E, #97A1DD' + } + } + } +</script> + +<style scoped> + a { + color: black; + text-decoration: none; + } +</style> diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue index 89a89cf4..485c6adb 100644 --- a/frontend/src/components/submission_notes/SubmissionCorrection.vue +++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue @@ -13,21 +13,23 @@ @toggleEditor="toggleEditorOnLine(lineNo)" > <template> - <feedback-comment - v-if="origFeedback[lineNo]" - v-for="(comment, index) in origFeedback[lineNo]" - v-bind="comment" - :key="index" - @click.native="toggleEditorOnLine(lineNo, comment)" - /> + <div v-if="origFeedback[lineNo]"> + <feedback-comment + v-for="(comment, index) in origFeedback[lineNo]" + v-bind="comment" + :line-no="lineNo" + :key="index" + :deletable="comment.of_tutor === user || isReviewer" + @click.native="toggleEditorOnLine(lineNo, comment)" + /> + </div> </template> <feedback-comment v-if="updatedFeedback[lineNo]" - borderColor="orange" v-bind="updatedFeedback[lineNo]" + :line-no="lineNo" :deletable="true" @click.native="toggleEditorOnLine(lineNo, updatedFeedback[lineNo])" - @delete="deleteFeedback(lineNo)" /> <comment-form v-if="showEditorOnLine[lineNo]" @@ -93,6 +95,7 @@ }, computed: { ...mapState({ + user: state => state.authentication.username, showEditorOnLine: state => state.submissionNotes.ui.showEditorOnLine, selectedComment: state => state.submissionNotes.ui.selectedCommentOnLine, origFeedback: state => state.submissionNotes.origFeedback.feedback_lines, @@ -117,9 +120,6 @@ } }, methods: { - deleteFeedback (lineNo) { - this.$store.commit(subNotesNamespace(subNotesMut.DELETE_FEEDBACK_LINE), lineNo) - }, toggleEditorOnLine (lineNo, comment = '') { this.$store.commit(subNotesNamespace(subNotesMut.TOGGLE_EDITOR_ON_LINE), {lineNo, comment}) }, diff --git a/frontend/src/components/submission_notes/base/FeedbackComment.vue b/frontend/src/components/submission_notes/base/FeedbackComment.vue index 4672a2c6..792a1917 100644 --- a/frontend/src/components/submission_notes/base/FeedbackComment.vue +++ b/frontend/src/components/submission_notes/base/FeedbackComment.vue @@ -24,17 +24,29 @@ flat icon class="delete-button" v-if="deletable" - @click.stop="$emit('delete')" - ><v-icon color="grey darken-1" size="20px">delete_forever</v-icon></v-btn> + @click.stop="toggleDeleteComment" + > + <v-icon + v-if="!markedForDeletion.hasOwnProperty(this.pk)" + color="grey darken-1" size="20px">delete_forever</v-icon> + <v-icon v-else size="20px">restore</v-icon> + </v-btn> </div> </div> </template> <script> + import {mapState} from 'vuex' + import {subNotesMut, subNotesNamespace} from '@/store/modules/submission-notes' + export default { name: 'feedback-comment', props: { + pk: { + type: String, + required: false + }, text: { type: String, required: true @@ -47,6 +59,10 @@ type: String, required: false }, + lineNo: { + type: String, + required: true + }, deletable: { type: Boolean, default: false @@ -54,19 +70,36 @@ visible_to_student: { type: Boolean, default: true - }, - borderColor: { - type: String, - default: '#3D8FC1' } }, computed: { + ...mapState({ + markedForDeletion: state => state.submissionNotes.commentsMarkedForDeletetion + }), parsedCreated () { if (this.created) { return new Date(this.created).toLocaleString() } else { return 'Just now' } + }, + borderColor () { + if (this.pk) { + return this.markedForDeletion.hasOwnProperty(this.pk) ? '#B5B5B5' : '#3D8FC1' + } + return 'orange' + } + }, + methods: { + toggleDeleteComment () { + if (this.pk) { + if (!this.markedForDeletion.hasOwnProperty(this.pk)) { + this.$store.commit(subNotesNamespace(subNotesMut.MARK_COMMENT_FOR_DELETION), {pk: this.pk}) + } else { + this.$store.commit(subNotesNamespace(subNotesMut.UN_MARK_COMMENT_FOR_DELETION), {pk: this.pk}) + } + } + this.$store.commit(subNotesNamespace(subNotesMut.DELETE_FEEDBACK_LINE), this.lineNo) } } } diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue index a723a5bc..b34f78f7 100644 --- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue +++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue @@ -1,11 +1,11 @@ <template> <v-toolbar dense class="bottom-toolbar"> <v-tooltip top v-if="skippable"> - <v-btn - slot="activator" - outline round color="grey darken-2" - @click="$emit('skip')" - >Skip</v-btn> + <v-btn + slot="activator" + outline round color="grey darken-2" + @click="$emit('skip')" + >Skip</v-btn> <span>Skip this submission</span> </v-tooltip> <v-spacer/> @@ -61,7 +61,7 @@ data () { return { scoreError: '', - isFinal: !this.$store.state.submissionNotes.isFeedbackCreation || this.$store.getters.isReviewer + isFinal: !this.$store.getters['submissionNotes/isFeedbackCreation'] || this.$store.getters.isReviewer } }, props: { @@ -88,7 +88,7 @@ } }, showFinalCheckbox () { - return !this.$store.state.submissionNotes.isFeedbackCreation || this.$store.getters.isReviewer + return !this.$store.getters['submissionNotes/isFeedbackCreation'] || this.$store.getters.isReviewer } }, methods: { diff --git a/frontend/src/components/subscriptions/SubscriptionForList.vue b/frontend/src/components/subscriptions/SubscriptionForList.vue index 4cbe731f..0326bfce 100644 --- a/frontend/src/components/subscriptions/SubscriptionForList.vue +++ b/frontend/src/components/subscriptions/SubscriptionForList.vue @@ -82,8 +82,8 @@ }, methods: { deactivate () { - this.$store.dispatch('deactivateSubscription', {pk: this.subscriptionPk}).then(() => { - if (this.$route.params.pk === this.subscriptionPk) { + this.$store.dispatch('deactivateSubscription', {pk: this.pk}).then(() => { + if (this.$route.params.pk === this.pk) { this.$router.push('/') } }).catch(err => { diff --git a/frontend/src/components/subscriptions/SubscriptionList.vue b/frontend/src/components/subscriptions/SubscriptionList.vue index 1aba1a7b..1fad138f 100644 --- a/frontend/src/components/subscriptions/SubscriptionList.vue +++ b/frontend/src/components/subscriptions/SubscriptionList.vue @@ -5,15 +5,16 @@ <v-toolbar-title v-if="!sidebar || (sidebar && !sideBarCollapsed)"> Your subscriptions </v-toolbar-title> - <v-btn icon @click="getSubscriptions"> - <v-icon v-if="!updating">refresh</v-icon> - <v-progress-circular - v-else - indeterminate - color="black" - size="20" - /> - </v-btn> + <v-spacer/> + <v-btn icon @click="getSubscriptions"> + <v-icon v-if="!updating">refresh</v-icon> + <v-progress-circular + v-else + indeterminate + color="black" + size="20" + /> + </v-btn> </v-toolbar> <v-list :dense="sidebar" v-if="!sidebar || (sidebar && !sideBarCollapsed)"> <div v-for="item in subscriptionTypes" :key="item.type"> diff --git a/frontend/src/pages/LayoutSelector.vue b/frontend/src/pages/LayoutSelector.vue index df530c96..5adadda9 100644 --- a/frontend/src/pages/LayoutSelector.vue +++ b/frontend/src/pages/LayoutSelector.vue @@ -1,5 +1,5 @@ <template> - <div style="height: 100%;"> + <div> <component :is="layout"></component> <v-content> <router-view></router-view> diff --git a/frontend/src/pages/SubscriptionWorkPage.vue b/frontend/src/pages/SubscriptionWorkPage.vue index 0a25e0d0..747fc714 100644 --- a/frontend/src/pages/SubscriptionWorkPage.vue +++ b/frontend/src/pages/SubscriptionWorkPage.vue @@ -23,7 +23,7 @@ :key="submissionType.pk" :reverse="true" :expandedByDefault="{ Description: false, Solution: true }" - class="mt-4" + class="mt-4 mr-4" /> </v-flex> </v-layout> diff --git a/frontend/src/pages/reviewer/ReviewerStartPage.vue b/frontend/src/pages/reviewer/ReviewerStartPage.vue index 71f05532..69092cc6 100644 --- a/frontend/src/pages/reviewer/ReviewerStartPage.vue +++ b/frontend/src/pages/reviewer/ReviewerStartPage.vue @@ -1,13 +1,29 @@ <template> - <v-container fill-height> - <v-layout align-center justify-center> - <h1>You are reviewer!</h1> - </v-layout> - </v-container> + <div> + <v-flex lg6 md10 xs12 mx-auto class="mt-4"> + <welcome-jumbotron></welcome-jumbotron> + </v-flex> + <v-layout row wrap> + <v-flex lg5 md9 xs12> + <correction-statistics class="ma-4"></correction-statistics> + </v-flex> + <v-flex lg5 md9 xs12> + <subscription-list class="ma-4"></subscription-list> + </v-flex> + </v-layout> + </div> </template> <script> + import CorrectionStatistics from '@/components/CorrectionStatistics' + import WelcomeJumbotron from '@/components/WelcomeJumbotron' + import SubscriptionList from '@/components/subscriptions/SubscriptionList' + export default { + components: { + SubscriptionList, + WelcomeJumbotron, + CorrectionStatistics}, name: 'reviewer-start-page' } </script> diff --git a/frontend/src/pages/tutor/TutorStartPage.vue b/frontend/src/pages/tutor/TutorStartPage.vue index 5230e7ee..fee486f4 100644 --- a/frontend/src/pages/tutor/TutorStartPage.vue +++ b/frontend/src/pages/tutor/TutorStartPage.vue @@ -1,15 +1,27 @@ <template> - <v-flex xs5> - <correction-statistics class="ma-4"></correction-statistics> + <div> + <v-flex lg6 md10 xs12 mx-auto class="mt-4"> + <welcome-jumbotron></welcome-jumbotron> </v-flex> + <v-layout row wrap> + <v-flex lg5 md9 xs12> + <correction-statistics class="ma-4"></correction-statistics> + </v-flex> + <v-flex lg5 md9 xs12> + <subscription-list class="ma-4"></subscription-list> + </v-flex> + </v-layout> + </div> </template> <script> import SubscriptionList from '@/components/subscriptions/SubscriptionList' import CorrectionStatistics from '@/components/CorrectionStatistics' + import WelcomeJumbotron from '@/components/WelcomeJumbotron' export default { components: { + WelcomeJumbotron, CorrectionStatistics, SubscriptionList}, name: 'tutor-start-page' diff --git a/frontend/src/store/actions.js b/frontend/src/store/actions.js index 9b65d33f..e9556598 100644 --- a/frontend/src/store/actions.js +++ b/frontend/src/store/actions.js @@ -49,7 +49,7 @@ const actions = { try { const subscriptionPk = subscription ? subscription.pk : pk await api.deactivateSubscription({pk: subscriptionPk}) - commit(mut.SET_SUBSCRIPTION_DEACTIVATED, {pk: subscriptionPk}) + commit(mut.DELETE_SUBSCRIPTION, {pk: subscriptionPk}) } catch (err) { handleError(err, dispatch, 'Unable to deactivate subscription') } @@ -147,6 +147,20 @@ const actions = { handleError(err, dispatch, 'Unable to fetch statistics') } }, + async deleteComment ({commit, dispatch}, comment) { + try { + await api.deleteComment(comment) + } catch (err) { + handleError(err, dispatch, `Unable to delete comment: ${comment.text}`) + } + }, + async patchComment ({commit, dispatch}, comment) { + try { + return await api.patchComment(comment) + } catch (err) { + handleError(err, dispatch, `Unable to patch comment: ${comment.text}`) + } + }, logout ({ commit }, message = '') { commit(mut.RESET_STATE) commit('submissionNotes/' + subNotesMut.RESET_STATE) diff --git a/frontend/src/store/modules/submission-notes.js b/frontend/src/store/modules/submission-notes.js index cfcbee82..c25e9954 100644 --- a/frontend/src/store/modules/submission-notes.js +++ b/frontend/src/store/modules/submission-notes.js @@ -12,6 +12,8 @@ export const subNotesMut = Object.freeze({ UPDATE_FEEDBACK_SCORE: 'UPDATE_FEEDBACK_SCORE', DELETE_FEEDBACK_LINE: 'DELETE_FEEDBACK_LINE', TOGGLE_EDITOR_ON_LINE: 'TOGGLE_EDITOR_ON_LINE', + MARK_COMMENT_FOR_DELETION: 'MARK_COMMENT_FOR_DELETION', + UN_MARK_COMMENT_FOR_DELETION: 'UN_MARK_COMMENT_FOR_DELETION', RESET_UPDATED_FEEDBACK: 'RESET_UPDATED_FEEDBACK', RESET_STATE: 'RESET_STATE' }) @@ -19,7 +21,6 @@ export const subNotesMut = Object.freeze({ function initialState () { return { assignment: '', - isFeedbackCreation: false, submission: { text: '' }, @@ -27,6 +28,7 @@ function initialState () { showEditorOnLine: {}, selectedCommentOnLine: {} }, + hasOrigFeedback: false, origFeedback: { score: null, feedback_lines: {} @@ -34,7 +36,8 @@ function initialState () { updatedFeedback: { score: null, feedback_lines: {} - } + }, + commentsMarkedForDeletetion: {} } } @@ -55,10 +58,9 @@ const submissionNotes = { score: state => { return state.updatedFeedback.score !== null ? state.updatedFeedback.score : state.origFeedback.score }, - openEditorOrWrittenFeedback: state => { - const openEdit = Object.values(state.ui.showEditorOnLine).some(bool => bool) - const hasWrittenFeedback = Object.keys(state.updatedFeedback.feedback_lines).length > 0 - return openEdit || hasWrittenFeedback + isFeedbackCreation: state => { + return !state.origFeedback['feedback_stage_for_user'] || + state.origFeedback['feedback_stage_for_user'] === 'feedback-creation' } }, mutations: { @@ -68,8 +70,7 @@ const submissionNotes = { [subNotesMut.SET_ORIG_FEEDBACK]: function (state, feedback) { if (feedback) { state.origFeedback = feedback - } else { - state.isFeedbackCreation = true + state.hasOrigFeedback = true } }, [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback) { @@ -85,6 +86,12 @@ const submissionNotes = { Vue.set(state.ui.selectedCommentOnLine, lineNo, comment) Vue.set(state.ui.showEditorOnLine, lineNo, !state.ui.showEditorOnLine[lineNo]) }, + [subNotesMut.MARK_COMMENT_FOR_DELETION]: function (state, comment) { + Vue.set(state.commentsMarkedForDeletetion, comment.pk, comment) + }, + [subNotesMut.UN_MARK_COMMENT_FOR_DELETION]: function (state, comment) { + Vue.delete(state.commentsMarkedForDeletetion, comment.pk) + }, [subNotesMut.RESET_UPDATED_FEEDBACK]: function (state) { state.updatedFeedback = initialState().updatedFeedback }, @@ -93,7 +100,14 @@ const submissionNotes = { } }, actions: { - submitFeedback: async function ({state}, {isFinal = false}) { + deleteComments: async function ({state}) { + return Promise.all( + Object.values(state.commentsMarkedForDeletetion).map(comment => { + return api.deleteComment(comment) + }) + ) + }, + submitFeedback: async function ({state, dispatch, getters}, {isFinal = false}) { let feedback = { is_final: isFinal, of_submission: state.submission.pk @@ -106,7 +120,8 @@ const submissionNotes = { } else if (state.updatedFeedback.score !== null) { feedback['score'] = state.updatedFeedback.score } - if (state.isFeedbackCreation) { + await dispatch('deleteComments') + if (!state.hasOrigFeedback) { return api.submitFeedbackForAssignment({feedback}) } else { feedback.pk = state.origFeedback.pk diff --git a/frontend/src/store/mutations.js b/frontend/src/store/mutations.js index 0a5c9e72..98a5330b 100644 --- a/frontend/src/store/mutations.js +++ b/frontend/src/store/mutations.js @@ -8,7 +8,7 @@ export const mut = Object.freeze({ SET_ASSIGNMENT: 'SET_ASSIGNMENT', SET_SUBSCRIPTIONS: 'SET_SUBSCRIPTIONS', SET_SUBSCRIPTION: 'SET_SUBSCRIPTION', - SET_SUBSCRIPTION_DEACTIVATED: 'SET_SUBSCRIPTION_DEACTIVATED', + DELETE_SUBSCRIPTION: 'DELETE_SUBSCRIPTION', SET_LAST_INTERACTION: 'SET_LAST_INTERACTION', SET_EXAM_TYPES: 'SET_EXAM_TYPES', SET_STUDENTS: 'SET_STUDENTS', @@ -40,8 +40,8 @@ const mutations = { [mut.SET_SUBSCRIPTION] (state, subscription) { Vue.set(state.subscriptions, subscription.pk, subscription) }, - [mut.SET_SUBSCRIPTION_DEACTIVATED] (state, {pk}) { - state.subscriptions[pk].deactivated = true + [mut.DELETE_SUBSCRIPTION] (state, {pk}) { + Vue.delete(state.subscriptions, pk) }, [mut.SET_STUDENTS] (state, students) { state.students = students.reduce((acc, curr) => { @@ -68,7 +68,11 @@ const mutations = { }, {}) }, [mut.SET_FEEDBACK] (state, feedback) { - Vue.set(state.feedback, feedback.pk, feedback) + Vue.set(state.feedback, feedback.pk, { + ...state.feedback[feedback.pk], + ...feedback, + of_submission_type: state.submissionTypes[feedback['of_submission_type']] + }) }, [mut.SET_STATISTICS] (state, statistics) { state.statistics = { -- GitLab