diff --git a/resources/vue/components/courseware/tasks/CoursewareDashboardStudents.vue b/resources/vue/components/courseware/tasks/CoursewareDashboardStudents.vue
index 1bdbf3ce0e17b2e1b048207169eed4603a3219b3..455f7206c92292588a0bd0bb71c357da91340a5d 100644
--- a/resources/vue/components/courseware/tasks/CoursewareDashboardStudents.vue
+++ b/resources/vue/components/courseware/tasks/CoursewareDashboardStudents.vue
@@ -2,38 +2,47 @@
     <div class="cw-dashboard-students-wrapper">
         <table class="default" v-if="taskGroups.length">
             <thead>
-                <tr>
-                    <th>{{ $gettext('Status') }}</th>
-                    <th>{{ $gettext('Titel') }}</th>
-                    <th>{{ $gettext('Bearbeitungszeit') }}</th>
+                <tr class="sortable">
+                    <th>
+                        {{ $gettext('Status') }}
+                    </th>
+                    <th :class="getSortClass('task-group-title')" @click="sort('task-group-title')">
+                        {{ $gettext('Titel') }}
+                    </th>
+                    <th :class="getSortClass('end-date')" @click="sort('end-date')">
+                        {{ $gettext('Bearbeitungszeit') }}
+                    </th>
                 </tr>
             </thead>
             <tbody>
-            <tr v-for="(taskGroup, index) in taskGroups">
-                <td>
-                    <StudipIcon
-                        :shape="status(taskGroup).shape"
-                        :role="status(taskGroup).role"
-                        :title="status(taskGroup).description"
-                        aria-hidden="true"
+                <tr v-for="(taskGroup, index) in sortedTaskGroups" :key="index">
+                    <td>
+                        <StudipIcon
+                            :shape="status(taskGroup).shape"
+                            :role="status(taskGroup).role"
+                            :title="status(taskGroup).description"
+                            aria-hidden="true"
                         />
-                    <span class="sr-only">{{ status(taskGroup).description }}</span>
-                </td>
-                <td>
-                    <router-link :to="{ name: 'task-groups-show', params: { id: taskGroup.id } }">{{ taskGroup.attributes.title }}</router-link>
-                </td>
-                <td>
-                    <StudipDate :date="new Date(taskGroup.attributes['start-date'])" />–<StudipDate :date="new Date(taskGroup.attributes['end-date'])" />
-                </td>
-            </tr>
+                        <span class="sr-only">{{ status(taskGroup).description }}</span>
+                    </td>
+                    <td>
+                        <router-link :to="{ name: 'task-groups-show', params: { id: taskGroup.id } }">{{
+                            taskGroup.attributes.title
+                        }}</router-link>
+                    </td>
+                    <td>
+                        <StudipDate :date="new Date(taskGroup.attributes['start-date'])" />–<StudipDate
+                            :date="new Date(taskGroup.attributes['end-date'])"
+                        />
+                    </td>
+                </tr>
             </tbody>
         </table>
 
-        <CompanionBox v-else-if="!tasksLoading"
-            :msgCompanion="$gettext('Es wurden noch keine Aufgaben verteilt.')">
+        <CompanionBox v-else-if="!tasksLoading" :msgCompanion="$gettext('Es wurden noch keine Aufgaben verteilt.')">
             <template #companionActions>
                 <button @click="setShowTasksDistributeDialog(true)" type="button" class="button">
-                    {{ $gettext("Aufgabe verteilen") }}
+                    {{ $gettext('Aufgabe verteilen') }}
                 </button>
             </template>
         </CompanionBox>
@@ -59,6 +68,10 @@ export default {
         StudipDate,
         StudipIcon,
     },
+    data: () => ({
+        sortBy: 'end-date',
+        sortASC: false,
+    }),
     computed: {
         ...mapGetters({
             context: 'context',
@@ -66,10 +79,19 @@ export default {
             taskGroupsByCid: 'tasks/taskGroupsByCid',
             tasksLoading: 'courseware-tasks/isLoading',
         }),
+        sortedTaskGroups() {
+            const sorters = {
+                'task-group-title': (taskGroup) => taskGroup.attributes.title,
+                'end-date': (taskGroup) => new Date(taskGroup.attributes['end-date']),
+            };
+
+            return _.chain(this.taskGroups)
+                .sortBy([sorters[this.sortBy]])
+                .thru((sorted) => (this.sortAsc ? sorted : _.reverse(sorted)))
+                .value();
+        },
         taskGroups() {
-            return _.sortBy(this.taskGroupsByCid(this.context.id), [
-                (taskGroup) => -new Date(taskGroup.attributes['end-date']),
-            ]);
+            return this.taskGroupsByCid(this.context.id);
         },
     },
     methods: {
@@ -77,7 +99,12 @@ export default {
             loadAllTasks: 'courseware-tasks/loadAll',
             setShowTasksDistributeDialog: 'tasks/setShowTasksDistributeDialog',
         }),
-        status: getStatus,
+        getSortClass(col) {
+            if (col === this.sortBy) {
+                return this.sortASC ? 'sortasc' : 'sortdesc';
+            }
+            return '';
+        },
         reloadTasks() {
             this.loadAllTasks({
                 options: {
@@ -86,6 +113,23 @@ export default {
                 },
             });
         },
+        sort(sortBy) {
+            if (this.sortBy === sortBy) {
+                this.sortASC = !this.sortASC;
+            } else {
+                this.sortBy = sortBy;
+            }
+        },
+        status: getStatus,
     },
 };
 </script>
+
+<style scoped>
+th {
+    cursor: pointer;
+}
+th:first-child {
+    cursor: not-allowed;
+}
+</style>