Merged in feature/detailpage (pull request #5)

Feature/detailpage

Approved-by: Ramon Wenger <ramon.wenger@iterativ.ch>
This commit is contained in:
Christian Cueni 2018-10-22 17:22:38 +00:00 committed by Ramon Wenger
commit 033372848e
12 changed files with 215 additions and 55 deletions

View File

@ -1,13 +1,15 @@
<template> <template>
<div class="assignment-with-submissions"> <div class="assignment-with-submissions">
<h1 class="assignment-with-submissions__title">{{assignment.title}}</h1> <h1 class="assignment-with-submissions__title">{{assignment.title}}</h1>
<router-link
<student-submission class="assignment-with-submissions__submission" :to="submissionLink(submission)"
v-for="(submission, index) in assignment.submissions" v-for="(submission, index) in assignment.submissions"
:key="index" :key="index">
:submission="submission" <student-submission class="assignment-with-submissions__submission"
> :submission="submission"
</student-submission> >
</student-submission>
</router-link>
</div> </div>
</template> </template>
@ -19,6 +21,11 @@
components: { components: {
StudentSubmission StudentSubmission
},
methods: {
submissionLink(submission) {
return `/submission/${submission.id}`
}
} }
} }
</script> </script>

View File

@ -3,20 +3,43 @@
<div class="student-submission__student-name"> <div class="student-submission__student-name">
{{name}} {{name}}
</div> </div>
<div class="student-submission__entry"> <div class="student-submission__entry entry">
{{submission.text}} <p>{{submission.text | trimToLength(50)}}</p>
<p v-if="submission.document && submission.document.length > 0" class="entry__document">
<student-submission-document :document="submission.document" class="entry-document"></student-submission-document>
</p>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import DocumentIcon from '@/components/icons/DocumentIcon'
import StudentSubmissionDocument from '@/components/StudentSubmissionDocument';
export default { export default {
props: ['submission'], props: ['submission'],
components: { DocumentIcon, StudentSubmissionDocument },
computed: { computed: {
name() { name() {
return this.submission && this.submission.student return this.submission && this.submission.student
? `${this.submission.student.firstName} ${this.submission.student.lastName}` : ''; ? `${this.submission.student.firstName} ${this.submission.student.lastName}` : '';
},
},
filters: {
trimToLength: function(text, numberOfChars) {
if (!text) {
return '';
}
if (text.length <= numberOfChars) {
return text;
}
const index = text.indexOf(' ', numberOfChars - 1);
if (index === -1) {
return text;
}
return `${text.substring(0, index)}`;
} }
} }
} }
@ -43,5 +66,9 @@
font-size: toRem(14px); font-size: toRem(14px);
font-family: $sans-serif-font-family; font-family: $sans-serif-font-family;
} }
.entry-document {
margin-top: 1rem;
}
} }
</style> </style>

View File

@ -0,0 +1,41 @@
<template>
<div class="submission-document">
<p v-if="document && document.length > 0" class="submission-document__content content">
<document-icon class="content__icon"></document-icon><span class="content__text">{{filename}}</span>
</p>
</div>
</template>
<script>
import DocumentIcon from '@/components/icons/DocumentIcon'
import filenameFromUrl from '@/helpers/urls';
export default {
props: ['document'],
name: 'student-submission-document',
components: { DocumentIcon },
computed: {
filename() {
return filenameFromUrl(this.document)
}
},
}
</script>
<style scoped lang="scss">
.content {
display: flex;
&__icon {
width: 25px;
align-self: center;
}
&__text {
align-self: center;
padding-left: 5px;
}
}
</style>

View File

@ -0,0 +1,14 @@
query StudentSubmissions($id: ID!) {
studentSubmission(id: $id) {
id
text
document
student {
firstName
lastName
}
assignment {
title
}
}
}

View File

@ -0,0 +1,6 @@
export default function filenameFromUrl(url) {
const index = url.lastIndexOf('/');
if (index === -1) { return url }
return url.substring(index + 1);
}

View File

@ -64,46 +64,3 @@
} }
} }
</script> </script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_functions.scss";
.article {
&__header {
grid-template-rows: 50px minmax(50px, max-content) auto;
grid-row-gap: 30px;
display: -ms-grid;
display: grid;
}
&__meta {
border-bottom: 1px solid $color-lightgrey;
align-self: end;
padding: 20px 0;
}
&__title {
grid-row: 3;
}
&__subtitle {
grid-row: 2;
font-family: $serif-font-family;
font-weight: 400;
}
&__content {
display: -ms-grid;
display: grid;
grid-row-gap: 40px;
padding-bottom: 40px;
/deep/ p {
font-size: toRem(18px);
line-height: 1.5;
}
}
}
</style>

View File

