Add snapshot module header
Also refactor some queries and other code
This commit is contained in:
parent
046b741458
commit
3d78761e20
|
|
@ -42,6 +42,7 @@ module.exports = {
|
||||||
alias: {
|
alias: {
|
||||||
'@': resolve('src'),
|
'@': resolve('src'),
|
||||||
styles: resolve('src/styles'),
|
styles: resolve('src/styles'),
|
||||||
|
gql: resolve('src/graphql/gql')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
<template>
|
||||||
|
<div class="snapshot-header">
|
||||||
|
<h1>Snapshot {{ id }}</h1>
|
||||||
|
<div class="snapshot-header__meta">
|
||||||
|
{{ created }} – {{ creator }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="snapshot-header__section">
|
||||||
|
<h2 class="snapshot-header__subtitle">
|
||||||
|
In diesem Snapshot sind {{ changesCount }} Anpassungen gespeichert:
|
||||||
|
</h2>
|
||||||
|
<ul class="snapshot-header__list">
|
||||||
|
<li class="snapshot-header__list-item">{{ hiddenObjectives }} Lernziele wurden ausgeblendet
|
||||||
|
</li>
|
||||||
|
<li class="snapshot-header__list-item">{{ newObjectives }} Lernziele wurde erfasst</li>
|
||||||
|
<li class="snapshot-header__list-item">{{ hiddenContentBlocks }} Inhaltsblöcke wurden
|
||||||
|
ausgeblendet
|
||||||
|
</li>
|
||||||
|
<li class="snapshot-header__list-item">{{ newContentBlocks }} Inhaltsblock wurde erfasst</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="snapshot-header__section">
|
||||||
|
<h2 class="snapshot-header__subtitle">
|
||||||
|
Willst du diesen Snapshot anwenden?
|
||||||
|
</h2>
|
||||||
|
<div>
|
||||||
|
<checkbox
|
||||||
|
:checked="agreement"
|
||||||
|
label="Ich will die Anpassungen aus diesem Snapshot in das Modul kopieren."
|
||||||
|
@input="agreement = $event"/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="snapshot-header__buttons snapshot-header__section">
|
||||||
|
<button
|
||||||
|
:disabled="!agreement"
|
||||||
|
:class="{'button--disabled-alt': !agreement}"
|
||||||
|
class="button button--primary"
|
||||||
|
@click="apply">Snapshot anwenden
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--secondary"
|
||||||
|
@click="back">Abbrechen
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import dateformat from '@/helpers/date-format';
|
||||||
|
import Checkbox from '@/components/ui/Checkbox';
|
||||||
|
|
||||||
|
import me from '@/mixins/me';
|
||||||
|
|
||||||
|
import APPLY_SNAPSHOT_MUTATION from 'gql/mutations/snapshots/applySnapshot.gql';
|
||||||
|
import {MODULE_PAGE} from '@/router/module.names';
|
||||||
|
|
||||||
|
const _getChange = (snapshot, index) => {
|
||||||
|
try {
|
||||||
|
return snapshot.changes[index];
|
||||||
|
} catch (e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
snapshot: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [me],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Checkbox,
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
agreement: false,
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
created() {
|
||||||
|
return dateformat(this.snapshot.created);
|
||||||
|
},
|
||||||
|
creator() {
|
||||||
|
const {firstName, lastName} = this.snapshot.creator || {};
|
||||||
|
return `${firstName} ${lastName}`;
|
||||||
|
},
|
||||||
|
hiddenObjectives() {
|
||||||
|
return _getChange(this.snapshot, 'hiddenObjectives');
|
||||||
|
},
|
||||||
|
newObjectives() {
|
||||||
|
return _getChange(this.snapshot, 'newObjectives');
|
||||||
|
},
|
||||||
|
hiddenContentBlocks() {
|
||||||
|
return _getChange(this.snapshot, 'hiddenContentBlocks');
|
||||||
|
},
|
||||||
|
newContentBlocks() {
|
||||||
|
return _getChange(this.snapshot, 'newContentBlocks');
|
||||||
|
},
|
||||||
|
changesCount() {
|
||||||
|
return this.hiddenObjectives + this.newObjectives + this.hiddenContentBlocks + this.newContentBlocks;
|
||||||
|
},
|
||||||
|
id() {
|
||||||
|
try {
|
||||||
|
return atob(this.snapshot.id).split(':')[1];
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
apply() {
|
||||||
|
this.$apollo.mutate({
|
||||||
|
mutation: APPLY_SNAPSHOT_MUTATION,
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
snapshot: this.snapshot.id,
|
||||||
|
selectedClass: this.me.selectedClass.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).then(({data: {applySnapshot: {module: {slug}}}}) => {
|
||||||
|
this.$router.push({
|
||||||
|
name: MODULE_PAGE,
|
||||||
|
params: {
|
||||||
|
slug: slug,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
back() {
|
||||||
|
this.$router.go(-1);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '~styles/helpers';
|
||||||
|
|
||||||
|
.snapshot-header {
|
||||||
|
&__subtitle {
|
||||||
|
@include heading-3;
|
||||||
|
margin-bottom: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta {
|
||||||
|
@include regular-text;
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
padding-left: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list-item {
|
||||||
|
@include regular-text;
|
||||||
|
line-height: 1.5;
|
||||||
|
list-style-type: '–';
|
||||||
|
padding-left: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__section {
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -59,6 +59,7 @@
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
@include default-link;
|
@include default-link;
|
||||||
|
color: $color-brand;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
#import "../fragments/moduleParts.gql"
|
|
||||||
query ModuleQuery($id: ID!) {
|
|
||||||
module(id: $id) {
|
|
||||||
...ModuleParts
|
|
||||||
chapters {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
contentBlocks {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
slug
|
|
||||||
title
|
|
||||||
type
|
|
||||||
contents
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#import "../fragments/moduleParts.gql"
|
||||||
|
query ModuleQuery($id: ID, $slug: String) {
|
||||||
|
module(id: $id, slug:$slug) {
|
||||||
|
...ModuleParts
|
||||||
|
chapters {
|
||||||
|
id
|
||||||
|
contentBlocks {
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
title
|
||||||
|
type
|
||||||
|
contents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
#import "../../fragments/chapterParts.gql"
|
#import "gql/fragments/chapterParts.gql"
|
||||||
#import "../../fragments/assignmentParts.gql"
|
#import "gql/fragments/assignmentParts.gql"
|
||||||
#import "../../fragments/objectiveGroupParts.gql"
|
#import "gql/fragments/objectiveGroupParts.gql"
|
||||||
#import "../../fragments/objectiveParts.gql"
|
#import "gql/fragments/objectiveParts.gql"
|
||||||
#import "../../fragments/moduleParts.gql"
|
#import "gql/fragments/moduleParts.gql"
|
||||||
#import "../../fragments/contentBlockInterfaceParts.gql"
|
#import "gql/fragments/contentBlockInterfaceParts.gql"
|
||||||
#import "../../fragments/contentBlockParts.gql"
|
#import "gql/fragments/contentBlockParts.gql"
|
||||||
query ModuleDetailsQuery($slug: String!) {
|
query ModuleDetailsQuery($slug: String, $id: ID) {
|
||||||
module(slug: $slug) {
|
module(slug: $slug, id: $id) {
|
||||||
...ModuleParts
|
...ModuleParts
|
||||||
assignments {
|
assignments {
|
||||||
edges {
|
edges {
|
||||||
|
|
@ -16,28 +16,16 @@ query ModuleDetailsQuery($slug: String!) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objectiveGroups {
|
objectiveGroups {
|
||||||
edges {
|
...ObjectiveGroupParts
|
||||||
node {
|
objectives {
|
||||||
...ObjectiveGroupParts
|
...ObjectiveParts
|
||||||
objectives {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...ObjectiveParts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chapters {
|
chapters {
|
||||||
edges {
|
...ChapterParts
|
||||||
node {
|
contentBlocks {
|
||||||
...ChapterParts
|
...ContentBlockInterfaceParts
|
||||||
contentBlocks {
|
...ContentBlockParts
|
||||||
...ContentBlockInterfaceParts
|
|
||||||
...ContentBlockParts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import ADD_NOTE_MUTATION from '@/graphql/gql/mutations/addNote.gql';
|
import ADD_NOTE_MUTATION from '@/graphql/gql/mutations/addNote.gql';
|
||||||
import CONTENT_BLOCK_QUERY from '@/graphql/gql/queries/contentBlockQuery.gql';
|
import CONTENT_BLOCK_QUERY from '@/graphql/gql/queries/contentBlockQuery.gql';
|
||||||
import CHAPTER_QUERY from '@/graphql/gql/queries/chapterQuery.gql';
|
import CHAPTER_QUERY from '@/graphql/gql/queries/chapterQuery.gql';
|
||||||
import MODULE_QUERY from '@/graphql/gql/queries/moduleByIdQuery.gql';
|
import MODULE_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
|
||||||
import INSTRUMENT_FRAGMENT from '@/graphql/gql/fragments/instrumentParts.gql';
|
import INSTRUMENT_FRAGMENT from '@/graphql/gql/fragments/instrumentParts.gql';
|
||||||
|
|
||||||
const getBlockType = id => atob(id).split(':')[0];
|
const getBlockType = id => atob(id).split(':')[0];
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,3 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import "@/styles/_variables.scss";
|
|
||||||
@import "@/styles/_mixins.scss";
|
|
||||||
@import "@/styles/_default-layout.scss";
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="skillbox layout layout--simple">
|
<div
|
||||||
|
:class="{'layout--full-width': $route.meta.fullWidth}"
|
||||||
|
class="skillbox layout layout--simple">
|
||||||
<div
|
<div
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click="back">
|
@click="back">
|
||||||
<cross class="close-button__icon"/>
|
<cross class="close-button__icon"/>
|
||||||
</div>
|
</div>
|
||||||
<router-view/>
|
<router-view class="layout__content" />
|
||||||
<simple-footer
|
<simple-footer
|
||||||
class="layout__footer"
|
class="layout__footer"
|
||||||
v-if="enableFooter"/>
|
v-if="enableFooter"/>
|
||||||
|
|
@ -36,8 +38,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "@/styles/_variables.scss";
|
@import "~styles/helpers";
|
||||||
@import "@/styles/_mixins.scss";
|
|
||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
&--simple {
|
&--simple {
|
||||||
|
|
@ -60,6 +61,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$parent: &;
|
||||||
|
|
||||||
|
&--full-width {
|
||||||
|
#{$parent}__content {
|
||||||
|
grid-column: 1 / span 3;
|
||||||
|
grid-row: 1 / span 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
grid-column: 1 / span 3;
|
grid-column: 1 / span 3;
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +84,9 @@
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
grid-column: 3;
|
grid-column: 3;
|
||||||
|
grid-row: 1;
|
||||||
-ms-grid-column: 3;
|
-ms-grid-column: 3;
|
||||||
|
-ms-grid-row: 1;
|
||||||
margin-right: $medium-spacing;
|
margin-right: $medium-spacing;
|
||||||
margin-top: $medium-spacing;
|
margin-top: $medium-spacing;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<module :module="snapshot"/>
|
<div class="snapshot">
|
||||||
|
<header class="snapshot__header">
|
||||||
|
<snapshot-header
|
||||||
|
:snapshot="snapshot"
|
||||||
|
/>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<module
|
||||||
|
:module="snapshot"
|
||||||
|
class="snapshot__module"/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SNAPSHOT_DETAIL_QUERY from '@/graphql/gql/queries/snapshots/detail.gql';
|
import SNAPSHOT_DETAIL_QUERY from '@/graphql/gql/queries/snapshots/detail.gql';
|
||||||
|
|
||||||
import Module from '@/components/modules/Module';
|
import Module from '@/components/modules/Module';
|
||||||
|
import Checkbox from '@/components/ui/Checkbox';
|
||||||
|
import SnapshotHeader from '@/components/modules/SnapshotHeader';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -16,6 +28,8 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
SnapshotHeader,
|
||||||
|
Checkbox,
|
||||||
Module,
|
Module,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -37,4 +51,21 @@
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~styles/helpers';
|
@import '~styles/helpers';
|
||||||
|
|
||||||
|
.snapshot {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 800px 1fr;
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
background-color: $color-brand-light;
|
||||||
|
grid-column: 1 / span 3;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__module {
|
||||||
|
grid-column: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
<script>
|
<script>
|
||||||
import * as SurveyVue from 'survey-vue';
|
import * as SurveyVue from 'survey-vue';
|
||||||
import {css} from '@/survey.config';
|
import {css} from '@/survey.config';
|
||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
import SURVEY_QUERY from '@/graphql/gql/queries/surveyQuery.gql';
|
import SURVEY_QUERY from '@/graphql/gql/queries/surveyQuery.gql';
|
||||||
import MODULE_QUERY from '@/graphql/gql/queries/moduleByIdQuery.gql';
|
|
||||||
import UPDATE_ANSWER from '@/graphql/gql/mutations/updateAnswer.gql';
|
import UPDATE_ANSWER from '@/graphql/gql/mutations/updateAnswer.gql';
|
||||||
import Solution from '@/components/content-blocks/Solution';
|
import Solution from '@/components/content-blocks/Solution';
|
||||||
|
|
||||||
|
|
@ -30,12 +30,20 @@
|
||||||
|
|
||||||
const Survey = SurveyVue.Survey;
|
const Survey = SurveyVue.Survey;
|
||||||
|
|
||||||
|
const MODULE_QUERY = gql`
|
||||||
|
query Module($id: ID) {
|
||||||
|
module(id: $id) {
|
||||||
|
solutionsEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['id'],
|
props: ['id'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Solution,
|
Solution,
|
||||||
Survey
|
Survey,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -45,9 +53,9 @@
|
||||||
module: {},
|
module: {},
|
||||||
completed: false,
|
completed: false,
|
||||||
me: {
|
me: {
|
||||||
permissions: []
|
permissions: [],
|
||||||
},
|
},
|
||||||
saveDisabled: false
|
saveDisabled: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -80,7 +88,7 @@
|
||||||
<p class="solution-text__answer">${answer.answer}</p>
|
<p class="solution-text__answer">${answer.answer}</p>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}, '')
|
}, ''),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
answers() {
|
answers() {
|
||||||
|
|
@ -90,7 +98,7 @@
|
||||||
},
|
},
|
||||||
isTeacher() {
|
isTeacher() {
|
||||||
return isTeacher(this);
|
return isTeacher(this);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -116,21 +124,21 @@
|
||||||
let question = sender.getQuestionByName(k);
|
let question = sender.getQuestionByName(k);
|
||||||
data[k] = {
|
data[k] = {
|
||||||
answer: survey.data[k],
|
answer: survey.data[k],
|
||||||
correct: question && question.correctAnswer ? question.correctAnswer : ''
|
correct: question && question.correctAnswer ? question.correctAnswer : '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const answer = {
|
const answer = {
|
||||||
surveyId: this.id,
|
surveyId: this.id,
|
||||||
data: JSON.stringify(data)
|
data: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$apollo.mutate({
|
this.$apollo.mutate({
|
||||||
mutation: UPDATE_ANSWER,
|
mutation: UPDATE_ANSWER,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
answer
|
answer,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
update: (store, {data: {updateAnswer: {answer}}}) => {
|
update: (store, {data: {updateAnswer: {answer}}}) => {
|
||||||
const query = SURVEY_QUERY;
|
const query = SURVEY_QUERY;
|
||||||
|
|
@ -140,7 +148,7 @@
|
||||||
queryData.survey.answer = answer;
|
queryData.survey.answer = answer;
|
||||||
store.writeQuery({query, variables, data: queryData});
|
store.writeQuery({query, variables, data: queryData});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -160,7 +168,7 @@
|
||||||
this.survey.clear();
|
this.survey.clear();
|
||||||
this.survey.data = data; // reapply it
|
this.survey.data = data; // reapply it
|
||||||
this.saveDisabled = false;
|
this.saveDisabled = false;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
apollo: {
|
apollo: {
|
||||||
|
|
@ -168,7 +176,7 @@
|
||||||
query: SURVEY_QUERY,
|
query: SURVEY_QUERY,
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
id: this.id
|
id: this.id,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
manual: true,
|
manual: true,
|
||||||
|
|
@ -190,14 +198,14 @@
|
||||||
this.$apollo.addSmartQuery('module', {
|
this.$apollo.addSmartQuery('module', {
|
||||||
query: MODULE_QUERY,
|
query: MODULE_QUERY,
|
||||||
variables: {
|
variables: {
|
||||||
id: module.id
|
id: module.id,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
me: meQuery
|
me: meQuery,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import authRoutes from './auth.routes';
|
||||||
import roomRoutes from './room.routes';
|
import roomRoutes from './room.routes';
|
||||||
|
|
||||||
import store from '@/store/index';
|
import store from '@/store/index';
|
||||||
|
import {LAYOUT_SIMPLE} from '@/router/core.constants';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
|
|
@ -34,24 +35,25 @@ const routes = [
|
||||||
...moduleRoutes,
|
...moduleRoutes,
|
||||||
...authRoutes,
|
...authRoutes,
|
||||||
...roomRoutes,
|
...roomRoutes,
|
||||||
{path: '/article/:slug', name: 'article', component: article, meta: {layout: 'simple'}},
|
...onboardingRoutes,
|
||||||
|
...portfolioRoutes,
|
||||||
|
...meRoutes,
|
||||||
|
{path: '/article/:slug', name: 'article', component: article, meta: {layout: LAYOUT_SIMPLE}},
|
||||||
{
|
{
|
||||||
path: '/instruments/',
|
path: '/instruments/',
|
||||||
name: 'instrument-overview',
|
name: 'instrument-overview',
|
||||||
component: instrumentOverview,
|
component: instrumentOverview,
|
||||||
},
|
},
|
||||||
{path: '/instrument/:slug', name: 'instrument', component: instrument, meta: {layout: 'simple'}},
|
{path: '/instrument/:slug', name: 'instrument', component: instrument, meta: {layout: LAYOUT_SIMPLE}},
|
||||||
{path: '/submission/:id', name: 'submission', component: submission, meta: {layout: 'simple'}},
|
{path: '/submission/:id', name: 'submission', component: submission, meta: {layout: LAYOUT_SIMPLE}},
|
||||||
...portfolioRoutes,
|
|
||||||
{path: '/topic/:topicSlug', name: 'topic', component: topic, alias: '/book/topic/:topicSlug'},
|
{path: '/topic/:topicSlug', name: 'topic', component: topic, alias: '/book/topic/:topicSlug'},
|
||||||
...meRoutes,
|
{path: '/join-class', name: 'join-class', component: joinClass, meta: {layout: LAYOUT_SIMPLE}},
|
||||||
{path: '/join-class', name: 'join-class', component: joinClass, meta: {layout: 'simple'}},
|
|
||||||
{
|
{
|
||||||
path: '/survey/:id',
|
path: '/survey/:id',
|
||||||
component: surveyPage,
|
component: surveyPage,
|
||||||
name: 'survey',
|
name: 'survey',
|
||||||
props: true,
|
props: true,
|
||||||
meta: {layout: 'simple'},
|
meta: {layout: LAYOUT_SIMPLE},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/check-email',
|
path: '/check-email',
|
||||||
|
|
@ -93,7 +95,6 @@ const routes = [
|
||||||
component: news,
|
component: news,
|
||||||
name: 'news',
|
name: 'news',
|
||||||
},
|
},
|
||||||
...onboardingRoutes,
|
|
||||||
{path: '/styleguide', component: styleGuidePage},
|
{path: '/styleguide', component: styleGuidePage},
|
||||||
{
|
{
|
||||||
path: '*',
|
path: '*',
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import joinTeam from '@/pages/me/joinTeam';
|
||||||
import createTeam from '@/pages/me/createTeam';
|
import createTeam from '@/pages/me/createTeam';
|
||||||
|
|
||||||
import {CREATE_TEAM, JOIN_TEAM, MY_TEAM, SHOW_SCHOOL_CLASS_CODE, SHOW_TEAM_CODE} from './me.names';
|
import {CREATE_TEAM, JOIN_TEAM, MY_TEAM, SHOW_SCHOOL_CLASS_CODE, SHOW_TEAM_CODE} from './me.names';
|
||||||
|
import {LAYOUT_SIMPLE} from '@/router/core.constants';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
|
|
@ -32,23 +33,23 @@ export default [
|
||||||
alias: 'create-class',
|
alias: 'create-class',
|
||||||
name: 'create-class',
|
name: 'create-class',
|
||||||
component: createClass,
|
component: createClass,
|
||||||
meta: {layout: 'simple'},
|
meta: {layout: LAYOUT_SIMPLE},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'class/code',
|
path: 'class/code',
|
||||||
alias: 'show-code',
|
alias: 'show-code',
|
||||||
name: SHOW_SCHOOL_CLASS_CODE,
|
name: SHOW_SCHOOL_CLASS_CODE,
|
||||||
component: showSchoolClassCode,
|
component: showSchoolClassCode,
|
||||||
meta: {layout: 'simple'},
|
meta: {layout: LAYOUT_SIMPLE},
|
||||||
},
|
},
|
||||||
{path: 'team', name: MY_TEAM, component: myTeam, meta: {isProfile: true}},
|
{path: 'team', name: MY_TEAM, component: myTeam, meta: {isProfile: true}},
|
||||||
{path: 'team/join', name: JOIN_TEAM, component: joinTeam, meta: {isProfile: true, layout: 'simple'}},
|
{path: 'team/join', name: JOIN_TEAM, component: joinTeam, meta: {isProfile: true, layout: LAYOUT_SIMPLE}},
|
||||||
{path: 'team/create', name: CREATE_TEAM, component: createTeam, meta: {isProfile: true, layout: 'simple'}},
|
{path: 'team/create', name: CREATE_TEAM, component: createTeam, meta: {isProfile: true, layout: LAYOUT_SIMPLE}},
|
||||||
{
|
{
|
||||||
path: 'team/code',
|
path: 'team/code',
|
||||||
name: SHOW_TEAM_CODE,
|
name: SHOW_TEAM_CODE,
|
||||||
component: showTeamCode,
|
component: showTeamCode,
|
||||||
meta: {layout: 'simple'},
|
meta: {layout: LAYOUT_SIMPLE},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,18 @@ import moduleBase from '@/pages/module/module-base';
|
||||||
import module from '@/pages/module/module';
|
import module from '@/pages/module/module';
|
||||||
import submissions from '@/pages/submissions';
|
import submissions from '@/pages/submissions';
|
||||||
import moduleVisibility from '@/pages/module/moduleVisibility';
|
import moduleVisibility from '@/pages/module/moduleVisibility';
|
||||||
import {MODULE_PAGE, MODULE_SETTINGS_PAGE, SUBMISSIONS_PAGE, VISIBILITY_PAGE, SNAPSHOT_LIST, SNAPSHOT_DETAIL} from '@/router/module.names';
|
import {
|
||||||
|
MODULE_PAGE,
|
||||||
|
MODULE_SETTINGS_PAGE,
|
||||||
|
SNAPSHOT_DETAIL,
|
||||||
|
SNAPSHOT_LIST,
|
||||||
|
SUBMISSIONS_PAGE,
|
||||||
|
VISIBILITY_PAGE,
|
||||||
|
} from '@/router/module.names';
|
||||||
import settingsPage from '@/pages/module/moduleSettings';
|
import settingsPage from '@/pages/module/moduleSettings';
|
||||||
import snapshots from '@/pages/snapshot/snapshots';
|
import snapshots from '@/pages/snapshot/snapshots';
|
||||||
import snapshot from '@/pages/snapshot/snapshot';
|
import snapshot from '@/pages/snapshot/snapshot';
|
||||||
|
import {LAYOUT_SIMPLE} from '@/router/core.constants';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
|
|
@ -40,7 +48,7 @@ export default [
|
||||||
name: VISIBILITY_PAGE,
|
name: VISIBILITY_PAGE,
|
||||||
component: moduleVisibility,
|
component: moduleVisibility,
|
||||||
meta: {
|
meta: {
|
||||||
layout: 'simple',
|
layout: LAYOUT_SIMPLE,
|
||||||
hideNavigation: true,
|
hideNavigation: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -56,8 +64,13 @@ export default [
|
||||||
path: 'snapshot/:id',
|
path: 'snapshot/:id',
|
||||||
component: snapshot,
|
component: snapshot,
|
||||||
name: SNAPSHOT_DETAIL,
|
name: SNAPSHOT_DETAIL,
|
||||||
props: true
|
props: true,
|
||||||
}
|
meta: {
|
||||||
|
layout: LAYOUT_SIMPLE,
|
||||||
|
hideNavigation: true,
|
||||||
|
fullWidth: true
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,17 @@
|
||||||
&--white-bg {
|
&--white-bg {
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
}
|
}
|
||||||
|
@mixin disabled {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
&--disabled {
|
&--disabled {
|
||||||
|
@include disabled;
|
||||||
background-color: $color-silver-light;
|
background-color: $color-silver-light;
|
||||||
}
|
}
|
||||||
|
&--disabled-alt {
|
||||||
|
@include disabled;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
&--big {
|
&--big {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ $icon-size: 20px;
|
||||||
&__icon {
|
&__icon {
|
||||||
width: $icon-size;
|
width: $icon-size;
|
||||||
height: $icon-size;
|
height: $icon-size;
|
||||||
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
border: 2px solid $color-silver-dark;
|
border: 2px solid $color-silver-dark;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from books.schema.interfaces.module import ModuleInterface
|
||||||
from books.schema.nodes.chapter import ChapterNode
|
from books.schema.nodes.chapter import ChapterNode
|
||||||
from notes.models import ModuleBookmark, ContentBlockBookmark, ChapterBookmark
|
from notes.models import ModuleBookmark, ContentBlockBookmark, ChapterBookmark
|
||||||
from notes.schema import ModuleBookmarkNode, ContentBlockBookmarkNode, ChapterBookmarkNode
|
from notes.schema import ModuleBookmarkNode, ContentBlockBookmarkNode, ChapterBookmarkNode
|
||||||
|
from objectives.schema import ObjectiveGroupNode
|
||||||
from surveys.models import Answer
|
from surveys.models import Answer
|
||||||
from surveys.schema import AnswerNode
|
from surveys.schema import AnswerNode
|
||||||
|
|
||||||
|
|
@ -26,7 +27,7 @@ class ModuleNode(DjangoObjectType):
|
||||||
}
|
}
|
||||||
interfaces = (ModuleInterface,)
|
interfaces = (ModuleInterface,)
|
||||||
|
|
||||||
chapters = DjangoFilterConnectionField(ChapterNode)
|
chapters = graphene.List(ChapterNode)
|
||||||
solutions_enabled = graphene.Boolean()
|
solutions_enabled = graphene.Boolean()
|
||||||
bookmark = graphene.Field(ModuleBookmarkNode)
|
bookmark = graphene.Field(ModuleBookmarkNode)
|
||||||
my_submissions = DjangoFilterConnectionField(StudentSubmissionNode)
|
my_submissions = DjangoFilterConnectionField(StudentSubmissionNode)
|
||||||
|
|
@ -34,6 +35,7 @@ class ModuleNode(DjangoObjectType):
|
||||||
my_content_bookmarks = DjangoFilterConnectionField(ContentBlockBookmarkNode)
|
my_content_bookmarks = DjangoFilterConnectionField(ContentBlockBookmarkNode)
|
||||||
my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode)
|
my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode)
|
||||||
snapshots = graphene.List('books.schema.nodes.SnapshotNode')
|
snapshots = graphene.List('books.schema.nodes.SnapshotNode')
|
||||||
|
objective_groups = graphene.List(ObjectiveGroupNode)
|
||||||
|
|
||||||
def resolve_chapters(self, info, **kwargs):
|
def resolve_chapters(self, info, **kwargs):
|
||||||
return Chapter.get_by_parent(self)
|
return Chapter.get_by_parent(self)
|
||||||
|
|
|
||||||
|
|
@ -44,14 +44,17 @@ class BookQuery(object):
|
||||||
slug = kwargs.get('slug')
|
slug = kwargs.get('slug')
|
||||||
id = kwargs.get('id')
|
id = kwargs.get('id')
|
||||||
module = None
|
module = None
|
||||||
|
try:
|
||||||
|
if id is not None:
|
||||||
|
module = get_object(Module, id)
|
||||||
|
|
||||||
if id is not None:
|
elif slug is not None:
|
||||||
module = get_object(Module, id)
|
module = Module.objects.get(slug=slug)
|
||||||
|
|
||||||
elif slug is not None:
|
return module
|
||||||
module = Module.objects.get(slug=slug)
|
|
||||||
|
|
||||||
return module
|
except Module.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
def resolve_topic(self, info, **kwargs):
|
def resolve_topic(self, info, **kwargs):
|
||||||
slug = kwargs.get('slug')
|
slug = kwargs.get('slug')
|
||||||
|
|
|
||||||
|
|
@ -6,28 +6,25 @@ from api.schema import schema
|
||||||
from books.factories import ModuleFactory, ChapterFactory, ContentBlockFactory
|
from books.factories import ModuleFactory, ChapterFactory, ContentBlockFactory
|
||||||
from books.models import Snapshot, ChapterSnapshot
|
from books.models import Snapshot, ChapterSnapshot
|
||||||
from core.tests.base_test import SkillboxTestCase
|
from core.tests.base_test import SkillboxTestCase
|
||||||
|
from users.factories import SchoolClassFactory
|
||||||
from users.models import User, SchoolClass
|
from users.models import User, SchoolClass
|
||||||
|
|
||||||
MODULE_QUERY = """
|
MODULE_QUERY = """
|
||||||
query ModulesQuery($slug: String!) {
|
query ModulesQuery($slug: String, $id: ID) {
|
||||||
module(slug: $slug) {
|
module(slug: $slug, id: $id) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
chapters {
|
chapters {
|
||||||
edges {
|
id
|
||||||
node {
|
contentBlocks {
|
||||||
id
|
id
|
||||||
contentBlocks {
|
title
|
||||||
id
|
visibleFor {
|
||||||
title
|
name
|
||||||
visibleFor {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
hiddenFor {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
hiddenFor {
|
||||||
|
name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -226,3 +223,43 @@ class CreateSnapshotTestCase(SkillboxTestCase):
|
||||||
self.assertEqual(second['title'], 'hidden')
|
self.assertEqual(second['title'], 'hidden')
|
||||||
self.assertEqual(second['hidden'], True)
|
self.assertEqual(second['hidden'], True)
|
||||||
self.assertEqual(third['title'], 'custom')
|
self.assertEqual(third['title'], 'custom')
|
||||||
|
|
||||||
|
def test_not_too_much_user_creator_info(self):
|
||||||
|
self.assertTrue(False)
|
||||||
|
|
||||||
|
def test_apply_initial_snapshot(self):
|
||||||
|
teacher2 = User.objects.get(username='teacher2')
|
||||||
|
teacher2_client = self.get_client(user=teacher2)
|
||||||
|
third_class = SchoolClassFactory(
|
||||||
|
users=[teacher2],
|
||||||
|
name='third_class'
|
||||||
|
)
|
||||||
|
|
||||||
|
# make a neutral snapshot, nothing new, nothing hidden
|
||||||
|
result = teacher2_client.execute(CREATE_SNAPSHOT_MUTATION, variables={
|
||||||
|
'input': {
|
||||||
|
'module': self.slug,
|
||||||
|
'selectedClass': to_global_id('SchoolClassNode', third_class.pk),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.assertIsNone(result.get('errors'))
|
||||||
|
snapshot_id = result['data']['createSnapshot']['snapshot']['id']
|
||||||
|
|
||||||
|
result = self.client.execute(APPLY_SNAPSHOT_MUTATION, variables={
|
||||||
|
'input': {
|
||||||
|
'snapshot': snapshot_id,
|
||||||
|
'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.assertIsNone(result.get('errors'))
|
||||||
|
|
||||||
|
result = self.client.execute(MODULE_QUERY, variables={
|
||||||
|
'slug': self.module.slug
|
||||||
|
})
|
||||||
|
self.assertIsNone(result.get('errors'))
|
||||||
|
module = result['data']['module']
|
||||||
|
chapter1, chapter2 = module['chapters']
|
||||||
|
cb1, cb2, cb3 = chapter1['contentBlocks']
|
||||||
|
self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in cb1['hiddenFor']])
|
||||||
|
self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in cb2['hiddenFor']])
|
||||||
|
self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in cb3['visibleFor']])
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,30 @@ from core.mixins import HiddenAndVisibleForMixin, HiddenForMixin
|
||||||
from objectives.models import ObjectiveGroup, Objective
|
from objectives.models import ObjectiveGroup, Objective
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectiveNode(DjangoObjectType, HiddenAndVisibleForMixin):
|
||||||
|
pk = graphene.Int()
|
||||||
|
user_created = graphene.Boolean()
|
||||||
|
mine = graphene.Boolean()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Objective
|
||||||
|
filter_fields = ['text']
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
def resolve_objective_progress(self, info, **kwargs):
|
||||||
|
return self.objective_progress.filter(user=info.context.user)
|
||||||
|
|
||||||
|
def resolve_user_created(self, info, **kwargs):
|
||||||
|
return self.owner is not None
|
||||||
|
|
||||||
|
def resolve_mine(self, info, **kwargs):
|
||||||
|
return self.owner is not None and self.owner.pk == info.context.user.pk
|
||||||
|
|
||||||
|
|
||||||
class ObjectiveGroupNode(DjangoObjectType, HiddenForMixin):
|
class ObjectiveGroupNode(DjangoObjectType, HiddenForMixin):
|
||||||
pk = graphene.Int()
|
pk = graphene.Int()
|
||||||
display_title = graphene.String()
|
display_title = graphene.String()
|
||||||
|
objectives = graphene.List(ObjectiveNode)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ObjectiveGroup
|
model = ObjectiveGroup
|
||||||
|
|
@ -37,26 +58,6 @@ class ObjectiveGroupNode(DjangoObjectType, HiddenForMixin):
|
||||||
return self.objectives.filter(objectives_from_publisher | objectives_from_teacher)
|
return self.objectives.filter(objectives_from_publisher | objectives_from_teacher)
|
||||||
|
|
||||||
|
|
||||||
class ObjectiveNode(DjangoObjectType, HiddenAndVisibleForMixin):
|
|
||||||
pk = graphene.Int()
|
|
||||||
user_created = graphene.Boolean()
|
|
||||||
mine = graphene.Boolean()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Objective
|
|
||||||
filter_fields = ['text']
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
def resolve_objective_progress(self, info, **kwargs):
|
|
||||||
return self.objective_progress.filter(user=info.context.user)
|
|
||||||
|
|
||||||
def resolve_user_created(self, info, **kwargs):
|
|
||||||
return self.owner is not None
|
|
||||||
|
|
||||||
def resolve_mine(self, info, **kwargs):
|
|
||||||
return self.owner is not None and self.owner.pk == info.context.user.pk
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectivesQuery(object):
|
class ObjectivesQuery(object):
|
||||||
objective_group = relay.Node.Field(ObjectiveGroupNode)
|
objective_group = relay.Node.Field(ObjectiveGroupNode)
|
||||||
objective_groups = DjangoFilterConnectionField(ObjectiveGroupNode)
|
objective_groups = DjangoFilterConnectionField(ObjectiveGroupNode)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue