Add Feedback container component
This commit is contained in:
parent
ad07ada2f2
commit
d3a81e17d8
|
|
@ -4,7 +4,7 @@
|
||||||
<p class="assignment-with-submissions__text">{{assignment.assignment}}</p>
|
<p class="assignment-with-submissions__text">{{assignment.assignment}}</p>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<a class="button button--primary submissions-page__back" @click="back">Aufgabe im Modul anzeigen</a>
|
<a class="button button--primary submissions-page__back" @click="$emit('back')">Aufgabe im Modul anzeigen</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="assignment-with-submissions__solution" v-if="assignment.solution">
|
<div class="assignment-with-submissions__solution" v-if="assignment.solution">
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,20 @@
|
||||||
<solution :value="solution" v-if="assignment.solution"></solution>
|
<solution :value="solution" v-if="assignment.solution"></solution>
|
||||||
|
|
||||||
<template v-if="isStudent">
|
<template v-if="isStudent">
|
||||||
<div class="assignment__submission">
|
<div class="assignment__submission submissionform-container">
|
||||||
|
|
||||||
<div class="assignment__inputs">
|
<div class="submissionform-container__inputs">
|
||||||
<submission-form
|
<submission-form
|
||||||
@input="saveInput"
|
@input="saveInput"
|
||||||
:submission="submission"
|
:input-text="submission.text"
|
||||||
:saved="!unsaved"
|
:saved="!unsaved"
|
||||||
:final="final"
|
:final="final"
|
||||||
></submission-form>
|
></submission-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="assignment__actions" v-if="!final">
|
<div class="submissionform-container__actions" v-if="!final">
|
||||||
<button
|
<button
|
||||||
class="assignment__submit button button--primary button--white-bg"
|
class="submissionform-container__submit button button--primary button--white-bg"
|
||||||
@click="turnIn"
|
@click="turnIn"
|
||||||
>Ergebnis mit Lehrperson teilen
|
>Ergebnis mit Lehrperson teilen
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -265,26 +265,5 @@
|
||||||
color: $color-charcoal-dark;
|
color: $color-charcoal-dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__submission {
|
|
||||||
@include input-box-shadow;
|
|
||||||
background-color: $color-white;
|
|
||||||
border-radius: $input-border-radius;
|
|
||||||
border: 1px solid $color-silver;
|
|
||||||
padding: $medium-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__inputs {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__submit {
|
|
||||||
margin-right: $medium-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
<template>
|
||||||
|
<div class="feedback__submission submissionform-container">
|
||||||
|
<div class="submissionform-container__inputs">
|
||||||
|
<submission-form
|
||||||
|
@input="saveInput"
|
||||||
|
:input-text="feedback.text"
|
||||||
|
:saved="saved"
|
||||||
|
:final="final"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
></submission-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="submissionform-container__actions" v-if="!final">
|
||||||
|
<button class="submissionform-container__submit button button--primary button--white-bg"
|
||||||
|
@click="$emit('turnIn')"
|
||||||
|
>{{action}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <final-submission :submission="assignment.submission" v-if="final" @reopen="reopen"></final-submission> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SubmissionForm from '@/components/content-blocks/assignment/SubmissionForm';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SubmissionForm
|
||||||
|
},
|
||||||
|
|
||||||
|
props: ['feedback', 'saved', 'placeholder', 'action'],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
final() {
|
||||||
|
return !!this.feedback && this.feedback.final
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
saveInput(input) {
|
||||||
|
this.$emit('saveInput', input);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
</style>
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
v-auto-grow
|
v-auto-grow
|
||||||
rows="1"
|
rows="1"
|
||||||
class="submission-form__textarea"
|
class="submission-form__textarea"
|
||||||
placeholder="Ergebnis erfassen"
|
:placeholder="placeholder"
|
||||||
:readonly="final"
|
:readonly="final"
|
||||||
:value="submission.text"
|
:value="inputText"
|
||||||
@input="$emit('input', $event.target.value)"
|
@input="$emit('input', $event.target.value)"
|
||||||
></textarea>
|
></textarea>
|
||||||
<div class="submission-form__save-status submission-form__save-status--saved" v-if="saved">
|
<div class="submission-form__save-status submission-form__save-status--saved" v-if="saved">
|
||||||
|
|
@ -23,8 +23,15 @@
|
||||||
import LoadingIcon from '@/components/icons/LoadingIcon';
|
import LoadingIcon from '@/components/icons/LoadingIcon';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['submission', 'saved', 'final'],
|
props: {
|
||||||
|
inputText: String,
|
||||||
|
saved: Boolean,
|
||||||
|
final: Boolean,
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: 'Ergebnis erfassen'
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
TickCircleIcon,
|
TickCircleIcon,
|
||||||
LoadingIcon
|
LoadingIcon
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
mutation UpdateSubmissionFeedback($input: UpdateSubmissionFeedbackInput!) {
|
||||||
|
updateSubmissionFeedback(input: $input){
|
||||||
|
successful
|
||||||
|
updatedSubmissionFeedback {
|
||||||
|
id
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,11 @@ query StudentSubmissions($id: ID!) {
|
||||||
}
|
}
|
||||||
assignment {
|
assignment {
|
||||||
title
|
title
|
||||||
|
assignment
|
||||||
|
}
|
||||||
|
submissionfeedback {
|
||||||
|
text
|
||||||
|
final
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="article submission-page">
|
<div class="article submission-page">
|
||||||
<div class="article__header">
|
<div class="article__header">
|
||||||
<h1 class="article__title">{{studentSubmission.assignment.title}}</h1>
|
<!-- <h1 class="article__title">{{studentSubmission.assignment.title}}</h1> -->
|
||||||
<h2 class="article__subtitle">{{fullName}}</h2>
|
<h1>Aufgabe</h1>
|
||||||
|
<p>{{studentSubmission.assignment.assignment}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="article__content article-content">
|
<div class="article__content article-content">
|
||||||
|
<h2 class="article__subtitle">Ergebnis von {{fullName}}</h2>
|
||||||
<p v-if="studentSubmission.document && studentSubmission.document.length > 0" class="article-content__document">
|
<p v-if="studentSubmission.document && studentSubmission.document.length > 0" class="article-content__document">
|
||||||
<a :href="studentSubmission.document" class="entry-document__link link" target="_blank">
|
<a :href="studentSubmission.document" class="entry-document__link link" target="_blank">
|
||||||
<student-submission-document :document="studentSubmission.document"></student-submission-document>
|
<student-submission-document :document="studentSubmission.document"></student-submission-document>
|
||||||
|
|
@ -12,21 +14,38 @@
|
||||||
</p>
|
</p>
|
||||||
<p class="article-content__text" v-html="text"></p>
|
<p class="article-content__text" v-html="text"></p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="feedback__submission">
|
||||||
|
<feedback-form
|
||||||
|
:feedback="studentSubmission.submissionfeedback"
|
||||||
|
@turnIn="turnIn"
|
||||||
|
@saveInput="saveInput"
|
||||||
|
:placholder="'Feedback erfassen'"
|
||||||
|
:action="'Feedback teilen'"
|
||||||
|
:saved="!unsaved"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {newLineToParagraph} from '@/helpers/text';
|
import {newLineToParagraph} from '@/helpers/text';
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
import StudentSubmissionDocument from '@/components/StudentSubmissionDocument';
|
import StudentSubmissionDocument from '@/components/StudentSubmissionDocument';
|
||||||
import STUDENT_SUBMISSIONS_QUERY from '@/graphql/gql/studentSubmissionQuery.gql';
|
import STUDENT_SUBMISSIONS_QUERY from '@/graphql/gql/studentSubmissionQuery.gql';
|
||||||
|
import UPDATE_FEEDBACK_MUTATION from '@/graphql/gql/mutations/updateFeedback.gql';
|
||||||
|
import FeedbackForm from '@/components/content-blocks/assignment/FeedbackForm';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
StudentSubmissionDocument
|
StudentSubmissionDocument,
|
||||||
|
FeedbackForm
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
final() {
|
||||||
|
return !!this.studentSubmission && this.studentSubmission.final;
|
||||||
|
},
|
||||||
text() {
|
text() {
|
||||||
return newLineToParagraph(this.studentSubmission.text);
|
return newLineToParagraph(this.studentSubmission.text);
|
||||||
},
|
},
|
||||||
|
|
@ -48,6 +67,73 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
_save: debounce(function () {
|
||||||
|
const that = this;
|
||||||
|
|
||||||
|
this.saving++;
|
||||||
|
this.$apollo.mutate({
|
||||||
|
mutation: UPDATE_FEEDBACK_MUTATION,
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
submissionFeedback: {
|
||||||
|
studentSubmission: this.studentSubmission.id,
|
||||||
|
text: this.studentSubmission.submissionfeedback.text,
|
||||||
|
final: this.studentSubmission.submissionfeedback.final
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update(store, {data: {updateSubmissionFeedback: {successful, updatedSubmissionFeedback}}}) {
|
||||||
|
try {
|
||||||
|
if (successful) {
|
||||||
|
const query = STUDENT_SUBMISSIONS_QUERY;
|
||||||
|
const variables = {
|
||||||
|
id: that.studentSubmission.id
|
||||||
|
};
|
||||||
|
const data = store.readQuery({query, variables});
|
||||||
|
|
||||||
|
data.studentSubmission = Object.assign({}, that.studentSubmission);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).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.studentSubmission = Object.assign({}, this.studentSubmission, {
|
||||||
|
submissionfeedback: Object.assign({}, this.studentSubmission.submissionfeedback, {text: feedbackText})
|
||||||
|
})
|
||||||
|
this._save();
|
||||||
|
},
|
||||||
|
turnIn() {
|
||||||
|
this.$apollo.mutate({
|
||||||
|
mutation: UPDATE_FEEDBACK_MUTATION,
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
submissionFeedback: {
|
||||||
|
studentSubmission: this.studentSubmission.id,
|
||||||
|
text: this.studentSubmission.submissionfeedback.text,
|
||||||
|
final: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
studentSubmission: {
|
studentSubmission: {
|
||||||
|
|
@ -59,8 +145,14 @@
|
||||||
lastName: ''
|
lastName: ''
|
||||||
},
|
},
|
||||||
text: '',
|
text: '',
|
||||||
document: ''
|
document: '',
|
||||||
}
|
submissionfeedback: {
|
||||||
|
text: '',
|
||||||
|
final: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unsaved: false,
|
||||||
|
saving: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
<div class="submissions-page skillbox__content">
|
<div class="submissions-page skillbox__content">
|
||||||
<h2 class="submissions-page__heading">Aufgabe</h2>
|
<h2 class="submissions-page__heading">Aufgabe</h2>
|
||||||
<assignment-with-submissions v-if="!$apollo.queries.assignment.loading"
|
<assignment-with-submissions v-if="!$apollo.queries.assignment.loading"
|
||||||
:assignment="assignment"></assignment-with-submissions>
|
:assignment="assignment"
|
||||||
|
@back="back"></assignment-with-submissions>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AssignmentWithSubmissions from '@/components/AssignmentWithSubmissions';
|
import AssignmentWithSubmissions from '@/components/AssignmentWithSubmissions';
|
||||||
|
|
||||||
import ASSIGNMENT_WITH_SUBMISSIONS_QUERY from '@/graphql/gql/assignmentWithSubmissionsQuery.gql';
|
import ASSIGNMENT_WITH_SUBMISSIONS_QUERY from '@/graphql/gql/assignmentWithSubmissionsQuery.gql';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -61,6 +61,4 @@
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
@import '@/styles/_mixins.scss';
|
||||||
|
|
||||||
|
.submissionform-container {
|
||||||
|
|
||||||
|
@include input-box-shadow;
|
||||||
|
background-color: $color-white;
|
||||||
|
border-radius: $input-border-radius;
|
||||||
|
border: 1px solid $color-silver;
|
||||||
|
padding: $medium-spacing;
|
||||||
|
|
||||||
|
&__inputs {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__submit {
|
||||||
|
margin-right: $medium-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,4 +20,5 @@
|
||||||
@import "solutions";
|
@import "solutions";
|
||||||
@import "password_forms";
|
@import "password_forms";
|
||||||
@import "public-page";
|
@import "public-page";
|
||||||
@import "student-submission"
|
@import "student-submission";
|
||||||
|
@import "submissionform-container";
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
import graphene
|
|
||||||
from graphene import relay
|
from graphene import relay
|
||||||
from graphene_django.filter import DjangoFilterConnectionField
|
from graphene_django.filter import DjangoFilterConnectionField
|
||||||
from rest_framework.exceptions import PermissionDenied
|
|
||||||
|
|
||||||
from api.utils import get_by_id_or_slug
|
from assignments.models import StudentSubmission
|
||||||
from assignments.models import StudentSubmission, SubmissionFeedback
|
from assignments.schema.types import AssignmentNode, StudentSubmissionNode
|
||||||
from assignments.schema.types import AssignmentNode, StudentSubmissionNode, SubmissionFeedbackNode
|
|
||||||
|
|
||||||
|
|
||||||
class AssignmentsQuery(object):
|
class AssignmentsQuery(object):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue