use activity data in frontent

This commit is contained in:
Christian Cueni 2019-04-11 17:00:59 +02:00
parent fa98141f3c
commit 59d88d5143
12 changed files with 183 additions and 56 deletions

View File

@ -1,5 +1,5 @@
<template> <template>
<div :class="{'no-scroll': showModal || showMobileNavigation}" class="app"> <div :class="{'no-scroll': showModal || showMobileNavigation}" class="app" id="app">
<component :is="showModal" v-if="showModal"></component> <component :is="showModal" v-if="showModal"></component>
<component :is="layout"></component> <component :is="layout"></component>
<mobile-navigation v-if="showMobileNavigation"></mobile-navigation> <mobile-navigation v-if="showMobileNavigation"></mobile-navigation>

View File

@ -1,5 +1,5 @@
<template> <template>
<router-link to="/me"> <router-link to="/me/activity">
<div class="user-widget" :class="{'user-widget--is-profile': isProfile}"> <div class="user-widget" :class="{'user-widget--is-profile': isProfile}">
<user-icon class="user-widget__avatar" :src="avatar"></user-icon> <user-icon class="user-widget__avatar" :src="avatar"></user-icon>
<span class="user-widget__name">{{firstName}} {{lastName}}</span> <span class="user-widget__name">{{firstName}} {{lastName}}</span>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="assignment"> <div class="assignment">
<h3 class="assignment__title">{{assignment.title}}</h3> <h3 class="assignment__title" :id="assignment.id.replace(/=/g, '')">{{assignment.title}}</h3>
<p class="assignment__assignment-text"> <p class="assignment__assignment-text">
{{assignment.assignment}} {{assignment.assignment}}
</p> </p>
@ -67,6 +67,7 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex';
import ASSIGNMENT_QUERY from '@/graphql/gql/assignmentQuery.gql'; import ASSIGNMENT_QUERY from '@/graphql/gql/assignmentQuery.gql';
import ME_QUERY from '@/graphql/gql/meQuery.gql'; import ME_QUERY from '@/graphql/gql/meQuery.gql';
import UPDATE_ASSIGNMENT_MUTATION from '@/graphql/gql/mutations/updateAssignmentMutation.gql'; import UPDATE_ASSIGNMENT_MUTATION from '@/graphql/gql/mutations/updateAssignmentMutation.gql';
@ -92,6 +93,7 @@
}, },
computed: { computed: {
...mapGetters(['scrollToAssignmentId']),
final() { final() {
return !!this.submission && this.submission.final return !!this.submission && this.submission.final
}, },
@ -109,6 +111,7 @@
}, },
methods: { methods: {
...mapActions(['scrollToAssignmentReady']),
_save: debounce(function (submission) { _save: debounce(function (submission) {
this.saving++; this.saving++;
this.$apollo.mutate({ this.$apollo.mutate({
@ -196,6 +199,9 @@
result({data}) { result({data}) {
this.assignment = cloneDeep(data.assignment); this.assignment = cloneDeep(data.assignment);
this.assignment.submission = Object.assign(this.initialSubmission(), this.assignment.submission); this.assignment.submission = Object.assign(this.initialSubmission(), this.assignment.submission);
if (this.assignment.id === this.scrollToAssignmentId) {
this.$nextTick(() => this.scrollToAssignmentReady(true));
}
} }
}, },
me: { me: {

View File

@ -1,34 +1,14 @@
<template> <template>
<div class="module-activity"> <div class="module-activity">
<h3 class="module-activity__module-name">Geld und Kauf - Modul 9</h3> <h3 class="module-activity__module-name">{{moduleTitle}}</h3>
<h2 class="module-activity__title">Ökololololologie</h2> <h2 class="module-activity__title">{{title}}</h2>
<div class="module-activity__tasks activity-tasks"> <div class="module-activity__tasks activity-tasks">
<h4 class="activity-tasks__title">Aufträge</h4> <h4 class="activity-tasks__title">Aufträge</h4>
<ol class="activity-tasks__task-list task-list"> <ol class="activity-tasks__task-list task-list">
<li class="task-list__item task-item"> <li v-for="activity in activities" :key="activity.key" class="task-list__item task-item">
<h5 class="task-item__title">Auftrag x, Begründung</h5> <h5 class="task-item__title">{{activity.assignmentTitle}}</h5>
<p class="task-item__submission">"eine Antwrotasdfasdfasdf"</p> <p class="task-item__submission">{{activity.answer}}</p>
</li> <a href="#" @click="goToAssignment(activity)"><chevron-right class="task-item__chevron"></chevron-right></a>
<li class="task-list__item task-item">
<h5 class="task-item__title">Auftrag x, Begründung</h5>
<p class="task-item__submission">"eine Antwrotasdfasdfasdf"</p>
</li>
<li class="task-list__item task-item">
<h5 class="task-item__title">Auftrag x, Begründung</h5>
<p class="task-item__submission">"eine Antwrotasdfasdfasdf"</p>
</li>
<li class="task-list__item task-item">
<h5 class="task-item__title">Auftrag x, Begründung</h5>
<p class="task-item__submission">"eine Antwrotasdfasdfasdf"</p>
</li>
</ol>
</div>
<div class="module-activity__tasks activity-tasks">
<h4 class="activity-tasks__title activity-tasks__title--alternative">Basiswissen</h4>
<ol class="activity-tasks__task-list task-list">
<li class="task-list__item task-item">
<h5 class="task-item__title">Auftrag x, Begründung</h5>:
<p class="task-item__submission">"eine Antwrotasdfasdfasdf"</p>
</li> </li>
</ol> </ol>
</div> </div>
@ -37,7 +17,25 @@
<script> <script>
import { mapActions } from 'vuex'
import ChevronRight from '@/components/icons/ChevronRight';
export default { export default {
props: ['metaTitle', 'title', 'activities', 'topic', 'slug'],
components: {ChevronRight},
computed: {
moduleTitle () {
return `${this.topic.title} - ${this.metaTitle}`
}
},
methods: {
...mapActions(['scrollToAssignmentId']),
goToAssignment (activity) {
const url = `/module/${this.slug}/`;
this.scrollToAssignmentId(activity.assignmentId);
this.$router.push(url);
}
}
} }
</script> </script>
@ -70,7 +68,9 @@
.task-item { .task-item {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin-left: $medium-spacing; justify-content: space-between;
$line-height: 50px;
&__title { &__title {
&::after { &::after {
@ -79,9 +79,23 @@
margin-right: $medium-spacing; margin-right: $medium-spacing;
} }
&__submission {
width: 100%;
}
&__title, &__submission { &__title, &__submission {
line-height: 50px; line-height: $line-height;
height: 50px; height: $line-height;
}
&__chevron {
line-height: $line-height;
height: $medium-spacing;
vertical-align: middle;
position: relative;
top: ($line-height - $medium-spacing) / 2;
fill: $color-brand;
width: 30px;
} }
} }

View File

@ -0,0 +1,23 @@
query {
myActivity {
edges {
node {
id
text
assignment {
id
title
module {
title
metaTitle
slug
id
topic {
title
}
}
}
}
}
}
}

View File

@ -1,37 +1,59 @@
<template> <template>
<div class="activity"> <div class="activity">
<h1 class="activity__header">Meine Aktivität</h1> <h1 class="activity__header">Meine Aktivität</h1>
<module-activity class="activity"></module-activity> <div class="modules">
<module-activity class="activity"></module-activity> <module-activity v-for="moduleId in Object.keys(modules)" v-bind="modules[moduleId]" :key="moduleId" class="activity"></module-activity>
<module-activity class="activity"></module-activity> </div>
</div> </div>
</template> </template>
<script> <script>
import ModuleActivity from '@/components/profile/ModuleActivity'; import ModuleActivity from '@/components/profile/ModuleActivity';
import MY_ACTIVITY_QUERY from '@/graphql/gql/myActivity.gql'
export default { export default {
components: { components: {
ModuleActivity ModuleActivity
}, },
// apollo: { apollo: {
// schoolClasses: { submissions: {
// query: MY_SCHOOL_CLASSES_QUERY, query: MY_ACTIVITY_QUERY,
// update(data) { update(data) {
// return this.$getRidOfEdges(data).me.schoolClasses return this.$getRidOfEdges(data).myActivity;
// } }
// } }
// }, },
data() { data() {
return { return {
activities: [] submissions: []
} }
}, },
methods: { computed: {
modules () {
let modules = {};
this.submissions.map((submission) => {
let activity = {
assignmentId: submission.assignment.id,
assignmentTitle: submission.assignment.title,
answer: submission.text
};
const module = submission.assignment.module
if (!(module.id in modules)) {
modules[module.id] = {
...module,
activities: []
}
}
modules[module.id].activities = [...modules[module.id].activities, activity];
});
return modules
}
} }
} }
</script> </script>

View File

@ -3,6 +3,7 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'
import ASSIGNMENTS_QUERY from '@/graphql/gql/assignmentsQuery.gql'; import ASSIGNMENTS_QUERY from '@/graphql/gql/assignmentsQuery.gql';
import {moduleQuery} from '@/graphql/queries'; import {moduleQuery} from '@/graphql/queries';
@ -13,18 +14,60 @@
Module Module
}, },
methods: {
...mapActions(['scrollToAssignmentReady']),
},
computed: {
...mapGetters(['scrollToAssignmentId']),
},
apollo: { apollo: {
module: moduleQuery, module: moduleQuery,
assignments: { assignments() {
query: ASSIGNMENTS_QUERY return {
query: ASSIGNMENTS_QUERY
}
} }
}, },
data() { data() {
return { return {
module: {}, module: {},
assignments: [] assignments: [],
isScrolling: false
} }
},
mounted () {
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'setScrollToAssignmentReady' && state.scrollToAssignmentReady && !this.isScrolling) {
this.isScrolling = true;
// this.$nextTick(() => {
let options = {
container: '#app',
easing: 'ease-in',
offset: -60,
force: true,
cancelable: true,
onStart: (element) => {
},
onDone: (element) => {
this.scrollToAssignmentReady(false);
this.isScrolling = false;
},
onCancel: function() {
// scrolling has been interrupted
},
x: false,
y: true
};
this.$scrollTo(`#${this.scrollToAssignmentId.replace(/=/g, '')}`, 1000, options);
// })
}
})
},
beforeDestroy () {
this.scrollToAssignmentReady(false);
} }
} }
</script> </script>

View File

@ -79,15 +79,12 @@ const routes = [
}, },
{ {
path: '/me', path: '/me',
name: 'profile',
component: profilePage, component: profilePage,
meta: {
isProfile: true
},
children: [ children: [
{path: 'password-change', name: 'pw-change', component: passwordChange, meta: {isProfile: true}}, {path: 'password-change', name: 'pw-change', component: passwordChange, meta: {isProfile: true}},
{path: 'myclasses', name: 'my-classes', component: myClasses, meta: {isProfile: true}}, {path: 'myclasses', name: 'my-classes', component: myClasses, meta: {isProfile: true}},
{path: 'activity', name: 'activity', component: activity, meta: {isProfile: true}}, {path: 'activity', name: 'activity', component: activity, meta: {isProfile: true}},
{path: '', name: 'profile-activity', component: activity, meta: {isProfile: true}},
] ]
}, },
{path: '*', component: p404} {path: '*', component: p404}

View File

@ -25,7 +25,9 @@ export default new Vuex.Store({
id: 0, id: 0,
type: '' type: ''
}, },
vimeoId: null vimeoId: null,
scrollToAssignmentId: '',
scrollToAssignmentReady: false
}, },
getters: { getters: {
@ -34,7 +36,9 @@ export default new Vuex.Store({
}, },
showMobileNavigation: state => { showMobileNavigation: state => {
return state.showMobileNavigation return state.showMobileNavigation
} },
scrollToAssignmentId: state => state.scrollToAssignmentId,
scrollToAssignmentReady: state => state.scrollToAssignmentReady,
}, },
actions: { actions: {
@ -118,6 +122,12 @@ export default new Vuex.Store({
}, },
showMobileNavigation({commit}, payload) { showMobileNavigation({commit}, payload) {
commit('setShowMobileNavigation', payload); commit('setShowMobileNavigation', payload);
},
scrollToAssignmentId({commit}, payload) {
commit('setScrollToAssignmentId', payload);
},
scrollToAssignmentReady({commit}, payload) {
commit('setScrollToAssignmentReady', payload);
} }
}, },
@ -172,6 +182,12 @@ export default new Vuex.Store({
}, },
setShowMobileNavigation(state, payload) { setShowMobileNavigation(state, payload) {
state.showMobileNavigation = payload; state.showMobileNavigation = payload;
},
setScrollToAssignmentId(state, payload) {
state.scrollToAssignmentId = payload;
},
setScrollToAssignmentReady(state, payload) {
state.scrollToAssignmentReady = payload;
} }
} }
}) })