@ -0,0 +1,62 @@
<template>
<div class="article submission-page">
<div class="article__header">
<h1 class="article__title">{{studentSubmission.assignment.title}}</h1>
<h2 class="article__subtitle">{{`${studentSubmission.student.firstName} ${studentSubmission.student.lastName}`}}</h2>
</div>
<div class="article__content article-content">
<p v-if="studentSubmission.document && studentSubmission.document.length > 0" class="article-content__document">
<a :href="studentSubmission.document" class="entry-document__link link" target="_blank">
<student-submission-document :document="studentSubmission.document"></student-submission-document>
</a>
</p>
<p>{{studentSubmission.text}}</p>
</div>
</div>
</template>
<script>
import DocumentIcon from '@/components/icons/DocumentIcon';
import StudentSubmissionDocument from '@/components/StudentSubmissionDocument';
import STUDENT_SUBMISSIONS_QUERY from '@/graphql/gql/studentSubmissionQuery.gql';
export default {
components: { DocumentIcon, StudentSubmissionDocument },
data() {
return {
studentSubmission: {
assignment: {
title: ''
},
student: {
firstName: '',
lastName: ''
},
text: '',
document: ''
}
}
},
apollo: {
studentSubmission() {
return {
query: STUDENT_SUBMISSIONS_QUERY,
variables() {
return {
id: this.$route.params.id
}
}
}
},
},
}
</script>
<style scoped lang="scss">
.article-content {
&__document {
margin-bottom: 1rem;
}
}
</style>

View File

@ -14,6 +14,7 @@ import basicknowledge from '@/pages/basicknowledge'
import submissions from '@/pages/submissions' import submissions from '@/pages/submissions'
import p404 from '@/pages/p404' import p404 from '@/pages/p404'
import start from '@/pages/start' import start from '@/pages/start'
import submission from '@/pages/studentSubmission'
const routes = [ const routes = [
{path: '/', component: start, meta: {layout: 'blank'}}, {path: '/', component: start, meta: {layout: 'blank'}},
@ -43,6 +44,7 @@ const routes = [
{path: '/room/:slug', name: 'room', component: room, props: true}, {path: '/room/:slug', name: 'room', component: room, props: true},
{path: '/article/:slug', name: 'article', component: article, meta: {layout: 'simple'}}, {path: '/article/:slug', name: 'article', component: article, meta: {layout: 'simple'}},
{path: '/basic-knowledge', name: 'basic-knowledge', component: basicknowledge, meta: {layout: 'simple'}}, {path: '/basic-knowledge', name: 'basic-knowledge', component: basicknowledge, meta: {layout: 'simple'}},
{path: '/submission/:id', name: 'submission', component: submission, meta: {layout: 'simple'}},
{ {
path: '/book', path: '/book',
name: 'book', name: 'book',

View File

@ -0,0 +1,37 @@
.article {
&__header {
grid-template-rows: 50px minmax(50px, max-content) auto;
grid-row-gap: 30px;
display: -ms-grid;
display: grid;
}
&__meta {
border-bottom: 1px solid $color-lightgrey;
align-self: end;
padding: 20px 0;
}
&__title {
grid-row: 3;
}
&__subtitle {
grid-row: 2;
font-family: $serif-font-family;
font-weight: 400;
}
&__content {
display: -ms-grid;
display: grid;
grid-row-gap: 40px;
padding-bottom: 40px;
/deep/ p {
font-size: toRem(18px);
line-height: 1.5;
}
}
}

View File

@ -11,3 +11,4 @@
@import "uploadcare_overwrite"; @import "uploadcare_overwrite";
@import "help-text"; @import "help-text";
@import "objective-group"; @import "objective-group";
@import "article";

View File

@ -6,7 +6,7 @@ from graphene_django.debug import DjangoDebug
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from api import graphene_wagtail # Keep this import exactly here, it's necessary for StreamField conversion from api import graphene_wagtail # Keep this import exactly here, it's necessary for StreamField conversion
from assignments.schema.mutations import AssignmentMutations from assignments.schema.mutations import AssignmentMutations
from assignments.schema.queries import AssignmentsQuery from assignments.schema.queries import AssignmentsQuery, StudentSubmissionQuery
from books.schema.mutations.main import BookMutations from books.schema.mutations.main import BookMutations
from books.schema.queries import BookQuery from books.schema.queries import BookQuery
from objectives.mutations import ObjectiveMutations from objectives.mutations import ObjectiveMutations
@ -16,7 +16,8 @@ from rooms.schema import RoomsQuery
from users.schema import UsersQuery from users.schema import UsersQuery
class Query(UsersQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, graphene.ObjectType): class Query(UsersQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, StudentSubmissionQuery,
graphene.ObjectType):
node = relay.Node.Field() node = relay.Node.Field()
if settings.DEBUG: if settings.DEBUG:

View File

@ -1,9 +1,14 @@
from graphene import relay from graphene import relay
from graphene_django.filter import DjangoFilterConnectionField from graphene_django.filter import DjangoFilterConnectionField
from assignments.schema.types import AssignmentNode from assignments.schema.types import AssignmentNode, StudentSubmissionNode
class AssignmentsQuery(object): class AssignmentsQuery(object):
assignment = relay.Node.Field(AssignmentNode) assignment = relay.Node.Field(AssignmentNode)
assignments = DjangoFilterConnectionField(AssignmentNode) assignments = DjangoFilterConnectionField(AssignmentNode)
class StudentSubmissionQuery(object):
studentSubmission = relay.Node.Field(StudentSubmissionNode)