skillbox/client/src/pages/studentSubmission.vue

274 lines
8.3 KiB
Vue

<template>
<div class="submission-page">
<div class="submission-page__header submission-header">
<h2>Aufgabe</h2>
<p>{{ studentSubmission.assignment.assignment }}</p>
</div>
<div class="submission-page__content submission-content">
<h2>Ergebnis von {{ fullName }}</h2>
<p v-html="text"/>
<p
class="article-content__document"
v-if="studentSubmission.document && studentSubmission.document.length > 0">
<a
:href="studentSubmission.document"
class="entry-document__link link"
target="_blank">
<student-submission-document :document="studentSubmission.document"/>
</a>
</p>
</div>
<div class="submission-page__feedback feedback">
<submission-form
:user-input="feedback"
:saved="!unsaved"
placeholder="Feedback erfassen"
action="Feedback teilen"
shared-msg="Dieses Feedback wurde geteilt."
v-if="studentSubmission"
@turnIn="turnIn"
@saveInput="saveInput"
@reopen="reopen"
>
<div
class="feedback-submission__emojis emojis"
v-if="!final">
<span
:key="index"
class="emojis__emoji"
v-for="(emoji, index) in emojis"
@click="addEmoji(emoji)">{{ emoji }}</span>
</div>
</submission-form>
</div>
</div>
</template>
<script>
import {newLineToParagraph} from '@/helpers/text';
import debounce from 'lodash/debounce';
import cloneDeep from 'lodash/cloneDeep';
import StudentSubmissionDocument from '@/components/StudentSubmissionDocument';
import STUDENT_SUBMISSIONS_QUERY from '@/graphql/gql/studentSubmissionQuery.gql';
import UPDATE_FEEDBACK_MUTATION from '@/graphql/gql/mutations/updateFeedback.gql';
import UPDATE_FEEDBACK_WITH_TEXT_MUTATION from '@/graphql/gql/mutations/updateFeedbackWithText.gql';
import SubmissionForm from '@/components/content-blocks/assignment/SubmissionForm';
export default {
components: {
StudentSubmissionDocument,
SubmissionForm
},
data() {
return {
studentSubmission: {
assignment: {
title: ''
},
student: {
firstName: '',
lastName: ''
},
text: '',
document: '',
submissionFeedback: {
text: '',
final: false
}
},
unsaved: false,
saving: 0,
emojis: ['👍', '👎', '🙂', '😐', '😕', '🙁', '😮', '😉', '🙄', '❕', '❔', '🧐', '🤩', '🤗', '🤬', '🤢']
};
},
computed: {
final() {
return !!this.studentSubmission && this.studentSubmission.final;
},
text() {
return newLineToParagraph(this.studentSubmission.text);
},
fullName() {
return `${this.studentSubmission.student.firstName} ${this.studentSubmission.student.lastName}`;
},
feedback() {
return this.studentSubmission.submissionFeedback ? this.studentSubmission.submissionFeedback : {
text: '',
final: false
};
}
},
apollo: {
studentSubmission() {
return {
query: STUDENT_SUBMISSIONS_QUERY,
variables() {
return {
id: this.$route.params.id
};
},
result({data: {studentSubmission}}) {
if (!studentSubmission.submissionFeedback) { // create something for the cache to find
this.create();
}
this.studentSubmission = cloneDeep(studentSubmission); // we don't want to update the value when the server updates
}
};
},
},
methods: {
addEmoji(emoji) {
const feedbackText = this.feedback.text + emoji;
this.updateFeedbackText(feedbackText);
},
_save: debounce(function () {
this.saving++;
this.$apollo.mutate({
mutation: UPDATE_FEEDBACK_MUTATION,
variables: {
input: {
submissionFeedback: {
studentSubmission: this.studentSubmission.id,
text: this.studentSubmission.submissionFeedback.text,
}
}
},
update: this.updateCache
}).then(() => {
this.saving--;
if (this.saving === 0) {
this.unsaved = false;
}
});
}, 500),
saveInput: function (feedbackText) {
this.unsaved = true;
/*
We update the assignment on this component, so the changes are reflected on it. The server does not return
the updated entity, to prevent the UI to update when the user is entering his input
*/
this.updateFeedbackText(feedbackText);
this._save();
},
create() {
this.$apollo.mutate({
mutation: UPDATE_FEEDBACK_WITH_TEXT_MUTATION,
variables: {
input: {
submissionFeedback: {
studentSubmission: this.studentSubmission.id,
text: '',
final: false
}
}
},
update: this.updateCache
});
},
turnIn() {
this.$apollo.mutate({
mutation: UPDATE_FEEDBACK_WITH_TEXT_MUTATION,
variables: {
input: {
submissionFeedback: {
studentSubmission: this.studentSubmission.id,
text: this.studentSubmission.submissionFeedback.text,
final: true
}
}
},
update: this.updateCache
});
},
reopen() {
if (!this.studentSubmission.id) {
return;
}
this.$apollo.mutate({
mutation: UPDATE_FEEDBACK_MUTATION,
variables: {
input: {
submissionFeedback: {
studentSubmission: this.studentSubmission.id,
text: this.studentSubmission.submissionFeedback.text,
final: false
}
}
},
update: this.updateCache
});
},
updateCache(store, {data: {updateSubmissionFeedback: {successful, updatedSubmissionFeedback}}}) {
try {
if (successful) {
const query = STUDENT_SUBMISSIONS_QUERY;
const variables = {
id: this.studentSubmission.id
};
const data = store.readQuery({query, variables});
if (data) {
if (!data.studentSubmission.submissionFeedback) {
data.studentSubmission.submissionFeedback = {
'__typename': 'SubmissionFeedbackNode'
};
}
data.studentSubmission.submissionFeedback = Object.assign({}, data.studentSubmission.submissionFeedback, {
id: updatedSubmissionFeedback.id,
final: updatedSubmissionFeedback.final
});
if (updatedSubmissionFeedback.text !== undefined) {
// text is only being set on create and on turning in, then we'll update the cache with it. Otherwise, we'll trust the local state, as to not overwrite the input field
data.studentSubmission.submissionFeedback.text = updatedSubmissionFeedback.text;
} else {
data.studentSubmission.submissionFeedback.text = this.studentSubmission.submissionFeedback ? this.studentSubmission.submissionFeedback.text : '';
}
store.writeQuery({query, variables, data});
}
}
} catch (e) {
console.error(e);
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
}
},
updateFeedbackText(text) {
this.studentSubmission = Object.assign({}, this.studentSubmission, {
submissionFeedback: Object.assign({}, this.studentSubmission.submissionFeedback, {text: text})
});
}
},
};
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_functions.scss";
.submission-page {
&__content {
margin-top: 1.5*$large-spacing;
}
&__feedback {
margin-top: $large-spacing;
}
}
.emojis {
font-size: toRem(32px);
&__emoji {
cursor: pointer;
}
}
</style>