diff --git a/frontend/@types/v-clipboard/index.d.ts b/frontend/@types/v-clipboard/index.d.ts
index 54bf07180256d6c40fc30d44b346cb01ebdf61eb..8619c86521a4634b98954c93b4cede9db2e182fa 100644
--- a/frontend/@types/v-clipboard/index.d.ts
+++ b/frontend/@types/v-clipboard/index.d.ts
@@ -1 +1,2 @@
 declare module 'v-clipboard'
+declare module 'vue-color'
diff --git a/frontend/package.json b/frontend/package.json
index a2ed358a0dfb4e34bc7fe544379fa11f31de08c5..2e960ea71a34a9b2f8a0ab68ad04ce9eb41e6fce 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -19,6 +19,7 @@
     "v-clipboard": "^2.0.1",
     "vue": "^2.5.16",
     "vue-class-component": "^6.0.0",
+    "vue-color": "^2.7.0",
     "vue-notification": "^1.3.12",
     "vue-property-decorator": "^7.3.0",
     "vue-router": "^3.0.1",
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index 22263bfd91698bba027e9e8ece295627f8e1d58d..a96d779f563aa4eec09f9a14733999aaeb3a7985 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -212,6 +212,14 @@ export async function getLabels () {
   return (await ax.get('/api/label')).data
 }
 