View File

@ -15,7 +15,6 @@ class StudentSubmissionQuery(object):
class MyActivityQuery(object): class MyActivityQuery(object):
# my_activity = relay.Node.Field(StudentSubmissionNode)
my_activity = DjangoFilterConnectionField(StudentSubmissionNode) my_activity = DjangoFilterConnectionField(StudentSubmissionNode)
def resolve_my_activity(self, info, **kwargs): def resolve_my_activity(self, info, **kwargs):

View File

@ -43,8 +43,10 @@ class MyAssignemntsText(DefaultUserTestCase):
myActivity { myActivity {
edges { edges {
node { node {
id
text text
assignment { assignment {
id
title title
} }
} }

View File

@ -67,13 +67,14 @@ class ChapterNode(DjangoObjectType):
class ModuleNode(DjangoObjectType): class ModuleNode(DjangoObjectType):
pk = graphene.Int() pk = graphene.Int()
chapters = DjangoFilterConnectionField(ChapterNode) chapters = DjangoFilterConnectionField(ChapterNode)
topic = graphene.Field('books.schema.queries.TopicNode')
hero_image = graphene.String() hero_image = graphene.String()
solutions_enabled = graphene.Boolean() solutions_enabled = graphene.Boolean()
class Meta: class Meta:
model = Module model = Module
only_fields = [ only_fields = [
'slug', 'title', 'meta_title', 'teaser', 'intro', 'objective_groups', 'assignments', 'hero_image' 'slug', 'title', 'meta_title', 'teaser', 'intro', 'objective_groups', 'assignments', 'hero_image', 'topic'
] ]
filter_fields = { filter_fields = {
'slug': ['exact', 'icontains', 'in'], 'slug': ['exact', 'icontains', 'in'],
@ -91,6 +92,10 @@ class ModuleNode(DjangoObjectType):
def resolve_chapters(self, info, **kwargs): def resolve_chapters(self, info, **kwargs):
return Chapter.get_by_parent(self) return Chapter.get_by_parent(self)
def resolve_topic(self, info, **kwargs):
some = self.get_parent().specific
return self.get_parent().specific
def resolve_solutions_enabled(self, info, **kwargs): def resolve_solutions_enabled(self, info, **kwargs):
return self.solutions_enabled_by.filter(pk=info.context.user.pk).exists() return self.solutions_enabled_by.filter(pk=info.context.user.pk).exists()