+export async function createLabel (payload: Partial<FeedbackLabel>) {
+  return (await ax.post('/api/label/', payload)).data
+}
+
+export async function updateLabel (payload: FeedbackLabel) {
+  return (await ax.put('/api/label/' + payload.pk + '/', payload))
+}
+
 export interface StudentExportOptions { setPasswords?: boolean }
 export interface StudentExportItem {
   Matrikel: string,
diff --git a/frontend/src/components/BaseLayout.vue b/frontend/src/components/BaseLayout.vue
index b68d805f57860f6bd58bfd373c0333fa2251228a..ae32a6af46cbccbbbde8429a8d25b7cd0f03d559 100644
--- a/frontend/src/components/BaseLayout.vue
+++ b/frontend/src/components/BaseLayout.vue
@@ -140,7 +140,6 @@ export default {
 
 <style scoped>
   .sidebar-footer {
-    position: absolute;
     width: 100%;
     bottom: 0px;
   }
diff --git a/frontend/src/components/feedback_labels/FeedbackLabel.vue b/frontend/src/components/feedback_labels/FeedbackLabel.vue
index bf14cbe05e6bc29f1cf5880318d4d11cb1e2299b..1f21ee2cbb671b56fbd459c50c10ae14d78bf759 100644
--- a/frontend/src/components/feedback_labels/FeedbackLabel.vue
+++ b/frontend/src/components/feedback_labels/FeedbackLabel.vue
@@ -1,8 +1,10 @@
 <template>
   <v-tooltip top>
       <v-chip
-        close
+        :close=removable
+        :color="colour"
         slot="activator"
+        @input="onClose"
       > {{ name }} </v-chip>
     <span> {{ description }} </span>
   </v-tooltip>
@@ -15,8 +17,14 @@ import { Prop } from "vue-property-decorator"
 
 @Component
 export default class FeedbackLabel extends Vue {
+  @Prop({ type: Number, required :true }) readonly pk!: number
   @Prop({ type: String, required: true }) readonly name!: string
   @Prop({ type: String, required: true }) readonly description!: string
+  @Prop({ type: String, required: true }) readonly colour!: string
+  @Prop({ type: Boolean, default: false }) readonly removable!: boolean
+  onClose() {
+    this.$emit("remove-clicked", this.pk)
+  }
 }
 </script>
 
diff --git a/frontend/src/components/feedback_labels/FeedbackLabelForm.vue b/frontend/src/components/feedback_labels/FeedbackLabelForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..3cd91c4761f03ed83ab15429fdea53a8f69d251f
--- /dev/null
+++ b/frontend/src/components/feedback_labels/FeedbackLabelForm.vue
@@ -0,0 +1,128 @@
+<template>
+  <v-layout wrap justify-start>
+    <v-flex ml-3 xs9>
+      <v-text-field
+        label="Name"
+        v-model="name"
+      />
+    </v-flex>
+    <v-flex ml-3 xs9>
+      <v-textarea
+        label="Description"
+        v-model="description"
+        placeholder="The description can be seen when hovering above the label"
+        auto-grow
+      />
+    </v-flex>
+    <v-flex ml-2 xs12>
+      <compact-picker style="width:85%;box-shadow:none;" v-model="colour"/>
+    </v-flex>
+    <v-flex ml-1 mb-3 xs4>
+        <v-btn id="create-label-btn" v-if="!is_update" :loading="loading" color="teal" @click="createLabel">Create</v-btn>
+        <v-btn id="update-label-btn" v-else color="teal" :loading="loading" @click="updateLabel">Update</v-btn>
+    </v-flex>
+  </v-layout>
+</template>
+
+<script lang="ts">
+import Vue from "vue"
+import Component from "vue-class-component"
+import { Prop } from "vue-property-decorator"
+import * as api from "@/api";
+import { Compact } from "vue-color"
+import { FeedbackLabels } from "@/store/modules/feedback-labels";
+
+@Component({
+  components: {
+    'compact-picker': Compact,
+  }
+})
+export default class FeedbackLabelForm extends Vue {
+  @Prop({ type: String, default: "" }) name!: string
+  @Prop({ type: String, default: "" }) description!: string
+  @Prop({ type: String, default: "#4d4d4d" }) colour!: string
+  @Prop({ type: Number, required: false }) readonly pk!: number
+  @Prop({ type: Boolean, default: false }) readonly is_update!: boolean
+
+  loading = false
+
+  get feedbackLabels () {
+    return FeedbackLabels.availableLabels
+  }
+
+  async createLabel () {
+    this.loading = true
+    const label = {
+      name: this.name,
+      description: this.description,
+      // @ts-ignore
+      colour: this.colour.hex,
+    }
+
+    const duplicate = this.feedbackLabels.find((val) => {
+      return val.name === label.name
+    })
+
+    if (duplicate) {
+      this.$notify({
+        title: "Label creation error",
+        text: "A label with the same name already exists. " + 
+          "You can update it if you are a reviewer or ask a reviewer to update it if not.",
+        type: 'error',
+        duration: -1
+      })
+      this.resetFields()
+      this.loading = false
+      return;
+    }
+
+    let res
+    try {
+      res = await api.createLabel(label)
+    } catch (ex) {
+      // user will be notified by the interceptor
+      this.resetFields()
+      this.loading = false
+      return;
+    }
+
+    FeedbackLabels.ADD_LABEL(res)
+    this.resetFields()
+    this.loading = false
+  }
+
+  async updateLabel () {
+    this.loading = true
+    const label = {
+      pk: this.pk,
+      name: this.name,
+      description: this.description,
+      // @ts-ignore
+      colour: this.colour.hex,
+    }
+
+    let res
+    try {
+      res = await api.updateLabel(label)
+    } catch (ex) {
+      // user will be notified by the interceptor
+      this.loading = false
+      return;
+    }
+
+    FeedbackLabels.UPDATE_LABEL(label)
+    this.$emit("label-updated", label.pk)
+    this.loading = false
+  }
+
+  resetFields () {
+    this.name = ""
+    this.description = ""
+    this.colour = "#4d4d4d"
+  }
+}
+</script>
+
+<style>
+
+</style>
diff --git a/frontend/src/components/feedback_labels/FeedbackLabelUpdater.vue b/frontend/src/components/feedback_labels/FeedbackLabelUpdater.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4d6a8ddee67b296849d2b69b7162c59c5997c45d
--- /dev/null
+++ b/frontend/src/components/feedback_labels/FeedbackLabelUpdater.vue
@@ -0,0 +1,65 @@
+<template>
+  <v-layout wrap>
+    <v-flex mx-2 xs12>
+      <v-autocomplete
+        :items="feedbackLabels"
+        item-text="name"
+        item-value="pk"
+        append-icon="search"
+        placeholder="search for keywords"
+        @input="setLabel"
+      />
+    </v-flex>
+    <v-flex xs12 v-if="label.pk !== -1">
+      <feedback-label-form
+        is_update
+        :pk="label.pk"
+        :name="label.name"
+        :description="label.description"
+        :colour="label.colour"
+        @label-updated="setLabel"
+      />
+    </v-flex>
+  </v-layout>
+</template>
+
+<script lang="ts">
+import Vue from "vue"
+import Component from "vue-class-component"
+import { FeedbackLabels } from "@/store/modules/feedback-labels"
+import FeedbackLabelForm from "./FeedbackLabelForm.vue"
+import { FeedbackLabel } from '../../models';
+
+@Component({
+  components: {
+    FeedbackLabelForm,
+  }
+})
+export default class FeedbackLabelUpdater extends Vue {
+  label: FeedbackLabel = {
+    pk: -1,
+    name: "",
+    description: "",
+    colour: "#4d4d4d",
+  }
+  loading = false
+
+  get feedbackLabels() {
+    return FeedbackLabels.availableLabels
+  }
+
+  setLabel (pk: number) {
+    const label = this.feedbackLabels.find((val: FeedbackLabel) => {
+      return val.pk === pk
+    })
+
+    if (label !== undefined) {
+      this.label = label
+    }
+  }
+}
+</script>
+
+<style>
+
+</style>
diff --git a/frontend/src/components/feedback_labels/FeedbackLabelsList.vue b/frontend/src/components/feedback_labels/FeedbackLabelsList.vue
index be2413e2731f3f1e910469e9cd5210a7de6ba547..d4ea78176dabf9db91f963a79f2166e77648dedf 100644
--- a/frontend/src/components/feedback_labels/FeedbackLabelsList.vue
+++ b/frontend/src/components/feedback_labels/FeedbackLabelsList.vue
@@ -1,20 +1,65 @@
 <template>
-  <div>
-    test test test
-  </div>
+  <v-card>
+    <v-toolbar color="teal" :dense="sidebar">
+      <v-toolbar-side-icon>
+        <v-icon>chat_bubble</v-icon>
+      </v-toolbar-side-icon>
+      <v-toolbar-title v-if="showDetail" style="min-width: fit-content;">
+        Labels
+      </v-toolbar-title>
+      <v-spacer />
+      <v-btn icon @click="refreshLabels">
+        <v-icon v-if="!updating">refresh</v-icon>
+        <v-progress-circular v-else indeterminate color="black" size="20" />
+      </v-btn>
+    </v-toolbar>
+    <v-tabs grow color="teal lighten-1" v-if="showDetail">
+      <v-tab>Create</v-tab>
+      <v-tab>Update</v-tab>
+      <v-tab-item>
+        <feedback-label-form/>
+      </v-tab-item>
+      <v-tab-item>
+        <feedback-label-updater/>
+      </v-tab-item>
+    </v-tabs>
+  </v-card>
 </template>
 
 <script lang="ts">
 import Vue from 'vue'
 import Component from 'vue-class-component'
+import { Prop } from 'vue-property-decorator'
 import { getLabels } from '@/api'
 import { FeedbackLabels } from '@/store/modules/feedback-labels'
+import { UI } from '@/store/modules/ui'
+import FeedbackLabelForm from "./FeedbackLabelForm.vue"
+import FeedbackLabelUpdater from "./FeedbackLabelUpdater.vue"
 
-@Component
+@Component({
+  components: {
+    FeedbackLabelForm,
+    FeedbackLabelUpdater,
+  }
+})
 export default class FeedbackLabelsList extends Vue {
+  @Prop({type: Boolean, default: false}) sidebar!: boolean
+
+  updating = false
+
+  get showDetail () {
+    return !this.sidebar || (this.sidebar && !UI.state.sideBarCollapsed)
+  }
+
   async created() {
+    await this.refreshLabels()
+  }
+
+  async refreshLabels() {
+    this.updating = true
     const labels = await getLabels()
     FeedbackLabels.SET_LABELS(labels)
+    this.updating = false
   }
 }
 </script>
diff --git a/frontend/src/components/feedback_labels/LabelSelector.vue b/frontend/src/components/feedback_labels/LabelSelector.vue
index 0bc6e3548fb173944bb59dd81437bb4a1c751bcd..d0725b339e1455e837f855af41c82e743235a886 100644
--- a/frontend/src/components/feedback_labels/LabelSelector.vue
+++ b/frontend/src/components/feedback_labels/LabelSelector.vue
@@ -10,11 +10,17 @@
           item-value="pk"
           append-icon="search"
           placeholder="search for keywords"
-          @input="onChange"
+          @input="addLabel"
         />
       </v-flex>
       <v-flex lg8>
-
+        <feedback-label
+          removable
+          v-for="label in labelsToShow"
+          v-bind="label"
+          :key="label.pk"
+          @remove-clicked="removeLabel"
+        />
       </v-flex>
     </v-layout>
   </v-card>
@@ -23,21 +29,121 @@
 <script lang="ts">
 import Vue from "vue";
 import Component from "vue-class-component";
+import { Prop } from "vue-property-decorator";
 import { FeedbackLabels } from '@/store/modules/feedback-labels'
-import { FeedbackLabel } from '@/models';
+import { SubmissionNotes } from '@/store/modules/submission-notes'
+import FeedbackLabel from "@/components/feedback_labels/FeedbackLabel.vue"
+import { FeedbackComment } from '../../models';
 
-@Component
+@Component({
+  components: {
+    FeedbackLabel,
+  }
+})
 export default class LabelSelector extends Vue {
+  @Prop({ type: String }) readonly lineNo!: string
+  @Prop({ type: Boolean, required: true }) readonly assignedToFeedback!: boolean
+  @Prop({ type: Array }) readonly labelsDraft!: number[]
 
   get feedbackLabels() {
     return FeedbackLabels.availableLabels
   }
 
-  onChange(val: string) {
-    const selectedLabel = this.feedbackLabels.find((label) => {
-      return label.pk === val
-    });
-    console.log(selectedLabel)
+  get labelsToShow() {
+    if (this.assignedToFeedback) {
+      return this.assignedFeedbackLabels()
+    }
+    return this.mapPksToLabelObj(this.labelsDraft)
+  }
+
+  get labelsGetter () {
+    if (SubmissionNotes.state.changedLabels) {
+      return SubmissionNotes.state.updatedFeedback.labels
+    }
+
+    // merge labels from originalFeedback and updatedFeedback
+    let merged: number[] = []
+    const concated = SubmissionNotes.state.origFeedback.labels.concat(
+      SubmissionNotes.state.updatedFeedback.labels
+    )
+
+    concated.forEach((val) => {
+      if (!(SubmissionNotes.state.origFeedback.labels.includes(val) &&
+        SubmissionNotes.state.updatedFeedback.labels.includes(val))) {
+          merged.push(val)
+      }
+    })
+
+    return merged
+  }
+
+  /**
+   * Returns an array of labels assigned to the currently loaded feedback
+   */
+  assignedFeedbackLabels() {
+    const labels = this.labelsGetter
+
+    if (labels.length === 0) return {}
+    const mapped = this.mapPksToLabelObj(labels)
+    return mapped ? mapped : {}
+  }
+
+  /**
+   * Maps label pk's to the objects stored in vuex store
+   */
+  mapPksToLabelObj(pkArr: number[]): FeedbackLabel[] {
+    const mappedLabels = pkArr.map((val) => {
+      const label = FeedbackLabels.availableLabels.find((label) => {
+        return label.pk === val
+      })
+
+      if (!label) return
+      return { 
+        pk: val,
+        name: label.name,
+        description: label.description,
+        colour: label.colour
+      }
+    })
+
+    return mappedLabels ? mappedLabels : new Array()
+  }
+
+  /**
+   * Removes given label from the feedback or fires an event
+   * to remove the label from a comment
+   */
+  removeLabel(pk: number) {
+    if (this.assignedToFeedback) {
+      const labels = this.labelsGetter.filter((val) => {
+        return val !== pk
+      })
+      SubmissionNotes.SET_FEEDBACK_LABELS(labels)
+    }
+    this.$emit("label-removed", pk)
+  }
+
+  /**
+   * Adds the given label to the feedback or fires an event
+   * to add the label to a comment
+   * Calling this with an already added label will instead remove the label
+   */
+  addLabel(pk: number) {
+    // there seems to be an issue with the autocomplete
+    // which fires when a user hits backspace
+    if (pk == undefined) return
+    if (this.assignedToFeedback) {
+      let labels = this.labelsGetter
+
+      if (!labels.includes(pk)) {
+        labels.push(pk)
+        SubmissionNotes.SET_FEEDBACK_LABELS(labels)
+      } else {
+        this.removeLabel(pk)
+      }
+    } else {
+      this.$emit("label-added", pk)
+    }
   }
 }
 </script>
diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue
index d84324ebd76a165270a23a2c14f0534910d307db..58b9b8f55bf4f6083afe699eb40819a47db5f80f 100644
--- a/frontend/src/components/submission_notes/SubmissionCorrection.vue
+++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue
@@ -42,8 +42,9 @@
           </submission-line>
         </tr>
       </template>
-      <annotated-submission-labels
-        class="elevation-1"
+      <label-selector
+        :assignedToFeedback="true"
+        class="mt-1 elevation-1"
         slot="labels"
       />
       <annotated-submission-bottom-toolbar
@@ -65,8 +66,10 @@ import CommentForm from '@/components/submission_notes/base/CommentForm.vue'
 import FeedbackComment from '@/components/submission_notes/base/FeedbackComment.vue'
 import AnnotatedSubmissionTopToolbar from '@/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar'
 import AnnotatedSubmissionBottomToolbar from '@/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar'
-import AnnotatedSubmissionLabels from '@/components/submission_notes/toolbars/AnnotatedSubmissionLabels.vue'
 import BaseAnnotatedSubmission from '@/components/submission_notes/base/BaseAnnotatedSubmission'
+import FeedbackLabel from "@/components/feedback_labels/FeedbackLabel.vue"
+import { FeedbackLabels as Labels } from '@/store/modules/feedback-labels'
+import LabelSelector from '@/components/feedback_labels/LabelSelector.vue'
 import SubmissionLine from '@/components/submission_notes/base/SubmissionLine'
 import { SubmissionNotes } from '@/store/modules/submission-notes'
 import { Authentication } from '@/store/modules/authentication'
@@ -79,8 +82,8 @@ export default {
     BaseAnnotatedSubmission,
     AnnotatedSubmissionBottomToolbar,
     AnnotatedSubmissionTopToolbar,
-    AnnotatedSubmissionLabels,
     FeedbackComment,
+    LabelSelector,
     CommentForm },
   name: 'submission-correction',
   data () {
diff --git a/frontend/src/components/submission_notes/base/CommentForm.vue b/frontend/src/components/submission_notes/base/CommentForm.vue
index 33628127237ca3aadd9e0af88de75dc48e4b61aa..9b27fc398b4d79db8ba3de4bb118fbf56b1774d2 100644
--- a/frontend/src/components/submission_notes/base/CommentForm.vue
+++ b/frontend/src/components/submission_notes/base/CommentForm.vue
@@ -16,7 +16,13 @@
       />
     </v-flex>
     <v-flex lg10 md8 sm8 my-2>
-      <label-selector/>
+      <label-selector
+        :assignedToFeedback="false"
+        :lineNo="this.lineNo"
+        :labelsDraft="this.labelsDraft"
+        @label-added="labelAdded"
+        @label-removed="labelRemoved"
+      />
     </v-flex>
     <v-flex lg2 ma-0>
       <v-btn id="submit-comment" color="success" @click="submitFeedback"><v-icon>check</v-icon>Submit</v-btn>
@@ -31,6 +37,7 @@ import Component from 'vue-class-component'
 import { Prop, Watch } from 'vue-property-decorator'
 import { SubmissionNotes } from '@/store/modules/submission-notes'
 import LabelSelector from "@/components/feedback_labels/LabelSelector.vue"
+import { FeedbackComment } from '@/models';
 
 @Component({
   components: {
@@ -42,6 +49,7 @@ export default class CommentForm extends Vue {
   @Prop({ type: String, required: true }) readonly lineNo!: string
 
   currentFeedback = this.feedback
+  labelsDraft: number[] = this.copyExistingLabels()
 
   selectInput (event: Event) {
     if (event !== null) {
@@ -50,15 +58,46 @@ export default class CommentForm extends Vue {
     }
   }
 
+  copyExistingLabels(): number[] {
+    const linesOrig = SubmissionNotes.state.origFeedback.feedbackLines
+    const linesUpdated = SubmissionNotes.state.updatedFeedback.feedbackLines
+
+    // priority for updatedFeedback and always select last created comment
+    if (linesUpdated && Object.keys(linesUpdated).length > 0) {
+      let line = <Partial<FeedbackComment>> linesUpdated[Number(this.lineNo)]
+      if (line.labels) return line.labels
+    } else if (linesOrig && Object.keys(linesOrig).length > 0) {
+      let lines = linesOrig[Number(this.lineNo)]
+      return lines[lines.length - 1].labels
+    }
+
+    return new Array()
+  }
+
   collapseTextField () {
     this.$emit('collapseFeedbackForm')
   }
+
+  labelAdded (pk: number) {
+    if (this.labelsDraft.includes(pk)) {
+      this.labelRemoved(pk)
+    } else {
+      this.labelsDraft.push(pk)
+    }
+  }
+
+  labelRemoved (pk: number) {
+    this.labelsDraft = this.labelsDraft.filter((val) => {
+      return val !== pk
+    })
+  }
   
-  submitFeedback () {
+  submitFeedback (labelPk?: string) {
     SubmissionNotes.UPDATE_FEEDBACK_LINE({
       lineNo: Number(this.lineNo),
       comment: {
-        text: this.currentFeedback
+        text: this.currentFeedback,
+        labels: this.labelsDraft,
       }
     })
     this.collapseTextField()
diff --git a/frontend/src/components/submission_notes/base/FeedbackComment.vue b/frontend/src/components/submission_notes/base/FeedbackComment.vue
index 9ef2ca7cf826be5fb6adb2991d272f0098aca4fb..a8cb74520d67c7dedd1d22a5d9c5d4a2e8d5bec7 100644
--- a/frontend/src/components/submission_notes/base/FeedbackComment.vue
+++ b/frontend/src/components/submission_notes/base/FeedbackComment.vue
@@ -32,6 +32,15 @@
         <v-icon v-else size="20px">restore</v-icon>
       </v-btn>
     </div>
+    <v-layout>
+      <v-flex>
+        <feedback-label
+          v-for="label in labelsToShow"
+          v-bind="label"
+          :key="label.pk"
+        />
+      </v-flex>
+    </v-layout>
   </div>
 </template>
 
@@ -39,9 +48,17 @@
 import { mapState } from 'vuex'
 import { UI } from '@/store/modules/ui'
 import { SubmissionNotes } from '@/store/modules/submission-notes'
+import FeedbackLabel from "@/components/feedback_labels/FeedbackLabel.vue"
+import { FeedbackLabels as Labels } from '@/store/modules/feedback-labels'
+
+// TODO: allow for displaying of empty comments when they have labels assigned
+// TODO: also make labels directly removable 
 
 export default {
   name: 'feedback-comment',
+  components: {
+    FeedbackLabel,
+  },
   props: {
     pk: {
       type: String,
@@ -74,7 +91,11 @@ export default {
     showVisibilityIcon: {
       type: Boolean,
       default: true
-    }
+    },
+    labels: {
+      type: Array,
+      required: true,
+    },
   },
   computed: {
     markedForDeletion () { return SubmissionNotes.state.commentsMarkedForDeletion },
@@ -93,7 +114,23 @@ export default {
     },
     backgroundColor () {
       return UI.state.darkMode ? 'grey' : '#F3F3F3'
-    }
+    },
+    labelsToShow () {
+      const mappedLabels = this.labels.map((val) => {
+      const label = Labels.availableLabels.find((label) => {
+        return label.pk === val
+      })
+
+      if (!label) return new Array()
+        return { 
+          pk: val,
+          name: label.name,
+          description: label.description,
+          colour: label.colour
+        }
+      })
+      return mappedLabels ? mappedLabels : new Array()
+    },
   },
   methods: {
     toggleDeleteComment () {
diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionLabels.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionLabels.vue
deleted file mode 100644
index 57c9647c11bd7c245dd9ca71c4695f23adbc6f10..0000000000000000000000000000000000000000
--- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionLabels.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<template>
-  <v-container>
-    <v-layout wrap>
-      <v-flex>
-        <feedback-label
-          v-for="(label, index) in labels"
-          v-bind="label"
-          :key="index"
-        />
-      </v-flex>
-    </v-layout>
-  </v-container>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Component from 'vue-class-component'
-import FeedbackLabel from "@/components/feedback_labels/FeedbackLabel.vue"
-import { SubmissionNotes } from '@/store/modules/submission-notes'
-import { FeedbackLabels as Labels } from '@/store/modules/feedback-labels'
-
-@Component({
-  components: {
-    FeedbackLabel
-  }
-})
-export default class AnnotatedSubmissionLabels extends Vue {
-  // maps origFeedback's label pk's to the ones stored in vuex
-  get labels() { 
-    const labels = SubmissionNotes.state.origFeedback.labels
-    if (labels) {
-      const mappedLabels = labels.map((val) => {
-        const label = Labels.availableLabels.find((label) => {
-          return Number(label.pk) === val
-        })
-
-        if (!label) return // TODO: throw error
-        return { pk: val, name: label.name, description: label.description }
-      })
-
-      return mappedLabels
-    }
-
-    return {}
-  }
-}
-</script>
-
diff --git a/frontend/src/models.ts b/frontend/src/models.ts
index ae98cc96b3077d6c105d3f7c0f78151c6d8ddcc4..6186106764718d559b974315ffe6aaf09bcc0b8e 100644
--- a/frontend/src/models.ts
+++ b/frontend/src/models.ts
@@ -139,7 +139,7 @@ export interface Feedback {
      * @memberof Feedback
      */
     feedbackStageForUser?: string,
-    labels?: number[],
+    labels: number[],
 }
 
 /**
@@ -184,7 +184,7 @@ export interface FeedbackComment {
      * @memberof FeedbackComment
      */
     visibleToStudent?: boolean
-    labels?: FeedbackLabel[]
+    labels: number[]
 }
 
 /**
@@ -193,10 +193,10 @@ export interface FeedbackComment {
  * @interface FeedbackLabel
  */
 export interface FeedbackLabel {
-    pk: string
+    pk: number
     name: string
     description: string
-    color: string
+    colour: string
 }
 
 /**
diff --git a/frontend/src/store/modules/feedback-labels.ts b/frontend/src/store/modules/feedback-labels.ts
index 2056979cdba61f38b0115869455f35efdd2f130c..cc4a95257a116b277ab4ec87e8ffd8085cc5996f 100644
--- a/frontend/src/store/modules/feedback-labels.ts
+++ b/frontend/src/store/modules/feedback-labels.ts
@@ -30,7 +30,14 @@ function ADD_LABEL(state: FeedbackLabelsState, label: FeedbackLabel) {
 }
 
 function REMOVE_LABEL(state: FeedbackLabelsState, label: FeedbackLabel) {
-  
+  state.labels = state.labels.filter((val) => {
+    return val.pk !== label.pk
+  })
+}
+
+function UPDATE_LABEL(state: FeedbackLabelsState, label: FeedbackLabel) {
+  REMOVE_LABEL(state, label)
+  ADD_LABEL(state, label)
 }
 
 export const FeedbackLabels = {
@@ -39,6 +46,7 @@ export const FeedbackLabels = {
 
   SET_LABELS: mb.commit(SET_LABELS),
   ADD_LABEL: mb.commit(ADD_LABEL),
-  REMOVE_LABEL: mb.commit(REMOVE_LABEL)
+  REMOVE_LABEL: mb.commit(REMOVE_LABEL),
+  UPDATE_LABEL: mb.commit(UPDATE_LABEL),
 }
 
diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index e7cd1e7089538282a0c68b4e09f37316dd0f5bd2..51c8f6f1c6787aa01106b2bb783267227b0e68fa 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -18,6 +18,7 @@ export interface SubmissionNotesState {
   origFeedback: Feedback
   updatedFeedback: Feedback
   commentsMarkedForDeletion: { [pk: string]: FeedbackComment }
+  changedLabels: boolean
 }
 
 function initialState(): SubmissionNotesState {
@@ -47,7 +48,8 @@ function initialState(): SubmissionNotesState {
       feedbackLines: {},
       labels: [],
     },
-    commentsMarkedForDeletion: {}
+    commentsMarkedForDeletion: {},
+    changedLabels: false
   }
 }
 
@@ -80,7 +82,7 @@ const scoreGetter = mb.read(function score(state) {
 const workInProgressGetter = mb.read(function workInProgress(state) {
   const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false)
   const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines || {}).length > 0
-  return openEditor || feedbackWritten
+  return openEditor || feedbackWritten || state.changedLabels
 })
 const isFeedbackCreationGetter = mb.read(function isFeedbackCreation(state) {
   return !state.origFeedback['feedbackStageForUser'] ||
@@ -94,6 +96,7 @@ function SET_ORIG_FEEDBACK(state: SubmissionNotesState, feedback: Feedback) {
   if (feedback) {
     state.origFeedback = feedback
     state.hasOrigFeedback = true
+    //state.updatedFeedback.labels = feedback.labels
   }
 }
 function SET_SHOW_FEEDBACK(state: SubmissionNotesState, val: boolean) {
@@ -105,6 +108,10 @@ function UPDATE_FEEDBACK_LINE(state: SubmissionNotesState, feedback: { lineNo: n
     Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo.toString(), feedback.comment)
   }
 }
+function SET_FEEDBACK_LABELS(state: SubmissionNotesState, labels: number[]) {
+  state.changedLabels = true
+  state.updatedFeedback.labels = labels
+}
 function UPDATE_FEEDBACK_SCORE(state: SubmissionNotesState, score: number) {
   state.updatedFeedback.score = score
 }
@@ -143,13 +150,13 @@ async function deleteComments({ state }: BareActionContext<SubmissionNotesState,
 }
 async function submitFeedback(
 { state }: BareActionContext<SubmissionNotesState, RootState>,
-{ isFinal = false, labels = [] }):
+{ isFinal = false}):
 Promise<AxiosResponse<void>[]> {
 
   let feedback: Partial<Feedback> = {
     isFinal: isFinal,
     ofSubmission: state.submission.pk,
-    labels: labels
+    labels: state.updatedFeedback.labels
   }
   if (state.origFeedback.score === undefined && state.updatedFeedback.score === undefined) {
     throw new Error('You need to give a score.')
@@ -185,6 +192,7 @@ export const SubmissionNotes = {
   SET_SUBMISSION: mb.commit(SET_SUBMISSION),
   SET_ORIG_FEEDBACK: mb.commit(SET_ORIG_FEEDBACK),
   SET_SHOW_FEEDBACK: mb.commit(SET_SHOW_FEEDBACK),
+  SET_FEEDBACK_LABELS: mb.commit(SET_FEEDBACK_LABELS),
   UPDATE_FEEDBACK_LINE: mb.commit(UPDATE_FEEDBACK_LINE),
   UPDATE_FEEDBACK_SCORE: mb.commit(UPDATE_FEEDBACK_SCORE),
   DELETE_FEEDBACK_LINE: mb.commit(DELETE_FEEDBACK_LINE),
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index a8cf2ddead1be9878c724525637bd0c472c39dfe..dc391a2453ff7cb2e473d8c468e24f711190be98 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -1412,6 +1412,11 @@ circular-json@^0.3.1:
   resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
   integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==
 
+clamp@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/clamp/-/clamp-1.0.1.tgz#66a0e64011816e37196828fdc8c8c147312c8634"
+  integrity sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ=
+
 class-utils@^0.3.5:
   version "0.3.6"
   resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
@@ -4517,6 +4522,11 @@ lodash.sortby@^4.7.0:
   resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
   integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
 
+lodash.throttle@^4.0.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
+  integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
+
 lodash.transform@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0"
@@ -4605,6 +4615,11 @@ map-visit@^1.0.0:
   dependencies:
     object-visit "^1.0.0"
 
+material-colors@^1.0.0:
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
+  integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==
+
 math-random@^1.0.1:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
@@ -7285,6 +7300,11 @@ timsort@^0.3.0:
   resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
   integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
 
+tinycolor2@^1.1.2:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
+  integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=
+
 tmp@^0.0.33:
   version "0.0.33"
   resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -7689,6 +7709,16 @@ vue-class-component@^6.0.0, vue-class-component@^6.2.0:
   resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-6.3.2.tgz#e6037e84d1df2af3bde4f455e50ca1b9eec02be6"
   integrity sha512-cH208IoM+jgZyEf/g7mnFyofwPDJTM/QvBNhYMjqGB8fCsRyTf68rH2ISw/G20tJv+5mIThQ3upKwoL4jLTr1A==
 
+vue-color@^2.7.0:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/vue-color/-/vue-color-2.7.0.tgz#31e898370a5786fd6c3007388cb7242db6ba6988"
+  integrity sha512-fak9oPRL3BsYtakTGmWIS2yNRppRYNlMgGGq78CMH34ipU8fLgi/bT9JiSPcscpdTNLGracuOFuZ8OFeml+SQQ==
+  dependencies:
+    clamp "^1.0.1"
+    lodash.throttle "^4.0.0"
+    material-colors "^1.0.0"
+    tinycolor2 "^1.1.2"
+
 vue-eslint-parser@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz#c268c96c6d94cfe3d938a5f7593959b0ca3360d1"
@@ -7742,6 +7772,11 @@ vue-style-loader@^4.1.0:
     hash-sum "^1.0.2"
     loader-utils "^1.0.2"
 
+vue-swatches@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/vue-swatches/-/vue-swatches-1.0.3.tgz#84eb23ba99bbbb0d56698f3a8bdb1b340203d33b"
+  integrity sha512-3J+Nc3bisvhhp0BW0pfTbQvdl3i+dhwoPjoM+2D6R6hW65KNpXOA+sJwcSg2j1Xd6fLcOV7LMb2sz6s4vKSWRg==
+
 vue-template-compiler@^2.5.16:
   version "2.5.22"
   resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.22.tgz#c3d3c02c65f1908205c4fbd3b0ef579e51239955"